mirror of https://github.com/webpack/webpack.git
Merge branch 'master' into exports-field
# Conflicts: # lib/dependencies/ImportDependency.js # lib/sharing/ConsumeSharedPlugin.js # lib/sharing/ProvideSharedPlugin.js # types.d.ts
This commit is contained in:
commit
198e51a485
|
@ -170,6 +170,9 @@
|
|||
"prewalking",
|
||||
"overridables",
|
||||
"overridable",
|
||||
"darkblue",
|
||||
"darkgreen",
|
||||
"darkred",
|
||||
|
||||
"webassemblyjs",
|
||||
"fsevents",
|
||||
|
@ -209,5 +212,5 @@
|
|||
"dependabot"
|
||||
],
|
||||
"ignoreRegExpList": ["/Author.+/", "/data:.*/", "/\"mappings\":\".+\"/"],
|
||||
"ignorePaths": ["**/dist/**"]
|
||||
"ignorePaths": ["**/dist/**", "examples/**/README.md"]
|
||||
}
|
||||
|
|
|
@ -151,7 +151,8 @@ export type ExternalsType =
|
|||
| "jsonp"
|
||||
| "system"
|
||||
| "promise"
|
||||
| "import";
|
||||
| "import"
|
||||
| "script";
|
||||
/**
|
||||
* Filtering values.
|
||||
*/
|
||||
|
@ -1970,7 +1971,7 @@ export interface EntryDescriptionNormalized {
|
|||
/**
|
||||
* Module(s) that are loaded upon startup. The last one is exported.
|
||||
*/
|
||||
import: [string, ...string[]];
|
||||
import?: [string, ...string[]];
|
||||
/**
|
||||
* Options for library.
|
||||
*/
|
||||
|
|
|
@ -41,10 +41,14 @@ export interface SharedConfig {
|
|||
* Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name.
|
||||
*/
|
||||
import?: false | SharedItem;
|
||||
/**
|
||||
* Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.
|
||||
*/
|
||||
packageName?: string;
|
||||
/**
|
||||
* Version requirement from module in share scope.
|
||||
*/
|
||||
requiredVersion?: string | SharedVersionArray;
|
||||
requiredVersion?: false | string | SharedVersionArray;
|
||||
/**
|
||||
* Module is looked up under this key from the share scope.
|
||||
*/
|
||||
|
|
|
@ -25,7 +25,8 @@ export type ExternalsType =
|
|||
| "jsonp"
|
||||
| "system"
|
||||
| "promise"
|
||||
| "import";
|
||||
| "import"
|
||||
| "script";
|
||||
/**
|
||||
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
|
||||
*/
|
||||
|
|
|
@ -73,7 +73,8 @@ export type ExternalsType =
|
|||
| "jsonp"
|
||||
| "system"
|
||||
| "promise"
|
||||
| "import";
|
||||
| "import"
|
||||
| "script";
|
||||
/**
|
||||
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
|
||||
*/
|
||||
|
@ -257,10 +258,14 @@ export interface SharedConfig {
|
|||
* Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name.
|
||||
*/
|
||||
import?: false | SharedItem;
|
||||
/**
|
||||
* Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.
|
||||
*/
|
||||
packageName?: string;
|
||||
/**
|
||||
* Version requirement from module in share scope.
|
||||
*/
|
||||
requiredVersion?: string | SharedVersionArray;
|
||||
requiredVersion?: false | string | SharedVersionArray;
|
||||
/**
|
||||
* Module is looked up under this key from the share scope.
|
||||
*/
|
||||
|
|
|
@ -51,10 +51,14 @@ export interface ConsumesConfig {
|
|||
* Fallback module if no shared module is found in share scope. Defaults to the property name.
|
||||
*/
|
||||
import?: false | ConsumesItem;
|
||||
/**
|
||||
* Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.
|
||||
*/
|
||||
packageName?: string;
|
||||
/**
|
||||
* Version requirement from module in share scope.
|
||||
*/
|
||||
requiredVersion?: string | SharedVersionArray;
|
||||
requiredVersion?: false | string | SharedVersionArray;
|
||||
/**
|
||||
* Module is looked up under this key from the share scope.
|
||||
*/
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Modules that should be provided as shared modules to the share scope. When provided, property name is used as share key, otherwise share key is automatically inferred from request.
|
||||
* Modules that should be provided as shared modules to the share scope. When provided, property name is used to match modules, otherwise this is automatically inferred from share key.
|
||||
*/
|
||||
export type Provides = (ProvidesItem | ProvidesObject)[] | ProvidesObject;
|
||||
/**
|
||||
* Request to a module that should be provided as shared module to the share scope.
|
||||
* Request to a module that should be provided as shared module to the share scope (will be resolved when relative).
|
||||
*/
|
||||
export type ProvidesItem = string;
|
||||
/**
|
||||
|
@ -19,7 +19,7 @@ export type SharedVersionArray = (number | string)[];
|
|||
|
||||
export interface ProvideSharedPluginOptions {
|
||||
/**
|
||||
* Modules that should be provided as shared modules to the share scope. When provided, property name is used as share key, otherwise share key is automatically inferred from request.
|
||||
* Modules that should be provided as shared modules to the share scope. When provided, property name is used to match modules, otherwise this is automatically inferred from share key.
|
||||
*/
|
||||
provides: Provides;
|
||||
/**
|
||||
|
@ -45,9 +45,9 @@ export interface ProvidesConfig {
|
|||
*/
|
||||
eager?: boolean;
|
||||
/**
|
||||
* Request to a module that should be provided as shared module to the share scope.
|
||||
* Key in the share scope under which the shared modules should be stored.
|
||||
*/
|
||||
import: ProvidesItem;
|
||||
shareKey?: string;
|
||||
/**
|
||||
* Share scope name.
|
||||
*/
|
||||
|
|
|
@ -51,10 +51,14 @@ export interface SharedConfig {
|
|||
* Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name.
|
||||
*/
|
||||
import?: false | SharedItem;
|
||||
/**
|
||||
* Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.
|
||||
*/
|
||||
packageName?: string;
|
||||
/**
|
||||
* Version requirement from module in share scope.
|
||||
*/
|
||||
requiredVersion?: string | SharedVersionArray;
|
||||
requiredVersion?: false | string | SharedVersionArray;
|
||||
/**
|
||||
* Module is looked up under this key from the share scope.
|
||||
*/
|
||||
|
|
|
@ -12,9 +12,11 @@ const async = require("neo-async");
|
|||
|
||||
const extraArgs = "";
|
||||
|
||||
const targetArgs = global.NO_TARGET_ARGS ? "" : " ./example.js -o dist/output.js ";
|
||||
const displayReasons = global.NO_REASONS ? "" : " --display-reasons --display-used-exports --display-provided-exports";
|
||||
const commonArgs = `--display-chunks --no-color --display-max-modules 99999 --display-origins --output-public-path "dist/" ${extraArgs} ${targetArgs}`;
|
||||
const targetArgs = global.NO_TARGET_ARGS ? "" : "./example.js -o dist/output.js ";
|
||||
const displayReasons = global.NO_REASONS ? "" : "--display-reasons --display-used-exports --display-provided-exports";
|
||||
const statsArgs = global.NO_STATS_OPTIONS ? "" : "--display-chunks --display-max-modules 99999 --display-origins";
|
||||
const publicPathArgs = global.NO_PUBLIC_PATH ? "" : '--output-public-path "dist/"';
|
||||
const commonArgs = `--no-color ${statsArgs} ${publicPathArgs} ${extraArgs} ${targetArgs}`;
|
||||
|
||||
let readme = fs.readFileSync(require("path").join(process.cwd(), "template.md"), "utf-8");
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
global.NO_TARGET_ARGS = true;
|
||||
global.NO_REASONS = true;
|
||||
global.NO_STATS_OPTIONS = true;
|
||||
global.NO_PUBLIC_PATH = true;
|
||||
require("../build-common");
|
|
@ -0,0 +1,83 @@
|
|||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.spinner {
|
||||
font-size: 10px;
|
||||
margin: 50px auto;
|
||||
text-indent: -9999em;
|
||||
width: 11em;
|
||||
height: 11em;
|
||||
border-radius: 50%;
|
||||
background: #595959;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
#595959 10%,
|
||||
rgba(89, 89, 89, 0) 42%
|
||||
);
|
||||
position: relative;
|
||||
animation: spin 1.4s infinite linear;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
.spinner:before {
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
background: #595959;
|
||||
border-radius: 100% 0 0 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
}
|
||||
.spinner:after {
|
||||
background: white;
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
border-radius: 50%;
|
||||
content: "";
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- A spinner -->
|
||||
<div class="spinner"></div>
|
||||
|
||||
<!-- This script only contains boostrapping logic -->
|
||||
<!-- It will load all other scripts if neccessary -->
|
||||
<script src="/dist/aaa/app.js" async></script>
|
||||
|
||||
<!-- These script tags are optional -->
|
||||
<!-- They improve loading performance -->
|
||||
<!-- Omitting them will add an additional round trip -->
|
||||
<script src="/dist/bbb/mfeBBB.js" async></script>
|
||||
<script src="/dist/ccc/mfeCCC.js" async></script>
|
||||
|
||||
<!-- All these scripts are pretty small ~5kb -->
|
||||
<!-- For optimal performance they can be inlined -->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
import React from "react";
|
||||
import { formatRelative, subDays } from "date-fns";
|
||||
// date-fns is a shared module, but used as usual
|
||||
// exposing modules act as async boundary,
|
||||
// so no additional async boundary need to be added here
|
||||
// As data-fns is an shared module, it will be placed in a separate file
|
||||
// It will be loaded in parallel to the code of this module
|
||||
|
||||
const Component = ({ locale }) => (
|
||||
<div style={{ border: "5px solid darkblue" }}>
|
||||
<p>I'm a Component exposed from container B!</p>
|
||||
<p>
|
||||
Using date-fn in Remote:{" "}
|
||||
{formatRelative(subDays(new Date(), 2), new Date(), { locale })}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
export default Component;
|
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import { formatRelative, subDays } from "date-fns";
|
||||
|
||||
const Component = ({ locale }) => (
|
||||
<div style={{ border: "5px solid darkred" }}>
|
||||
<p>I'm a Component exposed from container C!</p>
|
||||
<p>
|
||||
Using date-fn in Remote:{" "}
|
||||
{formatRelative(subDays(new Date(), 3), new Date(), { locale })}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
export default Component;
|
|
@ -0,0 +1,11 @@
|
|||
import React from "react";
|
||||
import random from "lodash/random";
|
||||
|
||||
const Component = () => (
|
||||
<div style={{ border: "5px solid darkgreen" }}>
|
||||
<p>I'm a lazy Component exposed from container C!</p>
|
||||
<p>I'm lazy loaded by the app and lazy load another component myself.</p>
|
||||
<p>Using lodash in Remote: {random(0, 6)}</p>
|
||||
</div>
|
||||
);
|
||||
export default Component;
|
|
@ -0,0 +1,26 @@
|
|||
import React from "react";
|
||||
import ComponentB from "mfe-b/Component"; // <- these are remote modules,
|
||||
import ComponentC from "mfe-c/Component"; // <- but they are used as usual packages
|
||||
import { de } from "date-fns/locale";
|
||||
|
||||
// remote modules can also be used with import() which lazy loads them as usual
|
||||
const ComponentD = React.lazy(() => import("mfe-c/Component2"));
|
||||
|
||||
const App = () => (
|
||||
<article>
|
||||
<header>
|
||||
<h1>Hello World</h1>
|
||||
</header>
|
||||
<p>This component is from a remote container:</p>
|
||||
<ComponentB locale={de} />
|
||||
<p>And this component is from another remote container:</p>
|
||||
<ComponentC locale={de} />
|
||||
<React.Suspense fallback={<p>Lazy loading component...</p>}>
|
||||
<p>
|
||||
And this component is from this remote container too, but lazy loaded:
|
||||
</p>
|
||||
<ComponentD />
|
||||
</React.Suspense>
|
||||
</article>
|
||||
);
|
||||
export default App;
|
|
@ -0,0 +1,11 @@
|
|||
import ReactDom from "react-dom";
|
||||
import React from "react"; // <- this is a shared module, but used as usual
|
||||
import App from "./App";
|
||||
|
||||
// load app
|
||||
const el = document.createElement("main");
|
||||
ReactDom.render(<App />, el);
|
||||
document.body.appendChild(el);
|
||||
|
||||
// remove spinner
|
||||
document.body.removeChild(document.getElementsByClassName("spinner")[0]);
|
|
@ -0,0 +1,13 @@
|
|||
// Sharing modules requires that all remotes are initialized
|
||||
// and can provide shared modules to the common scope
|
||||
// As this is an async operation we need an async boundary (import())
|
||||
|
||||
// Using modules from remotes is also an async operation
|
||||
// as chunks need to be loaded for the code of the remote module
|
||||
// This also requires an async boundary (import())
|
||||
|
||||
// At this point shared modules initialized and remote modules are loaded
|
||||
import("./bootstrap");
|
||||
|
||||
// It's possible to place more code here to do stuff on page init
|
||||
// but it can't use any of the shared modules or remote modules.
|
|
@ -0,0 +1,67 @@
|
|||
# webpack.config.js
|
||||
|
||||
```javascript
|
||||
_{{webpack.config.js}}_
|
||||
```
|
||||
|
||||
# src/index.js
|
||||
|
||||
```javascript
|
||||
_{{src/index.js}}_
|
||||
```
|
||||
|
||||
# src/bootstrap.js
|
||||
|
||||
```jsx
|
||||
_{{src/bootstrap.js}}_
|
||||
```
|
||||
|
||||
# src/App.js
|
||||
|
||||
```jsx
|
||||
_{{src/App.js}}_
|
||||
```
|
||||
|
||||
# index.html
|
||||
|
||||
```html
|
||||
_{{index.html}}_
|
||||
```
|
||||
|
||||
# src-b/Component.js
|
||||
|
||||
```jsx
|
||||
_{{src-b/Component.js}}_
|
||||
```
|
||||
|
||||
# dist/aaa/app.js
|
||||
|
||||
```javascript
|
||||
_{{dist/aaa/app.js}}_
|
||||
```
|
||||
|
||||
# dist/bbb/mfeBBB.js
|
||||
|
||||
```javascript
|
||||
_{{dist/bbb/mfeBBB.js}}_
|
||||
```
|
||||
|
||||
# dist/ccc/mfeCCC.js
|
||||
|
||||
```javascript
|
||||
_{{dist/ccc/mfeCCC.js}}_
|
||||
```
|
||||
|
||||
# Info
|
||||
|
||||
## Unoptimized
|
||||
|
||||
```
|
||||
_{{stdout}}_
|
||||
```
|
||||
|
||||
## Production mode
|
||||
|
||||
```
|
||||
_{{production:stdout}}_
|
||||
```
|
|
@ -0,0 +1,151 @@
|
|||
const path = require("path");
|
||||
const { ModuleFederationPlugin } = require("../../").container;
|
||||
const rules = [
|
||||
{
|
||||
test: /\.js$/,
|
||||
include: path.resolve(__dirname, "src"),
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["@babel/react"]
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
const optimization = {
|
||||
chunkIds: "named", // for this example only: readable filenames in production too
|
||||
nodeEnv: "production" // for this example only: always production version of react
|
||||
};
|
||||
const stats = {
|
||||
chunks: true,
|
||||
modules: false,
|
||||
chunkModules: false,
|
||||
chunkRootModules: true,
|
||||
chunkOrigins: true
|
||||
};
|
||||
module.exports = (env = "development") => [
|
||||
// For this example we have 3 configs in a single file
|
||||
// In practice you probably would have separate config
|
||||
// maybe even separate repos for each build.
|
||||
// For Module Federation there is not compile-time dependency
|
||||
// between the builds.
|
||||
// Each one can have different config options.
|
||||
{
|
||||
name: "app",
|
||||
mode: env,
|
||||
entry: {
|
||||
app: "./src/index.js"
|
||||
},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
path: path.resolve(__dirname, "dist/aaa"),
|
||||
publicPath: "dist/aaa/",
|
||||
|
||||
// Each build needs a unique name
|
||||
// to avoid runtime collisions
|
||||
// The default uses "name" from package.json
|
||||
uniqueName: "module-federation-aaa"
|
||||
},
|
||||
module: { rules },
|
||||
optimization,
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
// List of remotes with URLs
|
||||
remotes: {
|
||||
"mfe-b": "mfeBBB@/dist/bbb/mfeBBB.js",
|
||||
"mfe-c": "mfeCCC@/dist/ccc/mfeCCC.js"
|
||||
},
|
||||
|
||||
// list of shared modules with optional options
|
||||
shared: {
|
||||
// specifying a module request as shared module
|
||||
// will provide all used modules matching this name (version from package.json)
|
||||
// and consume shared modules in the version specified in dependencies from package.json
|
||||
// (or in dev/peer/optionalDependencies)
|
||||
// So it use the highest available version of this package matching the version requirement
|
||||
// from package.json, while providing it's own version to others.
|
||||
react: {
|
||||
singleton: true // make sure only a single react module is used
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
stats
|
||||
},
|
||||
{
|
||||
name: "mfe-b",
|
||||
mode: env,
|
||||
entry: {},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
path: path.resolve(__dirname, "dist/bbb"),
|
||||
publicPath: "dist/bbb/",
|
||||
uniqueName: "module-federation-bbb"
|
||||
},
|
||||
module: { rules },
|
||||
optimization,
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
// A unique name
|
||||
name: "mfeBBB",
|
||||
|
||||
// List of exposed modules
|
||||
exposes: {
|
||||
"./Component": "./src-b/Component"
|
||||
},
|
||||
|
||||
// list of shared modules
|
||||
shared: [
|
||||
// date-fns is shared with the other remote, app doesn't know about that
|
||||
"date-fns",
|
||||
{
|
||||
react: {
|
||||
singleton: true // must be specified in each config
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
stats
|
||||
},
|
||||
{
|
||||
name: "mfe-c",
|
||||
mode: env,
|
||||
entry: {},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
path: path.resolve(__dirname, "dist/ccc"),
|
||||
publicPath: "dist/ccc/",
|
||||
uniqueName: "module-federation-ccc"
|
||||
},
|
||||
module: { rules },
|
||||
optimization,
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
name: "mfeCCC",
|
||||
|
||||
exposes: {
|
||||
"./Component": "./src-c/Component",
|
||||
"./Component2": "./src-c/LazyComponent"
|
||||
},
|
||||
|
||||
shared: [
|
||||
// All (used) requests within lodash are shared.
|
||||
"lodash/",
|
||||
"date-fns",
|
||||
{
|
||||
react: {
|
||||
// Do not load our own version.
|
||||
// There must be a valid shared module available at runtime.
|
||||
// This improves build time as this module doesn't need to be compiled,
|
||||
// but it opts-out of possible fallbacks and runtime version upgrade.
|
||||
import: false,
|
||||
singleton: true
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
stats
|
||||
}
|
||||
];
|
|
@ -81,17 +81,6 @@ class BannerPlugin {
|
|||
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
|
||||
},
|
||||
() => {
|
||||
let files;
|
||||
if (options.entryOnly) {
|
||||
files = new Set();
|
||||
for (const chunk of compilation.chunks) {
|
||||
if (chunk.canBeInitial()) {
|
||||
for (const file of chunk.files) files.add(file);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
files = Object.keys(compilation.assets);
|
||||
}
|
||||
for (const chunk of compilation.chunks) {
|
||||
if (options.entryOnly && !chunk.canBeInitial()) {
|
||||
continue;
|
||||
|
@ -102,11 +91,9 @@ class BannerPlugin {
|
|||
continue;
|
||||
}
|
||||
|
||||
let filename = file;
|
||||
|
||||
const data = {
|
||||
chunk,
|
||||
filename
|
||||
filename: file
|
||||
};
|
||||
|
||||
const comment = compilation.getPath(banner, data);
|
||||
|
|
|
@ -77,6 +77,7 @@ const {
|
|||
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
|
||||
/** @typedef {import("./Dependency")} Dependency */
|
||||
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
|
||||
/** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("./DependencyTemplate")} DependencyTemplate */
|
||||
/** @typedef {import("./Module")} Module */
|
||||
/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
|
||||
|
@ -151,7 +152,7 @@ const {
|
|||
/**
|
||||
* @typedef {Object} EntryData
|
||||
* @property {Dependency[]} dependencies dependencies of the entrypoint that should be evaluated at startup
|
||||
* @property {Dependency[]} includeDependencies dependencies of the entrypoint that should be included by not evaluated
|
||||
* @property {Dependency[]} includeDependencies dependencies of the entrypoint that should be included but not evaluated
|
||||
* @property {EntryOptions} options options of the entrypoint
|
||||
*/
|
||||
|
||||
|
@ -357,7 +358,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
|
|||
/** @type {SyncHook<[Dependency, EntryOptions, Module]>} */
|
||||
succeedEntry: new SyncHook(["entry", "options", "module"]),
|
||||
|
||||
/** @type {SyncWaterfallHook<[string[][], Dependency]>} */
|
||||
/** @type {SyncWaterfallHook<[(string[] | ReferencedExport)[], Dependency]>} */
|
||||
dependencyReferencedExports: new SyncWaterfallHook([
|
||||
"referencedExports",
|
||||
"dependency"
|
||||
|
@ -1582,6 +1583,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
|
|||
} else {
|
||||
entryData[target].push(entry);
|
||||
for (const key of Object.keys(options)) {
|
||||
if (options[key] === undefined) continue;
|
||||
if (entryData.options[key] === options[key]) continue;
|
||||
if (entryData.options[key] === undefined) {
|
||||
entryData.options[key] = options[key];
|
||||
|
@ -2250,7 +2252,7 @@ Make sure to select an appropriate stage from Compilation.PROCESS_ASSETS_STAGE_*
|
|||
|
||||
/**
|
||||
* @param {Dependency} dependency the dependency
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getDependencyReferencedExports(dependency) {
|
||||
const referencedExports = dependency.getReferencedExports(this.moduleGraph);
|
||||
|
|
|
@ -137,6 +137,8 @@ class Compiler {
|
|||
compile: new SyncHook(["params"]),
|
||||
/** @type {AsyncParallelHook<[Compilation], Module>} */
|
||||
make: new AsyncParallelHook(["compilation"]),
|
||||
/** @type {AsyncParallelHook<[Compilation], Module>} */
|
||||
finishMake: new AsyncSeriesHook(["compilation"]),
|
||||
/** @type {AsyncSeriesHook<[Compilation]>} */
|
||||
afterCompile: new AsyncSeriesHook(["compilation"]),
|
||||
|
||||
|
@ -913,23 +915,29 @@ class Compiler {
|
|||
logger.timeEnd("make hook");
|
||||
if (err) return callback(err);
|
||||
|
||||
process.nextTick(() => {
|
||||
logger.time("finish compilation");
|
||||
compilation.finish(err => {
|
||||
logger.timeEnd("finish compilation");
|
||||
if (err) return callback(err);
|
||||
logger.time("finish make hook");
|
||||
this.hooks.finishMake.callAsync(compilation, err => {
|
||||
logger.timeEnd("finish make hook");
|
||||
if (err) return callback(err);
|
||||
|
||||
logger.time("seal compilation");
|
||||
compilation.seal(err => {
|
||||
logger.timeEnd("seal compilation");
|
||||
process.nextTick(() => {
|
||||
logger.time("finish compilation");
|
||||
compilation.finish(err => {
|
||||
logger.timeEnd("finish compilation");
|
||||
if (err) return callback(err);
|
||||
|
||||
logger.time("afterCompile hook");
|
||||
this.hooks.afterCompile.callAsync(compilation, err => {
|
||||
logger.timeEnd("afterCompile hook");
|
||||
logger.time("seal compilation");
|
||||
compilation.seal(err => {
|
||||
logger.timeEnd("seal compilation");
|
||||
if (err) return callback(err);
|
||||
|
||||
return callback(null, compilation);
|
||||
logger.time("afterCompile hook");
|
||||
this.hooks.afterCompile.callAsync(compilation, err => {
|
||||
logger.timeEnd("afterCompile hook");
|
||||
if (err) return callback(err);
|
||||
|
||||
return callback(null, compilation);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -52,6 +52,7 @@ const makeSerializable = require("./util/makeSerializable");
|
|||
* @property {RegExp=} include
|
||||
* @property {RegExp=} exclude
|
||||
* @property {RawChunkGroupOptions=} groupOptions
|
||||
* @property {string[][]=} referencedExports exports referenced from modules (won't be mangled)
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -66,13 +67,13 @@ const makeSerializable = require("./util/makeSerializable");
|
|||
/**
|
||||
* @callback ResolveDependenciesCallback
|
||||
* @param {Error=} err
|
||||
* @param {ContextElementDependency[]} dependencies
|
||||
* @param {ContextElementDependency[]=} dependencies
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback ResolveDependencies
|
||||
* @param {TODO} fs
|
||||
* @param {TODO} options
|
||||
* @param {InputFileSystem} fs
|
||||
* @param {ContextModuleOptions} options
|
||||
* @param {ResolveDependenciesCallback} callback
|
||||
*/
|
||||
|
||||
|
@ -116,6 +117,7 @@ class ContextModule extends Module {
|
|||
exclude: options.exclude,
|
||||
chunkName: options.chunkName,
|
||||
groupOptions: options.groupOptions,
|
||||
referencedExports: options.referencedExports,
|
||||
namespaceObject: options.namespaceObject
|
||||
};
|
||||
if (options.resolveOptions !== undefined) {
|
||||
|
@ -183,6 +185,11 @@ class ContextModule extends Module {
|
|||
if (this.options.exclude) {
|
||||
identifier += `|exclude: ${this.options.exclude}`;
|
||||
}
|
||||
if (this.options.referencedExports) {
|
||||
identifier += `|referencedExports: ${JSON.stringify(
|
||||
this.options.referencedExports
|
||||
)}`;
|
||||
}
|
||||
if (this.options.chunkName) {
|
||||
identifier += `|chunkName: ${this.options.chunkName}`;
|
||||
}
|
||||
|
@ -234,6 +241,11 @@ class ContextModule extends Module {
|
|||
if (this.options.exclude) {
|
||||
identifier += ` exclude: ${this.prettyRegExp(this.options.exclude + "")}`;
|
||||
}
|
||||
if (this.options.referencedExports) {
|
||||
identifier += ` referencedExports: ${this.options.referencedExports
|
||||
.map(e => e.join("."))
|
||||
.join(", ")}`;
|
||||
}
|
||||
if (this.options.chunkName) {
|
||||
identifier += ` chunkName: ${this.options.chunkName}`;
|
||||
}
|
||||
|
@ -284,6 +296,11 @@ class ContextModule extends Module {
|
|||
if (this.options.exclude) {
|
||||
identifier += ` exclude: ${this.prettyRegExp(this.options.exclude + "")}`;
|
||||
}
|
||||
if (this.options.referencedExports) {
|
||||
identifier += ` referencedExports: ${this.options.referencedExports
|
||||
.map(e => e.join("."))
|
||||
.join(", ")}`;
|
||||
}
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
|
|
@ -13,11 +13,14 @@ const ContextElementDependency = require("./dependencies/ContextElementDependenc
|
|||
const { cachedSetProperty } = require("./util/cleverMerge");
|
||||
const { join } = require("./util/fs");
|
||||
|
||||
/** @typedef {import("./ContextModule").ContextModuleOptions} ContextModuleOptions */
|
||||
/** @typedef {import("./ContextModule").ResolveDependenciesCallback} ResolveDependenciesCallback */
|
||||
/** @typedef {import("./Module")} Module */
|
||||
/** @typedef {import("./ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
|
||||
/** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
|
||||
/** @typedef {import("./ResolverFactory")} ResolverFactory */
|
||||
/** @typedef {import("./dependencies/ContextDependency")} ContextDependency */
|
||||
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
|
||||
|
||||
const EMPTY_RESOLVE_OPTIONS = {};
|
||||
|
||||
|
@ -218,14 +221,23 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {InputFileSystem} fs file system
|
||||
* @param {ContextModuleOptions} options options
|
||||
* @param {ResolveDependenciesCallback} callback callback function
|
||||
* @returns {void}
|
||||
*/
|
||||
resolveDependencies(fs, options, callback) {
|
||||
const cmf = this;
|
||||
let resource = options.resource;
|
||||
let resourceQuery = options.resourceQuery;
|
||||
let recursive = options.recursive;
|
||||
let regExp = options.regExp;
|
||||
let include = options.include;
|
||||
let exclude = options.exclude;
|
||||
const {
|
||||
resource,
|
||||
resourceQuery,
|
||||
recursive,
|
||||
regExp,
|
||||
include,
|
||||
exclude,
|
||||
referencedExports
|
||||
} = options;
|
||||
if (!regExp || !resource) return callback(null, []);
|
||||
|
||||
const addDirectory = (directory, callback) => {
|
||||
|
@ -274,7 +286,8 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
|
|||
.map(obj => {
|
||||
const dep = new ContextElementDependency(
|
||||
obj.request + resourceQuery,
|
||||
obj.request
|
||||
obj.request,
|
||||
referencedExports
|
||||
);
|
||||
dep.optional = true;
|
||||
return dep;
|
||||
|
|
|
@ -10,6 +10,7 @@ const Module = require("./Module");
|
|||
const RuntimeGlobals = require("./RuntimeGlobals");
|
||||
const DelegatedSourceDependency = require("./dependencies/DelegatedSourceDependency");
|
||||
const StaticExportsDependency = require("./dependencies/StaticExportsDependency");
|
||||
const makeSerializable = require("./util/makeSerializable");
|
||||
|
||||
/** @typedef {import("webpack-sources").Source} Source */
|
||||
/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
|
||||
|
@ -182,6 +183,48 @@ class DelegatedModule extends Module {
|
|||
hash.update(JSON.stringify(this.request));
|
||||
super.updateHash(hash, chunkGraph);
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
const { write } = context;
|
||||
// constructor
|
||||
write(this.sourceRequest);
|
||||
write(this.delegateData);
|
||||
write(this.delegationType);
|
||||
write(this.userRequest);
|
||||
write(this.originalRequest);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
static deserialize(context) {
|
||||
const { read } = context;
|
||||
const obj = new DelegatedModule(
|
||||
read(), // sourceRequest
|
||||
read(), // delegateData
|
||||
read(), // delegationType
|
||||
read(), // userRequest
|
||||
read() // originalRequest
|
||||
);
|
||||
obj.deserialize(context);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming this module is in the cache. Update the (cached) module with
|
||||
* the fresh module from the factory. Usually updates internal references
|
||||
* and properties.
|
||||
* @param {Module} module fresh module
|
||||
* @returns {void}
|
||||
*/
|
||||
updateCacheModule(module) {
|
||||
super.updateCacheModule(module);
|
||||
const m = /** @type {DelegatedModule} */ (module);
|
||||
this.delegationType = m.delegationType;
|
||||
this.userRequest = m.userRequest;
|
||||
this.originalRequest = m.originalRequest;
|
||||
this.delegateData = m.delegateData;
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(DelegatedModule, "webpack/lib/DelegatedModule");
|
||||
|
||||
module.exports = DelegatedModule;
|
||||
|
|
|
@ -52,6 +52,12 @@
|
|||
* @property {Module[]=} dependencies module on which the result depends on
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ReferencedExport
|
||||
* @property {string[]} name name of the referenced export
|
||||
* @property {boolean=} canMangle when false, referenced export can not be mangled, defaults to true
|
||||
*/
|
||||
|
||||
class Dependency {
|
||||
constructor() {
|
||||
// TODO check if this can be moved into ModuleDependency
|
||||
|
@ -100,7 +106,7 @@ class Dependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return Dependency.EXPORTS_OBJECT_REFERENCED;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
const { RawSource } = require("webpack-sources");
|
||||
const Module = require("./Module");
|
||||
const RuntimeGlobals = require("./RuntimeGlobals");
|
||||
const makeSerializable = require("./util/makeSerializable");
|
||||
|
||||
/** @typedef {import("webpack-sources").Source} Source */
|
||||
/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
|
||||
|
@ -119,6 +120,30 @@ class DllModule extends Module {
|
|||
hash.update(this.name || "");
|
||||
super.updateHash(hash, chunkGraph);
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
context.write(this.name);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
this.name = context.read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming this module is in the cache. Update the (cached) module with
|
||||
* the fresh module from the factory. Usually updates internal references
|
||||
* and properties.
|
||||
* @param {Module} module fresh module
|
||||
* @returns {void}
|
||||
*/
|
||||
updateCacheModule(module) {
|
||||
super.updateCacheModule(module);
|
||||
this.dependencies = module.dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(DllModule, "webpack/lib/DllModule");
|
||||
|
||||
module.exports = DllModule;
|
||||
|
|
|
@ -86,6 +86,42 @@ const getSourceForImportExternal = (moduleAndSpecifiers, runtimeTemplate) => {
|
|||
)});`;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string|string[]} urlAndGlobal the script request
|
||||
* @param {RuntimeTemplate} runtimeTemplate the runtime template
|
||||
* @returns {string} the generated source
|
||||
*/
|
||||
const getSourceForScriptExternal = (urlAndGlobal, runtimeTemplate) => {
|
||||
if (typeof urlAndGlobal === "string") {
|
||||
urlAndGlobal = urlAndGlobal.split("@").reverse();
|
||||
}
|
||||
const url = urlAndGlobal[0];
|
||||
const globalName = urlAndGlobal[1];
|
||||
return Template.asString([
|
||||
"var error = new Error();",
|
||||
`module.exports = new Promise(${runtimeTemplate.basicFunction(
|
||||
"resolve, reject",
|
||||
[
|
||||
`if(typeof ${globalName} !== "undefined") return resolve();`,
|
||||
`${RuntimeGlobals.loadScript}(${JSON.stringify(
|
||||
url
|
||||
)}, ${runtimeTemplate.basicFunction("event", [
|
||||
`if(typeof ${globalName} !== "undefined") return resolve();`,
|
||||
"var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
|
||||
"var realSrc = event && event.target && event.target.src;",
|
||||
"error.message = 'Loading script failed.\\n(' + errorType + ': ' + realSrc + ')';",
|
||||
"error.name = 'ScriptExternalLoadError';",
|
||||
"error.type = errorType;",
|
||||
"error.request = realSrc;",
|
||||
"reject(error);"
|
||||
])}, ${JSON.stringify(globalName)});`
|
||||
]
|
||||
)}).then(${runtimeTemplate.returningFunction(
|
||||
`${globalName}${propertyAccess(urlAndGlobal, 2)}`
|
||||
)})`
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} variableName the variable name to check
|
||||
* @param {string} request the request path
|
||||
|
@ -146,6 +182,10 @@ const getSourceForDefaultCase = (optional, request, runtimeTemplate) => {
|
|||
|
||||
const TYPES = new Set(["javascript"]);
|
||||
const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]);
|
||||
const RUNTIME_REQUIREMENTS_FOR_SCRIPT = new Set([
|
||||
RuntimeGlobals.module,
|
||||
RuntimeGlobals.loadScript
|
||||
]);
|
||||
|
||||
class ExternalModule extends Module {
|
||||
constructor(request, type, userRequest) {
|
||||
|
@ -239,6 +279,9 @@ class ExternalModule extends Module {
|
|||
if (!Array.isArray(this.request) || this.request.length === 1)
|
||||
this.buildMeta.exportsType = "namespace";
|
||||
break;
|
||||
case "script":
|
||||
this.buildMeta.async = true;
|
||||
break;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
@ -276,6 +319,8 @@ class ExternalModule extends Module {
|
|||
);
|
||||
case "import":
|
||||
return getSourceForImportExternal(request, runtimeTemplate);
|
||||
case "script":
|
||||
return getSourceForScriptExternal(request, runtimeTemplate);
|
||||
case "module":
|
||||
throw new Error("Module external type is not implemented yet");
|
||||
case "var":
|
||||
|
@ -313,7 +358,13 @@ class ExternalModule extends Module {
|
|||
sources.set("javascript", new RawSource(sourceString));
|
||||
}
|
||||
|
||||
return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS };
|
||||
return {
|
||||
sources,
|
||||
runtimeRequirements:
|
||||
this.externalType === "script"
|
||||
? RUNTIME_REQUIREMENTS_FOR_SCRIPT
|
||||
: RUNTIME_REQUIREMENTS
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,7 @@ const Queue = require("./util/Queue");
|
|||
/** @typedef {import("./Compiler")} Compiler */
|
||||
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
|
||||
/** @typedef {import("./Dependency")} Dependency */
|
||||
/** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("./Module")} Module */
|
||||
/** @typedef {import("./ModuleGraph").ExportsInfo} ExportsInfo */
|
||||
|
||||
|
@ -39,15 +40,22 @@ class FlagDependencyUsagePlugin {
|
|||
const exportInfoToModuleMap = new Map();
|
||||
|
||||
/**
|
||||
* @typedef {string[]} StringArray
|
||||
* @param {Module} module module to process
|
||||
* @param {StringArray[]} usedExports list of used exports
|
||||
* @param {(string[] | ReferencedExport)[]} usedExports list of used exports
|
||||
* @returns {void}
|
||||
*/
|
||||
const processModule = (module, usedExports) => {
|
||||
const exportsInfo = moduleGraph.getExportsInfo(module);
|
||||
if (usedExports.length > 0) {
|
||||
for (let usedExport of usedExports) {
|
||||
for (const usedExportInfo of usedExports) {
|
||||
let usedExport;
|
||||
let canMangle = true;
|
||||
if (Array.isArray(usedExportInfo)) {
|
||||
usedExport = usedExportInfo;
|
||||
} else {
|
||||
usedExport = usedExportInfo.name;
|
||||
canMangle = usedExportInfo.canMangle !== false;
|
||||
}
|
||||
if (usedExport.length === 0) {
|
||||
if (exportsInfo.setUsedInUnknownWay()) {
|
||||
queue.enqueue(module);
|
||||
|
@ -55,10 +63,12 @@ class FlagDependencyUsagePlugin {
|
|||
} else {
|
||||
let currentExportsInfo = exportsInfo;
|
||||
for (let i = 0; i < usedExport.length; i++) {
|
||||
const exportName = usedExport[i];
|
||||
const exportInfo = currentExportsInfo.getExportInfo(
|
||||
exportName
|
||||
usedExport[i]
|
||||
);
|
||||
if (canMangle === false) {
|
||||
exportInfo.canMangleUse = false;
|
||||
}
|
||||
const lastOne = i === usedExport.length - 1;
|
||||
if (!lastOne) {
|
||||
const nestedInfo = exportInfo.getNestedExportsInfo();
|
||||
|
|
|
@ -147,8 +147,8 @@ class NormalModuleFactory extends ModuleFactory {
|
|||
beforeResolve: new AsyncSeriesBailHook(["resolveData"]),
|
||||
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
|
||||
afterResolve: new AsyncSeriesBailHook(["resolveData"]),
|
||||
/** @type {SyncBailHook<[ResolveData], TODO>} */
|
||||
createModule: new SyncBailHook(["resolveData"]),
|
||||
/** @type {AsyncSeriesBailHook<[ResolveData["createData"], ResolveData], TODO>} */
|
||||
createModule: new AsyncSeriesBailHook(["createData", "resolveData"]),
|
||||
/** @type {SyncWaterfallHook<[Module, ResolveData["createData"], ResolveData], TODO>} */
|
||||
module: new SyncWaterfallHook(["module", "createData", "resolveData"]),
|
||||
createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
|
||||
|
@ -216,22 +216,27 @@ class NormalModuleFactory extends ModuleFactory {
|
|||
|
||||
const createData = resolveData.createData;
|
||||
|
||||
let createdModule = this.hooks.createModule.call(createData);
|
||||
if (!createdModule) {
|
||||
if (!resolveData.request) {
|
||||
return callback(new Error("Empty dependency (no request)"));
|
||||
}
|
||||
|
||||
createdModule = new NormalModule(createData);
|
||||
}
|
||||
|
||||
createdModule = this.hooks.module.call(
|
||||
createdModule,
|
||||
this.hooks.createModule.callAsync(
|
||||
createData,
|
||||
resolveData
|
||||
);
|
||||
resolveData,
|
||||
(err, createdModule) => {
|
||||
if (!createdModule) {
|
||||
if (!resolveData.request) {
|
||||
return callback(new Error("Empty dependency (no request)"));
|
||||
}
|
||||
|
||||
return callback(null, createdModule);
|
||||
createdModule = new NormalModule(createData);
|
||||
}
|
||||
|
||||
createdModule = this.hooks.module.call(
|
||||
createdModule,
|
||||
createData,
|
||||
resolveData
|
||||
);
|
||||
|
||||
return callback(null, createdModule);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ class ProgressPlugin {
|
|||
);
|
||||
}
|
||||
|
||||
const percentage = 0.1 + percentageFactor * 0.6;
|
||||
const percentage = 0.1 + percentageFactor * 0.55;
|
||||
|
||||
if (showEntries) {
|
||||
items.push(`${doneEntries}/${entriesCount} entries`);
|
||||
|
@ -440,7 +440,7 @@ class ProgressPlugin {
|
|||
handler(0.1, "building");
|
||||
},
|
||||
done() {
|
||||
handler(0.7, "building");
|
||||
handler(0.65, "building");
|
||||
}
|
||||
});
|
||||
const interceptHook = (hook, progress, name) => {
|
||||
|
@ -484,6 +484,7 @@ class ProgressPlugin {
|
|||
interceptHook(compiler.hooks.compile, 0.04, "compile");
|
||||
interceptHook(compiler.hooks.thisCompilation, 0.05, "setup compilation");
|
||||
interceptHook(compiler.hooks.compilation, 0.06, "setup compilation");
|
||||
interceptHook(compiler.hooks.finishMake, 0.69, "finish building");
|
||||
interceptHook(compiler.hooks.emit, 0.95, "emitting");
|
||||
interceptHook(compiler.hooks.afterEmit, 0.98, "after emitting");
|
||||
interceptHook(compiler.hooks.done, 0.99, "done");
|
||||
|
|
|
@ -160,6 +160,14 @@ exports.uncaughtErrorHandler = "__webpack_require__.oe";
|
|||
*/
|
||||
exports.scriptNonce = "__webpack_require__.nc";
|
||||
|
||||
/**
|
||||
* function to load a script tag.
|
||||
* Arguments: (url: string, done: (event) => void), key?: 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.
|
||||
*/
|
||||
exports.loadScript = "__webpack_require__.l";
|
||||
|
||||
/**
|
||||
* the chunk name of the chunk with the runtime
|
||||
*/
|
||||
|
|
|
@ -17,6 +17,7 @@ const GetChunkFilenameRuntimeModule = require("./runtime/GetChunkFilenameRuntime
|
|||
const GetMainFilenameRuntimeModule = require("./runtime/GetMainFilenameRuntimeModule");
|
||||
const GlobalRuntimeModule = require("./runtime/GlobalRuntimeModule");
|
||||
const HasOwnPropertyRuntimeModule = require("./runtime/HasOwnPropertyRuntimeModule");
|
||||
const LoadScriptRuntimeModule = require("./runtime/LoadScriptRuntimeModule");
|
||||
const MakeNamespaceObjectRuntimeModule = require("./runtime/MakeNamespaceObjectRuntimeModule");
|
||||
const PublicPathRuntimeModule = require("./runtime/PublicPathRuntimeModule");
|
||||
const SystemContextRuntimeModule = require("./runtime/SystemContextRuntimeModule");
|
||||
|
@ -46,7 +47,8 @@ const GLOBALS_ON_REQUIRE = [
|
|||
RuntimeGlobals.wasmInstances,
|
||||
RuntimeGlobals.instantiateWasm,
|
||||
RuntimeGlobals.shareScopeMap,
|
||||
RuntimeGlobals.initializeSharing
|
||||
RuntimeGlobals.initializeSharing,
|
||||
RuntimeGlobals.loadScript
|
||||
];
|
||||
|
||||
const MODULE_DEPENDENCIES = {
|
||||
|
@ -265,6 +267,12 @@ class RuntimePlugin {
|
|||
compilation.addRuntimeModule(chunk, new ShareRuntimeModule());
|
||||
return true;
|
||||
});
|
||||
compilation.hooks.runtimeRequirementInTree
|
||||
.for(RuntimeGlobals.loadScript)
|
||||
.tap("RuntimePlugin", (chunk, set) => {
|
||||
compilation.addRuntimeModule(chunk, new LoadScriptRuntimeModule());
|
||||
return true;
|
||||
});
|
||||
// TODO webpack 6: remove CompatRuntimePlugin
|
||||
compilation.hooks.additionalTreeRuntimeRequirements.tap(
|
||||
"RuntimePlugin",
|
||||
|
|
|
@ -19,7 +19,7 @@ class Stats {
|
|||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} true if the compilation encountered an error
|
||||
* @returns {boolean} true if the compilation had a warning
|
||||
*/
|
||||
hasWarnings() {
|
||||
return (
|
||||
|
@ -29,7 +29,7 @@ class Stats {
|
|||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} true if the compilation had a warning
|
||||
* @returns {boolean} true if the compilation encountered an error
|
||||
*/
|
||||
hasErrors() {
|
||||
return (
|
||||
|
|
|
@ -82,11 +82,14 @@ const applyWebpackOptionsDefaults = options => {
|
|||
const development = mode === "development";
|
||||
const production = mode === "production" || !mode;
|
||||
|
||||
if (
|
||||
typeof options.entry !== "function" &&
|
||||
Object.keys(options.entry).length === 0
|
||||
) {
|
||||
options.entry.main = { import: ["./src"] };
|
||||
if (typeof options.entry !== "function") {
|
||||
for (const key of Object.keys(options.entry)) {
|
||||
F(
|
||||
options.entry[key],
|
||||
"import",
|
||||
() => /** @type {[string]} */ (["./src"])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
F(options, "devtool", () => (development ? "eval" : false));
|
||||
|
|
|
@ -129,13 +129,15 @@ const getNormalizedWebpackOptions = config => {
|
|||
...devServer
|
||||
})),
|
||||
devtool: config.devtool,
|
||||
entry: nestedConfig(config.entry, entry => {
|
||||
if (typeof entry === "function") {
|
||||
return () =>
|
||||
Promise.resolve().then(entry).then(getNormalizedEntryStatic);
|
||||
}
|
||||
return getNormalizedEntryStatic(entry);
|
||||
}),
|
||||
entry:
|
||||
config.entry === undefined
|
||||
? { main: {} }
|
||||
: typeof config.entry === "function"
|
||||
? (fn => () =>
|
||||
Promise.resolve().then(fn).then(getNormalizedEntryStatic))(
|
||||
config.entry
|
||||
)
|
||||
: getNormalizedEntryStatic(config.entry),
|
||||
experiments: nestedConfig(config.experiments, experiments => ({
|
||||
...experiments
|
||||
})),
|
||||
|
|
|
@ -32,13 +32,14 @@ class ModuleFederationPlugin {
|
|||
*/
|
||||
apply(compiler) {
|
||||
const { _options: options } = this;
|
||||
const library = options.library || { type: "var", name: options.name };
|
||||
const remoteType =
|
||||
options.remoteType || (options.library ? options.library.type : "script");
|
||||
if (
|
||||
options.library &&
|
||||
!compiler.options.output.enabledLibraryTypes.includes(
|
||||
options.library.type
|
||||
)
|
||||
library &&
|
||||
!compiler.options.output.enabledLibraryTypes.includes(library.type)
|
||||
) {
|
||||
compiler.options.output.enabledLibraryTypes.push(options.library.type);
|
||||
compiler.options.output.enabledLibraryTypes.push(library.type);
|
||||
}
|
||||
compiler.hooks.afterPlugins.tap("ModuleFederationPlugin", () => {
|
||||
if (
|
||||
|
@ -49,7 +50,7 @@ class ModuleFederationPlugin {
|
|||
) {
|
||||
new ContainerPlugin({
|
||||
name: options.name,
|
||||
library: options.library || compiler.options.output.library,
|
||||
library,
|
||||
filename: options.filename,
|
||||
exposes: options.exposes
|
||||
}).apply(compiler);
|
||||
|
@ -61,10 +62,7 @@ class ModuleFederationPlugin {
|
|||
: Object.keys(options.remotes).length > 0)
|
||||
) {
|
||||
new ContainerReferencePlugin({
|
||||
remoteType:
|
||||
options.remoteType ||
|
||||
(options.library && options.library.type) ||
|
||||
compiler.options.externalsType,
|
||||
remoteType,
|
||||
remotes: options.remotes
|
||||
}).apply(compiler);
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ const createTrace = (fs, outputPath) => {
|
|||
profiler,
|
||||
end: callback => {
|
||||
// Wait until the write stream finishes.
|
||||
fsStream.on("finish", () => {
|
||||
fsStream.on("close", () => {
|
||||
callback();
|
||||
});
|
||||
// Tear down the readable trace stream.
|
||||
|
|
|
@ -10,6 +10,7 @@ const ModuleDependency = require("./ModuleDependency");
|
|||
|
||||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
|
@ -30,7 +31,7 @@ class CommonJsFullRequireDependency extends ModuleDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
if (this.call) {
|
||||
|
|
|
@ -14,6 +14,7 @@ const NullDependency = require("./NullDependency");
|
|||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
|
@ -39,7 +40,7 @@ class CommonJsSelfReferenceDependency extends NullDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return [this.names];
|
||||
|
|
|
@ -5,12 +5,17 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Dependency = require("../Dependency");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
class ContextElementDependency extends ModuleDependency {
|
||||
constructor(request, userRequest) {
|
||||
constructor(request, userRequest, referencedExports) {
|
||||
super(request);
|
||||
this.referencedExports = referencedExports;
|
||||
|
||||
if (userRequest) {
|
||||
this.userRequest = userRequest;
|
||||
|
@ -20,6 +25,30 @@ class ContextElementDependency extends ModuleDependency {
|
|||
get type() {
|
||||
return "context element";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return this.referencedExports
|
||||
? this.referencedExports.map(e => ({
|
||||
name: e,
|
||||
canMangle: false
|
||||
}))
|
||||
: Dependency.EXPORTS_OBJECT_REFERENCED;
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
context.write(this.referencedExports);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
this.referencedExports = context.read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
class DelegatedSourceDependency extends ModuleDependency {
|
||||
|
@ -17,4 +18,9 @@ class DelegatedSourceDependency extends ModuleDependency {
|
|||
}
|
||||
}
|
||||
|
||||
makeSerializable(
|
||||
DelegatedSourceDependency,
|
||||
"webpack/lib/dependencies/DelegatedSourceDependency"
|
||||
);
|
||||
|
||||
module.exports = DelegatedSourceDependency;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
class EntryDependency extends ModuleDependency {
|
||||
|
@ -20,4 +21,6 @@ class EntryDependency extends ModuleDependency {
|
|||
}
|
||||
}
|
||||
|
||||
makeSerializable(EntryDependency, "webpack/lib/dependencies/EntryDependency");
|
||||
|
||||
module.exports = EntryDependency;
|
||||
|
|
|
@ -19,6 +19,7 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
|
|||
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../Module")} Module */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
@ -395,7 +396,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
const mode = this.getMode(moduleGraph, false);
|
||||
|
|
|
@ -16,6 +16,7 @@ const ModuleDependency = require("./ModuleDependency");
|
|||
/** @typedef {import("webpack-sources").Source} Source */
|
||||
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../Module")} Module */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
@ -42,7 +43,7 @@ class HarmonyImportDependency extends ModuleDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return Dependency.NO_EXPORTS_REFERENCED;
|
||||
|
|
|
@ -14,6 +14,7 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
|
|||
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
/** @typedef {import("../WebpackError")} WebpackError */
|
||||
|
@ -99,7 +100,7 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
let ids = this.getIds(moduleGraph);
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
|
||||
/** @typedef {import("../ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
|
||||
/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
|
||||
|
||||
class ImportDependenciesBlock extends AsyncDependenciesBlock {
|
||||
/**
|
||||
* @param {ChunkGroupOptions} groupOptions options for the chunk group
|
||||
* @param {DependencyLocation} loc location info
|
||||
* @param {string} request request string for the block
|
||||
* @param {[number, number]} range position of the block
|
||||
*/
|
||||
constructor(groupOptions, loc, request, range) {
|
||||
super(groupOptions, loc, request);
|
||||
/** @type {[number, number]} */
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
const { write } = context;
|
||||
write(this.range);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
const { read } = context;
|
||||
this.range = read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(
|
||||
ImportDependenciesBlock,
|
||||
"webpack/lib/dependencies/ImportDependenciesBlock"
|
||||
);
|
||||
|
||||
module.exports = ImportDependenciesBlock;
|
|
@ -5,17 +5,27 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Dependency = require("../Dependency");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../AsyncDependenciesBlock")} AsyncDependenciesBlock */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../dependencies/ImportDependenciesBlock")} ImportDependenciesBlock */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
class ImportDependency extends ModuleDependency {
|
||||
constructor(request) {
|
||||
/**
|
||||
* @param {string} request the request
|
||||
* @param {[number, number]} range expression range
|
||||
* @param {string[][]=} referencedExports list of referenced exports
|
||||
*/
|
||||
constructor(request, range, referencedExports) {
|
||||
super(request);
|
||||
this.range = range;
|
||||
this.referencedExports = referencedExports;
|
||||
}
|
||||
|
||||
get type() {
|
||||
|
@ -25,6 +35,32 @@ class ImportDependency extends ModuleDependency {
|
|||
get category() {
|
||||
return "esm";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return this.referencedExports
|
||||
? this.referencedExports.map(e => ({
|
||||
name: e,
|
||||
canMangle: false
|
||||
}))
|
||||
: Dependency.EXPORTS_OBJECT_REFERENCED;
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
context.write(this.range);
|
||||
context.write(this.referencedExports);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
this.range = context.read();
|
||||
this.referencedExports = context.read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(ImportDependency, "webpack/lib/dependencies/ImportDependency");
|
||||
|
@ -42,7 +78,7 @@ ImportDependency.Template = class ImportDependencyTemplate extends ModuleDepende
|
|||
{ runtimeTemplate, module, moduleGraph, chunkGraph, runtimeRequirements }
|
||||
) {
|
||||
const dep = /** @type {ImportDependency} */ (dependency);
|
||||
const block = /** @type {ImportDependenciesBlock} */ (moduleGraph.getParentBlock(
|
||||
const block = /** @type {AsyncDependenciesBlock} */ (moduleGraph.getParentBlock(
|
||||
dep
|
||||
));
|
||||
const content = runtimeTemplate.moduleNamespacePromise({
|
||||
|
@ -55,7 +91,7 @@ ImportDependency.Template = class ImportDependencyTemplate extends ModuleDepende
|
|||
runtimeRequirements
|
||||
});
|
||||
|
||||
source.replace(block.range[0], block.range[1] - 1, content);
|
||||
source.replace(dep.range[0], dep.range[1] - 1, content);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -6,17 +6,22 @@
|
|||
"use strict";
|
||||
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const ModuleDependency = require("./ModuleDependency");
|
||||
const ImportDependency = require("./ImportDependency");
|
||||
|
||||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
class ImportEagerDependency extends ModuleDependency {
|
||||
constructor(request, range) {
|
||||
super(request);
|
||||
|
||||
this.range = range;
|
||||
class ImportEagerDependency extends ImportDependency {
|
||||
/**
|
||||
* @param {string} request the request
|
||||
* @param {[number, number]} range expression range
|
||||
* @param {string[][]=} referencedExports list of referenced exports
|
||||
*/
|
||||
constructor(request, range, referencedExports) {
|
||||
super(request, range, referencedExports);
|
||||
}
|
||||
|
||||
get type() {
|
||||
|
@ -33,7 +38,7 @@ makeSerializable(
|
|||
"webpack/lib/dependencies/ImportEagerDependency"
|
||||
);
|
||||
|
||||
ImportEagerDependency.Template = class ImportEagerDependencyTemplate extends ModuleDependency.Template {
|
||||
ImportEagerDependency.Template = class ImportEagerDependencyTemplate extends ImportDependency.Template {
|
||||
/**
|
||||
* @param {Dependency} dependency the dependency for which the template should be applied
|
||||
* @param {ReplaceSource} source the current replace source which can be modified
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
|
||||
const CommentCompilationWarning = require("../CommentCompilationWarning");
|
||||
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
|
||||
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
|
||||
const ImportContextDependency = require("./ImportContextDependency");
|
||||
const ImportDependenciesBlock = require("./ImportDependenciesBlock");
|
||||
const ImportDependency = require("./ImportDependency");
|
||||
const ImportEagerDependency = require("./ImportEagerDependency");
|
||||
const ImportWeakDependency = require("./ImportWeakDependency");
|
||||
|
@ -31,6 +31,8 @@ class ImportParserPlugin {
|
|||
let mode = "lazy";
|
||||
let include = null;
|
||||
let exclude = null;
|
||||
/** @type {string[][] | null} */
|
||||
let exports = null;
|
||||
/** @type {RawChunkGroupOptions} */
|
||||
const groupOptions = {};
|
||||
|
||||
|
@ -149,6 +151,30 @@ class ImportParserPlugin {
|
|||
exclude = new RegExp(importOptions.webpackExclude);
|
||||
}
|
||||
}
|
||||
if (importOptions.webpackExports !== undefined) {
|
||||
if (
|
||||
!(
|
||||
typeof importOptions.webpackExports === "string" ||
|
||||
(Array.isArray(importOptions.webpackExports) &&
|
||||
importOptions.webpackExports.every(
|
||||
item => typeof item === "string"
|
||||
))
|
||||
)
|
||||
) {
|
||||
parser.state.module.addWarning(
|
||||
new UnsupportedFeatureWarning(
|
||||
`\`webpackExports\` expected a string or an array of strings, but received: ${importOptions.webpackExports}.`,
|
||||
expr.loc
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (typeof importOptions.webpackExports === "string") {
|
||||
exports = [[importOptions.webpackExports]];
|
||||
} else {
|
||||
exports = Array.from(importOptions.webpackExports, e => [e]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (param.isString()) {
|
||||
|
@ -162,22 +188,29 @@ class ImportParserPlugin {
|
|||
}
|
||||
|
||||
if (mode === "eager") {
|
||||
const dep = new ImportEagerDependency(param.string, expr.range);
|
||||
const dep = new ImportEagerDependency(
|
||||
param.string,
|
||||
expr.range,
|
||||
exports
|
||||
);
|
||||
parser.state.current.addDependency(dep);
|
||||
} else if (mode === "weak") {
|
||||
const dep = new ImportWeakDependency(param.string, expr.range);
|
||||
const dep = new ImportWeakDependency(
|
||||
param.string,
|
||||
expr.range,
|
||||
exports
|
||||
);
|
||||
parser.state.current.addDependency(dep);
|
||||
} else {
|
||||
const depBlock = new ImportDependenciesBlock(
|
||||
const depBlock = new AsyncDependenciesBlock(
|
||||
{
|
||||
...groupOptions,
|
||||
name: chunkName
|
||||
},
|
||||
expr.loc,
|
||||
param.string,
|
||||
expr.range
|
||||
param.string
|
||||
);
|
||||
const dep = new ImportDependency(param.string);
|
||||
const dep = new ImportDependency(param.string, expr.range, exports);
|
||||
dep.loc = expr.loc;
|
||||
depBlock.addDependency(dep);
|
||||
parser.state.current.addBlock(depBlock);
|
||||
|
@ -216,7 +249,8 @@ class ImportParserPlugin {
|
|||
mode,
|
||||
namespaceObject: parser.state.module.buildMeta.strictHarmonyModule
|
||||
? "strict"
|
||||
: true
|
||||
: true,
|
||||
referencedExports: exports
|
||||
},
|
||||
parser
|
||||
);
|
||||
|
|
|
@ -6,17 +6,22 @@
|
|||
"use strict";
|
||||
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const ModuleDependency = require("./ModuleDependency");
|
||||
const ImportDependency = require("./ImportDependency");
|
||||
|
||||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
class ImportWeakDependency extends ModuleDependency {
|
||||
constructor(request, range) {
|
||||
super(request);
|
||||
|
||||
this.range = range;
|
||||
class ImportWeakDependency extends ImportDependency {
|
||||
/**
|
||||
* @param {string} request the request
|
||||
* @param {[number, number]} range expression range
|
||||
* @param {string[][]=} referencedExports list of referenced exports
|
||||
*/
|
||||
constructor(request, range, referencedExports) {
|
||||
super(request, range, referencedExports);
|
||||
this.weak = true;
|
||||
}
|
||||
|
||||
|
@ -30,7 +35,7 @@ makeSerializable(
|
|||
"webpack/lib/dependencies/ImportWeakDependency"
|
||||
);
|
||||
|
||||
ImportWeakDependency.Template = class ImportDependencyTemplate extends ModuleDependency.Template {
|
||||
ImportWeakDependency.Template = class ImportDependencyTemplate extends ImportDependency.Template {
|
||||
/**
|
||||
* @param {Dependency} dependency the dependency for which the template should be applied
|
||||
* @param {ReplaceSource} source the current replace source which can be modified
|
||||
|
|
|
@ -14,6 +14,7 @@ const NullDependency = require("./NullDependency");
|
|||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
@ -47,7 +48,7 @@ class ModuleDecoratorDependency extends NullDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return this.allowExportsAccess
|
||||
|
|
|
@ -12,6 +12,7 @@ const ModuleDependency = require("./ModuleDependency");
|
|||
|
||||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
|
@ -25,7 +26,7 @@ class RequireIncludeDependency extends ModuleDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
// This doesn't use any export
|
||||
|
|
|
@ -10,6 +10,7 @@ const makeSerializable = require("../util/makeSerializable");
|
|||
const ModuleDependency = require("./ModuleDependency");
|
||||
const ModuleDependencyAsId = require("./ModuleDependencyTemplateAsId");
|
||||
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
class RequireResolveDependency extends ModuleDependency {
|
||||
|
@ -30,7 +31,7 @@ class RequireResolveDependency extends ModuleDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
// This doesn't use any export
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
const makeSerializable = require("../util/makeSerializable");
|
||||
const ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
|
||||
class WebAssemblyExportImportedDependency extends ModuleDependency {
|
||||
|
@ -24,7 +25,7 @@ class WebAssemblyExportImportedDependency extends ModuleDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return [[this.name]];
|
||||
|
|
|
@ -10,6 +10,7 @@ const UnsupportedWebAssemblyFeatureError = require("../wasm/UnsupportedWebAssemb
|
|||
const ModuleDependency = require("./ModuleDependency");
|
||||
|
||||
/** @typedef {import("@webassemblyjs/ast").ModuleImportDescription} ModuleImportDescription */
|
||||
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
||||
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
||||
/** @typedef {import("../WebpackError")} WebpackError */
|
||||
|
||||
|
@ -33,7 +34,7 @@ class WebAssemblyImportDependency extends ModuleDependency {
|
|||
/**
|
||||
* Returns list of exports referenced by this dependency
|
||||
* @param {ModuleGraph} moduleGraph module graph
|
||||
* @returns {string[][]} referenced exports
|
||||
* @returns {(string[] | ReferencedExport)[]} referenced exports
|
||||
*/
|
||||
getReferencedExports(moduleGraph) {
|
||||
return [[this.name]];
|
||||
|
|
|
@ -32,7 +32,7 @@ const makeSerializable = require("../util/makeSerializable");
|
|||
const propertyAccess = require("../util/propertyAccess");
|
||||
|
||||
/** @typedef {import("webpack-sources").Source} Source */
|
||||
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
|
||||
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
|
||||
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
||||
/** @typedef {import("../Compilation")} Compilation */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
|
|
|
@ -485,7 +485,9 @@ class ModuleConcatenationPlugin {
|
|||
const importedNames = compilation.getDependencyReferencedExports(dep);
|
||||
|
||||
if (
|
||||
importedNames.every(i => i.length > 0) ||
|
||||
importedNames.every(i =>
|
||||
Array.isArray(i) ? i.length > 0 : i.name.length > 0
|
||||
) ||
|
||||
Array.isArray(moduleGraph.getProvidedExports(module))
|
||||
) {
|
||||
set.add(connection.module);
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { SyncWaterfallHook } = require("tapable");
|
||||
const Compilation = require("../Compilation");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const Template = require("../Template");
|
||||
const HelperRuntimeModule = require("./HelperRuntimeModule");
|
||||
|
||||
/** @typedef {import("../Chunk")} Chunk */
|
||||
/** @typedef {import("../Compilation")} Compilation */
|
||||
/** @typedef {import("../Compiler")} Compiler */
|
||||
|
||||
/**
|
||||
* @typedef {Object} LoadScriptCompilationHooks
|
||||
* @property {SyncWaterfallHook<[string, Chunk]>} createScript
|
||||
*/
|
||||
|
||||
/** @type {WeakMap<Compilation, LoadScriptCompilationHooks>} */
|
||||
const compilationHooksMap = new WeakMap();
|
||||
|
||||
class LoadScriptRuntimeModule extends HelperRuntimeModule {
|
||||
/**
|
||||
* @param {Compilation} compilation the compilation
|
||||
* @returns {LoadScriptCompilationHooks} 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 = {
|
||||
createScript: new SyncWaterfallHook(["source", "chunk"])
|
||||
};
|
||||
compilationHooksMap.set(compilation, hooks);
|
||||
}
|
||||
return hooks;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super("load script");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} runtime code
|
||||
*/
|
||||
generate() {
|
||||
const { compilation } = this;
|
||||
const { runtimeTemplate, outputOptions } = compilation;
|
||||
const {
|
||||
jsonpScriptType: scriptType,
|
||||
chunkLoadTimeout: loadTimeout,
|
||||
crossOriginLoading
|
||||
} = outputOptions;
|
||||
const fn = RuntimeGlobals.loadScript;
|
||||
|
||||
const { createScript } = LoadScriptRuntimeModule.getCompilationHooks(
|
||||
compilation
|
||||
);
|
||||
|
||||
const code = Template.asString([
|
||||
"script = document.createElement('script');",
|
||||
scriptType ? `script.type = ${JSON.stringify(scriptType)};` : "",
|
||||
"script.charset = 'utf-8';",
|
||||
`script.timeout = ${loadTimeout / 1000};`,
|
||||
`if (${RuntimeGlobals.scriptNonce}) {`,
|
||||
Template.indent(
|
||||
`script.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
|
||||
),
|
||||
"}",
|
||||
'script.setAttribute("data-webpack", key);',
|
||||
`script.src = url;`,
|
||||
crossOriginLoading
|
||||
? Template.asString([
|
||||
"if (script.src.indexOf(window.location.origin + '/') !== 0) {",
|
||||
Template.indent(
|
||||
`script.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
|
||||
),
|
||||
"}"
|
||||
])
|
||||
: ""
|
||||
]);
|
||||
|
||||
return Template.asString([
|
||||
"var inProgress = {};",
|
||||
"// loadScript function to load a script via script tag",
|
||||
`${fn} = ${runtimeTemplate.basicFunction("url, done, key", [
|
||||
"if(inProgress[url]) { inProgress[url].push(done); return; }",
|
||||
"var script, needAttach;",
|
||||
"if(key !== undefined) {",
|
||||
Template.indent([
|
||||
'var scripts = document.getElementsByTagName("script");',
|
||||
"for(var i = 0; i < scripts.length; i++) {",
|
||||
Template.indent([
|
||||
"var s = scripts[i];",
|
||||
'if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == key) { script = s; break; }'
|
||||
]),
|
||||
"}"
|
||||
]),
|
||||
"}",
|
||||
"if(!script) {",
|
||||
Template.indent([
|
||||
"needAttach = true;",
|
||||
createScript.call(code, this.chunk)
|
||||
]),
|
||||
"}",
|
||||
"inProgress[url] = [done];",
|
||||
"var onScriptComplete = " +
|
||||
runtimeTemplate.basicFunction(
|
||||
"event",
|
||||
Template.asString([
|
||||
`onScriptComplete = ${runtimeTemplate.basicFunction("", "")}`,
|
||||
"// avoid mem leaks in IE.",
|
||||
"script.onerror = script.onload = null;",
|
||||
"clearTimeout(timeout);",
|
||||
"var doneFns = inProgress[url];",
|
||||
"delete inProgress[url];",
|
||||
"script.parentNode.removeChild(script);",
|
||||
`doneFns && doneFns.forEach(${runtimeTemplate.returningFunction(
|
||||
"fn(event)",
|
||||
"fn"
|
||||
)});`
|
||||
])
|
||||
),
|
||||
";",
|
||||
`var timeout = setTimeout(${runtimeTemplate.basicFunction(
|
||||
"",
|
||||
"onScriptComplete({ type: 'timeout', target: script })"
|
||||
)}, ${loadTimeout});`,
|
||||
"script.onerror = script.onload = onScriptComplete;",
|
||||
"needAttach && document.head.appendChild(script);"
|
||||
])};`
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LoadScriptRuntimeModule;
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
class DateObjectSerializer {
|
||||
serialize(obj, { write }) {
|
||||
write(obj.getTime());
|
||||
}
|
||||
deserialize({ read }) {
|
||||
return new Date(read());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DateObjectSerializer;
|
|
@ -5,6 +5,7 @@
|
|||
"use strict";
|
||||
|
||||
const ArraySerializer = require("./ArraySerializer");
|
||||
const DateObjectSerializer = require("./DateObjectSerializer");
|
||||
const ErrorObjectSerializer = require("./ErrorObjectSerializer");
|
||||
const MapObjectSerializer = require("./MapObjectSerializer");
|
||||
const NullPrototypeObjectSerializer = require("./NullPrototypeObjectSerializer");
|
||||
|
@ -94,6 +95,7 @@ jsTypes.set(Array, new ArraySerializer());
|
|||
jsTypes.set(null, new NullPrototypeObjectSerializer());
|
||||
jsTypes.set(Map, new MapObjectSerializer());
|
||||
jsTypes.set(Set, new SetObjectSerializer());
|
||||
jsTypes.set(Date, new DateObjectSerializer());
|
||||
jsTypes.set(RegExp, new RegExpObjectSerializer());
|
||||
jsTypes.set(Error, new ErrorObjectSerializer(Error));
|
||||
jsTypes.set(EvalError, new ErrorObjectSerializer(EvalError));
|
||||
|
@ -267,17 +269,26 @@ class ObjectMiddleware extends SerializerMiddleware {
|
|||
} catch (e) {
|
||||
// ignore -> fallback
|
||||
}
|
||||
if (typeof item === "object" && item !== null && item.constructor) {
|
||||
if (item.constructor === Object)
|
||||
return `Object { ${Object.keys(item).join(", ")} }`;
|
||||
if (item.constructor === Map) return `Map { ${item.size} items }`;
|
||||
if (item.constructor === Array)
|
||||
return `Array { ${item.length} items }`;
|
||||
if (item.constructor === Set) return `Set { ${item.size} items }`;
|
||||
if (item.constructor === RegExp) return item.toString();
|
||||
return `${item.constructor.name}`;
|
||||
if (typeof item === "object" && item !== null) {
|
||||
if (item.constructor) {
|
||||
if (item.constructor === Object)
|
||||
return `Object { ${Object.keys(item).join(", ")} }`;
|
||||
if (item.constructor === Map) return `Map { ${item.size} items }`;
|
||||
if (item.constructor === Array)
|
||||
return `Array { ${item.length} items }`;
|
||||
if (item.constructor === Set) return `Set { ${item.size} items }`;
|
||||
if (item.constructor === RegExp) return item.toString();
|
||||
return `${item.constructor.name}`;
|
||||
}
|
||||
return `Object [null prototype] { ${Object.keys(item).join(
|
||||
", "
|
||||
)} }`;
|
||||
}
|
||||
try {
|
||||
return `${item}`;
|
||||
} catch (e) {
|
||||
return `(${e.message})`;
|
||||
}
|
||||
return `${item}`;
|
||||
})
|
||||
.join(" -> ");
|
||||
};
|
||||
|
|
|
@ -30,9 +30,11 @@ const { versionToString } = require("./utils");
|
|||
/**
|
||||
* @typedef {Object} ConsumeOptions
|
||||
* @property {string=} import fallback request
|
||||
* @property {string=} importResolved resolved fallback request
|
||||
* @property {string} shareKey global share key
|
||||
* @property {string} shareScope share scope
|
||||
* @property {(number|string)[]} requiredVersion version requirement
|
||||
* @property {(number|string)[] | undefined} requiredVersion version requirement
|
||||
* @property {string} packageName package name to determine required version automatically
|
||||
* @property {boolean} strictVersion don't use shared version even if version isn't valid
|
||||
* @property {boolean} singleton use single global version
|
||||
* @property {boolean} eager include the fallback module in a sync way
|
||||
|
@ -57,15 +59,15 @@ class ConsumeSharedModule extends Module {
|
|||
const {
|
||||
shareKey,
|
||||
shareScope,
|
||||
import: request,
|
||||
importResolved,
|
||||
requiredVersion,
|
||||
strictVersion,
|
||||
singleton,
|
||||
eager
|
||||
} = this.options;
|
||||
return `consume-shared-module|${shareScope}|${shareKey}|${versionToString(
|
||||
requiredVersion
|
||||
)}|${strictVersion}|${request}|${singleton}|${eager}`;
|
||||
return `consume-shared-module|${shareScope}|${shareKey}|${
|
||||
requiredVersion && versionToString(requiredVersion)
|
||||
}|${strictVersion}|${importResolved}|${singleton}|${eager}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,16 +78,18 @@ class ConsumeSharedModule extends Module {
|
|||
const {
|
||||
shareKey,
|
||||
shareScope,
|
||||
import: request,
|
||||
importResolved,
|
||||
requiredVersion,
|
||||
strictVersion,
|
||||
singleton,
|
||||
eager
|
||||
} = this.options;
|
||||
return `shared module (${shareScope}) ${shareKey}@${versionToString(
|
||||
return `consume shared module (${shareScope}) ${shareKey}@${versionToString(
|
||||
requiredVersion
|
||||
)}${strictVersion ? " (strict)" : ""}${singleton ? " (singleton)" : ""}${
|
||||
request ? ` -> ${request}` : ""
|
||||
importResolved
|
||||
? ` (fallback: ${requestShortener.shorten(importResolved)})`
|
||||
: ""
|
||||
}${eager ? " (eager)" : ""}`;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,13 +9,20 @@ const validateOptions = require("schema-utils");
|
|||
const schema = require("../../schemas/plugins/sharing/ConsumeSharedPlugin.json");
|
||||
const ModuleNotFoundError = require("../ModuleNotFoundError");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const WebpackError = require("../WebpackError");
|
||||
const { parseOptions } = require("../container/options");
|
||||
const LazySet = require("../util/LazySet");
|
||||
const ConsumeFallbackDependency = require("./ConsumeFallbackDependency");
|
||||
const ConsumeSharedModule = require("./ConsumeSharedModule");
|
||||
const ConsumeSharedRuntimeModule = require("./ConsumeSharedRuntimeModule");
|
||||
const ProvidedDependency = require("./ProvidedDependency");
|
||||
const { parseRequiredVersion, isRequiredVersion } = require("./utils");
|
||||
const { resolveMatchedConfigs } = require("./resolveMatchedConfigs");
|
||||
const {
|
||||
parseRequiredVersion,
|
||||
isRequiredVersion,
|
||||
getDescriptionFile,
|
||||
getRequiredVersionFromDescriptionFile
|
||||
} = require("./utils");
|
||||
|
||||
/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */
|
||||
/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumeSharedPluginOptions} ConsumeSharedPluginOptions */
|
||||
|
@ -37,12 +44,12 @@ class ConsumeSharedPlugin {
|
|||
validateOptions(schema, options, { name: "Consumes Shared Plugin" });
|
||||
}
|
||||
|
||||
/** @type {[string, ConsumeOptions][]} */
|
||||
/** @type {[string, ConsumesConfig][]} */
|
||||
this._consumes = parseOptions(
|
||||
options.consumes,
|
||||
(item, key) => {
|
||||
if (Array.isArray(item)) throw new Error("Unexpected array in options");
|
||||
/** @type {ConsumeOptions} */
|
||||
/** @type {ConsumesConfig} */
|
||||
let result =
|
||||
item === key || !isRequiredVersion(item)
|
||||
? // item is a request/key
|
||||
|
@ -51,6 +58,7 @@ class ConsumeSharedPlugin {
|
|||
shareScope: options.shareScope || "default",
|
||||
shareKey: key,
|
||||
requiredVersion: undefined,
|
||||
packageName: undefined,
|
||||
strictVersion: false,
|
||||
singleton: false,
|
||||
eager: false
|
||||
|
@ -61,8 +69,10 @@ class ConsumeSharedPlugin {
|
|||
import: key,
|
||||
shareScope: options.shareScope || "default",
|
||||
shareKey: key,
|
||||
requiredVersion: parseRequiredVersion(item),
|
||||
requiredVersion:
|
||||
typeof item === "string" ? parseRequiredVersion(item) : item,
|
||||
strictVersion: true,
|
||||
packageName: undefined,
|
||||
singleton: false,
|
||||
eager: false
|
||||
};
|
||||
|
@ -77,10 +87,10 @@ class ConsumeSharedPlugin {
|
|||
? parseRequiredVersion(item.requiredVersion)
|
||||
: item.requiredVersion,
|
||||
strictVersion:
|
||||
item.requiredVersion &&
|
||||
(typeof item.strictVersion === "boolean"
|
||||
typeof item.strictVersion === "boolean"
|
||||
? item.strictVersion
|
||||
: item.import !== false && !item.singleton),
|
||||
: item.import !== false && !item.singleton,
|
||||
packageName: item.packageName,
|
||||
singleton: !!item.singleton,
|
||||
eager: !!item.eager
|
||||
})
|
||||
|
@ -101,74 +111,146 @@ class ConsumeSharedPlugin {
|
|||
normalModuleFactory
|
||||
);
|
||||
|
||||
/** @type {Map<string, ConsumeOptions>} */
|
||||
const resolvedConsumes = new Map();
|
||||
/** @type {Map<string, ConsumeOptions>} */
|
||||
const unresolvedConsumes = new Map();
|
||||
/** @type {Map<string, ConsumeOptions>} */
|
||||
const prefixConsumes = new Map();
|
||||
const resolveContext = {
|
||||
/** @type {LazySet<string>} */
|
||||
fileDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
contextDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
missingDependencies: new LazySet()
|
||||
};
|
||||
let unresolvedConsumes, resolvedConsumes, prefixedConsumes;
|
||||
const promise = resolveMatchedConfigs(compilation, this._consumes).then(
|
||||
({ resolved, unresolved, prefixed }) => {
|
||||
resolvedConsumes = resolved;
|
||||
unresolvedConsumes = unresolved;
|
||||
prefixedConsumes = prefixed;
|
||||
}
|
||||
);
|
||||
|
||||
const resolver = compilation.resolverFactory.get(
|
||||
"normal",
|
||||
RESOLVE_OPTIONS
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {string} request imported request
|
||||
* @param {ConsumeOptions} options options
|
||||
* @returns {Promise<void>} promise
|
||||
* @param {string} context issuer directory
|
||||
* @param {string} request request
|
||||
* @param {ConsumeOptions} config options
|
||||
* @returns {Promise<ConsumeSharedModule>} create module
|
||||
*/
|
||||
const resolveConsume = (request, options) => {
|
||||
if (/^\.\.?(\/|$)/.test(request)) {
|
||||
// relative request
|
||||
return new Promise(resolve => {
|
||||
const createConsumeSharedModule = (context, request, config) => {
|
||||
const requiredVersionWarning = details => {
|
||||
const error = new WebpackError(
|
||||
`No required version specified and unable to automatically determine one. ${details}`
|
||||
);
|
||||
error.file = `shared module ${request}`;
|
||||
compilation.warnings.push(error);
|
||||
};
|
||||
const directFallback =
|
||||
config.import &&
|
||||
/^(\.\.?(\/|$)|\/|[A-Za-z]:|\\\\)/.test(config.import);
|
||||
return Promise.all([
|
||||
new Promise(resolve => {
|
||||
if (!config.import) return resolve();
|
||||
const resolveContext = {
|
||||
/** @type {LazySet<string>} */
|
||||
fileDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
contextDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
missingDependencies: new LazySet()
|
||||
};
|
||||
resolver.resolve(
|
||||
{},
|
||||
compiler.context,
|
||||
request,
|
||||
directFallback ? compiler.context : context,
|
||||
config.import,
|
||||
resolveContext,
|
||||
(err, result) => {
|
||||
compilation.contextDependencies.addAll(
|
||||
resolveContext.contextDependencies
|
||||
);
|
||||
compilation.fileDependencies.addAll(
|
||||
resolveContext.fileDependencies
|
||||
);
|
||||
compilation.missingDependencies.addAll(
|
||||
resolveContext.missingDependencies
|
||||
);
|
||||
if (err) {
|
||||
compilation.errors.push(
|
||||
new ModuleNotFoundError(null, err, {
|
||||
name: `consumed shared module ${request}`
|
||||
name: `resolving fallback for shared module ${request}`
|
||||
})
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
resolvedConsumes.set(result, options);
|
||||
resolve();
|
||||
resolve(result);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(request)) {
|
||||
// absolute path
|
||||
resolvedConsumes.set(request, options);
|
||||
} else if (request.endsWith("/")) {
|
||||
// module request prefix
|
||||
prefixConsumes.set(request, options);
|
||||
} else {
|
||||
// module request
|
||||
unresolvedConsumes.set(request, options);
|
||||
}
|
||||
}),
|
||||
new Promise(resolve => {
|
||||
if (config.requiredVersion !== undefined)
|
||||
return resolve(config.requiredVersion);
|
||||
let packageName = config.packageName;
|
||||
if (packageName === undefined) {
|
||||
if (/^(\/|[A-Za-z]:|\\\\)/.test(request)) {
|
||||
// For relative or absolute requests we don't automatically use a packageName.
|
||||
// If wished one can specify one with the packageName option.
|
||||
return resolve();
|
||||
}
|
||||
const match = /^((?:@[^\\/]+[\\/])?[^\\/]+)/.exec(request);
|
||||
if (!match) {
|
||||
requiredVersionWarning(
|
||||
"Unable to extract the package name from request."
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
packageName = match[0];
|
||||
}
|
||||
|
||||
getDescriptionFile(
|
||||
compilation.inputFileSystem,
|
||||
context,
|
||||
["package.json"],
|
||||
(err, result) => {
|
||||
if (err) {
|
||||
requiredVersionWarning(
|
||||
`Unable to read description file: ${err}`
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
const { data, path: descriptionPath } = result;
|
||||
if (!data) {
|
||||
requiredVersionWarning(
|
||||
`Unable to find description file in ${context}.`
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
const requiredVersion = getRequiredVersionFromDescriptionFile(
|
||||
data,
|
||||
packageName
|
||||
);
|
||||
if (typeof requiredVersion !== "string") {
|
||||
requiredVersionWarning(
|
||||
`Unable to find required version for "${packageName}" in description file (${descriptionPath}). It need to be in dependencies, devDependencies or peerDependencies.`
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
if (!isRequiredVersion(requiredVersion)) {
|
||||
requiredVersionWarning(
|
||||
`Required version in description file ("${requiredVersion}") is unsupported (too complex, weird syntax).`
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
resolve(parseRequiredVersion(requiredVersion));
|
||||
}
|
||||
);
|
||||
})
|
||||
]).then(([importResolved, requiredVersion]) => {
|
||||
return new ConsumeSharedModule(
|
||||
directFallback ? compiler.context : context,
|
||||
{
|
||||
...config,
|
||||
importResolved,
|
||||
import: importResolved ? config.import : undefined,
|
||||
requiredVersion
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
const promise = Promise.all(
|
||||
this._consumes.map(([name, config]) => resolveConsume(name, config))
|
||||
).then(() => {
|
||||
compilation.contextDependencies.addAll(
|
||||
resolveContext.contextDependencies
|
||||
);
|
||||
compilation.fileDependencies.addAll(resolveContext.fileDependencies);
|
||||
compilation.missingDependencies.addAll(
|
||||
resolveContext.missingDependencies
|
||||
);
|
||||
});
|
||||
|
||||
normalModuleFactory.hooks.factorize.tapPromise(
|
||||
PLUGIN_NAME,
|
||||
({ context, request, dependencies }) =>
|
||||
|
@ -182,12 +264,12 @@ class ConsumeSharedPlugin {
|
|||
}
|
||||
const match = unresolvedConsumes.get(request);
|
||||
if (match !== undefined) {
|
||||
return new ConsumeSharedModule(compiler.context, match);
|
||||
return createConsumeSharedModule(context, request, match);
|
||||
}
|
||||
for (const [prefix, options] of prefixConsumes) {
|
||||
for (const [prefix, options] of prefixedConsumes) {
|
||||
if (request.startsWith(prefix)) {
|
||||
const remainder = request.slice(prefix.length);
|
||||
return new ConsumeSharedModule(compiler.context, {
|
||||
return createConsumeSharedModule(context, request, {
|
||||
...options,
|
||||
import: options.import
|
||||
? options.import + remainder
|
||||
|
@ -198,19 +280,20 @@ class ConsumeSharedPlugin {
|
|||
}
|
||||
})
|
||||
);
|
||||
normalModuleFactory.hooks.module.tap(
|
||||
normalModuleFactory.hooks.createModule.tapPromise(
|
||||
PLUGIN_NAME,
|
||||
(module, createData, { context, request, dependencies }) => {
|
||||
({ resource }, { context, dependencies }) => {
|
||||
if (
|
||||
dependencies[0] instanceof ConsumeFallbackDependency ||
|
||||
dependencies[0] instanceof ProvidedDependency
|
||||
) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
const options = resolvedConsumes.get(createData.resource);
|
||||
const options = resolvedConsumes.get(resource);
|
||||
if (options !== undefined) {
|
||||
return new ConsumeSharedModule(compiler.context, options);
|
||||
return createConsumeSharedModule(context, resource, options);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
);
|
||||
compilation.hooks.additionalTreeRuntimeRequirements.tap(
|
||||
|
|
|
@ -59,9 +59,9 @@ class ProvideModule extends Module {
|
|||
* @returns {string} a user readable identifier of the module
|
||||
*/
|
||||
readableIdentifier(requestShortener) {
|
||||
return `provide module (${this._shareScope}) ${this._name}@${
|
||||
return `provide shared module (${this._shareScope}) ${this._name}@${
|
||||
this._version && this._version.join(".")
|
||||
} = ${this._request}`;
|
||||
} = ${requestShortener.shorten(this._request)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,15 +108,6 @@ class ProvideModule extends Module {
|
|||
callback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Chunk} chunk the chunk which condition should be checked
|
||||
* @param {Compilation} compilation the compilation
|
||||
* @returns {boolean} true, if the chunk is ok for the module
|
||||
*/
|
||||
chunkCondition(chunk, { chunkGraph }) {
|
||||
return chunkGraph.getNumberOfEntryModules(chunk) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
|
|
|
@ -9,26 +9,24 @@ const validateOptions = require("schema-utils");
|
|||
const schema = require("../../schemas/plugins/sharing/ProvideSharedPlugin.json");
|
||||
const WebpackError = require("../WebpackError");
|
||||
const { parseOptions } = require("../container/options");
|
||||
const LazySet = require("../util/LazySet");
|
||||
const ProvideDependency = require("./ProvideDependency");
|
||||
const ProvideModuleFactory = require("./ProvideModuleFactory");
|
||||
const ProvidedDependency = require("./ProvidedDependency");
|
||||
const { parseVersion } = require("./utils");
|
||||
|
||||
/** @typedef {import("../../declarations/plugins/sharing/ProvideSharedPlugin").ProvideSharedPluginOptions} ProvideSharedPluginOptions */
|
||||
/** @typedef {import("../Compilation")} Compilation */
|
||||
/** @typedef {import("../Compiler")} Compiler */
|
||||
/** @typedef {import("../ResolverFactory").ResolveOptionsWithDependencyType} ResolveOptionsWithDependencyType */
|
||||
|
||||
/**
|
||||
* @typedef {Object} ProvideOptions
|
||||
* @property {string} import
|
||||
* @property {string} shareKey
|
||||
* @property {string} shareScope
|
||||
* @property {(string|number)[] | undefined | false} version
|
||||
* @property {boolean} eager
|
||||
*/
|
||||
|
||||
/** @type {ResolveOptionsWithDependencyType} */
|
||||
const RESOLVE_OPTIONS = { dependencyType: "esm" };
|
||||
/** @typedef {Map<string, { config: ProvideOptions, version: (string|number)[] | undefined | false }>} ResolvedProvideMap */
|
||||
|
||||
class ProvideSharedPlugin {
|
||||
/**
|
||||
|
@ -45,7 +43,7 @@ class ProvideSharedPlugin {
|
|||
throw new Error("Unexpected array of provides");
|
||||
/** @type {ProvideOptions} */
|
||||
const result = {
|
||||
import: item,
|
||||
shareKey: item,
|
||||
version: undefined,
|
||||
shareScope: options.shareScope || "default",
|
||||
eager: false
|
||||
|
@ -53,7 +51,7 @@ class ProvideSharedPlugin {
|
|||
return result;
|
||||
},
|
||||
item => ({
|
||||
import: item.import,
|
||||
shareKey: item.shareKey,
|
||||
version:
|
||||
typeof item.version === "string"
|
||||
? parseVersion(item.version)
|
||||
|
@ -75,102 +73,132 @@ class ProvideSharedPlugin {
|
|||
* @returns {void}
|
||||
*/
|
||||
apply(compiler) {
|
||||
const { _provides: provides } = this;
|
||||
/** @type {WeakMap<Compilation, ResolvedProvideMap>} */
|
||||
const compilationData = new WeakMap();
|
||||
|
||||
for (const [name, config] of provides) {
|
||||
compiler.hooks.make.tapAsync(
|
||||
"ProvideSharedPlugin",
|
||||
(compilation, callback) => {
|
||||
let version = config.version;
|
||||
const addModule = () => {
|
||||
compilation.addInclude(
|
||||
compiler.context,
|
||||
new ProvideDependency(
|
||||
config.shareScope,
|
||||
name,
|
||||
version || false,
|
||||
config.import,
|
||||
config.eager
|
||||
),
|
||||
{
|
||||
name: undefined
|
||||
},
|
||||
err => callback(err)
|
||||
);
|
||||
};
|
||||
if (
|
||||
version !== undefined ||
|
||||
config.import.startsWith("./") ||
|
||||
config.import.startsWith("../")
|
||||
) {
|
||||
return addModule();
|
||||
compiler.hooks.compilation.tap(
|
||||
"ProvideSharedPlugin",
|
||||
(compilation, { normalModuleFactory }) => {
|
||||
/** @type {ResolvedProvideMap} */
|
||||
const resolvedProvideMap = new Map();
|
||||
/** @type {Map<string, ProvideOptions>} */
|
||||
const matchProvides = new Map();
|
||||
/** @type {Map<string, ProvideOptions>} */
|
||||
const prefixMatchProvides = new Map();
|
||||
for (const [request, config] of this._provides) {
|
||||
if (/^(\/|[A-Za-z]:\\|\\\\|\.\.?(\/|$))/.test(request)) {
|
||||
// relative request
|
||||
resolvedProvideMap.set(request, {
|
||||
config,
|
||||
version: config.version
|
||||
});
|
||||
} else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(request)) {
|
||||
// absolute path
|
||||
resolvedProvideMap.set(request, {
|
||||
config,
|
||||
version: config.version
|
||||
});
|
||||
} else if (request.endsWith("/")) {
|
||||
// module request prefix
|
||||
prefixMatchProvides.set(request, config);
|
||||
} else {
|
||||
// module request
|
||||
matchProvides.set(request, config);
|
||||
}
|
||||
const resolveContext = {
|
||||
/** @type {LazySet<string>} */
|
||||
fileDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
contextDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
missingDependencies: new LazySet()
|
||||
};
|
||||
const resolver = compiler.resolverFactory.get(
|
||||
"normal",
|
||||
RESOLVE_OPTIONS
|
||||
);
|
||||
resolver.resolve(
|
||||
{},
|
||||
compiler.context,
|
||||
config.import,
|
||||
resolveContext,
|
||||
(err, result, additionalInfo) => {
|
||||
compilation.fileDependencies.addAll(
|
||||
resolveContext.fileDependencies
|
||||
);
|
||||
compilation.contextDependencies.addAll(
|
||||
resolveContext.contextDependencies
|
||||
);
|
||||
compilation.missingDependencies.addAll(
|
||||
resolveContext.missingDependencies
|
||||
);
|
||||
let details;
|
||||
if (err) {
|
||||
details = `Failed to resolve: ${err}.`;
|
||||
} else if (!result) {
|
||||
details = `Resolved to void.`;
|
||||
} else if (!additionalInfo) {
|
||||
details = `No additional info provided from resolver.`;
|
||||
} else {
|
||||
const info = /** @type {any} */ (additionalInfo);
|
||||
const descriptionFileData = info.descriptionFileData;
|
||||
if (!descriptionFileData) {
|
||||
details =
|
||||
"No description file (usually package.json) found. Add description file with name and version, or manually specify version in shared config.";
|
||||
} else if (!descriptionFileData.version) {
|
||||
details =
|
||||
"No version in description file (usually package.json). Add version to description file, or manually specify version in shared config.";
|
||||
} else if (
|
||||
descriptionFileData.name &&
|
||||
config.import !== descriptionFileData.name &&
|
||||
!config.import.startsWith(`${descriptionFileData.name}/`)
|
||||
) {
|
||||
details = `Invalid name in description file (usually package.json): ${descriptionFileData.name}. Check location of description file, update name in description file, add missing description file to the package, or manually specify version in shared config.`;
|
||||
} else {
|
||||
version = parseVersion(descriptionFileData.version);
|
||||
}
|
||||
}
|
||||
if (!version) {
|
||||
const error = new WebpackError(
|
||||
`No version specified and unable to automatically determine one. ${details}`
|
||||
);
|
||||
error.file = `shared module ${name} -> ${config.import}`;
|
||||
compilation.warnings.push(error);
|
||||
}
|
||||
addModule();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
compilationData.set(compilation, resolvedProvideMap);
|
||||
const provideModule = (key, config, resource, resourceResolveData) => {
|
||||
let version = config.version;
|
||||
if (version === undefined) {
|
||||
let details = "";
|
||||
if (!resourceResolveData) {
|
||||
details = `No resolve data provided from resolver.`;
|
||||
} else {
|
||||
const descriptionFileData =
|
||||
resourceResolveData.descriptionFileData;
|
||||
if (!descriptionFileData) {
|
||||
details =
|
||||
"No description file (usually package.json) found. Add description file with name and version, or manually specify version in shared config.";
|
||||
} else if (!descriptionFileData.version) {
|
||||
details =
|
||||
"No version in description file (usually package.json). Add version to description file, or manually specify version in shared config.";
|
||||
} else {
|
||||
version = parseVersion(descriptionFileData.version);
|
||||
}
|
||||
}
|
||||
if (!version) {
|
||||
const error = new WebpackError(
|
||||
`No version specified and unable to automatically determine one. ${details}`
|
||||
);
|
||||
error.file = `shared module ${key} -> ${resource}`;
|
||||
compilation.warnings.push(error);
|
||||
}
|
||||
}
|
||||
resolvedProvideMap.set(resource, {
|
||||
config,
|
||||
version
|
||||
});
|
||||
};
|
||||
normalModuleFactory.hooks.module.tap(
|
||||
"ProvideSharedPlugin",
|
||||
(module, { resource, resourceResolveData }, { request }) => {
|
||||
if (resolvedProvideMap.has(resource)) {
|
||||
return module;
|
||||
}
|
||||
{
|
||||
const config = matchProvides.get(request);
|
||||
if (config !== undefined) {
|
||||
provideModule(request, config, resource, resourceResolveData);
|
||||
}
|
||||
}
|
||||
for (const [prefix, config] of prefixMatchProvides) {
|
||||
if (request.startsWith(prefix)) {
|
||||
const remainder = request.slice(prefix.length);
|
||||
provideModule(
|
||||
resource,
|
||||
{
|
||||
...config,
|
||||
shareKey: config.shareKey + remainder
|
||||
},
|
||||
resource,
|
||||
resourceResolveData
|
||||
);
|
||||
}
|
||||
}
|
||||
return module;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
compiler.hooks.finishMake.tapPromise("ProvideSharedPlugin", compilation => {
|
||||
const resolvedProvideMap = compilationData.get(compilation);
|
||||
if (!resolvedProvideMap) return Promise.resolve();
|
||||
return Promise.all(
|
||||
Array.from(
|
||||
resolvedProvideMap,
|
||||
([resource, { config, version }]) =>
|
||||
new Promise((resolve, reject) => {
|
||||
compilation.addInclude(
|
||||
compiler.context,
|
||||
new ProvideDependency(
|
||||
config.shareScope,
|
||||
config.shareKey,
|
||||
version || false,
|
||||
resource,
|
||||
config.eager
|
||||
),
|
||||
{
|
||||
name: undefined
|
||||
},
|
||||
err => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
})
|
||||
)
|
||||
).then(() => {});
|
||||
});
|
||||
|
||||
compiler.hooks.compilation.tap(
|
||||
"ProvideSharedPlugin",
|
||||
|
|
|
@ -47,10 +47,11 @@ class SharePlugin {
|
|||
const consumes = sharedOptions.map(([key, options]) => ({
|
||||
[key]: {
|
||||
import: options.import,
|
||||
shareKey: options.shareKey,
|
||||
shareKey: options.shareKey || key,
|
||||
requiredVersion: options.requiredVersion,
|
||||
strictVersion: options.strictVersion,
|
||||
singleton: options.singleton,
|
||||
packageName: options.packageName,
|
||||
eager: options.eager
|
||||
}
|
||||
}));
|
||||
|
@ -58,8 +59,8 @@ class SharePlugin {
|
|||
const provides = sharedOptions
|
||||
.filter(([, options]) => options.import !== false)
|
||||
.map(([key, options]) => ({
|
||||
[options.shareKey || key]: {
|
||||
import: options.import || key,
|
||||
[options.import || key]: {
|
||||
shareKey: options.shareKey || key,
|
||||
version: options.version,
|
||||
eager: options.eager
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const ModuleNotFoundError = require("../ModuleNotFoundError");
|
||||
const LazySet = require("../util/LazySet");
|
||||
|
||||
/** @typedef {import("../Compilation")} Compilation */
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} MatchedConfigs
|
||||
* @property {Map<string, T>} resolved
|
||||
* @property {Map<string, T>} unresolved
|
||||
* @property {Map<string, T>} prefixed
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {Compilation} compilation the compilation
|
||||
* @param {[string, T][]} configs to be processed configs
|
||||
* @returns {Promise<MatchedConfigs<T>>} resolved matchers
|
||||
*/
|
||||
exports.resolveMatchedConfigs = (compilation, configs) => {
|
||||
/** @type {Map<string, T>} */
|
||||
const resolved = new Map();
|
||||
/** @type {Map<string, T>} */
|
||||
const unresolved = new Map();
|
||||
/** @type {Map<string, T>} */
|
||||
const prefixed = new Map();
|
||||
const resolveContext = {
|
||||
/** @type {LazySet<string>} */
|
||||
fileDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
contextDependencies: new LazySet(),
|
||||
/** @type {LazySet<string>} */
|
||||
missingDependencies: new LazySet()
|
||||
};
|
||||
const resolver = compilation.resolverFactory.get("normal");
|
||||
const context = compilation.compiler.context;
|
||||
|
||||
return Promise.all(
|
||||
configs.map(([request, config]) => {
|
||||
if (/^\.\.?(\/|$)/.test(request)) {
|
||||
// relative request
|
||||
return new Promise(resolve => {
|
||||
resolver.resolve(
|
||||
{},
|
||||
context,
|
||||
request,
|
||||
resolveContext,
|
||||
(err, result) => {
|
||||
if (err) {
|
||||
compilation.errors.push(
|
||||
new ModuleNotFoundError(null, err, {
|
||||
name: `shared module ${request}`
|
||||
})
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
resolved.set(result, config);
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
} else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(request)) {
|
||||
// absolute path
|
||||
resolved.set(request, config);
|
||||
} else if (request.endsWith("/")) {
|
||||
// module request prefix
|
||||
prefixed.set(request, config);
|
||||
} else {
|
||||
// module request
|
||||
unresolved.set(request, config);
|
||||
}
|
||||
})
|
||||
).then(() => {
|
||||
compilation.contextDependencies.addAll(resolveContext.contextDependencies);
|
||||
compilation.fileDependencies.addAll(resolveContext.fileDependencies);
|
||||
compilation.missingDependencies.addAll(resolveContext.missingDependencies);
|
||||
return { resolved, unresolved, prefixed };
|
||||
});
|
||||
};
|
|
@ -5,11 +5,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { join, dirname, readJson } = require("../util/fs");
|
||||
|
||||
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
|
||||
|
||||
/**
|
||||
* @param {string} version version as string
|
||||
* @returns {(number|string)[]} version as array
|
||||
*/
|
||||
exports.parseRequiredVersion = version => {
|
||||
if (version === "*") return [];
|
||||
let fuzzyStart = Infinity;
|
||||
if (version.startsWith(">=")) {
|
||||
fuzzyStart = 0;
|
||||
|
@ -40,6 +45,7 @@ exports.parseVersion = version => {
|
|||
*/
|
||||
exports.versionToString = version => {
|
||||
if (!version) return "(unknown)";
|
||||
if (version.length === 0) return "*";
|
||||
const info = version.map(value =>
|
||||
typeof value !== "string"
|
||||
? {
|
||||
|
@ -96,8 +102,82 @@ exports.versionToString = version => {
|
|||
* @returns {boolean} true, if it looks like a version
|
||||
*/
|
||||
exports.isRequiredVersion = str => {
|
||||
if (str === "*") return true;
|
||||
if (/&&|\|\|/.test(str)) return false;
|
||||
if (str.startsWith("^")) return true;
|
||||
if (str.startsWith("~")) return true;
|
||||
if (str.startsWith(">=")) return true;
|
||||
return /^\d/.test(str);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {InputFileSystem} fs file system
|
||||
* @param {string} directory directory to start looking into
|
||||
* @param {string[]} descriptionFiles possible description filenames
|
||||
* @param {function(Error=, {data: object, path: string}=): void} callback callback
|
||||
*/
|
||||
const getDescriptionFile = (fs, directory, descriptionFiles, callback) => {
|
||||
let i = 0;
|
||||
const tryLoadCurrent = () => {
|
||||
if (i >= descriptionFiles.length) {
|
||||
const parentDirectory = dirname(fs, directory);
|
||||
if (!parentDirectory || parentDirectory === directory) return callback();
|
||||
return getDescriptionFile(
|
||||
fs,
|
||||
parentDirectory,
|
||||
descriptionFiles,
|
||||
callback
|
||||
);
|
||||
}
|
||||
const filePath = join(fs, directory, descriptionFiles[i]);
|
||||
readJson(fs, filePath, (err, data) => {
|
||||
if (err) {
|
||||
if ("code" in err && err.code === "ENOENT") {
|
||||
i++;
|
||||
return tryLoadCurrent();
|
||||
}
|
||||
return callback(err);
|
||||
}
|
||||
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
||||
return callback(
|
||||
new Error(`Description file ${filePath} is not an object`)
|
||||
);
|
||||
}
|
||||
callback(null, { data, path: filePath });
|
||||
});
|
||||
};
|
||||
tryLoadCurrent();
|
||||
};
|
||||
exports.getDescriptionFile = getDescriptionFile;
|
||||
|
||||
exports.getRequiredVersionFromDescriptionFile = (data, packageName) => {
|
||||
if (
|
||||
data.optionalDependencies &&
|
||||
typeof data.optionalDependencies === "object" &&
|
||||
packageName in data.optionalDependencies
|
||||
) {
|
||||
return data.optionalDependencies[packageName];
|
||||
}
|
||||
if (
|
||||
data.dependencies &&
|
||||
typeof data.dependencies === "object" &&
|
||||
packageName in data.dependencies
|
||||
) {
|
||||
return data.dependencies[packageName];
|
||||
}
|
||||
if (
|
||||
data.peerDependencies &&
|
||||
typeof data.peerDependencies === "object" &&
|
||||
packageName in data.peerDependencies
|
||||
) {
|
||||
return data.peerDependencies[packageName];
|
||||
}
|
||||
if (
|
||||
data.devDependencies &&
|
||||
typeof data.devDependencies === "object" &&
|
||||
packageName in data.devDependencies
|
||||
) {
|
||||
return data.devDependencies[packageName];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ const path = require("path");
|
|||
/** @typedef {function(NodeJS.ErrnoException=, string[]=): void} StringArrayCallback */
|
||||
/** @typedef {function(NodeJS.ErrnoException=, string=): void} StringCallback */
|
||||
/** @typedef {function(NodeJS.ErrnoException=, NodeFsStats=): void} StatsCallback */
|
||||
/** @typedef {function((NodeJS.ErrnoException | Error)=, any=): void} ReadJsonCallback */
|
||||
|
||||
/**
|
||||
* @typedef {Object} OutputFileSystem
|
||||
|
@ -29,6 +30,7 @@ const path = require("path");
|
|||
/**
|
||||
* @typedef {Object} InputFileSystem
|
||||
* @property {function(string, BufferCallback): void} readFile
|
||||
* @property {(function(string, ReadJsonCallback): void)=} readJson
|
||||
* @property {function(string, StringArrayCallback): void} readdir
|
||||
* @property {function(string, StatsCallback): void} stat
|
||||
* @property {(function(string, StringCallback): void)=} realpath
|
||||
|
@ -181,3 +183,24 @@ const mkdirpSync = (fs, p) => {
|
|||
}
|
||||
};
|
||||
exports.mkdirpSync = mkdirpSync;
|
||||
|
||||
/**
|
||||
* @param {InputFileSystem} fs a file system
|
||||
* @param {string} p an absolute path
|
||||
* @param {ReadJsonCallback} callback callback
|
||||
* @returns {void}
|
||||
*/
|
||||
const readJson = (fs, p, callback) => {
|
||||
if ("readJson" in fs) return fs.readJson(p, callback);
|
||||
fs.readFile(p, (err, buf) => {
|
||||
if (err) return callback(err);
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(buf.toString("utf-8"));
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
return callback(null, data);
|
||||
});
|
||||
};
|
||||
exports.readJson = readJson;
|
||||
|
|
|
@ -63,8 +63,12 @@ module.exports = {
|
|||
require("../dependencies/ContextElementDependency"),
|
||||
"dependencies/CriticalDependencyWarning": () =>
|
||||
require("../dependencies/CriticalDependencyWarning"),
|
||||
"dependencies/DelegatedSourceDependency": () =>
|
||||
require("../dependencies/DelegatedSourceDependency"),
|
||||
"dependencies/DllEntryDependency": () =>
|
||||
require("../dependencies/DllEntryDependency"),
|
||||
"dependencies/EntryDependency": () =>
|
||||
require("../dependencies/EntryDependency"),
|
||||
"dependencies/ExportsInfoDependency": () =>
|
||||
require("../dependencies/ExportsInfoDependency"),
|
||||
"dependencies/HarmonyAcceptDependency": () =>
|
||||
|
@ -87,8 +91,6 @@ module.exports = {
|
|||
require("../dependencies/HarmonyImportSpecifierDependency"),
|
||||
"dependencies/ImportContextDependency": () =>
|
||||
require("../dependencies/ImportContextDependency"),
|
||||
"dependencies/ImportDependenciesBlock": () =>
|
||||
require("../dependencies/ImportDependenciesBlock"),
|
||||
"dependencies/ImportDependency": () =>
|
||||
require("../dependencies/ImportDependency"),
|
||||
"dependencies/ImportEagerDependency": () =>
|
||||
|
@ -144,7 +146,9 @@ module.exports = {
|
|||
require("../dependencies/WebAssemblyImportDependency"),
|
||||
"optimize/ConcatenatedModule": () =>
|
||||
require("../optimize/ConcatenatedModule"),
|
||||
DelegatedModule: () => require("../DelegatedModule"),
|
||||
DependenciesBlock: () => require("../DependenciesBlock"),
|
||||
DllModule: () => require("../DllModule"),
|
||||
ExternalModule: () => require("../ExternalModule"),
|
||||
Module: () => require("../Module"),
|
||||
ModuleBuildError: () => require("../ModuleBuildError"),
|
||||
|
|
|
@ -44,9 +44,11 @@ class WasmFinalizeExportsPlugin {
|
|||
connection.dependency
|
||||
);
|
||||
|
||||
for (const names of referencedExports) {
|
||||
for (const info of referencedExports) {
|
||||
const names = Array.isArray(info) ? info : info.name;
|
||||
if (names.length === 0) continue;
|
||||
const name = names[0];
|
||||
if (typeof name === "object") continue;
|
||||
// 3. and uses a func with an incompatible JS signature
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
|
|
|
@ -15,7 +15,6 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
constructor(runtimeRequirements, jsonpScript, linkPreload, linkPrefetch) {
|
||||
super("jsonp chunk loading", 10);
|
||||
this.runtimeRequirements = runtimeRequirements;
|
||||
this.jsonpScript = jsonpScript;
|
||||
this.linkPreload = linkPreload;
|
||||
this.linkPrefetch = linkPrefetch;
|
||||
}
|
||||
|
@ -24,7 +23,7 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
* @returns {string} runtime code
|
||||
*/
|
||||
generate() {
|
||||
const { compilation, chunk, jsonpScript, linkPreload, linkPrefetch } = this;
|
||||
const { compilation, chunk, linkPreload, linkPrefetch } = this;
|
||||
const { runtimeTemplate, chunkGraph, outputOptions } = compilation;
|
||||
const fn = RuntimeGlobals.ensureChunkHandlers;
|
||||
const withLoading = this.runtimeRequirements.has(
|
||||
|
@ -100,20 +99,31 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
"",
|
||||
"// start chunk loading",
|
||||
`var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(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) return installedChunkData[1];"
|
||||
"if(installedChunkData) {",
|
||||
Template.indent([
|
||||
"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);"
|
||||
]),
|
||||
"}"
|
||||
]),
|
||||
"}"
|
||||
]
|
||||
)};`,
|
||||
jsonpScript.call("", chunk),
|
||||
"document.head.appendChild(script);"
|
||||
`${RuntimeGlobals.loadScript}(url, loadingEnded, "chunk-" + chunkId);`
|
||||
]),
|
||||
"} else installedChunks[chunkId] = 0;"
|
||||
]),
|
||||
|
@ -174,16 +184,23 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
"waitingUpdateResolves[chunkId] = resolve;",
|
||||
"// start update chunk loading",
|
||||
`var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId);`,
|
||||
`var loadingEnded = ${runtimeTemplate.basicFunction("", [
|
||||
"// create error before stack unwound to get useful stacktrace later",
|
||||
"var error = new Error();",
|
||||
`var loadingEnded = ${runtimeTemplate.basicFunction("event", [
|
||||
"if(waitingUpdateResolves[chunkId]) {",
|
||||
Template.indent([
|
||||
"waitingUpdateResolves[chunkId] = undefined",
|
||||
"return reject;"
|
||||
"var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
|
||||
"var realSrc = event && event.target && event.target.src;",
|
||||
"error.message = 'Loading hot update chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';",
|
||||
"error.name = 'ChunkLoadError';",
|
||||
"error.type = errorType;",
|
||||
"error.request = realSrc;",
|
||||
"reject(error);"
|
||||
]),
|
||||
"}"
|
||||
])};`,
|
||||
jsonpScript.call("", chunk),
|
||||
"document.head.appendChild(script);"
|
||||
`${RuntimeGlobals.loadScript}(url, loadingEnded);`
|
||||
]
|
||||
)});`
|
||||
]),
|
||||
|
|
|
@ -242,6 +242,7 @@ class JsonpTemplatePlugin {
|
|||
onceForChunkSet.add(chunk);
|
||||
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
|
||||
set.add(RuntimeGlobals.hasOwnProperty);
|
||||
set.add(RuntimeGlobals.loadScript);
|
||||
compilation.addRuntimeModule(
|
||||
chunk,
|
||||
new JsonpChunkLoadingRuntimeModule(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "webpack",
|
||||
"version": "5.0.0-beta.16",
|
||||
"version": "5.0.0-beta.17",
|
||||
"author": "Tobias Koppers @sokra",
|
||||
"description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",
|
||||
"license": "MIT",
|
||||
|
@ -30,6 +30,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.2",
|
||||
"@babel/preset-react": "^7.10.1",
|
||||
"@types/jest": "^25.1.5",
|
||||
"@types/node": "^12.6.9",
|
||||
"babel-loader": "^8.0.6",
|
||||
|
@ -80,7 +81,7 @@
|
|||
"strip-ansi": "^6.0.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"toml": "^3.0.0",
|
||||
"tooling": "webpack/tooling#v1.7.0",
|
||||
"tooling": "webpack/tooling#v1.8.0",
|
||||
"ts-loader": "^6.0.4",
|
||||
"typescript": "^3.9.2",
|
||||
"url-loader": "^4.1.0",
|
||||
|
|
|
@ -254,8 +254,7 @@
|
|||
"library": {
|
||||
"$ref": "#/definitions/LibraryOptions"
|
||||
}
|
||||
},
|
||||
"required": ["import"]
|
||||
}
|
||||
},
|
||||
"EntryDynamic": {
|
||||
"description": "A Function returning an entry object, an entry string, an entry array or a promise to these things.",
|
||||
|
@ -312,8 +311,7 @@
|
|||
"$ref": "#/definitions/EntryDescription"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minProperties": 1
|
||||
}
|
||||
},
|
||||
"EntryStatic": {
|
||||
"description": "A static entry description.",
|
||||
|
@ -336,8 +334,7 @@
|
|||
"$ref": "#/definitions/EntryDescriptionNormalized"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minProperties": 1
|
||||
}
|
||||
},
|
||||
"EntryUnnamed": {
|
||||
"description": "An entry point without name.",
|
||||
|
@ -467,7 +464,8 @@
|
|||
"jsonp",
|
||||
"system",
|
||||
"promise",
|
||||
"import"
|
||||
"import",
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"FileCacheOptions": {
|
||||
|
|
|
@ -43,9 +43,18 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"packageName": {
|
||||
"description": "Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"requiredVersion": {
|
||||
"description": "Version requirement from module in share scope.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "No version requirement check.",
|
||||
"enum": [false]
|
||||
},
|
||||
{
|
||||
"description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.",
|
||||
"type": "string"
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
"jsonp",
|
||||
"system",
|
||||
"promise",
|
||||
"import"
|
||||
"import",
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"Remotes": {
|
||||
|
|
|
@ -103,7 +103,8 @@
|
|||
"jsonp",
|
||||
"system",
|
||||
"promise",
|
||||
"import"
|
||||
"import",
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"LibraryCustomUmdCommentObject": {
|
||||
|
@ -363,9 +364,18 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"packageName": {
|
||||
"description": "Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"requiredVersion": {
|
||||
"description": "Version requirement from module in share scope.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "No version requirement check.",
|
||||
"enum": [false]
|
||||
},
|
||||
{
|
||||
"description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.",
|
||||
"type": "string"
|
||||
|
|
|
@ -43,9 +43,18 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"packageName": {
|
||||
"description": "Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"requiredVersion": {
|
||||
"description": "Version requirement from module in share scope.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "No version requirement check.",
|
||||
"enum": [false]
|
||||
},
|
||||
{
|
||||
"description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.",
|
||||
"type": "string"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"definitions": {
|
||||
"Provides": {
|
||||
"description": "Modules that should be provided as shared modules to the share scope. When provided, property name is used as share key, otherwise share key is automatically inferred from request.",
|
||||
"description": "Modules that should be provided as shared modules to the share scope. When provided, property name is used to match modules, otherwise this is automatically inferred from share key.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
|
@ -31,8 +31,10 @@
|
|||
"description": "Include the provided module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"import": {
|
||||
"$ref": "#/definitions/ProvidesItem"
|
||||
"shareKey": {
|
||||
"description": "Key in the share scope under which the shared modules should be stored.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"shareScope": {
|
||||
"description": "Share scope name.",
|
||||
|
@ -55,11 +57,10 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["import"]
|
||||
}
|
||||
},
|
||||
"ProvidesItem": {
|
||||
"description": "Request to a module that should be provided as shared module to the share scope.",
|
||||
"description": "Request to a module that should be provided as shared module to the share scope (will be resolved when relative).",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
|
|
|
@ -43,9 +43,18 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"packageName": {
|
||||
"description": "Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"requiredVersion": {
|
||||
"description": "Version requirement from module in share scope.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "No version requirement check.",
|
||||
"enum": [false]
|
||||
},
|
||||
{
|
||||
"description": "Version as string. Can be prefixed with '^' or '~' for minimum matches. Each part of the version should be separated by a dot '.'.",
|
||||
"type": "string"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
const path = require("path");
|
||||
const { describeCases } = require("./ConfigTestCases.template");
|
||||
|
||||
describeCases({
|
||||
name: "ConfigCacheTestCases",
|
||||
cache: {
|
||||
type: "filesystem",
|
||||
managedPaths: [path.resolve(__dirname, "../node_modules")]
|
||||
}
|
||||
});
|
|
@ -0,0 +1,349 @@
|
|||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("graceful-fs");
|
||||
const vm = require("vm");
|
||||
const rimraf = require("rimraf");
|
||||
const checkArrayExpectation = require("./checkArrayExpectation");
|
||||
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
|
||||
const deprecationTracking = require("./helpers/deprecationTracking");
|
||||
const FakeDocument = require("./helpers/FakeDocument");
|
||||
|
||||
const webpack = require("..");
|
||||
const prepareOptions = require("./helpers/prepareOptions");
|
||||
|
||||
const casesPath = path.join(__dirname, "configCases");
|
||||
const categories = fs.readdirSync(casesPath).map(cat => {
|
||||
return {
|
||||
name: cat,
|
||||
tests: fs
|
||||
.readdirSync(path.join(casesPath, cat))
|
||||
.filter(folder => !folder.startsWith("_"))
|
||||
.sort()
|
||||
};
|
||||
});
|
||||
|
||||
const describeCases = config => {
|
||||
describe(config.name, () => {
|
||||
jest.setTimeout(20000);
|
||||
|
||||
for (const category of categories) {
|
||||
describe(category.name, () => {
|
||||
for (const testName of category.tests) {
|
||||
describe(testName, function () {
|
||||
const testDirectory = path.join(casesPath, category.name, testName);
|
||||
const filterPath = path.join(testDirectory, "test.filter.js");
|
||||
if (fs.existsSync(filterPath) && !require(filterPath)()) {
|
||||
describe.skip(testName, () => {
|
||||
it("filtered", () => {});
|
||||
});
|
||||
return;
|
||||
}
|
||||
const outBaseDir = path.join(__dirname, "js");
|
||||
const testSubPath = path.join(config.name, category.name, testName);
|
||||
const outputDirectory = path.join(outBaseDir, testSubPath);
|
||||
const cacheDirectory = path.join(outBaseDir, ".cache", testSubPath);
|
||||
let options, optionsArr, testConfig;
|
||||
beforeAll(() => {
|
||||
options = prepareOptions(
|
||||
require(path.join(testDirectory, "webpack.config.js")),
|
||||
{ testPath: outputDirectory }
|
||||
);
|
||||
optionsArr = [].concat(options);
|
||||
optionsArr.forEach((options, idx) => {
|
||||
if (!options.context) options.context = testDirectory;
|
||||
if (!options.mode) options.mode = "production";
|
||||
if (!options.optimization) options.optimization = {};
|
||||
if (options.optimization.minimize === undefined)
|
||||
options.optimization.minimize = false;
|
||||
if (!options.entry) options.entry = "./index.js";
|
||||
if (!options.target) options.target = "async-node";
|
||||
if (!options.output) options.output = {};
|
||||
if (!options.output.path) options.output.path = outputDirectory;
|
||||
if (typeof options.output.pathinfo === "undefined")
|
||||
options.output.pathinfo = true;
|
||||
if (!options.output.filename)
|
||||
options.output.filename = "bundle" + idx + ".js";
|
||||
if (config.cache) {
|
||||
options.cache = {
|
||||
cacheDirectory,
|
||||
name: `config-${idx}`,
|
||||
...config.cache
|
||||
};
|
||||
}
|
||||
});
|
||||
testConfig = {
|
||||
findBundle: function (i, options) {
|
||||
const ext = path.extname(options.output.filename);
|
||||
if (
|
||||
fs.existsSync(
|
||||
path.join(options.output.path, "bundle" + i + ext)
|
||||
)
|
||||
) {
|
||||
return "./bundle" + i + ext;
|
||||
}
|
||||
},
|
||||
timeout: 30000
|
||||
};
|
||||
try {
|
||||
// try to load a test file
|
||||
testConfig = Object.assign(
|
||||
testConfig,
|
||||
require(path.join(testDirectory, "test.config.js"))
|
||||
);
|
||||
} catch (e) {
|
||||
// ignored
|
||||
}
|
||||
if (testConfig.timeout) setDefaultTimeout(testConfig.timeout);
|
||||
});
|
||||
beforeAll(() => {
|
||||
rimraf.sync(cacheDirectory);
|
||||
});
|
||||
const handleFatalError = (err, done) => {
|
||||
const fakeStats = {
|
||||
errors: [
|
||||
{
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
}
|
||||
]
|
||||
};
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
fakeStats,
|
||||
"error",
|
||||
"Error",
|
||||
done
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Wait for uncaught errors to occur
|
||||
setTimeout(done, 200);
|
||||
return;
|
||||
};
|
||||
if (config.cache) {
|
||||
it(`${testName} should pre-compile to fill disk cache (1st)`, done => {
|
||||
rimraf.sync(outputDirectory);
|
||||
fs.mkdirSync(outputDirectory, { recursive: true });
|
||||
const deprecationTracker = deprecationTracking.start();
|
||||
webpack(options, err => {
|
||||
deprecationTracker();
|
||||
if (err) return handleFatalError(err, done);
|
||||
done();
|
||||
});
|
||||
}, 60000);
|
||||
it(`${testName} should pre-compile to fill disk cache (2nd)`, done => {
|
||||
rimraf.sync(outputDirectory);
|
||||
fs.mkdirSync(outputDirectory, { recursive: true });
|
||||
const deprecationTracker = deprecationTracking.start();
|
||||
webpack(options, err => {
|
||||
deprecationTracker();
|
||||
if (err) return handleFatalError(err, done);
|
||||
done();
|
||||
});
|
||||
}, 20000);
|
||||
}
|
||||
it(`${testName} should compile`, done => {
|
||||
rimraf.sync(outputDirectory);
|
||||
fs.mkdirSync(outputDirectory, { recursive: true });
|
||||
const deprecationTracker = deprecationTracking.start();
|
||||
webpack(options, (err, stats) => {
|
||||
const deprecations = deprecationTracker();
|
||||
if (err) return handleFatalError(err, done);
|
||||
const statOptions = {
|
||||
preset: "verbose",
|
||||
colors: false
|
||||
};
|
||||
fs.mkdirSync(outputDirectory, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(outputDirectory, "stats.txt"),
|
||||
stats.toString(statOptions),
|
||||
"utf-8"
|
||||
);
|
||||
const jsonStats = stats.toJson({
|
||||
errorDetails: true
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(outputDirectory, "stats.json"),
|
||||
JSON.stringify(jsonStats, null, 2),
|
||||
"utf-8"
|
||||
);
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
jsonStats,
|
||||
"error",
|
||||
"Error",
|
||||
done
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
jsonStats,
|
||||
"warning",
|
||||
"Warning",
|
||||
done
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
{ deprecations },
|
||||
"deprecation",
|
||||
"Deprecation",
|
||||
done
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const globalContext = {
|
||||
console: console,
|
||||
expect: expect,
|
||||
setTimeout: setTimeout,
|
||||
clearTimeout: clearTimeout,
|
||||
document: new FakeDocument(),
|
||||
location: {
|
||||
href: "https://test.cases/path/index.html",
|
||||
origin: "https://test.cases",
|
||||
toString() {
|
||||
return "https://test.cases/path/index.html";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const requireCache = Object.create(null);
|
||||
function _require(currentDirectory, options, module) {
|
||||
if (Array.isArray(module) || /^\.\.?\//.test(module)) {
|
||||
let content;
|
||||
let p;
|
||||
if (Array.isArray(module)) {
|
||||
p = path.join(currentDirectory, ".array-require.js");
|
||||
content = `module.exports = (${module
|
||||
.map(arg => {
|
||||
return `require(${JSON.stringify(`./${arg}`)})`;
|
||||
})
|
||||
.join(", ")});`;
|
||||
} else {
|
||||
p = path.join(currentDirectory, module);
|
||||
content = fs.readFileSync(p, "utf-8");
|
||||
}
|
||||
if (p in requireCache) {
|
||||
return requireCache[p].exports;
|
||||
}
|
||||
const m = {
|
||||
exports: {}
|
||||
};
|
||||
requireCache[p] = m;
|
||||
let runInNewContext = false;
|
||||
const moduleScope = {
|
||||
require: _require.bind(null, path.dirname(p), options),
|
||||
importScripts: _require.bind(
|
||||
null,
|
||||
path.dirname(p),
|
||||
options
|
||||
),
|
||||
module: m,
|
||||
exports: m.exports,
|
||||
__dirname: path.dirname(p),
|
||||
__filename: p,
|
||||
it: _it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
expect,
|
||||
jest,
|
||||
_globalAssign: { expect },
|
||||
__STATS__: jsonStats,
|
||||
nsObj: m => {
|
||||
Object.defineProperty(m, Symbol.toStringTag, {
|
||||
value: "Module"
|
||||
});
|
||||
return m;
|
||||
}
|
||||
};
|
||||
if (
|
||||
options.target === "web" ||
|
||||
options.target === "webworker"
|
||||
) {
|
||||
moduleScope.window = globalContext;
|
||||
moduleScope.self = globalContext;
|
||||
runInNewContext = true;
|
||||
}
|
||||
if (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(moduleScope);
|
||||
}
|
||||
const args = Object.keys(moduleScope).join(", ");
|
||||
if (!runInNewContext)
|
||||
content = `Object.assign(global, _globalAssign); ${content}`;
|
||||
const code = `(function({${args}}) {${content}\n})`;
|
||||
const fn = runInNewContext
|
||||
? vm.runInNewContext(code, globalContext, p)
|
||||
: vm.runInThisContext(code, p);
|
||||
fn.call(m.exports, moduleScope);
|
||||
return m.exports;
|
||||
} else if (
|
||||
testConfig.modules &&
|
||||
module in testConfig.modules
|
||||
) {
|
||||
return testConfig.modules[module];
|
||||
} else return require(module);
|
||||
}
|
||||
let filesCount = 0;
|
||||
|
||||
if (testConfig.noTests) return process.nextTick(done);
|
||||
if (testConfig.beforeExecute) testConfig.beforeExecute();
|
||||
const results = [];
|
||||
for (let i = 0; i < optionsArr.length; i++) {
|
||||
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
|
||||
if (bundlePath) {
|
||||
filesCount++;
|
||||
results.push(
|
||||
_require(outputDirectory, optionsArr[i], bundlePath)
|
||||
);
|
||||
}
|
||||
}
|
||||
// give a free pass to compilation that generated an error
|
||||
if (
|
||||
!jsonStats.errors.length &&
|
||||
filesCount !== optionsArr.length
|
||||
) {
|
||||
return done(
|
||||
new Error(
|
||||
"Should have found at least one bundle file per webpack config"
|
||||
)
|
||||
);
|
||||
}
|
||||
Promise.all(results)
|
||||
.then(() => {
|
||||
if (testConfig.afterExecute) testConfig.afterExecute();
|
||||
if (getNumberOfTests() < filesCount) {
|
||||
return done(new Error("No tests exported by test case"));
|
||||
}
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
const {
|
||||
it: _it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
setDefaultTimeout,
|
||||
getNumberOfTests
|
||||
} = createLazyTestEnv(jasmine.getEnv(), 10000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.describeCases = describeCases;
|
|
@ -1,325 +1,5 @@
|
|||
"use strict";
|
||||
const { describeCases } = require("./ConfigTestCases.template");
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("graceful-fs");
|
||||
const vm = require("vm");
|
||||
const rimraf = require("rimraf");
|
||||
const checkArrayExpectation = require("./checkArrayExpectation");
|
||||
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
|
||||
const deprecationTracking = require("./helpers/deprecationTracking");
|
||||
const FakeDocument = require("./helpers/FakeDocument");
|
||||
|
||||
const webpack = require("..");
|
||||
const prepareOptions = require("./helpers/prepareOptions");
|
||||
|
||||
describe("ConfigTestCases", () => {
|
||||
const casesPath = path.join(__dirname, "configCases");
|
||||
let categories = fs.readdirSync(casesPath);
|
||||
|
||||
jest.setTimeout(20000);
|
||||
|
||||
categories = categories.map(cat => {
|
||||
return {
|
||||
name: cat,
|
||||
tests: fs
|
||||
.readdirSync(path.join(casesPath, cat))
|
||||
.filter(folder => {
|
||||
return folder.indexOf("_") < 0;
|
||||
})
|
||||
.sort()
|
||||
.filter(testName => {
|
||||
const testDirectory = path.join(casesPath, cat, testName);
|
||||
const filterPath = path.join(testDirectory, "test.filter.js");
|
||||
if (fs.existsSync(filterPath) && !require(filterPath)()) {
|
||||
describe.skip(testName, () => {
|
||||
it("filtered", () => {});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
};
|
||||
});
|
||||
categories.forEach(category => {
|
||||
describe(category.name, () => {
|
||||
category.tests.forEach(testName => {
|
||||
describe(testName, function () {
|
||||
const testDirectory = path.join(casesPath, category.name, testName);
|
||||
const outputDirectory = path.join(
|
||||
__dirname,
|
||||
"js",
|
||||
"config",
|
||||
category.name,
|
||||
testName
|
||||
);
|
||||
it(
|
||||
testName + " should compile",
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
const done = err => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
};
|
||||
rimraf.sync(outputDirectory);
|
||||
fs.mkdirSync(outputDirectory, { recursive: true });
|
||||
const options = prepareOptions(
|
||||
require(path.join(testDirectory, "webpack.config.js")),
|
||||
{ testPath: outputDirectory }
|
||||
);
|
||||
const optionsArr = [].concat(options);
|
||||
optionsArr.forEach((options, idx) => {
|
||||
if (!options.context) options.context = testDirectory;
|
||||
if (!options.mode) options.mode = "production";
|
||||
if (!options.optimization) options.optimization = {};
|
||||
if (options.optimization.minimize === undefined)
|
||||
options.optimization.minimize = false;
|
||||
if (!options.entry) options.entry = "./index.js";
|
||||
if (!options.target) options.target = "async-node";
|
||||
if (!options.output) options.output = {};
|
||||
if (!options.output.path)
|
||||
options.output.path = outputDirectory;
|
||||
if (typeof options.output.pathinfo === "undefined")
|
||||
options.output.pathinfo = true;
|
||||
if (!options.output.filename)
|
||||
options.output.filename = "bundle" + idx + ".js";
|
||||
});
|
||||
let testConfig = {
|
||||
findBundle: function (i, options) {
|
||||
const ext = path.extname(options.output.filename);
|
||||
if (
|
||||
fs.existsSync(
|
||||
path.join(options.output.path, "bundle" + i + ext)
|
||||
)
|
||||
) {
|
||||
return "./bundle" + i + ext;
|
||||
}
|
||||
},
|
||||
timeout: 30000
|
||||
};
|
||||
try {
|
||||
// try to load a test file
|
||||
testConfig = Object.assign(
|
||||
testConfig,
|
||||
require(path.join(testDirectory, "test.config.js"))
|
||||
);
|
||||
} catch (e) {
|
||||
// ignored
|
||||
}
|
||||
if (testConfig.timeout) setDefaultTimeout(testConfig.timeout);
|
||||
|
||||
const deprecationTracker = deprecationTracking.start();
|
||||
webpack(options, (err, stats) => {
|
||||
if (err) {
|
||||
const fakeStats = {
|
||||
errors: [
|
||||
{
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
}
|
||||
]
|
||||
};
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
fakeStats,
|
||||
"error",
|
||||
"Error",
|
||||
done
|
||||
)
|
||||
)
|
||||
return;
|
||||
// Wait for uncaught errors to occur
|
||||
return setTimeout(done, 200);
|
||||
}
|
||||
const statOptions = {
|
||||
preset: "verbose",
|
||||
colors: false
|
||||
};
|
||||
fs.mkdirSync(outputDirectory, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(outputDirectory, "stats.txt"),
|
||||
stats.toString(statOptions),
|
||||
"utf-8"
|
||||
);
|
||||
const jsonStats = stats.toJson({
|
||||
errorDetails: true
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(outputDirectory, "stats.json"),
|
||||
JSON.stringify(jsonStats, null, 2),
|
||||
"utf-8"
|
||||
);
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
jsonStats,
|
||||
"error",
|
||||
"Error",
|
||||
done
|
||||
)
|
||||
)
|
||||
return;
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
jsonStats,
|
||||
"warning",
|
||||
"Warning",
|
||||
done
|
||||
)
|
||||
)
|
||||
return;
|
||||
const deprecations = deprecationTracker();
|
||||
if (
|
||||
checkArrayExpectation(
|
||||
testDirectory,
|
||||
{ deprecations },
|
||||
"deprecation",
|
||||
"Deprecation",
|
||||
done
|
||||
)
|
||||
)
|
||||
return;
|
||||
|
||||
const globalContext = {
|
||||
console: console,
|
||||
expect: expect,
|
||||
setTimeout: setTimeout,
|
||||
clearTimeout: clearTimeout,
|
||||
document: new FakeDocument(),
|
||||
location: {
|
||||
href: "https://test.cases/path/index.html",
|
||||
origin: "https://test.cases",
|
||||
toString() {
|
||||
return "https://test.cases/path/index.html";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const requireCache = Object.create(null);
|
||||
function _require(currentDirectory, options, module) {
|
||||
if (Array.isArray(module) || /^\.\.?\//.test(module)) {
|
||||
let content;
|
||||
let p;
|
||||
if (Array.isArray(module)) {
|
||||
p = path.join(currentDirectory, ".array-require.js");
|
||||
content = `module.exports = (${module
|
||||
.map(arg => {
|
||||
return `require(${JSON.stringify(`./${arg}`)})`;
|
||||
})
|
||||
.join(", ")});`;
|
||||
} else {
|
||||
p = path.join(currentDirectory, module);
|
||||
content = fs.readFileSync(p, "utf-8");
|
||||
}
|
||||
if (p in requireCache) {
|
||||
return requireCache[p].exports;
|
||||
}
|
||||
const m = {
|
||||
exports: {}
|
||||
};
|
||||
requireCache[p] = m;
|
||||
let runInNewContext = false;
|
||||
const moduleScope = {
|
||||
require: _require.bind(null, path.dirname(p), options),
|
||||
importScripts: _require.bind(
|
||||
null,
|
||||
path.dirname(p),
|
||||
options
|
||||
),
|
||||
module: m,
|
||||
exports: m.exports,
|
||||
__dirname: path.dirname(p),
|
||||
__filename: p,
|
||||
it: _it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
expect,
|
||||
jest,
|
||||
_globalAssign: { expect },
|
||||
__STATS__: jsonStats,
|
||||
nsObj: m => {
|
||||
Object.defineProperty(m, Symbol.toStringTag, {
|
||||
value: "Module"
|
||||
});
|
||||
return m;
|
||||
}
|
||||
};
|
||||
if (
|
||||
options.target === "web" ||
|
||||
options.target === "webworker"
|
||||
) {
|
||||
moduleScope.window = globalContext;
|
||||
moduleScope.self = globalContext;
|
||||
runInNewContext = true;
|
||||
}
|
||||
if (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(moduleScope);
|
||||
}
|
||||
const args = Object.keys(moduleScope).join(", ");
|
||||
if (!runInNewContext)
|
||||
content = `Object.assign(global, _globalAssign); ${content}`;
|
||||
const code = `(function({${args}}) {${content}\n})`;
|
||||
const fn = runInNewContext
|
||||
? vm.runInNewContext(code, globalContext, p)
|
||||
: vm.runInThisContext(code, p);
|
||||
fn.call(m.exports, moduleScope);
|
||||
return m.exports;
|
||||
} else if (
|
||||
testConfig.modules &&
|
||||
module in testConfig.modules
|
||||
) {
|
||||
return testConfig.modules[module];
|
||||
} else return require(module);
|
||||
}
|
||||
let filesCount = 0;
|
||||
|
||||
if (testConfig.noTests) return process.nextTick(done);
|
||||
if (testConfig.beforeExecute) testConfig.beforeExecute();
|
||||
const results = [];
|
||||
for (let i = 0; i < optionsArr.length; i++) {
|
||||
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
|
||||
if (bundlePath) {
|
||||
filesCount++;
|
||||
results.push(
|
||||
_require(outputDirectory, optionsArr[i], bundlePath)
|
||||
);
|
||||
}
|
||||
}
|
||||
// give a free pass to compilation that generated an error
|
||||
if (
|
||||
!jsonStats.errors.length &&
|
||||
filesCount !== optionsArr.length
|
||||
)
|
||||
return done(
|
||||
new Error(
|
||||
"Should have found at least one bundle file per webpack config"
|
||||
)
|
||||
);
|
||||
Promise.all(results)
|
||||
.then(() => {
|
||||
if (testConfig.afterExecute) testConfig.afterExecute();
|
||||
if (getNumberOfTests() < filesCount) {
|
||||
return done(
|
||||
new Error("No tests exported by test case")
|
||||
);
|
||||
}
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const {
|
||||
it: _it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
setDefaultTimeout,
|
||||
getNumberOfTests
|
||||
} = createLazyTestEnv(jasmine.getEnv(), 10000);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describeCases({
|
||||
name: "ConfigTestCases"
|
||||
});
|
||||
|
|
|
@ -138,6 +138,11 @@ const describeCases = config => {
|
|||
_attrs: {},
|
||||
setAttribute(name, value) {
|
||||
this._attrs[name] = value;
|
||||
},
|
||||
parentNode: {
|
||||
removeChild(node) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
@ -153,6 +158,7 @@ const describeCases = config => {
|
|||
},
|
||||
getElementsByTagName(name) {
|
||||
if (name === "head") return [this.head];
|
||||
if (name === "script") return [];
|
||||
throw new Error("Not supported");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,8 @@ export default ${files.map((_, i) => `f${i}`).join(" + ")};
|
|||
"index.js":
|
||||
"export default import('container/src/exposed').then(m => m.default);",
|
||||
"exposed.js": "import lib from 'lib'; export default 21 + lib;",
|
||||
"lib.js": "export default 21"
|
||||
"lib.js": "export default 20",
|
||||
"lib2.js": "export default 21"
|
||||
};
|
||||
await updateSrc(data);
|
||||
const configAdditions = {
|
||||
|
@ -157,8 +158,12 @@ export default ${files.map((_, i) => `f${i}`).join(" + ")};
|
|||
lib: {
|
||||
import: "./src/lib",
|
||||
shareKey: "lib",
|
||||
version: "1.2.3",
|
||||
version: "1.2.0",
|
||||
requiredVersion: "^1.0.0"
|
||||
},
|
||||
"./src/lib2": {
|
||||
shareKey: "lib",
|
||||
version: "1.2.3"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -217,10 +217,10 @@ describe("Stats", () => {
|
|||
"comparedForEmit": false,
|
||||
"emitted": true,
|
||||
"info": Object {
|
||||
"size": 1865,
|
||||
"size": 2207,
|
||||
},
|
||||
"name": "entryB.js",
|
||||
"size": 1865,
|
||||
"size": 2207,
|
||||
},
|
||||
],
|
||||
"assetsByChunkName": Object {
|
||||
|
|
|
@ -322,4 +322,4 @@ const describeCases = config => {
|
|||
});
|
||||
};
|
||||
|
||||
module.exports.describeCases = describeCases;
|
||||
exports.describeCases = describeCases;
|
||||
|
|
|
@ -490,6 +490,7 @@ Object {
|
|||
"system",
|
||||
"promise",
|
||||
"import",
|
||||
"script",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ConfigCacheTestCases custom-modules json-custom exported tests should transform toml to json 1`] = `
|
||||
Object {
|
||||
"owner": Object {
|
||||
"bio": "GitHub Cofounder & CEO
|
||||
Likes tater tots and beer.",
|
||||
"dob": "1979-05-27T07:32:00.000Z",
|
||||
"name": "Tom Preston-Werner",
|
||||
"organization": "GitHub",
|
||||
},
|
||||
"title": "TOML Example",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`ConfigCacheTestCases records issue-2991 exported tests should write relative paths to records 1`] = `
|
||||
"{
|
||||
\\"chunks\\": {
|
||||
\\"byName\\": {
|
||||
\\"main\\": 179
|
||||
},
|
||||
\\"bySource\\": {
|
||||
\\"0 main\\": 179
|
||||
},
|
||||
\\"usedIds\\": [
|
||||
179
|
||||
]
|
||||
},
|
||||
\\"modules\\": {
|
||||
\\"byIdentifier\\": {
|
||||
\\"./test.js\\": 393,
|
||||
\\"external \\\\\\"fs\\\\\\"\\": 747,
|
||||
\\"external \\\\\\"path\\\\\\"\\": 622,
|
||||
\\"ignored|pkgs/somepackage/foo\\": 713
|
||||
},
|
||||
\\"usedIds\\": [
|
||||
393,
|
||||
622,
|
||||
713,
|
||||
747
|
||||
]
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`ConfigCacheTestCases records issue-7339 exported tests should write relative dynamic-require paths to records 1`] = `
|
||||
"{
|
||||
\\"chunks\\": {
|
||||
\\"byName\\": {
|
||||
\\"main\\": 179
|
||||
},
|
||||
\\"bySource\\": {
|
||||
\\"0 main\\": 179
|
||||
},
|
||||
\\"usedIds\\": [
|
||||
179
|
||||
]
|
||||
},
|
||||
\\"modules\\": {
|
||||
\\"byIdentifier\\": {
|
||||
\\"./dependencies/bar.js\\": 379,
|
||||
\\"./dependencies/foo.js\\": 117,
|
||||
\\"./dependencies|sync|/^\\\\\\\\.\\\\\\\\/.*$/\\": 412,
|
||||
\\"./test.js\\": 393,
|
||||
\\"external \\\\\\"fs\\\\\\"\\": 747,
|
||||
\\"external \\\\\\"path\\\\\\"\\": 622
|
||||
},
|
||||
\\"usedIds\\": [
|
||||
117,
|
||||
379,
|
||||
393,
|
||||
412,
|
||||
622,
|
||||
747
|
||||
]
|
||||
}
|
||||
}"
|
||||
`;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
export const c = "c";
|
||||
|
||||
export const d = "d";
|
||||
|
||||
export const longnameforexport = "longnameforexport";
|
||||
|
||||
export default "default2";
|
||||
|
||||
export const usedExports = __webpack_exports_info__.usedExports;
|
|
@ -0,0 +1,7 @@
|
|||
export const c = "c";
|
||||
|
||||
export const d = "d";
|
||||
|
||||
export default "default2";
|
||||
|
||||
export const usedExports = __webpack_exports_info__.usedExports;
|
|
@ -0,0 +1,7 @@
|
|||
export const a = "a";
|
||||
|
||||
export const b = "b";
|
||||
|
||||
export default "default";
|
||||
|
||||
export const usedExports = __webpack_exports_info__.usedExports;
|
|
@ -1,48 +1,56 @@
|
|||
it("should be able to use eager mode", function() {
|
||||
it("should be able to use eager mode", function () {
|
||||
function load(name) {
|
||||
return import(/* webpackMode: "eager" */ "./dir1/" + name);
|
||||
}
|
||||
return testChunkLoading(load, true, true);
|
||||
});
|
||||
|
||||
it("should be able to use lazy-once mode", function() {
|
||||
it("should be able to use lazy-once mode", function () {
|
||||
function load(name) {
|
||||
return import(/* webpackMode: "lazy-once" */ "./dir2/" + name);
|
||||
}
|
||||
return testChunkLoading(load, false, true);
|
||||
});
|
||||
|
||||
it("should be able to use lazy-once mode with name", function() {
|
||||
it("should be able to use lazy-once mode with name", function () {
|
||||
function load(name) {
|
||||
return import(/* webpackMode: "lazy-once", webpackChunkName: "name-lazy-once" */ "./dir3/" + name);
|
||||
return import(
|
||||
/* webpackMode: "lazy-once", webpackChunkName: "name-lazy-once" */ "./dir3/" +
|
||||
name
|
||||
);
|
||||
}
|
||||
return testChunkLoading(load, false, true);
|
||||
});
|
||||
|
||||
it("should be able to use lazy mode", function() {
|
||||
it("should be able to use lazy mode", function () {
|
||||
function load(name) {
|
||||
return import(/* webpackMode: "lazy" */ "./dir4/" + name);
|
||||
}
|
||||
return testChunkLoading(load, false, false);
|
||||
});
|
||||
|
||||
it("should be able to use lazy mode with name", function() {
|
||||
it("should be able to use lazy mode with name", function () {
|
||||
function load(name) {
|
||||
return import(/* webpackMode: "lazy", webpackChunkName: "name-lazy" */ "./dir5/" + name);
|
||||
return import(
|
||||
/* webpackMode: "lazy", webpackChunkName: "name-lazy" */ "./dir5/" + name
|
||||
);
|
||||
}
|
||||
return testChunkLoading(load, false, false);
|
||||
});
|
||||
|
||||
it("should be able to use lazy mode with name and placeholder", function() {
|
||||
it("should be able to use lazy mode with name and placeholder", function () {
|
||||
function load(name) {
|
||||
return import(/* webpackMode: "lazy", webpackChunkName: "name-lazy-[request]" */ "./dir6/" + name);
|
||||
return import(
|
||||
/* webpackMode: "lazy", webpackChunkName: "name-lazy-[request]" */ "./dir6/" +
|
||||
name
|
||||
);
|
||||
}
|
||||
return testChunkLoading(load, false, false);
|
||||
});
|
||||
|
||||
it("should be able to combine chunks by name", function() {
|
||||
it("should be able to combine chunks by name", function () {
|
||||
function load(name) {
|
||||
switch(name) {
|
||||
switch (name) {
|
||||
case "a":
|
||||
return import(/* webpackMode: "eager" */ "./dir7/a");
|
||||
case "b":
|
||||
|
@ -58,19 +66,19 @@ it("should be able to combine chunks by name", function() {
|
|||
return testChunkLoading(load, false, true);
|
||||
});
|
||||
|
||||
it("should be able to use weak mode", function() {
|
||||
it("should be able to use weak mode", function () {
|
||||
function load(name) {
|
||||
return import(/* webpackMode: "weak" */ "./dir8/" + name);
|
||||
}
|
||||
require("./dir8/a") // chunks served manually by the user
|
||||
require("./dir8/b")
|
||||
require("./dir8/c")
|
||||
require("./dir8/a"); // chunks served manually by the user
|
||||
require("./dir8/b");
|
||||
require("./dir8/c");
|
||||
return testChunkLoading(load, true, true);
|
||||
});
|
||||
|
||||
it("should be able to use weak mode (without context)", function() {
|
||||
it("should be able to use weak mode (without context)", function () {
|
||||
function load(name) {
|
||||
switch(name) {
|
||||
switch (name) {
|
||||
case "a":
|
||||
return import(/* webpackMode: "weak" */ "./dir9/a");
|
||||
case "b":
|
||||
|
@ -81,54 +89,134 @@ it("should be able to use weak mode (without context)", function() {
|
|||
throw new Error("Unexcepted test data");
|
||||
}
|
||||
}
|
||||
require("./dir9/a") // chunks served manually by the user
|
||||
require("./dir9/b")
|
||||
require("./dir9/c")
|
||||
require("./dir9/a"); // chunks served manually by the user
|
||||
require("./dir9/b");
|
||||
require("./dir9/c");
|
||||
return testChunkLoading(load, true, true);
|
||||
});
|
||||
|
||||
it("should not find module when mode is weak and chunk not served elsewhere", function() {
|
||||
it("should not find module when mode is weak and chunk not served elsewhere", function () {
|
||||
var name = "a";
|
||||
return import(/* webpackMode: "weak" */ "./dir10/" + name)
|
||||
.catch(function(e) {
|
||||
expect(e).toMatchObject({ message: /not available/, code: /MODULE_NOT_FOUND/ });
|
||||
return import(/* webpackMode: "weak" */ "./dir10/" + name).catch(function (
|
||||
e
|
||||
) {
|
||||
expect(e).toMatchObject({
|
||||
message: /not available/,
|
||||
code: /MODULE_NOT_FOUND/
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should not find module when mode is weak and chunk not served elsewhere (without context)", function() {
|
||||
return import(/* webpackMode: "weak" */ "./dir11/a")
|
||||
.catch(function(e) {
|
||||
expect(e).toMatchObject({ message: /not available/, code: /MODULE_NOT_FOUND/ });
|
||||
it("should not find module when mode is weak and chunk not served elsewhere (without context)", function () {
|
||||
return import(/* webpackMode: "weak" */ "./dir11/a").catch(function (e) {
|
||||
expect(e).toMatchObject({
|
||||
message: /not available/,
|
||||
code: /MODULE_NOT_FOUND/
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should contain only one export from webpackExports from module", function () {
|
||||
return import(/* webpackExports: "usedExports" */ "./dir12/a?1").then(
|
||||
module => {
|
||||
expect(module.usedExports).toEqual(["usedExports"]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("should contain only webpackExports from module", function () {
|
||||
return import(
|
||||
/* webpackExports: ["a", "usedExports", "b"] */ "./dir12/a?2"
|
||||
).then(module => {
|
||||
expect(module.usedExports).toEqual(["a", "b", "usedExports"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("should contain only webpackExports from module in eager mode", function () {
|
||||
return import(
|
||||
/*
|
||||
webpackMode: "eager",
|
||||
webpackExports: ["a", "usedExports", "b"]
|
||||
*/ "./dir12/a?3"
|
||||
).then(module => {
|
||||
expect(module.usedExports).toEqual(["a", "b", "usedExports"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("should contain webpackExports from module in weak mode", function () {
|
||||
require.resolve("./dir12/a?4");
|
||||
return import(
|
||||
/*
|
||||
webpackMode: "weak",
|
||||
webpackExports: ["a", "usedExports", "b"]
|
||||
*/ "./dir12/a?4"
|
||||
).then(module => {
|
||||
expect(module.usedExports).toEqual(["a", "b", "usedExports"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not mangle webpackExports from module", function () {
|
||||
return import(/* webpackExports: "longnameforexport" */ "./dir12/a?5").then(
|
||||
module => {
|
||||
expect(module).toHaveProperty("longnameforexport");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("should not mangle default webpackExports from module", function () {
|
||||
return import(/* webpackExports: "default" */ "./dir12/a?6").then(module => {
|
||||
expect(module).toHaveProperty("default");
|
||||
});
|
||||
});
|
||||
|
||||
it("should contain only webpackExports from module in context mode", function () {
|
||||
const x = "b";
|
||||
return import(/* webpackExports: "usedExports" */ `./dir13/${x}`).then(
|
||||
module => {
|
||||
expect(module.usedExports).toEqual(["usedExports"]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function testChunkLoading(load, expectedSyncInitial, expectedSyncRequested) {
|
||||
var sync = false;
|
||||
var syncInitial = true;
|
||||
var p = Promise.all([load("a"), load("b")]).then(function() {
|
||||
var p = Promise.all([load("a"), load("b")]).then(function () {
|
||||
expect(syncInitial).toBe(expectedSyncInitial);
|
||||
sync = true;
|
||||
var p = Promise.all([
|
||||
load("a").then(function(a) {
|
||||
expect(a).toEqual(nsObj({
|
||||
default: "a"
|
||||
}));
|
||||
load("a").then(function (a) {
|
||||
expect(a).toEqual(
|
||||
nsObj({
|
||||
default: "a"
|
||||
})
|
||||
);
|
||||
expect(sync).toBe(true);
|
||||
}),
|
||||
load("c").then(function(c) {
|
||||
expect(c).toEqual(nsObj({
|
||||
default: "c"
|
||||
}));
|
||||
load("c").then(function (c) {
|
||||
expect(c).toEqual(
|
||||
nsObj({
|
||||
default: "c"
|
||||
})
|
||||
);
|
||||
expect(sync).toBe(expectedSyncRequested);
|
||||
})
|
||||
]);
|
||||
Promise.resolve().then(function(){}).then(function(){}).then(function(){}).then(function(){
|
||||
sync = false;
|
||||
});
|
||||
Promise.resolve()
|
||||
.then(function () {})
|
||||
.then(function () {})
|
||||
.then(function () {})
|
||||
.then(function () {
|
||||
sync = false;
|
||||
});
|
||||
return p;
|
||||
});
|
||||
Promise.resolve().then(function(){}).then(function(){}).then(function(){}).then(function(){
|
||||
syncInitial = false;
|
||||
});
|
||||
Promise.resolve()
|
||||
.then(function () {})
|
||||
.then(function () {})
|
||||
.then(function () {})
|
||||
.then(function () {
|
||||
syncInitial = false;
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,12 @@ module.exports = {
|
|||
external: "./container.js"
|
||||
}
|
||||
},
|
||||
shared: { react: { version: false } }
|
||||
shared: {
|
||||
react: {
|
||||
version: false,
|
||||
requiredVersion: false
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// eslint-disable-next-line node/no-unpublished-require
|
||||
const { ModuleFederationPlugin } = require("../../../../").container;
|
||||
|
||||
/** @type {import("../../../../").Configuration} */
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue