Merge branch 'main' into feature-14072

# Conflicts:
#	lib/DependencyTemplate.js
#	lib/javascript/JavascriptGenerator.js
#	schemas/WebpackOptions.check.js
#	types.d.ts
This commit is contained in:
Ivan Kopeykin 2022-04-05 09:26:24 +03:00
commit d6d6fb584d
501 changed files with 13660 additions and 2923 deletions

1
.gitattributes vendored
View File

@ -3,4 +3,5 @@ test/statsCases/** eol=lf
examples/* eol=lf
bin/* eol=lf
*.svg eol=lf
*.css eol=lf
**/*webpack.lock.data/** -text

View File

@ -1,74 +0,0 @@
dist: trusty
language: node_js
branches:
only:
- webpack-4
- main
- next
- dev-1
cache:
yarn: true
directories:
- ".jest-cache"
- ".eslintcache"
stages:
- basic
- advanced
matrix:
include:
- os: linux
node_js: "12"
env: NO_WATCH_TESTS=1 JEST="--maxWorkers=2 --cacheDirectory .jest-cache" JOB_PART=basic
stage: basic
- os: linux
node_js: "12"
env: NO_WATCH_TESTS=1 JEST="--maxWorkers=2 --cacheDirectory .jest-cache" JOB_PART=lintunit
stage: advanced
- os: linux
node_js: "12"
env: NO_WATCH_TESTS=1 JEST="--maxWorkers=2 --cacheDirectory .jest-cache" JOB_PART=integration
stage: advanced
- os: linux
node_js: "12"
env: NO_WATCH_TESTS=1 ALTERNATIVE_SORT=1 JEST="--maxWorkers=2 --cacheDirectory .jest-cache" JOB_PART=integration
stage: advanced
- os: linux
node_js: "10"
env:
- NODEJS_VERSION=v15.0.0-nightly2020082003293aa3a1
- YARN_EXTRA_ARGS="--ignore-engines"
- NO_WATCH_TESTS=1
- JEST="--maxWorkers=2 --cacheDirectory .jest-cache"
- JOB_PART=integration
stage: advanced
fast_finish: true
before_install:
- |
if [ "$NODEJS_VERSION" != "" ];
then
mkdir /opt/node
curl --silent "https://nodejs.org/download/nightly/$NODEJS_VERSION/node-$NODEJS_VERSION-linux-x64.tar.gz" | tar -zxf - --directory /opt/node
export PATH="/opt/node/node-$NODEJS_VERSION-linux-x64/bin:$PATH"
node --version
fi
install:
- yarn --frozen-lockfile $YARN_EXTRA_ARGS
- yarn link --frozen-lockfile $YARN_EXTRA_ARGS || true
- yarn link webpack --frozen-lockfile $YARN_EXTRA_ARGS
script: yarn travis:$JOB_PART
after_success:
- cat ./coverage/lcov.info | node_modules/.bin/coveralls --verbose
- bash <(curl -s https://codecov.io/bash) -F $JOB_PART -X gcov
- rm -f .jest-cache/haste-map* .jest-cache/perf-cache*
notifications:
slack:
secure: JduSdKWwbnLCwo7Z4E59SGE+Uw832UwnXzQiKEpg1BV45MYDPRiGltly1tRHmPh9OGjvGx3XSkC2tNGOBLtL4UL2SCkf012x0t7jDutKRfcv/njynl8jk8l+UhPmaWiHXDQAgGiiKdL4RfzPLW3HeVHCOWm0LKMzcarTa8tw+rE=

View File

@ -687,7 +687,7 @@ src="https://static.monei.net/monei-logo.svg" height="30" alt="MONEI"></a>
- [@google](https://github.com/google) for [Google Web Toolkit (GWT)](http://www.gwtproject.org/), which aims to compile Java to JavaScript. It features a similar [Code Splitting](http://www.gwtproject.org/doc/latest/DevGuideCodeSplitting.html) as webpack.
- [@medikoo](https://github.com/medikoo) for [modules-webmake](https://github.com/medikoo/modules-webmake), which is a similar project. webpack was born because I wanted Code Splitting for modules-webmake. Interestingly the [Code Splitting issue is still open](https://github.com/medikoo/modules-webmake/issues/7) (thanks also to @Phoscur for the discussion).
- [@substack](https://github.com/substack) for [browserify](http://browserify.org/), which is a similar project and source for many ideas.
- [@substack](https://github.com/substack) for [browserify](https://browserify.org/), which is a similar project and source for many ideas.
- [@jrburke](https://github.com/jrburke) for [require.js](https://requirejs.org/), which is a similar project and source for many ideas.
- [@defunctzombie](https://github.com/defunctzombie) for the [browser-field spec](https://github.com/defunctzombie/package-browser-field-spec), which makes modules available for node.js, browserify and webpack.
- Every early webpack user, which contributed to webpack by writing issues or PRs. You influenced the direction...

View File

@ -1,241 +1,308 @@
{
"version": "0.1",
"version": "0.2",
"language": "en",
"words": [
"webpack",
"webpack's",
"endregion",
"entrypoint",
"entrypoints",
"splitted",
"dedupe",
"deduplication",
"deduplicating",
"unoptimized",
"prefetch",
"prefetching",
"prefetched",
"preload",
"preloading",
"preloaded",
"gzipping",
"brotli",
"submodule",
"submodules",
"typeof",
"devtool",
"wasi",
"wasm",
"webassembly",
"IIFE",
"IIFE's",
"bigint",
"unexception",
"etag",
"builtins",
"uncacheable",
"cacheable",
"mergeable",
"devtools",
"transpiled",
"mixins",
"subdir",
"errored",
"eval",
"multiplicator",
"finalizer",
"promisify",
"absolutify",
"camelcase",
"filebase",
"moduleid",
"modulehash",
"chunkfilename",
"chunkname",
"chunkhash",
"fullhash",
"contenthash",
"hashable",
"hotupdatechunk",
"middleware",
"middlewares",
"serializer",
"serializers",
"deserialization",
"referenceable",
"polyfilled",
"transpiling",
"transpile",
"transpiles",
"snapshotting",
"sourcemap",
"nosources",
"filepath",
"subpath",
"pathinfo",
"undelayed",
"microtask",
"microtasks",
"deopt",
"deopts",
"sandboxed",
"mangleable",
"passthrough",
"prioritise",
"booleanized",
"serializables",
"unreviewed",
"unshifted",
"nonrecursive",
"symlinked",
"subfolder",
"prettierrc",
"templated",
"templating",
"kibibytes",
"mebibytes",
"gibibytes",
"typechecker",
"recurse",
"preparsed",
"acircular",
"amdmodule",
"asmjs",
"assemblyscript",
"asyncloader",
"atlaskit",
"autocrlf",
"lcov",
"lcovonly",
"gcov",
"lintunit",
"instanceof",
"loglevel",
"runtime",
"runtimes",
"mimetype",
"testvalue",
"unusedkey",
"unusedvalue",
"performant",
"watchings",
"jsons",
"exitance",
"ident",
"idents",
"globstar",
"inversed",
"concated",
"RBDT",
"opensource",
"babeljs",
"backport",
"backported",
"basictest",
"bigint",
"Biró",
"bitfield",
"bomfile",
"booleanized",
"boolish",
"brotli",
"browserified",
"browserslist",
"browserslistrc",
"Brumme",
"bugfix",
"bugfixes",
"declarators",
"rrrlll",
"undefine",
"finializer",
"quasis",
"hashs",
"functype",
"funcindex",
"Descr",
"valtype",
"informations",
"reexecuted",
"global's",
"unsplittable",
"builtins",
"cacheable",
"callme",
"camelcase",
"chainable",
"metacharacters",
"Xarray",
"Xfactory",
"Xmodule",
"Xexports",
"moji",
"bitfield",
"precompute",
"toplevel",
"modulos",
"untaint",
"ufeff",
"timestamping",
"loadername",
"laof",
"chunkfilename",
"chunkhash",
"chunkname",
"cmodule",
"codecov",
"cofounder",
"hashchange",
"popstate",
"hotpink",
"navigations",
"Collet's",
"compat",
"noimport",
"tmpl",
"concated",
"contenthash",
"contextifies",
"crossorigin",
"csvg",
"repo",
"repos",
"triaging",
"valign",
"returnfalse",
"return'development",
"datastructures",
"prewalking",
"overridables",
"overridable",
"cujojs",
"Dani",
"darkblue",
"darkgreen",
"darkred",
"eqeqeq",
"boolish",
"analysing",
"etags",
"datastructures",
"declarators",
"dedupe",
"deduplicating",
"deduplication",
"defunctzombie",
"deopt",
"deopts",
"dependabot",
"Descr",
"deserialization",
"destructure",
"onconnect",
"nwjs",
"redeclaration",
"kaios",
"parallelism",
"gitattributes",
"hashbang",
"webassemblyjs",
"assemblyscript",
"fsevents",
"watchpack",
"tapable",
"junit",
"memfs",
"rimraf",
"devtool",
"devtools",
"donotcallme",
"endregion",
"entrypoint",
"entrypoints",
"Eoksni",
"eqeqeq",
"errored",
"esmodule",
"estree",
"posthtml",
"etag",
"etags",
"eval",
"Ewald",
"exitance",
"filebase",
"fileoverview",
"filepath",
"finalizer",
"finializer",
"fsevents",
"fullhash",
"funcindex",
"functype",
"gcov",
"gibibytes",
"gitattributes",
"gitter",
"global's",
"globstar",
"gzipping",
"hashable",
"hashbang",
"hashchange",
"hashs",
"hotpink",
"hotupdatechunk",
"ident",
"idents",
"IIFE's",
"IIFE",
"informations",
"instanceof",
"inversed",
"jhnns",
"jrburke",
"jsfile",
"jsons",
"junit",
"Junya",
"kaios",
"Kees",
"kibibytes",
"Kluskens",
"Koppers",
"laof",
"Larkin",
"lcov",
"lcovonly",
"lintunit",
"loadername",
"loglevel",
"longnameforexport",
"mangleable",
"MCEP",
"mebibytes",
"medikoo",
"membertest",
"memfs",
"mergeable",
"metacharacters",
"microtask",
"microtasks",
"middleware",
"middlewares",
"mimetype",
"mixins",
"modulehash",
"moduleid",
"modulos",
"moji",
"MONEI",
"mult",
"multiplicator",
"mylibrary",
"mynamespace",
"navigations",
"nmodule",
"noimport",
"nonexistentfile",
"nonrecursive",
"nosource",
"nosources",
"nwjs",
"onconnect",
"opencollective",
"opensource",
"opuuus",
"overridable",
"overridables",
"parallelism",
"passthrough",
"pathinfo",
"performant",
"Phoscur",
"pmodule",
"pnpm",
"polyfilled",
"popstate",
"posthtml",
"precompute",
"prefetch",
"prefetched",
"prefetching",
"preload",
"preloaded",
"preloading",
"preparsed",
"prettierrc",
"prewalking",
"prioritise",
"promisify",
"quasis",
"queryloader",
"querystrings",
"RBDT",
"recurse",
"redeclaration",
"reexecuted",
"referenceable",
"referencer",
"repo",
"repos",
"return'development",
"returnfalse",
"revparse",
"rimraf",
"Rivest",
"rrrlll",
"runtime",
"runtimes",
"samsunginternet",
"sandboxed",
"serializables",
"serializer",
"serializers",
"shama",
"skypack",
"snapshotting",
"sokra",
"somepackage",
"somepath",
"sourcemap",
"sourcemapped",
"splitted",
"stylesheet",
"subdir",
"subfolder",
"submodule",
"submodules",
"subpath",
"substack",
"symlinked",
"syncloader",
"systemjs",
"tapable",
"templated",
"templating",
"testcase",
"testlink",
"testloader",
"testvalue",
"timestamping",
"tmpl",
"toplevel",
"traceur",
"atlaskit",
"transpile",
"transpiled",
"transpiles",
"transpiling",
"triaging",
"Tshs",
"typechecker",
"typeof",
"ufeff",
"uncacheable",
"undefine",
"undelayed",
"unexception",
"unoptimized",
"unreviewed",
"unshifted",
"unsplittable",
"untaint",
"unusedkey",
"unusedvalue",
"url's",
"valign",
"valtype",
"wasi",
"wasm",
"watchings",
"watchpack",
"webassembly",
"webassemblyjs",
"webmake",
"webpack's",
"webpack",
"Xarray",
"Xexports",
"Xfactory",
"Xmodule",
"xxhash",
"xxhashjs",
"systemjs",
"skypack",
"sokra",
"Koppers",
"Junya",
"Eoksni",
"Ewald",
"Larkin",
"Kees",
"Kluskens",
"Phoscur",
"defunctzombie",
"shama",
"jhnns",
"substack",
"MONEI",
"medikoo",
"webmake",
"jrburke",
"gitter",
"codecov",
"opencollective",
"dependabot",
"browserslist",
"samsunginternet",
"pnpm"
"Yann"
],
"ignoreRegExpList": [
"/Author.+/",
"/data:.*/",
"/\"mappings\":\".+\"/",
"/toMatchInlineSnapshot\\(\\s*`[^`]*`\\s*\\)/"
],
"ignoreRegExpList": ["/Author.+/", "/data:.*/", "/\"mappings\":\".+\"/"],
"ignorePaths": [
"**/dist/**",
"examples/**/README.md",
"**/webpack.lock.data/**"
"**/webpack.lock.data/**",
"package.json",
"yarn.lock",
"types.d.ts",
"**/**/*.snap",
"test/cases/json/weird-properties/globals.json",
"**/*.svg",
"*.log",
"**/*.wasm",
"coverage/**",
"test/**/module.js",
"test/js/**",
"test/cases/**",
"test/configCases/**",
"test/statsCases/**",
"test/fixtures/**"
]
}

2
declarations.d.ts vendored
View File

@ -204,7 +204,7 @@ declare module "@webassemblyjs/ast" {
raw?: string
): FloatLiteral;
export function global(globalType: string, nodes: Node[]): Global;
export function identifier(indentifier: string): Identifier;
export function identifier(identifier: string): Identifier;
export function funcParam(valType: string, id: Identifier): FuncParam;
export function instruction(inst: string, args?: Node[]): Instruction;
export function callInstruction(funcIndex: Index): CallInstruction;

View File

@ -95,7 +95,7 @@ export type LibraryExport = string[] | string;
*/
export type LibraryName = string[] | string | LibraryCustomUmdObject;
/**
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
*/
export type LibraryType =
| (
@ -110,6 +110,7 @@ export type LibraryType =
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "commonjs-static"
| "amd"
| "amd-require"
| "umd"
@ -189,6 +190,7 @@ export type ExternalsType =
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "commonjs-static"
| "amd"
| "amd-require"
| "umd"
@ -443,6 +445,14 @@ export type CompareBeforeEmit = boolean;
* This option enables cross-origin loading of chunks.
*/
export type CrossOriginLoading = false | "anonymous" | "use-credentials";
/**
* Specifies the filename template of non-initial output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
*/
export type CssChunkFilename = FilenameTemplate;
/**
* Specifies the filename template of output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
*/
export type CssFilename = FilenameTemplate;
/**
* Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers.
*/
@ -671,7 +681,7 @@ export type AssetGeneratorDataUrl =
| AssetGeneratorDataUrlOptions
| AssetGeneratorDataUrlFunction;
/**
* Function that executes for module and should return an DataUrl string.
* Function that executes for module and should return an DataUrl string. It can have a string as 'ident' property which contributes to the module hash.
*/
export type AssetGeneratorDataUrlFunction = (
source: string | Buffer,
@ -682,6 +692,15 @@ export type AssetGeneratorDataUrlFunction = (
*/
export type AssetGeneratorOptions = AssetInlineGeneratorOptions &
AssetResourceGeneratorOptions;
/**
* Emit the asset in the specified folder relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.
*/
export type AssetModuleOutputPath =
| string
| ((
pathData: import("../lib/Compilation").PathData,
assetInfo?: import("../lib/Compilation").AssetInfo
) => string);
/**
* Function that executes for module and should return whenever asset should be inlined as DataUrl.
*/
@ -1023,6 +1042,10 @@ export interface EntryDescription {
* Enable/disable creating async chunks that are loaded on demand.
*/
asyncChunks?: boolean;
/**
* Base uri for this entry.
*/
baseUri?: string;
/**
* The method of loading chunks (methods included by default are 'jsonp' (web), 'import' (ESM), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).
*/
@ -1077,7 +1100,7 @@ export interface LibraryOptions {
*/
name?: LibraryName;
/**
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
*/
type: LibraryType;
/**
@ -1982,6 +2005,14 @@ export interface Output {
* This option enables cross-origin loading of chunks.
*/
crossOriginLoading?: CrossOriginLoading;
/**
* Specifies the filename template of non-initial output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
*/
cssChunkFilename?: CssChunkFilename;
/**
* Specifies the filename template of output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
*/
cssFilename?: CssFilename;
/**
* Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers.
*/
@ -2067,7 +2098,7 @@ export interface Output {
*/
libraryExport?: LibraryExport;
/**
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
*/
libraryTarget?: LibraryType;
/**
@ -2693,11 +2724,32 @@ export interface AssetResourceGeneratorOptions {
* Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
*/
filename?: FilenameTemplate;
/**
* Emit the asset in the specified folder relative to 'output.path'. This should only be needed when custom 'publicPath' is specified to match the folder structure there.
*/
outputPath?: AssetModuleOutputPath;
/**
* The 'publicPath' specifies the public URL address of the output files when referenced in a browser.
*/
publicPath?: RawPublicPath;
}
/**
* Options for css handling.
*/
export interface CssExperimentOptions {
/**
* Avoid generating and loading a stylesheet and only embed exports from css into output javascript files.
*/
exportsOnly?: boolean;
}
/**
* Generator options for css modules.
*/
export interface CssGeneratorOptions {}
/**
* Parser options for css modules.
*/
export interface CssParserOptions {}
/**
* No generator options are supported for this module type.
*/
@ -2714,6 +2766,10 @@ export interface EntryDescriptionNormalized {
* Enable/disable creating async chunks that are loaded on demand.
*/
asyncChunks?: boolean;
/**
* Base uri for this entry.
*/
baseUri?: string;
/**
* The method of loading chunks (methods included by default are 'jsonp' (web), 'import' (ESM), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).
*/
@ -2850,6 +2906,10 @@ export interface HttpUriOptions {
* Location of the lockfile.
*/
lockfileLocation?: string;
/**
* Proxy configuration, which can be used to specify a proxy server to use for HTTP requests.
*/
proxy?: string;
/**
* When set, resources of existing lockfile entries will be fetched and entries will be upgraded when resource content has changed.
*/
@ -2907,6 +2967,14 @@ export interface JavascriptParserOptions {
* Specifies the behavior of invalid export names in "import ... from ...".
*/
importExportsPresence?: "error" | "warn" | "auto" | false;
/**
* Enable/disable evaluating import.meta.
*/
importMeta?: boolean;
/**
* Enable/disable evaluating import.meta.webpackContext.
*/
importMetaContext?: boolean;
/**
* Include polyfills or mocks for various node stuff.
*/
@ -3118,6 +3186,14 @@ export interface OutputNormalized {
* This option enables cross-origin loading of chunks.
*/
crossOriginLoading?: CrossOriginLoading;
/**
* Specifies the filename template of non-initial output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
*/
cssChunkFilename?: CssChunkFilename;
/**
* Specifies the filename template of output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.
*/
cssFilename?: CssFilename;
/**
* Similar to `output.devtoolModuleFilenameTemplate`, but used in the case of duplicate module identifiers.
*/
@ -3408,6 +3484,10 @@ export interface ExperimentsExtra {
* Build http(s): urls using a lockfile and resource content cache.
*/
buildHttp?: HttpUriAllowedUris | HttpUriOptions;
/**
* Enable css support.
*/
css?: boolean | CssExperimentOptions;
/**
* Compile entrypoints and import()s only when they are accessed.
*/
@ -3421,6 +3501,10 @@ export interface ExperimentsNormalizedExtra {
* Build http(s): urls using a lockfile and resource content cache.
*/
buildHttp?: HttpUriOptions;
/**
* Enable css support.
*/
css?: CssExperimentOptions;
/**
* Compile entrypoints and import()s only when they are accessed.
*/

View File

@ -38,6 +38,10 @@ export interface BannerPluginOptions {
* Exclude all modules matching any of these conditions.
*/
exclude?: Rules;
/**
* If true, banner will be placed at the end of the output.
*/
footer?: boolean;
/**
* Include all modules matching any of these conditions.
*/

View File

@ -29,7 +29,7 @@ export type LibraryExport = string[] | string;
*/
export type LibraryName = string[] | string | LibraryCustomUmdObject;
/**
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
*/
export type LibraryType =
| (
@ -44,6 +44,7 @@ export type LibraryType =
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "commonjs-static"
| "amd"
| "amd-require"
| "umd"
@ -126,7 +127,7 @@ export interface LibraryOptions {
*/
name?: LibraryName;
/**
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
*/
type: LibraryType;
/**

View File

@ -18,6 +18,7 @@ export type ExternalsType =
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "commonjs-static"
| "amd"
| "amd-require"
| "umd"

View File

@ -29,7 +29,7 @@ export type LibraryExport = string[] | string;
*/
export type LibraryName = string[] | string | LibraryCustomUmdObject;
/**
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
*/
export type LibraryType =
| (
@ -44,6 +44,7 @@ export type LibraryType =
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "commonjs-static"
| "amd"
| "amd-require"
| "umd"
@ -70,6 +71,7 @@ export type ExternalsType =
| "commonjs"
| "commonjs2"
| "commonjs-module"
| "commonjs-static"
| "amd"
| "amd-require"
| "umd"
@ -182,7 +184,7 @@ export interface LibraryOptions {
*/
name?: LibraryName;
/**
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
*/
type: LibraryType;
/**

View File

@ -34,6 +34,10 @@ export interface HttpUriOptions {
* Location of the lockfile.
*/
lockfileLocation?: string;
/**
* Proxy configuration, which can be used to specify a proxy server to use for HTTP requests.
*/
proxy?: string;
/**
* When set, resources of existing lockfile entries will be fetched and entries will be upgraded when resource content has changed.
*/

536
examples/css/README.md Normal file
View File

@ -0,0 +1,536 @@
# example.js
```javascript
import "./style.css";
import "./style2.css";
import { main } from "./style.module.css";
import("./lazy-style.css");
document.getElementsByTagName("main")[0].className = main;
```
# style.css
```javascript
@import "style-imported.css";
@import "https://fonts.googleapis.com/css?family=Open+Sans";
body {
background: green;
font-family: "Open Sans";
}
```
# dist/output.js
```javascript
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 3:
/*!*************************!*\
!*** ./images/file.png ***!
\*************************/
/*! default exports */
/*! exports [not provided] [no usage info] */
/*! runtime requirements: module, __webpack_require__.p, __webpack_require__.* */
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
module.exports = __webpack_require__.p + "89a353e9c515885abd8e.png";
/***/ })
/******/ });
```
<details><summary><code>/* webpack runtime code */</code></summary>
``` js
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = (chunkId) => {
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/ __webpack_require__.f[key](chunkId, promises);
/******/ return promises;
/******/ }, []));
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/get css chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks
/******/ __webpack_require__.k = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" + chunkId + ".output.css";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" + chunkId + ".output.js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/load script */
/******/ (() => {
/******/ var inProgress = {};
/******/ var dataWebpackPrefix = "app:";
/******/ // loadScript function to load a script via script tag
/******/ __webpack_require__.l = (url, done, key, chunkId) => {
/******/ if(inProgress[url]) { inProgress[url].push(done); return; }
/******/ var script, needAttach;
/******/ if(key !== undefined) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ for(var i = 0; i < scripts.length; i++) {
/******/ var s = scripts[i];
/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; }
/******/ }
/******/ }
/******/ if(!script) {
/******/ needAttach = true;
/******/ script = document.createElement('script');
/******/
/******/ script.charset = 'utf-8';
/******/ script.timeout = 120;
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/ script.setAttribute("data-webpack", dataWebpackPrefix + key);
/******/ script.src = url;
/******/ }
/******/ inProgress[url] = [done];
/******/ var onScriptComplete = (prev, event) => {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var doneFns = inProgress[url];
/******/ delete inProgress[url];
/******/ script.parentNode && script.parentNode.removeChild(script);
/******/ doneFns && doneFns.forEach((fn) => (fn(event)));
/******/ if(prev) return prev(event);
/******/ }
/******/ ;
/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
/******/ script.onerror = onScriptComplete.bind(null, script.onerror);
/******/ script.onload = onScriptComplete.bind(null, script.onload);
/******/ needAttach && document.head.appendChild(script);
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/publicPath */
/******/ (() => {
/******/ __webpack_require__.p = "dist/";
/******/ })();
/******/
/******/ /* webpack/runtime/css loading */
/******/ (() => {
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {};
/******/
/******/ var uniqueName = "app";
/******/ var loadCssChunkData = (target, link, chunkId) => {
/******/ var data, token = "", token2, exports = {}, exportsWithId = [], exportsWithDashes = [], i = 0, cc = 1;
/******/ try { if(!link) link = loadStylesheet(chunkId); data = link.sheet.cssRules; data = data[data.length - 1].style; } catch(e) { data = getComputedStyle(document.head); }
/******/ data = data.getPropertyValue("--webpack-" + uniqueName + "-" + chunkId);
/******/ if(!data) return [];
/******/ for(; cc; i++) {
/******/ cc = data.charCodeAt(i);
/******/ if(cc == 40) { token2 = token; token = ""; }
/******/ else if(cc == 41) { exports[token2.replace(/^_/, "")] = token.replace(/^_/, ""); token = ""; }
/******/ else if(cc == 47 || cc == 37) { token = token.replace(/^_/, ""); exports[token] = token; exportsWithId.push(token); if(cc == 37) exportsWithDashes.push(token); token = ""; }
/******/ else if(!cc || cc == 44) { token = token.replace(/^_/, ""); exportsWithId.forEach((x) => (exports[x] = uniqueName + "-" + token + "-" + exports[x])); exportsWithDashes.forEach((x) => (exports[x] = "--" + exports[x])); __webpack_require__.r(exports); target[token] = ((exports, module) => {
/******/ module.exports = exports;
/******/ }).bind(null, exports); token = ""; exports = {}; exportsWithId.length = 0; }
/******/ else if(cc == 92) { token += data[++i] }
/******/ else { token += data[i]; }
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/
/******/ }
/******/ var loadingAttribute = "data-webpack-loading";
/******/ var loadStylesheet = (chunkId, url, done) => {
/******/ var link, needAttach, key = "chunk-" + chunkId;
/******/
/******/ var links = document.getElementsByTagName("link");
/******/ for(var i = 0; i < links.length; i++) {
/******/ var l = links[i];
/******/ if(l.rel == "stylesheet" && (l.href == url || l.getAttribute("href") == url || l.getAttribute("data-webpack") == uniqueName + ":" + key)) { link = l; break; }
/******/ }
/******/ if(!done) return link;
/******/
/******/ if(!link) {
/******/ needAttach = true;
/******/ link = document.createElement('link');
/******/ link.setAttribute("data-webpack", uniqueName + ":" + key);
/******/ link.setAttribute(loadingAttribute, 1);
/******/ link.rel = "stylesheet";
/******/ link.href = url;
/******/ }
/******/ var onLinkComplete = (prev, event) => {
/******/ link.onerror = link.onload = null;
/******/ link.removeAttribute(loadingAttribute);
/******/ clearTimeout(timeout);
/******/ if(event && event.type != "load") link.parentNode.removeChild(link)
/******/ done(event);
/******/ if(prev) return prev(event);
/******/ };
/******/ if(link.getAttribute(loadingAttribute)) {
/******/ var timeout = setTimeout(onLinkComplete.bind(null, undefined, { type: 'timeout', target: link }), 120000);
/******/ link.onerror = onLinkComplete.bind(null, link.onerror);
/******/ link.onload = onLinkComplete.bind(null, link.onload);
/******/ } else onLinkComplete(undefined, { type: 'load', target: link });
/******/
/******/ needAttach && document.head.appendChild(link);
/******/ return link;
/******/ };
/******/ loadCssChunkData(__webpack_require__.m, 0, 0);
/******/
/******/ __webpack_require__.f.css = (chunkId, promises) => {
/******/ // css chunk loading
/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/ } else {
/******/ if(true) { // all chunks have CSS
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));
/******/ promises.push(installedChunkData[2] = promise);
/******/
/******/ // start chunk loading
/******/ var url = __webpack_require__.p + __webpack_require__.k(chunkId);
/******/ // create error before stack unwound to get useful stacktrace later
/******/ var error = new Error();
/******/ var loadingEnded = (event) => {
/******/ if(__webpack_require__.o(installedChunks, chunkId)) {
/******/ installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined;
/******/ if(installedChunkData) {
/******/ if(event.type !== "load") {
/******/ var errorType = event && event.type;
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading css chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ error.name = 'ChunkLoadError';
/******/ error.type = errorType;
/******/ error.request = realSrc;
/******/ installedChunkData[1](error);
/******/ } else {
/******/ loadCssChunkData(__webpack_require__.m, link, chunkId);
/******/ installedChunkData[0]();
/******/ }
/******/ }
/******/ }
/******/ };
/******/ var link = loadStylesheet(chunkId, url, loadingEnded);
/******/ } else installedChunks[chunkId] = 0;
/******/ }
/******/ }
/******/ };
/******/
/******/ // no hmr
/******/ })();
/******/
/******/ /* webpack/runtime/jsonp chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ 0: 0
/******/ };
/******/
/******/ __webpack_require__.f.j = (chunkId, promises) => {
/******/ // JSONP chunk loading for javascript
/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/ } else {
/******/ if(0 == chunkId) {
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));
/******/ promises.push(installedChunkData[2] = promise);
/******/
/******/ // start chunk loading
/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId);
/******/ // create error before stack unwound to get useful stacktrace later
/******/ var error = new Error();
/******/ var loadingEnded = (event) => {
/******/ if(__webpack_require__.o(installedChunks, chunkId)) {
/******/ installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined;
/******/ if(installedChunkData) {
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ error.name = 'ChunkLoadError';
/******/ error.type = errorType;
/******/ error.request = realSrc;
/******/ installedChunkData[1](error);
/******/ }
/******/ }
/******/ };
/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId);
/******/ } else installedChunks[chunkId] = 0;
/******/ }
/******/ }
/******/ };
/******/
/******/ // no prefetching
/******/
/******/ // no preloaded
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/
/******/ // no on chunks loaded
/******/
/******/ // install a JSONP callback for chunk loading
/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0;
/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) {
/******/ for(moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) var result = runtime(__webpack_require__);
/******/ }
/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ installedChunks[chunkId][0]();
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/
/******/ }
/******/
/******/ var chunkLoadingGlobal = self["webpackChunkapp"] = self["webpackChunkapp"] || [];
/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
/******/ })();
/******/
/************************************************************************/
```
</details>
``` js
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!********************!*\
!*** ./example.js ***!
\********************/
/*! namespace exports */
/*! exports [not provided] [no usage info] */
/*! runtime requirements: __webpack_require__, __webpack_require__.r, __webpack_exports__, __webpack_require__.e, __webpack_require__.* */
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./style.css */ 1);
/* harmony import */ var _style2_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./style2.css */ 5);
/* harmony import */ var _style_module_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./style.module.css */ 6);
__webpack_require__.e(/*! import() */ 1).then(__webpack_require__.bind(__webpack_require__, /*! ./lazy-style.css */ 7));
document.getElementsByTagName("main")[0].className = _style_module_css__WEBPACK_IMPORTED_MODULE_2__.main;
})();
/******/ })()
;
```
# dist/output.css
```javascript
@import url("https://fonts.googleapis.com/css?family=Open+Sans");
.img {
width: 150px;
height: 150px;
background: url(89a353e9c515885abd8e.png);
}
body {
background: green;
font-family: "Open Sans";
}
body {
background: red;
}
:root {
--app-6-large: 72px;
}
.app-6-main {
font-size: var(--app-6-large);
color: darkblue;
}
head{--webpack-app-0:_4,_2,_1,_5,large%main/_6;}
```
## production
```javascript
@import url("https://fonts.googleapis.com/css?family=Open+Sans");
.img {
width: 150px;
height: 150px;
background: url(89a353e9c515885abd8e.png);
}
body {
background: green;
font-family: "Open Sans";
}
body {
background: red;
}
:root {
--app-491-b: 72px;
}
.app-491-D {
font-size: var(--app-491-b);
color: darkblue;
}
head{--webpack-app-179:_548,_431,_258,_268,b%D/_491;}
```
# dist/1.output.css
```javascript
body {
color: blue;
}
head{--webpack-app-1:_7;}
```
# Info
## Unoptimized
```
assets by chunk 16.9 KiB (name: main)
asset output.js 16.5 KiB [emitted] (name: main)
asset output.css 385 bytes [emitted] (name: main)
asset 89a353e9c515885abd8e.png 14.6 KiB [emitted] [immutable] [from: images/file.png] (auxiliary name: main)
asset 1.output.css 49 bytes [emitted]
Entrypoint main 16.9 KiB (14.6 KiB) = output.js 16.5 KiB output.css 385 bytes 1 auxiliary asset
chunk (runtime: main) output.js, output.css (main) 218 bytes (javascript) 335 bytes (css) 14.6 KiB (asset) 42 bytes (css-import) 10 KiB (runtime) [entry] [rendered]
> ./example.js main
runtime modules 10 KiB 9 modules
dependent modules 42 bytes (javascript) 14.6 KiB (asset) 335 bytes (css) 42 bytes (css-import) [dependent] 6 modules
./example.js 176 bytes [built] [code generated]
[no exports]
[used exports unknown]
entry ./example.js main
chunk (runtime: main) 1.output.css 23 bytes
> ./lazy-style.css ./example.js 4:0-26
./lazy-style.css 23 bytes [built] [code generated]
[no exports]
[used exports unknown]
import() ./lazy-style.css ./example.js 4:0-26
webpack 5.66.0 compiled successfully
```
## Production mode
```
assets by chunk 4.25 KiB (name: main)
asset output.js 3.87 KiB [emitted] [minimized] (name: main)
asset output.css 385 bytes [emitted] (name: main)
asset 89a353e9c515885abd8e.png 14.6 KiB [emitted] [immutable] [from: images/file.png] (auxiliary name: main)
asset 159.output.css 53 bytes [emitted]
Entrypoint main 4.25 KiB (14.6 KiB) = output.js 3.87 KiB output.css 385 bytes 1 auxiliary asset
chunk (runtime: main) 159.output.css 23 bytes
> ./lazy-style.css ./example.js 4:0-26
./lazy-style.css 23 bytes [built] [code generated]
[no exports]
import() ./lazy-style.css ./example.js 4:0-26
chunk (runtime: main) output.js, output.css (main) 218 bytes (javascript) 335 bytes (css) 14.6 KiB (asset) 42 bytes (css-import) 10 KiB (runtime) [entry] [rendered]
> ./example.js main
runtime modules 10 KiB 9 modules
dependent modules 42 bytes (javascript) 14.6 KiB (asset) 335 bytes (css) 42 bytes (css-import) [dependent] 6 modules
./example.js 176 bytes [built] [code generated]
[no exports]
[no exports used]
entry ./example.js main
webpack 5.66.0 compiled successfully
```

1
examples/css/build.js Normal file
View File

@ -0,0 +1 @@
require("../build-common");

6
examples/css/example.js Normal file
View File

@ -0,0 +1,6 @@
import "./style.css";
import "./style2.css";
import { main } from "./style.module.css";
import("./lazy-style.css");
document.getElementsByTagName("main")[0].className = main;

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

10
examples/css/index.html Normal file
View File

@ -0,0 +1,10 @@
<html>
<head>
<link rel="stylesheet" href="dist/output.css" data-webpack="app:chunk-0" />
</head>
<body>
<main>Hello World</main>
<p class="img"></p>
<script src="dist/output.js"></script>
</body>
</html>

View File

@ -0,0 +1,3 @@
body {
color: blue;
}

View File

@ -0,0 +1,5 @@
.img {
width: 150px;
height: 150px;
background: url("./images/file.png");
}

7
examples/css/style.css Normal file
View File

@ -0,0 +1,7 @@
@import "style-imported.css";
@import "https://fonts.googleapis.com/css?family=Open+Sans";
body {
background: green;
font-family: "Open Sans";
}

View File

@ -0,0 +1,8 @@
:root {
--large: 72px;
}
.main {
font-size: var(--large);
color: darkblue;
}

3
examples/css/style2.css Normal file
View File

@ -0,0 +1,3 @@
body {
background: red;
}

49
examples/css/template.md Normal file
View File

@ -0,0 +1,49 @@
# example.js
```javascript
_{{example.js}}_
```
# style.css
```javascript
_{{style.css}}_
```
# dist/output.js
```javascript
_{{dist/output.js}}_
```
# dist/output.css
```javascript
_{{dist/output.css}}_
```
## production
```javascript
_{{production:dist/output.css}}_
```
# dist/1.output.css
```javascript
_{{dist/1.output.css}}_
```
# Info
## Unoptimized
```
_{{stdout}}_
```
## Production mode
```
_{{production:stdout}}_
```

View File

@ -0,0 +1,8 @@
module.exports = {
output: {
uniqueName: "app"
},
experiments: {
css: true
}
};

View File

@ -67,7 +67,7 @@
<!-- A spinner -->
<div class="spinner"></div>
<!-- This script only contains boostrapping logic -->
<!-- This script only contains bootstrapping logic -->
<!-- It will load all other scripts if necessary -->
<script src="/dist/aaa/app.js" async></script>

View File

@ -60,7 +60,7 @@ exports.replaceResults = (template, baseDir, stdout, prefix) => {
const regexp = new RegExp("_\\{\\{" + (prefix ? prefix + ":" : "") + "([^:\\}]+)\\}\\}_", "g");
return template.replace(regexp, function(match) {
match = match.substr(3 + (prefix ? prefix.length + 1 : 0), match.length - 6 - (prefix ? prefix.length + 1 : 0));
match = match.slice(3 + (prefix ? prefix.length + 1 : 0), -3);
if(match === "stdout")
return stdout;
try {

View File

@ -4,7 +4,7 @@
*/
/*globals __resourceQuery */
if (module.hot) {
var hotPollInterval = +__resourceQuery.substr(1) || 10 * 60 * 1000;
var hotPollInterval = +__resourceQuery.slice(1) || 10 * 60 * 1000;
var log = require("./log");
var checkForUpdate = function checkForUpdate(fromUpdate) {

View File

@ -45,7 +45,7 @@ if (module.hot) {
});
};
process.on(__resourceQuery.substr(1) || "SIGUSR2", function () {
process.on(__resourceQuery.slice(1) || "SIGUSR2", function () {
if (module.hot.status() !== "idle") {
log(
"warning",

View File

@ -201,6 +201,39 @@ class APIPlugin {
)
.setRange(expr.range)
);
parser.hooks.expression
.for("__webpack_module__.id")
.tap("APIPlugin", expr => {
parser.state.module.buildInfo.moduleConcatenationBailout =
"__webpack_module__.id";
const dep = new ConstDependency(
parser.state.module.moduleArgument + ".id",
expr.range,
[RuntimeGlobals.moduleId]
);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
parser.hooks.expression
.for("__webpack_module__")
.tap("APIPlugin", expr => {
parser.state.module.buildInfo.moduleConcatenationBailout =
"__webpack_module__";
const dep = new ConstDependency(
parser.state.module.moduleArgument,
expr.range,
[RuntimeGlobals.module]
);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
parser.hooks.evaluateTypeof
.for("__webpack_module__")
.tap("APIPlugin", evaluateToString("object"));
};
normalModuleFactory.hooks.parser

View File

@ -77,6 +77,7 @@ class BannerPlugin {
undefined,
options
);
const cache = new WeakMap();
compiler.hooks.compilation.tap("BannerPlugin", compilation => {
compilation.hooks.processAssets.tap(
@ -102,10 +103,17 @@ class BannerPlugin {
const comment = compilation.getPath(banner, data);
compilation.updateAsset(
file,
old => new ConcatSource(comment, "\n", old)
);
compilation.updateAsset(file, old => {
let cached = cache.get(old);
if (!cached || cached.comment !== comment) {
const source = options.footer
? new ConcatSource(old, "\n", comment)
: new ConcatSource(comment, "\n", old);
cache.set(old, { source, comment });
return source;
}
return cached.source;
});
}
}
}

View File

@ -21,7 +21,7 @@ const {
/**
* @template T
* @callback CallbackCache
* @param {WebpackError=} err
* @param {(WebpackError | null)=} err
* @param {T=} result
* @returns {void}
*/

View File

@ -5,6 +5,7 @@
"use strict";
const { forEachBail } = require("enhanced-resolve");
const asyncLib = require("neo-async");
const getLazyHashedEtag = require("./cache/getLazyHashedEtag");
const mergeEtags = require("./cache/mergeEtags");
@ -18,7 +19,7 @@ const mergeEtags = require("./cache/mergeEtags");
/**
* @template T
* @callback CallbackCache
* @param {WebpackError=} err
* @param {(WebpackError | null)=} err
* @param {T=} result
* @returns {void}
*/
@ -26,7 +27,7 @@ const mergeEtags = require("./cache/mergeEtags");
/**
* @template T
* @callback CallbackNormalErrorCache
* @param {Error=} err
* @param {(Error | null)=} err
* @param {T=} result
* @returns {void}
*/
@ -46,15 +47,7 @@ class MultiItemCache {
* @returns {void}
*/
get(callback) {
const next = i => {
this._items[i].get((err, result) => {
if (err) return callback(err);
if (result !== undefined) return callback(null, result);
if (++i >= this._items.length) return callback();
next(i);
});
};
next(0);
forEachBail(this._items, (item, callback) => item.get(callback), callback);
}
/**

View File

@ -80,6 +80,8 @@ class Chunk {
this.preventIntegration = false;
/** @type {(string | function(PathData, AssetInfo=): string)?} */
this.filenameTemplate = undefined;
/** @type {(string | function(PathData, AssetInfo=): string)?} */
this.cssFilenameTemplate = undefined;
/** @private @type {SortableSet<ChunkGroup>} */
this._groups = new SortableSet(undefined, compareChunkGroupsByIndex);
/** @type {RuntimeSpec} */
@ -688,7 +690,7 @@ class Chunk {
for (const childGroup of group.childrenIterable) {
for (const key of Object.keys(childGroup.options)) {
if (key.endsWith("Order")) {
const name = key.substr(0, key.length - "Order".length);
const name = key.slice(0, key.length - "Order".length);
let list = lists.get(name);
if (list === undefined) {
list = [];

View File

@ -46,6 +46,7 @@ const compareModuleIterables = compareIterables(compareModulesByIdentifier);
/** @typedef {(c: Chunk, chunkGraph: ChunkGraph) => boolean} ChunkFilterPredicate */
/** @typedef {(m: Module) => boolean} ModuleFilterPredicate */
/** @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup */
/**
* @typedef {Object} ChunkSizeOptions
@ -1180,8 +1181,6 @@ class ChunkGraph {
return cgc.dependentHashModules;
}
/** @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup */
/**
* @param {Chunk} chunk the chunk
* @returns {Iterable<EntryModuleWithChunkGroup>} iterable of modules (do not modify)

View File

@ -486,7 +486,7 @@ class ChunkGroup {
for (const childGroup of this._children) {
for (const key of Object.keys(childGroup.options)) {
if (key.endsWith("Order")) {
const name = key.substr(0, key.length - "Order".length);
const name = key.slice(0, key.length - "Order".length);
let list = lists.get(name);
if (list === undefined) {
lists.set(name, (list = []));

View File

@ -19,6 +19,7 @@ const processAsyncTree = require("./util/processAsyncTree");
/** @typedef {import("./util/fs").StatsCallback} StatsCallback */
/** @typedef {(function(string):boolean)|RegExp} IgnoreItem */
/** @typedef {Map<string, number>} Assets */
/** @typedef {function(IgnoreItem): void} AddToIgnoreCallback */
/**
@ -40,18 +41,32 @@ const validate = createSchemaValidation(
baseDataPath: "options"
}
);
const _10sec = 10 * 1000;
/**
* marge assets map 2 into map 1
* @param {Assets} as1 assets
* @param {Assets} as2 assets
* @returns {void}
*/
const mergeAssets = (as1, as2) => {
for (const [key, value1] of as2) {
const value2 = as1.get(key);
if (!value2 || value1 > value2) as1.set(key, value1);
}
};
/**
* @param {OutputFileSystem} fs filesystem
* @param {string} outputPath output path
* @param {Set<string>} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator)
* @param {function(Error=, Set<string>=): void} callback returns the filenames of the assets that shouldn't be there
* @param {Map<string, number>} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator)
* @param {function((Error | null)=, Set<string>=): void} callback returns the filenames of the assets that shouldn't be there
* @returns {void}
*/
const getDiffToFs = (fs, outputPath, currentAssets, callback) => {
const directories = new Set();
// get directories of assets
for (const asset of currentAssets) {
for (const [asset] of currentAssets) {
directories.add(asset.replace(/(^|\/)[^/]*$/, ""));
}
// and all parent directories
@ -91,13 +106,15 @@ const getDiffToFs = (fs, outputPath, currentAssets, callback) => {
};
/**
* @param {Set<string>} currentAssets assets list
* @param {Set<string>} oldAssets old assets list
* @param {Assets} currentAssets assets list
* @param {Assets} oldAssets old assets list
* @returns {Set<string>} diff
*/
const getDiffToOldAssets = (currentAssets, oldAssets) => {
const diff = new Set();
for (const asset of oldAssets) {
const now = Date.now();
for (const [asset, ts] of oldAssets) {
if (ts >= now) continue;
if (!currentAssets.has(asset)) diff.add(asset);
}
return diff;
@ -124,7 +141,7 @@ const doStat = (fs, filename, callback) => {
* @param {Logger} logger logger
* @param {Set<string>} diff filenames of the assets that shouldn't be there
* @param {function(string): boolean} isKept check if the entry is ignored
* @param {function(Error=): void} callback callback
* @param {function(Error=, Assets=): void} callback callback
* @returns {void}
*/
const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
@ -137,11 +154,13 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
};
/** @typedef {{ type: "check" | "unlink" | "rmdir", filename: string, parent: { remaining: number, job: Job } | undefined }} Job */
/** @type {Job[]} */
const jobs = Array.from(diff, filename => ({
const jobs = Array.from(diff.keys(), filename => ({
type: "check",
filename,
parent: undefined
}));
/** @type {Assets} */
const keptAssets = new Map();
processAsyncTree(
jobs,
10,
@ -161,6 +180,7 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
switch (type) {
case "check":
if (isKept(filename)) {
keptAssets.set(filename, 0);
// do not decrement parent entry as we don't want to delete the parent
log(`${filename} will be kept`);
return process.nextTick(callback);
@ -247,7 +267,10 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
break;
}
},
callback
err => {
if (err) return callback(err);
callback(undefined, keptAssets);
}
);
};
@ -302,6 +325,7 @@ class CleanPlugin {
// We assume that no external modification happens while the compiler is active
// So we can store the old assets and only diff to them to avoid fs access on
// incremental builds
/** @type {undefined|Assets} */
let oldAssets;
compiler.hooks.emit.tapAsync(
@ -322,7 +346,9 @@ class CleanPlugin {
);
}
const currentAssets = new Set();
/** @type {Assets} */
const currentAssets = new Map();
const now = Date.now();
for (const asset of Object.keys(compilation.assets)) {
if (/^[A-Za-z]:\\|^\/|^\\\\/.test(asset)) continue;
let normalizedAsset;
@ -335,7 +361,12 @@ class CleanPlugin {
);
} while (newNormalizedAsset !== normalizedAsset);
if (normalizedAsset.startsWith("../")) continue;
currentAssets.add(normalizedAsset);
const assetInfo = compilation.assetsInfo.get(asset);
if (assetInfo && assetInfo.hotModuleReplacement) {
currentAssets.set(normalizedAsset, now + _10sec);
} else {
currentAssets.set(normalizedAsset, 0);
}
}
const outputPath = compilation.getPath(compiler.outputPath, {});
@ -346,19 +377,34 @@ class CleanPlugin {
return keepFn(path);
};
/**
* @param {Error=} err err
* @param {Set<string>=} diff diff
*/
const diffCallback = (err, diff) => {
if (err) {
oldAssets = undefined;
return callback(err);
}
applyDiff(fs, outputPath, dry, logger, diff, isKept, err => {
if (err) {
oldAssets = undefined;
} else {
oldAssets = currentAssets;
}
callback(err);
});
return;
}
applyDiff(
fs,
outputPath,
dry,
logger,
diff,
isKept,
(err, keptAssets) => {
if (err) {
oldAssets = undefined;
} else {
if (oldAssets) mergeAssets(currentAssets, oldAssets);
oldAssets = currentAssets;
if (keptAssets) mergeAssets(oldAssets, keptAssets);
}
callback(err);
}
);
};
if (oldAssets) {

View File

@ -43,6 +43,7 @@ const Module = require("./Module");
const ModuleDependencyError = require("./ModuleDependencyError");
const ModuleDependencyWarning = require("./ModuleDependencyWarning");
const ModuleGraph = require("./ModuleGraph");
const ModuleHashingError = require("./ModuleHashingError");
const ModuleNotFoundError = require("./ModuleNotFoundError");
const ModuleProfile = require("./ModuleProfile");
const ModuleRestoreError = require("./ModuleRestoreError");
@ -116,34 +117,34 @@ const { isSourceEqual } = require("./util/source");
/**
* @callback Callback
* @param {WebpackError=} err
* @param {(WebpackError | null)=} err
* @returns {void}
*/
/**
* @callback ModuleCallback
* @param {WebpackError=} err
* @param {(WebpackError | null)=} err
* @param {Module=} result
* @returns {void}
*/
/**
* @callback ModuleFactoryResultCallback
* @param {WebpackError=} err
* @param {(WebpackError | null)=} err
* @param {ModuleFactoryResult=} result
* @returns {void}
*/
/**
* @callback ModuleOrFactoryResultCallback
* @param {WebpackError=} err
* @param {(WebpackError | null)=} err
* @param {Module | ModuleFactoryResult=} result
* @returns {void}
*/
/**
* @callback ExecuteModuleCallback
* @param {WebpackError=} err
* @param {(WebpackError | null)=} err
* @param {ExecuteModuleResult=} result
* @returns {void}
*/
@ -182,6 +183,7 @@ const { isSourceEqual } = require("./util/source");
/**
* @typedef {Object} ChunkHashContext
* @property {CodeGenerationResults} codeGenerationResults results of code generation
* @property {RuntimeTemplate} runtimeTemplate the runtime template
* @property {ModuleGraph} moduleGraph the module graph
* @property {ChunkGraph} chunkGraph the chunk graph
@ -1020,7 +1022,9 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
/** @type {Map<DepConstructor, ModuleFactory>} */
this.dependencyFactories = new Map();
/** @type {DependencyTemplates} */
this.dependencyTemplates = new DependencyTemplates();
this.dependencyTemplates = new DependencyTemplates(
this.outputOptions.hashFunction
);
this.childrenCounters = {};
/** @type {Set<number|string>} */
this.usedChunkIds = null;
@ -1432,7 +1436,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
* @returns {void}
*/
_processModuleDependencies(module, callback) {
/** @type {Array<{factory: ModuleFactory, dependencies: Dependency[], originModule: Module|null}>} */
/** @type {Array<{factory: ModuleFactory, dependencies: Dependency[], context: string|undefined, originModule: Module|null}>} */
const sortedDependencies = [];
/** @type {DependenciesBlock} */
@ -1664,6 +1668,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
sortedDependencies.push({
factory: factoryCacheKey2,
dependencies: list,
context: dep.getContext(),
originModule: module
});
}
@ -3193,47 +3198,87 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
this;
const results = this.codeGenerationResults;
const errors = [];
asyncLib.eachLimit(
jobs,
this.options.parallelism,
({ module, hash, runtime, runtimes }, callback) => {
this._codeGenerationModule(
module,
runtime,
runtimes,
hash,
dependencyTemplates,
chunkGraph,
moduleGraph,
runtimeTemplate,
errors,
results,
(err, codeGenerated) => {
if (codeGenerated) statModulesGenerated++;
else statModulesFromCache++;
callback(err);
/** @type {Set<Module> | undefined} */
let notCodeGeneratedModules = undefined;
const runIteration = () => {
let delayedJobs = [];
let delayedModules = new Set();
asyncLib.eachLimit(
jobs,
this.options.parallelism,
(job, callback) => {
const { module } = job;
const { codeGenerationDependencies } = module;
if (codeGenerationDependencies !== undefined) {
if (
notCodeGeneratedModules === undefined ||
codeGenerationDependencies.some(dep => {
const referencedModule = moduleGraph.getModule(dep);
return notCodeGeneratedModules.has(referencedModule);
})
) {
delayedJobs.push(job);
delayedModules.add(module);
return callback();
}
}
);
},
err => {
if (err) return callback(err);
if (errors.length > 0) {
errors.sort(
compareSelect(err => err.module, compareModulesByIdentifier)
const { hash, runtime, runtimes } = job;
this._codeGenerationModule(
module,
runtime,
runtimes,
hash,
dependencyTemplates,
chunkGraph,
moduleGraph,
runtimeTemplate,
errors,
results,
(err, codeGenerated) => {
if (codeGenerated) statModulesGenerated++;
else statModulesFromCache++;
callback(err);
}
);
for (const error of errors) {
this.errors.push(error);
},
err => {
if (err) return callback(err);
if (delayedJobs.length > 0) {
if (delayedJobs.length === jobs.length) {
return callback(
new Error(
`Unable to make progress during code generation because of circular code generation dependency: ${Array.from(
delayedModules,
m => m.identifier()
).join(", ")}`
)
);
}
jobs = delayedJobs;
delayedJobs = [];
notCodeGeneratedModules = delayedModules;
delayedModules = new Set();
return runIteration();
}
if (errors.length > 0) {
errors.sort(
compareSelect(err => err.module, compareModulesByIdentifier)
);
for (const error of errors) {
this.errors.push(error);
}
}
this.logger.log(
`${Math.round(
(100 * statModulesGenerated) /
(statModulesGenerated + statModulesFromCache)
)}% code generated (${statModulesGenerated} generated, ${statModulesFromCache} from cache)`
);
callback();
}
this.logger.log(
`${Math.round(
(100 * statModulesGenerated) /
(statModulesGenerated + statModulesFromCache)
)}% code generated (${statModulesGenerated} generated, ${statModulesFromCache} from cache)`
);
callback();
}
);
);
};
runIteration();
}
/**
@ -3283,7 +3328,9 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
moduleGraph,
dependencyTemplates,
runtimeTemplate,
runtime
runtime,
codeGenerationResults: results,
compilation: this
});
} catch (err) {
errors.push(new CodeGenerationError(module, err));
@ -3512,10 +3559,11 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
}
/**
* If `module` is passed, `loc` and `request` must also be passed.
* @param {string | ChunkGroupOptions} groupOptions options for the chunk group
* @param {Module} module the module the references the chunk group
* @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
* @param {string} request the request from which the the chunk group is referenced
* @param {Module=} module the module the references the chunk group
* @param {DependencyLocation=} loc the location from with the chunk group is referenced (inside of module)
* @param {string=} request the request from which the the chunk group is referenced
* @returns {ChunkGroup} the new or existing chunk group
*/
addChunkInGroup(groupOptions, module, loc, request) {
@ -3838,6 +3886,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
let statModulesFromCache = 0;
const { chunkGraph, runtimeTemplate, moduleMemCaches2 } = this;
const { hashFunction, hashDigest, hashDigestLength } = this.outputOptions;
const errors = [];
for (const module of this.modules) {
const memCache = moduleMemCaches2 && moduleMemCaches2.get(module);
for (const runtime of chunkGraph.getModuleRuntimes(module)) {
@ -3848,7 +3897,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
module,
runtime,
digest,
digest.substr(0, hashDigestLength)
digest.slice(0, hashDigestLength)
);
statModulesFromCache++;
continue;
@ -3862,13 +3911,20 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
hashFunction,
runtimeTemplate,
hashDigest,
hashDigestLength
hashDigestLength,
errors
);
if (memCache) {
memCache.set(`moduleHash-${getRuntimeKey(runtime)}`, digest);
}
}
}
if (errors.length > 0) {
errors.sort(compareSelect(err => err.module, compareModulesByIdentifier));
for (const error of errors) {
this.errors.push(error);
}
}
this.logger.log(
`${statModulesHashed} modules hashed, ${statModulesFromCache} from cache (${
Math.round(
@ -3885,22 +3941,27 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
hashFunction,
runtimeTemplate,
hashDigest,
hashDigestLength
hashDigestLength,
errors
) {
const moduleHash = createHash(hashFunction);
module.updateHash(moduleHash, {
chunkGraph,
runtime,
runtimeTemplate
});
const moduleHashDigest = /** @type {string} */ (
moduleHash.digest(hashDigest)
);
let moduleHashDigest;
try {
const moduleHash = createHash(hashFunction);
module.updateHash(moduleHash, {
chunkGraph,
runtime,
runtimeTemplate
});
moduleHashDigest = /** @type {string} */ (moduleHash.digest(hashDigest));
} catch (err) {
errors.push(new ModuleHashingError(module, err));
moduleHashDigest = "XXXXXX";
}
chunkGraph.setModuleHashes(
module,
runtime,
moduleHashDigest,
moduleHashDigest.substr(0, hashDigestLength)
moduleHashDigest.slice(0, hashDigestLength)
);
return moduleHashDigest;
}
@ -4046,6 +4107,7 @@ This prevents using hashes of each other and should be avoided.`);
const codeGenerationJobs = [];
/** @type {Map<string, Map<Module, {module: Module, hash: string, runtime: RuntimeSpec, runtimes: RuntimeSpec[]}>>} */
const codeGenerationJobsMap = new Map();
const errors = [];
const processChunk = chunk => {
// Last minute module hash generation for modules that depend on chunk hashes
@ -4060,7 +4122,8 @@ This prevents using hashes of each other and should be avoided.`);
hashFunction,
runtimeTemplate,
hashDigest,
hashDigestLength
hashDigestLength,
errors
);
let hashMap = codeGenerationJobsMap.get(hash);
if (hashMap) {
@ -4084,15 +4147,16 @@ This prevents using hashes of each other and should be avoided.`);
}
}
this.logger.timeAggregate("hashing: hash runtime modules");
this.logger.time("hashing: hash chunks");
const chunkHash = createHash(hashFunction);
try {
this.logger.time("hashing: hash chunks");
const chunkHash = createHash(hashFunction);
if (outputOptions.hashSalt) {
chunkHash.update(outputOptions.hashSalt);
}
chunk.updateHash(chunkHash, chunkGraph);
this.hooks.chunkHash.call(chunk, chunkHash, {
chunkGraph,
codeGenerationResults: this.codeGenerationResults,
moduleGraph: this.moduleGraph,
runtimeTemplate: this.runtimeTemplate
});
@ -4101,7 +4165,7 @@ This prevents using hashes of each other and should be avoided.`);
);
hash.update(chunkHashDigest);
chunk.hash = chunkHashDigest;
chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
chunk.renderedHash = chunk.hash.slice(0, hashDigestLength);
const fullHashModules =
chunkGraph.getChunkFullHashModulesIterable(chunk);
if (fullHashModules) {
@ -4116,13 +4180,19 @@ This prevents using hashes of each other and should be avoided.`);
};
otherChunks.forEach(processChunk);
for (const chunk of runtimeChunks) processChunk(chunk);
if (errors.length > 0) {
errors.sort(compareSelect(err => err.module, compareModulesByIdentifier));
for (const error of errors) {
this.errors.push(error);
}
}
this.logger.timeAggregateEnd("hashing: hash runtime modules");
this.logger.timeAggregateEnd("hashing: hash chunks");
this.logger.time("hashing: hash digest");
this.hooks.fullHash.call(hash);
this.fullHash = /** @type {string} */ (hash.digest(hashDigest));
this.hash = this.fullHash.substr(0, hashDigestLength);
this.hash = this.fullHash.slice(0, hashDigestLength);
this.logger.timeEnd("hashing: hash digest");
this.logger.time("hashing: process full hash modules");
@ -4142,7 +4212,7 @@ This prevents using hashes of each other and should be avoided.`);
module,
chunk.runtime,
moduleHashDigest,
moduleHashDigest.substr(0, hashDigestLength)
moduleHashDigest.slice(0, hashDigestLength)
);
codeGenerationJobsMap.get(oldHash).get(module).hash = moduleHashDigest;
}
@ -4153,7 +4223,7 @@ This prevents using hashes of each other and should be avoided.`);
chunkHash.digest(hashDigest)
);
chunk.hash = chunkHashDigest;
chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
chunk.renderedHash = chunk.hash.slice(0, hashDigestLength);
this.hooks.contentHash.call(chunk);
}
this.logger.timeEnd("hashing: process full hash modules");
@ -4755,6 +4825,9 @@ This prevents using hashes of each other and should be avoided.`);
chunkGraph.connectChunkAndModule(chunk, module);
}
/** @type {WebpackError[]} */
const errors = [];
// Hash modules
for (const module of modules) {
this._createModuleHash(
@ -4764,15 +4837,14 @@ This prevents using hashes of each other and should be avoided.`);
hashFunction,
runtimeTemplate,
hashDigest,
hashDigestLength
hashDigestLength,
errors
);
}
const codeGenerationResults = new CodeGenerationResults(
this.outputOptions.hashFunction
);
/** @type {WebpackError[]} */
const errors = [];
/**
* @param {Module} module the module
* @param {Callback} callback callback

View File

@ -58,13 +58,13 @@ const { isSourceEqual } = require("./util/source");
/**
* @template T
* @callback Callback
* @param {Error=} err
* @param {(Error | null)=} err
* @param {T=} result
*/
/**
* @callback RunAsChildCallback
* @param {Error=} err
* @param {(Error | null)=} err
* @param {Chunk[]=} entries
* @param {Compilation=} compilation
*/
@ -165,6 +165,11 @@ class Compiler {
/** @type {AsyncSeriesHook<[Compilation]>} */
afterCompile: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<[]>} */
readRecords: new AsyncSeriesHook([]),
/** @type {AsyncSeriesHook<[]>} */
emitRecords: new AsyncSeriesHook([]),
/** @type {AsyncSeriesHook<[Compiler]>} */
watchRun: new AsyncSeriesHook(["compiler"]),
/** @type {SyncHook<[Error]>} */
@ -540,8 +545,21 @@ class Compiler {
*/
runAsChild(callback) {
const startTime = Date.now();
const finalCallback = (err, entries, compilation) => {
try {
callback(err, entries, compilation);
} catch (e) {
const err = new WebpackError(
`compiler.runAsChild callback error: ${e}`
);
err.details = e.stack;
this.parentCompilation.errors.push(err);
}
};
this.compile((err, compilation) => {
if (err) return callback(err);
if (err) return finalCallback(err);
this.parentCompilation.children.push(compilation);
for (const { name, source, info } of compilation.getAssets()) {
@ -556,7 +574,7 @@ class Compiler {
compilation.startTime = startTime;
compilation.endTime = Date.now();
return callback(null, entries, compilation);
return finalCallback(null, entries, compilation);
});
}
@ -591,7 +609,7 @@ class Compiler {
let immutable = info.immutable;
const queryStringIdx = targetFile.indexOf("?");
if (queryStringIdx >= 0) {
targetFile = targetFile.substr(0, queryStringIdx);
targetFile = targetFile.slice(0, queryStringIdx);
// We may remove the hash, which is in the query string
// So we recheck if the file is immutable
// This doesn't cover all cases, but immutable is only a performance optimization anyway
@ -882,8 +900,32 @@ ${other}`);
* @returns {void}
*/
emitRecords(callback) {
if (!this.recordsOutputPath) return callback();
if (this.hooks.emitRecords.isUsed()) {
if (this.recordsOutputPath) {
asyncLib.parallel(
[
cb => this.hooks.emitRecords.callAsync(cb),
this._emitRecords.bind(this)
],
err => callback(err)
);
} else {
this.hooks.emitRecords.callAsync(callback);
}
} else {
if (this.recordsOutputPath) {
this._emitRecords(callback);
} else {
callback();
}
}
}
/**
* @param {Callback<void>} callback signals when the call finishes
* @returns {void}
*/
_emitRecords(callback) {
const writeFile = () => {
this.outputFileSystem.writeFile(
this.recordsOutputPath,
@ -926,6 +968,31 @@ ${other}`);
* @returns {void}
*/
readRecords(callback) {
if (this.hooks.readRecords.isUsed()) {
if (this.recordsInputPath) {
asyncLib.parallel([
cb => this.hooks.readRecords.callAsync(cb),
this._readRecords.bind(this)
]);
} else {
this.records = {};
this.hooks.readRecords.callAsync(callback);
}
} else {
if (this.recordsInputPath) {
this._readRecords(callback);
} else {
this.records = {};
callback();
}
}
}
/**
* @param {Callback<void>} callback signals when the call finishes
* @returns {void}
*/
_readRecords(callback) {
if (!this.recordsInputPath) {
this.records = {};
return callback();

View File

@ -324,7 +324,7 @@ class ConstPlugin {
}
} else if (expression.operator === "??") {
const param = parser.evaluateExpression(expression.left);
const keepRight = param && param.asNullish();
const keepRight = param.asNullish();
if (typeof keepRight === "boolean") {
// ------------------------------------------
//
@ -407,7 +407,7 @@ class ConstPlugin {
const expression = optionalExpressionsStack.pop();
const evaluated = parser.evaluateExpression(expression);
if (evaluated && evaluated.asNullish()) {
if (evaluated.asNullish()) {
// ------------------------------------------
//
// Given the following code:

View File

@ -19,7 +19,11 @@ const {
keepOriginalOrder,
compareModulesById
} = require("./util/comparators");
const { contextify, parseResource } = require("./util/identifier");
const {
contextify,
parseResource,
makePathsRelative
} = require("./util/identifier");
const makeSerializable = require("./util/makeSerializable");
/** @typedef {import("webpack-sources").Source} Source */
@ -61,7 +65,7 @@ const makeSerializable = require("./util/makeSerializable");
/**
* @typedef {Object} ContextModuleOptionsExtras
* @property {string} resource
* @property {false|string|string[]} resource
* @property {string=} resourceQuery
* @property {string=} resourceFragment
* @property {TODO} resolveOptions
@ -71,7 +75,7 @@ const makeSerializable = require("./util/makeSerializable");
/**
* @callback ResolveDependenciesCallback
* @param {Error=} err
* @param {(Error | null)=} err
* @param {ContextElementDependency[]=} dependencies
*/
@ -92,23 +96,36 @@ class ContextModule extends Module {
* @param {ContextModuleOptions} options options object
*/
constructor(resolveDependencies, options) {
const parsed = parseResource(options ? options.resource : "");
const resource = parsed.path;
const resourceQuery = (options && options.resourceQuery) || parsed.query;
const resourceFragment =
(options && options.resourceFragment) || parsed.fragment;
if (!options || typeof options.resource === "string") {
const parsed = parseResource(
options ? /** @type {string} */ (options.resource) : ""
);
const resource = parsed.path;
const resourceQuery = (options && options.resourceQuery) || parsed.query;
const resourceFragment =
(options && options.resourceFragment) || parsed.fragment;
super("javascript/dynamic", resource);
super("javascript/dynamic", resource);
/** @type {ContextModuleOptions} */
this.options = {
...options,
resource,
resourceQuery,
resourceFragment
};
} else {
super("javascript/dynamic");
/** @type {ContextModuleOptions} */
this.options = {
...options,
resource: options.resource,
resourceQuery: options.resourceQuery || "",
resourceFragment: options.resourceFragment || ""
};
}
// Info from Factory
this.resolveDependencies = resolveDependencies;
/** @type {ContextModuleOptions} */
this.options = {
...options,
resource,
resourceQuery,
resourceFragment
};
if (options && options.resolveOptions !== undefined) {
this.resolveOptions = options.resolveOptions;
}
@ -149,16 +166,18 @@ class ContextModule extends Module {
this.resolveDependencies = undefined;
}
prettyRegExp(regexString) {
// remove the "/" at the front and the beginning
// "/foo/" -> "foo"
return regexString
.substring(1, regexString.length - 1)
.replace(/!/g, "%21");
_prettyRegExp(regexString, stripSlash = true) {
const str = (regexString + "").replace(/!/g, "%21").replace(/\|/g, "%7C");
return stripSlash ? str.substring(1, str.length - 1) : str;
}
_createIdentifier() {
let identifier = this.context;
let identifier =
this.context ||
(typeof this.options.resource === "string" ||
this.options.resource === false
? `${this.options.resource}`
: this.options.resource.join("|"));
if (this.options.resourceQuery) {
identifier += `|${this.options.resourceQuery}`;
}
@ -175,13 +194,19 @@ class ContextModule extends Module {
identifier += `|${this.options.addon}`;
}
if (this.options.regExp) {
identifier += `|${this.options.regExp}`;
identifier += `|${this._prettyRegExp(this.options.regExp, false)}`;
}
if (this.options.include) {
identifier += `|include: ${this.options.include}`;
identifier += `|include: ${this._prettyRegExp(
this.options.include,
false
)}`;
}
if (this.options.exclude) {
identifier += `|exclude: ${this.options.exclude}`;
identifier += `|exclude: ${this._prettyRegExp(
this.options.exclude,
false
)}`;
}
if (this.options.referencedExports) {
identifier += `|referencedExports: ${JSON.stringify(
@ -217,7 +242,19 @@ class ContextModule extends Module {
* @returns {string} a user readable identifier of the module
*/
readableIdentifier(requestShortener) {
let identifier = requestShortener.shorten(this.context) + "/";
let identifier;
if (this.context) {
identifier = requestShortener.shorten(this.context) + "/";
} else if (
typeof this.options.resource === "string" ||
this.options.resource === false
) {
identifier = requestShortener.shorten(`${this.options.resource}`) + "/";
} else {
identifier = this.options.resource
.map(r => requestShortener.shorten(r) + "/")
.join(" ");
}
if (this.options.resourceQuery) {
identifier += ` ${this.options.resourceQuery}`;
}
@ -231,13 +268,13 @@ class ContextModule extends Module {
identifier += ` ${requestShortener.shorten(this.options.addon)}`;
}
if (this.options.regExp) {
identifier += ` ${this.prettyRegExp(this.options.regExp + "")}`;
identifier += ` ${this._prettyRegExp(this.options.regExp)}`;
}
if (this.options.include) {
identifier += ` include: ${this.prettyRegExp(this.options.include + "")}`;
identifier += ` include: ${this._prettyRegExp(this.options.include)}`;
}
if (this.options.exclude) {
identifier += ` exclude: ${this.prettyRegExp(this.options.exclude + "")}`;
identifier += ` exclude: ${this._prettyRegExp(this.options.exclude)}`;
}
if (this.options.referencedExports) {
identifier += ` referencedExports: ${this.options.referencedExports
@ -267,11 +304,31 @@ class ContextModule extends Module {
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
let identifier = contextify(
options.context,
this.context,
options.associatedObjectForCache
);
let identifier;
if (this.context) {
identifier = contextify(
options.context,
this.context,
options.associatedObjectForCache
);
} else if (typeof this.options.resource === "string") {
identifier = contextify(
options.context,
this.options.resource,
options.associatedObjectForCache
);
} else if (this.options.resource === false) {
identifier = "false";
} else {
identifier = this.options.resource
.map(res =>
contextify(options.context, res, options.associatedObjectForCache)
)
.join(" ");
}
if (this.layer) identifier = `(${this.layer})/${identifier}`;
if (this.options.mode) {
identifier += ` ${this.options.mode}`;
}
@ -286,13 +343,13 @@ class ContextModule extends Module {
)}`;
}
if (this.options.regExp) {
identifier += ` ${this.prettyRegExp(this.options.regExp + "")}`;
identifier += ` ${this._prettyRegExp(this.options.regExp)}`;
}
if (this.options.include) {
identifier += ` include: ${this.prettyRegExp(this.options.include + "")}`;
identifier += ` include: ${this._prettyRegExp(this.options.include)}`;
}
if (this.options.exclude) {
identifier += ` exclude: ${this.prettyRegExp(this.options.exclude + "")}`;
identifier += ` exclude: ${this._prettyRegExp(this.options.exclude)}`;
}
if (this.options.referencedExports) {
identifier += ` referencedExports: ${this.options.referencedExports
@ -312,15 +369,16 @@ class ContextModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild({ fileSystemInfo }, callback) {
// build if enforced
if (this._forceBuild) return callback(null, true);
// always build when we have no snapshot
if (!this.buildInfo.snapshot) return callback(null, true);
// always build when we have no snapshot and context
if (!this.buildInfo.snapshot)
return callback(null, Boolean(this.context || this.options.resource));
fileSystemInfo.checkSnapshotValid(this.buildInfo.snapshot, (err, valid) => {
callback(err, !valid);
@ -435,10 +493,16 @@ class ContextModule extends Module {
);
return;
}
if (!this.context && !this.options.resource) return callback();
compilation.fileSystemInfo.createSnapshot(
startTime,
null,
[this.context],
this.context
? [this.context]
: typeof this.options.resource === "string"
? [this.options.resource]
: /** @type {string[]} */ (this.options.resource),
null,
SNAPSHOT_OPTIONS,
(err, snapshot) => {
@ -462,7 +526,15 @@ class ContextModule extends Module {
missingDependencies,
buildDependencies
) {
contextDependencies.add(this.context);
if (this.context) {
contextDependencies.add(this.context);
} else if (typeof this.options.resource === "string") {
contextDependencies.add(this.options.resource);
} else if (this.options.resource === false) {
return;
} else {
for (const res of this.options.resource) contextDependencies.add(res);
}
}
/**
@ -998,9 +1070,21 @@ module.exports = webpackEmptyAsyncContext;`;
return this.getSourceForEmptyContext(id, runtimeTemplate);
}
getSource(sourceString) {
/**
* @param {string} sourceString source content
* @param {Compilation=} compilation the compilation
* @returns {Source} generated source
*/
getSource(sourceString, compilation) {
if (this.useSourceMap || this.useSimpleSourceMap) {
return new OriginalSource(sourceString, this.identifier());
return new OriginalSource(
sourceString,
`webpack://${makePathsRelative(
(compilation && compilation.compiler.context) || "",
this.identifier(),
compilation && compilation.compiler.root
)}`
);
}
return new RawSource(sourceString);
}
@ -1010,16 +1094,23 @@ module.exports = webpackEmptyAsyncContext;`;
* @returns {CodeGenerationResult} result
*/
codeGeneration(context) {
const { chunkGraph } = context;
const { chunkGraph, compilation } = context;
const sources = new Map();
sources.set(
"javascript",
this.getSource(this.getSourceString(this.options.mode, context))
this.getSource(
this.getSourceString(this.options.mode, context),
compilation
)
);
const set = new Set();
const allDeps = /** @type {ContextElementDependency[]} */ (
this.dependencies.concat(this.blocks.map(b => b.dependencies[0]))
);
const allDeps =
this.dependencies.length > 0
? /** @type {ContextElementDependency[]} */ (this.dependencies).slice()
: [];
for (const block of this.blocks)
for (const dep of block.dependencies)
allDeps.push(/** @type {ContextElementDependency} */ (dep));
set.add(RuntimeGlobals.module);
set.add(RuntimeGlobals.hasOwnProperty);
if (allDeps.length > 0) {

View File

@ -128,7 +128,7 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
loadersPrefix = "";
const idx = request.lastIndexOf("!");
if (idx >= 0) {
let loadersRequest = request.substr(0, idx + 1);
let loadersRequest = request.slice(0, idx + 1);
let i;
for (
i = 0;
@ -138,7 +138,7 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
loadersPrefix += "!";
}
loadersRequest = loadersRequest
.substr(i)
.slice(i)
.replace(/!+$/, "")
.replace(/!!+/g, "!");
if (loadersRequest === "") {
@ -146,7 +146,7 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
} else {
loaders = loadersRequest.split("!");
}
resource = request.substr(idx + 1);
resource = request.slice(idx + 1);
} else {
loaders = [];
resource = request;
@ -167,6 +167,9 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
asyncLib.parallel(
[
callback => {
const results = [];
const yield_ = obj => results.push(obj);
contextResolver.resolve(
{},
context,
@ -174,11 +177,12 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
{
fileDependencies,
missingDependencies,
contextDependencies
contextDependencies,
yield: yield_
},
(err, result) => {
err => {
if (err) return callback(err);
callback(null, result);
callback(null, results);
}
);
},
@ -213,15 +217,25 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
contextDependencies
});
}
let [contextResult, loaderResult] = result;
if (contextResult.length > 1) {
const first = contextResult[0];
contextResult = contextResult.filter(r => r.path);
if (contextResult.length === 0) contextResult.push(first);
}
this.hooks.afterResolve.callAsync(
{
addon:
loadersPrefix +
result[1].join("!") +
(result[1].length > 0 ? "!" : ""),
resource: result[0],
loaderResult.join("!") +
(loaderResult.length > 0 ? "!" : ""),
resource:
contextResult.length > 1
? contextResult.map(r => r.path)
: contextResult[0].path,
resolveDependencies: this.resolveDependencies.bind(this),
resourceQuery: contextResult[0].query,
resourceFragment: contextResult[0].fragment,
...beforeResolveResult
},
(err, result) => {
@ -278,26 +292,27 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
} = options;
if (!regExp || !resource) return callback(null, []);
const addDirectoryChecked = (directory, visited, callback) => {
const addDirectoryChecked = (ctx, directory, visited, callback) => {
fs.realpath(directory, (err, realPath) => {
if (err) return callback(err);
if (visited.has(realPath)) return callback(null, []);
let recursionStack;
addDirectory(
ctx,
directory,
(dir, callback) => {
(_, dir, callback) => {
if (recursionStack === undefined) {
recursionStack = new Set(visited);
recursionStack.add(realPath);
}
addDirectoryChecked(dir, recursionStack, callback);
addDirectoryChecked(ctx, dir, recursionStack, callback);
},
callback
);
});
};
const addDirectory = (directory, addSubDirectory, callback) => {
const addDirectory = (ctx, directory, addSubDirectory, callback) => {
fs.readdir(directory, (err, files) => {
if (err) return callback(err);
const processedFiles = cmf.hooks.contextModuleFiles.call(
@ -324,16 +339,15 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
if (stat.isDirectory()) {
if (!recursive) return callback();
addSubDirectory(subResource, callback);
addSubDirectory(ctx, subResource, callback);
} else if (
stat.isFile() &&
(!include || subResource.match(include))
) {
const obj = {
context: resource,
context: ctx,
request:
"." +
subResource.substr(resource.length).replace(/\\/g, "/")
"." + subResource.slice(ctx.length).replace(/\\/g, "/")
};
this.hooks.alternativeRequests.callAsync(
@ -345,11 +359,12 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
.filter(obj => regExp.test(obj.request))
.map(obj => {
const dep = new ContextElementDependency(
obj.request + resourceQuery + resourceFragment,
`${obj.request}${resourceQuery}${resourceFragment}`,
obj.request,
typePrefix,
category,
referencedExports
referencedExports,
obj.context
);
dep.optional = true;
return dep;
@ -382,12 +397,37 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
});
};
if (typeof fs.realpath === "function") {
addDirectoryChecked(resource, new Set(), callback);
const addSubDirectory = (ctx, dir, callback) =>
addDirectory(ctx, dir, addSubDirectory, callback);
const visitResource = (resource, callback) => {
if (typeof fs.realpath === "function") {
addDirectoryChecked(resource, resource, new Set(), callback);
} else {
addDirectory(resource, resource, addSubDirectory, callback);
}
};
if (typeof resource === "string") {
visitResource(resource, callback);
} else {
const addSubDirectory = (dir, callback) =>
addDirectory(dir, addSubDirectory, callback);
addDirectory(resource, addSubDirectory, callback);
asyncLib.map(resource, visitResource, (err, result) => {
if (err) return callback(err);
// result dependencies should have unique userRequest
// ordered by resolve result
const temp = new Set();
const res = [];
for (let i = 0; i < result.length; i++) {
const inner = result[i];
for (const el of inner) {
if (temp.has(el.userRequest)) continue;
res.push(el);
temp.add(el.userRequest);
}
}
callback(null, res);
});
}
}
};

View File

@ -91,7 +91,7 @@ class DelegatedModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -29,7 +29,7 @@ class DelegatedModuleFactoryPlugin {
const [dependency] = data.dependencies;
const { request } = dependency;
if (request && request.startsWith(`${scope}/`)) {
const innerRequest = "." + request.substr(scope.length);
const innerRequest = "." + request.slice(scope.length);
let resolved;
if (innerRequest in this.options.content) {
resolved = this.options.content[innerRequest];

View File

@ -172,6 +172,23 @@ class Dependency {
this._loc = loc;
}
setLoc(startLine, startColumn, endLine, endColumn) {
this._locSL = startLine;
this._locSC = startColumn;
this._locEL = endLine;
this._locEC = endColumn;
this._locI = undefined;
this._locN = undefined;
this._loc = undefined;
}
/**
* @returns {string | undefined} a request context
*/
getContext() {
return undefined;
}
/**
* @returns {string | null} an identifier to merge equal requests
*/

View File

@ -7,6 +7,7 @@
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./CodeGenerationResults")} CodeGenerationResults */
/** @typedef {import("./ConcatenationScope")} ConcatenationScope */
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./Dependency").RuntimeSpec} RuntimeSpec */
@ -28,9 +29,17 @@
* @property {RuntimeSpec} runtime current runtimes, for which code is generated
* @property {InitFragment<GenerateContext>[]} initFragments mutable array of init fragments for the current module
* @property {ConcatenationScope=} concatenationScope when in a concatenated module, information about other concatenated modules
* @property {CodeGenerationResults} codeGenerationResults the code generation results
* @property {InitFragment<GenerateContext>[]} chunkInitFragments chunkInitFragments
*/
/**
* @typedef {Object} CssDependencyTemplateContextExtras
* @property {Map<string, string>} cssExports the css exports
*/
/** @typedef {DependencyTemplateContext & CssDependencyTemplateContextExtras} CssDependencyTemplateContext */
class DependencyTemplate {
/* istanbul ignore next */
/**

View File

@ -57,7 +57,7 @@ class DependencyTemplates {
}
clone() {
const newInstance = new DependencyTemplates();
const newInstance = new DependencyTemplates(this._hashFunction);
newInstance._map = new Map(this._map);
newInstance._hash = this._hash;
return newInstance;

View File

@ -96,7 +96,7 @@ class DllModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -62,6 +62,7 @@ class EntryOptionPlugin {
runtime: desc.runtime,
layer: desc.layer,
dependOn: desc.dependOn,
baseUri: desc.baseUri,
publicPath: desc.publicPath,
chunkLoading: desc.chunkLoading,
asyncChunks: desc.asyncChunks,

View File

@ -43,8 +43,8 @@ exports.cutOffMessage = (stack, message) => {
if (nextLine === -1) {
return stack === message ? "" : stack;
} else {
const firstLine = stack.substr(0, nextLine);
return firstLine === message ? stack.substr(nextLine + 1) : stack;
const firstLine = stack.slice(0, nextLine);
return firstLine === message ? stack.slice(nextLine + 1) : stack;
}
};

View File

@ -8,6 +8,7 @@
const { ConcatSource, RawSource } = require("webpack-sources");
const ExternalModule = require("./ExternalModule");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const RuntimeGlobals = require("./RuntimeGlobals");
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
/** @typedef {import("webpack-sources").Source} Source */
@ -77,7 +78,13 @@ class EvalDevToolModulePlugin {
.replace(/^\//, "")
);
const result = new RawSource(
`eval(${JSON.stringify(content + footer)});`
`eval(${
compilation.outputOptions.trustedTypes
? `${RuntimeGlobals.createScript}(${JSON.stringify(
content + footer
)})`
: JSON.stringify(content + footer)
});`
);
cache.set(source, result);
return result;
@ -95,6 +102,14 @@ class EvalDevToolModulePlugin {
hash.update("EvalDevToolModulePlugin");
hash.update("2");
});
if (compilation.outputOptions.trustedTypes) {
compilation.hooks.additionalModuleRuntimeRequirements.tap(
"EvalDevToolModulePlugin",
(module, set, context) => {
set.add(RuntimeGlobals.createScript);
}
);
}
});
}
}

View File

@ -8,6 +8,7 @@
const { ConcatSource, RawSource } = require("webpack-sources");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const NormalModule = require("./NormalModule");
const RuntimeGlobals = require("./RuntimeGlobals");
const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
const ConcatenatedModule = require("./optimize/ConcatenatedModule");
@ -165,7 +166,15 @@ class EvalSourceMapDevToolPlugin {
) + `\n//# sourceURL=webpack-internal:///${moduleId}\n`; // workaround for chrome bug
return result(
new RawSource(`eval(${JSON.stringify(content + footer)});`)
new RawSource(
`eval(${
compilation.outputOptions.trustedTypes
? `${RuntimeGlobals.createScript}(${JSON.stringify(
content + footer
)})`
: JSON.stringify(content + footer)
});`
)
);
}
);
@ -181,6 +190,14 @@ class EvalSourceMapDevToolPlugin {
hash.update("EvalSourceMapDevToolPlugin");
hash.update("2");
});
if (compilation.outputOptions.trustedTypes) {
compilation.hooks.additionalModuleRuntimeRequirements.tap(
"EvalSourceMapDevToolPlugin",
(module, set, context) => {
set.add(RuntimeGlobals.createScript);
}
);
}
}
);
}

View File

@ -291,15 +291,15 @@ class ExportsInfo {
}
}
for (const exportInfo of this._exports.values()) {
if (!canMangle && exportInfo.canMangleProvide !== false) {
exportInfo.canMangleProvide = false;
changed = true;
}
if (excludeExports && excludeExports.has(exportInfo.name)) continue;
if (exportInfo.provided !== true && exportInfo.provided !== null) {
exportInfo.provided = null;
changed = true;
}
if (!canMangle && exportInfo.canMangleProvide !== false) {
exportInfo.canMangleProvide = false;
changed = true;
}
if (targetKey) {
exportInfo.setTarget(targetKey, targetModule, [exportInfo.name], -1);
}

View File

@ -53,6 +53,7 @@ const { register } = require("./util/serialization");
*/
const TYPES = new Set(["javascript"]);
const CSS_TYPES = new Set(["css-import"]);
const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]);
const RUNTIME_REQUIREMENTS_FOR_SCRIPT = new Set([RuntimeGlobals.loadScript]);
const RUNTIME_REQUIREMENTS_FOR_MODULE = new Set([
@ -392,7 +393,7 @@ class ExternalModule extends Module {
* @returns {Set<string>} types available (do not mutate)
*/
getSourceTypes() {
return TYPES;
return this.externalType === "css-import" ? CSS_TYPES : TYPES;
}
/**
@ -409,7 +410,9 @@ class ExternalModule extends Module {
* @returns {boolean} true, if the chunk is ok for the module
*/
chunkCondition(chunk, { chunkGraph }) {
return chunkGraph.getNumberOfEntryModules(chunk) > 0;
return this.externalType === "css-import"
? true
: chunkGraph.getNumberOfEntryModules(chunk) > 0;
}
/**
@ -429,7 +432,7 @@ class ExternalModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {
@ -526,8 +529,14 @@ class ExternalModule extends Module {
return { request, externalType };
}
_getSourceData(runtimeTemplate, moduleGraph, chunkGraph, runtime) {
const { request, externalType } = this._getRequestAndExternalType();
_getSourceData(
request,
externalType,
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime
) {
switch (externalType) {
case "this":
case "window":
@ -541,6 +550,7 @@ class ExternalModule extends Module {
case "commonjs":
case "commonjs2":
case "commonjs-module":
case "commonjs-static":
return getSourceForCommonJsExternal(request);
case "node-commonjs":
return this.buildInfo.module
@ -613,60 +623,90 @@ class ExternalModule extends Module {
runtime,
concatenationScope
}) {
const sourceData = this._getSourceData(
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime
);
const { request, externalType } = this._getRequestAndExternalType();
switch (externalType) {
case "asset": {
const sources = new Map();
sources.set(
"javascript",
new RawSource(`module.exports = ${JSON.stringify(request)};`)
);
const data = new Map();
data.set("url", request);
return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS, data };
}
case "css-import": {
const sources = new Map();
sources.set(
"css-import",
new RawSource(`@import url(${JSON.stringify(request)});`)
);
return {
sources,
runtimeRequirements: EMPTY_RUNTIME_REQUIREMENTS
};
}
default: {
const sourceData = this._getSourceData(
request,
externalType,
runtimeTemplate,
moduleGraph,
chunkGraph,
runtime
);
let sourceString = sourceData.expression;
if (sourceData.iife)
sourceString = `(function() { return ${sourceString}; }())`;
if (concatenationScope) {
sourceString = `${runtimeTemplate.supportsConst() ? "const" : "var"} ${
ConcatenationScope.NAMESPACE_OBJECT_EXPORT
} = ${sourceString};`;
concatenationScope.registerNamespaceExport(
ConcatenationScope.NAMESPACE_OBJECT_EXPORT
);
} else {
sourceString = `module.exports = ${sourceString};`;
}
if (sourceData.init) sourceString = `${sourceData.init}\n${sourceString}`;
let sourceString = sourceData.expression;
if (sourceData.iife)
sourceString = `(function() { return ${sourceString}; }())`;
if (concatenationScope) {
sourceString = `${
runtimeTemplate.supportsConst() ? "const" : "var"
} ${ConcatenationScope.NAMESPACE_OBJECT_EXPORT} = ${sourceString};`;
concatenationScope.registerNamespaceExport(
ConcatenationScope.NAMESPACE_OBJECT_EXPORT
);
} else {
sourceString = `module.exports = ${sourceString};`;
}
if (sourceData.init)
sourceString = `${sourceData.init}\n${sourceString}`;
let data = undefined;
if (sourceData.chunkInitFragments) {
data = new Map();
data.set("chunkInitFragments", sourceData.chunkInitFragments);
}
let data = undefined;
if (sourceData.chunkInitFragments) {
data = new Map();
data.set("chunkInitFragments", sourceData.chunkInitFragments);
}
const sources = new Map();
if (this.useSourceMap || this.useSimpleSourceMap) {
sources.set(
"javascript",
new OriginalSource(sourceString, this.identifier())
);
} else {
sources.set("javascript", new RawSource(sourceString));
}
const sources = new Map();
if (this.useSourceMap || this.useSimpleSourceMap) {
sources.set(
"javascript",
new OriginalSource(sourceString, this.identifier())
);
} else {
sources.set("javascript", new RawSource(sourceString));
}
let runtimeRequirements = sourceData.runtimeRequirements;
if (!concatenationScope) {
if (!runtimeRequirements) {
runtimeRequirements = RUNTIME_REQUIREMENTS;
} else {
const set = new Set(runtimeRequirements);
set.add(RuntimeGlobals.module);
runtimeRequirements = set;
let runtimeRequirements = sourceData.runtimeRequirements;
if (!concatenationScope) {
if (!runtimeRequirements) {
runtimeRequirements = RUNTIME_REQUIREMENTS;
} else {
const set = new Set(runtimeRequirements);
set.add(RuntimeGlobals.module);
runtimeRequirements = set;
}
}
return {
sources,
runtimeRequirements:
runtimeRequirements || EMPTY_RUNTIME_REQUIREMENTS,
data
};
}
}
return {
sources,
runtimeRequirements: runtimeRequirements || EMPTY_RUNTIME_REQUIREMENTS,
data
};
}
/**

View File

@ -89,8 +89,8 @@ class ExternalModuleFactoryPlugin {
UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
) {
const idx = externalConfig.indexOf(" ");
type = externalConfig.substr(0, idx);
externalConfig = externalConfig.substr(idx + 1);
type = externalConfig.slice(0, idx);
externalConfig = externalConfig.slice(idx + 1);
} else if (
Array.isArray(externalConfig) &&
externalConfig.length > 0 &&
@ -98,9 +98,9 @@ class ExternalModuleFactoryPlugin {
) {
const firstItem = externalConfig[0];
const idx = firstItem.indexOf(" ");
type = firstItem.substr(0, idx);
type = firstItem.slice(0, idx);
externalConfig = [
firstItem.substr(idx + 1),
firstItem.slice(idx + 1),
...externalConfig.slice(1)
];
}
@ -117,7 +117,7 @@ class ExternalModuleFactoryPlugin {
/**
* @param {Externals} externals externals config
* @param {function(Error=, ExternalModule=): void} callback callback
* @param {function((Error | null)=, ExternalModule=): void} callback callback
* @returns {void}
*/
const handleExternals = (externals, callback) => {

View File

@ -6,6 +6,7 @@
"use strict";
const { create: createResolver } = require("enhanced-resolve");
const nodeModule = require("module");
const asyncLib = require("neo-async");
const AsyncQueue = require("./util/AsyncQueue");
const StackedCacheMap = require("./util/StackedCacheMap");
@ -22,6 +23,8 @@ const processAsyncTree = require("./util/processAsyncTree");
const supportsEsm = +process.versions.modules >= 83;
const builtinModules = new Set(nodeModule.builtinModules);
let FS_ACCURACY = 2000;
const EMPTY_SET = new Set();
@ -1185,7 +1188,7 @@ class FileSystemInfo {
/**
* @param {string} path file path
* @param {function(WebpackError=, (FileSystemInfoEntry | "ignore" | null)=): void} callback callback function
* @param {function((WebpackError | null)=, (FileSystemInfoEntry | "ignore" | null)=): void} callback callback function
* @returns {void}
*/
getFileTimestamp(path, callback) {
@ -1196,7 +1199,7 @@ class FileSystemInfo {
/**
* @param {string} path context path
* @param {function(WebpackError=, (ResolvedContextFileSystemInfoEntry | "ignore" | null)=): void} callback callback function
* @param {function((WebpackError | null)=, (ResolvedContextFileSystemInfoEntry | "ignore" | null)=): void} callback callback function
* @returns {void}
*/
getContextTimestamp(path, callback) {
@ -1217,7 +1220,7 @@ class FileSystemInfo {
/**
* @param {string} path context path
* @param {function(WebpackError=, (ContextFileSystemInfoEntry | "ignore" | null)=): void} callback callback function
* @param {function((WebpackError | null)=, (ContextFileSystemInfoEntry | "ignore" | null)=): void} callback callback function
* @returns {void}
*/
_getUnresolvedContextTimestamp(path, callback) {
@ -1228,7 +1231,7 @@ class FileSystemInfo {
/**
* @param {string} path file path
* @param {function(WebpackError=, string=): void} callback callback function
* @param {function((WebpackError | null)=, string=): void} callback callback function
* @returns {void}
*/
getFileHash(path, callback) {
@ -1239,7 +1242,7 @@ class FileSystemInfo {
/**
* @param {string} path context path
* @param {function(WebpackError=, string=): void} callback callback function
* @param {function((WebpackError | null)=, string=): void} callback callback function
* @returns {void}
*/
getContextHash(path, callback) {
@ -1259,7 +1262,7 @@ class FileSystemInfo {
/**
* @param {string} path context path
* @param {function(WebpackError=, ContextHash=): void} callback callback function
* @param {function((WebpackError | null)=, ContextHash=): void} callback callback function
* @returns {void}
*/
_getUnresolvedContextHash(path, callback) {
@ -1270,7 +1273,7 @@ class FileSystemInfo {
/**
* @param {string} path context path
* @param {function(WebpackError=, ResolvedContextTimestampAndHash=): void} callback callback function
* @param {function((WebpackError | null)=, ResolvedContextTimestampAndHash=): void} callback callback function
* @returns {void}
*/
getContextTsh(path, callback) {
@ -1290,7 +1293,7 @@ class FileSystemInfo {
/**
* @param {string} path context path
* @param {function(WebpackError=, ContextTimestampAndHash=): void} callback callback function
* @param {function((WebpackError | null)=, ContextTimestampAndHash=): void} callback callback function
* @returns {void}
*/
_getUnresolvedContextTsh(path, callback) {
@ -1330,7 +1333,7 @@ class FileSystemInfo {
/**
* @param {string} context context directory
* @param {Iterable<string>} deps dependencies
* @param {function(Error=, ResolveBuildDependenciesResult=): void} callback callback function
* @param {function((Error | null)=, ResolveBuildDependenciesResult=): void} callback callback function
* @returns {void}
*/
resolveBuildDependencies(context, deps, callback) {
@ -1673,6 +1676,11 @@ class FileSystemInfo {
// e.g. import.meta
continue;
}
// we should not track Node.js build dependencies
if (dependency.startsWith("node:")) continue;
if (builtinModules.has(dependency)) continue;
push({
type: RBDT_RESOLVE_ESM_FILE,
context,
@ -1798,7 +1806,7 @@ class FileSystemInfo {
/**
* @param {Map<string, string | false>} resolveResults results from resolving
* @param {function(Error=, boolean=): void} callback callback with true when resolveResults resolve the same way
* @param {function((Error | null)=, boolean=): void} callback callback with true when resolveResults resolve the same way
* @returns {void}
*/
checkResolveResultsValid(resolveResults, callback) {
@ -1880,7 +1888,7 @@ class FileSystemInfo {
* @param {Object} options options object (for future extensions)
* @param {boolean=} options.hash should use hash to snapshot
* @param {boolean=} options.timestamp should use timestamp to snapshot
* @param {function(WebpackError=, Snapshot=): void} callback callback function
* @param {function((WebpackError | null)=, (Snapshot | null)=): void} callback callback function
* @returns {void}
*/
createSnapshot(startTime, files, directories, missing, options, callback) {
@ -2256,8 +2264,10 @@ class FileSystemInfo {
for (const path of managedItems) {
const cache = this._managedItems.get(path);
if (cache !== undefined) {
if (cache !== "missing") {
if (!cache.startsWith("*")) {
managedFiles.add(join(this.fs, path, "package.json"));
} else if (cache === "*nested") {
managedMissing.add(join(this.fs, path, "package.json"));
}
managedItemInfo.set(path, cache);
} else {
@ -2271,8 +2281,10 @@ class FileSystemInfo {
}
jobError();
} else if (entry) {
if (entry !== "missing") {
if (!entry.startsWith("*")) {
managedFiles.add(join(this.fs, path, "package.json"));
} else if (cache === "*nested") {
managedMissing.add(join(this.fs, path, "package.json"));
}
managedItemInfo.set(path, entry);
jobDone();
@ -2375,7 +2387,7 @@ class FileSystemInfo {
/**
* @param {Snapshot} snapshot the snapshot made
* @param {function(WebpackError=, boolean=): void} callback callback function
* @param {function((WebpackError | null)=, boolean=): void} callback callback function
* @returns {void}
*/
checkSnapshotValid(snapshot, callback) {
@ -2395,7 +2407,7 @@ class FileSystemInfo {
/**
* @param {Snapshot} snapshot the snapshot made
* @param {function(WebpackError=, boolean=): void} callback callback function
* @param {function((WebpackError | null)=, boolean=): void} callback callback function
* @returns {void}
*/
_checkSnapshotValidNoCache(snapshot, callback) {
@ -2944,7 +2956,7 @@ class FileSystemInfo {
* @param {function(string, IStats, function(Error=, ItemType=): void): void} options.fromFile called when context item is a file
* @param {function(string, IStats, function(Error=, ItemType=): void): void} options.fromDirectory called when context item is a directory
* @param {function(string[], ItemType[]): T} options.reduce called from all context items
* @param {function(Error=, (T)=): void} callback callback
* @param {function((Error | null)=, (T)=): void} callback callback
*/
_readContext(
{
@ -3127,7 +3139,7 @@ class FileSystemInfo {
/**
* @param {ContextFileSystemInfoEntry} entry entry
* @param {function(Error=, ResolvedContextFileSystemInfoEntry=): void} callback callback
* @param {function((Error | null)=, ResolvedContextFileSystemInfoEntry=): void} callback callback
* @returns {void}
*/
_resolveContextTimestamp(entry, callback) {
@ -3235,7 +3247,7 @@ class FileSystemInfo {
/**
* @param {ContextHash} entry context hash
* @param {function(Error=, string=): void} callback callback
* @param {function((Error | null)=, string=): void} callback callback
* @returns {void}
*/
_resolveContextHash(entry, callback) {
@ -3391,7 +3403,7 @@ class FileSystemInfo {
/**
* @param {ContextTimestampAndHash} entry entry
* @param {function(Error=, ResolvedContextTimestampAndHash=): void} callback callback
* @param {function((Error | null)=, ResolvedContextTimestampAndHash=): void} callback callback
* @returns {void}
*/
_resolveContextTsh(entry, callback) {
@ -3471,8 +3483,8 @@ class FileSystemInfo {
}
if (!elements.has(path)) {
// file or directory doesn't exist
this._managedItems.set(path, "missing");
return callback(null, "missing");
this._managedItems.set(path, "*missing");
return callback(null, "*missing");
}
// something exists
// it may be a file or directory
@ -3481,8 +3493,8 @@ class FileSystemInfo {
(path.endsWith("/node_modules") || path.endsWith("\\node_modules"))
) {
// we are only interested in existence of this special directory
this._managedItems.set(path, "exists");
return callback(null, "exists");
this._managedItems.set(path, "*node_modules");
return callback(null, "*node_modules");
}
// we assume it's a directory, as files shouldn't occur in managed paths
@ -3499,8 +3511,8 @@ class FileSystemInfo {
) {
// This is only a grouping folder e. g. used by yarn
// we are only interested in existence of this special directory
this._managedItems.set(path, "nested");
return callback(null, "nested");
this._managedItems.set(path, "*nested");
return callback(null, "*nested");
}
this.logger.warn(
`Managed item ${path} isn't a directory or doesn't contain a package.json (see snapshot.managedPaths option)`

View File

@ -7,6 +7,7 @@
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./CodeGenerationResults")} CodeGenerationResults */
/** @typedef {import("./Compilation")} Compilation */
/** @typedef {import("./ConcatenationScope")} ConcatenationScope */
/** @typedef {import("./DependencyTemplate")} DependencyTemplate */
@ -27,6 +28,7 @@
* @property {Set<string>} runtimeRequirements the requirements for runtime
* @property {RuntimeSpec} runtime the runtime
* @property {ConcatenationScope=} concatenationScope when in concatenated module, information about other concatenated modules
* @property {CodeGenerationResults=} codeGenerationResults code generation results of other modules (need to have a codeGenerationDependency to use that)
* @property {string} type which kind of code should be generated
* @property {function(): Map<string, any>=} getData get access to the code generation data
*/
@ -36,6 +38,7 @@
* @property {NormalModule} module the module
* @property {ChunkGraph} chunkGraph
* @property {RuntimeSpec} runtime
* @property {RuntimeTemplate=} runtimeTemplate
*/
/**

View File

@ -51,7 +51,7 @@ module.exports.makeWebpackError = makeWebpackError;
/**
* @template T
* @param {function(WebpackError=, T=): void} callback webpack error callback
* @param {function((WebpackError | null)=, T=): void} callback webpack error callback
* @param {string} hook name of hook
* @returns {Callback<T>} generic callback
*/

View File

@ -52,7 +52,7 @@ class LoaderOptionsPlugin {
if (
ModuleFilenameHelpers.matchObject(
options,
i < 0 ? resource : resource.substr(0, i)
i < 0 ? resource : resource.slice(0, i)
)
) {
for (const key of Object.keys(options)) {

View File

@ -19,6 +19,7 @@ const makeSerializable = require("./util/makeSerializable");
/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./CodeGenerationResults")} CodeGenerationResults */
/** @typedef {import("./Compilation")} Compilation */
/** @typedef {import("./ConcatenationScope")} ConcatenationScope */
/** @typedef {import("./Dependency")} Dependency */
@ -48,6 +49,7 @@ const makeSerializable = require("./util/makeSerializable");
* @property {string=} type the type of source that should be generated
*/
// TODO webpack 6: compilation will be required in CodeGenerationContext
/**
* @typedef {Object} CodeGenerationContext
* @property {DependencyTemplates} dependencyTemplates the dependency templates
@ -56,6 +58,8 @@ const makeSerializable = require("./util/makeSerializable");
* @property {ChunkGraph} chunkGraph the chunk graph
* @property {RuntimeSpec} runtime the runtimes code should be generated for
* @property {ConcatenationScope=} concatenationScope when in concatenated module, information about other concatenated modules
* @property {CodeGenerationResults} codeGenerationResults code generation results of other modules (need to have a codeGenerationDependency to use that)
* @property {Compilation=} compilation the compilation
*/
/**
@ -165,6 +169,8 @@ class Module extends DependenciesBlock {
this.buildInfo = undefined;
/** @type {Dependency[] | undefined} */
this.presentationalDependencies = undefined;
/** @type {Dependency[] | undefined} */
this.codeGenerationDependencies = undefined;
}
// TODO remove in webpack 6
@ -396,7 +402,6 @@ class Module extends DependenciesBlock {
// BACKWARD-COMPAT END
/**
* @deprecated moved to .buildInfo.exportsArgument
* @returns {string} name of the exports argument
*/
get exportsArgument() {
@ -404,7 +409,6 @@ class Module extends DependenciesBlock {
}
/**
* @deprecated moved to .buildInfo.moduleArgument
* @returns {string} name of the module argument
*/
get moduleArgument() {
@ -493,6 +497,19 @@ class Module extends DependenciesBlock {
this.presentationalDependencies.push(presentationalDependency);
}
/**
* @param {Dependency} codeGenerationDependency dependency being tied to module.
* This is a Dependency where the code generation result of the referenced module is needed during code generation.
* The Dependency should also be added to normal dependencies via addDependency.
* @returns {void}
*/
addCodeGenerationDependency(codeGenerationDependency) {
if (this.codeGenerationDependencies === undefined) {
this.codeGenerationDependencies = [];
}
this.codeGenerationDependencies.push(codeGenerationDependency);
}
/**
* Removes all dependencies and blocks
* @returns {void}
@ -501,6 +518,9 @@ class Module extends DependenciesBlock {
if (this.presentationalDependencies !== undefined) {
this.presentationalDependencies.length = 0;
}
if (this.codeGenerationDependencies !== undefined) {
this.codeGenerationDependencies.length = 0;
}
super.clearDependenciesAndBlocks();
}
@ -671,7 +691,7 @@ class Module extends DependenciesBlock {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {
@ -799,7 +819,8 @@ class Module extends DependenciesBlock {
runtimeTemplate,
moduleGraph: chunkGraph.moduleGraph,
chunkGraph,
runtime: undefined
runtime: undefined,
codeGenerationResults: undefined
};
const sources = this.codeGeneration(codeGenContext).sources;
return type ? sources.get(type) : sources.get(first(this.getSourceTypes()));
@ -976,6 +997,7 @@ class Module extends DependenciesBlock {
write(this.buildMeta);
write(this.buildInfo);
write(this.presentationalDependencies);
write(this.codeGenerationDependencies);
super.serialize(context);
}
@ -993,6 +1015,7 @@ class Module extends DependenciesBlock {
this.buildMeta = read();
this.buildInfo = read();
this.presentationalDependencies = read();
this.codeGenerationDependencies = read();
super.deserialize(context);
}
}

View File

@ -5,6 +5,7 @@
"use strict";
const NormalModule = require("./NormalModule");
const createHash = require("./util/createHash");
const memoize = require("./util/memoize");
@ -46,7 +47,7 @@ const getAfter = (strFn, token) => {
return () => {
const str = strFn();
const idx = str.indexOf(token);
return idx < 0 ? "" : str.substr(idx);
return idx < 0 ? "" : str.slice(idx);
};
};
@ -54,7 +55,7 @@ const getBefore = (strFn, token) => {
return () => {
const str = strFn();
const idx = str.lastIndexOf(token);
return idx < 0 ? "" : str.substr(0, idx);
return idx < 0 ? "" : str.slice(0, idx);
};
};
@ -63,7 +64,7 @@ const getHash = (strFn, hashFunction) => {
const hash = createHash(hashFunction);
hash.update(strFn());
const digest = /** @type {string} */ (hash.digest("hex"));
return digest.substr(0, 4);
return digest.slice(0, 4);
};
};
@ -138,7 +139,10 @@ ModuleFilenameHelpers.createFilename = (
);
identifier = memoize(() => requestShortener.shorten(module.identifier()));
moduleId = () => chunkGraph.getModuleId(module);
absoluteResourcePath = () => module.identifier().split("!").pop();
absoluteResourcePath = () =>
module instanceof NormalModule
? module.resource
: module.identifier().split("!").pop();
hash = getHash(identifier, hashFunction);
}
const resource = memoize(() => shortIdentifier().split("!").pop());

29
lib/ModuleHashingError.js Normal file
View File

@ -0,0 +1,29 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const WebpackError = require("./WebpackError");
/** @typedef {import("./Module")} Module */
class ModuleHashingError extends WebpackError {
/**
* Create a new ModuleHashingError
* @param {Module} module related module
* @param {Error} error Original error
*/
constructor(module, error) {
super();
this.name = "ModuleHashingError";
this.error = error;
this.message = error.message;
this.details = error.stack;
this.module = module;
}
}
module.exports = ModuleHashingError;

View File

@ -27,7 +27,7 @@ const ArrayQueue = require("./util/ArrayQueue");
/**
* @template T
* @callback Callback
* @param {Error=} err
* @param {(Error | null)=} err
* @param {T=} result
*/

View File

@ -13,7 +13,7 @@ const asyncLib = require("neo-async");
/**
* @template T
* @callback Callback
* @param {Error=} err
* @param {(Error | null)=} err
* @param {T=} result
*/

View File

@ -81,6 +81,16 @@ class NodeStuffPlugin {
);
}
});
parser.hooks.rename.for("global").tap("NodeStuffPlugin", expr => {
const dep = new ConstDependency(
RuntimeGlobals.global,
expr.range,
[RuntimeGlobals.global]
);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return false;
});
}
const setModuleConstant = (expressionName, fn, warning) => {

View File

@ -50,6 +50,7 @@ const memoize = require("./util/memoize");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../declarations/LoaderContext").NormalModuleLoaderContext} NormalModuleLoaderContext */
/** @typedef {import("../declarations/WebpackOptions").Mode} Mode */
/** @typedef {import("../declarations/WebpackOptions").ResolveOptions} ResolveOptions */
/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./Compiler")} Compiler */
@ -194,6 +195,25 @@ makeSerializable(
* @property {AsyncSeriesBailHook<[NormalModule, NeedBuildContext], boolean>} needBuild
*/
/**
* @typedef {Object} NormalModuleCreateData
* @property {string=} layer an optional layer in which the module is
* @property {string} type module type
* @property {string} request request string
* @property {string} userRequest request intended by user (without loaders from config)
* @property {string} rawRequest request without resolving
* @property {LoaderItem[]} loaders list of loaders
* @property {string} resource path + query of the real resource
* @property {Record<string, any>=} resourceResolveData resource resolve data
* @property {string} context context directory for resolving
* @property {string=} matchResource path + query of the matched resource (virtual)
* @property {Parser} parser the parser used
* @property {Record<string, any>=} parserOptions the options of the parser used
* @property {Generator} generator the generator used
* @property {Record<string, any>=} generatorOptions the options of the generator used
* @property {ResolveOptions=} resolveOptions options used for resolving requests from this module
*/
/** @type {WeakMap<Compilation, NormalModuleCompilationHooks>} */
const compilationHooksMap = new WeakMap();
@ -246,22 +266,7 @@ class NormalModule extends Module {
}
/**
* @param {Object} options options object
* @param {string=} options.layer an optional layer in which the module is
* @param {string} options.type module type
* @param {string} options.request request string
* @param {string} options.userRequest request intended by user (without loaders from config)
* @param {string} options.rawRequest request without resolving
* @param {LoaderItem[]} options.loaders list of loaders
* @param {string} options.resource path + query of the real resource
* @param {Record<string, any>=} options.resourceResolveData resource resolve data
* @param {string} options.context context directory for resolving
* @param {string | undefined} options.matchResource path + query of the matched resource (virtual)
* @param {Parser} options.parser the parser used
* @param {object} options.parserOptions the options of the parser used
* @param {Generator} options.generator the generator used
* @param {object} options.generatorOptions the options of the generator used
* @param {Object} options.resolveOptions options used for resolving requests from this module
* @param {NormalModuleCreateData} options options object
*/
constructor({
layer,
@ -310,7 +315,7 @@ class NormalModule extends Module {
}
// Info from Build
/** @type {WebpackError=} */
/** @type {(WebpackError | null)=} */
this.error = null;
/** @private @type {Source=} */
this._source = null;
@ -355,11 +360,13 @@ class NormalModule extends Module {
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
return contextify(
let ident = contextify(
options.context,
this.userRequest,
options.associatedObjectForCache
);
if (this.layer) ident = `(${this.layer})/${ident}`;
return ident;
}
/**
@ -368,7 +375,7 @@ class NormalModule extends Module {
nameForCondition() {
const resource = this.matchResource || this.resource;
const idx = resource.indexOf("?");
if (idx >= 0) return resource.substr(0, idx);
if (idx >= 0) return resource.slice(0, idx);
return resource;
}
@ -551,7 +558,7 @@ class NormalModule extends Module {
let { options } = loader;
if (typeof options === "string") {
if (options.substr(0, 1) === "{" && options.substr(-1) === "}") {
if (options.startsWith("{") && options.endsWith("}")) {
try {
options = parseJson(options);
} catch (e) {
@ -730,7 +737,7 @@ class NormalModule extends Module {
* @param {ResolverWithOptions} resolver the resolver
* @param {InputFileSystem} fs the file system
* @param {NormalModuleCompilationHooks} hooks the hooks
* @param {function(WebpackError=): void} callback callback function
* @param {function((WebpackError | null)=): void} callback callback function
* @returns {void}
*/
_doBuild(options, compilation, resolver, fs, hooks, callback) {
@ -1168,7 +1175,8 @@ class NormalModule extends Module {
moduleGraph,
chunkGraph,
runtime,
concatenationScope
concatenationScope,
codeGenerationResults
}) {
/** @type {Set<string>} */
const runtimeRequirements = new Set();
@ -1200,6 +1208,7 @@ class NormalModule extends Module {
runtimeRequirements,
runtime,
concatenationScope,
codeGenerationResults,
getData,
type
});
@ -1234,7 +1243,7 @@ class NormalModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -28,17 +28,25 @@ const LazySet = require("./util/LazySet");
const { getScheme } = require("./util/URLAbsoluteSpecifier");
const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
const { join } = require("./util/fs");
const { parseResource } = require("./util/identifier");
const {
parseResource,
parseResourceWithoutFragment
} = require("./util/identifier");
/** @typedef {import("../declarations/WebpackOptions").ModuleOptionsNormalized} ModuleOptions */
/** @typedef {import("../declarations/WebpackOptions").RuleSetRule} RuleSetRule */
/** @typedef {import("./Generator")} Generator */
/** @typedef {import("./ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
/** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
/** @typedef {import("./NormalModule").NormalModuleCreateData} NormalModuleCreateData */
/** @typedef {import("./Parser")} Parser */
/** @typedef {import("./ResolverFactory")} ResolverFactory */
/** @typedef {import("./dependencies/ModuleDependency")} ModuleDependency */
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
/** @typedef {Pick<RuleSetRule, 'type'|'sideEffects'|'parser'|'generator'|'resolve'|'layer'>} ModuleSettings */
/** @typedef {Partial<NormalModuleCreateData & {settings: ModuleSettings}>} CreateData */
/**
* @typedef {Object} ResolveData
* @property {ModuleFactoryCreateData["contextInfo"]} contextInfo
@ -48,7 +56,7 @@ const { parseResource } = require("./util/identifier");
* @property {Record<string, any> | undefined} assertions
* @property {ModuleDependency[]} dependencies
* @property {string} dependencyType
* @property {Object} createData
* @property {CreateData} createData
* @property {LazySet<string>} fileDependencies
* @property {LazySet<string>} missingDependencies
* @property {LazySet<string>} contextDependencies
@ -66,6 +74,11 @@ const { parseResource } = require("./util/identifier");
/** @typedef {ResourceData & { data: Record<string, any> }} ResourceDataWithData */
/** @typedef {Object} ParsedLoaderRequest
* @property {string} loader loader
* @property {string|undefined} options options
*/
const EMPTY_RESOLVE_OPTIONS = {};
const EMPTY_PARSER_OPTIONS = {};
const EMPTY_GENERATOR_OPTIONS = {};
@ -97,27 +110,6 @@ const stringifyLoadersAndResource = (loaders, resource) => {
return str + resource;
};
/**
* @param {string} resultString resultString
* @returns {{loader: string, options: string|undefined}} parsed loader request
*/
const identToLoaderRequest = resultString => {
const idx = resultString.indexOf("?");
if (idx >= 0) {
const loader = resultString.substr(0, idx);
const options = resultString.substr(idx + 1);
return {
loader,
options
};
} else {
return {
loader: resultString,
options: undefined
};
}
};
const needCalls = (times, callback) => {
return err => {
if (--times === 0) {
@ -212,7 +204,7 @@ class NormalModuleFactory extends ModuleFactory {
}) {
super();
this.hooks = Object.freeze({
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
/** @type {AsyncSeriesBailHook<[ResolveData], Module | false | void>} */
resolve: new AsyncSeriesBailHook(["resolveData"]),
/** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
resolveForScheme: new HookMap(
@ -222,15 +214,15 @@ class NormalModuleFactory extends ModuleFactory {
resolveInScheme: new HookMap(
() => new AsyncSeriesBailHook(["resourceData", "resolveData"])
),
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
/** @type {AsyncSeriesBailHook<[ResolveData], Module>} */
factorize: new AsyncSeriesBailHook(["resolveData"]),
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
/** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
beforeResolve: new AsyncSeriesBailHook(["resolveData"]),
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
/** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
afterResolve: new AsyncSeriesBailHook(["resolveData"]),
/** @type {AsyncSeriesBailHook<[ResolveData["createData"], ResolveData], TODO>} */
/** @type {AsyncSeriesBailHook<[ResolveData["createData"], ResolveData], Module | void>} */
createModule: new AsyncSeriesBailHook(["createData", "resolveData"]),
/** @type {SyncWaterfallHook<[Module, ResolveData["createData"], ResolveData], TODO>} */
/** @type {SyncWaterfallHook<[Module, ResolveData["createData"], ResolveData], Module>} */
module: new SyncWaterfallHook(["module", "createData", "resolveData"]),
createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
@ -264,6 +256,9 @@ class NormalModuleFactory extends ModuleFactory {
const cacheParseResource = parseResource.bindCache(
associatedObjectForCache
);
const cachedParseResourceWithoutFragment =
parseResourceWithoutFragment.bindCache(associatedObjectForCache);
this._parseResourceWithoutFragment = cachedParseResourceWithoutFragment;
this.hooks.factorize.tapAsync(
{
@ -311,7 +306,9 @@ class NormalModuleFactory extends ModuleFactory {
return callback(new Error("Empty dependency (no request)"));
}
createdModule = new NormalModule(createData);
createdModule = new NormalModule(
/** @type {NormalModuleCreateData} */ (createData)
);
}
createdModule = this.hooks.module.call(
@ -351,7 +348,7 @@ class NormalModuleFactory extends ModuleFactory {
let matchResourceData = undefined;
/** @type {string} */
let unresolvedResource;
/** @type {{loader: string, options: string|undefined}[]} */
/** @type {ParsedLoaderRequest[]} */
let elements;
let noPreAutoLoaders = false;
let noAutoLoaders = false;
@ -382,7 +379,7 @@ class NormalModuleFactory extends ModuleFactory {
resource: matchResource,
...cacheParseResource(matchResource)
};
requestWithoutMatchResource = request.substr(
requestWithoutMatchResource = request.slice(
matchResourceMatch[0].length
);
}
@ -405,7 +402,13 @@ class NormalModuleFactory extends ModuleFactory {
)
.split(/!+/);
unresolvedResource = rawElements.pop();
elements = rawElements.map(identToLoaderRequest);
elements = rawElements.map(el => {
const { path, query } = cachedParseResourceWithoutFragment(el);
return {
loader: path,
options: query ? query.slice(1) : undefined
};
});
scheme = getScheme(unresolvedResource);
} else {
unresolvedResource = requestWithoutMatchResource;
@ -434,7 +437,7 @@ class NormalModuleFactory extends ModuleFactory {
try {
for (const item of loaders) {
if (typeof item.options === "string" && item.options[0] === "?") {
const ident = item.options.substr(1);
const ident = item.options.slice(1);
if (ident === "[[missing ident]]") {
throw new Error(
"No ident is provided by referenced loader. " +
@ -1017,12 +1020,14 @@ If changing the source code is not an option there is also a resolve options cal
}
if (err) return callback(err);
const parsedResult = identToLoaderRequest(result);
const parsedResult = this._parseResourceWithoutFragment(result);
const resolved = {
loader: parsedResult.loader,
loader: parsedResult.path,
options:
item.options === undefined
? parsedResult.options
? parsedResult.query
? parsedResult.query.slice(1)
: undefined
: item.options,
ident: item.options === undefined ? undefined : item.ident
};

View File

@ -96,7 +96,7 @@ const createDefaultHandler = (profile, logger) => {
/**
* @callback ReportProgress
* @param {number} p
* @param {...string[]} [args]
* @param {...string} [args]
* @returns {void}
*/
@ -531,15 +531,14 @@ class ProgressPlugin {
}
});
interceptHook(compiler.cache.hooks.endIdle, 0.01, "cache", "end idle");
compiler.hooks.initialize.intercept({
compiler.hooks.beforeRun.intercept({
name: "ProgressPlugin",
call() {
handler(0, "");
}
});
interceptHook(compiler.hooks.initialize, 0.01, "setup", "initialize");
interceptHook(compiler.hooks.beforeRun, 0.02, "setup", "before run");
interceptHook(compiler.hooks.run, 0.03, "setup", "run");
interceptHook(compiler.hooks.beforeRun, 0.01, "setup", "before run");
interceptHook(compiler.hooks.run, 0.02, "setup", "run");
interceptHook(compiler.hooks.watchRun, 0.03, "setup", "watch run");
interceptHook(
compiler.hooks.normalModuleFactory,

View File

@ -74,7 +74,7 @@ class RawModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -164,10 +164,17 @@ exports.scriptNonce = "__webpack_require__.nc";
* function to load a script tag.
* Arguments: (url: string, done: (event) => void), key?: string | number, chunkId?: string | number) => void
* done function is called when loading has finished or timeout occurred.
* It will attach to existing script tags with data-webpack == key or src == url.
* It will attach to existing script tags with data-webpack == uniqueName + ":" + key or src == url.
*/
exports.loadScript = "__webpack_require__.l";
/**
* function to promote a string to a TrustedScript using webpack's Trusted
* Types policy
* Arguments: (script: string) => TrustedScript
*/
exports.createScript = "__webpack_require__.ts";
/**
* function to promote a string to a TrustedScriptURL using webpack's Trusted
* Types policy
@ -175,6 +182,12 @@ exports.loadScript = "__webpack_require__.l";
*/
exports.createScriptUrl = "__webpack_require__.tu";
/**
* function to return webpack's Trusted Types policy
* Arguments: () => TrustedTypePolicy
*/
exports.getTrustedTypesPolicy = "__webpack_require__.tt";
/**
* the chunk name of the chunk with the runtime
*/
@ -190,11 +203,26 @@ exports.runtimeId = "__webpack_require__.j";
*/
exports.getChunkScriptFilename = "__webpack_require__.u";
/**
* the filename of the css part of the chunk
*/
exports.getChunkCssFilename = "__webpack_require__.k";
/**
* a flag when a module/chunk/tree has css modules
*/
exports.hasCssModules = "has css modules";
/**
* the filename of the script part of the hot update chunk
*/
exports.getChunkUpdateScriptFilename = "__webpack_require__.hu";
/**
* the filename of the css part of the hot update chunk
*/
exports.getChunkUpdateCssFilename = "__webpack_require__.hk";
/**
* startup signal from runtime
* This will be called when the runtime chunk has been loaded.

View File

@ -78,7 +78,7 @@ class RuntimeModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -6,18 +6,22 @@
"use strict";
const RuntimeGlobals = require("./RuntimeGlobals");
const { getChunkFilenameTemplate } = require("./css/CssModulesPlugin");
const RuntimeRequirementsDependency = require("./dependencies/RuntimeRequirementsDependency");
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
const AsyncModuleRuntimeModule = require("./runtime/AsyncModuleRuntimeModule");
const AutoPublicPathRuntimeModule = require("./runtime/AutoPublicPathRuntimeModule");
const BaseUriRuntimeModule = require("./runtime/BaseUriRuntimeModule");
const CompatGetDefaultExportRuntimeModule = require("./runtime/CompatGetDefaultExportRuntimeModule");
const CompatRuntimeModule = require("./runtime/CompatRuntimeModule");
const CreateFakeNamespaceObjectRuntimeModule = require("./runtime/CreateFakeNamespaceObjectRuntimeModule");
const CreateScriptRuntimeModule = require("./runtime/CreateScriptRuntimeModule");
const CreateScriptUrlRuntimeModule = require("./runtime/CreateScriptUrlRuntimeModule");
const DefinePropertyGettersRuntimeModule = require("./runtime/DefinePropertyGettersRuntimeModule");
const EnsureChunkRuntimeModule = require("./runtime/EnsureChunkRuntimeModule");
const GetChunkFilenameRuntimeModule = require("./runtime/GetChunkFilenameRuntimeModule");
const GetMainFilenameRuntimeModule = require("./runtime/GetMainFilenameRuntimeModule");
const GetTrustedTypesPolicyRuntimeModule = require("./runtime/GetTrustedTypesPolicyRuntimeModule");
const GlobalRuntimeModule = require("./runtime/GlobalRuntimeModule");
const HasOwnPropertyRuntimeModule = require("./runtime/HasOwnPropertyRuntimeModule");
const LoadScriptRuntimeModule = require("./runtime/LoadScriptRuntimeModule");
@ -39,7 +43,9 @@ const GLOBALS_ON_REQUIRE = [
RuntimeGlobals.runtimeId,
RuntimeGlobals.compatGetDefaultExport,
RuntimeGlobals.createFakeNamespaceObject,
RuntimeGlobals.createScript,
RuntimeGlobals.createScriptUrl,
RuntimeGlobals.getTrustedTypesPolicy,
RuntimeGlobals.definePropertyGetters,
RuntimeGlobals.ensureChunk,
RuntimeGlobals.entryModuleId,
@ -91,6 +97,15 @@ class RuntimePlugin {
*/
apply(compiler) {
compiler.hooks.compilation.tap("RuntimePlugin", compilation => {
const globalChunkLoading = compilation.outputOptions.chunkLoading;
const isChunkLoadingDisabledForChunk = chunk => {
const options = chunk.getEntryOptions();
const chunkLoading =
options && options.chunkLoading !== undefined
? options.chunkLoading
: globalChunkLoading;
return chunkLoading === false;
};
compilation.dependencyTemplates.set(
RuntimeRequirementsDependency,
new RuntimeRequirementsDependency.Template()
@ -261,6 +276,30 @@ class RuntimePlugin {
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.getChunkCssFilename)
.tap("RuntimePlugin", (chunk, set) => {
if (
typeof compilation.outputOptions.cssChunkFilename === "string" &&
/\[(full)?hash(:\d+)?\]/.test(
compilation.outputOptions.cssChunkFilename
)
) {
set.add(RuntimeGlobals.getFullHash);
}
compilation.addRuntimeModule(
chunk,
new GetChunkFilenameRuntimeModule(
"css",
"css",
RuntimeGlobals.getChunkCssFilename,
chunk =>
getChunkFilenameTemplate(chunk, compilation.outputOptions),
set.has(RuntimeGlobals.hmrDownloadUpdateHandlers)
)
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.getChunkUpdateScriptFilename)
.tap("RuntimePlugin", (chunk, set) => {
@ -339,15 +378,36 @@ class RuntimePlugin {
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.createScript)
.tap("RuntimePlugin", (chunk, set) => {
if (compilation.outputOptions.trustedTypes) {
set.add(RuntimeGlobals.getTrustedTypesPolicy);
}
compilation.addRuntimeModule(chunk, new CreateScriptRuntimeModule());
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.createScriptUrl)
.tap("RuntimePlugin", (chunk, set) => {
if (compilation.outputOptions.trustedTypes) {
set.add(RuntimeGlobals.getTrustedTypesPolicy);
}
compilation.addRuntimeModule(
chunk,
new CreateScriptUrlRuntimeModule()
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.getTrustedTypesPolicy)
.tap("RuntimePlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new GetTrustedTypesPolicyRuntimeModule(set)
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.relativeUrl)
.tap("RuntimePlugin", (chunk, set) => {
@ -363,6 +423,14 @@ class RuntimePlugin {
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.baseURI)
.tap("RuntimePlugin", chunk => {
if (isChunkLoadingDisabledForChunk(chunk)) {
compilation.addRuntimeModule(chunk, new BaseUriRuntimeModule());
return true;
}
});
// TODO webpack 6: remove CompatRuntimeModule
compilation.hooks.additionalTreeRuntimeRequirements.tap(
"RuntimePlugin",

View File

@ -16,6 +16,7 @@ const { forEachRuntime, subtractRuntime } = require("./util/runtime");
/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./CodeGenerationResults")} CodeGenerationResults */
/** @typedef {import("./Compilation")} Compilation */
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./Module")} Module */
@ -82,6 +83,7 @@ class RuntimeTemplate {
this.outputOptions = outputOptions || {};
this.requestShortener = requestShortener;
this.globalObject = getGlobalObject(outputOptions.globalObject);
this.contentHashReplacement = "X".repeat(outputOptions.hashDigestLength);
}
isIIFE() {
@ -200,7 +202,7 @@ class RuntimeTemplate {
// when the first two args are expression, we need to prepend "" + to force string
// concatenation instead of number addition.
return typeof args[0] !== "string" && args[1] !== "string"
return typeof args[0] !== "string" && typeof args[1] !== "string"
? `"" + ${str}`
: str;
}
@ -1014,6 +1016,26 @@ class RuntimeTemplate {
runtimeRequirements.add(RuntimeGlobals.exports);
return `${RuntimeGlobals.makeNamespaceObject}(${exportsArgument});\n`;
}
/**
* @param {Object} options options object
* @param {Module} options.module the module
* @param {string} options.publicPath the public path
* @param {RuntimeSpec=} options.runtime runtime
* @param {CodeGenerationResults} options.codeGenerationResults the code generation results
* @returns {string} the url of the asset
*/
assetUrl({ publicPath, runtime, module, codeGenerationResults }) {
if (!module) {
return "data:,";
}
const codeGen = codeGenerationResults.get(module, runtime);
const { data } = codeGen;
const url = data.get("url");
if (url) return url.toString();
const filename = data.get("filename");
return publicPath + filename;
}
}
module.exports = RuntimeTemplate;

View File

@ -370,7 +370,8 @@ class Template {
dependencyTemplates: renderContext.dependencyTemplates,
moduleGraph: renderContext.moduleGraph,
runtimeTemplate: renderContext.runtimeTemplate,
runtime: renderContext.chunk.runtime
runtime: renderContext.chunk.runtime,
codeGenerationResults
});
if (!codeGenResult) continue;
runtimeSource = codeGenResult.sources.get("runtime");

View File

@ -5,6 +5,7 @@
"use strict";
const mime = require("mime-types");
const { basename, extname } = require("path");
const util = require("util");
const Chunk = require("./Chunk");
@ -117,29 +118,53 @@ const replacePathVariables = (path, data, assetInfo) => {
// [name] - file
// [ext] - .js
if (typeof data.filename === "string") {
const { path: file, query, fragment } = parseResource(data.filename);
// check that filename is data uri
let match = data.filename.match(/^data:([^;,]+)/);
if (match) {
const ext = mime.extension(match[1]);
const emptyReplacer = replacer("", true);
const ext = extname(file);
const base = basename(file);
const name = base.slice(0, base.length - ext.length);
const path = file.slice(0, file.length - base.length);
replacements.set("file", emptyReplacer);
replacements.set("query", emptyReplacer);
replacements.set("fragment", emptyReplacer);
replacements.set("path", emptyReplacer);
replacements.set("base", emptyReplacer);
replacements.set("name", emptyReplacer);
replacements.set("ext", replacer(ext ? `.${ext}` : "", true));
// Legacy
replacements.set(
"filebase",
deprecated(
emptyReplacer,
"[filebase] is now [base]",
"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
)
);
} else {
const { path: file, query, fragment } = parseResource(data.filename);
replacements.set("file", replacer(file));
replacements.set("query", replacer(query, true));
replacements.set("fragment", replacer(fragment, true));
replacements.set("path", replacer(path, true));
replacements.set("base", replacer(base));
replacements.set("name", replacer(name));
replacements.set("ext", replacer(ext, true));
// Legacy
replacements.set(
"filebase",
deprecated(
replacer(base),
"[filebase] is now [base]",
"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
)
);
const ext = extname(file);
const base = basename(file);
const name = base.slice(0, base.length - ext.length);
const path = file.slice(0, file.length - base.length);
replacements.set("file", replacer(file));
replacements.set("query", replacer(query, true));
replacements.set("fragment", replacer(fragment, true));
replacements.set("path", replacer(path, true));
replacements.set("base", replacer(base));
replacements.set("name", replacer(name));
replacements.set("ext", replacer(ext, true));
// Legacy
replacements.set(
"filebase",
deprecated(
replacer(base),
"[filebase] is now [base]",
"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
)
);
}
}
// Compilation context

View File

@ -15,7 +15,7 @@ const Stats = require("./Stats");
/**
* @template T
* @callback Callback
* @param {Error=} err
* @param {(Error | null)=} err
* @param {T=} result
*/
@ -49,7 +49,7 @@ class Watching {
this.watchOptions = {};
}
if (typeof this.watchOptions.aggregateTimeout !== "number") {
this.watchOptions.aggregateTimeout = 200;
this.watchOptions.aggregateTimeout = 20;
}
this.compiler = compiler;
this.running = false;

View File

@ -35,6 +35,7 @@ const ResolverCachePlugin = require("./cache/ResolverCachePlugin");
const CommonJsPlugin = require("./dependencies/CommonJsPlugin");
const HarmonyModulesPlugin = require("./dependencies/HarmonyModulesPlugin");
const ImportMetaContextPlugin = require("./dependencies/ImportMetaContextPlugin");
const ImportMetaPlugin = require("./dependencies/ImportMetaPlugin");
const ImportPlugin = require("./dependencies/ImportPlugin");
const LoaderPlugin = require("./dependencies/LoaderPlugin");
@ -118,11 +119,47 @@ class WebpackOptionsApply extends OptionsApply {
if (options.externalsPresets.webAsync) {
//@ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ExternalsPlugin = require("./ExternalsPlugin");
new ExternalsPlugin("import", /^(https?:\/\/|std:)/).apply(compiler);
new ExternalsPlugin(
"import",
options.experiments.css
? ({ request, dependencyType }, callback) => {
if (dependencyType === "url") {
if (/^(\/\/|https?:\/\/)/.test(request))
return callback(null, `asset ${request}`);
} else if (dependencyType === "css-import") {
if (/^(\/\/|https?:\/\/)/.test(request))
return callback(null, `css-import ${request}`);
} else if (/^(\/\/|https?:\/\/|std:)/.test(request)) {
if (/^\.css(\?|$)/.test(request))
return callback(null, `css-import ${request}`);
return callback(null, `import ${request}`);
}
callback();
}
: /^(\/\/|https?:\/\/|std:)/
).apply(compiler);
} else if (options.externalsPresets.web) {
//@ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ExternalsPlugin = require("./ExternalsPlugin");
new ExternalsPlugin("module", /^(https?:\/\/|std:)/).apply(compiler);
new ExternalsPlugin(
"module",
options.experiments.css
? ({ request, dependencyType }, callback) => {
if (dependencyType === "url") {
if (/^(\/\/|https?:\/\/)/.test(request))
return callback(null, `asset ${request}`);
} else if (dependencyType === "css-import") {
if (/^(\/\/|https?:\/\/)/.test(request))
return callback(null, `css-import ${request}`);
} else if (/^(\/\/|https?:\/\/|std:)/.test(request)) {
if (/^\.css(\?|$)/.test(request))
return callback(null, `css-import ${request}`);
return callback(null, `module ${request}`);
}
callback();
}
: /^(\/\/|https?:\/\/|std:)/
).apply(compiler);
}
new ChunkPrefetchPreloadPlugin().apply(compiler);
@ -253,6 +290,11 @@ class WebpackOptionsApply extends OptionsApply {
}).apply(compiler);
}
if (options.experiments.css) {
const CssModulesPlugin = require("./css/CssModulesPlugin");
new CssModulesPlugin(options.experiments.css).apply(compiler);
}
if (options.experiments.lazyCompilation) {
const LazyCompilationPlugin = require("./hmr/LazyCompilationPlugin");
const lazyOptions =
@ -320,6 +362,7 @@ class WebpackOptionsApply extends OptionsApply {
new RequireEnsurePlugin().apply(compiler);
new RequireContextPlugin().apply(compiler);
new ImportPlugin().apply(compiler);
new ImportMetaContextPlugin().apply(compiler);
new SystemPlugin().apply(compiler);
new ImportMetaPlugin().apply(compiler);
new URLPlugin().apply(compiler);

View File

@ -12,9 +12,11 @@ const Generator = require("../Generator");
const RuntimeGlobals = require("../RuntimeGlobals");
const createHash = require("../util/createHash");
const { makePathsRelative } = require("../util/identifier");
const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").AssetGeneratorOptions} AssetGeneratorOptions */
/** @typedef {import("../../declarations/WebpackOptions").AssetModuleOutputPath} AssetModuleOutputPath */
/** @typedef {import("../../declarations/WebpackOptions").RawPublicPath} RawPublicPath */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compiler")} Compiler */
@ -49,7 +51,7 @@ const mergeAssetInfo = (a, b) => {
case "immutable":
case "development":
case "hotModuleReplacement":
case "javascriptModule ":
case "javascriptModule":
result[key] = a[key] || b[key];
break;
case "related":
@ -74,24 +76,121 @@ const mergeRelatedInfo = (a, b) => {
return result;
};
const encodeDataUri = (encoding, source) => {
let encodedContent;
switch (encoding) {
case "base64": {
encodedContent = source.buffer().toString("base64");
break;
}
case false: {
const content = source.source();
if (typeof content !== "string") {
encodedContent = content.toString("utf-8");
}
encodedContent = encodeURIComponent(encodedContent).replace(
/[!'()*]/g,
character => "%" + character.codePointAt(0).toString(16)
);
break;
}
default:
throw new Error(`Unsupported encoding '${encoding}'`);
}
return encodedContent;
};
const decodeDataUriContent = (encoding, content) => {
const isBase64 = encoding === "base64";
return isBase64
? Buffer.from(content, "base64")
: Buffer.from(decodeURIComponent(content), "ascii");
};
const JS_TYPES = new Set(["javascript"]);
const JS_AND_ASSET_TYPES = new Set(["javascript", "asset"]);
const DEFAULT_ENCODING = "base64";
class AssetGenerator extends Generator {
/**
* @param {AssetGeneratorOptions["dataUrl"]=} dataUrlOptions the options for the data url
* @param {string=} filename override for output.assetModuleFilename
* @param {RawPublicPath=} publicPath override for output.assetModulePublicPath
* @param {AssetModuleOutputPath=} outputPath the output path for the emitted file which is not included in the runtime import
* @param {boolean=} emit generate output asset
*/
constructor(dataUrlOptions, filename, publicPath, emit) {
constructor(dataUrlOptions, filename, publicPath, outputPath, emit) {
super();
this.dataUrlOptions = dataUrlOptions;
this.filename = filename;
this.publicPath = publicPath;
this.outputPath = outputPath;
this.emit = emit;
}
/**
* @param {NormalModule} module module
* @param {RuntimeTemplate} runtimeTemplate runtime template
* @returns {string} source file name
*/
getSourceFileName(module, runtimeTemplate) {
return makePathsRelative(
runtimeTemplate.compilation.compiler.context,
module.matchResource || module.resource,
runtimeTemplate.compilation.compiler.root
).replace(/^\.\//, "");
}
/**
* @param {NormalModule} module module
* @returns {string} mime type
*/
getMimeType(module) {
if (typeof this.dataUrlOptions === "function") {
throw new Error(
"This method must not be called when dataUrlOptions is a function"
);
}
let mimeType = this.dataUrlOptions.mimetype;
if (mimeType === undefined) {
const ext = path.extname(module.nameForCondition());
if (
module.resourceResolveData &&
module.resourceResolveData.mimetype !== undefined
) {
mimeType =
module.resourceResolveData.mimetype +
module.resourceResolveData.parameters;
} else if (ext) {
mimeType = mimeTypes.lookup(ext);
if (typeof mimeType !== "string") {
throw new Error(
"DataUrl can't be generated automatically, " +
`because there is no mimetype for "${ext}" in mimetype database. ` +
'Either pass a mimetype via "generator.mimetype" or ' +
'use type: "asset/resource" to create a resource file instead of a DataUrl'
);
}
}
}
if (typeof mimeType !== "string") {
throw new Error(
"DataUrl can't be generated automatically. " +
'Either pass a mimetype via "generator.mimetype" or ' +
'use type: "asset/resource" to create a resource file instead of a DataUrl'
);
}
return mimeType;
}
/**
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
@ -131,66 +230,31 @@ class AssetGenerator extends Generator {
}
}
if (encoding === undefined) {
encoding = "base64";
}
let ext;
let mimeType = this.dataUrlOptions.mimetype;
if (mimeType === undefined) {
ext = path.extname(module.nameForCondition());
if (
module.resourceResolveData &&
module.resourceResolveData.mimetype !== undefined
) {
mimeType =
module.resourceResolveData.mimetype +
module.resourceResolveData.parameters;
} else if (ext) {
mimeType = mimeTypes.lookup(ext);
}
}
if (typeof mimeType !== "string") {
throw new Error(
"DataUrl can't be generated automatically, " +
`because there is no mimetype for "${ext}" in mimetype database. ` +
'Either pass a mimetype via "generator.mimetype" or ' +
'use type: "asset/resource" to create a resource file instead of a DataUrl'
);
encoding = DEFAULT_ENCODING;
}
const mimeType = this.getMimeType(module);
let encodedContent;
if (
module.resourceResolveData &&
module.resourceResolveData.encoding === encoding
module.resourceResolveData.encoding === encoding &&
decodeDataUriContent(
module.resourceResolveData.encoding,
module.resourceResolveData.encodedContent
).equals(originalSource.buffer())
) {
encodedContent = module.resourceResolveData.encodedContent;
} else {
switch (encoding) {
case "base64": {
encodedContent = originalSource.buffer().toString("base64");
break;
}
case false: {
const content = originalSource.source();
if (typeof content !== "string") {
encodedContent = content.toString("utf-8");
}
encodedContent = encodeURIComponent(encodedContent).replace(
/[!'()*]/g,
character => "%" + character.codePointAt(0).toString(16)
);
break;
}
default:
throw new Error(`Unsupported encoding '${encoding}'`);
}
encodedContent = encodeDataUri(encoding, originalSource);
}
encodedSource = `data:${mimeType}${
encoding ? `;${encoding}` : ""
},${encodedContent}`;
}
const data = getData();
data.set("url", Buffer.from(encodedSource));
return new RawSource(
`${RuntimeGlobals.module}.exports = ${JSON.stringify(
encodedSource
@ -207,16 +271,15 @@ class AssetGenerator extends Generator {
const fullHash = /** @type {string} */ (
hash.digest(runtimeTemplate.outputOptions.hashDigest)
);
const contentHash = fullHash.slice(
0,
const contentHash = nonNumericOnlyHash(
fullHash,
runtimeTemplate.outputOptions.hashDigestLength
);
module.buildInfo.fullContentHash = fullHash;
const sourceFilename = makePathsRelative(
runtimeTemplate.compilation.compiler.context,
module.matchResource || module.resource,
runtimeTemplate.compilation.compiler.root
).replace(/^\.\//, "");
const sourceFilename = this.getSourceFileName(
module,
runtimeTemplate
);
let { path: filename, info: assetInfo } =
runtimeTemplate.compilation.getAssetPathWithInfo(
assetModuleFilename,
@ -254,6 +317,21 @@ class AssetGenerator extends Generator {
sourceFilename,
...assetInfo
};
if (this.outputPath) {
const { path: outputPath, info } =
runtimeTemplate.compilation.getAssetPathWithInfo(
this.outputPath,
{
module,
runtime,
filename: sourceFilename,
chunkGraph,
contentHash
}
);
assetInfo = mergeAssetInfo(assetInfo, info);
filename = path.posix.join(outputPath, filename);
}
module.buildInfo.filename = filename;
module.buildInfo.assetInfo = assetInfo;
if (getData) {
@ -327,8 +405,59 @@ class AssetGenerator extends Generator {
* @param {Hash} hash hash that will be modified
* @param {UpdateHashContext} updateHashContext context for updating hash
*/
updateHash(hash, { module }) {
hash.update(module.buildInfo.dataUrl ? "data-url" : "resource");
updateHash(hash, { module, runtime, runtimeTemplate, chunkGraph }) {
if (module.buildInfo.dataUrl) {
hash.update("data-url");
// this.dataUrlOptions as function should be pure and only depend on input source and filename
// therefore it doesn't need to be hashed
if (typeof this.dataUrlOptions === "function") {
const ident = /** @type {{ ident?: string }} */ (this.dataUrlOptions)
.ident;
if (ident) hash.update(ident);
} else {
if (
this.dataUrlOptions.encoding &&
this.dataUrlOptions.encoding !== DEFAULT_ENCODING
) {
hash.update(this.dataUrlOptions.encoding);
}
if (this.dataUrlOptions.mimetype)
hash.update(this.dataUrlOptions.mimetype);
// computed mimetype depends only on module filename which is already part of the hash
}
} else {
hash.update("resource");
const pathData = {
module,
runtime,
filename: this.getSourceFileName(module, runtimeTemplate),
chunkGraph,
contentHash: runtimeTemplate.contentHashReplacement
};
if (typeof this.publicPath === "function") {
hash.update("path");
const assetInfo = {};
hash.update(this.publicPath(pathData, assetInfo));
hash.update(JSON.stringify(assetInfo));
} else if (this.publicPath) {
hash.update("path");
hash.update(this.publicPath);
} else {
hash.update("no-path");
}
const assetModuleFilename =
this.filename || runtimeTemplate.outputOptions.assetModuleFilename;
const { path: filename, info } =
runtimeTemplate.compilation.getAssetPathWithInfo(
assetModuleFilename,
pathData
);
hash.update(filename);
hash.update(JSON.stringify(info));
}
}
}

View File

@ -137,9 +137,11 @@ class AssetModulesPlugin {
let filename = undefined;
let publicPath = undefined;
let outputPath = undefined;
if (type !== "asset/inline") {
filename = generatorOptions.filename;
publicPath = generatorOptions.publicPath;
outputPath = generatorOptions.outputPath;
}
const AssetGenerator = getAssetGenerator();
@ -148,6 +150,7 @@ class AssetModulesPlugin {
dataUrl,
filename,
publicPath,
outputPath,
generatorOptions.emit !== false
);
});

View File

@ -0,0 +1,148 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { RawSource } = require("webpack-sources");
const Module = require("../Module");
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
/** @typedef {import("../RequestShortener")} RequestShortener */
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
/** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
const TYPES = new Set(["javascript"]);
class RawDataUrlModule extends Module {
/**
* @param {string} url raw url
* @param {string} identifier unique identifier
* @param {string=} readableIdentifier readable identifier
*/
constructor(url, identifier, readableIdentifier) {
super("asset/raw-data-url", null);
this.url = url;
this.urlBuffer = url ? Buffer.from(url) : undefined;
this.identifierStr = identifier || this.url;
this.readableIdentifierStr = readableIdentifier || this.identifierStr;
}
/**
* @returns {Set<string>} types available (do not mutate)
*/
getSourceTypes() {
return TYPES;
}
/**
* @returns {string} a unique identifier of the module
*/
identifier() {
return this.identifierStr;
}
/**
* @param {string=} type the source type for which the size should be estimated
* @returns {number} the estimated size of the module (must be non-zero)
*/
size(type) {
if (this.url === undefined) this.url = this.urlBuffer.toString();
return Math.max(1, this.url.length);
}
/**
* @param {RequestShortener} requestShortener the request shortener
* @returns {string} a user readable identifier of the module
*/
readableIdentifier(requestShortener) {
return requestShortener.shorten(this.readableIdentifierStr);
}
/**
* @param {NeedBuildContext} context context info
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {
return callback(null, !this.buildMeta);
}
/**
* @param {WebpackOptions} options webpack options
* @param {Compilation} compilation the compilation
* @param {ResolverWithOptions} resolver the resolver
* @param {InputFileSystem} fs the file system
* @param {function(WebpackError=): void} callback callback function
* @returns {void}
*/
build(options, compilation, resolver, fs, callback) {
this.buildMeta = {};
this.buildInfo = {
cacheable: true
};
callback();
}
/**
* @param {CodeGenerationContext} context context for code generation
* @returns {CodeGenerationResult} result
*/
codeGeneration(context) {
if (this.url === undefined) this.url = this.urlBuffer.toString();
const sources = new Map();
sources.set(
"javascript",
new RawSource(`module.exports = ${JSON.stringify(this.url)};`)
);
const data = new Map();
data.set("url", this.urlBuffer);
const runtimeRequirements = new Set();
runtimeRequirements.add(RuntimeGlobals.module);
return { sources, runtimeRequirements, data };
}
/**
* @param {Hash} hash the hash used to track dependencies
* @param {UpdateHashContext} context context
* @returns {void}
*/
updateHash(hash, context) {
hash.update(this.urlBuffer);
super.updateHash(hash, context);
}
serialize(context) {
const { write } = context;
write(this.urlBuffer);
write(this.identifierStr);
write(this.readableIdentifierStr);
super.serialize(context);
}
deserialize(context) {
const { read } = context;
this.urlBuffer = read();
this.identifierStr = read();
this.readableIdentifierStr = read();
super.deserialize(context);
}
}
makeSerializable(RawDataUrlModule, "webpack/lib/asset/RawDataUrlModule");
module.exports = RawDataUrlModule;

View File

@ -30,8 +30,8 @@ class AwaitDependenciesInitFragment extends InitFragment {
}
merge(other) {
const promises = new Set(this.promises);
for (const p of other.promises) {
const promises = new Set(other.promises);
for (const p of this.promises) {
promises.add(p);
}
return new AwaitDependenciesInitFragment(promises);
@ -51,7 +51,7 @@ class AwaitDependenciesInitFragment extends InitFragment {
for (const p of promises) {
return Template.asString([
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${p}]);`,
`${p} = (__webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__)[0];`,
`${p} = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__)[0];`,
""
]);
}
@ -60,7 +60,7 @@ class AwaitDependenciesInitFragment extends InitFragment {
// TODO check if destructuring is supported
return Template.asString([
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${sepPromises}]);`,
`([${sepPromises}] = __webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__);`,
`([${sepPromises}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);`,
""
]);
}

View File

@ -905,7 +905,7 @@ const visitModules = (
const module = it.value;
if (
availableModules.has(module) ||
availableModules.plus.has(m)
availableModules.plus.has(module)
) {
cachedMinAvailableModules.add(module);
}

View File

@ -639,10 +639,14 @@ class PackContentItems {
} catch (e) {
rollback(s);
if (e === NOT_SERIALIZABLE) continue;
logger.warn(
`Skipped not serializable cache item '${key}': ${e.message}`
);
logger.debug(e.stack);
const msg = "Skipped not serializable cache item";
if (e.message.includes("ModuleBuildError")) {
logger.log(`${msg} (in build error): ${e.message}`);
logger.debug(`${msg} '${key}' (in build error): ${e.stack}`);
} else {
logger.warn(`${msg}: ${e.message}`);
logger.debug(`${msg} '${key}': ${e.stack}`);
}
}
}
write(null);

View File

@ -106,7 +106,7 @@ class ResolverCachePlugin {
* @param {Resolver} resolver the resolver
* @param {Object} resolveContext context for resolving meta info
* @param {Object} request the request info object
* @param {function(Error=, Object=): void} callback callback function
* @param {function((Error | null)=, Object=): void} callback callback function
* @returns {void}
*/
const doRealResolve = (
@ -128,6 +128,13 @@ class ResolverCachePlugin {
fileDependencies: new LazySet(),
contextDependencies: new LazySet()
};
let yieldResult;
let withYield = false;
if (typeof newResolveContext.yield === "function") {
yieldResult = [];
withYield = true;
newResolveContext.yield = obj => yieldResult.push(obj);
}
const propagate = key => {
if (resolveContext[key]) {
addAllToSet(resolveContext[key], newResolveContext[key]);
@ -155,15 +162,22 @@ class ResolverCachePlugin {
snapshotOptions,
(err, snapshot) => {
if (err) return callback(err);
const resolveResult = withYield ? yieldResult : result;
// since we intercept resolve hook
// we still can get result in callback
if (withYield && result) yieldResult.push(result);
if (!snapshot) {
if (result) return callback(null, result);
if (resolveResult) return callback(null, resolveResult);
return callback();
}
itemCache.store(new CacheEntry(result, snapshot), storeErr => {
if (storeErr) return callback(storeErr);
if (result) return callback(null, result);
callback();
});
itemCache.store(
new CacheEntry(resolveResult, snapshot),
storeErr => {
if (storeErr) return callback(storeErr);
if (resolveResult) return callback(null, resolveResult);
callback();
}
);
}
);
}
@ -173,6 +187,8 @@ class ResolverCachePlugin {
factory(type, hook) {
/** @type {Map<string, (function(Error=, Object=): void)[]>} */
const activeRequests = new Map();
/** @type {Map<string, [function(Error=, Object=): void, function(Error=, Object=): void][]>} */
const activeRequestsWithYield = new Map();
hook.tap(
"ResolverCachePlugin",
/**
@ -197,29 +213,67 @@ class ResolverCachePlugin {
if (request._ResolverCachePluginCacheMiss || !fileSystemInfo) {
return callback();
}
const identifier = `${type}${optionsIdent}${objectToString(
request,
!cacheWithContext
)}`;
const activeRequest = activeRequests.get(identifier);
if (activeRequest) {
activeRequest.push(callback);
return;
const withYield = typeof resolveContext.yield === "function";
const identifier = `${type}${
withYield ? "|yield" : "|default"
}${optionsIdent}${objectToString(request, !cacheWithContext)}`;
if (withYield) {
const activeRequest = activeRequestsWithYield.get(identifier);
if (activeRequest) {
activeRequest[0].push(callback);
activeRequest[1].push(resolveContext.yield);
return;
}
} else {
const activeRequest = activeRequests.get(identifier);
if (activeRequest) {
activeRequest.push(callback);
return;
}
}
const itemCache = cache.getItemCache(identifier, null);
let callbacks;
const done = (err, result) => {
if (callbacks === undefined) {
callback(err, result);
callbacks = false;
} else {
for (const callback of callbacks) {
callback(err, result);
}
activeRequests.delete(identifier);
callbacks = false;
}
};
let callbacks, yields;
const done = withYield
? (err, result) => {
if (callbacks === undefined) {
if (err) {
callback(err);
} else {
if (result)
for (const r of result) resolveContext.yield(r);
callback(null, null);
}
yields = undefined;
callbacks = false;
} else {
if (err) {
for (const cb of callbacks) cb(err);
} else {
for (let i = 0; i < callbacks.length; i++) {
const cb = callbacks[i];
const yield_ = yields[i];
if (result) for (const r of result) yield_(r);
cb(null, null);
}
}
activeRequestsWithYield.delete(identifier);
yields = undefined;
callbacks = false;
}
}
: (err, result) => {
if (callbacks === undefined) {
callback(err, result);
callbacks = false;
} else {
for (const callback of callbacks) {
callback(err, result);
}
activeRequests.delete(identifier);
callbacks = false;
}
};
/**
* @param {Error=} err error if any
* @param {CacheEntry=} cacheEntry cache entry
@ -276,7 +330,14 @@ class ResolverCachePlugin {
}
};
itemCache.get(processCacheResult);
if (callbacks === undefined) {
if (withYield && callbacks === undefined) {
callbacks = [callback];
yields = [resolveContext.yield];
activeRequestsWithYield.set(
identifier,
/** @type {[any, any]} */ ([callbacks, yields])
);
} else if (callbacks === undefined) {
callbacks = [callback];
activeRequests.set(identifier, callbacks);
}

View File

@ -37,6 +37,7 @@ const webpackSchema = require("../schemas/WebpackOptions.json");
/**
* @typedef {Object} ArgumentConfig
* @property {string} description
* @property {string} [negatedDescription]
* @property {string} path
* @property {boolean} multiple
* @property {"enum"|"string"|"path"|"number"|"boolean"|"RegExp"|"reset"} type
@ -96,11 +97,42 @@ const getArguments = (schema = webpackSchema) => {
*/
const getDescription = path => {
for (const { schema } of path) {
if (schema.cli && schema.cli.helper) continue;
if (schema.cli) {
if (schema.cli.helper) continue;
if (schema.cli.description) return schema.cli.description;
}
if (schema.description) return schema.description;
}
};
/**
*
* @param {PathItem[]} path path in the schema
* @returns {string | undefined} negative description
*/
const getNegatedDescription = path => {
for (const { schema } of path) {
if (schema.cli) {
if (schema.cli.helper) continue;
if (schema.cli.negatedDescription) return schema.cli.negatedDescription;
}
}
};
/**
*
* @param {PathItem[]} path path in the schema
* @returns {string | undefined} reset description
*/
const getResetDescription = path => {
for (const { schema } of path) {
if (schema.cli) {
if (schema.cli.helper) continue;
if (schema.cli.resetDescription) return schema.cli.resetDescription;
}
}
};
/**
*
* @param {any} schemaPart schema
@ -142,13 +174,17 @@ const getArguments = (schema = webpackSchema) => {
const addResetFlag = path => {
const schemaPath = path[0].path;
const name = pathToArgumentName(`${schemaPath}.reset`);
const description = getDescription(path);
const description =
getResetDescription(path) ||
`Clear all items provided in '${schemaPath}' configuration. ${getDescription(
path
)}`;
flags[name] = {
configs: [
{
type: "reset",
multiple: false,
description: `Clear all items provided in '${schemaPath}' configuration. ${description}`,
description,
path: schemaPath
}
],
@ -167,6 +203,7 @@ const getArguments = (schema = webpackSchema) => {
const argConfigBase = schemaToArgumentConfig(path[0].schema);
if (!argConfigBase) return 0;
const negatedDescription = getNegatedDescription(path);
const name = pathToArgumentName(path[0].path);
/** @type {ArgumentConfig} */
const argConfig = {
@ -176,6 +213,10 @@ const getArguments = (schema = webpackSchema) => {
path: path[0].path
};
if (negatedDescription) {
argConfig.negatedDescription = negatedDescription;
}
if (!flags[name]) {
flags[name] = {
configs: [],

View File

@ -121,8 +121,7 @@ const resolve = browsers => {
// baidu: Not supported
// and_uc: Not supported
// kaios: Not supported
// Since Node.js 13.14.0 no warning about usage, but it was added 8.5.0 with some limitations and it was improved in 12.0.0 and 13.2.0
node: [13, 14]
node: [12, 17]
});
return {
@ -248,8 +247,7 @@ const resolve = browsers => {
// baidu: Not supported
// and_uc: Not supported
// kaios: Not supported
// Since Node.js 13.14.0 no warning about usage, but it was added 8.5.0 with some limitations and it was improved in 12.0.0 and 13.2.0
node: [13, 14]
node: [12, 17]
}),
dynamicImport: es6DynamicImport,
dynamicImportInWorker: es6DynamicImport && !anyNode,
@ -272,7 +270,7 @@ const resolve = browsers => {
// baidu: Unknown support
// and_uc: Unknown support
// kaios: Unknown support
node: [12, 0]
node: 12
}),
optionalChaining: rawChecker({
chrome: 80,

View File

@ -16,6 +16,7 @@ const {
} = require("./target");
/** @typedef {import("../../declarations/WebpackOptions").CacheOptionsNormalized} CacheOptions */
/** @typedef {import("../../declarations/WebpackOptions").CssExperimentOptions} CssExperimentOptions */
/** @typedef {import("../../declarations/WebpackOptions").EntryDescription} EntryDescription */
/** @typedef {import("../../declarations/WebpackOptions").EntryNormalized} Entry */
/** @typedef {import("../../declarations/WebpackOptions").Experiments} Experiments */
@ -160,7 +161,11 @@ const applyWebpackOptionsDefaults = options => {
D(options, "recordsInputPath", false);
D(options, "recordsOutputPath", false);
applyExperimentsDefaults(options.experiments, { production, development });
applyExperimentsDefaults(options.experiments, {
production,
development,
targetProperties
});
const futureDefaults = options.experiments.futureDefaults;
@ -184,6 +189,7 @@ const applyWebpackOptionsDefaults = options => {
cache,
syncWebAssembly: options.experiments.syncWebAssembly,
asyncWebAssembly: options.experiments.asyncWebAssembly,
css: options.experiments.css,
futureDefaults
});
@ -240,6 +246,7 @@ const applyWebpackOptionsDefaults = options => {
applyOptimizationDefaults(options.optimization, {
development,
production,
css: options.experiments.css,
records: !!(options.recordsInputPath || options.recordsOutputPath)
});
@ -264,9 +271,13 @@ const applyWebpackOptionsDefaults = options => {
* @param {Object} options options
* @param {boolean} options.production is production
* @param {boolean} options.development is development mode
* @param {TargetProperties | false} options.targetProperties target properties
* @returns {void}
*/
const applyExperimentsDefaults = (experiments, { production, development }) => {
const applyExperimentsDefaults = (
experiments,
{ production, development, targetProperties }
) => {
D(experiments, "futureDefaults", false);
D(experiments, "backCompat", !experiments.futureDefaults);
D(experiments, "topLevelAwait", experiments.futureDefaults);
@ -277,11 +288,20 @@ const applyExperimentsDefaults = (experiments, { production, development }) => {
D(experiments, "lazyCompilation", undefined);
D(experiments, "buildHttp", undefined);
D(experiments, "cacheUnaffected", experiments.futureDefaults);
F(experiments, "css", () => (experiments.futureDefaults ? {} : undefined));
if (typeof experiments.buildHttp === "object") {
D(experiments.buildHttp, "frozen", production);
D(experiments.buildHttp, "upgrade", false);
}
if (typeof experiments.css === "object") {
D(
experiments.css,
"exportsOnly",
!targetProperties || !targetProperties.document
);
}
};
/**
@ -450,6 +470,7 @@ const applyJavascriptParserOptionsDefaults = (
D(parserOptions, "wrappedContextRecursive", true);
D(parserOptions, "wrappedContextCritical", false);
D(parserOptions, "strictThisContextOnImports", false);
D(parserOptions, "importMeta", true);
if (futureDefaults) D(parserOptions, "exportsPresence", "error");
};
@ -459,12 +480,13 @@ const applyJavascriptParserOptionsDefaults = (
* @param {boolean} options.cache is caching enabled
* @param {boolean} options.syncWebAssembly is syncWebAssembly enabled
* @param {boolean} options.asyncWebAssembly is asyncWebAssembly enabled
* @param {CssExperimentOptions} options.css is css enabled
* @param {boolean} options.futureDefaults is future defaults enabled
* @returns {void}
*/
const applyModuleDefaults = (
module,
{ cache, syncWebAssembly, asyncWebAssembly, futureDefaults }
{ cache, syncWebAssembly, asyncWebAssembly, css, futureDefaults }
) => {
if (cache) {
D(module, "unsafeCache", module => {
@ -588,6 +610,41 @@ const applyModuleDefaults = (
...wasm
});
}
if (css) {
const cssRule = {
type: "css",
resolve: {
fullySpecified: true,
preferRelative: true
}
};
const cssModulesRule = {
type: "css/module",
resolve: {
fullySpecified: true
}
};
rules.push({
test: /\.css$/i,
oneOf: [
{
test: /\.module\.css$/i,
...cssModulesRule
},
{
...cssRule
}
]
});
rules.push({
mimetype: "text/css+module",
...cssModulesRule
});
rules.push({
mimetype: "text/css",
...cssRule
});
}
rules.push(
{
dependency: "url",
@ -659,7 +716,15 @@ const applyOutputDefaults = (
};
F(output, "uniqueName", () => {
const libraryName = getLibraryName(output.library);
const libraryName = getLibraryName(output.library).replace(
/^\[(\\*[\w:]+\\*)\](\.)|(\.)\[(\\*[\w:]+\\*)\](?=\.|$)|\[(\\*[\w:]+\\*)\]/g,
(m, a, d1, d2, b, c) => {
const content = a || b || c;
return content.startsWith("\\") && content.endsWith("\\")
? `${d2 || ""}[${content.slice(1, -1)}]${d1 || ""}`
: "";
}
);
if (libraryName) return libraryName;
const pkgPath = path.resolve(context, "package.json");
try {
@ -693,6 +758,20 @@ const applyOutputDefaults = (
}
return output.module ? "[id].mjs" : "[id].js";
});
F(output, "cssFilename", () => {
const filename = output.filename;
if (typeof filename !== "function") {
return filename.replace(/\.[mc]?js(\?|$)/, ".css$1");
}
return "[id].css";
});
F(output, "cssChunkFilename", () => {
const chunkFilename = output.chunkFilename;
if (typeof chunkFilename !== "function") {
return chunkFilename.replace(/\.[mc]?js(\?|$)/, ".css$1");
}
return "[id].css";
});
D(output, "assetModuleFilename", "[hash][ext][query]");
D(output, "webassemblyModuleFilename", "[hash].module.wasm");
D(output, "compareBeforeEmit", true);
@ -833,10 +912,11 @@ const applyOutputDefaults = (
D(output, "chunkLoadTimeout", 120000);
D(output, "hashFunction", futureDefaults ? "xxhash64" : "md4");
D(output, "hashDigest", "hex");
D(output, "hashDigestLength", 20);
D(output, "hashDigestLength", futureDefaults ? 16 : 20);
D(output, "strictModuleExceptionHandling", false);
const optimistic = v => v || v === undefined;
const conditionallyOptimistic = (v, c) => (v === undefined && c) || v;
F(
output.environment,
"arrowFunction",
@ -850,8 +930,12 @@ const applyOutputDefaults = (
);
F(output.environment, "forOf", () => tp && optimistic(tp.forOf));
F(output.environment, "bigIntLiteral", () => tp && tp.bigIntLiteral);
F(output.environment, "dynamicImport", () => tp && tp.dynamicImport);
F(output.environment, "module", () => tp && tp.module);
F(output.environment, "dynamicImport", () =>
conditionallyOptimistic(tp && tp.dynamicImport, output.module)
);
F(output.environment, "module", () =>
conditionallyOptimistic(tp && tp.module, output.module)
);
const { trustedTypes } = output;
if (trustedTypes) {
@ -1035,12 +1119,13 @@ const applyPerformanceDefaults = (performance, { production }) => {
* @param {Object} options options
* @param {boolean} options.production is production
* @param {boolean} options.development is development
* @param {CssExperimentOptions} options.css is css enabled
* @param {boolean} options.records using records
* @returns {void}
*/
const applyOptimizationDefaults = (
optimization,
{ production, development, records }
{ production, development, css, records }
) => {
D(optimization, "removeAvailableModules", false);
D(optimization, "removeEmptyChunks", true);
@ -1091,7 +1176,9 @@ const applyOptimizationDefaults = (
});
const { splitChunks } = optimization;
if (splitChunks) {
A(splitChunks, "defaultSizeTypes", () => ["javascript", "unknown"]);
A(splitChunks, "defaultSizeTypes", () =>
css ? ["javascript", "css", "unknown"] : ["javascript", "unknown"]
);
D(splitChunks, "hidePathInfo", production);
D(splitChunks, "chunks", "async");
D(splitChunks, "usedExports", optimization.usedExports === true);

View File

@ -180,6 +180,9 @@ const getNormalizedWebpackOptions = config => {
experiments.lazyCompilation,
options =>
options === true ? {} : options === false ? undefined : options
),
css: optionalNestedConfig(experiments.css, options =>
options === true ? {} : options === false ? undefined : options
)
})),
externals: config.externals,
@ -297,6 +300,8 @@ const getNormalizedWebpackOptions = config => {
chunkLoading: output.chunkLoading,
chunkLoadingGlobal: output.chunkLoadingGlobal,
chunkLoadTimeout: output.chunkLoadTimeout,
cssFilename: output.cssFilename,
cssChunkFilename: output.cssChunkFilename,
clean: output.clean,
compareBeforeEmit: output.compareBeforeEmit,
crossOriginLoading: output.crossOriginLoading,
@ -483,6 +488,7 @@ const getNormalizedEntryStatic = entry => {
filename: value.filename,
layer: value.layer,
runtime: value.runtime,
baseUri: value.baseUri,
publicPath: value.publicPath,
chunkLoading: value.chunkLoading,
asyncChunks: value.asyncChunks,

View File

@ -79,12 +79,14 @@ class ContainerEntryModule extends Module {
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
return `webpack/container/entry/${this._name}`;
return `${this.layer ? `(${this.layer})/` : ""}webpack/container/entry/${
this._name
}`;
}
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -60,9 +60,9 @@ class FallbackModule extends Module {
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
return `webpack/container/fallback/${this.requests[0]}/and ${
this.requests.length - 1
} more`;
return `${this.layer ? `(${this.layer})/` : ""}webpack/container/fallback/${
this.requests[0]
}/and ${this.requests.length - 1} more`;
}
/**
@ -76,7 +76,7 @@ class FallbackModule extends Module {
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -67,12 +67,14 @@ class RemoteModule extends Module {
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
return `webpack/container/remote/${this.request}`;
return `${this.layer ? `(${this.layer})/` : ""}webpack/container/remote/${
this.request
}`;
}
/**
* @param {NeedBuildContext} context context info
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
* @returns {void}
*/
needBuild(context, callback) {

View File

@ -73,9 +73,9 @@ class RemoteRuntimeModule extends RuntimeModule {
Template.indent(
`error.message += '\\nwhile loading "' + data[1] + '" from ' + data[2];`
),
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction("", [
"throw error;"
])}`,
`${
RuntimeGlobals.moduleFactories
}[id] = ${runtimeTemplate.basicFunction("", ["throw error;"])}`,
"data.p = 0;"
])};`,
`var handleFunction = ${runtimeTemplate.basicFunction(
@ -111,10 +111,11 @@ class RemoteRuntimeModule extends RuntimeModule {
)};`,
`var onFactory = ${runtimeTemplate.basicFunction("factory", [
"data.p = 1;",
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction(
"module",
["module.exports = factory();"]
)}`
`${
RuntimeGlobals.moduleFactories
}[id] = ${runtimeTemplate.basicFunction("module", [
"module.exports = factory();"
])}`
])};`,
"handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);"
])});`

View File

@ -0,0 +1,152 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sergey Melyukov @smelukov
*/
"use strict";
const { ReplaceSource, RawSource, ConcatSource } = require("webpack-sources");
const { UsageState } = require("../ExportsInfo");
const Generator = require("../Generator");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
/** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
/** @typedef {import("../NormalModule")} NormalModule */
/** @typedef {import("../util/Hash")} Hash */
const TYPES = new Set(["javascript"]);
class CssExportsGenerator extends Generator {
constructor() {
super();
}
// TODO add getConcatenationBailoutReason to allow concatenation
// but how to make it have a module id
/**
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
* @returns {Source} generated code
*/
generate(module, generateContext) {
const source = new ReplaceSource(new RawSource(""));
const initFragments = [];
const cssExports = new Map();
generateContext.runtimeRequirements.add(RuntimeGlobals.module);
let chunkInitFragments;
const runtimeRequirements = new Set();
const templateContext = {
runtimeTemplate: generateContext.runtimeTemplate,
dependencyTemplates: generateContext.dependencyTemplates,
moduleGraph: generateContext.moduleGraph,
chunkGraph: generateContext.chunkGraph,
module,
runtime: generateContext.runtime,
runtimeRequirements: runtimeRequirements,
concatenationScope: generateContext.concatenationScope,
codeGenerationResults: generateContext.codeGenerationResults,
initFragments,
cssExports,
get chunkInitFragments() {
if (!chunkInitFragments) {
const data = generateContext.getData();
chunkInitFragments = data.get("chunkInitFragments");
if (!chunkInitFragments) {
chunkInitFragments = [];
data.set("chunkInitFragments", chunkInitFragments);
}
}
return chunkInitFragments;
}
};
const handleDependency = dependency => {
const constructor = /** @type {new (...args: any[]) => Dependency} */ (
dependency.constructor
);
const template = generateContext.dependencyTemplates.get(constructor);
if (!template) {
throw new Error(
"No template for dependency: " + dependency.constructor.name
);
}
template.apply(dependency, source, templateContext);
};
module.dependencies.forEach(handleDependency);
if (generateContext.concatenationScope) {
const source = new ConcatSource();
const usedIdentifiers = new Set();
for (const [k, v] of cssExports) {
let identifier = Template.toIdentifier(k);
let i = 0;
while (usedIdentifiers.has(identifier)) {
identifier = Template.toIdentifier(k + i);
}
usedIdentifiers.add(identifier);
generateContext.concatenationScope.registerExport(k, identifier);
source.add(
`${
generateContext.runtimeTemplate.supportsConst ? "const" : "var"
} ${identifier} = ${JSON.stringify(v)};\n`
);
}
return source;
} else {
const otherUsed =
generateContext.moduleGraph
.getExportsInfo(module)
.otherExportsInfo.getUsed(generateContext.runtime) !==
UsageState.Unused;
if (otherUsed) {
generateContext.runtimeRequirements.add(
RuntimeGlobals.makeNamespaceObject
);
}
return new RawSource(
`${otherUsed ? `${RuntimeGlobals.makeNamespaceObject}(` : ""}${
module.moduleArgument
}.exports = {\n${Array.from(
cssExports,
([k, v]) => `\t${JSON.stringify(k)}: ${JSON.stringify(v)}`
).join(",\n")}\n}${otherUsed ? ")" : ""};`
);
}
}
/**
* @param {NormalModule} module fresh module
* @returns {Set<string>} available types (do not mutate)
*/
getTypes(module) {
return TYPES;
}
/**
* @param {NormalModule} module the module
* @param {string=} type source type
* @returns {number} estimate size of the module
*/
getSize(module, type) {
return 42;
}
/**
* @param {Hash} hash hash that will be modified
* @param {UpdateHashContext} updateHashContext context for updating hash
*/
updateHash(hash, { module }) {}
}
module.exports = CssExportsGenerator;

122
lib/css/CssGenerator.js Normal file
View File

@ -0,0 +1,122 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sergey Melyukov @smelukov
*/
"use strict";
const { ReplaceSource } = require("webpack-sources");
const Generator = require("../Generator");
const InitFragment = require("../InitFragment");
const RuntimeGlobals = require("../RuntimeGlobals");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
/** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../NormalModule")} NormalModule */
/** @typedef {import("../util/Hash")} Hash */
const TYPES = new Set(["css"]);
class CssGenerator extends Generator {
constructor() {
super();
}
/**
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
* @returns {Source} generated code
*/
generate(module, generateContext) {
const originalSource = module.originalSource();
const source = new ReplaceSource(originalSource);
const initFragments = [];
const cssExports = new Map();
generateContext.runtimeRequirements.add(RuntimeGlobals.hasCssModules);
let chunkInitFragments;
const templateContext = {
runtimeTemplate: generateContext.runtimeTemplate,
dependencyTemplates: generateContext.dependencyTemplates,
moduleGraph: generateContext.moduleGraph,
chunkGraph: generateContext.chunkGraph,
module,
runtime: generateContext.runtime,
runtimeRequirements: generateContext.runtimeRequirements,
concatenationScope: generateContext.concatenationScope,
codeGenerationResults: generateContext.codeGenerationResults,
initFragments,
cssExports,
get chunkInitFragments() {
if (!chunkInitFragments) {
const data = generateContext.getData();
chunkInitFragments = data.get("chunkInitFragments");
if (!chunkInitFragments) {
chunkInitFragments = [];
data.set("chunkInitFragments", chunkInitFragments);
}
}
return chunkInitFragments;
}
};
const handleDependency = dependency => {
const constructor = /** @type {new (...args: any[]) => Dependency} */ (
dependency.constructor
);
const template = generateContext.dependencyTemplates.get(constructor);
if (!template) {
throw new Error(
"No template for dependency: " + dependency.constructor.name
);
}
template.apply(dependency, source, templateContext);
};
module.dependencies.forEach(handleDependency);
if (module.presentationalDependencies !== undefined)
module.presentationalDependencies.forEach(handleDependency);
if (cssExports.size > 0) {
const data = generateContext.getData();
data.set("css-exports", cssExports);
}
return InitFragment.addToSource(source, initFragments, generateContext);
}
/**
* @param {NormalModule} module fresh module
* @returns {Set<string>} available types (do not mutate)
*/
getTypes(module) {
return TYPES;
}
/**
* @param {NormalModule} module the module
* @param {string=} type source type
* @returns {number} estimate size of the module
*/
getSize(module, type) {
const originalSource = module.originalSource();
if (!originalSource) {
return 0;
}
return originalSource.size();
}
/**
* @param {Hash} hash hash that will be modified
* @param {UpdateHashContext} updateHashContext context for updating hash
*/
updateHash(hash, { module }) {}
}
module.exports = CssGenerator;

View File

@ -0,0 +1,440 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { SyncWaterfallHook } = require("tapable");
const Compilation = require("../Compilation");
const RuntimeGlobals = require("../RuntimeGlobals");
const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
const compileBooleanMatcher = require("../util/compileBooleanMatcher");
const { chunkHasCss } = require("./CssModulesPlugin");
/** @typedef {import("../Chunk")} Chunk */
/**
* @typedef {Object} JsonpCompilationPluginHooks
* @property {SyncWaterfallHook<[string, Chunk]>} createStylesheet
*/
/** @type {WeakMap<Compilation, JsonpCompilationPluginHooks>} */
const compilationHooksMap = new WeakMap();
class CssLoadingRuntimeModule extends RuntimeModule {
/**
* @param {Compilation} compilation the compilation
* @returns {JsonpCompilationPluginHooks} hooks
*/
static getCompilationHooks(compilation) {
if (!(compilation instanceof Compilation)) {
throw new TypeError(
"The 'compilation' argument must be an instance of Compilation"
);
}
let hooks = compilationHooksMap.get(compilation);
if (hooks === undefined) {
hooks = {
createStylesheet: new SyncWaterfallHook(["source", "chunk"])
};
compilationHooksMap.set(compilation, hooks);
}
return hooks;
}
constructor(runtimeRequirements, runtimeOptions) {
super("css loading", 10);
this._runtimeRequirements = runtimeRequirements;
this.runtimeOptions = runtimeOptions;
}
/**
* @returns {string} runtime code
*/
generate() {
const { compilation, chunk, _runtimeRequirements } = this;
const {
chunkGraph,
runtimeTemplate,
outputOptions: {
crossOriginLoading,
uniqueName,
chunkLoadTimeout: loadTimeout
}
} = compilation;
const fn = RuntimeGlobals.ensureChunkHandlers;
const conditionMap = chunkGraph.getChunkConditionMap(
chunk,
(chunk, chunkGraph) =>
!!chunkGraph.getChunkModulesIterableBySourceType(chunk, "css")
);
const hasCssMatcher = compileBooleanMatcher(conditionMap);
const withLoading =
_runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) &&
hasCssMatcher !== false;
const withHmr = _runtimeRequirements.has(
RuntimeGlobals.hmrDownloadUpdateHandlers
);
const initialChunkIdsWithCss = new Set();
const initialChunkIdsWithoutCss = new Set();
for (const c of chunk.getAllInitialChunks()) {
(chunkHasCss(c, chunkGraph)
? initialChunkIdsWithCss
: initialChunkIdsWithoutCss
).add(c.id);
}
if (!withLoading && !withHmr && initialChunkIdsWithCss.size === 0) {
return null;
}
const { createStylesheet } =
CssLoadingRuntimeModule.getCompilationHooks(compilation);
const stateExpression = withHmr
? `${RuntimeGlobals.hmrRuntimeStatePrefix}_css`
: undefined;
const code = Template.asString([
"link = document.createElement('link');",
uniqueName
? 'link.setAttribute("data-webpack", uniqueName + ":" + key);'
: "",
"link.setAttribute(loadingAttribute, 1);",
'link.rel = "stylesheet";',
"link.href = url;",
crossOriginLoading
? Template.asString([
"if (link.src.indexOf(window.location.origin + '/') !== 0) {",
Template.indent(
`link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
),
"}"
])
: ""
]);
const cc = str => str.charCodeAt(0);
return Template.asString([
"// object to store loaded and loading chunks",
"// undefined = chunk not loaded, null = chunk preloaded/prefetched",
"// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded",
`var installedChunks = ${
stateExpression ? `${stateExpression} = ${stateExpression} || ` : ""
}{${Array.from(
initialChunkIdsWithoutCss,
id => `${JSON.stringify(id)}:0`
).join(",")}};`,
"",
uniqueName
? `var uniqueName = ${JSON.stringify(
runtimeTemplate.outputOptions.uniqueName
)};`
: "// data-webpack is not used as build has no uniqueName",
`var loadCssChunkData = ${runtimeTemplate.basicFunction(
"target, link, chunkId",
[
`var data, token = "", token2, exports = {}, exportsWithId = [], exportsWithDashes = [], ${
withHmr ? "moduleIds = [], " : ""
}i = 0, cc = 1;`,
"try { if(!link) link = loadStylesheet(chunkId); data = link.sheet.cssRules; data = data[data.length - 1].style; } catch(e) { data = getComputedStyle(document.head); }",
`data = data.getPropertyValue(${
uniqueName
? runtimeTemplate.concatenation(
"--webpack-",
{ expr: "uniqueName" },
"-",
{ expr: "chunkId" }
)
: runtimeTemplate.concatenation("--webpack-", { expr: "chunkId" })
});`,
"if(!data) return [];",
"for(; cc; i++) {",
Template.indent([
"cc = data.charCodeAt(i);",
`if(cc == ${cc("(")}) { token2 = token; token = ""; }`,
`else if(cc == ${cc(
")"
)}) { exports[token2.replace(/^_/, "")] = token.replace(/^_/, ""); token = ""; }`,
`else if(cc == ${cc("/")} || cc == ${cc(
"%"
)}) { token = token.replace(/^_/, ""); exports[token] = token; exportsWithId.push(token); if(cc == ${cc(
"%"
)}) exportsWithDashes.push(token); token = ""; }`,
`else if(!cc || cc == ${cc(
","
)}) { token = token.replace(/^_/, ""); exportsWithId.forEach(${runtimeTemplate.expressionFunction(
`exports[x] = ${
uniqueName
? runtimeTemplate.concatenation(
{ expr: "uniqueName" },
"-",
{ expr: "token" },
"-",
{ expr: "exports[x]" }
)
: runtimeTemplate.concatenation({ expr: "token" }, "-", {
expr: "exports[x]"
})
}`,
"x"
)}); exportsWithDashes.forEach(${runtimeTemplate.expressionFunction(
`exports[x] = "--" + exports[x]`,
"x"
)}); ${
RuntimeGlobals.makeNamespaceObject
}(exports); target[token] = (${runtimeTemplate.basicFunction(
"exports, module",
`module.exports = exports;`
)}).bind(null, exports); ${
withHmr ? "moduleIds.push(token); " : ""
}token = ""; exports = {}; exportsWithId.length = 0; }`,
`else if(cc == ${cc("\\")}) { token += data[++i] }`,
`else { token += data[i]; }`
]),
"}",
`${
withHmr ? `if(target == ${RuntimeGlobals.moduleFactories}) ` : ""
}installedChunks[chunkId] = 0;`,
withHmr ? "return moduleIds;" : ""
]
)}`,
'var loadingAttribute = "data-webpack-loading";',
`var loadStylesheet = ${runtimeTemplate.basicFunction(
"chunkId, url, done" + (withHmr ? ", hmr" : ""),
[
'var link, needAttach, key = "chunk-" + chunkId;',
withHmr ? "if(!hmr) {" : "",
'var links = document.getElementsByTagName("link");',
"for(var i = 0; i < links.length; i++) {",
Template.indent([
"var l = links[i];",
`if(l.rel == "stylesheet" && (${
withHmr
? 'l.href.startsWith(url) || l.getAttribute("href").startsWith(url)'
: 'l.href == url || l.getAttribute("href") == url'
}${
uniqueName
? ' || l.getAttribute("data-webpack") == uniqueName + ":" + key'
: ""
})) { link = l; break; }`
]),
"}",
"if(!done) return link;",
withHmr ? "}" : "",
"if(!link) {",
Template.indent([
"needAttach = true;",
createStylesheet.call(code, this.chunk)
]),
"}",
`var onLinkComplete = ${runtimeTemplate.basicFunction(
"prev, event",
Template.asString([
"link.onerror = link.onload = null;",
"link.removeAttribute(loadingAttribute);",
"clearTimeout(timeout);",
'if(event && event.type != "load") link.parentNode.removeChild(link)',
"done(event);",
"if(prev) return prev(event);"
])
)};`,
"if(link.getAttribute(loadingAttribute)) {",
Template.indent([
`var timeout = setTimeout(onLinkComplete.bind(null, undefined, { type: 'timeout', target: link }), ${loadTimeout});`,
"link.onerror = onLinkComplete.bind(null, link.onerror);",
"link.onload = onLinkComplete.bind(null, link.onload);"
]),
"} else onLinkComplete(undefined, { type: 'load', target: link });", // We assume any existing stylesheet is render blocking
withHmr ? "hmr ? document.head.insertBefore(link, hmr) :" : "",
"needAttach && document.head.appendChild(link);",
"return link;"
]
)};`,
initialChunkIdsWithCss.size > 2
? `${JSON.stringify(
Array.from(initialChunkIdsWithCss)
)}.forEach(loadCssChunkData.bind(null, ${
RuntimeGlobals.moduleFactories
}, 0));`
: initialChunkIdsWithCss.size > 0
? `${Array.from(
initialChunkIdsWithCss,
id =>
`loadCssChunkData(${
RuntimeGlobals.moduleFactories
}, 0, ${JSON.stringify(id)});`
).join("")}`
: "// no initial css",
"",
withLoading
? Template.asString([
`${fn}.css = ${runtimeTemplate.basicFunction("chunkId, promises", [
"// css chunk loading",
`var installedChunkData = ${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;`,
'if(installedChunkData !== 0) { // 0 means "already installed".',
Template.indent([
"",
'// a Promise means "currently loading".',
"if(installedChunkData) {",
Template.indent(["promises.push(installedChunkData[2]);"]),
"} else {",
Template.indent([
hasCssMatcher === true
? "if(true) { // all chunks have CSS"
: `if(${hasCssMatcher("chunkId")}) {`,
Template.indent([
"// setup Promise in chunk cache",
`var promise = new Promise(${runtimeTemplate.expressionFunction(
`installedChunkData = installedChunks[chunkId] = [resolve, reject]`,
"resolve, reject"
)});`,
"promises.push(installedChunkData[2] = promise);",
"",
"// start chunk loading",
`var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkCssFilename}(chunkId);`,
"// create error before stack unwound to get useful stacktrace later",
"var error = new Error();",
`var loadingEnded = ${runtimeTemplate.basicFunction(
"event",
[
`if(${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId)) {`,
Template.indent([
"installedChunkData = installedChunks[chunkId];",
"if(installedChunkData !== 0) installedChunks[chunkId] = undefined;",
"if(installedChunkData) {",
Template.indent([
'if(event.type !== "load") {',
Template.indent([
"var errorType = event && event.type;",
"var realSrc = event && event.target && event.target.src;",
"error.message = 'Loading css chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';",
"error.name = 'ChunkLoadError';",
"error.type = errorType;",
"error.request = realSrc;",
"installedChunkData[1](error);"
]),
"} else {",
Template.indent([
`loadCssChunkData(${RuntimeGlobals.moduleFactories}, link, chunkId);`,
"installedChunkData[0]();"
]),
"}"
]),
"}"
]),
"}"
]
)};`,
"var link = loadStylesheet(chunkId, url, loadingEnded);"
]),
"} else installedChunks[chunkId] = 0;"
]),
"}"
]),
"}"
])};`
])
: "// no chunk loading",
"",
withHmr
? Template.asString([
"var oldTags = [];",
"var newTags = [];",
`var applyHandler = ${runtimeTemplate.basicFunction("options", [
`return { dispose: ${runtimeTemplate.basicFunction(
"",
[]
)}, apply: ${runtimeTemplate.basicFunction("", [
"var moduleIds = [];",
`newTags.forEach(${runtimeTemplate.expressionFunction(
"info[1].sheet.disabled = false",
"info"
)});`,
"while(oldTags.length) {",
Template.indent([
"var oldTag = oldTags.pop();",
"if(oldTag.parentNode) oldTag.parentNode.removeChild(oldTag);"
]),
"}",
"while(newTags.length) {",
Template.indent([
`var info = newTags.pop();`,
`var chunkModuleIds = loadCssChunkData(${RuntimeGlobals.moduleFactories}, info[1], info[0]);`,
`chunkModuleIds.forEach(${runtimeTemplate.expressionFunction(
"moduleIds.push(id)",
"id"
)});`
]),
"}",
"return moduleIds;"
])} };`
])}`,
`var cssTextKey = ${runtimeTemplate.returningFunction(
`Array.from(link.sheet.cssRules, ${runtimeTemplate.returningFunction(
"r.cssText",
"r"
)}).join()`,
"link"
)}`,
`${
RuntimeGlobals.hmrDownloadUpdateHandlers
}.css = ${runtimeTemplate.basicFunction(
"chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList",
[
"applyHandlers.push(applyHandler);",
`chunkIds.forEach(${runtimeTemplate.basicFunction("chunkId", [
`var filename = ${RuntimeGlobals.getChunkCssFilename}(chunkId);`,
`var url = ${RuntimeGlobals.publicPath} + filename;`,
"var oldTag = loadStylesheet(chunkId, url);",
"if(!oldTag) return;",
`promises.push(new Promise(${runtimeTemplate.basicFunction(
"resolve, reject",
[
`var link = loadStylesheet(chunkId, url + (url.indexOf("?") < 0 ? "?" : "&") + "hmr=" + Date.now(), ${runtimeTemplate.basicFunction(
"event",
[
'if(event.type !== "load") {',
Template.indent([
"var errorType = event && event.type;",
"var realSrc = event && event.target && event.target.src;",
"error.message = 'Loading css hot update chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';",
"error.name = 'ChunkLoadError';",
"error.type = errorType;",
"error.request = realSrc;",
"reject(error);"
]),
"} else {",
Template.indent([
"try { if(cssTextKey(oldTag) == cssTextKey(link)) { if(link.parentNode) link.parentNode.removeChild(link); return resolve(); } } catch(e) {}",
"var factories = {};",
"loadCssChunkData(factories, link, chunkId);",
`Object.keys(factories).forEach(${runtimeTemplate.expressionFunction(
"updatedModulesList.push(id)",
"id"
)})`,
"link.sheet.disabled = true;",
"oldTags.push(oldTag);",
"newTags.push([chunkId, link]);",
"resolve();"
]),
"}"
]
)}, oldTag);`
]
)}));`
])});`
]
)}`
])
: "// no hmr"
]);
}
}
module.exports = CssLoadingRuntimeModule;

462
lib/css/CssModulesPlugin.js Normal file
View File

@ -0,0 +1,462 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const HotUpdateChunk = require("../HotUpdateChunk");
const RuntimeGlobals = require("../RuntimeGlobals");
const SelfModuleFactory = require("../SelfModuleFactory");
const CssExportDependency = require("../dependencies/CssExportDependency");
const CssImportDependency = require("../dependencies/CssImportDependency");
const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
const CssUrlDependency = require("../dependencies/CssUrlDependency");
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
const { compareModulesByIdentifier } = require("../util/comparators");
const createSchemaValidation = require("../util/create-schema-validation");
const createHash = require("../util/createHash");
const memoize = require("../util/memoize");
const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
const CssExportsGenerator = require("./CssExportsGenerator");
const CssGenerator = require("./CssGenerator");
const CssParser = require("./CssParser");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").CssExperimentOptions} CssExperimentOptions */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
const getCssLoadingRuntimeModule = memoize(() =>
require("./CssLoadingRuntimeModule")
);
const getSchema = name => {
const { definitions } = require("../../schemas/WebpackOptions.json");
return {
definitions,
oneOf: [{ $ref: `#/definitions/${name}` }]
};
};
const validateGeneratorOptions = createSchemaValidation(
require("../../schemas/plugins/css/CssGeneratorOptions.check.js"),
() => getSchema("CssGeneratorOptions"),
{
name: "Css Modules Plugin",
baseDataPath: "parser"
}
);
const validateParserOptions = createSchemaValidation(
require("../../schemas/plugins/css/CssParserOptions.check.js"),
() => getSchema("CssParserOptions"),
{
name: "Css Modules Plugin",
baseDataPath: "parser"
}
);
const escapeCss = (str, omitOptionalUnderscore) => {
const escaped = `${str}`.replace(
// cspell:word uffff
/[^a-zA-Z0-9_\u0081-\uffff-]/g,
s => `\\${s}`
);
return !omitOptionalUnderscore && /^(?!--)[0-9_-]/.test(escaped)
? `_${escaped}`
: escaped;
};
const plugin = "CssModulesPlugin";
class CssModulesPlugin {
/**
* @param {CssExperimentOptions} options options
*/
constructor({ exportsOnly = false }) {
this._exportsOnly = exportsOnly;
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
plugin,
(compilation, { normalModuleFactory }) => {
const selfFactory = new SelfModuleFactory(compilation.moduleGraph);
compilation.dependencyFactories.set(
CssUrlDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CssUrlDependency,
new CssUrlDependency.Template()
);
compilation.dependencyTemplates.set(
CssLocalIdentifierDependency,
new CssLocalIdentifierDependency.Template()
);
compilation.dependencyFactories.set(
CssSelfLocalIdentifierDependency,
selfFactory
);
compilation.dependencyTemplates.set(
CssSelfLocalIdentifierDependency,
new CssSelfLocalIdentifierDependency.Template()
);
compilation.dependencyTemplates.set(
CssExportDependency,
new CssExportDependency.Template()
);
compilation.dependencyFactories.set(
CssImportDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CssImportDependency,
new CssImportDependency.Template()
);
compilation.dependencyTemplates.set(
StaticExportsDependency,
new StaticExportsDependency.Template()
);
normalModuleFactory.hooks.createParser
.for("css")
.tap(plugin, parserOptions => {
validateParserOptions(parserOptions);
return new CssParser();
});
normalModuleFactory.hooks.createParser
.for("css/global")
.tap(plugin, parserOptions => {
validateParserOptions(parserOptions);
return new CssParser({
allowPseudoBlocks: false,
allowModeSwitch: false
});
});
normalModuleFactory.hooks.createParser
.for("css/module")
.tap(plugin, parserOptions => {
validateParserOptions(parserOptions);
return new CssParser({
defaultMode: "local"
});
});
normalModuleFactory.hooks.createGenerator
.for("css")
.tap(plugin, generatorOptions => {
validateGeneratorOptions(generatorOptions);
return this._exportsOnly
? new CssExportsGenerator()
: new CssGenerator();
});
normalModuleFactory.hooks.createGenerator
.for("css/global")
.tap(plugin, generatorOptions => {
validateGeneratorOptions(generatorOptions);
return this._exportsOnly
? new CssExportsGenerator()
: new CssGenerator();
});
normalModuleFactory.hooks.createGenerator
.for("css/module")
.tap(plugin, generatorOptions => {
validateGeneratorOptions(generatorOptions);
return this._exportsOnly
? new CssExportsGenerator()
: new CssGenerator();
});
const orderedCssModulesPerChunk = new WeakMap();
compilation.hooks.afterCodeGeneration.tap("CssModulesPlugin", () => {
const { chunkGraph } = compilation;
for (const chunk of compilation.chunks) {
if (CssModulesPlugin.chunkHasCss(chunk, chunkGraph)) {
orderedCssModulesPerChunk.set(
chunk,
this.getOrderedChunkCssModules(chunk, chunkGraph, compilation)
);
}
}
});
compilation.hooks.contentHash.tap("CssModulesPlugin", chunk => {
const {
chunkGraph,
outputOptions: {
hashSalt,
hashDigest,
hashDigestLength,
hashFunction
}
} = compilation;
const modules = orderedCssModulesPerChunk.get(chunk);
if (modules === undefined) return;
const hash = createHash(hashFunction);
if (hashSalt) hash.update(hashSalt);
for (const module of modules) {
hash.update(chunkGraph.getModuleHash(module, chunk.runtime));
}
const digest = /** @type {string} */ (hash.digest(hashDigest));
chunk.contentHash.css = nonNumericOnlyHash(digest, hashDigestLength);
});
compilation.hooks.renderManifest.tap(plugin, (result, options) => {
const { chunkGraph } = compilation;
const { hash, chunk, codeGenerationResults } = options;
if (chunk instanceof HotUpdateChunk) return result;
const modules = orderedCssModulesPerChunk.get(chunk);
if (modules !== undefined) {
result.push({
render: () =>
this.renderChunk({
chunk,
chunkGraph,
codeGenerationResults,
uniqueName: compilation.outputOptions.uniqueName,
modules
}),
filenameTemplate: CssModulesPlugin.getChunkFilenameTemplate(
chunk,
compilation.outputOptions
),
pathOptions: {
hash,
runtime: chunk.runtime,
chunk,
contentHashType: "css"
},
identifier: `css${chunk.id}`,
hash: chunk.contentHash.css
});
}
return result;
});
const enabledChunks = new WeakSet();
const handler = (chunk, set) => {
if (enabledChunks.has(chunk)) {
return;
}
enabledChunks.add(chunk);
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkCssFilename);
set.add(RuntimeGlobals.hasOwnProperty);
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
set.add(RuntimeGlobals.makeNamespaceObject);
const CssLoadingRuntimeModule = getCssLoadingRuntimeModule();
compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set));
};
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hasCssModules)
.tap(plugin, handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap(plugin, handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap(plugin, handler);
}
);
}
getModulesInOrder(chunk, modules, compilation) {
if (!modules) return [];
const modulesList = [...modules];
// Get ordered list of modules per chunk group
// Lists are in reverse order to allow to use Array.pop()
const modulesByChunkGroup = Array.from(chunk.groupsIterable, chunkGroup => {
const sortedModules = modulesList
.map(module => {
return {
module,
index: chunkGroup.getModulePostOrderIndex(module)
};
})
.filter(item => item.index !== undefined)
.sort((a, b) => b.index - a.index)
.map(item => item.module);
return { list: sortedModules, set: new Set(sortedModules) };
});
if (modulesByChunkGroup.length === 1)
return modulesByChunkGroup[0].list.reverse();
const compareModuleLists = ({ list: a }, { list: b }) => {
if (a.length === 0) {
return b.length === 0 ? 0 : 1;
} else {
if (b.length === 0) return -1;
return compareModulesByIdentifier(a[a.length - 1], b[b.length - 1]);
}
};
modulesByChunkGroup.sort(compareModuleLists);
const finalModules = [];
for (;;) {
const failedModules = new Set();
const list = modulesByChunkGroup[0].list;
if (list.length === 0) {
// done, everything empty
break;
}
let selectedModule = list[list.length - 1];
let hasFailed = undefined;
outer: for (;;) {
for (const { list, set } of modulesByChunkGroup) {
if (list.length === 0) continue;
const lastModule = list[list.length - 1];
if (lastModule === selectedModule) continue;
if (!set.has(selectedModule)) continue;
failedModules.add(selectedModule);
if (failedModules.has(lastModule)) {
// There is a conflict, try other alternatives
hasFailed = lastModule;
continue;
}
selectedModule = lastModule;
hasFailed = false;
continue outer; // restart
}
break;
}
if (hasFailed) {
// There is a not resolve-able conflict with the selectedModule
if (compilation) {
// TODO print better warning
compilation.warnings.push(
new Error(
`chunk ${
chunk.name || chunk.id
}\nConflicting order between ${hasFailed.readableIdentifier(
compilation.requestShortener
)} and ${selectedModule.readableIdentifier(
compilation.requestShortener
)}`
)
);
}
selectedModule = hasFailed;
}
// Insert the selected module into the final modules list
finalModules.push(selectedModule);
// Remove the selected module from all lists
for (const { list, set } of modulesByChunkGroup) {
const lastModule = list[list.length - 1];
if (lastModule === selectedModule) list.pop();
else if (hasFailed && set.has(selectedModule)) {
const idx = list.indexOf(selectedModule);
if (idx >= 0) list.splice(idx, 1);
}
}
modulesByChunkGroup.sort(compareModuleLists);
}
return finalModules;
}
getOrderedChunkCssModules(chunk, chunkGraph, compilation) {
return [
...this.getModulesInOrder(
chunk,
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css-import",
compareModulesByIdentifier
),
compilation
),
...this.getModulesInOrder(
chunk,
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css",
compareModulesByIdentifier
),
compilation
)
];
}
renderChunk({
uniqueName,
chunk,
chunkGraph,
codeGenerationResults,
modules
}) {
const source = new ConcatSource();
const metaData = [];
for (const module of modules) {
try {
const codeGenResult = codeGenerationResults.get(module, chunk.runtime);
const s =
codeGenResult.sources.get("css") ||
codeGenResult.sources.get("css-import");
if (s) {
source.add(s);
source.add("\n");
}
const exports =
codeGenResult.data && codeGenResult.data.get("css-exports");
const moduleId = chunkGraph.getModuleId(module) + "";
metaData.push(
`${
exports
? Array.from(exports, ([n, v]) => {
const shortcutValue = `${
uniqueName ? uniqueName + "-" : ""
}${moduleId}-${n}`;
return v === shortcutValue
? `${escapeCss(n)}/`
: v === "--" + shortcutValue
? `${escapeCss(n)}%`
: `${escapeCss(n)}(${escapeCss(v)})`;
}).join("")
: ""
}${escapeCss(moduleId)}`
);
} catch (e) {
e.message += `\nduring rendering of css ${module.identifier()}`;
throw e;
}
}
source.add(
`head{--webpack-${escapeCss(
(uniqueName ? uniqueName + "-" : "") + chunk.id,
true
)}:${metaData.join(",")};}`
);
return source;
}
static getChunkFilenameTemplate(chunk, outputOptions) {
if (chunk.cssFilenameTemplate) {
return chunk.cssFilenameTemplate;
} else if (chunk.canBeInitial()) {
return outputOptions.cssFilename;
} else {
return outputOptions.cssChunkFilename;
}
}
static chunkHasCss(chunk, chunkGraph) {
return (
!!chunkGraph.getChunkModulesIterableBySourceType(chunk, "css") ||
!!chunkGraph.getChunkModulesIterableBySourceType(chunk, "css-import")
);
}
}
module.exports = CssModulesPlugin;

618
lib/css/CssParser.js Normal file
View File

@ -0,0 +1,618 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const Parser = require("../Parser");
const ConstDependency = require("../dependencies/ConstDependency");
const CssExportDependency = require("../dependencies/CssExportDependency");
const CssImportDependency = require("../dependencies/CssImportDependency");
const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
const CssUrlDependency = require("../dependencies/CssUrlDependency");
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
const walkCssTokens = require("./walkCssTokens");
/** @typedef {import("../Parser").ParserState} ParserState */
/** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
const CC_LEFT_CURLY = "{".charCodeAt(0);
const CC_RIGHT_CURLY = "}".charCodeAt(0);
const CC_COLON = ":".charCodeAt(0);
const CC_SLASH = "/".charCodeAt(0);
const CC_SEMICOLON = ";".charCodeAt(0);
const cssUnescape = str => {
return str.replace(/\\([0-9a-fA-F]{1,6}[ \t\n\r\f]?|[\s\S])/g, match => {
if (match.length > 2) {
return String.fromCharCode(parseInt(match.slice(1).trim(), 16));
} else {
return match[1];
}
});
};
class LocConverter {
constructor(input) {
this._input = input;
this.line = 1;
this.column = 0;
this.pos = 0;
}
get(pos) {
if (this.pos !== pos) {
if (this.pos < pos) {
const str = this._input.slice(this.pos, pos);
let i = str.lastIndexOf("\n");
if (i === -1) {
this.column += str.length;
} else {
this.column = str.length - i - 1;
this.line++;
while (i > 0 && (i = str.lastIndexOf("\n", i - 1)) !== -1)
this.line++;
}
} else {
let i = this._input.lastIndexOf("\n", this.pos);
while (i >= pos) {
this.line--;
i = i > 0 ? this._input.lastIndexOf("\n", i - 1) : -1;
}
this.column = pos - i;
}
this.pos = pos;
}
return this;
}
}
const CSS_MODE_TOP_LEVEL = 0;
const CSS_MODE_IN_RULE = 1;
const CSS_MODE_IN_LOCAL_RULE = 2;
const CSS_MODE_AT_IMPORT_EXPECT_URL = 3;
// TODO implement layer and supports for @import
const CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS = 4;
const CSS_MODE_AT_IMPORT_EXPECT_MEDIA = 5;
const CSS_MODE_AT_OTHER = 6;
const explainMode = mode => {
switch (mode) {
case CSS_MODE_TOP_LEVEL:
return "parsing top level css";
case CSS_MODE_IN_RULE:
return "parsing css rule content (global)";
case CSS_MODE_IN_LOCAL_RULE:
return "parsing css rule content (local)";
case CSS_MODE_AT_IMPORT_EXPECT_URL:
return "parsing @import (expecting url)";
case CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS:
return "parsing @import (expecting optionally supports or media query)";
case CSS_MODE_AT_IMPORT_EXPECT_MEDIA:
return "parsing @import (expecting optionally media query)";
case CSS_MODE_AT_OTHER:
return "parsing at-rule";
default:
return mode;
}
};
class CssParser extends Parser {
constructor({
allowPseudoBlocks = true,
allowModeSwitch = true,
defaultMode = "global"
} = {}) {
super();
this.allowPseudoBlocks = allowPseudoBlocks;
this.allowModeSwitch = allowModeSwitch;
this.defaultMode = defaultMode;
}
/**
* @param {string | Buffer | PreparsedAst} source the source to parse
* @param {ParserState} state the parser state
* @returns {ParserState} the parser state
*/
parse(source, state) {
if (Buffer.isBuffer(source)) {
source = source.toString("utf-8");
} else if (typeof source === "object") {
throw new Error("webpackAst is unexpected for the CssParser");
}
if (source[0] === "\ufeff") {
source = source.slice(1);
}
const module = state.module;
const declaredCssVariables = new Set();
const locConverter = new LocConverter(source);
let mode = CSS_MODE_TOP_LEVEL;
let modePos = 0;
let modeNestingLevel = 0;
let modeData = undefined;
let singleClassSelector = undefined;
let lastIdentifier = undefined;
const modeStack = [];
const isTopLevelLocal = () =>
modeData === "local" ||
(this.defaultMode === "local" && modeData === undefined);
const eatWhiteLine = (input, pos) => {
for (;;) {
const cc = input.charCodeAt(pos);
if (cc === 32 || cc === 9) {
pos++;
continue;
}
if (cc === 10) pos++;
break;
}
return pos;
};
const eatUntil = chars => {
const charCodes = Array.from({ length: chars.length }, (_, i) =>
chars.charCodeAt(i)
);
const arr = Array.from(
{ length: charCodes.reduce((a, b) => Math.max(a, b), 0) + 1 },
() => false
);
charCodes.forEach(cc => (arr[cc] = true));
return (input, pos) => {
for (;;) {
const cc = input.charCodeAt(pos);
if (cc < arr.length && arr[cc]) {
return pos;
}
pos++;
if (pos === input.length) return pos;
}
};
};
const eatText = (input, pos, eater) => {
let text = "";
for (;;) {
if (input.charCodeAt(pos) === CC_SLASH) {
const newPos = walkCssTokens.eatComments(input, pos);
if (pos !== newPos) {
pos = newPos;
if (pos === input.length) break;
} else {
text += "/";
pos++;
if (pos === input.length) break;
}
}
const newPos = eater(input, pos);
if (pos !== newPos) {
text += input.slice(pos, newPos);
pos = newPos;
} else {
break;
}
if (pos === input.length) break;
}
return [pos, text.trimRight()];
};
const eatExportName = eatUntil(":};/");
const eatExportValue = eatUntil("};/");
const parseExports = (input, pos) => {
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
const cc = input.charCodeAt(pos);
if (cc !== CC_LEFT_CURLY)
throw new Error(
`Unexpected ${input[pos]} at ${pos} during parsing of ':export' (expected '{')`
);
pos++;
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
for (;;) {
if (input.charCodeAt(pos) === CC_RIGHT_CURLY) break;
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
if (pos === input.length) return pos;
let start = pos;
let name;
[pos, name] = eatText(input, pos, eatExportName);
if (pos === input.length) return pos;
if (input.charCodeAt(pos) !== CC_COLON) {
throw new Error(
`Unexpected ${input[pos]} at ${pos} during parsing of export name in ':export' (expected ':')`
);
}
pos++;
if (pos === input.length) return pos;
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
if (pos === input.length) return pos;
let value;
[pos, value] = eatText(input, pos, eatExportValue);
if (pos === input.length) return pos;
const cc = input.charCodeAt(pos);
if (cc === CC_SEMICOLON) {
pos++;
if (pos === input.length) return pos;
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
if (pos === input.length) return pos;
} else if (cc !== CC_RIGHT_CURLY) {
throw new Error(
`Unexpected ${input[pos]} at ${pos} during parsing of export value in ':export' (expected ';' or '}')`
);
}
const dep = new CssExportDependency(name, value);
const { line: sl, column: sc } = locConverter.get(start);
const { line: el, column: ec } = locConverter.get(pos);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
}
pos++;
if (pos === input.length) return pos;
pos = eatWhiteLine(input, pos);
return pos;
};
const eatPropertyName = eatUntil(":{};");
const processLocalDeclaration = (input, pos) => {
modeData = undefined;
const start = pos;
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
const propertyNameStart = pos;
const [propertyNameEnd, propertyName] = eatText(
input,
pos,
eatPropertyName
);
if (input.charCodeAt(propertyNameEnd) !== CC_COLON) return start;
pos = propertyNameEnd + 1;
if (propertyName.startsWith("--")) {
// CSS Variable
const { line: sl, column: sc } = locConverter.get(propertyNameStart);
const { line: el, column: ec } = locConverter.get(propertyNameEnd);
const name = propertyName.slice(2);
const dep = new CssLocalIdentifierDependency(
name,
[propertyNameStart, propertyNameEnd],
"--"
);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
declaredCssVariables.add(name);
} else if (
propertyName === "animation-name" ||
propertyName === "animation"
) {
modeData = "animation";
lastIdentifier = undefined;
}
return pos;
};
const processDeclarationValueDone = (input, pos) => {
if (modeData === "animation" && lastIdentifier) {
const { line: sl, column: sc } = locConverter.get(lastIdentifier[0]);
const { line: el, column: ec } = locConverter.get(lastIdentifier[1]);
const name = input.slice(lastIdentifier[0], lastIdentifier[1]);
const dep = new CssSelfLocalIdentifierDependency(name, lastIdentifier);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
}
};
const eatKeyframes = eatUntil("{};/");
const eatNameInVar = eatUntil(",)};/");
walkCssTokens(source, {
isSelector: () => {
return mode !== CSS_MODE_IN_RULE && mode !== CSS_MODE_IN_LOCAL_RULE;
},
url: (input, start, end, contentStart, contentEnd) => {
const value = cssUnescape(input.slice(contentStart, contentEnd));
switch (mode) {
case CSS_MODE_AT_IMPORT_EXPECT_URL: {
modeData.url = value;
mode = CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS;
break;
}
case CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS:
case CSS_MODE_AT_IMPORT_EXPECT_MEDIA:
throw new Error(
`Unexpected ${input.slice(
start,
end
)} at ${start} during ${explainMode(mode)}`
);
default: {
const dep = new CssUrlDependency(value, [start, end], "url");
const { line: sl, column: sc } = locConverter.get(start);
const { line: el, column: ec } = locConverter.get(end);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
module.addCodeGenerationDependency(dep);
break;
}
}
return end;
},
string: (input, start, end) => {
switch (mode) {
case CSS_MODE_AT_IMPORT_EXPECT_URL: {
modeData.url = cssUnescape(input.slice(start + 1, end - 1));
mode = CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS;
break;
}
}
return end;
},
atKeyword: (input, start, end) => {
const name = input.slice(start, end);
if (name === "@namespace") {
throw new Error("@namespace is not supported in bundled CSS");
}
if (name === "@import") {
if (mode !== CSS_MODE_TOP_LEVEL) {
throw new Error(
`Unexpected @import at ${start} during ${explainMode(mode)}`
);
}
mode = CSS_MODE_AT_IMPORT_EXPECT_URL;
modePos = end;
modeData = {
start: start,
url: undefined,
supports: undefined
};
}
if (name === "@keyframes") {
let pos = end;
pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
if (pos === input.length) return pos;
const [newPos, name] = eatText(input, pos, eatKeyframes);
const { line: sl, column: sc } = locConverter.get(pos);
const { line: el, column: ec } = locConverter.get(newPos);
const dep = new CssLocalIdentifierDependency(name, [pos, newPos]);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
pos = newPos;
if (pos === input.length) return pos;
if (input.charCodeAt(pos) !== CC_LEFT_CURLY) {
throw new Error(
`Unexpected ${input[pos]} at ${pos} during parsing of @keyframes (expected '{')`
);
}
mode = CSS_MODE_IN_LOCAL_RULE;
modeNestingLevel = 1;
return pos + 1;
}
return end;
},
semicolon: (input, start, end) => {
switch (mode) {
case CSS_MODE_AT_IMPORT_EXPECT_URL:
throw new Error(`Expected URL for @import at ${start}`);
case CSS_MODE_AT_IMPORT_EXPECT_MEDIA:
case CSS_MODE_AT_IMPORT_EXPECT_SUPPORTS: {
const { line: sl, column: sc } = locConverter.get(modeData.start);
const { line: el, column: ec } = locConverter.get(end);
end = eatWhiteLine(input, end);
const media = input.slice(modePos, start).trim();
const dep = new CssImportDependency(
modeData.url,
[modeData.start, end],
modeData.supports,
media
);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
break;
}
case CSS_MODE_IN_LOCAL_RULE: {
processDeclarationValueDone(input, start);
return processLocalDeclaration(input, end);
}
case CSS_MODE_IN_RULE: {
return end;
}
}
mode = CSS_MODE_TOP_LEVEL;
modeData = undefined;
singleClassSelector = undefined;
return end;
},
leftCurlyBracket: (input, start, end) => {
switch (mode) {
case CSS_MODE_TOP_LEVEL:
mode = isTopLevelLocal()
? CSS_MODE_IN_LOCAL_RULE
: CSS_MODE_IN_RULE;
modeNestingLevel = 1;
if (mode === CSS_MODE_IN_LOCAL_RULE)
return processLocalDeclaration(input, end);
break;
case CSS_MODE_IN_RULE:
case CSS_MODE_IN_LOCAL_RULE:
modeNestingLevel++;
break;
}
return end;
},
rightCurlyBracket: (input, start, end) => {
switch (mode) {
case CSS_MODE_IN_LOCAL_RULE:
processDeclarationValueDone(input, start);
/* falls through */
case CSS_MODE_IN_RULE:
if (--modeNestingLevel === 0) {
mode = CSS_MODE_TOP_LEVEL;
modeData = undefined;
singleClassSelector = undefined;
}
break;
}
return end;
},
id: (input, start, end) => {
singleClassSelector = false;
switch (mode) {
case CSS_MODE_TOP_LEVEL:
if (isTopLevelLocal()) {
const name = input.slice(start + 1, end);
const dep = new CssLocalIdentifierDependency(name, [
start + 1,
end
]);
const { line: sl, column: sc } = locConverter.get(start);
const { line: el, column: ec } = locConverter.get(end);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
}
break;
}
return end;
},
identifier: (input, start, end) => {
singleClassSelector = false;
switch (mode) {
case CSS_MODE_IN_LOCAL_RULE:
if (modeData === "animation") {
lastIdentifier = [start, end];
}
break;
}
return end;
},
class: (input, start, end) => {
switch (mode) {
case CSS_MODE_TOP_LEVEL: {
if (isTopLevelLocal()) {
const name = input.slice(start + 1, end);
const dep = new CssLocalIdentifierDependency(name, [
start + 1,
end
]);
const { line: sl, column: sc } = locConverter.get(start);
const { line: el, column: ec } = locConverter.get(end);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
if (singleClassSelector === undefined) singleClassSelector = name;
} else {
singleClassSelector = false;
}
break;
}
}
return end;
},
leftParenthesis: (input, start, end) => {
switch (mode) {
case CSS_MODE_TOP_LEVEL: {
modeStack.push(false);
break;
}
}
return end;
},
rightParenthesis: (input, start, end) => {
switch (mode) {
case CSS_MODE_TOP_LEVEL: {
const newModeData = modeStack.pop();
if (newModeData !== false) {
modeData = newModeData;
const dep = new ConstDependency("", [start, end]);
module.addPresentationalDependency(dep);
}
break;
}
}
return end;
},
pseudoClass: (input, start, end) => {
singleClassSelector = false;
switch (mode) {
case CSS_MODE_TOP_LEVEL: {
const name = input.slice(start, end);
if (this.allowModeSwitch && name === ":global") {
modeData = "global";
const dep = new ConstDependency("", [start, end]);
module.addPresentationalDependency(dep);
} else if (this.allowModeSwitch && name === ":local") {
modeData = "local";
const dep = new ConstDependency("", [start, end]);
module.addPresentationalDependency(dep);
} else if (this.allowPseudoBlocks && name === ":export") {
const pos = parseExports(input, end);
const dep = new ConstDependency("", [start, pos]);
module.addPresentationalDependency(dep);
return pos;
}
break;
}
}
return end;
},
pseudoFunction: (input, start, end) => {
switch (mode) {
case CSS_MODE_TOP_LEVEL: {
const name = input.slice(start, end - 1);
if (this.allowModeSwitch && name === ":global") {
modeStack.push(modeData);
modeData = "global";
const dep = new ConstDependency("", [start, end]);
module.addPresentationalDependency(dep);
} else if (this.allowModeSwitch && name === ":local") {
modeStack.push(modeData);
modeData = "local";
const dep = new ConstDependency("", [start, end]);
module.addPresentationalDependency(dep);
} else {
modeStack.push(false);
}
break;
}
}
return end;
},
function: (input, start, end) => {
switch (mode) {
case CSS_MODE_IN_LOCAL_RULE: {
const name = input.slice(start, end - 1);
if (name === "var") {
let pos = walkCssTokens.eatWhitespaceAndComments(input, end);
if (pos === input.length) return pos;
const [newPos, name] = eatText(input, pos, eatNameInVar);
if (!name.startsWith("--")) return end;
const { line: sl, column: sc } = locConverter.get(pos);
const { line: el, column: ec } = locConverter.get(newPos);
const dep = new CssSelfLocalIdentifierDependency(
name.slice(2),
[pos, newPos],
"--",
declaredCssVariables
);
dep.setLoc(sl, sc, el, ec);
module.addDependency(dep);
return newPos;
}
break;
}
}
return end;
},
comma: (input, start, end) => {
switch (mode) {
case CSS_MODE_TOP_LEVEL:
modeData = undefined;
modeStack.length = 0;
break;
case CSS_MODE_IN_LOCAL_RULE:
processDeclarationValueDone(input, start);
break;
}
return end;
}
});
module.buildInfo.strict = true;
module.buildMeta.exportsType = "namespace";
module.addDependency(new StaticExportsDependency([], true));
return state;
}
}
module.exports = CssParser;

659
lib/css/walkCssTokens.js Normal file
View File

@ -0,0 +1,659 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/**
* @typedef {Object} CssTokenCallbacks
* @property {function(string, number): boolean} isSelector
* @property {function(string, number, number, number, number): number=} url
* @property {function(string, number, number): number=} string
* @property {function(string, number, number): number=} leftParenthesis
* @property {function(string, number, number): number=} rightParenthesis
* @property {function(string, number, number): number=} pseudoFunction
* @property {function(string, number, number): number=} function
* @property {function(string, number, number): number=} pseudoClass
* @property {function(string, number, number): number=} atKeyword
* @property {function(string, number, number): number=} class
* @property {function(string, number, number): number=} identifier
* @property {function(string, number, number): number=} id
* @property {function(string, number, number): number=} leftCurlyBracket
* @property {function(string, number, number): number=} rightCurlyBracket
* @property {function(string, number, number): number=} semicolon
* @property {function(string, number, number): number=} comma
*/
/** @typedef {function(string, number, CssTokenCallbacks): number} CharHandler */
// spec: https://drafts.csswg.org/css-syntax/
const CC_LINE_FEED = "\n".charCodeAt(0);
const CC_CARRIAGE_RETURN = "\r".charCodeAt(0);
const CC_FORM_FEED = "\f".charCodeAt(0);
const CC_TAB = "\t".charCodeAt(0);
const CC_SPACE = " ".charCodeAt(0);
const CC_SLASH = "/".charCodeAt(0);
const CC_BACK_SLASH = "\\".charCodeAt(0);
const CC_ASTERISK = "*".charCodeAt(0);
const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
const CC_LEFT_CURLY = "{".charCodeAt(0);
const CC_RIGHT_CURLY = "}".charCodeAt(0);
const CC_QUOTATION_MARK = '"'.charCodeAt(0);
const CC_APOSTROPHE = "'".charCodeAt(0);
const CC_FULL_STOP = ".".charCodeAt(0);
const CC_COLON = ":".charCodeAt(0);
const CC_SEMICOLON = ";".charCodeAt(0);
const CC_COMMA = ",".charCodeAt(0);
const CC_PERCENTAGE = "%".charCodeAt(0);
const CC_AT_SIGN = "@".charCodeAt(0);
const CC_LOW_LINE = "_".charCodeAt(0);
const CC_LOWER_A = "a".charCodeAt(0);
const CC_LOWER_U = "u".charCodeAt(0);
const CC_LOWER_E = "e".charCodeAt(0);
const CC_LOWER_Z = "z".charCodeAt(0);
const CC_UPPER_A = "A".charCodeAt(0);
const CC_UPPER_E = "E".charCodeAt(0);
const CC_UPPER_Z = "Z".charCodeAt(0);
const CC_0 = "0".charCodeAt(0);
const CC_9 = "9".charCodeAt(0);
const CC_NUMBER_SIGN = "#".charCodeAt(0);
const CC_PLUS_SIGN = "+".charCodeAt(0);
const CC_HYPHEN_MINUS = "-".charCodeAt(0);
const CC_LESS_THAN_SIGN = "<".charCodeAt(0);
const CC_GREATER_THAN_SIGN = ">".charCodeAt(0);
const _isNewLine = cc => {
return (
cc === CC_LINE_FEED || cc === CC_CARRIAGE_RETURN || cc === CC_FORM_FEED
);
};
/** @type {CharHandler} */
const consumeSpace = (input, pos, callbacks) => {
let cc;
do {
pos++;
cc = input.charCodeAt(pos);
} while (_isWhiteSpace(cc));
return pos;
};
const _isWhiteSpace = cc => {
return (
cc === CC_LINE_FEED ||
cc === CC_CARRIAGE_RETURN ||
cc === CC_FORM_FEED ||
cc === CC_TAB ||
cc === CC_SPACE
);
};
/** @type {CharHandler} */
const consumeSingleCharToken = (input, pos, callbacks) => {
return pos + 1;
};
/** @type {CharHandler} */
const consumePotentialComment = (input, pos, callbacks) => {
pos++;
if (pos === input.length) return pos;
let cc = input.charCodeAt(pos);
if (cc !== CC_ASTERISK) return pos;
for (;;) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
while (cc === CC_ASTERISK) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
if (cc === CC_SLASH) return pos + 1;
}
}
};
/** @type {function(number): CharHandler} */
const consumeString = end => (input, pos, callbacks) => {
const start = pos;
pos = _consumeString(input, pos, end);
if (callbacks.string !== undefined) {
pos = callbacks.string(input, start, pos);
}
return pos;
};
const _consumeString = (input, pos, end) => {
pos++;
for (;;) {
if (pos === input.length) return pos;
const cc = input.charCodeAt(pos);
if (cc === end) return pos + 1;
if (_isNewLine(cc)) {
// bad string
return pos;
}
if (cc === CC_BACK_SLASH) {
// we don't need to fully parse the escaped code point
// just skip over a potential new line
pos++;
if (pos === input.length) return pos;
pos++;
} else {
pos++;
}
}
};
const _isIdentifierStartCode = cc => {
return (
cc === CC_LOW_LINE ||
(cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
(cc >= CC_UPPER_A && cc <= CC_UPPER_Z) ||
cc > 0x80
);
};
const _isDigit = cc => {
return cc >= CC_0 && cc <= CC_9;
};
const _startsIdentifier = (input, pos) => {
const cc = input.charCodeAt(pos);
if (cc === CC_HYPHEN_MINUS) {
if (pos === input.length) return false;
const cc = input.charCodeAt(pos + 1);
if (cc === CC_HYPHEN_MINUS) return true;
if (cc === CC_BACK_SLASH) {
const cc = input.charCodeAt(pos + 2);
return !_isNewLine(cc);
}
return _isIdentifierStartCode(cc);
}
if (cc === CC_BACK_SLASH) {
const cc = input.charCodeAt(pos + 1);
return !_isNewLine(cc);
}
return _isIdentifierStartCode(cc);
};
/** @type {CharHandler} */
const consumeNumberSign = (input, pos, callbacks) => {
const start = pos;
pos++;
if (pos === input.length) return pos;
if (callbacks.isSelector(input, pos) && _startsIdentifier(input, pos)) {
pos = _consumeIdentifier(input, pos);
if (callbacks.id !== undefined) {
return callbacks.id(input, start, pos);
}
}
return pos;
};
/** @type {CharHandler} */
const consumeMinus = (input, pos, callbacks) => {
const start = pos;
pos++;
if (pos === input.length) return pos;
const cc = input.charCodeAt(pos);
if (cc === CC_FULL_STOP || _isDigit(cc)) {
return consumeNumericToken(input, pos, callbacks);
} else if (cc === CC_HYPHEN_MINUS) {
pos++;
if (pos === input.length) return pos;
const cc = input.charCodeAt(pos);
if (cc === CC_GREATER_THAN_SIGN) {
return pos + 1;
} else {
pos = _consumeIdentifier(input, pos);
if (callbacks.identifier !== undefined) {
return callbacks.identifier(input, start, pos);
}
}
} else if (cc === CC_BACK_SLASH) {
if (pos + 1 === input.length) return pos;
const cc = input.charCodeAt(pos + 1);
if (_isNewLine(cc)) return pos;
pos = _consumeIdentifier(input, pos);
if (callbacks.identifier !== undefined) {
return callbacks.identifier(input, start, pos);
}
} else if (_isIdentifierStartCode(cc)) {
pos++;
pos = _consumeIdentifier(input, pos);
if (callbacks.identifier !== undefined) {
return callbacks.identifier(input, start, pos);
}
}
return pos;
};
/** @type {CharHandler} */
const consumeDot = (input, pos, callbacks) => {
const start = pos;
pos++;
if (pos === input.length) return pos;
const cc = input.charCodeAt(pos);
if (_isDigit(cc)) return consumeNumericToken(input, pos - 2, callbacks);
if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos))
return pos;
pos = _consumeIdentifier(input, pos);
if (callbacks.class !== undefined) return callbacks.class(input, start, pos);
return pos;
};
/** @type {CharHandler} */
const consumeNumericToken = (input, pos, callbacks) => {
pos = _consumeNumber(input, pos);
if (pos === input.length) return pos;
if (_startsIdentifier(input, pos)) return _consumeIdentifier(input, pos);
const cc = input.charCodeAt(pos);
if (cc === CC_PERCENTAGE) return pos + 1;
return pos;
};
/** @type {CharHandler} */
const consumeOtherIdentifier = (input, pos, callbacks) => {
const start = pos;
pos = _consumeIdentifier(input, pos);
if (
pos !== input.length &&
!callbacks.isSelector(input, pos) &&
input.charCodeAt(pos) === CC_LEFT_PARENTHESIS
) {
pos++;
if (callbacks.function !== undefined) {
return callbacks.function(input, start, pos);
}
} else {
if (callbacks.identifier !== undefined) {
return callbacks.identifier(input, start, pos);
}
}
return pos;
};
/** @type {CharHandler} */
const consumePotentialUrl = (input, pos, callbacks) => {
const start = pos;
pos = _consumeIdentifier(input, pos);
if (pos === start + 3 && input.slice(start, pos + 1) === "url(") {
pos++;
let cc = input.charCodeAt(pos);
while (_isWhiteSpace(cc)) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
}
if (cc === CC_QUOTATION_MARK || cc === CC_APOSTROPHE) {
pos++;
const contentStart = pos;
pos = _consumeString(input, pos, cc);
const contentEnd = pos - 1;
cc = input.charCodeAt(pos);
while (_isWhiteSpace(cc)) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
}
if (cc !== CC_RIGHT_PARENTHESIS) return pos;
pos++;
if (callbacks.url !== undefined)
return callbacks.url(input, start, pos, contentStart, contentEnd);
return pos;
} else {
const contentStart = pos;
let contentEnd;
for (;;) {
if (cc === CC_BACK_SLASH) {
pos++;
if (pos === input.length) return pos;
pos++;
} else if (_isWhiteSpace(cc)) {
contentEnd = pos;
do {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
} while (_isWhiteSpace(cc));
if (cc !== CC_RIGHT_PARENTHESIS) return pos;
pos++;
if (callbacks.url !== undefined) {
return callbacks.url(input, start, pos, contentStart, contentEnd);
}
return pos;
} else if (cc === CC_RIGHT_PARENTHESIS) {
contentEnd = pos;
pos++;
if (callbacks.url !== undefined) {
return callbacks.url(input, start, pos, contentStart, contentEnd);
}
return pos;
} else if (cc === CC_LEFT_PARENTHESIS) {
return pos;
} else {
pos++;
}
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
}
}
} else {
if (callbacks.identifier !== undefined) {
return callbacks.identifier(input, start, pos);
}
return pos;
}
};
/** @type {CharHandler} */
const consumePotentialPseudo = (input, pos, callbacks) => {
const start = pos;
pos++;
if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos))
return pos;
pos = _consumeIdentifier(input, pos);
let cc = input.charCodeAt(pos);
if (cc === CC_LEFT_PARENTHESIS) {
pos++;
if (callbacks.pseudoFunction !== undefined) {
return callbacks.pseudoFunction(input, start, pos);
}
return pos;
}
if (callbacks.pseudoClass !== undefined) {
return callbacks.pseudoClass(input, start, pos);
}
return pos;
};
/** @type {CharHandler} */
const consumeLeftParenthesis = (input, pos, callbacks) => {
pos++;
if (callbacks.leftParenthesis !== undefined) {
return callbacks.leftParenthesis(input, pos - 1, pos);
}
return pos;
};
/** @type {CharHandler} */
const consumeRightParenthesis = (input, pos, callbacks) => {
pos++;
if (callbacks.rightParenthesis !== undefined) {
return callbacks.rightParenthesis(input, pos - 1, pos);
}
return pos;
};
/** @type {CharHandler} */
const consumeLeftCurlyBracket = (input, pos, callbacks) => {
pos++;
if (callbacks.leftCurlyBracket !== undefined) {
return callbacks.leftCurlyBracket(input, pos - 1, pos);
}
return pos;
};
/** @type {CharHandler} */
const consumeRightCurlyBracket = (input, pos, callbacks) => {
pos++;
if (callbacks.rightCurlyBracket !== undefined) {
return callbacks.rightCurlyBracket(input, pos - 1, pos);
}
return pos;
};
/** @type {CharHandler} */
const consumeSemicolon = (input, pos, callbacks) => {
pos++;
if (callbacks.semicolon !== undefined) {
return callbacks.semicolon(input, pos - 1, pos);
}
return pos;
};
/** @type {CharHandler} */
const consumeComma = (input, pos, callbacks) => {
pos++;
if (callbacks.comma !== undefined) {
return callbacks.comma(input, pos - 1, pos);
}
return pos;
};
const _consumeIdentifier = (input, pos) => {
for (;;) {
const cc = input.charCodeAt(pos);
if (cc === CC_BACK_SLASH) {
pos++;
if (pos === input.length) return pos;
pos++;
} else if (
_isIdentifierStartCode(cc) ||
_isDigit(cc) ||
cc === CC_HYPHEN_MINUS
) {
pos++;
} else {
return pos;
}
}
};
const _consumeNumber = (input, pos) => {
pos++;
if (pos === input.length) return pos;
let cc = input.charCodeAt(pos);
while (_isDigit(cc)) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
}
if (cc === CC_FULL_STOP && pos + 1 !== input.length) {
const next = input.charCodeAt(pos + 1);
if (_isDigit(next)) {
pos += 2;
cc = input.charCodeAt(pos);
while (_isDigit(cc)) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
}
}
}
if (cc === CC_LOWER_E || cc === CC_UPPER_E) {
if (pos + 1 !== input.length) {
const next = input.charCodeAt(pos + 2);
if (_isDigit(next)) {
pos += 2;
} else if (
(next === CC_HYPHEN_MINUS || next === CC_PLUS_SIGN) &&
pos + 2 !== input.length
) {
const next = input.charCodeAt(pos + 2);
if (_isDigit(next)) {
pos += 3;
} else {
return pos;
}
} else {
return pos;
}
}
} else {
return pos;
}
cc = input.charCodeAt(pos);
while (_isDigit(cc)) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
}
return pos;
};
/** @type {CharHandler} */
const consumeLessThan = (input, pos, callbacks) => {
if (input.slice(pos + 1, pos + 4) === "!--") return pos + 4;
return pos + 1;
};
/** @type {CharHandler} */
const consumeAt = (input, pos, callbacks) => {
const start = pos;
pos++;
if (pos === input.length) return pos;
if (_startsIdentifier(input, pos)) {
pos = _consumeIdentifier(input, pos);
if (callbacks.atKeyword !== undefined) {
pos = callbacks.atKeyword(input, start, pos);
}
}
return pos;
};
const CHAR_MAP = Array.from({ length: 0x80 }, (_, cc) => {
// https://drafts.csswg.org/css-syntax/#consume-token
switch (cc) {
case CC_LINE_FEED:
case CC_CARRIAGE_RETURN:
case CC_FORM_FEED:
case CC_TAB:
case CC_SPACE:
return consumeSpace;
case CC_QUOTATION_MARK:
case CC_APOSTROPHE:
return consumeString(cc);
case CC_NUMBER_SIGN:
return consumeNumberSign;
case CC_SLASH:
return consumePotentialComment;
// case CC_LEFT_SQUARE:
// case CC_RIGHT_SQUARE:
// case CC_COMMA:
// case CC_COLON:
// return consumeSingleCharToken;
case CC_COMMA:
return consumeComma;
case CC_SEMICOLON:
return consumeSemicolon;
case CC_LEFT_PARENTHESIS:
return consumeLeftParenthesis;
case CC_RIGHT_PARENTHESIS:
return consumeRightParenthesis;
case CC_LEFT_CURLY:
return consumeLeftCurlyBracket;
case CC_RIGHT_CURLY:
return consumeRightCurlyBracket;
case CC_COLON:
return consumePotentialPseudo;
case CC_PLUS_SIGN:
return consumeNumericToken;
case CC_FULL_STOP:
return consumeDot;
case CC_HYPHEN_MINUS:
return consumeMinus;
case CC_LESS_THAN_SIGN:
return consumeLessThan;
case CC_AT_SIGN:
return consumeAt;
case CC_LOWER_U:
return consumePotentialUrl;
case CC_LOW_LINE:
return consumeOtherIdentifier;
default:
if (_isDigit(cc)) return consumeNumericToken;
if (
(cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
(cc >= CC_UPPER_A && cc <= CC_UPPER_Z)
) {
return consumeOtherIdentifier;
}
return consumeSingleCharToken;
}
});
/**
* @param {string} input input css
* @param {CssTokenCallbacks} callbacks callbacks
* @returns {void}
*/
module.exports = (input, callbacks) => {
let pos = 0;
while (pos < input.length) {
const cc = input.charCodeAt(pos);
if (cc < 0x80) {
pos = CHAR_MAP[cc](input, pos, callbacks);
} else {
pos++;
}
}
};
module.exports.eatComments = (input, pos) => {
loop: for (;;) {
const cc = input.charCodeAt(pos);
if (cc === CC_SLASH) {
if (pos === input.length) return pos;
let cc = input.charCodeAt(pos + 1);
if (cc !== CC_ASTERISK) return pos;
pos++;
for (;;) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
while (cc === CC_ASTERISK) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
if (cc === CC_SLASH) {
pos++;
continue loop;
}
}
}
}
return pos;
}
};
module.exports.eatWhitespaceAndComments = (input, pos) => {
loop: for (;;) {
const cc = input.charCodeAt(pos);
if (cc === CC_SLASH) {
if (pos === input.length) return pos;
let cc = input.charCodeAt(pos + 1);
if (cc !== CC_ASTERISK) return pos;
pos++;
for (;;) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
while (cc === CC_ASTERISK) {
pos++;
if (pos === input.length) return pos;
cc = input.charCodeAt(pos);
if (cc === CC_SLASH) {
pos++;
continue loop;
}
}
}
} else if (_isWhiteSpace(cc)) {
pos++;
continue;
}
return pos;
}
};

View File

@ -123,9 +123,7 @@ class Profiler {
* @returns {Trace} The trace object
*/
const createTrace = (fs, outputPath) => {
const trace = new Tracer({
noStream: true
});
const trace = new Tracer();
const profiler = new Profiler(inspector);
if (/\/|\\/.test(outputPath)) {
const dirPath = dirname(fs, outputPath);
@ -173,6 +171,7 @@ const createTrace = (fs, outputPath) => {
counter,
profiler,
end: callback => {
trace.push("]");
// Wait until the write stream finishes.
fsStream.on("close", () => {
callback();
@ -242,10 +241,10 @@ class ProfilingPlugin {
stage: Infinity
},
(stats, callback) => {
if (compiler.watchMode) return callback();
tracer.profiler.stopProfiling().then(parsedResults => {
if (parsedResults === undefined) {
tracer.profiler.destroy();
tracer.trace.flush();
tracer.end(callback);
return;
}
@ -293,7 +292,6 @@ class ProfilingPlugin {
});
tracer.profiler.destroy();
tracer.trace.flush();
tracer.end(callback);
});
}
@ -345,16 +343,19 @@ const interceptAllJavascriptModulesPluginHooks = (compilation, tracer) => {
};
const makeInterceptorFor = (instance, tracer) => hookName => ({
register: ({ name, type, context, fn }) => {
const newFn = makeNewProfiledTapFn(hookName, tracer, {
name,
type,
fn
});
register: tapInfo => {
const { name, type, fn } = tapInfo;
const newFn =
// Don't tap our own hooks to ensure stream can close cleanly
name === pluginName
? fn
: makeNewProfiledTapFn(hookName, tracer, {
name,
type,
fn
});
return {
name,
type,
context,
...tapInfo,
fn: newFn
};
}

View File

@ -201,7 +201,7 @@ class CommonJsExportsParserPlugin {
if (expr.arguments[1].type === "SpreadElement") return;
if (expr.arguments[2].type === "SpreadElement") return;
const exportsArg = parser.evaluateExpression(expr.arguments[0]);
if (!exportsArg || !exportsArg.isIdentifier()) return;
if (!exportsArg.isIdentifier()) return;
if (
exportsArg.identifier !== "exports" &&
exportsArg.identifier !== "module.exports" &&
@ -210,7 +210,6 @@ class CommonJsExportsParserPlugin {
return;
}
const propertyArg = parser.evaluateExpression(expr.arguments[1]);
if (!propertyArg) return;
const property = propertyArg.asString();
if (typeof property !== "string") return;
enableStructuredExports();

View File

@ -28,8 +28,8 @@ const splitContextFromPrefix = prefix => {
const idx = prefix.lastIndexOf("/");
let context = ".";
if (idx >= 0) {
context = prefix.substr(0, idx);
prefix = `.${prefix.substr(idx)}`;
context = prefix.slice(0, idx);
prefix = `.${prefix.slice(idx)}`;
}
return {
context,
@ -37,7 +37,7 @@ const splitContextFromPrefix = prefix => {
};
};
/** @typedef {Partial<Omit<ContextDependencyOptions, "resource"|"recursive"|"regExp">>} PartialContextDependencyOptions */
/** @typedef {Partial<Omit<ContextDependencyOptions, "resource">>} PartialContextDependencyOptions */
/** @typedef {{ new(options: ContextDependencyOptions, range: [number, number], valueRange: [number, number]): ContextDependency }} ContextDependencyConstructor */

View File

@ -14,11 +14,27 @@ const ModuleDependency = require("./ModuleDependency");
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
class ContextElementDependency extends ModuleDependency {
constructor(request, userRequest, typePrefix, category, referencedExports) {
/**
* @param {string} request request
* @param {string|undefined} userRequest user request
* @param {string} typePrefix type prefix
* @param {string} category category
* @param {string[][]=} referencedExports referenced exports
* @param {string=} context context
*/
constructor(
request,
userRequest,
typePrefix,
category,
referencedExports,
context
) {
super(request);
this.referencedExports = referencedExports;
this._typePrefix = typePrefix;
this._category = category;
this._context = context || undefined;
if (userRequest) {
this.userRequest = userRequest;
@ -33,6 +49,20 @@ class ContextElementDependency extends ModuleDependency {
return "context element";
}
/**
* @returns {string | undefined} a request context
*/
getContext() {
return this._context;
}
/**
* @returns {string | null} an identifier to merge equal requests
*/
getResourceIdentifier() {
return `context${this._context || ""}|${super.getResourceIdentifier()}`;
}
get category() {
return this._category;
}
@ -53,12 +83,20 @@ class ContextElementDependency extends ModuleDependency {
}
serialize(context) {
context.write(this.referencedExports);
const { write } = context;
write(this._typePrefix);
write(this._category);
write(this._context);
write(this.referencedExports);
super.serialize(context);
}
deserialize(context) {
this.referencedExports = context.read();
const { read } = context;
this._typePrefix = read();
this._category = read();
this._context = read();
this.referencedExports = read();
super.deserialize(context);
}
}

Some files were not shown because too many files have changed in this diff Show More