diff --git a/declarations/plugins/WatchIgnorePlugin.d.ts b/declarations/plugins/WatchIgnorePlugin.d.ts index 34271d801..5b11c3cfe 100644 --- a/declarations/plugins/WatchIgnorePlugin.d.ts +++ b/declarations/plugins/WatchIgnorePlugin.d.ts @@ -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)[]]; +} diff --git a/declarations/plugins/debug/ProfilingPlugin.d.ts b/declarations/plugins/debug/ProfilingPlugin.d.ts index d3cd664ad..5e1404f57 100644 --- a/declarations/plugins/debug/ProfilingPlugin.d.ts +++ b/declarations/plugins/debug/ProfilingPlugin.d.ts @@ -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; } diff --git a/examples/dll-app-and-vendor/1-app/webpack.config.js b/examples/dll-app-and-vendor/1-app/webpack.config.js index 2f43dacef..1f3343592 100644 --- a/examples/dll-app-and-vendor/1-app/webpack.config.js +++ b/examples/dll-app-and-vendor/1-app/webpack.config.js @@ -11,7 +11,6 @@ module.exports = { }, plugins: [ new webpack.DllReferencePlugin({ - context: ".", manifest: require("../0-vendor/dist/vendor-manifest.json") // eslint-disable-line }) ] diff --git a/lib/BannerPlugin.js b/lib/BannerPlugin.js index dd48a2106..8bdb21e5c 100644 --- a/lib/BannerPlugin.js +++ b/lib/BannerPlugin.js @@ -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 = { diff --git a/lib/DllPlugin.js b/lib/DllPlugin.js index 15f68b976..3795af124 100644 --- a/lib/DllPlugin.js +++ b/lib/DllPlugin.js @@ -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; } diff --git a/lib/DllReferencePlugin.js b/lib/DllReferencePlugin.js index 71bdbbbcc..89aa9645e 100644 --- a/lib/DllReferencePlugin.js +++ b/lib/DllReferencePlugin.js @@ -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} */ this._compilationData = new WeakMap(); diff --git a/lib/IgnorePlugin.js b/lib/IgnorePlugin.js index a77eb2e2c..943bead9e 100644 --- a/lib/IgnorePlugin.js +++ b/lib/IgnorePlugin.js @@ -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} */ diff --git a/lib/LoaderOptionsPlugin.js b/lib/LoaderOptionsPlugin.js index d88fa68af..da9126c0d 100644 --- a/lib/LoaderOptionsPlugin.js +++ b/lib/LoaderOptionsPlugin.js @@ -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 = { diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index ff08a8138..44565d2ad 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -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; diff --git a/lib/SourceMapDevToolPlugin.js b/lib/SourceMapDevToolPlugin.js index d0105f6a5..24ad4d45c 100644 --- a/lib/SourceMapDevToolPlugin.js +++ b/lib/SourceMapDevToolPlugin.js @@ -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; diff --git a/lib/WatchIgnorePlugin.js b/lib/WatchIgnorePlugin.js index e6f255085..f248652b9 100644 --- a/lib/WatchIgnorePlugin.js +++ b/lib/WatchIgnorePlugin.js @@ -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; } /** diff --git a/lib/WebpackOptionsValidationError.js b/lib/WebpackOptionsValidationError.js deleted file mode 100644 index e6806b208..000000000 --- a/lib/WebpackOptionsValidationError.js +++ /dev/null @@ -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 { : ${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; diff --git a/lib/debug/ProfilingPlugin.js b/lib/debug/ProfilingPlugin.js index 575405a09..e2912c92f 100644 --- a/lib/debug/ProfilingPlugin.js +++ b/lib/debug/ProfilingPlugin.js @@ -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) { diff --git a/lib/ids/HashedModuleIdsPlugin.js b/lib/ids/HashedModuleIdsPlugin.js index e39a9d229..f3d087b8d 100644 --- a/lib/ids/HashedModuleIdsPlugin.js +++ b/lib/ids/HashedModuleIdsPlugin.js @@ -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 = { diff --git a/lib/ids/OccurrenceChunkIdsPlugin.js b/lib/ids/OccurrenceChunkIdsPlugin.js index 751ce7793..1e58af1ed 100644 --- a/lib/ids/OccurrenceChunkIdsPlugin.js +++ b/lib/ids/OccurrenceChunkIdsPlugin.js @@ -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; } diff --git a/lib/ids/OccurrenceModuleIdsPlugin.js b/lib/ids/OccurrenceModuleIdsPlugin.js index 4d594f723..05915b119 100644 --- a/lib/ids/OccurrenceModuleIdsPlugin.js +++ b/lib/ids/OccurrenceModuleIdsPlugin.js @@ -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; } diff --git a/lib/index.js b/lib/index.js index 453692d66..3e62d02e0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -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); diff --git a/lib/optimize/AggressiveSplittingPlugin.js b/lib/optimize/AggressiveSplittingPlugin.js index 297f4527b..affc6d993 100644 --- a/lib/optimize/AggressiveSplittingPlugin.js +++ b/lib/optimize/AggressiveSplittingPlugin.js @@ -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") { diff --git a/lib/optimize/LimitChunkCountPlugin.js b/lib/optimize/LimitChunkCountPlugin.js index 47992e737..69f247df4 100644 --- a/lib/optimize/LimitChunkCountPlugin.js +++ b/lib/optimize/LimitChunkCountPlugin.js @@ -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; } diff --git a/lib/optimize/MinChunkSizePlugin.js b/lib/optimize/MinChunkSizePlugin.js index b4bf60fc2..1348fdfd6 100644 --- a/lib/optimize/MinChunkSizePlugin.js +++ b/lib/optimize/MinChunkSizePlugin.js @@ -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; } diff --git a/lib/validateSchema.js b/lib/validateSchema.js index 0e3af177f..387e4f561 100644 --- a/lib/validateSchema.js +++ b/lib/validateSchema.js @@ -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; diff --git a/lib/webpack.js b/lib/webpack.js index 66fa35711..7d2a6b729 100644 --- a/lib/webpack.js +++ b/lib/webpack.js @@ -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; diff --git a/package.json b/package.json index 2a0d729cb..80176781c 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/schemas/ajv.absolutePath.js b/schemas/ajv.absolutePath.js deleted file mode 100644 index 8ef11f435..000000000 --- a/schemas/ajv.absolutePath.js +++ /dev/null @@ -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; - } - }); diff --git a/schemas/plugins/WatchIgnorePlugin.json b/schemas/plugins/WatchIgnorePlugin.json index b08d50ba6..2acb7e78c 100644 --- a/schemas/plugins/WatchIgnorePlugin.json +++ b/schemas/plugins/WatchIgnorePlugin.json @@ -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"] } diff --git a/schemas/plugins/debug/ProfilingPlugin.json b/schemas/plugins/debug/ProfilingPlugin.json index e9a4bf18b..4de19e564 100644 --- a/schemas/plugins/debug/ProfilingPlugin.json +++ b/schemas/plugins/debug/ProfilingPlugin.json @@ -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 } } } diff --git a/test/ProfilingPlugin.unittest.js b/test/ProfilingPlugin.unittest.js index 0528c9ecb..b56bb8330 100644 --- a/test/ProfilingPlugin.unittest.js +++ b/test/ProfilingPlugin.unittest.js @@ -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", () => { diff --git a/test/Validation.test.js b/test/Validation.test.js index d7f7e1594..15f7caeb8 100644 --- a/test/Validation.test.js +++ b/test/Validation.test.js @@ -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? }" + `) ); }); diff --git a/test/watchCases/plugins/watch-ignore-plugin/webpack.config.js b/test/watchCases/plugins/watch-ignore-plugin/webpack.config.js index a24d87e2f..81c7a169c 100644 --- a/test/watchCases/plugins/watch-ignore-plugin/webpack.config.js +++ b/test/watchCases/plugins/watch-ignore-plugin/webpack.config.js @@ -1,5 +1,5 @@ var webpack = require("../../../../"); module.exports = { - plugins: [new webpack.WatchIgnorePlugin([/file\.js$/, /foo$/])] + plugins: [new webpack.WatchIgnorePlugin({ paths: [/file\.js$/, /foo$/] })] }; diff --git a/yarn.lock b/yarn.lock index 14a387145..da4001348 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"