Skip to content

Commit

Permalink
Merge pull request #10017 from webpack/feat-getOptions-util-for-loader
Browse files Browse the repository at this point in the history
feat: getOptions util for loader
  • Loading branch information
sokra committed Jan 17, 2020
2 parents 6400fd4 + d673e41 commit bd08639
Show file tree
Hide file tree
Showing 21 changed files with 298 additions and 2 deletions.
52 changes: 50 additions & 2 deletions lib/NormalModule.js
Expand Up @@ -5,7 +5,10 @@

"use strict";

const parseJson = require("json-parse-better-errors");
const { getContext, runLoaders } = require("loader-runner");
const querystring = require("querystring");
const validateOptions = require("schema-utils");
const { SyncHook } = require("tapable");
const {
CachedSource,
Expand Down Expand Up @@ -49,6 +52,13 @@ const makeSerializable = require("./util/makeSerializable");
/** @typedef {import("./util/Hash")} Hash */
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */

/**
* @typedef {Object} LoaderItem
* @property {string} loader
* @property {any} options
* @property {string?} ident
*/

/**
* @param {string} context absolute context path
* @param {string} source a source path
Expand Down Expand Up @@ -166,7 +176,7 @@ class NormalModule extends Module {
* @param {string} options.request request string
* @param {string} options.userRequest request intented by user (without loaders from config)
* @param {string} options.rawRequest request without resolving
* @param {TODO[]} options.loaders list of loaders
* @param {LoaderItem[]} options.loaders list of loaders
* @param {string} options.resource path + query of the real resource
* @param {string | undefined} options.matchResource path + query of the matched resource (virtuel
* @param {Parser} options.parser the parser used
Expand Down Expand Up @@ -204,7 +214,7 @@ class NormalModule extends Module {
this.resource = resource;
/** @type {string | undefined} */
this.matchResource = matchResource;
/** @type {TODO[]} */
/** @type {LoaderItem[]} */
this.loaders = loaders;
if (resolveOptions !== undefined) {
// already declared in super class
Expand Down Expand Up @@ -350,6 +360,44 @@ class NormalModule extends Module {
};
const loaderContext = {
version: 2,
getOptions: schema => {
const loader = this.getCurrentLoader(loaderContext);

let { options } = loader;

if (typeof options === "string") {
if (options.substr(0, 1) === "{" && options.substr(-1) === "}") {
try {
options = parseJson(options);
} catch (e) {
throw new Error(`Cannot parse string options: ${e.message}`);
}
} else {
options = querystring.parse(options, "&", "=", {
maxKeys: 0
});
}
}

if (options === null || options === undefined) {
options = {};
}

if (schema) {
let name = "Loader";
let baseDataPath = "options";
let match;
if (schema.title && (match = /^(.+) (.+)$/.exec(schema.title))) {
[, name, baseDataPath] = match;
}
validateOptions(schema, options, {
name,
baseDataPath
});
}

return options;
},
emitWarning: warning => {
if (!(warning instanceof Error)) {
warning = new NonErrorEmittedError(warning);
Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
9 changes: 9 additions & 0 deletions test/configCases/loaders/options/deprecations.js
@@ -0,0 +1,9 @@
module.exports = [
{ code: /DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING/ },
{ code: /DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING/ },
{ code: /DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING/ },
{ code: /DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING/ },
{ code: /DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING/ },
{ code: /DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING/ },
{ code: /DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING/ }
];
Empty file.
Empty file.
Empty file.
12 changes: 12 additions & 0 deletions test/configCases/loaders/options/errors.js
@@ -0,0 +1,12 @@
module.exports = [
[
/\.\/loader-1\.js/,
/Loader has been/,
/options\.arg6\.bar\.baz should be a string/
],
[
/\.\/loader-2\.js/,
/Custom Loader Name has been/,
/configuration\.arg should be true/
]
];
Empty file.
Empty file.
Empty file.
Empty file.
51 changes: 51 additions & 0 deletions test/configCases/loaders/options/index.js
@@ -0,0 +1,51 @@
it("should get options", function() {
expect(require("./a")).toStrictEqual({
arg: true,
arg1: null,
arg3: 1234567890,
arg4: "string",
arg5: [1, 2, 3],
arg6: { foo: "value", bar: { baz: "other-value" } }
});
expect(require("./b")).toStrictEqual({
arg: true,
arg1: null,
arg3: 1234567890,
arg4: "string",
arg5: [1, 2, 3],
arg6: { foo: "value", bar: { baz: "other-value" } }
});
expect(require("./c")).toStrictEqual({
arg: true,
arg1: null,
arg3: 1234567890,
arg4: "string",
arg5: [1, 2, 3],
arg6: { foo: "value", bar: { baz: "other-value" } }
});
expect(require("./d")).toStrictEqual({
arg4: "text"
});
expect(require("./e")).toStrictEqual({});
expect(require("./f")).toStrictEqual({
delicious: "",
name: "cheesecake",
slices: "8",
warm: "false"
});
expect(require("./g")).toStrictEqual({
"=": "="
});
expect(require("./h")).toStrictEqual({
foo: "bar"
});
expect(require("./i")).toStrictEqual({
foo: "bar"
});
});

const never = false;
if (never) {
require("./error1");
require("./error2");
}
11 changes: 11 additions & 0 deletions test/configCases/loaders/options/loader-1.js
@@ -0,0 +1,11 @@
const schema = require("./loader-1.options");

module.exports = function() {
const options = this.getOptions(schema);

const json = JSON.stringify(options)
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");

return `module.exports = ${json}`;
};
43 changes: 43 additions & 0 deletions test/configCases/loaders/options/loader-1.options.json
@@ -0,0 +1,43 @@
{
"additionalProperties": false,
"properties": {
"arg": {
"type": "boolean"
},
"arg1": {
"type": "null"
},
"arg2": {},
"arg3": {
"type": "number"
},
"arg4": {
"type": "string"
},
"arg5": {
"type": "array",
"items": {
"type": "number"
}
},
"arg6": {
"type": "object",
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "object",
"properties": {
"baz": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"type": "object"
}
11 changes: 11 additions & 0 deletions test/configCases/loaders/options/loader-2.js
@@ -0,0 +1,11 @@
const schema = require("./loader-2.options");

module.exports = function() {
const options = this.getOptions(schema);

const json = JSON.stringify(options)
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");

return `module.exports = ${json}`;
};
10 changes: 10 additions & 0 deletions test/configCases/loaders/options/loader-2.options.json
@@ -0,0 +1,10 @@
{
"title": "Custom Loader Name configuration",
"additionalProperties": false,
"properties": {
"arg": {
"enum": [true]
}
},
"type": "object"
}
9 changes: 9 additions & 0 deletions test/configCases/loaders/options/loader.js
@@ -0,0 +1,9 @@
module.exports = function() {
const options = this.getOptions();

const json = JSON.stringify(options)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');

return `module.exports = ${json}`;
};
92 changes: 92 additions & 0 deletions test/configCases/loaders/options/webpack.config.js
@@ -0,0 +1,92 @@
module.exports = {
mode: "none",
module: {
rules: [
{
test: /a\.js$/,
loader: "./loader",
options: {
arg: true,
arg1: null,
arg2: undefined,
arg3: 1234567890,
arg4: "string",
arg5: [1, 2, 3],
arg6: { foo: "value", bar: { baz: "other-value" } }
}
},
{
test: /b\.js$/,
loader: "./loader-1",
options: {
arg: true,
arg1: null,
arg2: undefined,
arg3: 1234567890,
arg4: "string",
arg5: [1, 2, 3],
arg6: { foo: "value", bar: { baz: "other-value" } }
}
},
{
test: /c\.js$/,
loader: "./loader-1",
options: JSON.stringify({
arg: true,
arg1: null,
arg2: undefined,
arg3: 1234567890,
arg4: "string",
arg5: [1, 2, 3],
arg6: { foo: "value", bar: { baz: "other-value" } }
})
},
{
test: /d\.js$/,
loader: "./loader-1",
options: "arg4=text"
},
{
test: /d\.js$/,
loader: "./loader",
options: ""
},
{
test: /f\.js$/,
loader: "./loader",
options: "name=cheesecake&slices=8&delicious&warm=false"
},
{
test: /g\.js$/,
loader: "./loader",
options: "%3d=%3D"
},
{
test: /h\.js$/,
loader: "./loader",
options: "foo=bar"
},
{
test: /i\.js$/,
loader: "./loader",
options: `${JSON.stringify({
foo: "bar"
})}`
},
{
test: /error1\.js$/,
loader: "./loader-1",
options: {
arg6: { foo: "value", bar: { baz: 42 } }
}
},
{
test: /error2\.js$/,
loader: "./loader-2",
options: {
arg: false
}
}
]
}
};

0 comments on commit bd08639

Please sign in to comment.