Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ModernJS async boundary #3279

Draft
wants to merge 57 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9df3cf5
use lerna 6 and uninstall any newer
ScriptedAlchemy Oct 26, 2023
33e7ec5
Merge branch 'master' into lerna-v6
ScriptedAlchemy Oct 26, 2023
6b30bf9
lerna 7 repair attempt
ScriptedAlchemy Oct 26, 2023
2e01049
lerna 7 repair attempt
ScriptedAlchemy Oct 26, 2023
8ceaca8
lerna 7 repair attempt
ScriptedAlchemy Oct 26, 2023
2fa8001
vmok async startup
ScriptedAlchemy Oct 26, 2023
d7f30c1
vmok async startup
ScriptedAlchemy Oct 27, 2023
acc7833
improve await boundary
ScriptedAlchemy Oct 27, 2023
8a1dd2f
improve await boundary
ScriptedAlchemy Oct 27, 2023
bf350aa
use lerna cmds
ScriptedAlchemy Oct 29, 2023
73b82ab
Merge branch 'master' into nested-workspace
ScriptedAlchemy Oct 29, 2023
3cef347
Merge branch 'master' into nested-workspace
ScriptedAlchemy Oct 29, 2023
54df9e8
Merge branch 'master' into nested-workspace
ScriptedAlchemy Oct 29, 2023
346c13d
private workspaces
ScriptedAlchemy Oct 29, 2023
d9699c3
Merge remote-tracking branch 'origin/nested-workspace' into nested-wo…
ScriptedAlchemy Oct 29, 2023
eeb0c86
private workspaces
ScriptedAlchemy Oct 29, 2023
d736cf2
private workspaces
ScriptedAlchemy Oct 29, 2023
1e8def2
private workspaces
ScriptedAlchemy Oct 29, 2023
54285f1
private workspaces
ScriptedAlchemy Oct 30, 2023
1a37363
private workspaces
ScriptedAlchemy Oct 30, 2023
1ef4a8c
cache yarn global cache always
ScriptedAlchemy Oct 30, 2023
08fc9b7
cache yarn global cache always
ScriptedAlchemy Oct 30, 2023
6dd8f88
cache yarn global cache always
ScriptedAlchemy Oct 30, 2023
61bbe17
cache yarn global cache always
ScriptedAlchemy Oct 30, 2023
e4c237c
cache yarn global cache always
ScriptedAlchemy Oct 30, 2023
cb15351
parallel cache ci
ScriptedAlchemy Oct 30, 2023
0c3c9f6
parallel cache ci
ScriptedAlchemy Oct 30, 2023
4c94bc2
parallel cache ci
ScriptedAlchemy Oct 30, 2023
8c25cc7
parallel cache ci
ScriptedAlchemy Oct 30, 2023
e1d7613
parallel cache ci
ScriptedAlchemy Oct 30, 2023
250f39c
install cache restore global
ScriptedAlchemy Oct 30, 2023
a9df88f
install cache restore global
ScriptedAlchemy Oct 30, 2023
a6c885a
install cache restore global
ScriptedAlchemy Oct 30, 2023
2e6b943
install cache restore global
ScriptedAlchemy Oct 30, 2023
d3bcdf0
install cache restore global
ScriptedAlchemy Oct 30, 2023
8dc1b84
install cache restore global
ScriptedAlchemy Oct 30, 2023
a420f80
Merge branch 'master' into nested-workspace
ScriptedAlchemy Oct 30, 2023
a8b2fcf
install cache restore global
ScriptedAlchemy Oct 30, 2023
9e5c70a
improve await boundary
ScriptedAlchemy Oct 30, 2023
dd97d66
improve await boundary
ScriptedAlchemy Oct 30, 2023
cfa2c2c
fix e2e in CI
ScriptedAlchemy Oct 30, 2023
e587b7b
fix e2e in CI
ScriptedAlchemy Oct 30, 2023
9cf819e
cache node module on yarn hash
ScriptedAlchemy Oct 30, 2023
6805da4
Merge branch 'master' into vmok-async-boundary
ScriptedAlchemy Oct 30, 2023
5926135
cache node module on yarn hash
ScriptedAlchemy Oct 30, 2023
cc17c3b
cache node module on yarn hash
ScriptedAlchemy Oct 30, 2023
c110c49
cache node module on yarn hash
ScriptedAlchemy Oct 30, 2023
7a10003
cache node module on yarn hash
ScriptedAlchemy Oct 30, 2023
c6d6497
feat: ModernJS implement async boundary.
ScriptedAlchemy Oct 31, 2023
391df91
Merge branch 'modernjs-async-boundary' into vmok-async-boundary
ScriptedAlchemy Oct 31, 2023
db8ed3d
Merge branch 'master' into vmok-async-boundary
ScriptedAlchemy Oct 31, 2023
279d86c
feat: ModernJS implement async boundary.
ScriptedAlchemy Oct 31, 2023
4411093
improve await boundary
ScriptedAlchemy Oct 31, 2023
5f3cb5c
Merge remote-tracking branch 'origin/vmok-async-boundary' into vmok-a…
ScriptedAlchemy Oct 31, 2023
5b0028d
fix next quirks
ScriptedAlchemy Oct 31, 2023
5f308e4
fix next issues
ScriptedAlchemy Oct 31, 2023
6ac1e2f
Merge remote-tracking branch 'origin/vmok-async-boundary' into vmok-a…
ScriptedAlchemy Oct 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8,186 changes: 0 additions & 8,186 deletions advanced-api/automatic-vendor-sharing/yarn.lock

This file was deleted.

7,232 changes: 0 additions & 7,232 deletions advanced-api/dynamic-remotes-runtime-environment-variables/yarn.lock

This file was deleted.

8,276 changes: 0 additions & 8,276 deletions advanced-api/dynamic-remotes-synchronous-imports/yarn.lock

This file was deleted.

8,276 changes: 0 additions & 8,276 deletions advanced-api/dynamic-remotes/yarn.lock

This file was deleted.

42 changes: 36 additions & 6 deletions basic-host-remote/app1/modern.config.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
import appTools, { defineConfig } from '@modern-js/app-tools';
import { ModuleFederationPlugin } from '@module-federation/enhanced';
import { RuntimeGlobals } from 'webpack';

const JavascriptModulesPlugin = require('webpack/lib/javascript/JavascriptModulesPlugin');

class AsyncEntryStartupPlugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap('AsyncEntryStartupPlugin', compilation => {
JavascriptModulesPlugin.getCompilationHooks(compilation).renderStartup.tap(
'AsyncEntryStartupPlugin',
(source, renderContext, upperContext) => {
if (upperContext.chunk.hasRuntime()) {
return source;
}
const startup = [
'var promiseTrack = [];',
`if(__webpack_require__.f && __webpack_require__.f.remotes) __webpack_require__.f.remotes(${JSON.stringify(
upperContext.chunk.id,
)}, promiseTrack);`,
`if(__webpack_require__.f && __webpack_require__.f.consumes) __webpack_require__.f.consumes(${JSON.stringify(
upperContext.chunk.id,
)}, promiseTrack);`,
`var __webpack_exports__ = Promise.all(promiseTrack).then(function() {`,
source.source(),
'return __webpack_exports__;',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entrypoints will always return a promise to webpack, if entrypoint is externally required (like commonjs)

then it will module.exports = new Promise(moduleExports)

`});`,
].join('\n');

return startup;
},
);
});
}
}
// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
server: {
port: 3001,
},
// dev: {
// // set publicPath
// assetPrefix: 'http://localhost:3001/',
// },
runtime: {
router: true,
},
source: {
// automatically generated asynchronous boundary via Dynamic Import, allowing the page code to consume remote modules generated by the module federation.
enableAsyncEntry: true,
enableAsyncEntry: false,
},
tools: {
webpack: (config, { webpack, appendPlugins }) => {
delete config.optimization.splitChunks;
config.output.publicPath = 'auto';

appendPlugins([
new webpack.container.ModuleFederationPlugin({
new AsyncEntryStartupPlugin(),
new ModuleFederationPlugin({
name: 'app1',
remotes: {
app2: 'app2@http://localhost:3002/static/js/remoteEntry.js',
Expand Down
1 change: 1 addition & 0 deletions basic-host-remote/app1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-dom": "~18.2.0"
},
"devDependencies": {
"@module-federation/enhanced": "^0.1.0",
"@modern-js/app-tools": "2.32.0",
"@modern-js/eslint-config": "2.32.0",
"@modern-js/tsconfig":"2.32.0",
Expand Down
41 changes: 38 additions & 3 deletions basic-host-remote/app2/modern.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,38 @@
import appTools, { defineConfig } from '@modern-js/app-tools';
import { ModuleFederationPlugin } from '@module-federation/enhanced';
import { RuntimeGlobals } from 'webpack';

const JavascriptModulesPlugin = require('webpack/lib/javascript/JavascriptModulesPlugin');

class AsyncEntryStartupPlugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap('AsyncEntryStartupPlugin', compilation => {
JavascriptModulesPlugin.getCompilationHooks(compilation).renderStartup.tap(
'AsyncEntryStartupPlugin',
(source, renderContext, upperContext) => {
if (upperContext.chunk.hasRuntime()) {
return source;
}
const startup = [
'var promiseTrack = [];',
`if(__webpack_require__.f && __webpack_require__.f.remotes) __webpack_require__.f.remotes(${JSON.stringify(
upperContext.chunk.id,
)}, promiseTrack);`,
`if(__webpack_require__.f && __webpack_require__.f.consumes) __webpack_require__.f.consumes(${JSON.stringify(
upperContext.chunk.id,
)}, promiseTrack);`,
`var __webpack_exports__ = Promise.all(promiseTrack).then(function() {`,
source.source(),
'return __webpack_exports__;',
`});`,
].join('\n');

return startup;
},
);
});
}
}

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
Expand All @@ -14,21 +48,22 @@ export default defineConfig({
},
source: {
// automatically generated asynchronous boundary via Dynamic Import, allowing the page code to consume remote modules generated by the module federation.
enableAsyncEntry: true,
enableAsyncEntry: false,
},
tools: {
webpack: (config, { webpack, appendPlugins }) => {
delete config.optimization.splitChunks;
config.output.publicPath = 'auto';

appendPlugins([
new webpack.container.ModuleFederationPlugin({
new AsyncEntryStartupPlugin(),
new ModuleFederationPlugin({
name: 'app2',
library: { type: 'window', name: 'app2' },
runtime: false,
filename: 'static/js/remoteEntry.js',
exposes: {
'./Button': './src/components/Button.js',
'./Button': './src/components/button.js',
},
shared: {
react: { singleton: true },
Expand Down
1 change: 1 addition & 0 deletions basic-host-remote/app2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-dom": "~18.2.0"
},
"devDependencies": {
"@module-federation/enhanced": "^0.1.0",
"@modern-js/app-tools": "2.32.0",
"@modern-js/eslint-config": "2.32.0",
"@modern-js/tsconfig":"2.32.0",
Expand Down
2 changes: 1 addition & 1 deletion complete-react-case/component-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"webpack": "webpack --watch",
"serve": "serve dist -p 3001",
"serve": "serve dist -p 3001 --cors",
"start": "concurrently \"npm run webpack\" \"npm run serve\""
},
"keywords": [],
Expand Down
5 changes: 5 additions & 0 deletions complete-react-case/component-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ module.exports = {
publicPath: 'http://localhost:3001/',
clean: true,
},
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
resolve: {
extensions: ['.jsx', '.js', '.json', '.css', '.scss', '.jpg', 'jpeg', 'png'],
},
Expand Down
2 changes: 1 addition & 1 deletion complete-react-case/lib-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"webpack": "webpack --watch",
"serve": "serve dist -p 3000",
"serve": "serve dist -p 3000 --cors",
"start": "concurrently \"npm run webpack\" \"npm run serve\""
},
"keywords": [],
Expand Down
5 changes: 5 additions & 0 deletions complete-react-case/lib-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ module.exports = {
publicPath: 'http://localhost:3000/',
clean: true,
},
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
module: {},
plugins: [
new ModuleFederationPlugin({
Expand Down
34 changes: 33 additions & 1 deletion complete-react-case/main-app/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
import('./bootstrap.js');
// top level await based
const promises = []
__webpack_require__.f.remotes('main',promises);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardcoded to main as proof of concept, in reality this would be part of codegen, injected into all entrypoint modules, each passing their own chunkID to the handlers.
effectively making startup asynchronous.

You can also export require(bootstrap) back out, however - the entrypoint will always export a promise. If webpack is handling this entrpoint loading, then async modules will resolve their inner promises. If not, your entrypoint will always export Promise.resolve(module.exports).

In most cases this should not be a problem, as the act of loading an entrypoint is async in ietslf so much should not experience an issue.

The other option involves replacing entrypoint startup code with a promise based export

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not required if we use the AsyncBoundaryPlugin from modernjs example

await Promise.all(promises);
console.log(promises)
const thing = await new Promise((r)=>r('test'))
console.log(thing);


if(globalThis.neverTrue) {
// dynamic import data uri
import('data:text/javascript,export default 42');
}
export default {}


// promise based


// const promises = []
// __webpack_require__.f.remotes('main',promises);

// const exportedModule = Promise.all(promises).then(()=>{
// // can also be
// // return require('./bootstrap')
// return import(/* webpackMode: "eager" */'./bootstrap');
// })
// import(/* webpackMode: "eager" */'./bootstrap');
// if(globalThis.neverTrue) {
// // dynamic import data uri
// import('data:text/javascript,export default 42');
// }
// export default exportedModule
3 changes: 1 addition & 2 deletions complete-react-case/main-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
"name": "complete-react-case_main-app",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"webpack": "webpack --watch",
"serve": "serve dist -p 3002",
"serve": "serve dist -p 3002 --cors",
"start": "concurrently \"npm run webpack\" \"npm run serve\""
},
"keywords": [],
Expand Down
9 changes: 9 additions & 0 deletions complete-react-case/main-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ module.exports = {
entry: './index.js',
mode: 'development',
devtool: 'hidden-source-map',
cache:false,
// experiments: {
// topLevelAwait: true
// },
output: {
publicPath: 'http://localhost:3002/',
clean: true,
Expand All @@ -28,6 +32,11 @@ module.exports = {
},
],
},
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
plugins: [
new ModuleFederationPlugin({
name: 'main_app',
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7578,7 +7578,7 @@
"@module-federation/utilities" "^1.3.0"
node-fetch "^2.6.0"

"@module-federation/[email protected]":
"@module-federation/[email protected]", "@module-federation/enhanced@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@module-federation/enhanced/-/enhanced-0.1.0.tgz#65d855e941c473e4f0cd12cc1c069ae9c8fdc6f3"
integrity sha512-CymUY20htVd55Ff0G8tNeh1q3at/6xRa26Db8KcO1537aV41CFC5qRcTWd58EG9pgJhu9psyJPVaAjFXliB66g==
Expand Down