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

feat: optimize lifecycle validation log #2841

Merged
merged 8 commits into from
Dec 21, 2023
7 changes: 7 additions & 0 deletions .changeset/gorgeous-planets-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'qiankun': patch
'@qiankunjs/sandbox': patch
'@qiankunjs/shared': patch
---

feat: optimize lifecycle validate log
6 changes: 6 additions & 0 deletions .changeset/long-radios-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
kuitos marked this conversation as resolved.
Show resolved Hide resolved
"qiankun": patch
"@qiankunjs/shared": patch
---

feat: add lifecycle check log at development environment
36 changes: 25 additions & 11 deletions packages/qiankun/src/core/loadApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import type { LoaderOpts } from '@qiankunjs/loader';
import { loadEntry } from '@qiankunjs/loader';
import type { Sandbox } from '@qiankunjs/sandbox';
import { createSandboxContainer } from '@qiankunjs/sandbox';
import { moduleResolver as defaultModuleResolver, transpileAssets, wrapFetchWithCache } from '@qiankunjs/shared';
import {
moduleResolver as defaultModuleResolver,
getValueType,
transpileAssets,
warn,
wrapFetchWithCache,
} from '@qiankunjs/shared';
import { concat, isFunction, mergeWith } from 'lodash';
import type { ParcelConfigObject } from 'single-spa';
import getAddOns from '../addons';
Expand Down Expand Up @@ -207,37 +213,45 @@ function getLifecyclesFromExports(
globalContext: WindowProxy,
globalLatestSetProp?: PropertyKey,
): MicroAppLifeCycles {
const validateExportLifecycle = (exports: ObjectType | undefined): boolean => {
const { bootstrap, mount, unmount } = exports ?? {};
return isFunction(bootstrap) && isFunction(mount) && isFunction(unmount);
const exportsWarningMsg: string[] = [];
const validateExportLifecycle = (exports: ObjectType | undefined, exportsSource: string): boolean => {
wanghangit marked this conversation as resolved.
Show resolved Hide resolved
const validateLifecycleKeys = ['bootstrap', 'mount', 'unmount'];
const illegalKeys = validateLifecycleKeys.filter((key) => !isFunction(exports?.[key]));
const illegalExports = illegalKeys.length > 0;
if (illegalExports) {
exportsWarningMsg.push(
`${exportsSource} not provide legal lifecycle function found {${illegalKeys
.map((key) => `${key}: ${getValueType(exports?.[key])}`)
.join(',')}}`,
);
}
return !illegalExports;
};

if (validateExportLifecycle(scriptExports)) {
if (validateExportLifecycle(scriptExports, 'entry')) {
return scriptExports;
}

// fallback to sandbox latest set property if it had
if (globalLatestSetProp) {
const lifecycles = (globalContext as unknown as ObjectType)[globalLatestSetProp as never] as MicroAppLifeCycles;
if (validateExportLifecycle(lifecycles)) {
if (validateExportLifecycle(lifecycles, 'globalLatestSetProp')) {
return lifecycles;
}
}

if (process.env.NODE_ENV === 'development') {
console.warn(
`[qiankun] lifecycle not found from ${appName} entry exports, fallback to get from window['${appName}']`,
);
warn(`lifecycle not found from ${appName} entry exports, fallback to get from window['${appName}']`);
}

// fallback to globalContext variable who named with ${appName} while module exports not found
const globalVariableExports = (globalContext as unknown as ObjectType)[appName as never] as MicroAppLifeCycles;

if (validateExportLifecycle(globalVariableExports)) {
if (validateExportLifecycle(globalVariableExports, `window['${appName}']`)) {
return globalVariableExports;
}

throw new QiankunError(`You need to export lifecycle functions in ${appName} entry`);
throw new QiankunError(`You need to export lifecycle functions at any of ${appName} entry, globalLatestSetProp or window['${appName}']\nmore information: ${exportsWarningMsg.join('\n')}`);
wanghangit marked this conversation as resolved.
Show resolved Hide resolved
}

function calcPublicPath(entry: string): string {
Expand Down
2 changes: 1 addition & 1 deletion packages/sandbox/src/patchers/dynamicAppend/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export function getOverwrittenAppendChildOrInsertBefore(
const result = appendChild.call(this, transpiledScriptElement, refChild) as T;

// the script have no src attribute after transpile, indicating that the script needs to wait for the src to be filled
if (externalSyncMode && !transpiledScriptElement.hasAttribute('scr')) {
if (externalSyncMode && !transpiledScriptElement.hasAttribute('src')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

这里看代码应该是拼错顺手改了

queueSyncScript!();
}

Expand Down
21 changes: 21 additions & 0 deletions packages/shared/src/__tests__/util.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, it } from 'vitest';
import { getValueType } from '../utils';

it('should return value type', () => {
expect(getValueType(1)).toBe('Number');
expect(getValueType('1')).toBe('String');
expect(getValueType(true)).toBe('Boolean');
expect(getValueType(null)).toBe('Null');
expect(getValueType(undefined)).toBe('Undefined');
expect(getValueType({})).toBe('Object');
expect(getValueType([])).toBe('Array');
expect(getValueType(function () {})).toBe('Function');
expect(getValueType(Symbol())).toBe('Symbol');
expect(getValueType(new Date())).toBe('Date');
expect(getValueType(new RegExp(''))).toBe('RegExp');
expect(getValueType(new Error())).toBe('Error');
expect(getValueType(new Map())).toBe('Map');
expect(getValueType(new Set())).toBe('Set');
expect(getValueType(new WeakMap())).toBe('WeakMap');
expect(getValueType(new WeakSet())).toBe('WeakSet');
});
1 change: 1 addition & 0 deletions packages/shared/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// eslint-disable-next-line @typescript-eslint/unbound-method
export const { create, defineProperty, getOwnPropertyDescriptor, getOwnPropertyNames, freeze, keys } = Object;
export const hasOwnProperty = (caller: unknown, p: PropertyKey) => Object.prototype.hasOwnProperty.call(caller, p);
export const getValueType = (value: unknown) => Object.prototype.toString.call(value).slice(8, -1);

export class Deferred<T> {
promise: Promise<T>;
Expand Down
Loading