From c65c0cf8f688490e5c6b6bad51f0fd712e0e9659 Mon Sep 17 00:00:00 2001 From: wanghangit <724690450@qq.com> Date: Wed, 13 Dec 2023 00:06:10 +0800 Subject: [PATCH] feat: add lifecycle check log at development environment (#2604) --- packages/qiankun/src/core/loadApp.ts | 39 +++++++++++++++------- packages/qiankun/src/version.ts | 2 +- packages/shared/src/__tests__/util.test.ts | 21 ++++++++++++ packages/shared/src/utils.ts | 1 + 4 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 packages/shared/src/__tests__/util.test.ts diff --git a/packages/qiankun/src/core/loadApp.ts b/packages/qiankun/src/core/loadApp.ts index 2a54eca5c..de1d10514 100644 --- a/packages/qiankun/src/core/loadApp.ts +++ b/packages/qiankun/src/core/loadApp.ts @@ -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'; @@ -207,29 +213,38 @@ 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 validateExportLifecycle = (exports: ObjectType | undefined, nextSourceName: string = ''): boolean => { + const validateLifecycleKeys = ['bootstrap', 'mount', 'unmount']; + const illegalKeys = validateLifecycleKeys.filter((key) => !isFunction(exports?.[key])); + const isIllegal = illegalKeys.length > 0; + if (process.env.NODE_ENV === 'development') { + if (isIllegal) { + const warnMsg = illegalKeys + .map((key) => `[${key} lifecycle] need Function but found ${getValueType(exports?.[key])}`) + .join('\n'); + warn( + `search lifecycle error\n${warnMsg}\nplease check the exported lifecycle in ${appName}${ + nextSourceName ? `,fallback to get from ${nextSourceName}` : '' + }`, + exports, + ); + } + } + return !isIllegal; }; - if (validateExportLifecycle(scriptExports)) { + if (validateExportLifecycle(scriptExports, globalLatestSetProp ? 'latestSetProp' : `window['${appName}']`)) { 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, `window['${appName}']`)) { return lifecycles; } } - if (process.env.NODE_ENV === 'development') { - console.warn( - `[qiankun] 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; diff --git a/packages/qiankun/src/version.ts b/packages/qiankun/src/version.ts index 2fccac6f3..190d2c68b 100644 --- a/packages/qiankun/src/version.ts +++ b/packages/qiankun/src/version.ts @@ -1 +1 @@ -export const version = '3.0.0-rc.11'; \ No newline at end of file +export const version = '3.0.0-rc.15'; \ No newline at end of file diff --git a/packages/shared/src/__tests__/util.test.ts b/packages/shared/src/__tests__/util.test.ts new file mode 100644 index 000000000..211d006e9 --- /dev/null +++ b/packages/shared/src/__tests__/util.test.ts @@ -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'); +}); diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 8dd73b0df..2bcce10f8 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -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 { promise: Promise;