mirror of https://github.com/webpack/webpack.git
				
				
				
			Merge pull request #10017 from webpack/feat-getOptions-util-for-loader
feat: getOptions util for loader
This commit is contained in:
		
						commit
						bd08639607
					
				|  | @ -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, | ||||
|  | @ -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 | ||||
|  | @ -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 | ||||
|  | @ -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
 | ||||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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/ } | ||||
| ]; | ||||
|  | @ -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/ | ||||
| 	] | ||||
| ]; | ||||
|  | @ -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"); | ||||
| } | ||||
|  | @ -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}`; | ||||
| }; | ||||
|  | @ -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" | ||||
| } | ||||
|  | @ -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}`; | ||||
| }; | ||||
|  | @ -0,0 +1,10 @@ | |||
| { | ||||
|   "title": "Custom Loader Name configuration", | ||||
|   "additionalProperties": false, | ||||
|   "properties": { | ||||
|     "arg": { | ||||
|       "enum": [true] | ||||
|     } | ||||
|   }, | ||||
|   "type": "object" | ||||
| } | ||||
|  | @ -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}`; | ||||
| }; | ||||
|  | @ -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 | ||||
| 				} | ||||
| 			} | ||||
| 		] | ||||
| 	} | ||||
| }; | ||||
		Loading…
	
		Reference in New Issue