fix issues with absolutePath, allOf and anyOf

make DllReferencePlugin schema more expressive
typing fixes
This commit is contained in:
Tobias Koppers 2018-09-21 11:00:44 +02:00
parent 8c31f2a708
commit c7681825f8
8 changed files with 554 additions and 173 deletions

4
declarations.d.ts vendored
View File

@ -224,6 +224,10 @@ declare module "@webassemblyjs/ast" {
// It's referenced from "ruleSet-conditions" in schemas/WebpackOptions.json
interface RuleSetConditionsRecursive
extends Array<import("./declarations/WebpackOptions").RuleSetCondition> {}
interface RuleSetConditionsAbsoluteRecursive
extends Array<
import("./declarations/WebpackOptions").RuleSetConditionAbsolute
> {}
/**
* Global variable declarations

View File

@ -87,7 +87,7 @@ export type RuleSetConditionOrConditions = RuleSetCondition | RuleSetConditions;
export type RuleSetCondition =
| RegExp
| string
| Function
| ((value: string) => boolean)
| RuleSetConditions
| {
/**
@ -120,6 +120,55 @@ export type RuleSetCondition =
* via the `definition` "RuleSetConditions".
*/
export type RuleSetConditions = RuleSetConditionsRecursive;
/**
* One or multiple rule conditions
*
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "RuleSetConditionOrConditionsAbsolute".
*/
export type RuleSetConditionOrConditionsAbsolute =
| RuleSetConditionAbsolute
| RuleSetConditionsAbsolute;
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "RuleSetConditionAbsolute".
*/
export type RuleSetConditionAbsolute =
| RegExp
| string
| ((value: string) => boolean)
| RuleSetConditionsAbsolute
| {
/**
* Logical AND
*/
and?: RuleSetConditionsAbsolute;
/**
* Exclude all modules matching any of these conditions
*/
exclude?: RuleSetConditionOrConditionsAbsolute;
/**
* Exclude all modules matching not any of these conditions
*/
include?: RuleSetConditionOrConditionsAbsolute;
/**
* Logical NOT
*/
not?: RuleSetConditionsAbsolute;
/**
* Logical OR
*/
or?: RuleSetConditionsAbsolute;
/**
* Exclude all modules matching any of these conditions
*/
test?: RuleSetConditionOrConditionsAbsolute;
};
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "RuleSetConditionsAbsolute".
*/
export type RuleSetConditionsAbsolute = RuleSetConditionsAbsoluteRecursive;
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "RuleSetLoader".
@ -461,21 +510,15 @@ export interface RuleSetRule {
/**
* Shortcut for resource.exclude
*/
exclude?: RuleSetConditionOrConditions & {
[k: string]: any;
};
exclude?: RuleSetConditionOrConditionsAbsolute;
/**
* Shortcut for resource.include
*/
include?: RuleSetConditionOrConditions & {
[k: string]: any;
};
include?: RuleSetConditionOrConditionsAbsolute;
/**
* Match the issuer of the module (The module pointing to this module)
*/
issuer?: RuleSetConditionOrConditions & {
[k: string]: any;
};
issuer?: RuleSetConditionOrConditionsAbsolute;
/**
* Shortcut for use.loader
*/
@ -509,9 +552,7 @@ export interface RuleSetRule {
/**
* Match the resource path of the module
*/
resource?: RuleSetConditionOrConditions & {
[k: string]: any;
};
resource?: RuleSetConditionOrConditionsAbsolute;
/**
* Match the resource query of the module
*/
@ -527,9 +568,7 @@ export interface RuleSetRule {
/**
* Shortcut for resource.test
*/
test?: RuleSetConditionOrConditions & {
[k: string]: any;
};
test?: RuleSetConditionOrConditionsAbsolute;
/**
* Module type to use for the module
*/

View File

@ -4,6 +4,122 @@
* Run `yarn special-lint-fix` to update
*/
export type DllReferencePluginOptions = {
[k: string]: any;
};
export type DllReferencePluginOptions =
| {
/**
* (absolute path) context of requests in the manifest (or content property)
*/
context?: string;
/**
* Extensions used to resolve modules in the dll bundle (only used when using 'scope')
*/
extensions?: string[];
/**
* An object containing content and name or a string to the absolute path of the JSON manifest to be loaded upon compilation
*/
manifest: DllReferencePluginOptionsManifest | string;
/**
* The name where the dll is exposed (external name, defaults to manifest.name)
*/
name?: string;
/**
* Prefix which is used for accessing the content of the dll
*/
scope?: string;
/**
* How the dll is exposed (libraryTarget, defaults to manifest.type)
*/
sourceType?: DllReferencePluginOptionsSourceType;
/**
* The way how the export of the dll bundle is used
*/
type?: "require" | "object";
}
| {
/**
* The mappings from request to module info
*/
content: DllReferencePluginOptionsContent;
/**
* (absolute path) context of requests in the manifest (or content property)
*/
context?: string;
/**
* Extensions used to resolve modules in the dll bundle (only used when using 'scope')
*/
extensions?: string[];
/**
* The name where the dll is exposed (external name)
*/
name: string;
/**
* Prefix which is used for accessing the content of the dll
*/
scope?: string;
/**
* How the dll is exposed (libraryTarget)
*/
sourceType?: DllReferencePluginOptionsSourceType;
/**
* The way how the export of the dll bundle is used
*/
type?: "require" | "object";
};
/**
* The type how the dll is exposed (external type)
*/
export type DllReferencePluginOptionsSourceType =
| "var"
| "assign"
| "this"
| "window"
| "global"
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "amd"
| "umd"
| "umd2"
| "jsonp";
/**
* An object containing content, name and type
*/
export interface DllReferencePluginOptionsManifest {
/**
* The mappings from request to module info
*/
content: DllReferencePluginOptionsContent;
/**
* The name where the dll is exposed (external name)
*/
name?: string;
/**
* The type how the dll is exposed (external type)
*/
type?: DllReferencePluginOptionsSourceType;
}
/**
* The mappings from request to module info
*/
export interface DllReferencePluginOptionsContent {
/**
* Module info
*/
[k: string]: {
/**
* Meta information about the module
*/
buildMeta?: {
[k: string]: any;
};
/**
* Information about the provided exports of the module
*/
exports?: true | string[];
/**
* Module ID
*/
id: number | string;
};
}

View File

@ -17,6 +17,7 @@ const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/DllReferencePlugin.json");
/** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptions} DllReferencePluginOptions */
/** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptionsManifest} DllReferencePluginOptionsManifest */
class DllReferencePlugin {
/**
@ -45,55 +46,71 @@ class DllReferencePlugin {
compiler.hooks.beforeCompile.tapAsync(
"DllReferencePlugin",
(params, callback) => {
const manifest = this.options.manifest;
if (typeof manifest === "string") {
params.compilationDependencies.add(manifest);
compiler.inputFileSystem.readFile(manifest, (err, result) => {
if (err) return callback(err);
// Catch errors parsing the manifest so that blank
// or malformed manifest files don't kill the process.
try {
params["dll reference " + manifest] = parseJson(
result.toString("utf-8")
);
} catch (e) {
// Store the error in the params so that it can
// be added as a compilation error later on.
const manifestPath = makePathsRelative(
compiler.options.context,
manifest
);
params[
"dll reference parse error " + manifest
] = new DllManifestError(manifestPath, e.message);
}
return callback();
});
} else {
return callback();
if ("manifest" in this.options) {
const manifest = this.options.manifest;
if (typeof manifest === "string") {
params.compilationDependencies.add(manifest);
compiler.inputFileSystem.readFile(manifest, (err, result) => {
if (err) return callback(err);
// Catch errors parsing the manifest so that blank
// or malformed manifest files don't kill the process.
try {
params["dll reference " + manifest] = parseJson(
result.toString("utf-8")
);
} catch (e) {
// Store the error in the params so that it can
// be added as a compilation error later on.
const manifestPath = makePathsRelative(
compiler.options.context,
manifest
);
params[
"dll reference parse error " + manifest
] = new DllManifestError(manifestPath, e.message);
}
return callback();
});
return;
}
}
return callback();
}
);
compiler.hooks.compile.tap("DllReferencePlugin", params => {
let manifest = this.options.manifest;
if (typeof manifest === "string") {
// If there was an error parsing the manifest
// file, exit now because the error will be added
// as a compilation error in the "compilation" hook.
if (params["dll reference parse error " + manifest]) {
return;
let name = this.options.name;
let sourceType = this.options.sourceType;
let content =
"content" in this.options ? this.options.content : undefined;
if ("manifest" in this.options) {
let manifestParameter = this.options.manifest;
let manifest;
if (typeof manifestParameter === "string") {
// If there was an error parsing the manifest
// file, exit now because the error will be added
// as a compilation error in the "compilation" hook.
if (params["dll reference parse error " + manifestParameter]) {
return;
}
manifest =
/** @type {DllReferencePluginOptionsManifest} */ (params[
"dll reference " + manifestParameter
]);
} else {
manifest = manifestParameter;
}
if (manifest) {
if (!name) name = manifest.name;
if (!sourceType) sourceType = manifest.type;
if (!content) content = manifest.content;
}
manifest = params["dll reference " + manifest];
}
const name = this.options.name || manifest.name;
const sourceType =
this.options.sourceType || (manifest && manifest.type) || "var";
const externals = {};
const source = "dll-reference " + name;
externals[source] = name;
const normalModuleFactory = params.normalModuleFactory;
new ExternalModuleFactoryPlugin(sourceType, externals).apply(
new ExternalModuleFactoryPlugin(sourceType || "var", externals).apply(
normalModuleFactory
);
new DelegatedModuleFactoryPlugin({
@ -101,7 +118,7 @@ class DllReferencePlugin {
type: this.options.type,
scope: this.options.scope,
context: this.options.context || compiler.options.context,
content: this.options.content || manifest.content,
content,
extensions: this.options.extensions
}).apply(normalModuleFactory);
});
@ -109,13 +126,15 @@ class DllReferencePlugin {
compiler.hooks.compilation.tap(
"DllReferencePlugin",
(compilation, params) => {
let manifest = this.options.manifest;
if (typeof manifest === "string") {
// If there was an error parsing the manifest file, add the
// error as a compilation error to make the compilation fail.
let e = params["dll reference parse error " + manifest];
if (e) {
compilation.errors.push(e);
if ("manifest" in this.options) {
let manifest = this.options.manifest;
if (typeof manifest === "string") {
// If there was an error parsing the manifest file, add the
// error as a compilation error to make the compilation fail.
let e = params["dll reference parse error " + manifest];
if (e) {
compilation.errors.push(e);
}
}
}
}

View File

@ -286,7 +286,7 @@
},
"rules": {
"description": "An array of rules applied for modules.",
"allOf": [
"anyOf": [
{
"$ref": "#/definitions/RuleSetRules"
}
@ -858,14 +858,14 @@
"description": "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.",
"anyOf": [
{
"type": "string"
"type": "string",
"absolutePath": false
},
{
"instanceof": "Function",
"tsType": "Function"
}
],
"absolutePath": false
]
},
"globalObject": {
"description": "An expression which is used to address the global object/scope in runtime code",
@ -903,14 +903,14 @@
"description": "The filename of the Hot Update Chunks. They are inside the output.path directory.",
"anyOf": [
{
"type": "string"
"type": "string",
"absolutePath": false
},
{
"instanceof": "Function",
"tsType": "Function"
}
],
"absolutePath": false
]
},
"hotUpdateFunction": {
"description": "The JSONP function used by webpack for async loading of hot update chunks.",
@ -920,14 +920,14 @@
"description": "The filename of the Hot Update Main File. It is inside the `output.path` directory.",
"anyOf": [
{
"type": "string"
"type": "string",
"absolutePath": false
},
{
"instanceof": "Function",
"tsType": "Function"
}
],
"absolutePath": false
]
},
"jsonpFunction": {
"description": "The JSONP function used by webpack for async loading of chunks.",
@ -1222,7 +1222,7 @@
},
{
"instanceof": "Function",
"tsType": "Function"
"tsType": "((value: string) => boolean)"
},
{
"$ref": "#/definitions/RuleSetConditions"
@ -1283,6 +1283,79 @@
}
]
},
"RuleSetConditionAbsolute": {
"anyOf": [
{
"instanceof": "RegExp",
"tsType": "RegExp"
},
{
"type": "string",
"absolutePath": true
},
{
"instanceof": "Function",
"tsType": "((value: string) => boolean)"
},
{
"$ref": "#/definitions/RuleSetConditionsAbsolute"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"and": {
"description": "Logical AND",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionsAbsolute"
}
]
},
"exclude": {
"description": "Exclude all modules matching any of these conditions",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
},
"include": {
"description": "Exclude all modules matching not any of these conditions",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
},
"not": {
"description": "Logical NOT",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionsAbsolute"
}
]
},
"or": {
"description": "Logical OR",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionsAbsolute"
}
]
},
"test": {
"description": "Exclude all modules matching any of these conditions",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
}
}
}
]
},
"RuleSetConditionOrConditions": {
"description": "One or multiple rule conditions",
"anyOf": [
@ -1294,6 +1367,17 @@
}
]
},
"RuleSetConditionOrConditionsAbsolute": {
"description": "One or multiple rule conditions",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionAbsolute"
},
{
"$ref": "#/definitions/RuleSetConditionsAbsolute"
}
]
},
"RuleSetConditions": {
"type": "array",
"items": {
@ -1306,6 +1390,18 @@
},
"tsType": "RuleSetConditionsRecursive"
},
"RuleSetConditionsAbsolute": {
"type": "array",
"items": {
"description": "A rule condition",
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionAbsolute"
}
]
},
"tsType": "RuleSetConditionsAbsoluteRecursive"
},
"RuleSetLoader": {
"type": "string",
"minLength": 1
@ -1338,34 +1434,25 @@
},
"exclude": {
"description": "Shortcut for resource.exclude",
"allOf": [
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditions"
},
{
"absolutePath": true
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
},
"include": {
"description": "Shortcut for resource.include",
"allOf": [
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditions"
},
{
"absolutePath": true
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
},
"issuer": {
"description": "Match the issuer of the module (The module pointing to this module)",
"allOf": [
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditions"
},
{
"absolutePath": true
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
},
@ -1428,12 +1515,9 @@
},
"resource": {
"description": "Match the resource path of the module",
"allOf": [
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditions"
},
{
"absolutePath": true
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
},
@ -1459,12 +1543,9 @@
},
"test": {
"description": "Shortcut for resource.test",
"allOf": [
"anyOf": [
{
"$ref": "#/definitions/RuleSetConditionOrConditions"
},
{
"absolutePath": true
"$ref": "#/definitions/RuleSetConditionOrConditionsAbsolute"
}
]
},
@ -1796,15 +1877,6 @@
},
"WebpackPluginFunction": {
"description": "Function acting as plugin",
"additionalProperties": true,
"properties": {
"apply": {
"description": "The run point of the plugin, required method.",
"instanceof": "Function",
"tsType": "Function"
}
},
"required": ["apply"],
"instanceof": "Function",
"tsType": "(compiler: import('../lib/Compiler')) => void"
},

View File

@ -1,5 +1,6 @@
{
"title": "DllPluginOptions",
"type": "object",
"additionalProperties": false,
"properties": {
"context": {

View File

@ -1,67 +1,83 @@
{
"title": "DllReferencePluginOptions",
"additionalProperties": false,
"properties": {
"content": {
"description": "The mappings from request to module id (defaults to manifest.content)",
"definitions": {
"DllReferencePluginOptionsContent": {
"description": "The mappings from request to module info",
"type": "object",
"minLength": 1
},
"context": {
"description": "(absolute path) context of requests in the manifest (or content property)",
"type": "string",
"absolutePath": true
},
"extensions": {
"description": "Extensions used to resolve modules in the dll bundle (only used when using 'scope')",
"type": "array",
"items": {
"description": "An extension",
"type": "string"
}
},
"manifest": {
"description": "An object containing content and name or a string to the absolute path of the JSON manifest to be loaded upon compilation",
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"content": {
"description": "The mappings from request to module id",
"type": "object",
"minLength": 1
},
"name": {
"description": "The name where the dll is exposed (external name)",
"type": "string",
"minLength": 1
},
"type": {
"description": "The type how the dll is exposed (external type)",
"type": "string",
"minLength": 1
}
"additionalProperties": {
"description": "Module info",
"type": "object",
"additionalProperties": false,
"properties": {
"buildMeta": {
"description": "Meta information about the module",
"type": "object"
},
"exports": {
"description": "Information about the provided exports of the module",
"anyOf": [
{
"description": "Exports unknown/dynamic",
"enum": [true]
},
{
"description": "List of provided exports of the module",
"type": "array",
"items": {
"description": "Name of the export",
"type": "string",
"minLength": 1
}
}
]
},
"id": {
"description": "Module ID",
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"minLength": 1
}
]
}
},
{
"required": ["id"]
},
"minProperties": 1
},
"DllReferencePluginOptionsManifest": {
"description": "An object containing content, name and type",
"type": "object",
"additionalProperties": false,
"properties": {
"content": {
"description": "The mappings from request to module info",
"anyOf": [
{
"$ref": "#/definitions/DllReferencePluginOptionsContent"
}
]
},
"name": {
"description": "The name where the dll is exposed (external name)",
"type": "string",
"absolutePath": true
"minLength": 1
},
"type": {
"description": "The type how the dll is exposed (external type)",
"anyOf": [
{
"$ref": "#/definitions/DllReferencePluginOptionsSourceType"
}
]
}
]
},
"required": ["content"]
},
"name": {
"description": "The name where the dll is exposed (external name, defaults to manifest.name)",
"type": "string",
"minLength": 1
},
"scope": {
"description": "Prefix which is used for accessing the content of the dll",
"type": "string",
"minLength": 1
},
"sourceType": {
"description": "How the dll is exposed (libraryTarget, defaults to manifest.type)",
"DllReferencePluginOptionsSourceType": {
"description": "The type how the dll is exposed (external type)",
"enum": [
"var",
"assign",
@ -76,18 +92,113 @@
"umd2",
"jsonp"
]
},
"type": {
"description": "The way how the export of the dll bundle is used",
"enum": ["require", "object"]
}
},
"title": "DllReferencePluginOptions",
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"context": {
"description": "(absolute path) context of requests in the manifest (or content property)",
"type": "string",
"absolutePath": true
},
"extensions": {
"description": "Extensions used to resolve modules in the dll bundle (only used when using 'scope')",
"type": "array",
"items": {
"description": "An extension",
"type": "string"
}
},
"manifest": {
"description": "An object containing content and name or a string to the absolute path of the JSON manifest to be loaded upon compilation",
"oneOf": [
{
"$ref": "#/definitions/DllReferencePluginOptionsManifest"
},
{
"type": "string",
"absolutePath": true
}
]
},
"name": {
"description": "The name where the dll is exposed (external name, defaults to manifest.name)",
"type": "string",
"minLength": 1
},
"scope": {
"description": "Prefix which is used for accessing the content of the dll",
"type": "string",
"minLength": 1
},
"sourceType": {
"description": "How the dll is exposed (libraryTarget, defaults to manifest.type)",
"anyOf": [
{
"$ref": "#/definitions/DllReferencePluginOptionsSourceType"
}
]
},
"type": {
"description": "The way how the export of the dll bundle is used",
"enum": ["require", "object"]
}
},
"required": ["manifest"]
},
{
"required": ["content"]
"type": "object",
"additionalProperties": false,
"properties": {
"content": {
"description": "The mappings from request to module info",
"anyOf": [
{
"$ref": "#/definitions/DllReferencePluginOptionsContent"
}
]
},
"context": {
"description": "(absolute path) context of requests in the manifest (or content property)",
"type": "string",
"absolutePath": true
},
"extensions": {
"description": "Extensions used to resolve modules in the dll bundle (only used when using 'scope')",
"type": "array",
"items": {
"description": "An extension",
"type": "string"
}
},
"name": {
"description": "The name where the dll is exposed (external name)",
"type": "string",
"minLength": 1
},
"scope": {
"description": "Prefix which is used for accessing the content of the dll",
"type": "string",
"minLength": 1
},
"sourceType": {
"description": "How the dll is exposed (libraryTarget)",
"anyOf": [
{
"$ref": "#/definitions/DllReferencePluginOptionsSourceType"
}
]
},
"type": {
"description": "The way how the export of the dll bundle is used",
"enum": ["require", "object"]
}
},
"required": ["content", "name"]
}
]
}

View File

@ -40,7 +40,6 @@ describe("Schemas", () => {
"type",
"oneOf",
"anyOf",
"allOf",
"absolutePath",
"description",
"enum",
@ -99,6 +98,26 @@ describe("Schemas", () => {
});
}
if ("absolutePath" in item) {
it("should have type: 'string' specified when using absolutePath", () => {
if (item.type !== "string") {
throw new Error(
"When using absolutePath, type must be 'string'"
);
}
});
}
if ("properties" in item || "additionalProperties" in item) {
it("should have type: 'object' specified when using properties or additionalProperties", () => {
if (item.type !== "object") {
throw new Error(
"When using properties or additionalProperties, type must be 'object'"
);
}
});
}
arrayProperties.forEach(prop => {
if (prop in item) {
describe(prop, () => {