@ui5/builder
Advanced tools
Comparing version 2.8.0 to 2.8.1
@@ -5,4 +5,14 @@ # Changelog | ||
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v2.8.0...HEAD). | ||
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v2.8.1...HEAD). | ||
<a name="v2.8.1"></a> | ||
## [v2.8.1] - 2021-03-04 | ||
### Bug Fixes | ||
- **generateResourcesJson:** Include dependencies of XML resources [`0fc364d`](https://github.com/SAP/ui5-builder/commit/0fc364ded64eb5bae4085397dc1831e04b19edf4) | ||
- **manifestCreator:** 'embeds' should list all components ([#575](https://github.com/SAP/ui5-builder/issues/575)) [`11f823a`](https://github.com/SAP/ui5-builder/commit/11f823a77e72cfa4c096e7e8f4277a6a6b9400b8) | ||
- **moduleBundler:** Apply defaultFileTypes ([#580](https://github.com/SAP/ui5-builder/issues/580)) [`42f6474`](https://github.com/SAP/ui5-builder/commit/42f64744a299e8548f6dbdb7bcbb8b3cef72f1f4) | ||
- **resourceListCreator:** Include dependencies of subModules ([#588](https://github.com/SAP/ui5-builder/issues/588)) [`fe61d6e`](https://github.com/SAP/ui5-builder/commit/fe61d6eba6671ca31f7c49a7d1281adb6d5f2114) | ||
- **versionInfoGenerator:** fix hasOwnPreload flag ([#591](https://github.com/SAP/ui5-builder/issues/591)) [`73a0f8b`](https://github.com/SAP/ui5-builder/commit/73a0f8baa0248aef3ac6c2b114082aa120ef6e22) | ||
<a name="v2.8.0"></a> | ||
@@ -534,2 +544,3 @@ ## [v2.8.0] - 2021-02-09 | ||
[v2.8.1]: https://github.com/SAP/ui5-builder/compare/v2.8.0...v2.8.1 | ||
[v2.8.0]: https://github.com/SAP/ui5-builder/compare/v2.7.2...v2.8.0 | ||
@@ -536,0 +547,0 @@ [v2.7.2]: https://github.com/SAP/ui5-builder/compare/v2.7.1...v2.7.2 |
@@ -50,3 +50,3 @@ "use strict"; | ||
// ---- resolve module definition | ||
const resolvedModule = await this.resolver.resolve(moduleDef, options); | ||
const resolvedModule = await this.resolver.resolve(moduleDef); | ||
// ---- calculate overall size of merged module | ||
@@ -53,0 +53,0 @@ |
@@ -136,3 +136,3 @@ /* eslint quotes: ["error", "double", { "allowTemplateLiterals": true }] */ | ||
async _createBundle(module, options) { | ||
const resolvedModule = await this.resolver.resolve(module, options); | ||
const resolvedModule = await this.resolver.resolve(module); | ||
if ( options.skipIfEmpty && isEmptyBundle(resolvedModule) ) { | ||
@@ -139,0 +139,0 @@ log.verbose(" skipping empty bundle " + module.name); |
@@ -16,2 +16,13 @@ /** | ||
const DEFAULT_FILE_TYPES = [ | ||
".js", | ||
".control.xml", // XMLComposite | ||
".fragment.html", | ||
".fragment.json", | ||
".fragment.xml", | ||
".view.html", | ||
".view.json", | ||
".view.xml" | ||
]; | ||
/** | ||
@@ -36,9 +47,7 @@ * Resolve a bundle definition. | ||
* @param {ModuleDefinition} bundle Bundle definition to resolve | ||
* @param {object} [options] Options | ||
* @param {string[]} [options.defaultFileTypes] | ||
List of default file types to which a prefix pattern shall be expanded. | ||
* @returns {Promise<ResolvedBundleDefinition>} | ||
*/ | ||
resolve(bundle, options) { | ||
const fileTypes = (options && options.defaultFileTypes) || undefined; | ||
resolve(bundle) { | ||
const fileTypes = bundle.defaultFileTypes || DEFAULT_FILE_TYPES; | ||
let visitedResources = Object.create(null); | ||
@@ -45,0 +54,0 @@ let selectedResources = Object.create(null); |
@@ -112,3 +112,3 @@ const ResourceInfoList = require("./ResourceInfoList"); | ||
async enrichWithDependencyInfo(resourceInfo) { | ||
return this._pool.getModuleInfo(resourceInfo.name).then((moduleInfo) => { | ||
return this._pool.getModuleInfo(resourceInfo.name).then(async (moduleInfo) => { | ||
if ( moduleInfo.name ) { | ||
@@ -120,2 +120,3 @@ resourceInfo.module = moduleInfo.name; | ||
} | ||
if ( moduleInfo.dependencies.length > 0 ) { | ||
@@ -132,2 +133,3 @@ resourceInfo.required = resourceInfo.required || new Set(); | ||
} | ||
if ( moduleInfo.subModules.length > 0 ) { | ||
@@ -138,2 +140,41 @@ resourceInfo.included = resourceInfo.included || new Set(); | ||
}); | ||
await Promise.all(moduleInfo.subModules.map(async (subModule) => { | ||
// Try to inherit dependency info | ||
let subModuleInfo; | ||
try { | ||
subModuleInfo = await this._pool.getModuleInfo(subModule); | ||
} catch (err) { | ||
log.verbose(` missing submodule ${subModule} included by ${moduleInfo.name}`); | ||
} | ||
if (subModuleInfo) { | ||
// Inherit subModule dependencies | ||
if ( subModuleInfo.dependencies.length > 0 ) { | ||
resourceInfo.required = resourceInfo.required || new Set(); | ||
resourceInfo.condRequired = resourceInfo.condRequired || new Set(); | ||
subModuleInfo.dependencies.forEach((dep) => { | ||
if (resourceInfo.included.has(dep)) { | ||
// Don't add dependency if module is already listed as "included" | ||
return; | ||
} | ||
if ( subModuleInfo.isConditionalDependency(dep) ) { | ||
// Avoid having module listed in both required and condRequired | ||
if (!resourceInfo.required.has(dep)) { | ||
resourceInfo.condRequired.add(dep); | ||
} | ||
} else if ( !subModuleInfo.isImplicitDependency(dep) ) { | ||
// Move module from condRequired to required | ||
if (resourceInfo.condRequired.has(dep)) { | ||
resourceInfo.condRequired.delete(dep); | ||
} | ||
resourceInfo.required.add(dep); | ||
} | ||
}); | ||
} | ||
// Inherit dynamicDependencies flag | ||
if ( moduleInfo.dynamicDependencies ) { | ||
resourceInfo.dynRequired = true; | ||
} | ||
} | ||
})); | ||
} | ||
@@ -140,0 +181,0 @@ |
@@ -185,6 +185,6 @@ "use strict"; | ||
if ( info == null ) { | ||
// console.log("analyzing ", name); | ||
const resource = await this.findResource(name); | ||
info = await determineDependencyInfo( resource, this._rawModuleInfos.get(name), this ); | ||
// console.log("finished analyzing ", name); | ||
info = Promise.resolve().then(async () => { | ||
const resource = await this.findResource(name); | ||
return determineDependencyInfo( resource, this._rawModuleInfos.get(name), this ); | ||
}); | ||
this._dependencyInfos.set(name, info); | ||
@@ -191,0 +191,0 @@ } |
@@ -43,6 +43,6 @@ const BundleBuilder = require("../../lbt/bundle/Builder"); | ||
* in- or excluded. | ||
* A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisks, | ||
* A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisk, | ||
* denote an arbitrary number of characters or folder names. | ||
* Excludes should be marked with a leading exclamation mark '!'. The order of filters is relevant, a later | ||
* exclusion overrides an earlier inclusion and vice versa. | ||
* Excludes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later | ||
* exclusion overrides an earlier inclusion, and vice versa. | ||
* @example <caption>List of modules as glob patterns that should be in- or excluded</caption> | ||
@@ -68,2 +68,3 @@ * // Includes everything from "some/path/to/module/", | ||
/* eslint-disable max-len */ | ||
/** | ||
@@ -75,6 +76,7 @@ * Module bundle definition | ||
* @property {string} name The module bundle name | ||
* @property {string[]} [defaultFileTypes=[".js", ".fragment.xml", ".view.xml", ".properties", ".json"]] | ||
* @property {string[]} [defaultFileTypes=[".js", ".control.xml", ".fragment.html", ".fragment.json", ".fragment.xml", ".view.html", ".view.json", ".view.xml"]] | ||
* List of default file types to be included in the bundle | ||
* @property {ModuleBundleDefinitionSection[]} sections List of module bundle definition sections. | ||
*/ | ||
/* eslint-enable max-len */ | ||
@@ -81,0 +83,0 @@ /** |
@@ -180,3 +180,3 @@ "use strict"; | ||
if ( descriptorVersion.compare(APP_DESCRIPTOR_V5) < 0 ) { | ||
return candidateVersion; | ||
return candidateVersion.toString(); | ||
} | ||
@@ -186,4 +186,4 @@ // return undefined | ||
async function createSapApp() { | ||
async function isEmbeddedByLibrary(componentPath, libraryPathPrefix) { | ||
function createSapApp() { | ||
function hasManifest(componentPath, libraryPathPrefix) { | ||
const manifestPath = componentPath + "/manifest.json"; | ||
@@ -196,57 +196,13 @@ | ||
} | ||
const manifestString = await manifestResource.getString(); | ||
let manifest; | ||
try { | ||
manifest = JSON.parse(manifestString); | ||
} catch (err) { | ||
log.error( | ||
" component '%s': failed to read the component's manifest.json, " + | ||
"it won't be listed as 'embedded'.\nError details: %s", componentPath, err.stack); | ||
return false; | ||
} | ||
let embeddedBy; | ||
if (manifest && manifest["sap.app"]) { | ||
embeddedBy = manifest["sap.app"]["embeddedBy"]; | ||
} | ||
if (typeof embeddedBy === "undefined") { | ||
log.verbose(" component doesn't declare 'sap.app/embeddedBy', don't list it as 'embedded'"); | ||
return false; | ||
} | ||
if (typeof embeddedBy !== "string") { | ||
log.error( | ||
" component '%s': property 'sap.app/embeddedBy' is of type '%s' (expected 'string'), " + | ||
"it won't be listed as 'embedded'", componentPath, typeof embeddedBy | ||
); | ||
return false; | ||
} | ||
if ( !embeddedBy.length ) { | ||
log.error( | ||
" component '%s': property 'sap.app/embeddedBy' has an empty string value (which is invalid), " + | ||
"it won't be listed as 'embedded'", componentPath | ||
); | ||
return false; | ||
} | ||
let resolvedEmbeddedBy = posixPath.resolve(componentPath, embeddedBy); | ||
if ( resolvedEmbeddedBy && !resolvedEmbeddedBy.endsWith("/") ) { | ||
resolvedEmbeddedBy = resolvedEmbeddedBy + "/"; | ||
} | ||
if ( libraryPathPrefix === resolvedEmbeddedBy ) { | ||
log.verbose(" component's 'sap.app/embeddedBy' property points to library, list it as 'embedded'"); | ||
return true; | ||
} else { | ||
log.verbose( | ||
" component's 'sap.app/embeddedBy' points to '%s', don't list it as 'embedded'", resolvedEmbeddedBy | ||
); | ||
return false; | ||
} | ||
return true; | ||
} | ||
async function findEmbeddedComponents() { | ||
function findEmbeddedComponents() { | ||
const result = []; | ||
const prefix = libraryResource.getPath().slice(0, - ".library".length); | ||
const prefix = posixPath.dirname(libraryResource.getPath()) + "/"; | ||
const components = libBundle.getResources(/^\/(?:[^/]+\/)*Component\.js$/); | ||
for (const comp of components) { | ||
const componentPath = comp.getPath().slice(0, - "Component.js".length - 1); | ||
const componentPath = posixPath.dirname(comp.getPath()); | ||
log.verbose("checking component at %s", componentPath); | ||
if ( componentPath.startsWith(prefix) && await isEmbeddedByLibrary(componentPath, prefix) ) { | ||
if ( componentPath.startsWith(prefix) && hasManifest(componentPath, prefix) ) { | ||
result.push( componentPath.substring(prefix.length) ); | ||
@@ -364,3 +320,3 @@ } else if ( prefix === "/resources/sap/apf/" ) { | ||
type: "library", | ||
embeds: await findEmbeddedComponents(), | ||
embeds: findEmbeddedComponents(), | ||
i18n, | ||
@@ -436,7 +392,2 @@ applicationVersion: { | ||
function getUI5Version() { | ||
let ui5Version; | ||
if ( ui5Version != null ) { | ||
return ui5Version; | ||
} | ||
const dummy = new Dependency({ | ||
@@ -697,3 +648,3 @@ libraryName: [{ | ||
"_version": descriptorVersion.toString(), | ||
"sap.app": await createSapApp(), | ||
"sap.app": createSapApp(), | ||
"sap.ui": createSapUi(), | ||
@@ -710,3 +661,3 @@ "sap.ui5": createSapUI5(), | ||
options = Object.assign({ | ||
descriptorVersion: APP_DESCRIPTOR_V22, | ||
descriptorVersion: APP_DESCRIPTOR_V22, // TODO change this to type string instead of a semver object | ||
include3rdParty: true, | ||
@@ -722,3 +673,3 @@ prettyPrint: true | ||
if ( manifestResource != null ) { | ||
log.info("Library manifest already exists at '%s', skipping generation", manifestResource.getPath()); | ||
log.verbose("Library manifest already exists at '%s', skipping generation", manifestResource.getPath()); | ||
return Promise.resolve(null); // a fulfillment of null indicates that no manifest has been created | ||
@@ -725,0 +676,0 @@ } |
@@ -130,6 +130,7 @@ "use strict"; | ||
* @param {module:@ui5/fs.Resource[]} parameters.resources List of resources | ||
* @param {module:@ui5/fs.Resource[]} [parameters.dependencyResources=[]] List of dependency resources | ||
* @param {object} [parameters.options] Options | ||
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving with the resources.json resources | ||
*/ | ||
module.exports = async function({resources, options}) { | ||
module.exports = async function({resources, dependencyResources = [], options}) { | ||
options = Object.assign({ | ||
@@ -147,2 +148,3 @@ failOnOrphans: false, | ||
await pool.prepare( resources ); | ||
await pool.prepare( dependencyResources ); | ||
@@ -149,0 +151,0 @@ const collector = new ResourceCollector(pool); |
@@ -257,7 +257,7 @@ const log = require("@ui5/logger").getLogger("builder:processors:versionInfoGenerator"); | ||
* Common type for Library and Component | ||
* embeds and bundled components makes only sense for library | ||
* embeds and bundled components make only sense for library | ||
* | ||
* @typedef {object} ArtifactInfo | ||
* @property {string} componentName The library name, e.g. "lib.x" | ||
* @property {Set<string>} bundledComponents The embedded components which have a embeddedBy reference to the library | ||
* @property {Set<string>} bundledComponents The embedded components which have an embeddedBy reference to the library | ||
* @property {DependencyInfo} dependencyInfo The dependency info object | ||
@@ -427,6 +427,10 @@ * @property {ArtifactInfo[]} embeds The embedded artifact infos | ||
artifactInfo.embeds.forEach((embeddedArtifactInfo) => { | ||
const componentObject = { | ||
library: artifactInfo.componentName | ||
}; | ||
const componentObject = {}; | ||
const bundledComponents = artifactInfo.bundledComponents; | ||
const componentName = embeddedArtifactInfo.componentName; | ||
if (!bundledComponents.has(componentName)) { | ||
componentObject.hasOwnPreload = true; | ||
} | ||
componentObject.library = artifactInfo.componentName; | ||
const manifestHints = getManifestHints(embeddedArtifactInfo.dependencyInfo, dependencyInfoMap); | ||
@@ -436,6 +440,3 @@ if (manifestHints) { | ||
} | ||
const bundledComponents = artifactInfo.bundledComponents; | ||
if (bundledComponents.has(componentName)) { | ||
componentObject.hasOwnPreload = true; | ||
} | ||
components = components || {}; | ||
@@ -442,0 +443,0 @@ components[componentName] = componentObject; |
@@ -20,6 +20,6 @@ const path = require("path"); | ||
* that should be excluded. | ||
* A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisks, | ||
* A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisk, | ||
* denote an arbitrary number of characters or folder names. | ||
* Re-includes should be marked with a leading exclamation mark '!'. The order of filters is relevant, a later | ||
* inclusion overrides an earlier exclusion and vice versa. | ||
* Re-includes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later | ||
* inclusion overrides an earlier exclusion, and vice versa. | ||
* @param {string[]} [parameters.options.paths] Array of paths (or glob patterns) for component files | ||
@@ -71,4 +71,6 @@ * @param {string[]} [parameters.options.namespaces] Array of component namespaces | ||
`${namespace}/`, | ||
`!${namespace}/test/`, | ||
`!${namespace}/*.html` | ||
`${namespace}/**/manifest.json`, | ||
`${namespace}/changes/changes-bundle.json`, | ||
`${namespace}/changes/flexibility-bundle.json`, | ||
`!${namespace}/test/` | ||
]; | ||
@@ -90,2 +92,5 @@ | ||
filters.push(`!${ns}/`); | ||
// Explicitly exclude manifest.json files of subcomponents since the general exclude above this | ||
// comment only applies to the configured default file types, which do not include ".json" | ||
filters.push(`!${ns}/**/manifest.json`); | ||
} | ||
@@ -96,3 +101,13 @@ }); | ||
name: `${namespace}/Component-preload.js`, | ||
defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], | ||
defaultFileTypes: [ | ||
".js", | ||
".control.xml", | ||
".fragment.html", | ||
".fragment.json", | ||
".fragment.xml", | ||
".view.html", | ||
".view.json", | ||
".view.xml", | ||
".properties" | ||
], | ||
sections: [ | ||
@@ -99,0 +114,0 @@ { |
@@ -6,25 +6,10 @@ const log = require("@ui5/logger").getLogger("builder:tasks:bundlers:generateLibraryPreload"); | ||
const DEFAULT_FILE_TYPES = [ | ||
".js", | ||
".control.xml", // XMLComposite | ||
".fragment.html", | ||
".fragment.json", | ||
".fragment.xml", | ||
".view.html", | ||
".view.json", | ||
".view.xml", | ||
".properties", | ||
".json" | ||
]; | ||
function getDefaultLibraryPreloadFilters(namespace, excludes) { | ||
const filters = [ | ||
`${namespace}/`, | ||
`!${namespace}/.library`, | ||
`${namespace}/**/manifest.json`, | ||
`!${namespace}/*-preload.js`, // exclude all bundles | ||
`!${namespace}/designtime/`, | ||
`!${namespace}/**/*.designtime.js`, | ||
`!${namespace}/**/*.support.js`, | ||
`!${namespace}/themes/`, | ||
`!${namespace}/messagebundle*` | ||
`!${namespace}/**/*.support.js` | ||
]; | ||
@@ -54,3 +39,2 @@ | ||
name: `${namespace}/library-preload.js`, | ||
defaultFileTypes: DEFAULT_FILE_TYPES, | ||
sections: [ | ||
@@ -111,3 +95,2 @@ { | ||
name: `${namespace}/library-preload.js`, | ||
defaultFileTypes: DEFAULT_FILE_TYPES, | ||
sections: [ | ||
@@ -128,3 +111,2 @@ { | ||
name: `${namespace}/designtime/library-preload.designtime.js`, | ||
defaultFileTypes: DEFAULT_FILE_TYPES, | ||
sections: [ | ||
@@ -152,3 +134,2 @@ { | ||
name: `${namespace}/library-preload.support.js`, | ||
defaultFileTypes: DEFAULT_FILE_TYPES, | ||
sections: [ | ||
@@ -290,6 +271,6 @@ { | ||
* that should be excluded from the library-preload.js bundle. | ||
* A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisks, | ||
* A pattern ending with a slash '/' will, similarly to the use of a single '*' or double '**' asterisk, | ||
* denote an arbitrary number of characters or folder names. | ||
* Re-includes should be marked with a leading exclamation mark '!'. The order of filters is relevant, a later | ||
* inclusion overrides an earlier exclusion and vice versa. | ||
* Re-includes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later | ||
* inclusion overrides an earlier exclusion, and vice versa. | ||
* @param {object} parameters.options Options | ||
@@ -296,0 +277,0 @@ * @param {string} parameters.options.projectName Project name |
@@ -7,3 +7,13 @@ const log = require("@ui5/logger").getLogger("builder:tasks:bundlers:generateStandaloneAppBundle"); | ||
name: config.name, | ||
defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], | ||
defaultFileTypes: [ | ||
".js", | ||
".control.xml", | ||
".fragment.html", | ||
".fragment.json", | ||
".fragment.xml", | ||
".view.html", | ||
".view.json", | ||
".view.xml", | ||
".properties" | ||
], | ||
sections: [] | ||
@@ -27,5 +37,7 @@ }; | ||
filters: [ | ||
`${config.namespace|| ""}/`, | ||
`${config.namespace || ""}/`, | ||
`${config.namespace || ""}/**/manifest.json`, | ||
`${config.namespace || ""}/changes/changes-bundle.json`, | ||
`${config.namespace || ""}/changes/flexibility-bundle.json`, | ||
`!${config.namespace || ""}/test/`, | ||
`!${config.namespace || ""}/*.html`, | ||
"sap/ui/core/Core.js" | ||
@@ -32,0 +44,0 @@ ], |
@@ -111,2 +111,3 @@ "use strict"; | ||
* @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files | ||
* @param {module:@ui5/fs.AbstractReader} [parameters.dependencies] Reader or Collection to read dependency files | ||
* @param {object} parameters.options Options | ||
@@ -116,8 +117,16 @@ * @param {string} parameters.options.projectName Project name | ||
*/ | ||
module.exports = async function({workspace, options: {projectName}}) { | ||
module.exports = async function({workspace, dependencies, options: {projectName}}) { | ||
const resources = await workspace.byGlob(["/resources/**/*.*"].concat(DEFAULT_EXCLUDES)); | ||
// TODO 3.0: Make dependencies parameter mandatory | ||
let dependencyResources; | ||
if (dependencies) { | ||
dependencyResources = | ||
await dependencies.byGlob("/resources/**/*.{js,json,xml,html,properties,library}"); | ||
} | ||
const resourceLists = await resourceListCreator({ | ||
resources, | ||
options: getCreatorOptions(projectName) | ||
dependencyResources, | ||
options: getCreatorOptions(projectName), | ||
}); | ||
@@ -124,0 +133,0 @@ await Promise.all( |
{ | ||
"name": "@ui5/builder", | ||
"version": "2.8.0", | ||
"version": "2.8.1", | ||
"description": "UI5 Tooling - Builder", | ||
@@ -116,3 +116,3 @@ "author": { | ||
"globby": "^11.0.2", | ||
"graceful-fs": "^4.2.5", | ||
"graceful-fs": "^4.2.6", | ||
"jsdoc": "^3.6.6", | ||
@@ -126,3 +126,3 @@ "less-openui5": "^0.10.0", | ||
"semver": "^7.3.4", | ||
"terser": "^5.5.1", | ||
"terser": "^5.6.0", | ||
"xml2js": "^0.4.23", | ||
@@ -137,7 +137,7 @@ "yazl": "^2.5.1" | ||
"cross-env": "^7.0.3", | ||
"depcheck": "^1.3.1", | ||
"depcheck": "^1.4.0", | ||
"docdash": "^1.2.0", | ||
"eslint": "^7.19.0", | ||
"eslint": "^7.21.0", | ||
"eslint-config-google": "^0.14.0", | ||
"eslint-plugin-jsdoc": "^31.6.0", | ||
"eslint-plugin-jsdoc": "^31.6.1", | ||
"extract-zip": "^2.0.1", | ||
@@ -144,0 +144,0 @@ "mock-require": "^3.0.3", |
796529
20727
Updatedgraceful-fs@^4.2.6
Updatedterser@^5.6.0