diff --git a/.changeset/chilled-radios-retire.md b/.changeset/chilled-radios-retire.md new file mode 100644 index 000000000..2c4f91db6 --- /dev/null +++ b/.changeset/chilled-radios-retire.md @@ -0,0 +1,5 @@ +--- +"@qiankunjs/shared": patch +--- + +feat: add online solution links for qiankun diff --git a/.dumi/pages/error/CodeSnippet.tsx b/.dumi/pages/error/CodeSnippet.tsx new file mode 100644 index 000000000..15d3639e5 --- /dev/null +++ b/.dumi/pages/error/CodeSnippet.tsx @@ -0,0 +1,16 @@ +import React from 'react' + +export default function CodeSnippet(props) { + const lines = props.children.split('\n') + const firstTextualLine = lines.find(l => l.trim() !== l) + const numLeadingSpaces = firstTextualLine ? /([^\s])/.exec(firstTextualLine).index : 0 + const formattedLines = lines.map(line => line.slice(numLeadingSpaces)).filter((line, i) => line.length > 0 || i > 0).join('\n') + return ( +
+
+      {formattedLines}
+    
+
+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/1.tsx b/.dumi/pages/error/codes/1.tsx new file mode 100644 index 000000000..c2fc5ab05 --- /dev/null +++ b/.dumi/pages/error/codes/1.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#1: Application died in status NOT_MOUNTED: Target container with #container not existed after {props.getErrorCodeArg(0)} mounted!

+

+ This error thrown as the container DOM does not exist after the micro app is loaded. The possible reasons are: +

+

+ 1.The root id of the micro app conflicts with other DOM, and the solution is to modify the search range of the root id. +

+
+

vue micro app:

+ + {` + function render(props = {}) { + const { container } = props; + instance = new Vue({ + router, + store, + render: (h) => h(App), + }).$mount(container ? container.querySelector('#app') : '#app'); + } + export async function mount(props) { + render(props); + } + `} + +

react micro app:

+ + { ` + function render(props) { + const { container } = props; + ReactDOM.render(, container ? container.querySelector('#root') : document.querySelector('#root')); + } + export async function mount(props) { + render(props); + } + export async function unmount(props) { + const { container } = props; + ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root')); + } + `} + +
+

Explanation:

+

+ FAQ +

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/2.tsx b/.dumi/pages/error/codes/2.tsx new file mode 100644 index 000000000..8bc3bc787 --- /dev/null +++ b/.dumi/pages/error/codes/2.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#2: You should not set multiply entry script in one entry html, but {props.getErrorCodeArg(0)} has at least 2 entry scripts

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/3.tsx b/.dumi/pages/error/codes/3.tsx new file mode 100644 index 000000000..859b9b2ba --- /dev/null +++ b/.dumi/pages/error/codes/3.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#3: entry {props.getErrorCodeArg(0)} load failed as entry script {props.getErrorCodeArg(1)} load failed

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/4.tsx b/.dumi/pages/error/codes/4.tsx new file mode 100644 index 000000000..524fbe3a2 --- /dev/null +++ b/.dumi/pages/error/codes/4.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#4: entry {props.getErrorCodeArg(0)} response body is empty!

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/5.tsx b/.dumi/pages/error/codes/5.tsx new file mode 100644 index 000000000..b394824eb --- /dev/null +++ b/.dumi/pages/error/codes/5.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#5: {props.getErrorCodeArg(0)} entry {props.getErrorCodeArg(1)} load failed as it not export lifecycles

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/6.tsx b/.dumi/pages/error/codes/6.tsx new file mode 100644 index 000000000..da0e4f98d --- /dev/null +++ b/.dumi/pages/error/codes/6.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#6: You need to export lifecycle functions in {props.getErrorCodeArg(0)} entry

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/7.tsx b/.dumi/pages/error/codes/7.tsx new file mode 100644 index 000000000..2b849ec56 --- /dev/null +++ b/.dumi/pages/error/codes/7.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#7: {props.getErrorCodeArg(0)} head element not existed while accessing document.head!

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/codes/8.tsx b/.dumi/pages/error/codes/8.tsx new file mode 100644 index 000000000..faa4f2ba2 --- /dev/null +++ b/.dumi/pages/error/codes/8.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import CodeSnippet from '../CodeSnippet' + +export default function ErrorCode1(props) { + return ( + <> +

#8: {props.getErrorCodeArg(0)} container {props.getErrorCodeArg(1)} element not ready while rebuilding!

+ + ) +} \ No newline at end of file diff --git a/.dumi/pages/error/index.tsx b/.dumi/pages/error/index.tsx new file mode 100644 index 000000000..2cb925ed4 --- /dev/null +++ b/.dumi/pages/error/index.tsx @@ -0,0 +1,29 @@ +/** + * sidebar: false + */ + +import React from 'react' +import { useLocation, useParams } from 'react-router-dom'; +import Error1 from './codes/1' +import { useSearchParams } from 'dumi'; +const r = require.context('./codes/', false, /\.tsx$/); + +export default function CodeSnippet() { + let [searchParams, setSearchParams] = useSearchParams(); + const code = searchParams.get('code'); + const Comp = code && r(`./${code}.tsx`).default; + const errorCodeArgs = searchParams.getAll('arg'); + + return code ? ( +
+ +
+ ) : (
Please enter the error code
) + + function getErrorCodeArg(index, argName) { + const missingArg = argName ? `(${argName})` : `(unknown)`; + return errorCodeArgs.length > index ? errorCodeArgs[index] : missingArg; + } +} \ No newline at end of file diff --git a/.dumirc.ts b/.dumirc.ts index 73ac6f343..6d1a42f58 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -58,4 +58,5 @@ export default defineConfig({ theme: { '@c-primary': '#6451AB', }, + styles: [`li a[href="/qiankun/error"],li a[href="/qiankun/error"] ~ * { display: none}]`], }); diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts index 6f09dad70..de0de1c60 100644 --- a/packages/loader/src/index.ts +++ b/packages/loader/src/index.ts @@ -121,7 +121,9 @@ export async function loadEntry(entry: Entry, container: HTMLElement, opts: L if (isEntryScript(script)) { if (foundEntryScript) { throw new QiankunError( + 2, `You should not set multiply entry script in one entry html, but ${entry} has at least 2 entry scripts`, + entry, ); } @@ -139,7 +141,12 @@ export async function loadEntry(entry: Entry, container: HTMLElement, opts: L onEntryLoaded(); } else { entryScriptLoadedDeferred.reject( - new QiankunError(`entry ${entry} load failed as entry script ${script.src} load failed}`), + new QiankunError( + 3, + `entry ${entry} load failed as entry script ${script.src} load failed`, + entry, + script.src, + ), ); } } @@ -171,5 +178,5 @@ export async function loadEntry(entry: Entry, container: HTMLElement, opts: L return entryScriptLoadedDeferred.promise; } - throw new QiankunError(`entry ${entry} response body is empty!`); + throw new QiankunError(4, `entry ${entry} response body is empty!`, entry); } diff --git a/packages/qiankun/src/core/loadApp.ts b/packages/qiankun/src/core/loadApp.ts index 85b528b2e..16c1d276e 100644 --- a/packages/qiankun/src/core/loadApp.ts +++ b/packages/qiankun/src/core/loadApp.ts @@ -10,7 +10,7 @@ import { moduleResolver as defaultModuleResolver, transpileAssets, wrapFetchWith import { concat, isFunction, mergeWith } from 'lodash'; import type { ParcelConfigObject } from 'single-spa'; import getAddOns from '../addons'; -import { QiankunError } from '../error'; +import { QiankunError } from '@qiankunjs/shared'; import type { AppConfiguration, LifeCycleFn, LifeCycles, LoadableApp, MicroAppLifeCycles, ObjectType } from '../types'; import { getPureHTMLStringWithoutScripts, @@ -94,7 +94,8 @@ export default async function loadApp( await execHooksChain(toArray(beforeLoad), app, global); const lifecycles = await lifecyclesPromise; - if (!lifecycles) throw new QiankunError(`${appName} entry ${entry} load failed as it not export lifecycles`); + if (!lifecycles) + throw new QiankunError(5, `${appName} entry ${entry} load failed as it not export lifecycles`, appName, entry); const { bootstrap, mount, unmount, update } = getLifecyclesFromExports( lifecycles, appName, @@ -235,7 +236,7 @@ function getLifecyclesFromExports( return globalVariableExports; } - throw new QiankunError(`You need to export lifecycle functions in ${appName} entry`); + throw new QiankunError(6, `You need to export lifecycle functions in ${appName} entry`, appName); } function calcPublicPath(entry: string): string { diff --git a/packages/qiankun/src/error.ts b/packages/qiankun/src/error.ts index a05d3a612..9c4777461 100644 --- a/packages/qiankun/src/error.ts +++ b/packages/qiankun/src/error.ts @@ -1,5 +1,13 @@ export class QiankunError extends Error { - constructor(message: string) { - super(`[qiankun]: ${message}`); + constructor(code: number, message: string, ...args: string[]) { + let errorMessage = `[qiankun #${code}]: ${message ? message + ' ' : ''}`; + if (process.env.NODE_ENV === 'production') { + errorMessage += `See https://qiankun.umijs.org/error/?code=${code}${ + args.length ? `&arg=${args.join('&arg=')}` : '' + }`; + } else { + console.warn('args', ...args); + } + super(errorMessage); } } diff --git a/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts b/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts index d03bf0933..1fffc516f 100644 --- a/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts +++ b/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts @@ -78,7 +78,11 @@ function patchDocument(sandbox: Sandbox, getContainer: () => HTMLElement): Calla const container = getContainer(); const containerHeadElement = getContainerHeadElement(container); if (!containerHeadElement) { - throw new QiankunError(`${sandbox.name} head element not existed while accessing document.head!`); + throw new QiankunError( + 7, + `${sandbox.name} head element not existed while accessing document.head!`, + sandbox.name, + ); } return containerHeadElement; }; @@ -374,7 +378,10 @@ export function patchStandardSandbox( const containerHeadElement = getContainerHeadElement(container); if (!containerHeadElement) { throw new QiankunError( + 8, `${appName} container ${qiankunHeadTagName} element not ready while rebuilding!`, + appName, + qiankunHeadTagName, ); } return containerHeadElement; diff --git a/packages/shared/src/error/QiankunError.ts b/packages/shared/src/error/QiankunError.ts new file mode 100644 index 000000000..9c4777461 --- /dev/null +++ b/packages/shared/src/error/QiankunError.ts @@ -0,0 +1,13 @@ +export class QiankunError extends Error { + constructor(code: number, message: string, ...args: string[]) { + let errorMessage = `[qiankun #${code}]: ${message ? message + ' ' : ''}`; + if (process.env.NODE_ENV === 'production') { + errorMessage += `See https://qiankun.umijs.org/error/?code=${code}${ + args.length ? `&arg=${args.join('&arg=')}` : '' + }`; + } else { + console.warn('args', ...args); + } + super(errorMessage); + } +} diff --git a/packages/shared/src/reporter/QiankunError.ts b/packages/shared/src/reporter/QiankunError.ts index a05d3a612..9c4777461 100644 --- a/packages/shared/src/reporter/QiankunError.ts +++ b/packages/shared/src/reporter/QiankunError.ts @@ -1,5 +1,13 @@ export class QiankunError extends Error { - constructor(message: string) { - super(`[qiankun]: ${message}`); + constructor(code: number, message: string, ...args: string[]) { + let errorMessage = `[qiankun #${code}]: ${message ? message + ' ' : ''}`; + if (process.env.NODE_ENV === 'production') { + errorMessage += `See https://qiankun.umijs.org/error/?code=${code}${ + args.length ? `&arg=${args.join('&arg=')}` : '' + }`; + } else { + console.warn('args', ...args); + } + super(errorMessage); } }