feat: 去掉文件名大写

This commit is contained in:
万纯 2020-11-30 16:05:15 +08:00
parent 67903dab8b
commit 05d994e50d
18 changed files with 474 additions and 152 deletions

View File

@ -60,7 +60,8 @@ export function pathToObj({ path, cwd }) {
if (pkgJSONPath) {
// eslint-disable-next-line
pkg = require(pkgJSONPath);
isPkgPlugin = winPath(join(dirname(pkgJSONPath), pkg.main || 'index.js')) === winPath(path);
isPkgPlugin = winPath(join(dirname(pkgJSONPath), pkg.main || 'index.js'))
=== winPath(path);
}
let id;
@ -107,7 +108,6 @@ export function resolvePlugins(opts) {
}));
}
export function isValidPlugin(plugin) {
return plugin.id && plugin.key && plugin.apply;
}

View File

@ -1,10 +1,5 @@
import {
existsSync
} from 'fs';
import {
extname,
join
} from 'path';
import { existsSync } from 'fs';
import { extname, join } from 'path';
import {
chalk,
chokidar,
@ -18,9 +13,7 @@ import {
} from '@umijs/utils';
import assert from 'assert';
import joi from 'joi';
import {
ServiceStage
} from '../Service/enums';
import { ServiceStage } from '../service/enums';
import {
getUserConfigWithKey,
updateUserConfigWithKey
@ -28,10 +21,7 @@ import {
import isEqual from './utils/isEqual';
import mergeDefault from './utils/mergeDefault';
const CONFIG_FILES = [
'.fes.js',
'config/config.js'
];
const CONFIG_FILES = ['.fes.js', 'config/config.js'];
// TODO:
// 1. custom config file
@ -57,10 +47,7 @@ export default class Config {
// collect default config
const defaultConfig = pluginIds.reduce((memo, pluginId) => {
const {
key,
config = {}
} = this.service.plugins[pluginId];
const { key, config = {} } = this.service.plugins[pluginId];
if ('default' in config) memo[key] = config.default;
return memo;
}, {});
@ -68,26 +55,23 @@ export default class Config {
return defaultConfig;
}
getConfig({
defaultConfig
}) {
getConfig({ defaultConfig }) {
assert(
this.service.stage >= ServiceStage.pluginReady,
'Config.getConfig() failed, it should not be executed before plugin is ready.',
'Config.getConfig() failed, it should not be executed before plugin is ready.'
);
const userConfig = this.getUserConfig();
// 用于提示用户哪些 key 是未定义的
// TODO: 考虑不排除 false 的 key
const userConfigKeys = Object.keys(userConfig).filter(key => userConfig[key] !== false);
const userConfigKeys = Object.keys(userConfig).filter(
key => userConfig[key] !== false
);
// get config
const pluginIds = Object.keys(this.service.plugins);
pluginIds.forEach((pluginId) => {
const {
key,
config = {}
} = this.service.plugins[pluginId];
const { key, config = {} } = this.service.plugins[pluginId];
// recognize as key if have schema config
if (!config.schema) return;
@ -102,14 +86,12 @@ export default class Config {
const schema = config.schema(joi);
assert(
joi.isSchema(schema),
`schema return from plugin ${pluginId} is not valid schema.`,
`schema return from plugin ${pluginId} is not valid schema.`
);
const {
error
} = schema.validate(value);
const { error } = schema.validate(value);
if (error) {
const e = new Error(
`Validate config "${key}" failed, ${error.message}`,
`Validate config "${key}" failed, ${error.message}`
);
e.stack = error.stack;
throw e;
@ -137,7 +119,9 @@ export default class Config {
if (userConfigKeys.length) {
const keys = userConfigKeys.length > 1 ? 'keys' : 'key';
throw new Error(`Invalid config ${keys}: ${userConfigKeys.join(', ')}`);
throw new Error(
`Invalid config ${keys}: ${userConfigKeys.join(', ')}`
);
}
return userConfig;
@ -153,11 +137,11 @@ export default class Config {
if (process.env.FES_ENV) {
const envConfigFileName = this.addAffix(
configFile,
process.env.FES_ENV,
process.env.FES_ENV
);
const fileNameWithoutExt = envConfigFileName.replace(
extname(envConfigFileName),
'',
''
);
envConfigFile = getFile({
base: this.cwd,
@ -166,7 +150,7 @@ export default class Config {
}).filename;
if (!envConfigFile) {
throw new Error(
`get user config failed, ${envConfigFile} does not exist, but process.env.FES_ENV is set to ${process.env.FES_ENV}.`,
`get user config failed, ${envConfigFile} does not exist, but process.env.FES_ENV is set to ${process.env.FES_ENV}.`
);
}
}
@ -203,7 +187,7 @@ export default class Config {
requireConfigs(configFiles) {
// eslint-disable-next-line
return configFiles.map(f => compatESModuleRequire(require(f)));
return configFiles.map((f) => compatESModuleRequire(require(f)));
}
mergeConfig(...configs) {
@ -266,10 +250,7 @@ export default class Config {
const pluginChanged = [];
const valueChanged = [];
Object.keys(this.service.plugins).forEach((pluginId) => {
const {
key,
config = {}
} = this.service.plugins[pluginId];
const { key, config = {} } = this.service.plugins[pluginId];
// recognize as key if have schema config
if (!config.schema) return;
if (!isEqual(newUserConfig[key], userConfig[key])) {
@ -277,7 +258,10 @@ export default class Config {
key,
pluginId
};
if (newUserConfig[key] === false || userConfig[key] === false) {
if (
newUserConfig[key] === false
|| userConfig[key] === false
) {
pluginChanged.push(changed);
} else {
valueChanged.push(changed);

View File

@ -0,0 +1,17 @@
import { lodash } from '@umijs/utils';
import set from 'set-value';
export function updateUserConfigWithKey({
key,
value,
userConfig
}) {
set(userConfig, key, value);
}
export function getUserConfigWithKey({
key,
userConfig
}) {
return lodash.get(userConfig, key);
}

View File

@ -0,0 +1,16 @@
import { lodash } from '@umijs/utils';
function funcToStr(obj) {
if (typeof obj === 'function') return obj.toString();
if (lodash.isPlainObject(obj)) {
return Object.keys(obj).reduce((memo, key) => {
memo[key] = funcToStr(obj[key]);
return memo;
}, {});
}
return obj;
}
export default function (a, b) {
return lodash.isEqual(funcToStr(a), funcToStr(b));
}

View File

@ -0,0 +1,9 @@
import { deepmerge, lodash } from '@umijs/utils';
export default ({ defaultConfig, config }) => {
if (lodash.isPlainObject(defaultConfig) && lodash.isPlainObject(config)) {
return deepmerge(defaultConfig, config);
}
return typeof config !== 'undefined' ? config : defaultConfig;
};

View File

@ -1,11 +1,11 @@
import Config from './Config/Config';
import Service from './Service/Service';
import PluginAPI from './Service/PluginAPI';
import Config from './config';
import Service from './service';
import PluginAPI from './service/pluginAPI';
import Logger from './logger/logger';
import { PluginType } from './Service/enums';
import { isPlugin } from './Service/utils/pluginUtils';
import { PluginType } from './service/enums';
import { isPlugin } from './service/utils/pluginUtils';
export * from './route';

View File

@ -0,0 +1,32 @@
export const PluginType = {
preset: 'preset',
plugin: 'plugin'
};
export const ServiceStage = {
uninitialized: 0,
constructor: 1,
init: 2,
initPlugins: 3,
initHooks: 4,
pluginReady: 5,
getConfig: 6,
getPaths: 7,
run: 8
};
export const ConfigChangeType = {
reload: 'reload',
regenerateTmpFiles: 'regenerateTmpFiles'
};
export const ApplyPluginsType = {
add: 'add',
modify: 'modify',
event: 'event'
};
export const EnableBy = {
register: 'register',
config: 'config'
};

View File

@ -0,0 +1,37 @@
import { join } from 'path';
import { existsSync, statSync } from 'fs';
import { lodash, winPath } from '@umijs/utils';
function isDirectoryAndExist(path) {
return existsSync(path) && statSync(path).isDirectory();
}
function normalizeWithWinPath(obj) {
return lodash.mapValues(obj, value => winPath(value));
}
export default function getServicePaths({
cwd,
config,
env
}) {
let absSrcPath = cwd;
if (isDirectoryAndExist(join(cwd, 'src'))) {
absSrcPath = join(cwd, 'src');
}
const absPagesPath = config.singular
? join(absSrcPath, 'page')
: join(absSrcPath, 'pages');
const tmpDir = ['.fes', env !== 'development' && env]
.filter(Boolean)
.join('-');
return normalizeWithWinPath({
cwd,
absNodeModulesPath: join(cwd, 'node_modules'),
absOutputPath: join(cwd, config.outputPath || './dist'),
absSrcPath,
absPagesPath,
absTmpPath: join(absSrcPath, tmpDir)
});
}

View File

@ -1,33 +1,21 @@
import {
join
} from 'path';
import {
EventEmitter
} from 'events';
import { join } from 'path';
import { EventEmitter } from 'events';
import assert from 'assert';
import {
AsyncSeriesWaterfallHook
} from 'tapable';
import {
existsSync
} from 'fs';
import { AsyncSeriesWaterfallHook } from 'tapable';
import { existsSync } from 'fs';
import { BabelRegister } from '@umijs/utils';
import {
resolvePlugins
} from './utils/pluginUtils';
import { resolvePlugins } from './utils/pluginUtils';
import loadDotEnv from './utils/loadDotEnv';
import isPromise from './utils/isPromise';
import PluginAPI from './PluginAPI';
import PluginAPI from './pluginAPI';
import {
ApplyPluginsType,
ConfigChangeType,
EnableBy,
ServiceStage
} from './enums';
import Config from '../Config/Config';
import {
getUserConfigWithKey
} from '../Config/utils/configUtils';
import Config from '../config';
import { getUserConfigWithKey } from '../config/utils/configUtils';
import getPaths from './getPaths';
// TODO
@ -137,7 +125,7 @@ export default class Service extends EventEmitter {
resolvePackage() {
try {
// eslint-disable-next-line
return require(join(this.cwd, 'package.json'));
return require(join(this.cwd, "package.json"));
} catch (e) {
return {};
}
@ -161,9 +149,7 @@ export default class Service extends EventEmitter {
Object.keys(this.hooksByPluginId).forEach((id) => {
const hooks = this.hooksByPluginId[id];
hooks.forEach((hook) => {
const {
key
} = hook;
const { key } = hook;
hook.pluginId = id;
this.hooks[key] = (this.hooks[key] || []).concat(hook);
});
@ -188,11 +174,11 @@ export default class Service extends EventEmitter {
if (this.config.outputPath) {
this.paths.absOutputPath = join(this.cwd, this.config.outputPath);
}
const paths = (await this.applyPlugins({
const paths = await this.applyPlugins({
key: 'modifyPaths',
type: ApplyPluginsType.modify,
initialValue: this.paths
}));
});
Object.keys(paths).forEach((key) => {
this.paths[key] = paths[key];
});
@ -283,11 +269,7 @@ export default class Service extends EventEmitter {
}
async initPlugin(plugin) {
const {
id,
key,
apply
} = plugin;
const { id, key, apply } = plugin;
const api = this.getPluginAPI({
id,
@ -318,10 +300,7 @@ export default class Service extends EventEmitter {
// api.skipPlugins() 的插件
if (this.skipPluginIds.has(pluginId)) return false;
const {
key,
enableBy
} = this.plugins[pluginId];
const { key, enableBy } = this.plugins[pluginId];
// 手动设置为 false
if (this.userConfig[key] === false) return false;
@ -354,16 +333,17 @@ export default class Service extends EventEmitter {
if ('initialValue' in opts) {
assert(
Array.isArray(opts.initialValue),
'applyPlugins failed, opts.initialValue must be Array if opts.type is add.',
'applyPlugins failed, opts.initialValue must be Array if opts.type is add.'
);
}
// eslint-disable-next-line
const tAdd = new AsyncSeriesWaterfallHook(['memo']);
const tAdd = new AsyncSeriesWaterfallHook(["memo"]);
for (const hook of hooks) {
if (!this.isPluginEnable(hook.pluginId)) {
continue;
}
tAdd.tapPromise({
tAdd.tapPromise(
{
name: hook.pluginId,
stage: hook.stage || 0,
// @ts-ignore
@ -372,33 +352,37 @@ export default class Service extends EventEmitter {
async (memo) => {
const items = await hook.fn(opts.args);
return memo.concat(items);
},);
}
);
}
return tAdd.promise(opts.initialValue || []);
case ApplyPluginsType.modify:
// eslint-disable-next-line
const tModify = new AsyncSeriesWaterfallHook(['memo']);
const tModify = new AsyncSeriesWaterfallHook(["memo"]);
for (const hook of hooks) {
if (!this.isPluginEnable(hook.pluginId)) {
continue;
}
tModify.tapPromise({
tModify.tapPromise(
{
name: hook.pluginId,
stage: hook.stage || 0,
// @ts-ignore
before: hook.before
},
async memo => hook.fn(memo, opts.args),);
async memo => hook.fn(memo, opts.args)
);
}
return tModify.promise(opts.initialValue);
case ApplyPluginsType.event:
// eslint-disable-next-line
const tEvent = new AsyncSeriesWaterfallHook(['_']);
const tEvent = new AsyncSeriesWaterfallHook(["_"]);
for (const hook of hooks) {
if (!this.isPluginEnable(hook.pluginId)) {
continue;
}
tEvent.tapPromise({
tEvent.tapPromise(
{
name: hook.pluginId,
stage: hook.stage || 0,
// @ts-ignore
@ -406,20 +390,18 @@ export default class Service extends EventEmitter {
},
async () => {
await hook.fn(opts.args);
},);
}
);
}
return tEvent.promise();
default:
throw new Error(
`applyPlugin failed, type is not defined or is not matched, got ${opts.type}.`,
`applyPlugin failed, type is not defined or is not matched, got ${opts.type}.`
);
}
}
async run({
name,
args = {}
}) {
async run({ name, args = {} }) {
args._ = args._ || [];
// shift the command itself
if (args._[0] === name) args._.shift();
@ -442,10 +424,7 @@ export default class Service extends EventEmitter {
});
}
async runCommand({
name,
args = {}
}) {
async runCommand({ name, args = {} }) {
assert(this.stage >= ServiceStage.init, 'service is not initialized.');
args._ = args._ || [];
@ -457,9 +436,7 @@ export default class Service extends EventEmitter {
: this.commands[name];
assert(command, `run command failed, command ${name} does not exists.`);
const {
fn
} = command;
const { fn } = command;
return fn({
args
});

View File

@ -0,0 +1,134 @@
import assert from 'assert';
import * as utils from '@umijs/utils';
import { isValidPlugin, pathToObj } from './utils/pluginUtils';
import { EnableBy, PluginType, ServiceStage } from './enums';
import Logger from '../logger/logger';
// TODO
// 标准化 logger
export default class PluginAPI {
constructor(opts) {
this.id = opts.id;
this.key = opts.key;
this.service = opts.service;
this.utils = utils;
this.logger = new Logger(`fes:plugin:${this.id || this.key}`);
}
// TODO: reversed keys
describe({
id,
key,
config,
enableBy
} = {}) {
const { plugins } = this.service;
// this.id and this.key is generated automatically
// so we need to diff first
if (id && this.id !== id) {
if (plugins[id]) {
const name = plugins[id].isPreset ? 'preset' : 'plugin';
throw new Error(
`api.describe() failed, ${name} ${id} is already registered by ${plugins[id].path}.`,
);
}
plugins[id] = plugins[this.id];
plugins[id].id = id;
delete plugins[this.id];
this.id = id;
}
if (key && this.key !== key) {
this.key = key;
plugins[this.id].key = key;
}
if (config) {
plugins[this.id].config = config;
}
plugins[this.id].enableBy = enableBy || EnableBy.register;
}
register(hook) {
assert(
hook.key && typeof hook.key === 'string',
`api.register() failed, hook.key must supplied and should be string, but got ${hook.key}.`,
);
assert(
hook.fn && typeof hook.fn === 'function',
`api.register() failed, hook.fn must supplied and should be function, but got ${hook.fn}.`,
);
this.service.hooksByPluginId[this.id] = (
this.service.hooksByPluginId[this.id] || []
).concat(hook);
}
registerCommand(command) {
const { name, alias } = command;
assert(
!this.service.commands[name],
`api.registerCommand() failed, the command ${name} is exists.`,
);
this.service.commands[name] = command;
if (alias) {
this.service.commands[alias] = name;
}
}
// 在 preset 初始化阶段放后面,在插件注册阶段放前面
registerPlugins(plugins) {
assert(
this.service.stage === ServiceStage.initPresets
|| this.service.stage === ServiceStage.initPlugins,
'api.registerPlugins() failed, it should only be used in registering stage.',
);
assert(
Array.isArray(plugins),
'api.registerPlugins() failed, plugins must be Array.',
);
const extraPlugins = plugins.map(plugin => (isValidPlugin(plugin)
? (plugin)
: pathToObj({
type: PluginType.plugin,
path: plugin,
cwd: this.service.cwd
})));
if (this.service.stage === ServiceStage.initPresets) {
this.service._extraPlugins.push(...extraPlugins);
} else {
this.service._extraPlugins.splice(0, 0, ...extraPlugins);
}
}
registerMethod({
name,
fn,
exitsError = true
}) {
if (this.service.pluginMethods[name]) {
if (exitsError) {
throw new Error(
`api.registerMethod() failed, method ${name} is already exist.`,
);
} else {
return;
}
}
this.service.pluginMethods[name] = fn
// 这里不能用 arrow functionthis 需指向执行此方法的 PluginAPI
// 否则 pluginId 会不会,导致不能正确 skip plugin
|| function (hookFn) {
const hook = {
key: name,
...(utils.lodash.isPlainObject(hookFn) ? hookFn : { fn: hookFn })
};
// @ts-ignore
this.register(hook);
};
}
skipPlugins(pluginIds) {
pluginIds.forEach((pluginId) => {
this.service.skipPluginIds.add(pluginId);
});
}
}

View File

@ -0,0 +1,7 @@
export default function isPromise(obj) {
return (
!!obj
&& (typeof obj === 'object' || typeof obj === 'function')
&& typeof obj.then === 'function'
);
}

View File

@ -0,0 +1,18 @@
import { readFileSync, existsSync } from 'fs';
import { parse } from 'dotenv';
/**
* dotenv wrapper
* @param envPath string
*/
export default function loadDotEnv(envPath) {
if (existsSync(envPath)) {
const parsed = parse(readFileSync(envPath, 'utf-8')) || {};
Object.keys(parsed).forEach((key) => {
// eslint-disable-next-line no-prototype-builtins
if (!process.env.hasOwnProperty(key)) {
process.env[key] = parsed[key];
}
});
}
}

View File

@ -0,0 +1,113 @@
import {
dirname, join, basename, relative, extname
} from 'path';
import {
compatESModuleRequire,
resolve,
winPath,
pkgUp,
lodash
} from '@umijs/utils';
const RE = {
plugin: /^(@webank\/)?fes-plugin-/
};
export function isPlugin(name) {
const hasScope = name.charAt(0) === '@';
const re = RE.plugin;
if (hasScope) {
return re.test(name.split('/')[1]) || re.test(name);
}
return re.test(name);
}
export function getPlugins(opts) {
return [
// dependencies
...opts.plugins,
...Object.keys(opts.pkg.devDependencies || {})
.concat(Object.keys(opts.pkg.dependencies || {}))
.filter(isPlugin.bind(null)),
...opts.userConfigPlugins
].map(path => resolve.sync(path, {
basedir: opts.cwd,
extensions: ['.js', '.ts']
}));
}
// e.g.
// initial-state -> initialState
// webpack.css-loader -> webpack.cssLoader
function nameToKey(name) {
return name
.split('.')
.map(part => lodash.camelCase(part))
.join('.');
}
function pkgNameToKey(pkgName) {
if (pkgName.charAt(0) === '@' && !pkgName.startsWith('@webank/')) {
pkgName = pkgName.split('/')[1];
}
return nameToKey(pkgName.replace(RE.plugin, ''));
}
export function pathToObj({ path, cwd }) {
let pkg = null;
let isPkgPlugin = false;
const pkgJSONPath = pkgUp.sync({ cwd: path });
if (pkgJSONPath) {
// eslint-disable-next-line
pkg = require(pkgJSONPath);
isPkgPlugin = winPath(join(dirname(pkgJSONPath), pkg.main || 'index.js'))
=== winPath(path);
}
let id;
if (isPkgPlugin) {
id = pkg.name;
} else if (winPath(path).startsWith(winPath(cwd))) {
id = `./${winPath(relative(cwd, path))}`;
} else if (pkgJSONPath) {
id = winPath(join(pkg.name, relative(dirname(pkgJSONPath), path)));
} else {
id = winPath(path);
}
id = id.replace('@webank/fes-plugin-built-in/lib/plugins', '@@');
id = id.replace(/\.js$/, '');
const key = isPkgPlugin
? pkgNameToKey(pkg.name)
: nameToKey(basename(path, extname(path)));
return {
id,
key,
path: winPath(path),
apply() {
// use function to delay require
try {
// eslint-disable-next-line
const ret = require(path);
// use the default member for es modules
return compatESModuleRequire(ret);
} catch (e) {
throw new Error(`Register ${path} failed, since ${e.message}`);
}
},
defaultConfig: null
};
}
export function resolvePlugins(opts) {
const plugins = getPlugins(opts);
return plugins.map(path => pathToObj({
path,
cwd: opts.cwd
}));
}
export function isValidPlugin(plugin) {
return plugin.id && plugin.key && plugin.apply;
}

View File

@ -10,4 +10,4 @@ export {
createRouter
} from 'vue-router';
export { default as Plugin, ApplyPluginsType } from './Plugin/Plugin';
export { default as Plugin, ApplyPluginsType } from './plugin';

View File

@ -1,4 +1,4 @@
// fes.config.js 只负责管理 cli 相关的配置
// fes.config.js 只负责管理 cli 相关的配置
export default {

View File

@ -1,4 +1,4 @@
// fes.config.js 只负责管理 cli 相关的配置
// fes.config.js 只负责管理 cli 相关的配置
module.exports = {

View File

@ -1404,14 +1404,6 @@
pirates "^4.0.0"
source-map-support "^0.5.9"
"@babel/runtime-corejs3@^7.11.2":
version "7.12.1"
resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.1.tgz#51b9092befbeeed938335a109dbe0df51451e9dc"
integrity sha512-umhPIcMrlBZ2aTWlWjUseW9LjQKxi1dpFlQS8DzsxB//5K+u6GLTC/JliPKHsd5kJVPIU6X/Hy0YvWOYPcMxBw==
dependencies:
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@7.10.4":
version "7.10.4"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz#a6724f1a6b8d2f6ea5236dbfe58c7d7ea9c5eb99"
@ -4930,7 +4922,7 @@ chardet@^0.7.0:
resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
cheerio@1.0.0-rc.3, cheerio@^1.0.0-rc.3:
cheerio@1.0.0-rc.3:
version "1.0.0-rc.3"
resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
@ -5619,11 +5611,6 @@ core-js-compat@^3.1.1, core-js-compat@^3.6.2:
browserslist "^4.8.5"
semver "7.0.0"
core-js-pure@^3.0.0:
version "3.6.5"
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
core-js@3.6.5, core-js@^3.0.0, core-js@^3.6.1:
version "3.6.5"
resolved "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
@ -5742,15 +5729,6 @@ crypto-random-string@^1.0.0:
resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
csp-html-webpack-plugin@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/csp-html-webpack-plugin/-/csp-html-webpack-plugin-4.0.0.tgz#1ed2cd0dee23186587f9642084715b163345b59b"
integrity sha512-1YqQefNG0SrZisysThlly2bgs4Ab/W91xOM17S8wd+6vTo3E0OdL+y4IAR0MKpthRluNGzFB3QhPqdOhkXAExg==
dependencies:
cheerio "^1.0.0-rc.3"
lodash "^4.17.15"
memory-fs "^0.5.0"
css-blank-pseudo@^0.1.4:
version "0.1.4"
resolved "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5"