├── .gitignore
├── .travis.yml
├── README.md
├── __test__
├── build.js
├── fixtures
│ ├── app.js
│ ├── foo.js
│ ├── project1
│ │ ├── index.js
│ │ └── webpack.config.js
│ └── project2
│ │ ├── index.js
│ │ └── webpack.config.js
└── index.spec.js
├── babel.config.js
├── examples
├── website1
│ ├── package.json
│ ├── src
│ │ ├── App.jsx
│ │ ├── Footer.jsx
│ │ ├── HelloWorld.jsx
│ │ ├── bootstrap.jsx
│ │ ├── index.js
│ │ └── template.html
│ ├── webpack.config.js
│ └── yarn.lock
└── website2
│ ├── package.json
│ ├── src
│ ├── App.jsx
│ ├── Footer.jsx
│ ├── HelloWorld.jsx
│ ├── Title.jsx
│ ├── bootstrap.jsx
│ ├── index.js
│ └── template.html
│ ├── webpack.config.js
│ └── yarn.lock
├── license
├── package.json
├── src
├── ContainerEntryDependency.js
├── ContainerEntryModule.js
├── ContainerEntryModuleFactory.js
├── ContainerExposedDependency.js
├── ModuleFederationPlugin.js
├── RemoteModule.js
├── SharedModule.js
├── SharedModuleFactoryPlugin.js
├── index.js
├── package.json
└── webpack
│ └── lib
│ ├── ModuleFactory.js
│ ├── RuntimeGlobals.js
│ └── propertyAccess.js
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | .tmp
4 | dist
5 | !src/webpack/lib
6 | .idea
7 | yarn.lock
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 12
4 | before_script:
5 | - yarn install
6 | - yarn build
7 | script: yarn test
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/alibaba/module-federation4)
2 |
3 | ## module-federation4
4 |
5 | `webpack-plugin-module-federation` for Webpack4, backport from https://github.com/ScriptedAlchemy/webpack-external-import
6 |
7 | ## State
8 |
9 | not production ready at present.
10 |
11 | ## Usage
12 |
13 | ```shell
14 | npm install --save-dev webpack-plugin-module-federation
15 | ```
16 |
17 | Configure your `webpack.config.js`
18 |
19 | ```js
20 | const ModuleFederationPlugin = require('webpack-plugin-module-federation');
21 |
22 | module.exports = {
23 | output: {
24 | publicPath: 'http://localhost:3002/',
25 | },
26 | plugins: [
27 | new ModuleFederationPlugin({
28 | name: '_federation_website2',
29 | library: 'website2',
30 | filename: 'remoteEntry.js',
31 | libraryTarget: 'global',
32 | remotes: {
33 | 'website1': 'website1'
34 | },
35 | expose: {
36 | Title: './src/Title',
37 | App: './src/App'
38 | },
39 | }),
40 | ]
41 | };
42 | ```
43 |
44 | ## Import module from remote
45 |
46 | In remote project, configure `webpack.config.js`.
47 |
48 | ```js
49 | const ModuleFederationPlugin = require('webpack-plugin-module-federation');
50 |
51 | module.exports = {
52 | output: {
53 | publicPath: 'http://localhost:3001/',
54 | },
55 | plugins: [
56 | new ModuleFederationPlugin({
57 | name: '_federation_website1',
58 | library: 'website1',
59 | filename: 'remoteEntry.js',
60 | libraryTarget: 'global',
61 | remotes: {
62 | 'website2': '_federation_website2'
63 | },
64 | expose: {
65 | App: './src/App'
66 | },
67 | }),
68 | ]
69 | };
70 | ```
71 |
72 | Add `remoteEntry` in your HTML
73 |
74 | ```html
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | ```
84 |
85 | Then use dynamic import
86 |
87 | ```jsx
88 | import React, { lazy, Suspense, useState } from 'react';
89 | import Footer from './Footer';
90 |
91 | const Title = lazy(() => import('website2/Title')); // federated
92 |
93 | export default () => {
94 | return (
95 | <>
96 |
97 |
98 |
99 |
100 | This app loads the heading above from website2, and doesnt expose
101 | anything itself.
102 |
103 |
104 | >
105 | );
106 | };
107 | ```
108 |
109 | ## Exmaple
110 |
111 | See [examples here](./examples).
112 |
113 | ```shell
114 | git clone
115 | yarn install
116 | yarn build
117 | yarn install:example
118 | yarn dev:example
119 | ```
120 |
121 | Open http://localhost:3001
122 |
123 | ## Preview
124 |
125 | 
126 |
--------------------------------------------------------------------------------
/__test__/build.js:
--------------------------------------------------------------------------------
1 | const { join } = require('path');
2 | const rimraf = require('rimraf');
3 | const shell = require('shelljs');
4 |
5 | const tmp = join(__dirname, '.tmp');
6 | const src = join(__dirname, 'fixtures');
7 |
8 | shell.rm('-r', tmp);
9 | shell.cp('-r', src, tmp);
10 |
11 | shell.exec(`cd ${join(tmp, './project1')} && npx webpack`);
12 | shell.exec(`cd ${join(tmp, './project2')} && npx webpack`);
13 |
--------------------------------------------------------------------------------
/__test__/fixtures/app.js:
--------------------------------------------------------------------------------
1 | import mri from 'mri';
2 | import foo from './foo';
3 |
4 | mri([]);
5 |
6 | console.log(`
7 | ENV: process.env.NODE_ENV
8 | BAR: DEMO_BAR
9 | FOO: FOO_BAR
10 | BAZ: FOO_BAZ
11 | ${ foo() }
12 | `);
13 |
--------------------------------------------------------------------------------
/__test__/fixtures/foo.js:
--------------------------------------------------------------------------------
1 | var FOO = DEMO_FOO;
2 |
3 | module.exports = () => `foo: ${ FOO }`;
4 |
--------------------------------------------------------------------------------
/__test__/fixtures/project1/index.js:
--------------------------------------------------------------------------------
1 | module.exports = 'hello project1';
2 |
--------------------------------------------------------------------------------
/__test__/fixtures/project1/webpack.config.js:
--------------------------------------------------------------------------------
1 | const ModuleFederationPlugin = require('../../../lib');
2 | const path = require('path');
3 |
4 | module.exports = {
5 | entry: {
6 | main: path.join(__dirname, './index.js'),
7 | },
8 | output: {
9 | publicPath: path.join(__dirname, './dist'),
10 | },
11 | target: 'node',
12 | resolve: {
13 | extensions: ['.jsx', '.js', '.json'],
14 | },
15 | optimization: {
16 | minimize: false,
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.jsx?$/,
22 | loader: require.resolve('babel-loader'),
23 | options: {
24 | rootMode: 'upward',
25 | presets: [],
26 | },
27 | },
28 | ],
29 | },
30 | plugins: [
31 | new ModuleFederationPlugin({
32 | name: '_federation_project1',
33 | library: {
34 | type: 'global',
35 | name: '_federation_project1',
36 | },
37 | filename: 'remoteEntry.js',
38 | exposes: {
39 | index: './index.js'
40 | },
41 | }),
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------
/__test__/fixtures/project2/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | hello: 'hello project2',
3 | remoteHello: import('project1/index').then(mod => mod.default),
4 | };
5 |
6 |
--------------------------------------------------------------------------------
/__test__/fixtures/project2/webpack.config.js:
--------------------------------------------------------------------------------
1 | const ModuleFederationPlugin = require('../../../lib');
2 | const path = require('path');
3 |
4 | module.exports = {
5 | entry: {
6 | main: path.join(__dirname, './index.js'),
7 | },
8 | output: {
9 | publicPath: path.join(__dirname, './dist'),
10 | libraryTarget: 'commonjs',
11 | },
12 | target: 'node',
13 | resolve: {
14 | extensions: ['.jsx', '.js', '.json'],
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.jsx?$/,
20 | loader: require.resolve('babel-loader'),
21 | options: {
22 | rootMode: 'upward',
23 | presets: [],
24 | },
25 | },
26 | ],
27 | },
28 | optimization: {
29 | minimize: false,
30 | },
31 | plugins: [
32 | new ModuleFederationPlugin({
33 | name: '_federation_project2',
34 | library: {
35 | type: 'global',
36 | name: '_federation_project2',
37 | },
38 | filename: 'remoteEntry.js',
39 | remotes: {
40 | 'project1': '_federation_project1'
41 | },
42 | }),
43 | ],
44 | };
45 |
--------------------------------------------------------------------------------
/__test__/index.spec.js:
--------------------------------------------------------------------------------
1 |
2 | const path = require('path');
3 | const fs = require('fs');
4 | const tmpdir = path.join(__dirname, '.tmp');
5 |
6 | const project1dir = path.join(tmpdir, './project1/dist/');
7 | const project2dir = path.join(tmpdir, './project2/dist/');
8 |
9 | require('./build');
10 |
11 | describe('Build', () => {
12 | test('should bundle', () => {
13 | expect(fs.existsSync(path.join(project1dir, './main.js'))).toBeTruthy();
14 | expect(fs.existsSync(path.join(project2dir, './main.js'))).toBeTruthy();
15 | });
16 |
17 | test('should have remoteEntry', () => {
18 | expect(fs.existsSync(path.join(project1dir, './remoteEntry.js'))).toBeTruthy();
19 | expect(fs.existsSync(path.join(project2dir, './remoteEntry.js'))).toBeTruthy();
20 | });
21 |
22 | });
23 |
24 | describe('Remote module', () => {
25 | test('can import remote module', async () => {
26 | // load remoteEntry of project1
27 | require(path.join(project1dir, './remoteEntry.js'));
28 |
29 | const {
30 | hello,
31 | remoteHello,
32 | } = require(path.join(project2dir, './main.js'));
33 |
34 | expect(hello).toEqual('hello project2');
35 |
36 | expect(await remoteHello).toEqual('hello project1');
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | require.resolve('@babel/preset-env'),
5 | {
6 | targets: {
7 | node: true,
8 | },
9 | },
10 | ],
11 | ],
12 | plugins: [
13 | require.resolve('@babel/plugin-proposal-optional-chaining'),
14 | require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
15 | ],
16 | };
--------------------------------------------------------------------------------
/examples/website1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website1",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "concurrently --raw -k \"webpack --watch\" \"serve dist/ -p 3001\"",
7 | "serve": "concurrently --raw -k \"serve dist/ -p 3001\"",
8 | "build": "webpack --mode production"
9 | },
10 | "dependencies": {
11 | "react": "^16.12.0",
12 | "react-dom": "^16.12.0",
13 | "react-router-dom": "^5.1.2"
14 | },
15 | "devDependencies": {
16 | "@babel/core": "^7.9.0",
17 | "@babel/preset-react": "^7.8.3",
18 | "babel-loader": "^8.0.6",
19 | "concurrently": "^5.1.0",
20 | "html-webpack-plugin": "^4.2.0",
21 | "serve": "^11.3.0",
22 | "webpack": "^4.42.1",
23 | "webpack-cli": "^3.3.11"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/website1/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { lazy, Suspense } from 'react';
2 | import HelloWorld from './HelloWorld';
3 |
4 | import {
5 | BrowserRouter as Router,
6 | Switch,
7 | Route,
8 | Link
9 | } from "react-router-dom";
10 |
11 |
12 | const Website2 = lazy(() => import('website2/App').then(mod => mod.default));
13 |
14 | export default () =>
15 |
25 |
26 |
27 | loading}>
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | ;
36 |
--------------------------------------------------------------------------------
/examples/website1/src/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => Bye bye website1!
;
4 |
--------------------------------------------------------------------------------
/examples/website1/src/HelloWorld.jsx:
--------------------------------------------------------------------------------
1 | import React, { lazy, Suspense, useState } from 'react';
2 | import Footer from './Footer';
3 |
4 | // import from another online project
5 | const Title = lazy(() => import('website2/Title').then(mod => mod.default));
6 |
7 | export default () => {
8 | return (
9 | <>
10 | loading}>
11 |
12 |
13 |
14 | This app loads the heading above from website2, and doesnt expose
15 | anything itself.
16 |
17 |
18 | >
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/examples/website1/src/bootstrap.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import App from './App';
4 |
5 | render(, document.getElementById('app'));
6 |
--------------------------------------------------------------------------------
/examples/website1/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap.jsx');
2 |
--------------------------------------------------------------------------------
/examples/website1/src/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/website1/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const ModuleFederationPlugin = require('../../');
3 |
4 | module.exports = {
5 | entry: './src/index',
6 | cache: false,
7 |
8 | mode: 'development',
9 | devtool: 'source-map',
10 |
11 | optimization: {
12 | minimize: false,
13 | },
14 |
15 | output: {
16 | publicPath: 'http://localhost:3001/',
17 | },
18 |
19 | resolve: {
20 | extensions: ['.jsx', '.js', '.json'],
21 | },
22 |
23 | module: {
24 | rules: [
25 | {
26 | test: /\.jsx?$/,
27 | loader: require.resolve('babel-loader'),
28 | options: {
29 | rootMode: 'upward',
30 | presets: [require.resolve('@babel/preset-react')],
31 | },
32 | },
33 | ],
34 | },
35 |
36 | plugins: [
37 | new ModuleFederationPlugin({
38 | remotes: {
39 | 'website2': '_federation_website2'
40 | },
41 | // shared is not support now
42 | // shared: ['react', 'react-dom']
43 | }),
44 | new HtmlWebpackPlugin({
45 | template: './src/template.html',
46 | chunks: ['main'],
47 | }),
48 | ],
49 |
50 | externals: {
51 | 'react': 'React',
52 | 'react-dom': 'ReactDOM'
53 | },
54 | };
55 |
--------------------------------------------------------------------------------
/examples/website2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website2",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "concurrently --raw -k \"webpack --watch\" \"serve dist/ -p 3002\"",
7 | "serve": "concurrently --raw -k \"serve dist/ -p 3002\"",
8 | "build": "webpack"
9 | },
10 | "dependencies": {
11 | "react": "^16.12.0",
12 | "react-dom": "^16.12.0"
13 | },
14 | "devDependencies": {
15 | "@babel/preset-react": "^7.8.3",
16 | "babel-loader": "^8.0.6",
17 | "concurrently": "^5.1.0",
18 | "serve": "^11.3.0",
19 | "html-webpack-plugin": "^4.2.0",
20 | "webpack": "^4.42.1",
21 | "webpack-cli": "^3.3.11"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/website2/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { lazy, Suspense } from 'react';
2 | import HelloWorld from './HelloWorld';
3 |
4 | export default () => ;
5 |
--------------------------------------------------------------------------------
/examples/website2/src/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => Bye bye website2!
;
4 |
--------------------------------------------------------------------------------
/examples/website2/src/HelloWorld.jsx:
--------------------------------------------------------------------------------
1 | import React, { lazy, Suspense } from 'react';
2 | // import Footer from 'website1/Footer';
3 | import Footer2 from './Footer';
4 |
5 | const Title = lazy(() => import('./Title'));
6 |
7 | export default () => (
8 | <>
9 |
10 |
11 |
12 | This is Website 2
13 |
14 | >
15 | );
16 |
--------------------------------------------------------------------------------
/examples/website2/src/Title.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => Hello world!
;
4 |
--------------------------------------------------------------------------------
/examples/website2/src/bootstrap.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import App from './App';
4 |
5 | render(, document.getElementById('app'));
6 |
--------------------------------------------------------------------------------
/examples/website2/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap.jsx');
2 |
--------------------------------------------------------------------------------
/examples/website2/src/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/website2/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const ModuleFederationPlugin = require('../../');
3 |
4 | module.exports = {
5 | entry: {
6 | main: './src/index',
7 | },
8 | cache: false,
9 | devtool: 'source-map',
10 | mode: 'development',
11 |
12 | optimization: {
13 | minimize: false,
14 | },
15 |
16 | output: {
17 | publicPath: 'http://localhost:3002/',
18 | },
19 |
20 | resolve: {
21 | extensions: ['.jsx', '.js', '.json'],
22 | },
23 |
24 | externals: {
25 | 'react': 'React',
26 | 'react-dom': 'ReactDOM'
27 | },
28 |
29 | module: {
30 | rules: [
31 | {
32 | test: /\.jsx?$/,
33 | loader: require.resolve('babel-loader'),
34 | options: {
35 | rootMode: 'upward',
36 | presets: [require.resolve('@babel/preset-react')],
37 | },
38 | },
39 | ],
40 | },
41 |
42 | plugins: [
43 | new ModuleFederationPlugin({
44 | name: '_federation_website2',
45 | library: {
46 | type: 'var',
47 | name: '_federation_website2',
48 | },
49 | filename: 'remoteEntry.js',
50 | // shared is not support now
51 | // shared: ['react', 'react-dom'],
52 | remotes: {
53 | 'website1': 'website1'
54 | },
55 | exposes: {
56 | Title: './src/Title',
57 | App: './src/App'
58 | },
59 | }),
60 | new HtmlWebpackPlugin({
61 | template: './src/template.html',
62 | chunks: ['main'],
63 | }),
64 | ],
65 |
66 | };
67 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 | ---------------
3 |
4 | Copyright © 2020 Alibaba All rights reserved.
5 | Copyright © 2018 Zackary Jackson All rights reserved.
6 | Copyright © 2018 ScriptedAlchemy LLC All rights reserved.
7 |
8 | Redistribution and use in source and binary forms, with or without modification,
9 | are permitted provided that the following conditions are met:
10 | * Redistributions of source code must retain the above copyright notice, this
11 | list of conditions and the following disclaimer.
12 | * Redistributions in binary form must reproduce the above copyright notice, this
13 | list of conditions and the following disclaimer in the documentation and/or
14 | other materials provided with the distribution.
15 | * Neither the name of “ScriptedAlchemy” nor the names of its contributors may be used to
16 | endorse or promote products derived from this software without specific prior
17 | written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-plugin-module-federation",
3 | "version": "1.0.0-beta-1",
4 | "description": "",
5 | "main": "./lib/index.js",
6 | "scripts": {
7 | "test": "yarn build && jest",
8 | "dev": "tsc -w",
9 | "build": "tsc",
10 | "dev:example": "cross-env NODE_ENV=development concurrently --raw \"cd examples/website1 && npm start\" \"cd examples/website2 && npm start\"",
11 | "install:example": "concurrently --raw \"cd examples/website1 && yarn install\" \"cd examples/website2 && yarn install\"",
12 | "clean:example": "concurrently --raw \"cd examples/website1 && rm -rf node_modules\" \"cd examples/website2 && rm -rf node_modules\"",
13 | "clean": "npm run clean:example && rm -rf node_modules"
14 | },
15 | "author": "",
16 | "license": "ISC",
17 | "devDependencies": {
18 | "@babel/core": "^7.9.0",
19 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
20 | "@babel/plugin-proposal-optional-chaining": "^7.9.0",
21 | "@babel/preset-env": "^7.9.5",
22 | "@types/expect": "^24.3.0",
23 | "@types/jest": "^25.2.1",
24 | "@types/webpack": "^4.41.12",
25 | "babel-loader": "^8.1.0",
26 | "concurrently": "^5.1.0",
27 | "cross-env": "^7.0.2",
28 | "jest": "^25.4.0",
29 | "serve": "^11.3.0",
30 | "shelljs": "^0.8.3",
31 | "typescript": "^3.8.3",
32 | "webpack": "^4.42.1",
33 | "webpack-cli": "^3.3.11"
34 | },
35 | "files": [
36 | "index.js",
37 | "lib"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/src/ContainerEntryDependency.js:
--------------------------------------------------------------------------------
1 | import Dependency from 'webpack/lib/Dependency';
2 |
3 | export default class ContainerEntryDependency extends Dependency {
4 | constructor(dependencies, name) {
5 | super();
6 | this.exposedDependencies = dependencies;
7 | this.optional = true;
8 | this.loc = { name };
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ContainerEntryModule.js:
--------------------------------------------------------------------------------
1 | import Module from 'webpack/lib/Module';
2 | import AsyncDependenciesBlock from 'webpack/lib/AsyncDependenciesBlock';
3 | import RuntimeGlobals from './webpack/lib/RuntimeGlobals';
4 | import Template from 'webpack/lib/Template';
5 | import { ConcatSource, OriginalSource, RawSource } from 'webpack-sources';
6 |
7 | const SOURCE_TYPES = new Set(['javascript']);
8 | const RUNTIME_REQUIREMENTS = new Set([
9 | RuntimeGlobals.definePropertyGetters,
10 | RuntimeGlobals.exports,
11 | RuntimeGlobals.returnExportsFromRuntime,
12 | ]);
13 |
14 | export default class ContainerEntryModule extends Module {
15 | constructor(dependency) {
16 | super('javascript/dynamic', null);
17 | this.expose = dependency?.exposedDependencies;
18 | }
19 |
20 | getSourceTypes() {
21 | return SOURCE_TYPES;
22 | }
23 |
24 | basicFunction(args, body) {
25 | return `function(${args}) {\n${Template.indent(body)}\n}`;
26 | }
27 |
28 | identifier() {
29 | return `container entry ${JSON.stringify(
30 | this.expose?.map(item => item.exposedName),
31 | )}`;
32 | }
33 |
34 | readableIdentifier() {
35 | return `container entry`;
36 | }
37 |
38 | needBuild(context, callback) {
39 | return callback(null, !this.buildMeta);
40 | }
41 |
42 | /**
43 | * Removes all dependencies and blocks
44 | * @returns {void}
45 | */
46 | clearDependenciesAndBlocks() {
47 | this.dependencies.length = 0;
48 | this.blocks.length = 0;
49 | }
50 |
51 | build(options, compilation, resolver, fs, callback) {
52 | this.buildMeta = {};
53 | this.buildInfo = {
54 | strict: true,
55 | };
56 |
57 | this.clearDependenciesAndBlocks();
58 |
59 | for (const dep of (this.expose || [])) {
60 | const block = new AsyncDependenciesBlock(
61 | undefined,
62 | dep.loc,
63 | dep.userRequest,
64 | );
65 | block.addDependency(dep);
66 | this.addBlock(block);
67 | }
68 |
69 | callback();
70 | }
71 |
72 | source(depTemplates, runtimeTemplate) {
73 | const runtimeRequirements = RUNTIME_REQUIREMENTS;
74 | const getters = [];
75 |
76 | let result = '';
77 |
78 | for (const block of this.blocks) {
79 | const {
80 | dependencies: [dep],
81 | } = block;
82 | const name = dep.exposedName;
83 | const mod = dep.module;
84 | const request = dep.userRequest;
85 |
86 | let str;
87 |
88 | if (!mod) {
89 | str = runtimeTemplate.throwMissingModuleErrorBlock({
90 | request: dep.userRequest,
91 | });
92 | } else {
93 | str = `return ${runtimeTemplate.blockPromise({
94 | block,
95 | message: request,
96 | })}.then(${this.basicFunction(
97 | '',
98 | `return ${runtimeTemplate.moduleRaw({
99 | module: mod,
100 | request,
101 | weak: false,
102 | runtimeRequirements,
103 | })}`,
104 | )});`;
105 | }
106 |
107 | getters.push(
108 | `${Template.toNormalComment(
109 | `[${name}] => ${request}`,
110 | )}"${name}": ${this.basicFunction('', str)}`,
111 | );
112 | }
113 |
114 | result = [
115 | `\n${"var"} __MODULE_MAP__ = {${getters.join(',')}};`,
116 | `\n${"var"} __GET_MODULE__ = ${this.basicFunction(
117 | ['module'],
118 | `return typeof __MODULE_MAP__[module] ==='function' ? __MODULE_MAP__[module].apply(null) : Promise.reject(new Error('Module ' + module + ' does not exist.'))`,
119 | )};`,
120 | `\n\n module.exports = {\n`,
121 | Template.indent([
122 | `get: ${this.basicFunction(
123 | 'id',
124 | 'return __GET_MODULE__(id)',
125 | )},`,
126 |
127 | `override: ${this.basicFunction(
128 | 'obj',
129 | `Object.assign(__MODULE_MAP__, obj)`,
130 | )},`,
131 | // ${RuntimeGlobals.definePropertyGetters(
132 | // ['module', 'getter'],
133 | // '__webpack_require__.shared[module] = getter;',
134 | // )}`
135 | ]),
136 | `};`,
137 | // `)`,
138 | ].join('');
139 |
140 | if (this.useSourceMap) {
141 | return new OriginalSource(result, this.identifier());
142 | } else {
143 | return new RawSource(result);
144 | }
145 | }
146 |
147 | /**
148 | * Get a list of runtime requirements
149 | * @param {SourceContext} context context for code generation
150 | * @returns {Iterable | null} required runtime modules
151 | */
152 | getRuntimeRequirements(context) {
153 | return [RuntimeGlobals.module, RuntimeGlobals.require];
154 | }
155 |
156 |
157 | size(type) {
158 | return 42;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/ContainerEntryModuleFactory.js:
--------------------------------------------------------------------------------
1 | import ModuleFactory from './webpack/lib/ModuleFactory';
2 | import ContainerEntryModule from './ContainerEntryModule';
3 |
4 | export default class ContainerEntryModuleFactory extends ModuleFactory {
5 | create({ dependencies: [dependency] }, callback) {
6 | callback(null, new ContainerEntryModule(dependency));
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/ContainerExposedDependency.js:
--------------------------------------------------------------------------------
1 | import ModuleDependency from 'webpack/lib/dependencies/ModuleDependency';
2 |
3 | export default class ContainerExposedDependency extends ModuleDependency {
4 | constructor(name, request) {
5 | super(request);
6 | this._name = name;
7 | }
8 |
9 | get exposedName() {
10 | return this._name;
11 | }
12 |
13 | getResourceIdentifier() {
14 | return `exposed dependency ${this._name}`;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/ModuleFederationPlugin.js:
--------------------------------------------------------------------------------
1 | import JavascriptModulesPlugin from 'webpack/lib/JavascriptModulesPlugin';
2 | import Template from 'webpack/lib/Template';
3 | import propertyAccess from './webpack/lib/propertyAccess';
4 | import validateOptions from 'schema-utils';
5 | import ContainerExposedDependency from './ContainerExposedDependency';
6 | import { ConcatSource } from 'webpack-sources';
7 | import ContainerEntryDependency from './ContainerEntryDependency';
8 | import ContainerEntryModuleFactory from './ContainerEntryModuleFactory';
9 | import RemoteModule from './RemoteModule';
10 | import SharedModule from './SharedModule';
11 | import ContainerEntryModule from './ContainerEntryModule';
12 |
13 | const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9]+ /;
14 |
15 | const globalType = 'global';
16 |
17 | export default class ModuleFederationPlugin {
18 | static get name() {
19 | return ModuleFederationPlugin.constructor.name;
20 | }
21 |
22 | constructor(options) {
23 | const name = options.name ?? `remoteEntry`;
24 | validateOptions(
25 | {
26 | type: 'object',
27 | properties: {
28 | // shared: {
29 | // type: 'object',
30 | // },
31 | exposes: {
32 | type: ['object', 'array'],
33 | },
34 |
35 | remotes: {
36 | type: ['object', 'array'],
37 | },
38 | name: {
39 | type: 'string',
40 | default: name,
41 | },
42 |
43 | library: {
44 | type: 'object',
45 | properties: {
46 | name: {
47 | type: 'string'
48 | },
49 | type: {
50 | type: 'string',
51 | default: 'var',
52 | enum: [
53 | 'var',
54 | 'this',
55 | 'window',
56 | 'self',
57 | 'global',
58 | 'commonjs',
59 | 'commonjs2',
60 | 'amd',
61 | 'amd-require',
62 | 'umd',
63 | 'umd2',
64 | 'system',
65 | ]
66 | },
67 | }
68 | },
69 |
70 | filename: {
71 | anyOf: [{ type: 'string' }, { instanceof: 'Function' }],
72 | },
73 | },
74 | additionalProperties: false,
75 | },
76 | options,
77 | { name: ModuleFederationPlugin.name },
78 | );
79 |
80 | this.options = {
81 | shared: options.shared ?? null,
82 | name,
83 | library: options.library ?? {
84 | type: 'global',
85 | name: name,
86 | },
87 | filename: options.filename ?? undefined, // Undefined means, use the default behaviour
88 | exposes: options.exposes ?? {},
89 | remotes: options.remotes ?? {},
90 | };
91 |
92 | let exposedMap = this.options.exposes || {};
93 |
94 | if (Array.isArray(this.options.exposes)) {
95 | exposedMap = {};
96 | for (const exp of this.options.exposes) {
97 | // TODO: Check if this regex handles all cases
98 | exposedMap[exp.replace(/(^(?:[^\w])+)/, '')] = exp;
99 | }
100 | }
101 |
102 | let sharedMap = this.options.shared || {};
103 |
104 | if (Array.isArray(this.options.shared)) {
105 | sharedMap = {};
106 | for (const exp of this.options.shared) {
107 | // TODO: Check if this regex handles all cases
108 | sharedMap[exp.replace(/(^(?:[^\w])+)/, '')] = exp;
109 | }
110 | }
111 |
112 | this.options.shared = sharedMap;
113 | this.options.exposes = exposedMap;
114 |
115 | if (!this.options.library.name) {
116 | this.options.library.name = name;
117 | }
118 | }
119 |
120 | apply(compiler) {
121 | if (compiler.options.optimization.runtimeChunk) {
122 | throw new Error(
123 | 'This plugin cannot integrate with RuntimeChunk plugin, please remote `optimization.runtimeChunk`.',
124 | );
125 | }
126 |
127 | compiler.options.output.jsonpFunction = `${
128 | compiler.options.output.jsonpFunction
129 | }${compiler.name ?? ''}${this.options.name}`;
130 |
131 | let deps = [];
132 |
133 | compiler.hooks.make.tapAsync(
134 | ModuleFederationPlugin.name,
135 | (compilation, callback) => {
136 |
137 | const asyncMap = {
138 | ...this.options.exposes,
139 | };
140 | deps = Object.entries(asyncMap).map(([name, request], idx) => {
141 | const dep = new ContainerExposedDependency(name, request);
142 | dep.loc = {
143 | name,
144 | index: idx,
145 | };
146 | return dep;
147 | })
148 |
149 |
150 | compilation.addEntry(
151 | compilation.context,
152 | new ContainerEntryDependency(
153 | deps,
154 | this.options.name,
155 | ),
156 | this.options.name,
157 | callback,
158 | );
159 |
160 | },
161 | );
162 |
163 | const handleRemote = (value, type, callback) => {
164 | /** @type {string} */
165 | let externalConfig = value;
166 | // When no explicit type is specified, extract it from the externalConfig
167 | // if (
168 | // type === undefined &&
169 | // UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
170 | // ) {
171 | // const idx = externalConfig.indexOf(' ');
172 | // type = externalConfig.substr(0, idx);
173 | // externalConfig = externalConfig.substr(idx + 1);
174 | // }
175 |
176 | callback(
177 | null,
178 | new RemoteModule(
179 | externalConfig,
180 | type || globalType,
181 | value,
182 | this.options.remotes,
183 | this.options.shared,
184 | ),
185 | );
186 | };
187 |
188 |
189 | // const handleShared = (value, type, callback) => {
190 |
191 | // let externalConfig = value;
192 |
193 | // // When no explicit type is specified, extract it from the externalConfig
194 | // if (
195 | // type === undefined &&
196 | // UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
197 | // ) {
198 | // const idx = externalConfig.indexOf(' ');
199 | // type = externalConfig.substr(0, idx);
200 | // externalConfig = externalConfig.substr(idx + 1);
201 | // }
202 |
203 | // callback(
204 | // null,
205 | // new SharedModule(
206 | // externalConfig,
207 | // 'default', // TODO: remove hardcode
208 | // value,
209 | // ),
210 | // );
211 | // };
212 |
213 |
214 | compiler.hooks.normalModuleFactory.tap(ModuleFederationPlugin.name, (nmf) => {
215 | nmf.hooks.factory.tap(ModuleFederationPlugin.name, (fn) => {
216 | return (result, callback) => {
217 | const request = result?.request;
218 | const requestScope = result?.request?.split('/')?.shift?.();
219 |
220 | if (this.options.remotes[requestScope]) {
221 | return handleRemote(result.request, null, callback);
222 | }
223 | // if (this.options.shared[request]) {
224 | // return handleShared(
225 | // this.options.shared[request],
226 | // undefined,
227 | // callback,
228 | // );
229 | // }
230 | fn(result, (error, mod) => {
231 |
232 | callback(error, mod);
233 | });
234 | };
235 | });
236 |
237 | // nmf.hooks.factory.tap();
238 | });
239 |
240 |
241 | // compiler.hooks.compile.tap(
242 | // ContainerPlugin.name,
243 | // () => {
244 | // new OverridablesPlugin(
245 | // this.options.shared,
246 | // ).apply(compiler);
247 | // },
248 | // );
249 |
250 |
251 | compiler.hooks.thisCompilation.tap(
252 | ModuleFederationPlugin.name,
253 | (compilation, { normalModuleFactory }) => {
254 | compilation.dependencyFactories.set(
255 | ContainerEntryDependency,
256 | new ContainerEntryModuleFactory(),
257 | );
258 |
259 | compilation.dependencyFactories.set(
260 | ContainerExposedDependency,
261 | normalModuleFactory,
262 | );
263 |
264 | compilation.hooks.afterOptimizeChunkAssets.tap(
265 | ModuleFederationPlugin.name,
266 | (chunks) => {
267 |
268 | for (let chunk of chunks) {
269 | if (!chunk.rendered) {
270 | // Skip already rendered (cached) chunks
271 | // to avoid rebuilding unchanged code.
272 | continue;
273 | }
274 |
275 | for (const fileName of chunk.files) {
276 | const source = compilation.assets[fileName];
277 |
278 | let result = source;
279 |
280 | if (chunk.name === this.options.name) {
281 | const libName = Template.toIdentifier(
282 | compilation.getPath(this.options.library.name, {
283 | chunk,
284 | }),
285 | );
286 |
287 | switch (this.options.library.type) {
288 | case 'var': {
289 | result = new ConcatSource(`var ${libName} =`, source);
290 | break;
291 | }
292 | case 'this':
293 | case 'window':
294 | case 'self':
295 | result = new ConcatSource(
296 | `${this.options.library.type}${propertyAccess([libName])} =`,
297 | source,
298 | );
299 | break;
300 | case 'global':
301 | result = new ConcatSource(
302 | `${compiler.options.output.globalObject}${propertyAccess([
303 | libName,
304 | ])} =`,
305 | source,
306 | );
307 | break;
308 | case 'commonjs':
309 | case 'commonjs2': {
310 | result = new ConcatSource(
311 | `exports${propertyAccess([libName])} =`,
312 | source,
313 | );
314 | break;
315 |
316 | }
317 | case 'amd': // TODO: Solve this?
318 | case 'amd-require': // TODO: Solve this?
319 | case 'umd': // TODO: Solve this?
320 | case 'umd2': // TODO: Solve this?
321 | case 'system': // TODO: Solve this?
322 | default:
323 | throw new Error(
324 | `${this.options.library.type} is not a valid Library target`,
325 | );
326 |
327 | }
328 |
329 | }
330 | compilation.assets[fileName] = result;
331 |
332 | }
333 | }
334 | }
335 | )
336 |
337 |
338 | compilation.hooks.afterChunks.tap(ModuleFederationPlugin.name, chunks => {
339 | for (const chunk of chunks) {
340 | if (chunk.name === this.options.name) {
341 | chunk.filenameTemplate = this.options.filename;
342 | }
343 | }
344 | });
345 | },
346 | );
347 | }
348 | }
349 |
--------------------------------------------------------------------------------
/src/RemoteModule.js:
--------------------------------------------------------------------------------
1 | const { OriginalSource, RawSource } = require('webpack-sources');
2 | const Module = require('webpack/lib/Module');
3 | const RuntimeGlobals = require('./webpack/lib/RuntimeGlobals');
4 | const Template = require('webpack/lib/Template');
5 |
6 | const getSourceForGlobalVariableExternal = (
7 | variableName,
8 | type,
9 | requestScope,
10 | ) => {
11 | if (!Array.isArray(variableName)) {
12 | // make it an array as the look up works the same basically
13 | variableName = [variableName];
14 | }
15 |
16 | const objectLookup = variableName.map(r => `${JSON.stringify(r)}`).join('');
17 |
18 | // will output the following:
19 | // (function() {
20 | // module.exports =
21 | // typeof self["websiteTwo"] !== "undefined" ? self["websiteTwo"].get("Title") :
22 | // Promise.reject("Missing Remote Runtime: self[\"websiteTwo\"] cannot be found when trying to import \"Title\"");
23 | // }());
24 |
25 | return Template.asString([
26 | '(function() {',
27 | // `console.log('shared',${type}["${requestScope}"])`,
28 | 'module.exports =',
29 | `typeof ${type}["${requestScope}"] !== 'undefined' ? ${type}["${requestScope}"].get(${objectLookup}) : `,
30 | `Promise.reject('Missing Remote Runtime: ${type}["${requestScope}"] cannot be found when trying to import ${objectLookup}'); `,
31 | '}());',
32 | ]);
33 | };
34 |
35 | /**
36 | * @param {string|string[]} moduleAndSpecifiers the module request
37 | * @param {string|string[]} requestScope the module request namespace scope
38 | * @returns {string} the generated source
39 | */
40 | const getSourceForCommonJsExternal = (moduleAndSpecifiers, requestScope) => {
41 | if (!Array.isArray(moduleAndSpecifiers)) {
42 | // returns module.exports = require("websiteTwo").get("Title");
43 | return `module.exports = require(${JSON.stringify(
44 | requestScope,
45 | )}).get(${JSON.stringify(moduleAndSpecifiers)});`;
46 | }
47 |
48 | const moduleName = moduleAndSpecifiers[0];
49 | const objectLookup = moduleAndSpecifiers
50 | .slice(1)
51 | .map(r => `[${JSON.stringify(r)}]`)
52 | .join('');
53 |
54 | // returns module.exports = require("websiteTwo").get("Title")["default"];
55 | return `module.exports = require(${JSON.stringify(
56 | requestScope,
57 | )}).get(${JSON.stringify(moduleName)})${objectLookup};`;
58 | };
59 |
60 | /**
61 | * @param {string} variableName the variable name to check
62 | * @param {string} request the request path
63 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
64 | * @returns {string} the generated source
65 | */
66 | const checkExternalVariable = (variableName, request, runtimeTemplate) => {
67 | return `if(typeof ${variableName} === 'undefined') { ${runtimeTemplate.throwMissingModuleErrorBlock(
68 | { request },
69 | )} }\n`;
70 | };
71 |
72 | /**
73 | * @param {string|number} id the module id
74 | * @param {boolean} optional true, if the module is optional
75 | * @param {string|string[]} request the request path
76 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
77 | * @returns {string} the generated source
78 | */
79 | const getSourceForAmdOrUmdExternal = (
80 | id,
81 | optional,
82 | request,
83 | runtimeTemplate,
84 | ) => {
85 | const externalVariable = `__WEBPACK_REMOTE_MODULE_${Template.toIdentifier(
86 | `${id}`,
87 | )}__`;
88 | const missingModuleError = optional
89 | ? checkExternalVariable(
90 | externalVariable,
91 | Array.isArray(request) ? request.join('.') : request,
92 | runtimeTemplate,
93 | )
94 | : '';
95 | return `${missingModuleError}module.exports = ${externalVariable};`;
96 | };
97 |
98 | /**
99 | * @param {boolean} optional true, if the module is optional
100 | * @param {string|string[]} request the request path
101 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
102 | * @returns {string} the generated source
103 | */
104 | const getSourceForDefaultCase = (
105 | optional,
106 | request,
107 | runtimeTemplate,
108 | requestScope,
109 | ) => {
110 | if (!Array.isArray(request)) {
111 | // make it an array as the look up works the same basically
112 | request = [request];
113 | }
114 |
115 | // TODO: use this for error handling
116 | const missingModuleError = optional
117 | ? checkExternalVariable(requestScope, request.join('.'), runtimeTemplate)
118 | : '';
119 |
120 | // refactor conditional into checkExternalVariable
121 | return Template.asString([
122 | 'module.exports = ',
123 | `typeof ${requestScope} !== 'undefined' ? ${requestScope}.get('${request}') : `,
124 | `Promise.reject("Missing Remote Runtime: ${requestScope} cannot be found when trying to import ${request}"); `,
125 | ]);
126 | };
127 |
128 | const TYPES = new Set(['javascript']);
129 | const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]);
130 |
131 |
132 | export default class RemoteModule extends Module {
133 | constructor(request, type, userRequest, remotes, shared) {
134 | super('javascript/dynamic', null);
135 |
136 | this.requestScope = request?.split('/')?.shift?.();
137 |
138 | // Info from Factory
139 | /** @type {string | string[] | Record} */
140 | this.request = request?.split(`${this.requestScope}/`)?.[1];
141 |
142 | if (remotes[this.requestScope]) {
143 | this.requestScope = remotes[this.requestScope];
144 | }
145 |
146 | this.shared = shared;
147 |
148 | /** @type {string} */
149 | this.remoteType = type;
150 | /** @type {string} */
151 | this.userRequest = userRequest;
152 |
153 | }
154 |
155 |
156 | /**
157 | * @returns {Set} types availiable (do not mutate)
158 | */
159 | getSourceTypes() {
160 | return TYPES;
161 | }
162 |
163 | /**
164 | * @param {LibIdentOptions} options options
165 | * @returns {string | null} an identifier for library inclusion
166 | */
167 | libIdent(options) {
168 | return this.userRequest;
169 | }
170 |
171 | /**
172 | * @param {Chunk} chunk the chunk which condition should be checked
173 | * @param {Compilation} compilation the compilation
174 | * @returns {boolean} true, if the chunk is ok for the module
175 | */
176 | // chunkCondition(chunk, { chunkGraph }) {
177 | // return chunkGraph.getNumberOfEntryModules(chunk) > 0;
178 | // }
179 |
180 | /**
181 | * @returns {string} a unique identifier of the module
182 | */
183 | identifier() {
184 | return `remote ${JSON.stringify(this.request)}`;
185 | }
186 |
187 | /**
188 | * @param {RequestShortener} requestShortener the request shortener
189 | * @returns {string} a user readable identifier of the module
190 | */
191 | readableIdentifier(requestShortener) {
192 | return `remote ${JSON.stringify(this.request)}`;
193 | }
194 |
195 | /**
196 | * @param {NeedBuildContext} context context info
197 | * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
198 | * @returns {void}
199 | */
200 | needBuild(context, callback) {
201 | return callback(null, !this.buildMeta);
202 | }
203 |
204 | /**
205 | * @param {WebpackOptions} options webpack options
206 | * @param {Compilation} compilation the compilation
207 | * @param {ResolverWithOptions} resolver the resolver
208 | * @param {InputFileSystem} fs the file system
209 | * @param {function(WebpackError=): void} callback callback function
210 | * @returns {void}
211 | */
212 | build(options, compilation, resolver, fs, callback) {
213 | this.buildMeta = {};
214 | this.buildInfo = {
215 | strict: true,
216 | };
217 |
218 | callback();
219 | }
220 |
221 | getSourceString(runtimeTemplate) {
222 | const request =
223 | typeof this.request === 'object' && !Array.isArray(this.request)
224 | ? this.request[this.remoteType]
225 | : this.request;
226 | switch (this.remoteType) {
227 | case 'this':
228 | case 'window':
229 | case 'self':
230 | return getSourceForGlobalVariableExternal(
231 | request,
232 | this.remoteType,
233 | this.requestScope,
234 | );
235 | case 'global':
236 | return getSourceForGlobalVariableExternal(
237 | request,
238 | runtimeTemplate.outputOptions.globalObject,
239 | this.requestScope,
240 | );
241 | case 'commonjs':
242 | case 'commonjs2':
243 | return getSourceForCommonJsExternal(request, this.requestScope);
244 | case 'amd':
245 | case 'amd-require':
246 | case 'umd':
247 | case 'umd2':
248 | case 'system':
249 | throw new Error(
250 | `${this.remoteType} is not supported with ContainerReferencePlugin`,
251 | );
252 | return getSourceForAmdOrUmdExternal(
253 | chunkGraph.getModuleId(this),
254 | this.isOptional(moduleGraph),
255 | request,
256 | runtimeTemplate,
257 | this.requestScope,
258 | );
259 | default:
260 | return getSourceForDefaultCase(
261 | this.isOptional(moduleGraph),
262 | request,
263 | runtimeTemplate,
264 | this.requestScope,
265 | );
266 | }
267 | }
268 |
269 |
270 | /**
271 | * Get a list of runtime requirements
272 | * @param {SourceContext} context context for code generation
273 | * @returns {Iterable | null} required runtime modules
274 | */
275 | getRuntimeRequirements(context) {
276 | return [RuntimeGlobals.module, RuntimeGlobals.require];
277 | }
278 |
279 |
280 | /**
281 | * @param {CodeGenerationContext} context context for code generation
282 | * @returns {CodeGenerationResult} result
283 | */
284 | source(depTemplates, runtimeTemplate) {
285 | let sourceString = this.getSourceString(
286 | runtimeTemplate,
287 | );
288 |
289 | // let sharedCode = `{`;
290 | // for (let key of Object.keys(this.shared)) {
291 | // sharedCode += `\n '${key}': function() {return __webpack_require__('${key}')},`;
292 | // }
293 | // sharedCode = sharedCode.replace(/,$/, '}');
294 |
295 | // sourceString = `${this.requestScope}.override(${sharedCode});` + sourceString;
296 |
297 | let sources;
298 | if (this.useSourceMap) {
299 | sources = new OriginalSource(sourceString, this.identifier());
300 | } else {
301 | sources = new RawSource(sourceString);
302 | }
303 |
304 | return sources;
305 | }
306 |
307 | /**
308 | * @param {string=} type the source type for which the size should be estimated
309 | * @returns {number} the estimated size of the module (must be non-zero)
310 | */
311 | size(type) {
312 | return 42;
313 | }
314 |
315 | /**
316 | * @param {Hash} hash the hash used to track dependencies
317 | * @param {ChunkGraph} chunkGraph the chunk graph
318 | * @returns {void}
319 | */
320 | updateHash(hash, chunkGraph) {
321 | // hash.update(this.remoteType);
322 | hash.update(JSON.stringify(this.request));
323 | // hash.update(
324 | // JSON.stringify(Boolean(this.isOptional(chunkGraph.moduleGraph)))
325 | // );
326 | super.updateHash(hash);
327 | }
328 |
329 | }
330 |
331 |
--------------------------------------------------------------------------------
/src/SharedModule.js:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License http://www.opensource.org/licenses/mit-license.php
3 | Author Tobias Koppers @sokra
4 | */
5 |
6 | 'use strict';
7 | import AsyncDependenciesBlock from 'webpack/lib/AsyncDependenciesBlock';
8 | import ContainerExposedDependency from './ContainerExposedDependency';
9 |
10 | const { OriginalSource, RawSource } = require('webpack-sources');
11 | const Module = require('webpack/lib/Module');
12 | const RuntimeGlobals = require('./webpack/lib/RuntimeGlobals');
13 | const Template = require('webpack/lib/Template');
14 | // const makeSerializable = require('webpack/lib/util/makeSerializable');
15 |
16 | /** @typedef {import("webpack-sources").Source} Source */
17 | /** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
18 | /** @typedef {import("./Chunk")} Chunk */
19 | /** @typedef {import("./ChunkGraph")} ChunkGraph */
20 | /** @typedef {import("./Compilation")} Compilation */
21 | /** @typedef {import("./DependencyTemplates")} DependencyTemplates */
22 | /** @typedef {import("./Module").CodeGenerationContext} CodeGenerationContext */
23 | /** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
24 | /** @typedef {import("./Module").LibIdentOptions} LibIdentOptions */
25 | /** @typedef {import("./Module").NeedBuildContext} NeedBuildContext */
26 | /** @typedef {import("./RequestShortener")} RequestShortener */
27 | /** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
28 | /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
29 | /** @typedef {import("./WebpackError")} WebpackError */
30 | /** @typedef {import("./util/Hash")} Hash */
31 | /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
32 |
33 | /**
34 | * @param {string|string[]} variableName the variable name or path
35 | * @param {string} type the module system
36 | * @returns {string} the generated source
37 | */
38 | const getSourceForGlobalVariableExternal = (variableName, type) => {
39 | if (!Array.isArray(variableName)) {
40 | // make it an array as the look up works the same basically
41 | variableName = [variableName];
42 | }
43 |
44 | // needed for e.g. window["some"]["thing"]
45 | const objectLookup = variableName.map(r => `[${JSON.stringify(r)}]`).join('');
46 | return `(function() { module.exports = ${type}${objectLookup}; }());`;
47 | };
48 |
49 | /**
50 | * @param {string|string[]} moduleAndSpecifiers the module request
51 | * @returns {string} the generated source
52 | */
53 | const getSourceForCommonJsExternal = moduleAndSpecifiers => {
54 | if (!Array.isArray(moduleAndSpecifiers)) {
55 | return `module.exports = require(${JSON.stringify(moduleAndSpecifiers)});`;
56 | }
57 | const moduleName = moduleAndSpecifiers[0];
58 | const objectLookup = moduleAndSpecifiers
59 | .slice(1)
60 | .map(r => `[${JSON.stringify(r)}]`)
61 | .join('');
62 | return `module.exports = require(${JSON.stringify(
63 | moduleName,
64 | )})${objectLookup};`;
65 | };
66 |
67 | /**
68 | * @param {string} variableName the variable name to check
69 | * @param {string} request the request path
70 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
71 | * @returns {string} the generated source
72 | */
73 | const checkExternalVariable = (variableName, request, runtimeTemplate) => {
74 | return `if(typeof ${variableName} === 'undefined') { ${runtimeTemplate.throwMissingModuleErrorBlock(
75 | { request },
76 | )} }\n`;
77 | };
78 |
79 | /**
80 | * @param {string|number} id the module id
81 | * @param {boolean} optional true, if the module is optional
82 | * @param {string|string[]} request the request path
83 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
84 | * @returns {string} the generated source
85 | */
86 | const getSourceForAmdOrUmdExternal = (
87 | id,
88 | optional,
89 | request,
90 | runtimeTemplate,
91 | ) => {
92 | const externalVariable = `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
93 | `${id}`,
94 | )}__`;
95 | const missingModuleError = optional
96 | ? checkExternalVariable(
97 | externalVariable,
98 | Array.isArray(request) ? request.join('.') : request,
99 | runtimeTemplate,
100 | )
101 | : '';
102 | return `${missingModuleError}module.exports = ${externalVariable};`;
103 | };
104 |
105 | /**
106 | * @param {boolean} optional true, if the module is optional
107 | * @param {string|string[]} request the request path
108 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
109 | * @returns {string} the generated source
110 | */
111 | const getSourceForDefaultCase = (
112 | moduleId,
113 | optional,
114 | request,
115 | runtimeTemplate,
116 | ) => {
117 | if (!Array.isArray(request)) {
118 | // make it an array as the look up works the same basically
119 | request = [request];
120 | }
121 |
122 | // TODO: use this for error handling
123 | const missingModuleError = optional
124 | ? checkExternalVariable(request.join('.'), runtimeTemplate)
125 | : '';
126 | const requestScope = null;
127 | // refactor conditional into checkExternalVariable
128 | return Template.asString([
129 | 'module.exports = ',
130 | `(typeof ${requestScope} !== 'undefined') ? typeof __webpack_require__.shared === 'function' ? (__webpack_require__.shared('${request}') || Promise.resolve(__webpack_require__('${request}'))) : Promise.resolve(__webpack_require__('${request}')):`,
131 | `Promise.reject("Missing Shared Module: ${requestScope} cannot be found when trying to override ${request}"); `,
132 | ]);
133 | };
134 |
135 | const TYPES = new Set(['javascript']);
136 | const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]);
137 |
138 | export default class SharedModule extends Module {
139 | constructor(request, type, userRequest) {
140 | super('javascript/dynamic', null);
141 |
142 | // Info from Factory
143 | /** @type {string | string[] | Record} */
144 | this.request = request;
145 | /** @type {string} */
146 | this.externalType = type;
147 | /** @type {string} */
148 | this.userRequest = userRequest;
149 | }
150 |
151 | /**
152 | * @returns {Set} types availiable (do not mutate)
153 | */
154 | getSourceTypes() {
155 | return TYPES;
156 | }
157 |
158 | /**
159 | * @param {LibIdentOptions} options options
160 | * @returns {string | null} an identifier for library inclusion
161 | */
162 | libIdent(options) {
163 | return this.userRequest;
164 | }
165 |
166 | // /**
167 | // * @param {Chunk} chunk the chunk which condition should be checked
168 | // * @param {Compilation} compilation the compilation
169 | // * @returns {boolean} true, if the chunk is ok for the module
170 | // */
171 | // chunkCondition(chunk, { chunkGraph }) {
172 | // return chunkGraph.getNumberOfEntryModules(chunk) > 0;
173 | // }
174 |
175 | /**
176 | * @returns {string} a unique identifier of the module
177 | */
178 | identifier() {
179 | return 'shared ' + JSON.stringify(this.request);
180 | }
181 |
182 | /**
183 | * @param {RequestShortener} requestShortener the request shortener
184 | * @returns {string} a user readable identifier of the module
185 | */
186 | readableIdentifier(requestShortener) {
187 | return 'shared ' + JSON.stringify(this.request);
188 | }
189 |
190 | /**
191 | * @param {NeedBuildContext} context context info
192 | * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
193 | * @returns {void}
194 | */
195 | needBuild(context, callback) {
196 | return callback(null, !this.buildMeta);
197 | }
198 |
199 |
200 | build(options, compilation, resolver, fs, callback) {
201 | this.buildMeta = {};
202 | this.buildInfo = {
203 | strict: true,
204 | exportsArgument: ['exports', '__webpack_require__'],
205 | };
206 |
207 | callback();
208 | }
209 |
210 | getSourceString(runtimeTemplate) {
211 | const request =
212 | typeof this.request === 'object' && !Array.isArray(this.request)
213 | ? this.request[this.externalType]
214 | : this.request;
215 |
216 |
217 | switch (this.externalType) {
218 | case 'this':
219 | case 'window':
220 | case 'self':
221 | return getSourceForGlobalVariableExternal(request, this.externalType);
222 | case 'global':
223 | return getSourceForGlobalVariableExternal(
224 | request,
225 | runtimeTemplate.outputOptions.globalObject,
226 | );
227 | case 'commonjs':
228 | case 'commonjs2':
229 | return getSourceForCommonJsExternal(request);
230 | case 'amd':
231 | case 'amd-require':
232 | case 'umd':
233 | case 'umd2':
234 | case 'system':
235 | return getSourceForAmdOrUmdExternal(
236 | chunkGraph.getModuleId(this),
237 | this.isOptional(moduleGraph),
238 | request,
239 | runtimeTemplate,
240 | );
241 | default:
242 | return getSourceForDefaultCase(
243 | this.index,
244 | null,
245 | request,
246 | runtimeTemplate,
247 | );
248 | }
249 | }
250 |
251 | source(depTemplates, runtimeTemplate) {
252 | const sourceString = this.getSourceString(
253 | runtimeTemplate,
254 | );
255 | let sources;
256 | if (this.useSourceMap) {
257 | sources = new OriginalSource(sourceString, this.identifier());
258 | } else {
259 | sources = new RawSource(sourceString);
260 | }
261 |
262 | return sources;
263 | }
264 |
265 | /**
266 | * @param {string=} type the source type for which the size should be estimated
267 | * @returns {number} the estimated size of the module (must be non-zero)
268 | */
269 | size(type) {
270 | return 42;
271 | }
272 |
273 | /**
274 | * @param {Hash} hash the hash used to track dependencies
275 | * @param {ChunkGraph} chunkGraph the chunk graph
276 | * @returns {void}
277 | */
278 | updateHash(hash, chunkGraph) {
279 | hash.update(this.externalType);
280 | hash.update(JSON.stringify(this.request));
281 | // hash.update(
282 | // JSON.stringify(Boolean(this.isOptional(chunkGraph.moduleGraph))),
283 | // );
284 | super.updateHash(hash);
285 | }
286 | }
287 |
288 | // makeSerializable(SharedModule, 'webpack/lib/SharedModule');
289 |
--------------------------------------------------------------------------------
/src/SharedModuleFactoryPlugin.js:
--------------------------------------------------------------------------------
1 | import SharedModule from './SharedModule';
2 | const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9]+ /;
3 |
4 | export default class SharedModuleFactoryPlugin {
5 | constructor(type, sharedModules) {
6 | this.shared = sharedModules;
7 | }
8 |
9 | apply(normalModuleFactory) {
10 | const globalType = this.remoteType;
11 | normalModuleFactory.hooks.factorize.tapAsync(
12 | 'SharedModuleFactoryPlugin',
13 | (data, callback) => {
14 | const { context } = data;
15 | const dependency = data.dependencies[0];
16 |
17 | const handleShared = (value, type, callback) => {
18 |
19 | if (value === false) {
20 | // Not externals, fallback to original factory
21 | return callback();
22 | }
23 |
24 | let externalConfig;
25 | if (value === true) {
26 | externalConfig = dependency.request;
27 | } else {
28 | externalConfig = value;
29 | }
30 |
31 | // When no explicit type is specified, extract it from the externalConfig
32 | if (
33 | type === undefined &&
34 | UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
35 | ) {
36 | const idx = externalConfig.indexOf(' ');
37 | type = externalConfig.substr(0, idx);
38 | externalConfig = externalConfig.substr(idx + 1);
39 | }
40 |
41 | callback(
42 | null,
43 | new SharedModule(
44 | externalConfig,
45 | type || globalType,
46 | dependency.request,
47 | ),
48 | );
49 | };
50 |
51 | const handleExternals = (shared, callback) => {
52 | if (typeof shared === 'string') {
53 | if (shared[dependency.request]) {
54 | return handleShared(dependency.request, undefined, callback);
55 | }
56 | } else if (Array.isArray(shared)) {
57 | let i = 0;
58 | const next = () => {
59 | let asyncFlag;
60 | const handleExternalsAndCallback = (err, module) => {
61 | if (err) return callback(err);
62 | if (!module) {
63 | if (asyncFlag) {
64 | asyncFlag = false;
65 | return;
66 | }
67 | return next();
68 | }
69 | callback(null, module);
70 | };
71 |
72 | do {
73 | asyncFlag = true;
74 | if (i >= shared.length) return callback();
75 | handleExternals(shared[i++], handleExternalsAndCallback);
76 | } while (!asyncFlag);
77 | asyncFlag = false;
78 | };
79 |
80 | next();
81 | return;
82 | } else if (
83 | typeof shared === 'object' &&
84 | Object.prototype.hasOwnProperty.call(shared, dependency.request)
85 | ) {
86 | if (shared[dependency.request]) {
87 | return handleShared(
88 | shared[dependency.request],
89 | undefined,
90 | callback,
91 | );
92 | }
93 | }
94 | callback();
95 | };
96 | handleExternals(this.shared, callback);
97 | },
98 | );
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./ModuleFederationPlugin').default;
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-external-import",
3 | "description": "dynamic import() external urls!",
4 | "main": "dist/webpack-external-import.cjs.js",
5 | "module": "dist/webpack-external-import.esm.js",
6 | "version": "0.0.0-development",
7 | "author": "Zack Jackson (https://github.com/ScriptedAlchemy)",
8 | "keywords": [
9 | "import url",
10 | "dynamic imports url",
11 | "over the wire",
12 | "import() url",
13 | "external url import",
14 | "script injection",
15 | "webpack import url",
16 | "webpack",
17 | "manifest",
18 | "dynamic dll plugin",
19 | "dll plugin",
20 | "runtime"
21 | ],
22 | "license": "BSD-3-Clause",
23 | "scripts": {
24 | "semantic-release": "semantic-release -e semantic-release-monorepo"
25 | },
26 | "dependencies": {
27 | "schema-utils": "2.6.1",
28 | "webpack-sources": "^1.4.3"
29 | },
30 | "devDependencies": {
31 | "@semantic-release/changelog": "^5.0.0",
32 | "@semantic-release/git": "^9.0.0",
33 | "semantic-release": "^17.0.4",
34 | "semantic-release-monorepo": "^7.0.0"
35 | },
36 | "peerDependencies": {
37 | },
38 | "preconstruct": {
39 | "source": "index"
40 | },
41 | "release": {
42 | "plugins": [
43 | [
44 | "@semantic-release/changelog",
45 | {
46 | "changelogFile": "CHANGELOG.md"
47 | }
48 | ],
49 | [
50 | "@semantic-release/git",
51 | {
52 | "assets": [
53 | "CHANGELOG.md"
54 | ]
55 | }
56 | ]
57 | ]
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/webpack/lib/ModuleFactory.js:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License http://www.opensource.org/licenses/mit-license.php
3 | Author Tobias Koppers @sokra
4 | */
5 |
6 | "use strict";
7 |
8 | /** @typedef {import("./Dependency")} Dependency */
9 | /** @typedef {import("./Module")} Module */
10 |
11 | /**
12 | * @typedef {Object} ModuleFactoryResult
13 | * @property {Module=} module the created module or unset if no module was created
14 | * @property {Set=} fileDependencies
15 | * @property {Set=} contextDependencies
16 | * @property {Set=} missingDependencies
17 | */
18 |
19 | /**
20 | * @typedef {Object} ModuleFactoryCreateDataContextInfo
21 | * @property {string} issuer
22 | * @property {string} compiler
23 | */
24 |
25 | /**
26 | * @typedef {Object} ModuleFactoryCreateData
27 | * @property {ModuleFactoryCreateDataContextInfo} contextInfo
28 | * @property {any=} resolveOptions
29 | * @property {string} context
30 | * @property {Dependency[]} dependencies
31 | */
32 |
33 | class ModuleFactory {
34 | /* istanbul ignore next */
35 | /**
36 | * @abstract
37 | * @param {ModuleFactoryCreateData} data data object
38 | * @param {function(Error=, ModuleFactoryResult=): void} callback callback
39 | * @returns {void}
40 | */
41 | create(data, callback) {
42 | const AbstractMethodError = require("webpack/lib/AbstractMethodError");
43 | throw new AbstractMethodError();
44 | }
45 | }
46 |
47 | module.exports = ModuleFactory;
--------------------------------------------------------------------------------
/src/webpack/lib/RuntimeGlobals.js:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License http://www.opensource.org/licenses/mit-license.php
3 | Author Tobias Koppers @sokra
4 | */
5 |
6 | "use strict";
7 |
8 | /**
9 | * the internal require function
10 | */
11 | exports.require = "__webpack_require__";
12 |
13 | /**
14 | * access to properties of the internal require function/object
15 | */
16 | exports.requireScope = "__webpack_require__.*";
17 |
18 | /**
19 | * the internal exports object
20 | */
21 | exports.exports = "__webpack_exports__";
22 |
23 | /**
24 | * top-level this need to be the exports object
25 | */
26 | exports.thisAsExports = "top-level-this-exports";
27 |
28 | /**
29 | * top-level this need to be the exports object
30 | */
31 | exports.returnExportsFromRuntime = "return-exports-from-runtime";
32 |
33 | /**
34 | * the internal module object
35 | */
36 | exports.module = "module";
37 |
38 | /**
39 | * the internal module object
40 | */
41 | exports.moduleId = "module.id";
42 |
43 | /**
44 | * the internal module object
45 | */
46 | exports.moduleLoaded = "module.loaded";
47 |
48 | /**
49 | * the bundle public path
50 | */
51 | exports.publicPath = "__webpack_require__.p";
52 |
53 | /**
54 | * the module id of the entry point
55 | */
56 | exports.entryModuleId = "__webpack_require__.s";
57 |
58 | /**
59 | * the module cache
60 | */
61 | exports.moduleCache = "__webpack_require__.c";
62 |
63 | /**
64 | * the module functions
65 | */
66 | exports.moduleFactories = "__webpack_require__.m";
67 |
68 | /**
69 | * the module functions, with only write access
70 | */
71 | exports.moduleFactoriesAddOnly = "__webpack_require__.m (add only)";
72 |
73 | /**
74 | * the chunk ensure function
75 | */
76 | exports.ensureChunk = "__webpack_require__.e";
77 |
78 | /**
79 | * an object with handlers to ensure a chunk
80 | */
81 | exports.ensureChunkHandlers = "__webpack_require__.f";
82 |
83 | /**
84 | * a runtime requirement if ensureChunkHandlers should include loading of chunk needed for entries
85 | */
86 | exports.ensureChunkIncludeEntries = "__webpack_require__.f (include entries)";
87 |
88 | /**
89 | * the chunk prefetch function
90 | */
91 | exports.prefetchChunk = "__webpack_require__.E";
92 |
93 | /**
94 | * an object with handlers to prefetch a chunk
95 | */
96 | exports.prefetchChunkHandlers = "__webpack_require__.F";
97 |
98 | /**
99 | * the chunk preload function
100 | */
101 | exports.preloadChunk = "__webpack_require__.G";
102 |
103 | /**
104 | * an object with handlers to preload a chunk
105 | */
106 | exports.preloadChunkHandlers = "__webpack_require__.H";
107 |
108 | /**
109 | * the exported property define getters function
110 | */
111 | exports.definePropertyGetters = "__webpack_require__.d";
112 |
113 | /**
114 | * define compatibility on export
115 | */
116 | exports.makeNamespaceObject = "__webpack_require__.r";
117 |
118 | /**
119 | * create a fake namespace object
120 | */
121 | exports.createFakeNamespaceObject = "__webpack_require__.t";
122 |
123 | /**
124 | * compatibility get default export
125 | */
126 | exports.compatGetDefaultExport = "__webpack_require__.n";
127 |
128 | /**
129 | * harmony module decorator
130 | */
131 | exports.harmonyModuleDecorator = "__webpack_require__.hmd";
132 |
133 | /**
134 | * node.js module decorator
135 | */
136 | exports.nodeModuleDecorator = "__webpack_require__.nmd";
137 |
138 | /**
139 | * the webpack hash
140 | */
141 | exports.getFullHash = "__webpack_require__.h";
142 |
143 | /**
144 | * an object containing all installed WebAssembly.Instance export objects keyed by module id
145 | */
146 | exports.wasmInstances = "__webpack_require__.w";
147 |
148 | /**
149 | * instantiate a wasm instance from url/filename and importsObject
150 | */
151 | exports.instantiateWasm = "__webpack_require__.v";
152 |
153 | /**
154 | * the uncaught error handler for the webpack runtime
155 | */
156 | exports.uncaughtErrorHandler = "__webpack_require__.oe";
157 |
158 | /**
159 | * the script nonce
160 | */
161 | exports.scriptNonce = "__webpack_require__.nc";
162 |
163 | /**
164 | * the chunk name of the chunk with the runtime
165 | */
166 | exports.chunkName = "__webpack_require__.cn";
167 |
168 | /**
169 | * the filename of the script part of the chunk
170 | */
171 | exports.getChunkScriptFilename = "__webpack_require__.u";
172 |
173 | /**
174 | * the filename of the script part of the hot update chunk
175 | */
176 | exports.getChunkUpdateScriptFilename = "__webpack_require__.hu";
177 |
178 | /**
179 | * startup signal from runtime
180 | */
181 | exports.startup = "__webpack_require__.x";
182 |
183 | /**
184 | * startup signal from runtime
185 | */
186 | exports.startupNoDefault = "__webpack_require__.x (no default handler)";
187 |
188 | /**
189 | * interceptor for module executions
190 | */
191 | exports.interceptModuleExecution = "__webpack_require__.i";
192 |
193 | /**
194 | * the global object
195 | */
196 | exports.global = "__webpack_require__.g";
197 |
198 | /**
199 | * the filename of the HMR manifest
200 | */
201 | exports.getUpdateManifestFilename = "__webpack_require__.hmrF";
202 |
203 | /**
204 | * function downloading the update manifest
205 | */
206 | exports.hmrDownloadManifest = "__webpack_require__.hmrM";
207 |
208 | /**
209 | * array with handler functions to download chunk updates
210 | */
211 | exports.hmrDownloadUpdateHandlers = "__webpack_require__.hmrC";
212 |
213 | /**
214 | * object with all hmr module data for all modules
215 | */
216 | exports.hmrModuleData = "__webpack_require__.hmrD";
217 |
218 | /**
219 | * array with handler functions when a module should be invalidated
220 | */
221 | exports.hmrInvalidateModuleHandlers = "__webpack_require__.hmrI";
222 |
223 | /**
224 | * the AMD define function
225 | */
226 | exports.amdDefine = "__webpack_require__.amdD";
227 |
228 | /**
229 | * the AMD options
230 | */
231 | exports.amdOptions = "__webpack_require__.amdO";
232 |
233 | /**
234 | * the System polyfill object
235 | */
236 | exports.system = "__webpack_require__.System";
237 |
238 | /**
239 | * the shorthand for Object.prototype.hasOwnProperty
240 | * using of it decreases the compiled bundle size
241 | */
242 | exports.hasOwnProperty = "__webpack_require__.o";
243 |
244 | /**
245 | * the System.register context object
246 | */
247 | exports.systemContext = "__webpack_require__.y";
--------------------------------------------------------------------------------
/src/webpack/lib/propertyAccess.js:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License http://www.opensource.org/licenses/mit-license.php
3 | Author Tobias Koppers @sokra
4 | */
5 |
6 | "use strict";
7 |
8 | const SAFE_IDENTIFIER = /^[_a-zA-Z$][_a-zA-z$0-9]*$/;
9 |
10 | const propertyAccess = (properties, start = 0) => {
11 | let str = "";
12 | for (let i = start; i < properties.length; i++) {
13 | const p = properties[i];
14 | if (`${+p}` === p) {
15 | str += `[${p}]`;
16 | } else if (SAFE_IDENTIFIER.test(p)) {
17 | str += `.${p}`;
18 | } else {
19 | str += `[${JSON.stringify(p)}]`;
20 | }
21 | }
22 | return str;
23 | };
24 |
25 | module.exports = propertyAccess;
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | // "incremental": true, /* Enable incremental compilation */
5 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
7 | // "lib": [], /* Specify library files to be included in the compilation. */
8 | "allowJs": true, /* Allow javascript files to be compiled. */
9 | "skipLibCheck": true,
10 | // "checkJs": true, /* Report errors in .js files. */
11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
14 | // "sourceMap": true, /* Generates corresponding '.map' file. */
15 | // "outFile": "./", /* Concatenate and emit output to single file. */
16 | "outDir": "./lib", /* Redirect output structure to the directory. */
17 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
18 | // "composite": true, /* Enable project compilation */
19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
20 | // "removeComments": true, /* Do not emit comments to output. */
21 | // "noEmit": true, /* Do not emit outputs. */
22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
25 |
26 | /* Strict Type-Checking Options */
27 | "strict": true, /* Enable all strict type-checking options. */
28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
29 | // "strictNullChecks": true, /* Enable strict null checks. */
30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
35 |
36 | /* Additional Checks */
37 | // "noUnusedLocals": true, /* Report errors on unused locals. */
38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
41 |
42 | /* Module Resolution Options */
43 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
44 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
45 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
47 | // "typeRoots": [], /* List of folders to include type definitions from. */
48 | // "types": [], /* Type declaration files to be included in compilation. */
49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
50 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
53 |
54 | /* Source Map Options */
55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
59 |
60 | /* Experimental Options */
61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
63 |
64 | /* Advanced Options */
65 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
66 | },
67 | "exclude": [
68 | "__test__",
69 | "node_modules",
70 | "lib",
71 | "examples",
72 | "index.js",
73 | "babel.config.js",
74 | ]
75 | }
76 |
--------------------------------------------------------------------------------