Merge pull request #9548 from webpack/refactor-migrate-on-schema-utils

Refactor migrate on schema utils
This commit is contained in:
Tobias Koppers 2019-08-08 13:42:52 +02:00 committed by GitHub
commit 24ef835375
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 338 additions and 702 deletions

View File

@ -4,10 +4,9 @@
* Run `yarn special-lint-fix` to update
*/
/**
* A list of RegExps or absolute paths to directories or files that should be ignored
*/
export type WatchIgnorePluginOptions = [
(string | RegExp),
...(string | RegExp)[]
];
export interface WatchIgnorePluginOptions {
/**
* A list of RegExps or absolute paths to directories or files that should be ignored
*/
paths: [(string | RegExp), ...(string | RegExp)[]];
}

View File

@ -6,7 +6,7 @@
export interface ProfilingPluginOptions {
/**
* Path to the output file e.g. `profiling/events.json`. Defaults to `events.json`.
* Path to the output file e.g. `path.resolve(__dirname, 'profiling/events.json')`. Defaults to `events.json`.
*/
outputPath?: string;
}

View File

@ -11,7 +11,6 @@ module.exports = {
},
plugins: [
new webpack.DllReferencePlugin({
context: ".",
manifest: require("../0-vendor/dist/vendor-manifest.json") // eslint-disable-line
})
]

View File

@ -31,7 +31,10 @@ class BannerPlugin {
* @param {BannerPluginArgument} options options object
*/
constructor(options) {
validateOptions(schema, options, "Banner Plugin");
validateOptions(schema, options, {
name: "Banner Plugin",
baseDataPath: "options"
});
if (typeof options === "string" || typeof options === "function") {
options = {

View File

@ -19,7 +19,10 @@ class DllPlugin {
* @param {DllPluginOptions} options options object
*/
constructor(options) {
validateOptions(schema, options, "Dll Plugin");
validateOptions(schema, options, {
name: "Dll Plugin",
baseDataPath: "options"
});
this.options = options;
}

View File

@ -23,7 +23,10 @@ class DllReferencePlugin {
* @param {DllReferencePluginOptions} options options object
*/
constructor(options) {
validateOptions(schema, options, "Dll Reference Plugin");
validateOptions(schema, options, {
name: "Dll Reference Plugin",
baseDataPath: "options"
});
this.options = options;
/** @type {WeakMap<Object, {path: string, data: DllReferencePluginOptionsManifest?, error: Error?}>} */
this._compilationData = new WeakMap();

View File

@ -17,7 +17,10 @@ class IgnorePlugin {
* @param {IgnorePluginOptions} options IgnorePlugin options
*/
constructor(options) {
validateOptions(schema, options, "IgnorePlugin");
validateOptions(schema, options, {
name: "Ignore Plugin",
baseDataPath: "options"
});
this.options = options;
/** @private @type {Function} */

View File

@ -18,9 +18,11 @@ class LoaderOptionsPlugin {
/**
* @param {LoaderOptionsPluginOptions} options options object
*/
constructor(options) {
validateOptions(schema, options || {}, "Loader Options Plugin");
constructor(options = {}) {
validateOptions(schema, options, {
name: "Loader Options Plugin",
baseDataPath: "options"
});
if (typeof options !== "object") options = {};
if (!options.test) {
options.test = {

View File

@ -81,7 +81,10 @@ class ProgressPlugin {
}
options = options || {};
validateOptions(schema, options, "Progress Plugin");
validateOptions(schema, options || {}, {
name: "Progress Plugin",
baseDataPath: "options"
});
options = { ...ProgressPlugin.defaultOptions, ...options };
this.profile = options.profile;

View File

@ -82,7 +82,10 @@ class SourceMapDevToolPlugin {
* @throws {Error} throws error, if got more than 1 arguments
*/
constructor(options = {}) {
validateOptions(schema, options, "SourceMap DevTool Plugin");
validateOptions(schema, options || {}, {
name: "SourceMap DevTool Plugin",
baseDataPath: "options"
});
/** @type {string | false} */
this.sourceMapFilename = options.filename;

View File

@ -72,11 +72,14 @@ class IgnoringWatchFileSystem {
class WatchIgnorePlugin {
/**
* @param {WatchIgnorePluginOptions} paths list of paths
* @param {WatchIgnorePluginOptions} options options
*/
constructor(paths) {
validateOptions(schema, paths, "Watch Ignore Plugin");
this.paths = paths;
constructor(options) {
validateOptions(schema, options, {
name: "Watch Ignore Plugin",
baseDataPath: "options"
});
this.paths = options.paths;
}
/**

View File

@ -1,381 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Gajus Kuizinas @gajus
*/
"use strict";
const webpackOptionsSchema = require("../schemas/WebpackOptions.json");
const WebpackError = require("./WebpackError");
const getSchemaPart = (path, parents, additionalPath) => {
parents = parents || 0;
path = path.split("/", path.length - parents);
if (additionalPath) {
additionalPath = additionalPath.split("/");
path = path.concat(additionalPath);
}
let schemaPart = webpackOptionsSchema;
for (let i = 1; i < path.length; i++) {
const inner = schemaPart[path[i]];
if (inner) schemaPart = inner;
}
return schemaPart;
};
const getSchemaPartText = (schemaPart, additionalPath) => {
if (additionalPath) {
for (let i = 0; i < additionalPath.length; i++) {
const inner = schemaPart[additionalPath[i]];
if (inner) schemaPart = inner;
}
}
while (schemaPart.$ref) {
schemaPart = getSchemaPart(schemaPart.$ref);
}
let schemaText = WebpackOptionsValidationError.formatSchema(schemaPart);
if (schemaPart.description) {
schemaText += `\n-> ${schemaPart.description}`;
}
return schemaText;
};
const getSchemaPartDescription = schemaPart => {
while (schemaPart.$ref) {
schemaPart = getSchemaPart(schemaPart.$ref);
}
if (schemaPart.description) {
return `\n-> ${schemaPart.description}`;
}
return "";
};
const SPECIFICITY = {
type: 1,
oneOf: 1,
anyOf: 1,
allOf: 1,
additionalProperties: 2,
enum: 1,
instanceof: 1,
required: 2,
minimum: 2,
uniqueItems: 2,
minLength: 2,
minItems: 2,
minProperties: 2,
absolutePath: 2
};
const filterMax = (array, fn) => {
const max = array.reduce((max, item) => Math.max(max, fn(item)), 0);
return array.filter(item => fn(item) === max);
};
const filterChildren = children => {
children = filterMax(children, err =>
err.dataPath ? err.dataPath.length : 0
);
children = filterMax(children, err => SPECIFICITY[err.keyword] || 2);
return children;
};
const indent = (str, prefix, firstLine) => {
if (firstLine) {
return prefix + str.replace(/\n(?!$)/g, "\n" + prefix);
} else {
return str.replace(/\n(?!$)/g, `\n${prefix}`);
}
};
class WebpackOptionsValidationError extends WebpackError {
constructor(validationErrors) {
super(
"Invalid configuration object. " +
"Webpack has been initialised using a configuration object that does not match the API schema.\n" +
validationErrors
.map(
err =>
" - " +
indent(
WebpackOptionsValidationError.formatValidationError(err),
" ",
false
)
)
.join("\n")
);
this.name = "WebpackOptionsValidationError";
this.validationErrors = validationErrors;
Error.captureStackTrace(this, this.constructor);
}
static formatSchema(schema, prevSchemas) {
prevSchemas = prevSchemas || [];
const formatInnerSchema = (innerSchema, addSelf) => {
if (!addSelf) {
return WebpackOptionsValidationError.formatSchema(
innerSchema,
prevSchemas
);
}
if (prevSchemas.includes(innerSchema)) {
return "(recursive)";
}
return WebpackOptionsValidationError.formatSchema(
innerSchema,
prevSchemas.concat(schema)
);
};
if (schema.type === "string") {
if (schema.minLength === 1) {
return "non-empty string";
}
if (schema.minLength > 1) {
return `string (min length ${schema.minLength})`;
}
return "string";
}
if (schema.type === "boolean") {
return "boolean";
}
if (schema.type === "number") {
return "number";
}
if (schema.type === "object") {
if (schema.properties) {
const required = schema.required || [];
return `object { ${Object.keys(schema.properties)
.map(property => {
if (!required.includes(property)) return property + "?";
return property;
})
.concat(schema.additionalProperties ? ["…"] : [])
.join(", ")} }`;
}
if (schema.additionalProperties) {
return `object { <key>: ${formatInnerSchema(
schema.additionalProperties
)} }`;
}
return "object";
}
if (schema.type === "array") {
return `[${formatInnerSchema(schema.items)}]`;
}
switch (schema.instanceof) {
case "Function":
return "function";
case "RegExp":
return "RegExp";
}
if (schema.enum) {
return schema.enum.map(item => JSON.stringify(item)).join(" | ");
}
if (schema.$ref) {
return formatInnerSchema(getSchemaPart(schema.$ref), true);
}
if (schema.allOf) {
return schema.allOf.map(formatInnerSchema).join(" & ");
}
if (schema.oneOf) {
return schema.oneOf.map(formatInnerSchema).join(" | ");
}
if (schema.anyOf) {
return schema.anyOf.map(formatInnerSchema).join(" | ");
}
return JSON.stringify(schema, null, 2);
}
static formatValidationError(err) {
const dataPath = `configuration${err.dataPath}`;
if (err.keyword === "additionalProperties") {
const baseMessage = `${dataPath} has an unknown property '${
err.params.additionalProperty
}'. These properties are valid:\n${getSchemaPartText(err.parentSchema)}`;
if (!err.dataPath) {
switch (err.params.additionalProperty) {
case "debug":
return (
`${baseMessage}\n` +
"The 'debug' property was removed in webpack 2.0.0.\n" +
"Loaders should be updated to allow passing this option via loader options in module.rules.\n" +
"Until loaders are updated one can use the LoaderOptionsPlugin to switch loaders into debug mode:\n" +
"plugins: [\n" +
" new webpack.LoaderOptionsPlugin({\n" +
" debug: true\n" +
" })\n" +
"]"
);
}
return (
`${baseMessage}\n` +
"For typos: please correct them.\n" +
"For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.\n" +
" Loaders should be updated to allow passing options via loader options in module.rules.\n" +
" Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:\n" +
" plugins: [\n" +
" new webpack.LoaderOptionsPlugin({\n" +
" // test: /\\.xxx$/, // may apply this only for some modules\n" +
" options: {\n" +
` ${err.params.additionalProperty}: …\n` +
" }\n" +
" })\n" +
" ]"
);
}
return baseMessage;
} else if (err.keyword === "oneOf" || err.keyword === "anyOf") {
if (err.children && err.children.length > 0) {
if (err.schema.length === 1) {
const lastChild = err.children[err.children.length - 1];
const remainingChildren = err.children.slice(
0,
err.children.length - 1
);
return WebpackOptionsValidationError.formatValidationError({
...lastChild,
children: remainingChildren,
parentSchema: { ...err.parentSchema, ...lastChild.parentSchema }
});
}
const children = filterChildren(err.children);
if (children.length === 1) {
return WebpackOptionsValidationError.formatValidationError(
children[0]
);
}
return (
`${dataPath} should be one of these:\n${getSchemaPartText(
err.parentSchema
)}\n` +
`Details:\n${children
.map(
err =>
" * " +
indent(
WebpackOptionsValidationError.formatValidationError(err),
" ",
false
)
)
.join("\n")}`
);
}
return `${dataPath} should be one of these:\n${getSchemaPartText(
err.parentSchema
)}`;
} else if (err.keyword === "enum") {
if (
err.parentSchema &&
err.parentSchema.enum &&
err.parentSchema.enum.length === 1
) {
return `${dataPath} should be ${getSchemaPartText(err.parentSchema)}`;
}
return `${dataPath} should be one of these:\n${getSchemaPartText(
err.parentSchema
)}`;
} else if (err.keyword === "allOf") {
return `${dataPath} should be:\n${getSchemaPartText(err.parentSchema)}`;
} else if (err.keyword === "type") {
switch (err.params.type) {
case "object":
return `${dataPath} should be an object.${getSchemaPartDescription(
err.parentSchema
)}`;
case "string":
return `${dataPath} should be a string.${getSchemaPartDescription(
err.parentSchema
)}`;
case "boolean":
return `${dataPath} should be a boolean.${getSchemaPartDescription(
err.parentSchema
)}`;
case "number":
return `${dataPath} should be a number.${getSchemaPartDescription(
err.parentSchema
)}`;
case "array":
return `${dataPath} should be an array:\n${getSchemaPartText(
err.parentSchema
)}`;
}
return `${dataPath} should be ${err.params.type}:\n${getSchemaPartText(
err.parentSchema
)}`;
} else if (err.keyword === "instanceof") {
return `${dataPath} should be an instance of ${getSchemaPartText(
err.parentSchema
)}`;
} else if (err.keyword === "required") {
const missingProperty = err.params.missingProperty.replace(/^\./, "");
return `${dataPath} misses the property '${missingProperty}'.\n${getSchemaPartText(
err.parentSchema,
["properties", missingProperty]
)}`;
} else if (err.keyword === "minimum") {
return `${dataPath} ${err.message}.${getSchemaPartDescription(
err.parentSchema
)}`;
} else if (err.keyword === "uniqueItems") {
return `${dataPath} should not contain the item '${
err.data[err.params.i]
}' twice.${getSchemaPartDescription(err.parentSchema)}`;
} else if (
err.keyword === "minLength" ||
err.keyword === "minItems" ||
err.keyword === "minProperties"
) {
if (err.params.limit === 1) {
switch (err.keyword) {
case "minLength":
return `${dataPath} should be an non-empty string.${getSchemaPartDescription(
err.parentSchema
)}`;
case "minItems":
return `${dataPath} should be an non-empty array.${getSchemaPartDescription(
err.parentSchema
)}`;
case "minProperties":
return `${dataPath} should be an non-empty object.${getSchemaPartDescription(
err.parentSchema
)}`;
}
return `${dataPath} should be not empty.${getSchemaPartDescription(
err.parentSchema
)}`;
} else {
return `${dataPath} ${err.message}${getSchemaPartDescription(
err.parentSchema
)}`;
}
} else if (err.keyword === "absolutePath") {
const baseMessage = `${dataPath}: ${
err.message
}${getSchemaPartDescription(err.parentSchema)}`;
if (dataPath === "configuration.output.filename") {
return (
`${baseMessage}\n` +
"Please use output.path to specify absolute path and output.filename for the file name."
);
}
return baseMessage;
} else {
return `${dataPath} ${err.message} (${JSON.stringify(
err,
null,
2
)}).\n${getSchemaPartText(err.parentSchema)}`;
}
}
}
module.exports = WebpackOptionsValidationError;

View File

@ -161,12 +161,14 @@ const pluginName = "ProfilingPlugin";
class ProfilingPlugin {
/**
* @param {ProfilingPluginOptions=} opts options object
* @param {ProfilingPluginOptions=} options options object
*/
constructor(opts) {
validateOptions(schema, opts || {}, "Profiling plugin");
opts = opts || {};
this.outputPath = opts.outputPath || "events.json";
constructor(options = {}) {
validateOptions(schema, options, {
name: "Profiling Plugin",
baseDataPath: "options"
});
this.outputPath = options.outputPath || "events.json";
}
apply(compiler) {

View File

@ -20,7 +20,10 @@ class HashedModuleIdsPlugin {
* @param {HashedModuleIdsPluginOptions=} options options object
*/
constructor(options = {}) {
validateOptions(schema, options || {}, "Hashed Module Ids Plugin");
validateOptions(schema, options, {
name: "Hashed Module Ids Plugin",
baseDataPath: "options"
});
/** @type {HashedModuleIdsPluginOptions} */
this.options = {

View File

@ -20,7 +20,10 @@ class OccurrenceChunkIdsPlugin {
* @param {OccurrenceChunkIdsPluginOptions=} options options object
*/
constructor(options = {}) {
validateOptions(schema, options, "Occurrence Order Chunk Ids Plugin");
validateOptions(schema, options, {
name: "Occurrence Order Chunk Ids Plugin",
baseDataPath: "options"
});
this.options = options;
}

View File

@ -22,7 +22,10 @@ class OccurrenceModuleIdsPlugin {
* @param {OccurrenceModuleIdsPluginOptions=} options options object
*/
constructor(options = {}) {
validateOptions(schema, options, "Occurrence Order Module Ids Plugin");
validateOptions(schema, options, {
name: "Occurrence Order Module Ids Plugin",
baseDataPath: "options"
});
this.options = options;
}

View File

@ -5,6 +5,7 @@
"use strict";
const validate = require("schema-utils");
const util = require("util");
const { version } = require("../package.json");
const webpackOptionsSchema = require("../schemas/WebpackOptions.json");
@ -12,14 +13,13 @@ const Compiler = require("./Compiler");
const MultiCompiler = require("./MultiCompiler");
const WebpackOptionsApply = require("./WebpackOptionsApply");
const WebpackOptionsDefaulter = require("./WebpackOptionsDefaulter");
const WebpackOptionsValidationError = require("./WebpackOptionsValidationError");
const validateSchema = require("./validateSchema");
const webpack = require("./webpack");
module.exports = webpack;
module.exports.WebpackOptionsApply = WebpackOptionsApply;
module.exports.WebpackOptionsDefaulter = WebpackOptionsDefaulter;
module.exports.WebpackOptionsValidationError = WebpackOptionsValidationError;
module.exports.WebpackOptionsValidationError = validate.ValidationError;
module.exports.Compiler = Compiler;
module.exports.MultiCompiler = MultiCompiler;
module.exports.validate = validateSchema.bind(null, webpackOptionsSchema);

View File

@ -47,7 +47,10 @@ class AggressiveSplittingPlugin {
* @param {AggressiveSplittingPluginOptions=} options options object
*/
constructor(options = {}) {
validateOptions(schema, options, "Aggressive Splitting Plugin");
validateOptions(schema, options, {
name: "Aggressive Splitting Plugin",
baseDataPath: "options"
});
this.options = options;
if (typeof this.options.minSize !== "number") {

View File

@ -20,7 +20,10 @@ class LimitChunkCountPlugin {
* @param {LimitChunkCountPluginOptions=} options options object
*/
constructor(options = {}) {
validateOptions(schema, options, "Limit Chunk Count Plugin");
validateOptions(schema, options, {
name: "Limit Chunk Count Plugin",
baseDataPath: "options"
});
this.options = options;
}

View File

@ -19,7 +19,10 @@ class MinChunkSizePlugin {
* @param {MinChunkSizePluginOptions} options options object
*/
constructor(options) {
validateOptions(schema, options, "Min Chunk Size Plugin");
validateOptions(schema, options, {
name: "Min Chunk Size Plugin",
baseDataPath: "options"
});
this.options = options;
}

View File

@ -1,70 +1,63 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Gajus Kuizinas @gajus
Author Tobias Koppers @sokra
*/
"use strict";
const Ajv = require("ajv");
const ajv = new Ajv({
errorDataPath: "configuration",
allErrors: true,
verbose: true
});
require("ajv-keywords")(ajv, ["instanceof"]);
require("../schemas/ajv.absolutePath")(ajv);
const validate = require("schema-utils");
const validateSchema = (schema, options) => {
if (Array.isArray(options)) {
const errors = Array.from(options).map(options =>
validateObject(schema, options)
);
errors.forEach((list, idx) => {
const applyPrefix = err => {
err.dataPath = `[${idx}]${err.dataPath}`;
if (err.children) {
err.children.forEach(applyPrefix);
}
};
list.forEach(applyPrefix);
});
return errors.reduce((arr, items) => {
return arr.concat(items);
}, []);
} else {
return validateObject(schema, options);
}
};
const validateObject = (schema, options) => {
const validate = ajv.compile(schema);
const valid = validate(options);
return valid ? [] : filterErrors(validate.errors);
};
const filterErrors = errors => {
let newErrors = [];
for (const err of errors) {
const dataPath = err.dataPath;
let children = [];
newErrors = newErrors.filter(oldError => {
if (oldError.dataPath.includes(dataPath)) {
if (oldError.children) {
children = children.concat(oldError.children.slice(0));
}
oldError.children = undefined;
children.push(oldError);
return false;
validate(schema, options, {
name: "Webpack",
postFormatter: (formattedError, error) => {
if (
error.children &&
error.children.some(
child =>
child.keyword === "absolutePath" &&
child.dataPath === ".output.filename"
)
) {
return `${formattedError}\nPlease use output.path to specify absolute path and output.filename for the file name.`;
}
return true;
});
if (children.length) {
err.children = children;
if (error.keyword === "additionalProperties" && !error.dataPath) {
if (error.params.additionalProperty === "debug") {
return (
`${formattedError}\n` +
"The 'debug' property was removed in webpack 2.0.0.\n" +
"Loaders should be updated to allow passing this option via loader options in module.rules.\n" +
"Until loaders are updated one can use the LoaderOptionsPlugin to switch loaders into debug mode:\n" +
"plugins: [\n" +
" new webpack.LoaderOptionsPlugin({\n" +
" debug: true\n" +
" })\n" +
"]"
);
}
if (error.params.additionalProperty) {
return (
`${formattedError}\n` +
"For typos: please correct them.\n" +
"For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.\n" +
" Loaders should be updated to allow passing options via loader options in module.rules.\n" +
" Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:\n" +
" plugins: [\n" +
" new webpack.LoaderOptionsPlugin({\n" +
" // test: /\\.xxx$/, // may apply this only for some modules\n" +
" options: {\n" +
` ${error.params.additionalProperty}: …\n` +
" }\n" +
" })\n" +
" ]"
);
}
}
return formattedError;
}
newErrors.push(err);
}
return newErrors;
});
};
module.exports = validateSchema;

View File

@ -10,7 +10,6 @@ const Compiler = require("./Compiler");
const MultiCompiler = require("./MultiCompiler");
const WebpackOptionsApply = require("./WebpackOptionsApply");
const WebpackOptionsDefaulter = require("./WebpackOptionsDefaulter");
const WebpackOptionsValidationError = require("./WebpackOptionsValidationError");
const NodeEnvironmentPlugin = require("./node/NodeEnvironmentPlugin");
const validateSchema = require("./validateSchema");
@ -76,10 +75,7 @@ const createCompiler = options => {
* @returns {Compiler | MultiCompiler} the compiler object
*/
const webpack = (options, callback) => {
const validationErrors = validateSchema(webpackOptionsSchema, options);
if (validationErrors.length) {
throw new WebpackOptionsValidationError(validationErrors);
}
validateSchema(webpackOptionsSchema, options);
/** @type {TODO} */
let compiler;
let watch = false;

View File

@ -10,8 +10,6 @@
"@webassemblyjs/wasm-edit": "1.8.5",
"@webassemblyjs/wasm-parser": "1.8.5",
"acorn": "^6.2.1",
"ajv": "^6.10.2",
"ajv-keywords": "^3.4.1",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "5.0.0-beta.2",
"eslint-scope": "^5.0.0",
@ -23,7 +21,7 @@
"loader-runner": "3.0.0",
"loader-utils": "^1.2.3",
"neo-async": "^2.6.1",
"schema-utils": "^1.0.0",
"schema-utils": "^2.1.0",
"tapable": "2.0.0-beta.8",
"terser-webpack-plugin": "^1.4.1",
"watchpack": "2.0.0-beta.7",

View File

@ -1,55 +0,0 @@
"use strict";
const errorMessage = (schema, data, message) => ({
keyword: "absolutePath",
params: { absolutePath: data },
message: message,
parentSchema: schema
});
const getErrorFor = (shouldBeAbsolute, data, schema) => {
const message = shouldBeAbsolute
? `The provided value ${JSON.stringify(data)} is not an absolute path!`
: `A relative path is expected. However, the provided value ${JSON.stringify(
data
)} is an absolute path!`;
return errorMessage(schema, data, message);
};
module.exports = ajv =>
ajv.addKeyword("absolutePath", {
errors: true,
type: "string",
compile(expected, schema) {
function callback(data) {
let passes = true;
const isExclamationMarkPresent = data.includes("!");
const isCorrectAbsoluteOrRelativePath =
expected === /^(?:[A-Za-z]:\\|\/)/.test(data);
if (isExclamationMarkPresent) {
callback.errors = [
errorMessage(
schema,
data,
`The provided value ${JSON.stringify(
data
)} contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.`
)
];
passes = false;
}
if (!isCorrectAbsoluteOrRelativePath) {
callback.errors = [getErrorFor(expected, data, schema)];
passes = false;
}
return passes;
}
callback.errors = [];
return callback;
}
});

View File

@ -1,18 +1,25 @@
{
"title": "WatchIgnorePluginOptions",
"description": "A list of RegExps or absolute paths to directories or files that should be ignored",
"type": "array",
"items": {
"description": "RegExp or absolute path to directories or files that should be ignored",
"oneOf": [
{
"type": "string"
"type": "object",
"additionalProperties": false,
"properties": {
"paths": {
"description": "A list of RegExps or absolute paths to directories or files that should be ignored",
"type": "array",
"items": {
"description": "RegExp or absolute path to directories or files that should be ignored",
"oneOf": [
{
"type": "string"
},
{
"instanceof": "RegExp",
"tsType": "RegExp"
}
]
},
{
"instanceof": "RegExp",
"tsType": "RegExp"
}
]
"minItems": 1
}
},
"minItems": 1
"required": ["paths"]
}

View File

@ -4,10 +4,9 @@
"additionalProperties": false,
"properties": {
"outputPath": {
"description": "Path to the output file e.g. `profiling/events.json`. Defaults to `events.json`.",
"description": "Path to the output file e.g. `path.resolve(__dirname, 'profiling/events.json')`. Defaults to `events.json`.",
"type": "string",
"absolutePath": false,
"minLength": 4
"absolutePath": true
}
}
}

View File

@ -1,13 +1,15 @@
"use strict";
const path = require("path");
const ProfilingPlugin = require("../lib/debug/ProfilingPlugin");
describe("Profiling Plugin", () => {
it("should persist the passed outpath", () => {
const outputPath = path.join(__dirname, "invest_in_doge_coin");
const plugin = new ProfilingPlugin({
outputPath: "invest_in_doge_coin"
outputPath: outputPath
});
expect(plugin.outputPath).toBe("invest_in_doge_coin");
expect(plugin.outputPath).toBe(outputPath);
});
it("should handle no options", () => {

View File

@ -8,7 +8,7 @@ describe("Validation", () => {
try {
webpack(config);
} catch (err) {
if (err.name !== "WebpackOptionsValidationError") throw err;
if (err.name !== "ValidationError") throw err;
expect(err.message).toMatch(/^Invalid configuration object./);
fn(err.message);
@ -22,16 +22,18 @@ describe("Validation", () => {
createTestCase("undefined configuration", undefined, msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration should be an object."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration should be an object:
object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }"
`)
);
createTestCase("null configuration", null, msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration should be an object."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration should be an object:
object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }"
`)
);
createTestCase(
@ -41,10 +43,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should be an non-empty string.
-> An entry point without name. The string is resolved to a module which is loaded upon startup."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should be an non-empty string.
-> An entry point without name. The string is resolved to a module which is loaded upon startup."
`)
);
createTestCase(
@ -56,10 +58,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry['bundle'] should be an non-empty array.
-> A non-empty array of non-empty strings"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry['bundle'] should be an non-empty array.
-> A non-empty array of non-empty strings"
`)
);
createTestCase(
@ -72,10 +74,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.wrappedContextRegExp should be an instance of RegExp
-> Set the inner regular expression for partial dynamic dependencies"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.wrappedContextRegExp should be an instance of RegExp
-> Set the inner regular expression for partial dynamic dependencies."
`)
);
createTestCase(
@ -86,10 +88,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.parallelism should be >= 1.
-> The number of parallel processed modules in the compilation."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.parallelism should be >= 1.
-> The number of parallel processed modules in the compilation."
`)
);
createTestCase(
@ -99,10 +101,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should not contain the item 'abc' twice.
-> A non-empty array of non-empty strings"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should not contain the item 'abc' twice.
-> A non-empty array of non-empty strings"
`)
);
createTestCase(
@ -115,16 +117,16 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry[0] should be a string.
-> A non-empty string
- configuration.output.filename should be one of these:
non-empty string | function
-> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The \`output.path\` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
Details:
* configuration.output.filename should be a string.
* configuration.output.filename should be an instance of function"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry[0] should be a non-empty string.
-> A non-empty string
- configuration.output.filename should be one of these:
non-empty string | function
-> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The \`output.path\` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
Details:
* configuration.output.filename should be a non-empty string.
* configuration.output.filename should be an instance of function."
`)
);
createTestCase(
@ -142,16 +144,16 @@ describe("Validation", () => {
],
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration[0].entry[0] should be a string.
-> A non-empty string
- configuration[1].output.filename should be one of these:
non-empty string | function
-> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The \`output.path\` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
Details:
* configuration[1].output.filename should be a string.
* configuration[1].output.filename should be an instance of function"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration[0].entry[0] should be a non-empty string.
-> A non-empty string
- configuration[1].output.filename should be one of these:
non-empty string | function
-> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The \`output.path\` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
Details:
* configuration[1].output.filename should be a non-empty string.
* configuration[1].output.filename should be an instance of function."
`)
);
createTestCase(
@ -175,11 +177,11 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.rules[0].oneOf[0] has an unknown property 'passer'. These properties are valid:
object { compiler?, enforce?, exclude?, include?, issuer?, loader?, oneOf?, options?, parser?, realResource?, resolve?, resource?, resourceQuery?, rules?, sideEffects?, test?, type?, use? }
-> A rule"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.rules[0].oneOf[0] has an unknown property 'passer'. These properties are valid:
object { compiler?, enforce?, exclude?, include?, issuer?, loader?, oneOf?, options?, parser?, realResource?, resolve?, resource?, resourceQuery?, rules?, sideEffects?, test?, type?, use? }
-> A rule"
`)
);
createTestCase(
@ -190,22 +192,22 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration has an unknown property 'postcss'. These properties are valid:
object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }
For typos: please correct them.
For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.
Loaders should be updated to allow passing options via loader options in module.rules.
Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
plugins: [
new webpack.LoaderOptionsPlugin({
// test: /\\\\.xxx$/, // may apply this only for some modules
options: {
postcss:
}
})
]"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration has an unknown property 'postcss'. These properties are valid:
object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }
For typos: please correct them.
For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.
Loaders should be updated to allow passing options via loader options in module.rules.
Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
plugins: [
new webpack.LoaderOptionsPlugin({
// test: /\\\\.xxx$/, // may apply this only for some modules
options: {
postcss:
}
})
]"
`)
);
createTestCase(
@ -216,14 +218,14 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.devtool should be one of these:
string | false
-> A developer tool to enhance debugging.
Details:
* configuration.devtool should be a string.
* configuration.devtool should be false"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.devtool should be one of these:
string | false
-> A developer tool to enhance debugging.
Details:
* configuration.devtool should be a string.
* configuration.devtool should be false."
`)
);
createTestCase(
@ -237,10 +239,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.output.path: The provided value \\"/somepath/!test\\" contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.
-> The output directory as **absolute path** (required)."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.output.path: The provided value \\"/somepath/!test\\" contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.
-> The output directory as **absolute path** (required)."
`)
);
createTestCase(
@ -253,10 +255,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.output.filename: A relative path is expected. However, the provided value \\"/bar\\" is an absolute path!
Please use output.path to specify absolute path and output.filename for the file name."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.output.filename: A relative path is expected. However, the provided value \\"/bar\\" is an absolute path!
Please use output.path to specify absolute path and output.filename for the file name."
`)
);
createTestCase(
@ -270,10 +272,10 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.context: The provided value \\"baz\\" is not an absolute path!
-> The base directory (absolute path!) for resolving the \`entry\` option. If \`output.pathinfo\` is set, the included pathinfo is shortened to this directory."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.context: The provided value \\"baz\\" is not an absolute path!
-> The base directory (absolute path!) for resolving the \`entry\` option. If \`output.pathinfo\` is set, the included pathinfo is shortened to this directory."
`)
);
createTestCase(
@ -290,10 +292,10 @@ describe("Validation", () => {
.replace(/object \{ .* \}/g, "object {...}")
.replace(/"none" \| .+/g, '"none" | ...')
).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.stats has an unknown property 'foobar'. These properties are valid:
object {...}"
`);
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.stats has an unknown property 'foobar'. These properties are valid:
object {...}"
`);
}
);
@ -305,16 +307,17 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object.
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object:
object { apply, }
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin."
`)
);
createTestCase(
@ -325,16 +328,17 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object.
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object:
object { apply, }
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin."
`)
);
createTestCase(
@ -345,16 +349,17 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object.
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object:
object { apply, }
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin."
`)
);
createTestCase(
@ -365,16 +370,17 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object.
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin"
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] should be an object:
object { apply, }
-> Plugin instance
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin."
`)
);
createTestCase(
@ -385,11 +391,11 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] misses the property 'apply'.
function
-> The run point of the plugin, required method."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] misses the property 'apply'. Should be:
function
-> The run point of the plugin, required method."
`)
);
createTestCase(
@ -399,11 +405,32 @@ describe("Validation", () => {
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.mode should be one of these:
\\"development\\" | \\"production\\" | \\"none\\"
-> Enable production optimizations or development hints."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.mode should be one of these:
\\"development\\" | \\"production\\" | \\"none\\"
-> Enable production optimizations or development hints."
`)
);
createTestCase(
"debug",
{
debug: true
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration has an unknown property 'debug'. These properties are valid:
object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }
The 'debug' property was removed in webpack 2.0.0.
Loaders should be updated to allow passing this option via loader options in module.rules.
Until loaders are updated one can use the LoaderOptionsPlugin to switch loaders into debug mode:
plugins: [
new webpack.LoaderOptionsPlugin({
debug: true
})
]"
`)
);
createTestCase(
@ -420,8 +447,9 @@ describe("Validation", () => {
],
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration[1] should be an object."
`)
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration[1] should be an object:
object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, experiments?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }"
`)
);
});

View File

@ -1,5 +1,5 @@
var webpack = require("../../../../");
module.exports = {
plugins: [new webpack.WatchIgnorePlugin([/file\.js$/, /foo$/])]
plugins: [new webpack.WatchIgnorePlugin({ paths: [/file\.js$/, /foo$/] })]
};

View File

@ -694,7 +694,7 @@ ajv-errors@^1.0.0:
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
ajv-keywords@^3.1.0:
version "3.4.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
@ -5627,6 +5627,14 @@ schema-utils@^2.0.0, schema-utils@^2.0.1:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
schema-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.1.0.tgz#940363b6b1ec407800a22951bdcc23363c039393"
integrity sha512-g6SViEZAfGNrToD82ZPUjq52KUPDYc+fN5+g6Euo5mLokl/9Yx14z0Cu4RR1m55HtBXejO0sBt+qw79axN+Fiw==
dependencies:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
script-loader@~0.7.0:
version "0.7.2"
resolved "https://registry.yarnpkg.com/script-loader/-/script-loader-0.7.2.tgz#2016db6f86f25f5cf56da38915d83378bb166ba7"