Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow array of trees #188

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Filter {
* Enforces that it is invoked on an instance of a class which prototypically
* inherits from Filter, and which is not itself Filter.
*/
constructor(inputNode: BroccoliNode, options: FilterOptions): Filter;
constructor(inputNodes: BroccoliNode | BroccoliNode[], options: FilterOptions): Filter;

/**
* method `processString`: must be implemented on subclasses of
Expand Down Expand Up @@ -124,6 +124,11 @@ var node = new Awk('docs', 'ES6', 'ECMAScript 2015');
module.exports = node;
```

Now, persistent filter supports array of input nodes.
```
module.exports = new Awk(['fixture','fixture_2'], 'test', 'real');
```

## Persistent Cache

Adding persist flag allows a subclass to persist state across restarts. This exists to mitigate the upfront cost of some more expensive transforms on warm boot. __It does not aim to improve incremental build performance, if it does, it should indicate something is wrong with the filter or input filter in question.__
Expand Down
35 changes: 21 additions & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,9 @@ abstract class Filter extends Plugin {
return !!result;
}

constructor(inputTree: InputNode, options: Options) {
super([inputTree], {
constructor(inputNodes: InputNode | InputNode[], options: Options) {
inputNodes = Array.isArray(inputNodes) ? inputNodes : [inputNodes];
super(inputNodes, {
name: (options && options.name),
annotation: (options && options.annotation),
persistentOutput: true
Expand Down Expand Up @@ -212,8 +213,6 @@ abstract class Filter extends Plugin {
}

async build() {
let srcDir = this.inputPaths[0];
let destDir = this.outputPath;

if (this.dependencyInvalidation && !this.dependencies) {
this.dependencies = this.processor.initialDependencies(this.input, this.inputEncoding || 'utf8');
Expand Down Expand Up @@ -309,7 +308,7 @@ abstract class Filter extends Plugin {
// wrap this in a function so it doesn't actually run yet, and can be throttled
let changeOperation = () => {
instrumentation.change++;
return this._handleFile(relativePath, srcDir, destDir, entry!, outputFilePath, forceInvalidation, true, instrumentation);
return this._handleFile(relativePath, entry!, outputFilePath, forceInvalidation, true, instrumentation);
};
if (this.async) {
pendingWork.push(changeOperation);
Expand All @@ -320,7 +319,7 @@ abstract class Filter extends Plugin {
// wrap this in a function so it doesn't actually run yet, and can be throttled
let createOperation = () => {
instrumentation.create++;
return this._handleFile(relativePath, srcDir, destDir, entry!, outputFilePath, forceInvalidation, false, instrumentation);
return this._handleFile(relativePath, entry!, outputFilePath, forceInvalidation, false, instrumentation);
};
if (this.async) {
pendingWork.push(createOperation);
Expand All @@ -342,27 +341,29 @@ abstract class Filter extends Plugin {
});
}

async _handleFile(relativePath: string, srcDir: string, destDir: string, entry: Entry, outputPath: string, forceInvalidation: boolean, isChange: boolean, stats: ApplyPatchesSchema) {
async _handleFile(relativePath: string, entry: Entry, outputPath: string, forceInvalidation: boolean, isChange: boolean, stats: ApplyPatchesSchema) {
stats.handleFile++;

let handleFileStart = process.hrtime();
try {
let result: string | ProcessResult | undefined;
let srcPath = srcDir + '/' + relativePath;

if (this.canProcessFile(relativePath, entry)) {
stats.processed++;
if (this._outputLinks[outputPath] === true) {
delete this._outputLinks[outputPath];
this.output.unlinkSync(outputPath);
}
result = await this.processAndCacheFile(srcDir, destDir, entry, forceInvalidation, isChange, stats);
result = await this.processAndCacheFile(entry, forceInvalidation, isChange, stats);
} else {
stats.linked++;
if (isChange) {
this.output.unlinkSync(outputPath);
}
this.output.symlinkOrCopySync(srcPath, outputPath);
let fileMeta = this.input.readFileMeta(relativePath);
if (fileMeta) {
this.output.symlinkOrCopySync(fileMeta.path, outputPath);
}
result = undefined;
this._outputLinks[outputPath] = true;
}
Expand Down Expand Up @@ -442,21 +443,27 @@ abstract class Filter extends Plugin {
return null;
}

async processAndCacheFile(srcDir: string, destDir: string, entry: Entry, forceInvalidation: boolean, isChange: boolean, instrumentation: ApplyPatchesSchema): Promise<string | ProcessResult | undefined> {
async processAndCacheFile(entry: Entry, forceInvalidation: boolean, isChange: boolean, instrumentation: ApplyPatchesSchema): Promise<string | ProcessResult | undefined> {
let filter = this;
let relativePath = entry.relativePath;
try {
return await filter.processFile(srcDir, destDir, relativePath, forceInvalidation, isChange, instrumentation, entry);
return await filter.processFile(relativePath, forceInvalidation, isChange, instrumentation, entry);
} catch (e) {
let error = e;
let fileMeta = this.input.readFileMeta(relativePath);
if (typeof e !== 'object') error = new Error('' + e);
error.file = relativePath;
error.treeDir = srcDir;
let treeDir = relativePath;
if (fileMeta) {
let rootRelativeToFilePath = fileMeta.path.replace(relativePath, ''); // remo
treeDir = rootRelativeToFilePath.replace(/\/$/, ''); // remove trailing slash
}
error.treeDir = treeDir;
throw error;
}
}

async processFile(_srcDir: string, _destDir: string, relativePath: string, forceInvalidation: boolean, isChange: boolean, instrumentation: ApplyPatchesSchema, entry: Entry): Promise<string | ProcessResult | undefined> {
async processFile(relativePath: string, forceInvalidation: boolean, isChange: boolean, instrumentation: ApplyPatchesSchema, entry: Entry): Promise<string | ProcessResult | undefined> {
let filter = this;
let inputEncoding = this.inputEncoding;
let outputEncoding = this.outputEncoding;
Expand Down
100 changes: 100 additions & 0 deletions test/filter-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,106 @@ describe('Filter', function() {

});
});

describe('accepts array of inputs', function() {
class TestFilter extends Filter {
processString(content) {
return content.replace(/broccoli/gi, `filter`);
}
}

let input, output;

beforeEach(async function() {
input = await createTempDir();
});

afterEach(async function() {
await input.dispose();
output && await output.dispose();
});

it ('input node array of strings', async function() {
input.write({
'example': {
'map.js': 'let broccoli = 0;',
},
'docs': {
'README.md': 'broccoli',
}
});

let subject = new TestFilter([path.join(input.path(), 'docs'), path.join(input.path(), 'example')]);
output = createBuilder(subject);
await output.build();
expect(output.readText('map.js')).to.equal('let filter = 0;');
expect(output.readText('README.md')).to.equal('filter');
});

it ('input node can be broccoli node', async function() {
input.write({
'example': {
'map.js': 'let broccoli = 0;\nbroccoli=1;',
},
'docs': {
'README.md': 'broccoli',
}
});

let subject = new TestFilter(new ReplaceFilter(input.path(), { glob: '**/*.js', search: '\n', replace: '\t' }));
output = createBuilder(subject);
await output.build();
expect(output.readText('example/map.js')).to.equal('let filter = 0;\tfilter=1;');
expect(output.readText('docs/README.md')).to.equal('filter');
});
it ('with extension', async function() {
input.write({
'example': {
'map.js': 'let broccoli = 0;',
},
'docs': {
'README.md': 'broccoli',
}
});

let subject = new TestFilter([path.join(input.path(), 'docs'), path.join(input.path(), 'example')], {
targetExtension: 'foo',
extensions: ['md']
});
output = createBuilder(subject);
await output.build();
expect(output.readText('map.js')).to.equal('let broccoli = 0;');
expect(output.readText('README.foo')).to.equal('filter');
});

it ('files with same name', async function() {
input.write({
'example': {
'map.js': 'let broccoli = 0;',
'test': {
'README.md': 'broccoli'
},
'change.md':'no change',
},
'docs': {
'README.md': 'broccoli',
'map.js': 'let test = 0;',
'change.md':'changed',
}
});

let subject = new TestFilter([path.join(input.path(), 'docs'), path.join(input.path(), 'example')], {
targetExtension: 'foo',
extensions: ['md']
});
output = createBuilder(subject);
await output.build();
expect(output.readText('map.js')).to.equal('let broccoli = 0;');
expect(output.readText('README.foo')).to.equal('filter');
expect(output.readText('test/README.foo')).to.equal('filter');
expect(output.readText('change.foo')).to.equal('no change');
});
});
SparshithNR marked this conversation as resolved.
Show resolved Hide resolved
});

describe('throttling', function() {
Expand Down