diff --git a/README.md b/README.md index 52d326a..0ae3627 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,71 @@ const answer = require("target-file"); And run `webpack` via your preferred method. +## Options + +| Name | Type | Default | Description | +| :-------------------------------------: | :--------: | :---------: | :-------------------------------------------- | +| **[`executableFile`](#executableFile)** | `{String}` | `undefined` | Allows to specify path to the executable file | + +### executableFile + +Type: `String` +Default: `undefined` + +Allows to specify path to the executable file + +**data.json** + +```json +{ + "years": "10" +} +``` + +**executable-file.js** + +```js +module.exports = function yearsInMs(options, loaderContext, content) { + const { years } = JSON.parse(content); + const value = years * 365 * 24 * 60 * 60 * 1000; + + return { + cacheable: true, + code: "module.exports = " + value, + }; +}; +``` + +**webpack.config.js** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.(json)$/i, + rules: [ + { + loader: "val-loader", + options: { + executableFile: path.resolve( + __dirname, + "fixtures", + "executableFile.js" + ), + }, + }, + ], + }, + { + test: /\.json$/i, + type: "asset/resource", + }, + ], + }, +}; +``` + ## Return Object Properties Targeted modules of this loader must export a `Function` that returns an object, diff --git a/src/index.js b/src/index.js index bd9d222..546e395 100644 --- a/src/index.js +++ b/src/index.js @@ -69,34 +69,49 @@ function processResult(loaderContext, result) { export default function loader(content) { const options = this.getOptions(schema); + const { executableFile } = options; + const callback = this.async(); let exports; - try { - exports = exec(content, this); - } catch (error) { - throw new Error(`Unable to execute "${this.resource}": ${error}`); + if (executableFile) { + try { + // eslint-disable-next-line global-require,import/no-dynamic-require + exports = require(executableFile); + } catch (error) { + callback(new Error(`Unable to require "${executableFile}": ${error}`)); + return; + } + } else { + try { + exports = exec(content, this); + } catch (error) { + callback(new Error(`Unable to execute "${this.resource}": ${error}`)); + return; + } } const func = exports && exports.default ? exports.default : exports; if (typeof func !== "function") { - throw new Error( - `Module "${this.resource}" does not export a function as default` + callback( + new Error( + `Module "${this.resource}" does not export a function as default` + ) ); + return; } let result; try { - result = func(options, this); + result = func(options, this, content); } catch (error) { - throw new Error(`Module "${this.resource}" throw error: ${error}`); + callback(new Error(`Module "${this.resource}" throw error: ${error}`)); + return; } if (result && typeof result.then === "function") { - const callback = this.async(); - result .then((res) => processResult(this, res)) .catch((error) => { diff --git a/test/__snapshots__/executableFile.test.js.snap b/test/__snapshots__/executableFile.test.js.snap new file mode 100644 index 0000000..446311f --- /dev/null +++ b/test/__snapshots__/executableFile.test.js.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`executableFile option should emit error: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +Error: Unable to require \\"/test/fixtures/error-require.js\\": Error: This is a typical require() error", + "ChunkRenderError: Cannot read property 'get' of undefined", +] +`; + +exports[`executableFile option should emit error: warnings 1`] = `Array []`; + +exports[`executableFile option should work: errors 1`] = `Array []`; + +exports[`executableFile option should work: result 1`] = ` +"{ + \\"content\\": \\"module.exports = 315360000000\\", + \\"map\\": null, + \\"meta\\": null, + \\"dependencies\\": [ + \\"test/fixtures/data.json\\" + ], + \\"contextDependencies\\": [], + \\"buildDependencies\\": [] +}" +`; + +exports[`executableFile option should work: warnings 1`] = `Array []`; diff --git a/test/executableFile.test.js b/test/executableFile.test.js new file mode 100644 index 0000000..caf085a --- /dev/null +++ b/test/executableFile.test.js @@ -0,0 +1,90 @@ +import path from "path"; + +import { getCompiler, compile, readAsset, normalizeErrors } from "./helpers"; + +describe("executableFile option", () => { + it("should work", async () => { + const compiler = getCompiler( + "executableFileEntry.js", + {}, + { + module: { + rules: [ + { + test: /\.(json)$/i, + rules: [ + { + loader: require.resolve("./helpers/helperLoader.js"), + }, + { + loader: require.resolve("../src"), + options: { + executableFile: path.resolve( + __dirname, + "fixtures", + "executableFile.js" + ), + }, + }, + ], + }, + { + test: /\.json$/i, + type: "asset/resource", + }, + ], + }, + } + ); + 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 emit error", async () => { + const compiler = getCompiler( + "executableFileEntry.js", + {}, + { + module: { + rules: [ + { + test: /\.(json)$/i, + rules: [ + { + loader: require.resolve("./helpers/helperLoader.js"), + }, + { + loader: require.resolve("../src"), + options: { + executableFile: path.resolve( + __dirname, + "fixtures", + "error-require.js" + ), + }, + }, + ], + }, + { + test: /\.json$/i, + type: "asset/resource", + }, + ], + }, + } + ); + const stats = await compile(compiler); + + expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot( + "warnings" + ); + expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot("errors"); + }); +}); diff --git a/test/fixtures/data.json b/test/fixtures/data.json new file mode 100644 index 0000000..ec9e1d8 --- /dev/null +++ b/test/fixtures/data.json @@ -0,0 +1,3 @@ +{ + "years": "10" +} diff --git a/test/fixtures/executableFile.js b/test/fixtures/executableFile.js new file mode 100644 index 0000000..2257cf8 --- /dev/null +++ b/test/fixtures/executableFile.js @@ -0,0 +1,9 @@ +module.exports = function yearsInMs(options, loaderContext, content) { + const {years} = JSON.parse(content); + const value = years * 365 * 24 * 60 * 60 * 1000; + + return { + cacheable: true, + code: "module.exports = " + value, + }; +}; diff --git a/test/fixtures/executableFileEntry.js b/test/fixtures/executableFileEntry.js new file mode 100644 index 0000000..2a13db3 --- /dev/null +++ b/test/fixtures/executableFileEntry.js @@ -0,0 +1,3 @@ +const data = require('./data.json'); + +module.exports = {data};