From 981a9b558c185eaba64d1a08e049777847f3106d Mon Sep 17 00:00:00 2001 From: Kristjan Broder Lund Date: Fri, 13 Dec 2019 14:31:55 +0000 Subject: [PATCH 1/2] feat: pass `loaderContext` as 2nd parameter resolves #46 --- src/index.js | 2 +- test/__snapshots__/loader.test.js.snap | 45 +++++++++++++++++++ test/fixtures/args.js | 5 +-- test/fixtures/dependencies-via-context.js | 11 +++++ .../error-emitted-with-dependencies.js | 14 ++++++ test/loader.test.js | 26 +++++++++++ 6 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/dependencies-via-context.js create mode 100644 test/fixtures/error-emitted-with-dependencies.js diff --git a/src/index.js b/src/index.js index 8d90c1c..113b6cb 100644 --- a/src/index.js +++ b/src/index.js @@ -93,7 +93,7 @@ export default function loader(content) { let result; try { - result = func(options); + result = func(options, this); } catch (error) { throw new Error(`Module "${this.resource}" throw error: ${error}`); } diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index c458b01..12eadab 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -1,5 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`loader should allow adding dependencies and contextDependencies via loader context: errors 1`] = `Array []`; + +exports[`loader should allow adding dependencies and contextDependencies via loader context: result 1`] = ` +"{ + \\"content\\": \\"module.exports = \\\\\\"hello world\\\\\\";\\", + \\"map\\": null, + \\"meta\\": null, + \\"dependencies\\": [ + \\"test/fixtures/dependencies-via-context.js\\", + \\"test/fixtures/args.js\\", + \\"test/fixtures/simple.js\\" + ], + \\"contextDependencies\\": [ + \\"test/fixtures\\" + ] +}" +`; + +exports[`loader should allow adding dependencies and contextDependencies via loader context: warnings 1`] = `Array []`; + exports[`loader should call the function with the loader options: errors 1`] = `Array []`; exports[`loader should call the function with the loader options: result 1`] = ` @@ -101,6 +121,31 @@ exports[`loader should has module.parent: result 1`] = ` exports[`loader should has module.parent: warnings 1`] = `Array []`; +exports[`loader should keep dependencies if errors are emitted: errors 1`] = ` +Array [ + "ModuleError: Module Error (from /src/index.js): +(Emitted value instead of an instance of Error) Error: Calling the function failed", +] +`; + +exports[`loader should keep dependencies if errors are emitted: result 1`] = ` +"{ + \\"content\\": \\"module.exports = \\\\\\"hello world\\\\\\";\\", + \\"map\\": null, + \\"meta\\": null, + \\"dependencies\\": [ + \\"test/fixtures/error-emitted-with-dependencies.js\\", + \\"test/fixtures/args.js\\", + \\"test/fixtures/simple.js\\" + ], + \\"contextDependencies\\": [ + \\"test/fixtures\\" + ] +}" +`; + +exports[`loader should keep dependencies if errors are emitted: warnings 1`] = `Array []`; + exports[`loader should not swallow function call errors (async): errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from /src/index.js): diff --git a/test/fixtures/args.js b/test/fixtures/args.js index 3fc7907..608fdab 100644 --- a/test/fixtures/args.js +++ b/test/fixtures/args.js @@ -1,9 +1,8 @@ -function args(...args) { +function args(options) { return { code: 'module.exports = "hello world";', - // We can't use rest parameters here because this code is not touched by babel // We use the ast property because it is not validated - ast: args, // eslint-disable-line prefer-rest-params + ast: [options], }; } diff --git a/test/fixtures/dependencies-via-context.js b/test/fixtures/dependencies-via-context.js new file mode 100644 index 0000000..c567a56 --- /dev/null +++ b/test/fixtures/dependencies-via-context.js @@ -0,0 +1,11 @@ +function dependencies(options, loaderContext) { + loaderContext.addDependency(require.resolve('./args.js')); + loaderContext.addDependency(require.resolve('./simple.js')); + loaderContext.addContextDependency(__dirname); + + return { + code: 'module.exports = "hello world";', + }; +} + +module.exports = dependencies; diff --git a/test/fixtures/error-emitted-with-dependencies.js b/test/fixtures/error-emitted-with-dependencies.js new file mode 100644 index 0000000..8fdba74 --- /dev/null +++ b/test/fixtures/error-emitted-with-dependencies.js @@ -0,0 +1,14 @@ +function errorEmittedWithDependencies(options, loaderOptions) { + loaderOptions.emitError(new Error('Calling the function failed')); + + return { + dependencies: [ + require.resolve('./args.js'), + require.resolve('./simple.js'), + ], + contextDependencies: [__dirname], + code: 'module.exports = "hello world";', + }; +} + +module.exports = errorEmittedWithDependencies; diff --git a/test/loader.test.js b/test/loader.test.js index f2576bb..a64af64 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -119,6 +119,19 @@ describe('loader', () => { expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors'); }); + it('should allow adding dependencies and contextDependencies via loader context', async () => { + const compiler = getCompiler('dependencies-via-context.js'); + const stats = await compile(compiler); + + expect(readAsset('val-loader.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot( + 'warnings' + ); + expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors'); + }); + it('should work the same if a promise is returned', async () => { const compiler = getCompiler('promise.js'); const stats = await compile(compiler); @@ -171,6 +184,19 @@ describe('loader', () => { expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors'); }); + it('should keep dependencies if errors are emitted', async () => { + const compiler = getCompiler('error-emitted-with-dependencies.js'); + const stats = await compile(compiler); + + expect(readAsset('val-loader.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot( + 'warnings' + ); + expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors'); + }); + it('should report require() errors with a useful stacktrace', async () => { const compiler = getCompiler('error-require.js'); const stats = await compile(compiler); From a14d96438357f479f76850b403197932fee1feae Mon Sep 17 00:00:00 2001 From: Kristjan Broder Lund Date: Fri, 13 Dec 2019 15:29:17 +0000 Subject: [PATCH 2/2] docs: describe parameters passed to the target module --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8be3db6..efaca0b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ the loader changes a module from code to a result. Another way to view `val-loader`, is that it allows a user a way to make their own custom loader logic, without having to write a custom loader. +The target module is called with two arguments: `(options, loaderContext)` + +- `options`: The loader options (for instance provided in the webpack config. See the [example](#examples) below). +- `loaderContext`: [The loader context](https://webpack.js.org/api/loaders/#the-loader-context). + ## Getting Started To begin, you'll need to install `val-loader`: @@ -34,7 +39,7 @@ Then add the loader to your `webpack` config. For example: **target-file.js** ```js -module.exports = () => { +module.exports = (options, loaderContext) => { return { code: 'module.exports = 42;' }; }; ``` @@ -104,6 +109,8 @@ Default: `[]` An array of absolute, native paths to file dependencies that should be watched by webpack for changes. +Dependencies can also be added using [`loaderContext.addDependency(file: string)`](https://webpack.js.org/api/loaders/#thisadddependency). + ### `contextDependencies` Type: `Array[String]` @@ -112,6 +119,8 @@ Default: `[]` An array of absolute, native paths to directory dependencies that should be watched by webpack for changes. +Context dependencies can also be added using [`loaderContext.addContextDependency(directory: string)`](https://webpack.js.org/api/loaders/#thisaddcontextdependency). + ### `cacheable` Type: `Boolean`