Skip to content

Commit

Permalink
fix: shared lib load failed (#338)
Browse files Browse the repository at this point in the history
* fix: shared lib load failed

* test: remove deleted features and parameters
  • Loading branch information
ruleeeer committed Feb 8, 2023
1 parent 97ed8f7 commit 49b6cae
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 375 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default defineConfig({
shared: ['vue', 'pinia']
})
],
base: 'http://localhost:5003',
base: 'http://localhost:5001',
build: {
minify: false,
target: ["chrome89", "edge89", "firefox89", "safari15"]
Expand Down
8 changes: 4 additions & 4 deletions packages/examples/vue3-demo-esm-expose-store/host/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
import {counterState} from 'remote-store/remoteStore'

export default {
setup() {
let counter = counterState();
return {counter};
},
data(){
let counter = counterState()
return {counter}
}
};
</script>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import federation from '@originjs/vite-plugin-federation'
import topLevelAwait from 'vite-plugin-top-level-await'

// https://vitejs.dev/config/
export default defineConfig({
Expand All @@ -19,14 +18,15 @@ export default defineConfig({
pinia: {}
}
}),
topLevelAwait({
// The export name of top-level await promise for each chunk module
promiseExportName: "__tla",
// The function to generate import names of top-level await promise in each chunk module
promiseImportName: i => `__tla_${i}`
})
// topLevelAwait({
// // The export name of top-level await promise for each chunk module
// promiseExportName: "__tla",
// // The function to generate import names of top-level await promise in each chunk module
// promiseImportName: i => `__tla_${i}`
// })
],
build: {
target:'esnext',
assetsInlineLimit: 40960,
minify: false,
cssCodeSplit: false,
Expand Down
6 changes: 3 additions & 3 deletions packages/examples/vue3-demo-esm/home/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export default defineConfig({
generate:false
},
// This is to test if the custom library can be SHARED, there is no real point
myStore:{
packagePath:'./src/store.js'
}
// myStore:{
// packagePath:'./src/store.js'
// }
}
}),
topLevelAwait({
Expand Down
1 change: 0 additions & 1 deletion packages/examples/vue3-demo-esm/layout/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export default defineConfig({
shared: {
vue:{
// This is an invalid configuration, because the generate attribute is not supported on the host side
generate:false
},
pinia:{
}
Expand Down
6 changes: 6 additions & 0 deletions packages/lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ export default function federation(
if (v) {
return v
}
if (args[0] === '\0virtual:__federation_fn_import') {
return {
id: '\0virtual:__federation_fn_import',
moduleSideEffects: true
}
}
return null
},

Expand Down
236 changes: 151 additions & 85 deletions packages/lib/src/prod/remote-production.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,92 +54,103 @@ export function prodRemotePlugin(
virtualFile: {
// language=JS
__federation__: `
${createRemotesMap(remotes)}
const loadJS = async (url, fn) => {
const resolvedUrl = typeof url === 'function' ? await url() : url;
const script = document.createElement('script')
script.type = 'text/javascript';
script.onload = fn;
script.src = resolvedUrl;
document.getElementsByTagName('head')[0].appendChild(script);
}
const scriptTypes = ['var'];
const importTypes = ['esm', 'systemjs']
function get(name, ${REMOTE_FROM_PARAMETER}){
return __federation_import(name).then(module => ()=> {
if (${REMOTE_FROM_PARAMETER} === 'webpack') {
return Object.prototype.toString.call(module).indexOf('Module') > -1 && module.default ? module.default : module
}
return module
})
}
const wrapShareModule = ${REMOTE_FROM_PARAMETER} => {
return {
${getModuleMarker('shareScope')}
}
}
async function __federation_import(name){
return import(name);
}
const initMap = Object.create(null);
async function __federation_method_ensure(remoteId) {
const remote = remotesMap[remoteId];
if (!remote.inited) {
if (scriptTypes.includes(remote.format)) {
// loading js with script tag
return new Promise(resolve => {
const callback = () => {
if (!remote.inited) {
remote.lib = window[remoteId];
remote.lib.init(wrapShareModule(remote.from))
remote.inited = true;
}
resolve(remote.lib);
}
return loadJS(remote.url, callback);
});
} else if (importTypes.includes(remote.format)) {
// loading js with import(...)
return new Promise((resolve, reject) => {
const getUrl = typeof remote.url === 'function' ? remote.url : () => Promise.resolve(remote.url);
getUrl().then(url => {
import(/* @vite-ignore */ url).then(lib => {
if (!remote.inited) {
const shareScope = wrapShareModule(remote.from)
lib.init(shareScope);
remote.lib = lib;
remote.lib.init(shareScope);
remote.inited = true;
}
resolve(remote.lib);
}).catch(reject)
})
})
}
} else {
return remote.lib;
}
}
${createRemotesMap(remotes)}
const loadJS = async (url, fn) => {
const resolvedUrl = typeof url === 'function' ? await url() : url;
const script = document.createElement('script')
script.type = 'text/javascript';
script.onload = fn;
script.src = resolvedUrl;
document.getElementsByTagName('head')[0].appendChild(script);
}
const scriptTypes = ['var'];
const importTypes = ['esm', 'systemjs']
function __federation_method_unwrapDefault(module) {
return (module?.__esModule || module?.[Symbol.toStringTag] === 'Module')?module.default:module
}
function get(name, ${REMOTE_FROM_PARAMETER}) {
return __federation_import(name).then(module => () => {
if (${REMOTE_FROM_PARAMETER} === 'webpack') {
return Object.prototype.toString.call(module).indexOf('Module') > -1 && module.default ? module.default : module
}
return module
})
}
function __federation_method_wrapDefault(module ,need){
if (!module?.default && need) {
let obj = Object.create(null);
obj.default = module;
obj.__esModule = true;
return obj;
}
return module;
}
const wrapShareModule = ${REMOTE_FROM_PARAMETER} => {
return {
${getModuleMarker('shareScope')}
}
}
function __federation_method_getRemote(remoteName, componentName){
return __federation_method_ensure(remoteName).then((remote) => remote.get(componentName).then(factory => factory()));
}
export {__federation_method_ensure, __federation_method_getRemote , __federation_method_unwrapDefault , __federation_method_wrapDefault}
`
async function __federation_import(name) {
return import(name);
}
const initMap = Object.create(null);
async function __federation_method_ensure(remoteId) {
const remote = remotesMap[remoteId];
if (!remote.inited) {
if (scriptTypes.includes(remote.format)) {
// loading js with script tag
return new Promise(resolve => {
const callback = () => {
if (!remote.inited) {
remote.lib = window[remoteId];
remote.lib.init(wrapShareModule(remote.from))
remote.inited = true;
}
resolve(remote.lib);
}
return loadJS(remote.url, callback);
});
} else if (importTypes.includes(remote.format)) {
// loading js with import(...)
return new Promise((resolve, reject) => {
const getUrl = typeof remote.url === 'function' ? remote.url : () => Promise.resolve(remote.url);
getUrl().then(url => {
import(/* @vite-ignore */ url).then(lib => {
if (!remote.inited) {
const shareScope = wrapShareModule(remote.from)
lib.init(shareScope);
remote.lib = lib;
remote.lib.init(shareScope);
remote.inited = true;
}
resolve(remote.lib);
}).catch(reject)
})
})
}
} else {
return remote.lib;
}
}
function __federation_method_unwrapDefault(module) {
return (module?.__esModule || module?.[Symbol.toStringTag] === 'Module') ? module.default : module
}
function __federation_method_wrapDefault(module, need) {
if (!module?.default && need) {
let obj = Object.create(null);
obj.default = module;
obj.__esModule = true;
return obj;
}
return module;
}
function __federation_method_getRemote(remoteName, componentName) {
return __federation_method_ensure(remoteName).then((remote) => remote.get(componentName).then(factory => factory()));
}
export {
__federation_method_ensure,
__federation_method_getRemote,
__federation_method_unwrapDefault,
__federation_method_wrapDefault
}
`
},

async transform(this: TransformPluginContext, code: string, id: string) {
Expand All @@ -158,7 +169,7 @@ export {__federation_method_ensure, __federation_method_getRemote , __federation
}${
sharedInfo[1].root ? sharedInfo[1].root[0] + '/' : ''
}${basename}`,
preserveSignature: 'allow-extension',
preserveSignature: 'strict',
name: sharedInfo[0]
})
sharedFileName2Prop.set(basename, sharedInfo as ConfigTypeSet)
Expand Down Expand Up @@ -232,7 +243,9 @@ export {__federation_method_ensure, __federation_method_getRemote , __federation
})
return code.replace(getModuleMarker('shareScope'), res.join(','))
}
}

if (builderInfo.isHost || builderInfo.isShared) {
let ast: AcornNode | null = null
try {
ast = this.parse(code)
Expand All @@ -246,8 +259,53 @@ export {__federation_method_ensure, __federation_method_getRemote , __federation
const magicString = new MagicString(code)
const hasStaticImported = new Map<string, string>()
let requiresRuntime = false
let needImportShared = false
let modify = false
walk(ast, {
enter(node: any) {
// handle share, eg. replace import {a} from b -> const a = importShared('b')
if (node.type === 'ImportDeclaration') {
const moduleName = node.source.value
if (
parsedOptions.prodShared.some(
(sharedInfo) => sharedInfo[0] === moduleName
)
) {
const declaration: (string | never)[] = []
if (!node.specifiers?.length) {
// invalid import , like import './__federation_shared_lib.js' , and remove it
magicString.remove(node.start, node.end)
modify = true
} else {
node.specifiers.forEach((specify) => {
declaration.push(
`${
specify.imported?.name
? `${
specify.imported.name === specify.local.name
? specify.local.name
: `${specify.imported.name}:${specify.local.name}`
}`
: `default:${specify.local.name}`
}`
)
})
needImportShared = true
}
if (declaration.length) {
magicString.overwrite(
node.start,
node.end,
`const {${declaration.join(
','
)}} = await importShared('${moduleName}');\n`
)
needImportShared = true
}
}
}

// handle remote import , eg replace import {a} from 'remote/b' to dynamic import
if (
(node.type === 'ImportExpression' ||
node.type === 'ImportDeclaration' ||
Expand Down Expand Up @@ -384,7 +442,15 @@ export {__federation_method_ensure, __federation_method_getRemote , __federation
)
}

return magicString.toString()
if (needImportShared) {
magicString.prepend(
`import {importShared} from '\0virtual:__federation_fn_import';\n`
)
}

if (requiresRuntime || needImportShared || modify) {
return magicString.toString()
}
}
}
}
Expand Down

0 comments on commit 49b6cae

Please sign in to comment.