From 7b76cbdd54378aa56a36f3eb7d4a0935f4ad8c4a Mon Sep 17 00:00:00 2001 From: zhangHongEn Date: Sun, 20 Nov 2022 02:00:38 +0800 Subject: [PATCH] umf compeled --- packages/umfjs/package.json | 4 +- .../README.md | 85 +++++++++----- .../package.json | 4 +- .../src/UmdPlugin.js | 107 ++++++++++++++++++ .../src/UniversalModuleFederationPlugin.js | 29 +++-- .../src/index.js | 98 +--------------- .../src/utils/stringifyHasFn.js | 40 ++++--- 7 files changed, 213 insertions(+), 154 deletions(-) create mode 100644 packages/universal-module-federation-plugin/src/UmdPlugin.js diff --git a/packages/umfjs/package.json b/packages/umfjs/package.json index a4a1140..da64ae3 100644 --- a/packages/umfjs/package.json +++ b/packages/umfjs/package.json @@ -1,6 +1,6 @@ { "name": "umfjs", - "version": "0.3.0", + "version": "0.3.1", "description": "universal module federation", "main": "dist/index.js", "devDependencies": { @@ -16,7 +16,7 @@ "webpack-dev-server": "^4.0.0" }, "dependencies": { - "semverhook": "^1.0.7", + "semverhook": "^1.1.0", "systemjs": "^6.13.0" }, "scripts": { diff --git a/packages/universal-module-federation-plugin/README.md b/packages/universal-module-federation-plugin/README.md index 65b89fe..209378b 100644 --- a/packages/universal-module-federation-plugin/README.md +++ b/packages/universal-module-federation-plugin/README.md @@ -70,50 +70,81 @@ runtimeUmdExposes({ $umdValue, $moduleName }) { ## custom module specification -If you have modified systemjs, or you have your own module specification, you can use UniversalModuleFederationPlugin to integrate it. The following is the source code of UmdPlugin, and the explanation of each API will be updated after the documentation. +If you have your own module specification, you can use UniversalModuleFederationPlugin to integrate it. +UmdPlugin is implemented using this plugin, you can refer to the [UmdPlugin source code](./src/UmdPlugin.js) + ``` js // webpack.config.js - -const PLUGIN_NAME = 'UmdPlugin'; const {UniversalModuleFederationPlugin} = require("universal-module-federation-plugin") -class UmdPlugin { +class CustomPlugin { apply(compiler) { new UniversalModuleFederationPlugin({ - runtimeInject: ({$semverhook, $getShare, $getRemote, $containerRemoteKeyMap, $injectVars}) => { - const {interceptSystemAllDep} = require("umfjs") - const {System, eventBus} = interceptSystemAllDep() - - $semverhook.on("import", (url) => { + includeRemotes: [/.*/], + excludeRemotes: [], + runtimeInject: { + // You can access "__umf__.$injectVars.testInjectVar" in any of the following runtime hooks + injectVars: { + testInjectVar: 111, + }, + // any runtime hook can using "__umf__" + initial: () => { + const {$getShare, $getRemote, $containerRemoteKeyMap, $injectVars, $context} = __umf__ + const testInjectVar = $injectVars + console.log("__umf__", __umf__, testInjectVar) + // $context is an empty object by default, used to pass values between multiple hooks + $context.testA = "testA" + }, + beforeImport(url) { + console.log(__umf__.$context) + return new Promise(resolve => { + setTimeout(function () { + resolve(url) + }, 3000) + }) + }, + import(url) { + console.log("__umf__", __umf__) return { init(){}, async get(moduleName = "") { - const res = await System.import(url) return function() { - return umdExposes({ - $umdValue: res, - $moduleName: moduleName - }) + return { + testModule: 123 + } } } } - }) - - eventBus.on("importDep", (dep) => { - if (referenceRemotes[dep]) { - return $getRemote(referenceRemotes[dep] || dep) - } - }) - eventBus.on("importDep", (dep) => { - if (referenceShares[dep]) { - return $getShare(referenceShares[dep].import || dep, referenceShares[dep]) - } - }) + } } }).apply(compiler) } } -``` \ No newline at end of file +``` + +| options | desc | default | examles | +|----------------------------------------------|--------------------------------------------------------------------------------------------|--------------|:--------------------| +| includeRemotes | match umd remotes | [] | [/umd-app/, "app3"] | +| excludeRemotes | exclude umd remotes | [] | ["app2"] | +| runtimeInject.injectVars | Inject variables for other runtime hooks, any runtime hook can using "\_\_umf\_\_.$injectVars" | {} | {test: 123} | +| runtimeInject.initial() | initial runtime hooks | function(){} | | +| runtimeInject.beforeImport(url):promise | Triggered before each remote is introduced | function(){} | | +| runtimeInject.import(url):promise | Introduce the hook of remote, need to return a container{init, get} | function(){} | + +#### \_\_umf\_\_ + +Any runtime hooks will inject the "\_\_umf\_\_" variable + + +| property | desc | examles | +|----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|-------------------------------------------| +| $getRemote("request"):promise | Get the remote module, same as import xxx from "xxxx/xxx" | $getRemote("app2/App") | +| $getShare(pkg, {singleton, requiredVersion, ......}):promise | Get modules from shareScopes, same as "shared" configuration | $getShare("react", {singleton: true}) | +| $containerRemoteKeyMap: object | The container name corresponds to the map of the key configured by remotes +remotes: {"@app2/xx": "app3@http://xxx"} | $containerRemoteKeyMap.app3 // "@app2/xx" | +| $injectVars: object | Variables injected by plugins | | +| $context: object | $context is an empty object by default, used to pass values between multiple hooks | $context.xxx = xxx | + diff --git a/packages/universal-module-federation-plugin/package.json b/packages/universal-module-federation-plugin/package.json index f2a1aac..7c1cf83 100644 --- a/packages/universal-module-federation-plugin/package.json +++ b/packages/universal-module-federation-plugin/package.json @@ -1,6 +1,6 @@ { "name": "universal-module-federation-plugin", - "version": "0.3.2", + "version": "0.3.3", "description": "Module federation supports the integration of various specifications, such as umd", "main": "src/index.js", "keywords": [ @@ -11,7 +11,7 @@ ], "devDependencies": {}, "dependencies": { - "semverhook": "^1.0.7", + "semverhook": "^1.1.0", "inject-webpack": "^0.1.5", "webpack-virtual-modules": "^0.4.6", "umfjs": "^0.3.0" diff --git a/packages/universal-module-federation-plugin/src/UmdPlugin.js b/packages/universal-module-federation-plugin/src/UmdPlugin.js new file mode 100644 index 0000000..9b89655 --- /dev/null +++ b/packages/universal-module-federation-plugin/src/UmdPlugin.js @@ -0,0 +1,107 @@ + +const PLUGIN_NAME = 'UmdPlugin'; +const UniversalModuleFederationPlugin = require("./UniversalModuleFederationPlugin") + +class UmdPlugin { + constructor(options = {}) { + options = Object.assign({ + includeRemotes: [], + excludeRemotes: [], + dependencies: { + // referenceShares: { + // react: { + // singleton: true + // } + // }, + // referenceRemotes: { + // "Button": "app5/Button" + // }, + // automatic: ["remotes", "shareScopes"] + }, + runtimeUmdExposes({$umdValue, $moduleName}) { + return $umdValue + } + }, options) + this.options = options + if (!options.dependencies.referenceShares) { + options.dependencies.referenceShares = {} + } + if (!options.dependencies.referenceRemotes) { + options.dependencies.referenceRemotes = {} + } + if (!options.dependencies.automatic) { + options.dependencies.automatic = ["remotes", "shareScopes"] + } + } + + apply(compiler) { + new UniversalModuleFederationPlugin({ + includeRemotes: this.options.includeRemotes, + excludeRemotes: this.options.excludeRemotes, + runtimeInject: { + injectVars: { + referenceShares: this.options.dependencies.referenceShares, + referenceRemotes: this.options.dependencies.referenceRemotes, + umdExposes: this.options.runtimeUmdExposes, + automatic: this.options.dependencies.automatic, + }, + initial: () => { + const {$getShare, $getRemote, $containerRemoteKeyMap, $injectVars, $context} = __umf__ + const { + referenceRemotes, + referenceShares, + automatic + } = $injectVars + const {interceptSystemAllDep} = require("umfjs") + const {System, eventBus} = interceptSystemAllDep() + $context.System = System + const isInterceptDepFromAllRemotes = automatic.indexOf("remotes") > -1 + const isInterceptDepFromAllShares = automatic.indexOf("shareScopes") > -1 + + eventBus.on("importDep", (dep) => { + if (referenceRemotes[dep]) { + return $getRemote(referenceRemotes[dep] || dep) + } + if (isInterceptDepFromAllRemotes) { + const containerName = Object.keys($containerRemoteKeyMap).filter(function (containerName) { + const remoteKey = $containerRemoteKeyMap[containerName] + return remoteKey === dep + })[0] + return containerName && $getRemote(dep) + } + }) + eventBus.on("importDep", (dep) => { + if (/^https?:/.test(dep)) return + if (referenceShares[dep]) { + return $getShare(referenceShares[dep].import || dep, referenceShares[dep]) + } + if (isInterceptDepFromAllShares) { + return $getShare(dep, {singleton: true}) + } + }) + }, + import(url) { + const {$injectVars} = __umf__ + const { + umdExposes, + } = $injectVars + return { + init(){}, + async get(moduleName = "") { + const res = await __umf__.$context.System.import(url) + return function() { + return umdExposes({ + $umdValue: res, + $moduleName: moduleName + }) + } + } + } + } + } + }).apply(compiler) + } + +} + +module.exports = UmdPlugin \ No newline at end of file diff --git a/packages/universal-module-federation-plugin/src/UniversalModuleFederationPlugin.js b/packages/universal-module-federation-plugin/src/UniversalModuleFederationPlugin.js index 7bf82a3..228858c 100644 --- a/packages/universal-module-federation-plugin/src/UniversalModuleFederationPlugin.js +++ b/packages/universal-module-federation-plugin/src/UniversalModuleFederationPlugin.js @@ -5,6 +5,7 @@ const PLUGIN_NAME = 'UniversalModuleFederationPlugin'; const {ExternalModule} = require("webpack") const {ModuleFederationPlugin} = require("webpack").container const stringifyHasFn = require("./utils/stringifyHasFn") + let hookIndex = 0 class UniversalModuleFederationPlugin { @@ -12,9 +13,16 @@ class UniversalModuleFederationPlugin { options = Object.assign({ includeRemotes: [], excludeRemotes: [], - runtimeInject: function ({$semverhook, $getShare, $getRemote, $injectVars}) {}, - runtimeInjectVars: {} + runtimeInject: {} }, options) + options.runtimeInject = Object.assign({ + injectVars: {}, + initial(){}, + beforeImport(){}, + resolvePath() {}, + resolveRequest() {}, + import() {} + }, options.runtimeInject) this.options = options this.appName = "" this.hookIndex = ++hookIndex @@ -31,7 +39,8 @@ class UniversalModuleFederationPlugin { $getRemote: null, $getShare: null, $containerRemoteKeyMap: null, - $injectVars: null + $injectVars: null, + $context: {} } if (!window.__umfplugin__semverhook_${this.appName}_${this.hookIndex}) { ;(function () { @@ -56,16 +65,20 @@ class UniversalModuleFederationPlugin { } return (await window[containerName].get(moduleName))() } - __umf__ = { + Object.assign(__umf__, { $semverhook: window.__umfplugin__semverhook_${this.appName}_${this.hookIndex}, $getRemote, $getShare, $containerRemoteKeyMap: ${JSON.stringify(this.containerRemoteKeyMap)}, - $injectVars: ${stringifyHasFn(this.options.runtimeInjectVars)} - } + }) })(); - const injectFn = ${stringifyHasFn({ fn: this.options.runtimeInject })}.fn - injectFn(__umf__) + const __runtimeInject = ${stringifyHasFn(this.options.runtimeInject)} + __umf__.$injectVars = __runtimeInject.injectVars + __runtimeInject.initial() + __umf__.$semverhook.on("beforeImport", __runtimeInject.beforeImport) + __umf__.$semverhook.on("import", __runtimeInject.import) + __umf__.$semverhook.on("resolvePath", __runtimeInject.resolvePath) + __umf__.$semverhook.on("resolveRequest", __runtimeInject.resolveRequest) } ` new Inject(() => { diff --git a/packages/universal-module-federation-plugin/src/index.js b/packages/universal-module-federation-plugin/src/index.js index da41348..b8b1d52 100644 --- a/packages/universal-module-federation-plugin/src/index.js +++ b/packages/universal-module-federation-plugin/src/index.js @@ -1,103 +1,7 @@ -const PLUGIN_NAME = 'UmdPlugin'; +const UmdPlugin = require("./UmdPlugin") const UniversalModuleFederationPlugin = require("./UniversalModuleFederationPlugin") -class UmdPlugin { - constructor(options = {}) { - options = Object.assign({ - includeRemotes: [], - excludeRemotes: [], - dependencies: { - // referenceShares: { - // react: { - // singleton: true - // } - // }, - // referenceRemotes: { - // "Button": "app5/Button" - // }, - // automatic: ["remotes", "shareScopes"] - }, - runtimeUmdExposes({$umdValue, $moduleName}) { - return $umdValue - } - }, options) - this.options = options - if (!options.dependencies.referenceShares) { - options.dependencies.referenceShares = {} - } - if (!options.dependencies.referenceRemotes) { - options.dependencies.referenceRemotes = {} - } - if (!options.dependencies.automatic) { - options.dependencies.automatic = ["remotes", "shareScopes"] - } - } - - apply(compiler) { - new UniversalModuleFederationPlugin({ - includeRemotes: this.options.includeRemotes, - excludeRemotes: this.options.excludeRemotes, - runtimeInjectVars: { - referenceShares: this.options.dependencies.referenceShares, - referenceRemotes: this.options.dependencies.referenceRemotes, - umdExposes: this.options.runtimeUmdExposes, - automatic: this.options.dependencies.automatic, - }, - runtimeInject: ({$semverhook, $getShare, $getRemote, $containerRemoteKeyMap, $injectVars}) => { - const { - referenceRemotes, - referenceShares, - umdExposes, - automatic - } = $injectVars - const {interceptSystemAllDep} = require("umfjs") - const {System, eventBus} = interceptSystemAllDep() - const isInterceptDepFromAllRemotes = automatic.indexOf("remotes") > -1 - const isInterceptDepFromAllShares = automatic.indexOf("shareScopes") > -1 - - $semverhook.on("import", (url) => { - return { - init(){}, - async get(moduleName = "") { - const res = await System.import(url) - return function() { - return umdExposes({ - $umdValue: res, - $moduleName: moduleName - }) - } - } - } - }) - - eventBus.on("importDep", (dep) => { - if (referenceRemotes[dep]) { - return $getRemote(referenceRemotes[dep] || dep) - } - if (isInterceptDepFromAllRemotes) { - const containerName = Object.keys($containerRemoteKeyMap).filter(function (containerName) { - const remoteKey = $containerRemoteKeyMap[containerName] - return remoteKey === dep - })[0] - return containerName && $getRemote(dep) - } - }) - eventBus.on("importDep", (dep) => { - if (/^https?:/.test(dep)) return - if (referenceShares[dep]) { - return $getShare(referenceShares[dep].import || dep, referenceShares[dep]) - } - if (isInterceptDepFromAllShares) { - return $getShare(dep, {singleton: true}) - } - }) - } - }).apply(compiler) - } - -} - module.exports = { UmdPlugin, UniversalModuleFederationPlugin diff --git a/packages/universal-module-federation-plugin/src/utils/stringifyHasFn.js b/packages/universal-module-federation-plugin/src/utils/stringifyHasFn.js index b9980de..7c03e53 100644 --- a/packages/universal-module-federation-plugin/src/utils/stringifyHasFn.js +++ b/packages/universal-module-federation-plugin/src/utils/stringifyHasFn.js @@ -1,21 +1,25 @@ +const {Template} = require("webpack") module.exports = function stringifyHasFn (obj) { - return JSON.parse(JSON.stringify({ - ...obj, - toJSON () { - const funJSON = [] - let hasFunction = false - let hasOther = false - Object.keys(obj).forEach(key => { - if (typeof obj[key] !== "function") { - hasOther = true - return - } - hasFunction = true - - const [args, body] = obj[key].toString().match(/\(([.\s\S]*?\))|(\{[.\s\S]*)\}/g) - funJSON.push(`"${key}": function ${args} ${body}`) - }) - return JSON.stringify(obj).replace(/\}$/, `${hasFunction && hasOther ? "," : ""}${funJSON.join(",")}}`) + const fns = [] + function converter(key, val) { + if (typeof val === 'function' || val && val.constructor === RegExp) { + const seat = `// # ---json-fn---start---${key}--${fns.length - 1}// # ---json-fn---end---` + fns.push([seat, val]) + return seat } - })) + return val + } + let jsonStr = JSON.stringify(obj, converter) + fns.forEach((fnInfo, index) => { + const seat = fnInfo[0] + const fnStr = fnInfo[1].toString() + const isAsyncFn = fnStr.substring(0, fnStr.indexOf("(")).indexOf("async ") > -1 + const argsAndBody = fnStr.match(/\([\s\S]*\}/)[0] + const isArrorFun = /^\([\s\S]*\)[\s]*=>[\s]*\{/.test(argsAndBody) + let fnStatement = isArrorFun ? argsAndBody : `function ${argsAndBody}` + if (isAsyncFn) fnStatement = `async ${fnStatement}` + + jsonStr = jsonStr.replace(`"${seat}"`, fnStatement) + }) + return jsonStr } \ No newline at end of file