move cli flag generation to runtime
This commit is contained in:
parent
d3aa5519aa
commit
c0c98f4254
2831
bin/cli-flags.js
2831
bin/cli-flags.js
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const schema = require("../schemas/WebpackOptions.json");
|
||||
|
||||
const getFlags = ({ filter = undefined } = {}) => {
|
||||
const flags = {};
|
||||
|
||||
function decamelize(input) {
|
||||
return input
|
||||
.replace(
|
||||
/(\p{Uppercase_Letter}+|\p{Lowercase_Letter}|\d)(\p{Uppercase_Letter})/gu,
|
||||
"$1-$2"
|
||||
)
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function getSchemaPart(path) {
|
||||
const newPath = path.split("/");
|
||||
|
||||
let schemaPart = schema;
|
||||
|
||||
for (let i = 1; i < newPath.length; i++) {
|
||||
const inner = schemaPart[newPath[i]];
|
||||
|
||||
if (!inner) {
|
||||
break;
|
||||
}
|
||||
|
||||
schemaPart = inner;
|
||||
}
|
||||
|
||||
return schemaPart;
|
||||
}
|
||||
|
||||
const ignoredSchemaPaths = new Set(["devServer"]);
|
||||
const specialSchemaPathNames = {
|
||||
"node/__dirname": "node/dirname",
|
||||
"node/__filename": "node/filename"
|
||||
};
|
||||
|
||||
function addFlag(schemaPath, schemaPart, multiple) {
|
||||
if (filter && !filter(schemaPath, schemaPart)) return;
|
||||
const name = decamelize(schemaPath.replace(/\./g, "-"));
|
||||
const types = schemaPart.enum
|
||||
? [...new Set(schemaPart.enum.map(item => typeof item))]
|
||||
: Array.isArray(schemaPart.type)
|
||||
? schemaPart.type
|
||||
: [schemaPart.type];
|
||||
|
||||
if (!flags[name]) {
|
||||
flags[name] = {
|
||||
path: schemaPath,
|
||||
description: schemaPart.description,
|
||||
types: []
|
||||
};
|
||||
}
|
||||
|
||||
for (const type of types) {
|
||||
const duplicateIndex = flags[name].types.findIndex(
|
||||
item => item.type === type
|
||||
);
|
||||
|
||||
if (duplicateIndex > -1) {
|
||||
if (multiple) {
|
||||
flags[name].types[duplicateIndex].multiple = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
flags[name].types.push({ type, multiple });
|
||||
}
|
||||
|
||||
if (
|
||||
flags[name].description &&
|
||||
schemaPart.description &&
|
||||
!flags[name].description.includes(schemaPart.description)
|
||||
) {
|
||||
flags[name].description += ` ${schemaPart.description}`;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO support `not` and `if/then/else`
|
||||
// TODO support `const`, but we don't use it on our schema
|
||||
function traverse(schemaPart, schemaPath = "", depth = 0, inArray = false) {
|
||||
if (ignoredSchemaPaths.has(schemaPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (depth === 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (schemaPart.$ref) {
|
||||
schemaPart = getSchemaPart(schemaPart.$ref);
|
||||
}
|
||||
|
||||
if (
|
||||
!schemaPart.type &&
|
||||
!schemaPart.enum &&
|
||||
!schemaPart.oneOf &&
|
||||
!schemaPart.anyOf &&
|
||||
!schemaPart.allOf
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaPart.type === "null") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaPart.type === "object") {
|
||||
if (schemaPart.properties) {
|
||||
Object.keys(schemaPart.properties).forEach(property =>
|
||||
traverse(
|
||||
schemaPart.properties[property],
|
||||
schemaPath ? `${schemaPath}.${property}` : property,
|
||||
depth + 1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaPart.type === "array") {
|
||||
if (Array.isArray(schemaPart.items)) {
|
||||
schemaPart.items.forEach(item => {
|
||||
traverse(item, schemaPath, depth + 1, true);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(schemaPart.items, schemaPath, depth + 1, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const maybeOf = schemaPart.oneOf || schemaPart.anyOf || schemaPart.allOf;
|
||||
|
||||
if (maybeOf) {
|
||||
const items = maybeOf;
|
||||
|
||||
items.forEach((item, index) =>
|
||||
traverse(items[index], schemaPath, depth + 1)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (specialSchemaPathNames[schemaPath]) {
|
||||
schemaPath = specialSchemaPathNames[schemaPath];
|
||||
}
|
||||
|
||||
addFlag(schemaPath, schemaPart, inArray);
|
||||
}
|
||||
|
||||
traverse(schema);
|
||||
|
||||
return flags;
|
||||
};
|
||||
|
||||
exports.getFlags = getFlags;
|
|
@ -31,6 +31,7 @@ const exportPlugins = (obj, mappings) => {
|
|||
};
|
||||
|
||||
exportPlugins(module.exports, {
|
||||
cli: () => require("./cli"),
|
||||
AutomaticPrefetchPlugin: () => require("./AutomaticPrefetchPlugin"),
|
||||
BannerPlugin: () => require("./BannerPlugin"),
|
||||
Cache: () => require("./Cache"),
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
"type-lint": "tsc --pretty",
|
||||
"spellcheck": "cspell \"{.github,benchmark,bin,examples,hot,lib,schemas,setup,tooling}/**/*.{md,yml,yaml,js,json}\" \"*.md\"",
|
||||
"special-lint": "node tooling/inherit-types && node tooling/format-schemas && node tooling/format-file-header && node tooling/compile-to-definitions",
|
||||
"special-lint-fix": "node tooling/inherit-types --write --override && node tooling/format-schemas --write && node tooling/format-file-header --write && node tooling/compile-to-definitions --write && node ./tooling/generate-cli-flags.js",
|
||||
"special-lint-fix": "node tooling/inherit-types --write --override && node tooling/format-schemas --write && node tooling/format-file-header --write && node tooling/compile-to-definitions --write",
|
||||
"fix": "yarn code-lint --fix && yarn special-lint-fix && yarn pretty-lint-fix",
|
||||
"pretty-lint-base": "prettier --loglevel warn \"*.{ts,js,json,yml,yaml,md}\" \"{setup,lib,bin,hot,benchmark,tooling,schemas}/**/*.{js,json}\" \"test/*.js\" \"test/helpers/*.js\" \"test/{configCases,watchCases,statsCases,hotCases}/**/webpack.config.js\" \"examples/**/webpack.config.js\" \"examples/*.md\"",
|
||||
"pretty-lint-fix": "yarn pretty-lint-base --write",
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
const webpack = require("../");
|
||||
|
||||
describe("Cli", () => {
|
||||
it("should generate the correct cli flags", () => {
|
||||
expect(webpack.cli.getFlags()).toMatchSnapshot();
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -1,206 +0,0 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const prettier = require("prettier");
|
||||
const schema = require("../schemas/WebpackOptions.json");
|
||||
|
||||
const flags = {};
|
||||
|
||||
function decamelize(input) {
|
||||
return input
|
||||
.replace(/([\p{Lowercase_Letter}\d])(\p{Uppercase_Letter})/gu, `$1${"-"}$2`)
|
||||
.replace(
|
||||
/(\p{Uppercase_Letter}+)(\p{Uppercase_Letter}\p{Lowercase_Letter}+)/gu,
|
||||
`$1${"-"}$2`
|
||||
)
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function getSchemaPart(path) {
|
||||
const newPath = path.split("/");
|
||||
|
||||
let schemaPart = schema;
|
||||
|
||||
for (let i = 1; i < newPath.length; i++) {
|
||||
const inner = schemaPart[newPath[i]];
|
||||
|
||||
if (!inner) {
|
||||
break;
|
||||
}
|
||||
|
||||
schemaPart = inner;
|
||||
}
|
||||
|
||||
return schemaPart;
|
||||
}
|
||||
|
||||
function addFlag(schemaPath, schemaPart, multiple) {
|
||||
const name = decamelize(schemaPath.replace(/\./g, "-"));
|
||||
const types = schemaPart.enum
|
||||
? [...new Set(schemaPart.enum.map(item => typeof item))]
|
||||
: Array.isArray(schemaPart.type)
|
||||
? schemaPart.type
|
||||
: [schemaPart.type];
|
||||
|
||||
if (!flags[name]) {
|
||||
flags[name] = {
|
||||
path: schemaPath,
|
||||
description: schemaPart.description,
|
||||
types: []
|
||||
};
|
||||
}
|
||||
|
||||
for (const type of types) {
|
||||
const duplicateIndex = flags[name].types.findIndex(
|
||||
item => item.type === type
|
||||
);
|
||||
|
||||
if (duplicateIndex > -1) {
|
||||
if (multiple) {
|
||||
flags[name].types[duplicateIndex].multiple = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
flags[name].types.push({ type: type, multiple });
|
||||
}
|
||||
}
|
||||
|
||||
const ignoredSchemaPaths = new Set([
|
||||
"devServer",
|
||||
"module.defaultRules.compiler",
|
||||
"module.defaultRules.rules",
|
||||
"module.defaultRules.oneOf",
|
||||
"module.defaultRules.loader",
|
||||
"module.defaultRules.use.loader",
|
||||
"module.defaultRules.use.options",
|
||||
"module.rules.compiler",
|
||||
"module.rules.rules",
|
||||
"module.rules.oneOf",
|
||||
"module.rules.loader",
|
||||
"module.rules.use.loader",
|
||||
"module.rules.use.options",
|
||||
...["and", "exclude", "include", "not", "or", "test"].reduce(
|
||||
(accumulator, currentValue) =>
|
||||
accumulator.concat(
|
||||
`module.defaultRules.test.${currentValue}`,
|
||||
`module.defaultRules.include.${currentValue}`,
|
||||
`module.defaultRules.exclude.${currentValue}`,
|
||||
`module.defaultRules.issuer.${currentValue}`,
|
||||
`module.defaultRules.resource.${currentValue}`,
|
||||
`module.defaultRules.resourceQuery.${currentValue}`,
|
||||
`module.defaultRules.realResource.${currentValue}`,
|
||||
`module.defaultRules.use.${currentValue}`,
|
||||
`module.rules.test.${currentValue}`,
|
||||
`module.rules.include.${currentValue}`,
|
||||
`module.rules.exclude.${currentValue}`,
|
||||
`module.rules.issuer.${currentValue}`,
|
||||
`module.rules.resource.${currentValue}`,
|
||||
`module.rules.resourceQuery.${currentValue}`,
|
||||
`module.rules.realResource.${currentValue}`,
|
||||
`module.rules.use.${currentValue}`
|
||||
),
|
||||
[]
|
||||
)
|
||||
]);
|
||||
const ignoredSchemaRefs = new Set([
|
||||
"#/definitions/RuleSetConditionsAbsolute",
|
||||
"#/definitions/RuleSetCondition"
|
||||
]);
|
||||
const specialSchemaPathNames = {
|
||||
"node.__dirname": "node.dirname",
|
||||
"node.__filename": "node.filename"
|
||||
};
|
||||
|
||||
// TODO support `not` and `if/then/else`
|
||||
// TODO support `const`, but we don't use it on our schema
|
||||
function traverse(schemaPart, schemaPath = "", inArray = false) {
|
||||
if (ignoredSchemaPaths.has(schemaPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (schemaPart.$ref) {
|
||||
if (ignoredSchemaRefs.has(schemaPart.$ref)) {
|
||||
return;
|
||||
}
|
||||
|
||||
schemaPart = getSchemaPart(schemaPart.$ref);
|
||||
}
|
||||
|
||||
if (
|
||||
!schemaPart.type &&
|
||||
!schemaPart.enum &&
|
||||
!schemaPart.oneOf &&
|
||||
!schemaPart.anyOf &&
|
||||
!schemaPart.allOf
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaPart.type === "null") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaPart.type === "object") {
|
||||
if (schemaPart.properties) {
|
||||
Object.keys(schemaPart.properties).forEach(property =>
|
||||
traverse(
|
||||
schemaPart.properties[property],
|
||||
schemaPath ? `${schemaPath}.${property}` : property,
|
||||
inArray
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaPart.type === "array") {
|
||||
if (Array.isArray(schemaPart.items)) {
|
||||
schemaPart.items.forEach(item => traverse(item, schemaPath, true));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(schemaPart.items, schemaPath, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const maybeOf = schemaPart.oneOf || schemaPart.anyOf || schemaPart.allOf;
|
||||
|
||||
if (maybeOf) {
|
||||
maybeOf.forEach((item, index) =>
|
||||
traverse(maybeOf[index], schemaPath, inArray)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (specialSchemaPathNames[schemaPath]) {
|
||||
schemaPath = specialSchemaPathNames[schemaPath];
|
||||
}
|
||||
|
||||
addFlag(schemaPath, schemaPart, inArray);
|
||||
}
|
||||
|
||||
traverse(schema);
|
||||
|
||||
const cliFlagsPath = path.resolve(__dirname, "../bin/cli-flags.js");
|
||||
const prettierConfig = prettier.resolveConfig.sync(cliFlagsPath);
|
||||
|
||||
fs.writeFileSync(
|
||||
cliFlagsPath,
|
||||
prettier.format(
|
||||
`/**
|
||||
* This file was automatically generated.
|
||||
* DO NOT MODIFY BY HAND.
|
||||
* Run \`yarn special-lint-fix\` to update
|
||||
*/\n
|
||||
module.exports = ${JSON.stringify(flags, null, 2)};`,
|
||||
{
|
||||
...prettierConfig,
|
||||
parser: "babel"
|
||||
}
|
||||
)
|
||||
);
|
Loading…
Reference in New Issue