diff --git a/README.md b/README.md index 009184a..1131004 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,24 @@ Current options: * `es6.mixedImports`: allows for all dependencies to be fetched from a file that contains both CJS and ES6 imports. * Note: This will work for any file format that contains an ES6 import. * `css.url`: tells the CSS detective to include `url()` references to images, fonts, etc. +* `walker`: options passed directly to the underlying [node-source-walk](https://github.com/dependents/node-source-walk) instance. + + Example: + + ```js + precinct(content, { + walker: { + allowImportExportEverywhere: true + } + }); + + // Or supply a fully custom parser: + precinct(content, { + walker: { + parser: myCustomParser // any object with a .parse(src, opts) method + } + }); + ``` Finding non-JavaScript (ex: Sass and Stylus) dependencies: @@ -79,6 +97,7 @@ Supported options: * `includeCore`: (default: `true`) set to `false` to exclude core Node.js dependencies from the list of dependencies. * `fileSystem`: (default: `undefined`) set to an alternative `fs` implementation that will be used to read the file path. +* `walker`: (default: `undefined`) options forwarded to the underlying [node-source-walk](https://github.com/dependents/node-source-walk) instance - same as the top-level `walker` option. * You may also pass detective-specific configuration like you would to `precinct(content, options)`. ### CLI diff --git a/index.d.ts b/index.d.ts index 5a030a4..fac8509 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,11 +6,14 @@ export = precinct; * @param {String|Object} content - File's content or AST * @param {Object} [options] * @param {String} [options.type] - The type of content being passed in. Useful if you want to use a non-js detective + * @param {Object} [options.walker] - Options to pass to the underlying source walker (node-source-walk) * @return {String[]} */ declare function precinct(content: string | any, options?: { type?: string; + walker?: Record; }): string[]; + declare namespace precinct { /** * Returns the dependencies for the given file path @@ -19,10 +22,12 @@ declare namespace precinct { * @param {Object} [options] * @param {Boolean} [options.includeCore=true] - Whether or not to include core modules in the dependency list * @param {Object} [options.fileSystem=undefined] - An alternative fs implementation to use for reading the file path. + * @param {Object} [options.walker] - Options to pass to the underlying source walker (node-source-walk) * @return {String[]} */ function paperwork(filename: string, options?: { includeCore?: boolean; fileSystem?: any; + walker?: Record; }): string[]; } diff --git a/index.js b/index.js index 545ffa0..dc9e9df 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,7 @@ const debug = debuglog('precinct'); * @param {String|Object} content - File's content or AST * @param {Object} [options] * @param {String} [options.type] - The type of content being passed in. Useful if you want to use a non-js detective + * @param {Object} [options.walker] - Options to pass to the underlying source walker (node-source-walk) * @return {String[]} */ function precinct(content, options = {}) { @@ -35,7 +36,7 @@ function precinct(content, options = {}) { // We assume we're dealing with a JS file if (!options.type && typeof content !== 'object') { debug('we assume this is JS'); - const walker = new Walker(); + const walker = new Walker(options.walker); try { // Parse once and distribute the AST to all detectives diff --git a/test/fixtures/es6ImportInsideBlock.js b/test/fixtures/es6ImportInsideBlock.js new file mode 100644 index 0000000..81b5d24 --- /dev/null +++ b/test/fixtures/es6ImportInsideBlock.js @@ -0,0 +1,3 @@ +if (true) { + import lib from 'lib'; +} diff --git a/test/test.js b/test/test.js index 7969efe..db5f689 100644 --- a/test/test.js +++ b/test/test.js @@ -322,6 +322,67 @@ describe('node-precinct', () => { const expected = ['mystyles', 'styles2.styl', 'styles3.styl', 'styles4']; assert.deepEqual(result, expected); }); + + describe('walker options', () => { + it('finds imports inside blocks when allowImportExportEverywhere is enabled', async() => { + // By default babel disallows import/export outside the top level, so + // a file with an import inside an if-block yields no dependencies. + const fixture = await read('es6ImportInsideBlock.js'); + const withoutOption = precinct(fixture); + assert.equal(withoutOption.length, 0); + + // With allowImportExportEverywhere the same file is parsed correctly. + const withOption = precinct(fixture, { + walker: { + allowImportExportEverywhere: true + } + }); + assert.equal(withOption.includes('lib'), true); + assert.equal(withOption.length, 1); + }); + + it('accepts a custom parser via walker options', async() => { + const fixture = await read('commonjs.js'); + + // Parse the AST up-front - the custom parser below returns it directly + // without invoking Babel, demonstrating that any object with a parse() + // method can be supplied, not just @babel/parser. + const prebuiltAst = require('@babel/parser').parse(fixture, { + sourceType: 'module', + allowHashBang: true + }); + + let parseCallCount = 0; + const customParser = { + parse() { + parseCallCount++; + return prebuiltAst; + } + }; + + const result = precinct(fixture, { + walker: { + parser: customParser + } + }); + assert.equal(parseCallCount, 1); + assert.equal(result.includes('./a'), true); + }); + + it('passes walker options through paperwork', () => { + const fixture = path.join(__dirname, 'fixtures/es6ImportInsideBlock.js'); + const withoutOption = precinct.paperwork(fixture); + assert.equal(withoutOption.length, 0); + + const withOption = precinct.paperwork(fixture, { + walker: { + allowImportExportEverywhere: true + } + }); + assert.equal(withOption.includes('lib'), true); + assert.equal(withOption.length, 1); + }); + }); }); describe('paperwork', () => {