@cfp-studio/plugin-react 17.3.0 → 17.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -1
- package/dist/index.d.mts +76 -27
- package/dist/index.mjs +1 -1
- package/package.json +7 -7
- package/preview/error-boundary.tsx +31 -37
- package/preview/index.tsx +68 -75
- package/preview/render-16.tsx +2 -1
- package/preview/render-18.tsx +5 -4
- package/preview/shims/router-utils.spec.ts +158 -0
- package/preview/shims/umi.tsx +580 -0
- package/preview/tsconfig.json +7 -7
package/README.md
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
React 框架的 CFP Studio 插件,支持 React 组件/Story 静态分析、属性/类型提取、预览渲染、CSF/Storybook 兼容等。
|
|
4
4
|
|
|
5
5
|
## 特性
|
|
6
|
+
|
|
6
7
|
- 自动检测并适配 React 16/17/18+ 项目
|
|
7
8
|
- 支持 React 组件/Story 的批量静态分析与属性/类型提取
|
|
8
9
|
- 兼容 CSF/Storybook 规范,支持 args/参数提取
|
|
9
10
|
- 支持 preview 目录下自定义 Wrapper、ErrorBoundary、不同 React 版本渲染器
|
|
10
11
|
- 内置丰富的 E2E/单元测试用例,覆盖多种工程模板
|
|
12
|
+
- **兼容 UmiJS 4 工程**(`umi` / `@umijs/max`),见下文
|
|
11
13
|
|
|
12
14
|
## 架构与工作原理
|
|
15
|
+
|
|
13
16
|
- 通过 `FrameworkPluginFactory` 实现插件注册与兼容性检测
|
|
14
17
|
- 使用 `@cfp-studio/analyzer-react` 进行 TS/JSX 类型分析
|
|
15
18
|
- `react-js-imports-plugin` 自动为 .js 文件注入 React 导入,兼容 React 17+ 新写法
|
|
@@ -17,6 +20,7 @@ React 框架的 CFP Studio 插件,支持 React 组件/Story 静态分析、属
|
|
|
17
20
|
- 支持多种 React 工程模板(apps/ 下有多种测试工程)
|
|
18
21
|
|
|
19
22
|
## 主要 API/模块
|
|
23
|
+
|
|
20
24
|
- `src/index.ts`:插件主入口,注册工厂与分析器
|
|
21
25
|
- `src/react-js-imports-plugin.ts`:自动注入 React 导入
|
|
22
26
|
- `preview/index.tsx`:前端渲染入口
|
|
@@ -24,19 +28,112 @@ React 框架的 CFP Studio 插件,支持 React 组件/Story 静态分析、属
|
|
|
24
28
|
- `tests/`、`apps/`:丰富的测试与模板
|
|
25
29
|
|
|
26
30
|
## 用法示例
|
|
31
|
+
|
|
27
32
|
```ts
|
|
28
|
-
import { reactFrameworkPlugin } from
|
|
33
|
+
import { reactFrameworkPlugin } from "@cfp-studio/plugin-react";
|
|
29
34
|
// 传入 core/Previewer 或 createChromelessWorkspace 等集成
|
|
30
35
|
```
|
|
31
36
|
|
|
37
|
+
## UmiJS 支持
|
|
38
|
+
|
|
39
|
+
支持 **Umi 4**(`umi` 或 `@umijs/max`,已用 `4.7.30` 验证)。Umi 5 不存在,npm 最新即 4.x。
|
|
40
|
+
|
|
41
|
+
**Umi 3 明确不支持**(不再维护,且用的是 react-router 5,与 Umi 4 是两套语义)。
|
|
42
|
+
Umi 3 工程会走普通 React 路径:不 alias `umi`,也不加 `@/` 别名。
|
|
43
|
+
纯 React 组件能渲染;用了 `import … from "umi"` 的组件会明确报错(不会给出误导性的假实现)。
|
|
44
|
+
|
|
45
|
+
完整设计与决策记录见 [`docs/spec-umijs-support.md`](docs/spec-umijs-support.md)。
|
|
46
|
+
|
|
47
|
+
自动生效的三件事(无需配置):
|
|
48
|
+
|
|
49
|
+
| 能力 | 说明 |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `@/` → `src/` 别名 | **不依赖工程的 `tsconfig.paths`**;`@@/` → `src/.umi/` 仅在生成目录存在时添加 |
|
|
52
|
+
| 传递依赖的 React | 真实 Umi 4 工程不声明 `react`,由框架包带入;插件会解析实际安装位置 |
|
|
53
|
+
| 生成目录排除 | `src/.umi*` 不进组件列表 |
|
|
54
|
+
|
|
55
|
+
### Umi 运行时的降级行为
|
|
56
|
+
|
|
57
|
+
`umi` 是服务端 + 客户端混合包,直接在浏览器里跑会用到 `process.platform` 等 Node API 而崩。
|
|
58
|
+
预览用 [`preview/shims/umi.tsx`](preview/shims/umi.tsx) 顶替它,**只改模块解析,不碰业务代码**。
|
|
59
|
+
|
|
60
|
+
| API | 预览里的行为 |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| `useModel` | **恒返回 `{}`**(不猜初值)。要真实初值请在 `__previewjs__/Wrapper.tsx` 里包 Provider |
|
|
63
|
+
| `useRequest` / `useFetcher` | **返回空态**:`{ data: undefined, loading: false, … }` / `state: "idle"`,`useRequest` 会打一条 `console.debug` |
|
|
64
|
+
| `history` / `useNavigate` / `useLocation` | 内存版位置栈,`push` 后可被 `useLocation` 观察到 |
|
|
65
|
+
| `useParams` / `useSearchParams` | 空值(`{}` / 空 `URLSearchParams`) |
|
|
66
|
+
| `useAppData` | 结构完整的空上下文(`routes: {}`、`clientRoutes: []` 等)。`clientRoutes.length` 为 `0`,不是 `undefined` |
|
|
67
|
+
| `useSelectedRoutes` / `useMatch` / `useRoutes` / `useOutlet` | 空值(`[]` / `null`) |
|
|
68
|
+
| `useRouteProps` / `useRouteData` / `useOutletContext` | 空对象(`{}` / `{ route: {} }`) |
|
|
69
|
+
| `useServerLoaderData` / `useClientLoaderData` / `useLoaderData` | 空态(`{ data: undefined }` / `undefined`) |
|
|
70
|
+
| `Link` / `NavLink` / `LinkWithPrefetch` | 渲染成 `<a>`,点击被拦截 |
|
|
71
|
+
| `Outlet` / `Navigate` | 渲染 `null`;`Navigate` 会把目标路径记进内存 location |
|
|
72
|
+
| `Helmet` / `HelmetProvider` | 渲染子节点,不改文档头 |
|
|
73
|
+
| `connect` / `withRouter` | 直通 HOC |
|
|
74
|
+
| `createSearchParams` / `generatePath` / `matchPath` / `resolvePath` | **真实语义**(不需要路由上下文,没必要降级) |
|
|
75
|
+
| `matchRoutes` | 恒 `null`(预览没有路由表) |
|
|
76
|
+
| `createMemoryHistory` / `createBrowserHistory` / `createHashHistory` | 同内存版 `history`(接口较真实 `history` 包小很多) |
|
|
77
|
+
| `request` / `renderClient` / `__getRoot` | **显式抛错**,并说明「请用 DevBrowser(应用预览)」 |
|
|
78
|
+
|
|
79
|
+
判据:**渲染路径上的高频 API 给降级值**(抛错会让整棵树挂掉),**不在渲染路径的显式失败**。
|
|
80
|
+
`useRequest` 返回空态意味着组件会显示「无数据」—— 那是预期行为,不是 bug。
|
|
81
|
+
|
|
82
|
+
### 为什么导出面必须完整
|
|
83
|
+
|
|
84
|
+
ESM 是**静态**导出校验:shim 少导出任何一个 Umi 名字,整个模块就
|
|
85
|
+
`SyntaxError: does not provide an export named '…'`,组件连挂载机会都没有 ——
|
|
86
|
+
**预览直接白屏**,而报错里只有 `__previewjs_internal__/renderer/shims/umi.tsx`
|
|
87
|
+
这个内部路径,用户看不出发生了什么。
|
|
88
|
+
|
|
89
|
+
所以即使是用不上的应用级 API(`renderClient` / `__getRoot`)也**显式导出并抛错**,
|
|
90
|
+
而不是省略。`tests/umi.spec.ts` 的用例 3d 把这条约束固化成了回归测试
|
|
91
|
+
(故意把一个导出去掉,它就会红)。
|
|
92
|
+
|
|
93
|
+
### 用真实的 `react-router`(accessory lib)
|
|
94
|
+
|
|
95
|
+
组件里直接 `import { useHref } from "react-router"` 是支持的 —— 插件会把
|
|
96
|
+
`react-router` / `react-router-dom` 解析到 `umi` 依赖树里的实际安装位置(它们是 **`umi` 的传递依赖**)。
|
|
97
|
+
|
|
98
|
+
但这些 hook 必须活在 `<Router>` 里。**预览不替你决定路由形态**(memory / hash / browser、
|
|
99
|
+
初始路径、basename),请在 `__previewjs__/Wrapper.tsx` 里自己包一层:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { ReactNode } from "react";
|
|
103
|
+
import { MemoryRouter } from "react-router-dom";
|
|
104
|
+
|
|
105
|
+
export const Wrapper = ({ children }: { children: ReactNode }) => {
|
|
106
|
+
return <MemoryRouter initialEntries={["/"]}>{children}</MemoryRouter>;
|
|
107
|
+
};
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
(导出必须是**具名** `Wrapper`。)不包的话会报
|
|
111
|
+
`useHref() may be used only in the context of a <Router> component`。
|
|
112
|
+
|
|
113
|
+
> 版本以 Umi 为准(当前 `react-router` **6.3.0**)。用了 6.4+ 的 API(如 `createBrowserRouter`)会报
|
|
114
|
+
> `does not provide an export named`。
|
|
115
|
+
|
|
116
|
+
更一般的 Wrapper 用法(主题、状态、布局、能做什么 / 做不到什么)见
|
|
117
|
+
[用户文档 · 组件预览](../../../../docs/user/04-component-preview.md#用-wrapper-包住组件)。
|
|
118
|
+
|
|
119
|
+
### 不覆盖的范围
|
|
120
|
+
|
|
121
|
+
- **路由 / 页面级约定**(`pages/*` 的 route props、layout、约定式路由)—— 预览把页面当普通组件渲染。
|
|
122
|
+
整站/带数据场景走应用预览(dev server),见 `docs/specs/0001-runtime-bridge`。
|
|
123
|
+
- **`.umirc.ts` 的 `alias` / `proxy` 配置** —— 预览不读 Umi 的工程配置(`alias` 的原因见 spec §23.1)。
|
|
124
|
+
- **`@umijs/max` 插件生态的语义**(`access` / `initialState` / `micro`)—— 只有上面表里的降级值。
|
|
125
|
+
|
|
32
126
|
## 依赖
|
|
127
|
+
|
|
33
128
|
- `react`、`@cfp-studio/*`、`typescript`、`vite`、`esbuild-plugin-polyfill-node`
|
|
34
129
|
- 测试:`vitest`、`@playwright/test`
|
|
35
130
|
|
|
36
131
|
## 构建
|
|
132
|
+
|
|
37
133
|
```sh
|
|
38
134
|
pnpm build
|
|
39
135
|
```
|
|
40
136
|
|
|
41
137
|
## 许可证
|
|
138
|
+
|
|
42
139
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { Reader } from "@cfp-studio/vfs";
|
|
3
2
|
import * as fs$1 from "node:fs";
|
|
4
3
|
import * as url$1 from "node:url";
|
|
5
4
|
import { URL as URL$1 } from "node:url";
|
|
5
|
+
import { Reader } from "@cfp-studio/vfs";
|
|
6
6
|
import { Analyzer } from "@cfp-studio/analyzer-api";
|
|
7
7
|
import * as http from "node:http";
|
|
8
8
|
import { Agent, ClientRequest, ClientRequestArgs, IncomingMessage, OutgoingHttpHeaders, Server } from "node:http";
|
|
@@ -15,6 +15,8 @@ import * as stream from "node:stream";
|
|
|
15
15
|
import { Duplex, DuplexOptions } from "node:stream";
|
|
16
16
|
import { SecureContextOptions } from "node:tls";
|
|
17
17
|
import { ZlibOptions } from "node:zlib";
|
|
18
|
+
import Lightningcss from "lightningcss";
|
|
19
|
+
import DartSass from "sass";
|
|
18
20
|
import SassEmbedded from "sass-embedded";
|
|
19
21
|
import Less from "less";
|
|
20
22
|
import Stylus from "stylus";
|
|
@@ -34,7 +36,7 @@ type FrameworkPluginInfo = {
|
|
|
34
36
|
* @returns 实现了 Api 接口的 AxiosApi 实例
|
|
35
37
|
*/
|
|
36
38
|
//#endregion
|
|
37
|
-
//#region ../../../../node_modules/.pnpm/@types+estree@1.0.
|
|
39
|
+
//#region ../../../../node_modules/.pnpm/@types+estree@1.0.9/node_modules/@types/estree/index.d.ts
|
|
38
40
|
// This definition file follows a somewhat unusual format. ESTree allows
|
|
39
41
|
// runtime type checks based on the `type` parameter. In order to explain this
|
|
40
42
|
// to typescript we want to use discriminated union types:
|
|
@@ -284,7 +286,7 @@ interface PrivateIdentifier extends BaseNode {
|
|
|
284
286
|
}
|
|
285
287
|
interface Property extends BaseNode {
|
|
286
288
|
type: "Property";
|
|
287
|
-
key: Expression
|
|
289
|
+
key: Expression;
|
|
288
290
|
value: Expression | Pattern; // Could be an AssignmentProperty
|
|
289
291
|
kind: "init" | "get" | "set";
|
|
290
292
|
method: boolean;
|
|
@@ -558,7 +560,7 @@ interface AwaitExpression extends BaseExpression {
|
|
|
558
560
|
argument: Expression;
|
|
559
561
|
}
|
|
560
562
|
//#endregion
|
|
561
|
-
//#region ../../../../node_modules/.pnpm/rollup@4.
|
|
563
|
+
//#region ../../../../node_modules/.pnpm/rollup@4.65.0/node_modules/rollup/dist/rollup.d.ts
|
|
562
564
|
declare module 'estree' {
|
|
563
565
|
export interface Decorator extends BaseNode {
|
|
564
566
|
type: 'Decorator';
|
|
@@ -638,11 +640,11 @@ type DecodedSourceMapOrMissing = {
|
|
|
638
640
|
missing?: false | undefined;
|
|
639
641
|
});
|
|
640
642
|
interface SourceMap$1 {
|
|
641
|
-
file
|
|
643
|
+
file?: string | undefined;
|
|
642
644
|
mappings: string;
|
|
643
645
|
names: string[];
|
|
644
646
|
sources: string[];
|
|
645
|
-
sourcesContent?: string[] | undefined;
|
|
647
|
+
sourcesContent?: (string | null)[] | undefined;
|
|
646
648
|
version: number;
|
|
647
649
|
debugId?: string | undefined;
|
|
648
650
|
toString(): string;
|
|
@@ -664,7 +666,8 @@ interface SourceDescription extends Partial<PartialNull<ModuleOptions>> {
|
|
|
664
666
|
}
|
|
665
667
|
interface TransformModuleJSON {
|
|
666
668
|
ast?: ProgramNode | undefined;
|
|
667
|
-
code: string;
|
|
669
|
+
code: string;
|
|
670
|
+
safeVariableNames: Record<string, string> | null; // note if plugins use new this.cache to opt-out auto transform cache
|
|
668
671
|
customTransformCache: boolean;
|
|
669
672
|
originalCode: string;
|
|
670
673
|
originalSourcemap: ExistingDecodedSourceMap | null;
|
|
@@ -672,6 +675,7 @@ interface TransformModuleJSON {
|
|
|
672
675
|
transformDependencies: string[];
|
|
673
676
|
}
|
|
674
677
|
interface ModuleJSON extends TransformModuleJSON, ModuleOptions {
|
|
678
|
+
safeVariableNames: Record<string, string> | null;
|
|
675
679
|
ast: ProgramNode;
|
|
676
680
|
dependencies: string[];
|
|
677
681
|
id: string;
|
|
@@ -727,6 +731,7 @@ interface ModuleInfo extends ModuleOptions {
|
|
|
727
731
|
dynamicallyImportedIds: readonly string[];
|
|
728
732
|
exportedBindings: Record<string, string[]> | null;
|
|
729
733
|
exports: string[] | null;
|
|
734
|
+
safeVariableNames: Record<string, string> | null;
|
|
730
735
|
hasDefaultExport: boolean | null;
|
|
731
736
|
id: string;
|
|
732
737
|
implicitlyLoadedAfterOneOf: readonly string[];
|
|
@@ -774,6 +779,7 @@ interface PluginContext extends MinimalPluginContext$1 {
|
|
|
774
779
|
} & Partial<PartialNull<ModuleOptions>>) => Promise<ModuleInfo>;
|
|
775
780
|
parse: ParseAst;
|
|
776
781
|
resolve: (source: string, importer?: string, options?: {
|
|
782
|
+
importerAttributes?: Record<string, string>;
|
|
777
783
|
attributes?: Record<string, string>;
|
|
778
784
|
custom?: CustomPluginOptions;
|
|
779
785
|
isEntry?: boolean;
|
|
@@ -810,10 +816,12 @@ type ResolveIdResult = string | NullValue | false | PartialResolvedId;
|
|
|
810
816
|
type ResolveIdHook = (this: PluginContext, source: string, importer: string | undefined, options: {
|
|
811
817
|
attributes: Record<string, string>;
|
|
812
818
|
custom?: CustomPluginOptions;
|
|
819
|
+
importerAttributes?: Record<string, string> | undefined;
|
|
813
820
|
isEntry: boolean;
|
|
814
821
|
}) => ResolveIdResult;
|
|
815
822
|
type ShouldTransformCachedModuleHook = (this: PluginContext, options: {
|
|
816
823
|
ast: ProgramNode;
|
|
824
|
+
attributes: Record<string, string>;
|
|
817
825
|
code: string;
|
|
818
826
|
id: string;
|
|
819
827
|
meta: CustomPluginOptions;
|
|
@@ -824,7 +832,12 @@ type ShouldTransformCachedModuleHook = (this: PluginContext, options: {
|
|
|
824
832
|
type IsExternal = (source: string, importer: string | undefined, isResolved: boolean) => boolean;
|
|
825
833
|
type HasModuleSideEffects = (id: string, external: boolean) => boolean;
|
|
826
834
|
type LoadResult = SourceDescription | string | NullValue;
|
|
827
|
-
type LoadHook = (this: PluginContext, id: string
|
|
835
|
+
type LoadHook = (this: PluginContext, id: string, // temporarily marked as optional for better Vite type-compatibility
|
|
836
|
+
options?: {
|
|
837
|
+
// unused, temporarily added for better Vite type-compatibility
|
|
838
|
+
ssr?: boolean | undefined; // temporarily marked as optional for better Vite type-compatibility
|
|
839
|
+
attributes?: Record<string, string>;
|
|
840
|
+
} | undefined) => LoadResult;
|
|
828
841
|
interface TransformPluginContext extends PluginContext {
|
|
829
842
|
debug: LoggingFunctionWithPosition;
|
|
830
843
|
error: (error: RollupError | string, pos?: number | {
|
|
@@ -836,7 +849,12 @@ interface TransformPluginContext extends PluginContext {
|
|
|
836
849
|
warn: LoggingFunctionWithPosition;
|
|
837
850
|
}
|
|
838
851
|
type TransformResult$2 = string | NullValue | Partial<SourceDescription>;
|
|
839
|
-
type TransformHook = (this: TransformPluginContext, code: string, id: string
|
|
852
|
+
type TransformHook = (this: TransformPluginContext, code: string, id: string, // temporarily marked as optional for better Vite type-compatibility
|
|
853
|
+
options?: {
|
|
854
|
+
// unused, temporarily added for better Vite type-compatibility
|
|
855
|
+
ssr?: boolean | undefined; // temporarily marked as optional for better Vite type-compatibility
|
|
856
|
+
attributes?: Record<string, string>;
|
|
857
|
+
} | undefined) => TransformResult$2;
|
|
840
858
|
type ModuleParsedHook = (this: PluginContext, info: ModuleInfo) => void;
|
|
841
859
|
type RenderChunkHook = (this: PluginContext, code: string, chunk: RenderedChunk, options: NormalizedOutputOptions, meta: {
|
|
842
860
|
chunks: Record<string, RenderedChunk>;
|
|
@@ -846,13 +864,16 @@ type RenderChunkHook = (this: PluginContext, code: string, chunk: RenderedChunk,
|
|
|
846
864
|
} | string | NullValue;
|
|
847
865
|
type ResolveDynamicImportHook = (this: PluginContext, specifier: string | AstNode, importer: string, options: {
|
|
848
866
|
attributes: Record<string, string>;
|
|
867
|
+
importerAttributes: Record<string, string>;
|
|
849
868
|
}) => ResolveIdResult;
|
|
850
869
|
type ResolveImportMetaHook = (this: PluginContext, property: string | null, options: {
|
|
870
|
+
attributes: Record<string, string>;
|
|
851
871
|
chunkId: string;
|
|
852
872
|
format: InternalModuleFormat;
|
|
853
873
|
moduleId: string;
|
|
854
874
|
}) => string | NullValue;
|
|
855
875
|
type ResolveFileUrlHook = (this: PluginContext, options: {
|
|
876
|
+
attributes: Record<string, string>;
|
|
856
877
|
chunkId: string;
|
|
857
878
|
fileName: string;
|
|
858
879
|
format: InternalModuleFormat;
|
|
@@ -903,6 +924,7 @@ interface FunctionPluginHooks {
|
|
|
903
924
|
chunk: PreRenderedChunkWithFileName;
|
|
904
925
|
targetChunk: PreRenderedChunkWithFileName | null;
|
|
905
926
|
getTargetChunkImports: () => DynamicImportTargetChunk[] | null;
|
|
927
|
+
targetModuleAttributes: Record<string, string>;
|
|
906
928
|
}) => {
|
|
907
929
|
left: string;
|
|
908
930
|
right: string;
|
|
@@ -1318,16 +1340,14 @@ interface AwaitingEventEmitter<T extends Record<string, (...parameters: any) =>
|
|
|
1318
1340
|
off<K extends keyof T>(event: K, listener: AwaitedEventListener<T, K>): this;
|
|
1319
1341
|
/**
|
|
1320
1342
|
* Registers an event listener that will be awaited before Rollup continues.
|
|
1321
|
-
* All listeners
|
|
1322
|
-
*
|
|
1343
|
+
* All listeners are invoked and awaited in parallel. Rollup waits until all
|
|
1344
|
+
* of them have settled and then throws the first rejection, if any.
|
|
1323
1345
|
*/
|
|
1324
1346
|
on<K extends keyof T>(event: K, listener: AwaitedEventListener<T, K>): this;
|
|
1325
1347
|
/**
|
|
1326
|
-
* Registers an event listener
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1329
|
-
* Listeners are removed automatically when removeListenersForCurrentRun is
|
|
1330
|
-
* called, which happens automatically after each run.
|
|
1348
|
+
* Registers an event listener like `on`. Listeners are removed automatically
|
|
1349
|
+
* when removeListenersForCurrentRun is called, which happens right before each
|
|
1350
|
+
* rebuild, after the changes that triggered it were announced.
|
|
1331
1351
|
*/
|
|
1332
1352
|
onCurrentRun<K extends keyof T>(event: K, listener: (...parameters: Parameters<T[K]>) => Promise<ReturnType<T[K]>>): this;
|
|
1333
1353
|
removeAllListeners(): this;
|
|
@@ -1428,7 +1448,7 @@ interface RollupFileStats {
|
|
|
1428
1448
|
birthtime: Date;
|
|
1429
1449
|
}
|
|
1430
1450
|
//#endregion
|
|
1431
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
1451
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/types/hmrPayload.d.ts
|
|
1432
1452
|
type HotPayload = ConnectedPayload | PingPayload | UpdatePayload | FullReloadPayload | CustomPayload | ErrorPayload | PrunePayload;
|
|
1433
1453
|
interface ConnectedPayload {
|
|
1434
1454
|
type: 'connected';
|
|
@@ -1487,7 +1507,7 @@ interface ErrorPayload {
|
|
|
1487
1507
|
};
|
|
1488
1508
|
}
|
|
1489
1509
|
//#endregion
|
|
1490
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
1510
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/types/customEvent.d.ts
|
|
1491
1511
|
interface CustomEventMap {
|
|
1492
1512
|
'vite:beforeUpdate': UpdatePayload;
|
|
1493
1513
|
'vite:afterUpdate': UpdatePayload;
|
|
@@ -1518,7 +1538,7 @@ interface InvalidatePayload {
|
|
|
1518
1538
|
*/
|
|
1519
1539
|
type InferCustomEventPayload<T extends string> = T extends keyof CustomEventMap ? CustomEventMap[T] : any;
|
|
1520
1540
|
//#endregion
|
|
1521
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
1541
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/dist/node/moduleRunnerTransport.d-DJ_mE5sf.d.ts
|
|
1522
1542
|
interface FetchFunctionOptions {
|
|
1523
1543
|
cached?: boolean;
|
|
1524
1544
|
startOffset?: number;
|
|
@@ -2181,12 +2201,12 @@ declare global {
|
|
|
2181
2201
|
interface URL {}
|
|
2182
2202
|
}
|
|
2183
2203
|
//#endregion
|
|
2184
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
2204
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/types/internal/lightningcssOptions.d.ts
|
|
2185
2205
|
/* eslint-enable @typescript-eslint/ban-ts-comment */
|
|
2186
2206
|
type LightningCSSOptions = Omit<Lightningcss.BundleAsyncOptions<Lightningcss.CustomAtRules>, 'filename' | 'resolver' | 'minify' | 'sourceMap' | 'analyzeDependencies' // properties not overridden by Vite, but does not make sense to set by end users
|
|
2187
2207
|
| 'inputSourceMap' | 'projectRoot'>;
|
|
2188
2208
|
//#endregion
|
|
2189
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
2209
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/types/internal/cssPreprocessorOptions.d.ts
|
|
2190
2210
|
/* eslint-enable @typescript-eslint/ban-ts-comment */
|
|
2191
2211
|
// https://github.com/type-challenges/type-challenges/issues/29285
|
|
2192
2212
|
type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;
|
|
@@ -2209,7 +2229,7 @@ declare global {
|
|
|
2209
2229
|
interface HTMLLinkElement {}
|
|
2210
2230
|
}
|
|
2211
2231
|
//#endregion
|
|
2212
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
2232
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/types/metadata.d.ts
|
|
2213
2233
|
interface ChunkMetadata {
|
|
2214
2234
|
importedAssets: Set<string>;
|
|
2215
2235
|
importedCss: Set<string>;
|
|
@@ -2242,7 +2262,7 @@ declare module 'rollup' {
|
|
|
2242
2262
|
}
|
|
2243
2263
|
}
|
|
2244
2264
|
//#endregion
|
|
2245
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
2265
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/dist/node/index.d.ts
|
|
2246
2266
|
interface Alias {
|
|
2247
2267
|
find: string | RegExp;
|
|
2248
2268
|
replacement: string;
|
|
@@ -5701,10 +5721,16 @@ interface FrameworkPluginFactory {
|
|
|
5701
5721
|
/**
|
|
5702
5722
|
* 检查当前项目的依赖是否与该框架插件兼容。
|
|
5703
5723
|
*
|
|
5704
|
-
* @param dependencies -
|
|
5724
|
+
* @param dependencies - 项目的依赖包信息。**只含工程 `package.json` 里显式声明的包**
|
|
5725
|
+
* (`dependencies` / `devDependencies` / `peerDependencies` 的并集),
|
|
5726
|
+
* 不含传递依赖。
|
|
5727
|
+
* @param rootDir - 工程根目录(可选,向后兼容)。
|
|
5728
|
+
* 有些框架(如 Umi 4)**不显式声明 `react`**,而由框架包作为传递依赖带入 ——
|
|
5729
|
+
* 那种情况下 `dependencies.react` 是 `undefined`,插件需要靠 `rootDir` 自行解析。
|
|
5730
|
+
* 见 `docs/spec-umijs-support.md` §15。
|
|
5705
5731
|
* @returns 一个 Promise,解析为布尔值,表示是否兼容。
|
|
5706
5732
|
*/
|
|
5707
|
-
isCompatible(dependencies: PackageDependencies): Promise<boolean>;
|
|
5733
|
+
isCompatible(dependencies: PackageDependencies, rootDir?: string): Promise<boolean>;
|
|
5708
5734
|
/**
|
|
5709
5735
|
* 创建框架插件实例。
|
|
5710
5736
|
*
|
|
@@ -5742,6 +5768,17 @@ interface FrameworkPlugin extends Analyzer {
|
|
|
5742
5768
|
* @returns Vite 用户配置对象。
|
|
5743
5769
|
*/
|
|
5744
5770
|
viteConfig: (configuredPlugins: Plugin[]) => UserConfig;
|
|
5771
|
+
/**
|
|
5772
|
+
* 该框架的**生成目录** glob,供文件爬取排除(可选)。
|
|
5773
|
+
*
|
|
5774
|
+
* 用于框架把代码生成到工程内的场景 —— 例如 Umi 4 的 `src/.umi`,
|
|
5775
|
+
* dev server 一跑就是几万行。不排除的话它们会进组件树,
|
|
5776
|
+
* 既慢又干扰(`docs/spec-umijs-support.md` G2)。
|
|
5777
|
+
*
|
|
5778
|
+
* 语义边界:**只是"不扫这些路径"**,不是 gitignore,也不影响文件读取
|
|
5779
|
+
* (`@cfp-studio/reader` 仍能读到)。不实现该字段即保持现状。
|
|
5780
|
+
*/
|
|
5781
|
+
ignoredPaths?: string[];
|
|
5745
5782
|
/**
|
|
5746
5783
|
* 清理插件资源的方法。
|
|
5747
5784
|
* 当插件不再使用时,调用此方法释放资源。
|
|
@@ -5758,11 +5795,23 @@ interface FrameworkPlugin extends Analyzer {
|
|
|
5758
5795
|
//#endregion
|
|
5759
5796
|
//#region src/index.d.ts
|
|
5760
5797
|
/**
|
|
5761
|
-
*
|
|
5798
|
+
* Umi 4 工程里需要从依赖预构建中排除的 native 模块。
|
|
5799
|
+
*
|
|
5800
|
+
* **为什么需要**:`@umijs/bundler-vite` 自带一份 `vite@7`,其依赖 `fsevents`
|
|
5801
|
+
* 含 `.node` 二进制;esbuild 的依赖预构建没有 `.node` 的 loader,会导致
|
|
5802
|
+
* `error while updating dependencies` 而整个预览起不来。
|
|
5803
|
+
*
|
|
5804
|
+
* 实测(`docs/spec-umijs-support.md` §11.2):不加这两项,任何
|
|
5805
|
+
* `import ... from "umi"` 的组件都无法预览。
|
|
5806
|
+
*
|
|
5807
|
+
* 只排除**已知的 native 模块**,不做 `platform: "node"` 之类的全局放宽 ——
|
|
5808
|
+
* 那会改变所有依赖的构建目标。
|
|
5762
5809
|
*/
|
|
5810
|
+
declare const UMI_NATIVE_MODULES_TO_EXCLUDE: string[];
|
|
5811
|
+
declare const UMI_GENERATED_DIR_GLOBS: string[];
|
|
5763
5812
|
declare const reactFrameworkPlugin: FrameworkPluginFactory;
|
|
5764
5813
|
/**
|
|
5765
5814
|
* 导出默认的 reactFrameworkPlugin 对象
|
|
5766
5815
|
*/
|
|
5767
5816
|
//#endregion
|
|
5768
|
-
export { reactFrameworkPlugin as default };
|
|
5817
|
+
export { UMI_GENERATED_DIR_GLOBS, UMI_NATIVE_MODULES_TO_EXCLUDE, reactFrameworkPlugin as default };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createRequire as e}from"node:module";import t from"node:fs";import n from"node:path";import r from"node:url";import i from"@vitejs/plugin-react";import{polyfillNode as a}from"esbuild-plugin-polyfill-node";import{createAnalyzer as o}from"@cfp-studio/analyzer-api/analyzer";import{createFileSystemReader as s,createStackedReader as c}from"@cfp-studio/vfs";import l from"typescript";import{decodePreviewableId as u,defineSpecialTypes as d,generatePreviewableId as f}from"@cfp-studio/analyzer-api";import{parseSerializableValue as p}from"@cfp-studio/serializable-values";import{extractArgs as m,extractCsf3Stories as h,extractStoriesInfo as g,resolvePreviewableId as _}from"@cfp-studio/storybook-helpers";import{EMPTY_OBJECT_TYPE as v,NODE_TYPE as y,UNKNOWN_TYPE as b,arrayType as x,dereferenceType as S,functionType as C,helpers as w,maybeOptionalType as T,objectType as E,stripUnusedTypes as D}from"@cfp-studio/type-analyzer";function O(e,t){for(let n of e.statements)if(l.isExpressionStatement(n)&&l.isBinaryExpression(n.expression)&&n.expression.operatorToken.kind===l.SyntaxKind.EqualsToken&&l.isPropertyAccessExpression(n.expression.left)&&l.isIdentifier(n.expression.left.expression)){if(n.expression.left.expression.text!==t)continue;if(n.expression.left.name.text===`propTypes`)return n.expression.right}else if(l.isClassDeclaration(n)&&n.name){if(n.name.text!==t)continue;for(let e of n.members)if(l.isPropertyDeclaration(e)&&e.initializer&&e.name.getText()===`propTypes`)return e.initializer}return null}function k(e,t,n,r,i){let a=t.sourceFile(n),o=null;a&&(o=O(a,r));let s=null;return s=o?j(t,o):A(e,t,i),{props:s.type,types:{...s.collected}}}function A(e,t,n){let r=n.getParameters()[0];if(!r)return{type:v,collected:{}};if(!r.valueDeclaration)return{type:b,collected:{}};let i=t.checker.getTypeOfSymbolAtLocation(r,r.valueDeclaration);try{let{type:e,collected:n}=t.resolveType(i);if([e]=S(e,n,[]),e.kind===`object`&&l.isParameter(r.valueDeclaration)&&l.isObjectBindingPattern(r.valueDeclaration.name)){let t=r.valueDeclaration.name,n=new Set,i=new Set,a=!1;for(let e of t.elements){if(e.dotDotDotToken){a=!0;break}let t=e.propertyName||e.name;if(!l.isIdentifier(t)){a=!0;break}n.add(t.text),e.initializer&&i.add(t.text)}a||(e=E(Object.fromEntries(Object.entries(e.fields).filter(([e])=>n.has(e)).map(([e,t])=>[e,T(t,i.has(e))]))))}return{type:e,collected:n}}catch(n){e.warn(`Unable to resolve props type for ${t.checker.typeToString(i)}`),e.error(n.toString())}return{type:b,collected:{}}}function j(e,t){let n=e.checker.getTypeAtLocation(t),r={},i={};for(let t of n.getProperties())r[t.name]=(()=>{if(!t.valueDeclaration)return b;let n=e.checker.getTypeAtLocation(t.valueDeclaration),r=e.resolveTypeArguments(n),a=r.types[0];return a?(i={...i,...r.collected},T(a,n.symbol.name===`Requireable`)):b})();let a=E(r);return{type:a,collected:D(i,[a])}}async function M(e,t,r,i){let a=t.sourceFile(i);if(!a)return[];let o=[];for(let e of a.statements)if(l.isExportAssignment(e)){if(l.isIdentifier(e.expression))continue;o.push([`default`,e,e.expression])}else if(l.isVariableStatement(e))for(let t of e.declarationList.declarations)!l.isIdentifier(t.name)||!t.initializer||o.push([t.name.text,e,t.initializer]);else if(l.isFunctionDeclaration(e)){let t=!!e.modifiers?.find(e=>e.kind===l.SyntaxKind.ExportKeyword)&&!!e.modifiers?.find(e=>e.kind===l.SyntaxKind.DefaultKeyword),n=e.name?.text;(t||n)&&o.push([n||`default`,e,e])}else l.isClassDeclaration(e)&&e.name&&o.push([e.name.text,e,e]);let s=g(a);e.debug(`**storiresInfo: ${s}`);let c=[],d=m(a),_=w.extractExportedNames(a);async function v(n,a,o){if(o===`default`&&s)return null;let c=d[o],l=o===`default`||!!_[o],u=P(t.checker,a);if(s&&l&&(c||u?.parameters.length===0)){let i=N(e,t,r,s.component);return{...n,analyze:async()=>({args:c?{sourcePosition:{start:c.getStart(),end:c.getEnd()},value:await p(c)}:null}),associatedComponent:i}}return u?{...n,exported:l,analyze:async()=>k(e,t,i,o,u)}:null}for(let[e,t,a]of o){let o=await v({id:f({filePath:n.relative(r,i),name:e}),sourcePosition:{start:t.getStart(),end:t.getEnd()}},a,e);o&&c.push(o)}return[...c,...await h(r,t,a,async i=>{let{filePath:a}=u(i),o=(await M(e,t,r,n.join(r,a))).find(e=>e.id===i);return!o||!(`exported`in o)?{props:b,types:{}}:o.analyze()})]}function N(e,t,r,i){let a=_(r,t.checker,i);return i&&a?{id:a,analyze:async()=>{let o=P(t.checker,i);if(!o)return{props:b,types:{}};let{filePath:s,name:c}=u(a);return k(e,t,n.join(r,s),c,o)}}:null}function P(e,t){let n=e.getTypeAtLocation(t);if(n.flags===l.TypeFlags.Any&&l.isCallExpression(t)&&l.isPropertyAccessExpression(t.expression)&&l.isIdentifier(t.expression.name)&&t.expression.name.text===`bind`){let r=e.getSymbolAtLocation(t.expression.expression);r?.valueDeclaration&&l.isVariableDeclaration(r.valueDeclaration)&&r.valueDeclaration.initializer&&(n=e.getTypeAtLocation(r.valueDeclaration.initializer))}for(let e of n.getCallSignatures())if(F(e.getReturnType()))return e;if(n.symbol){let r=e.getTypeOfSymbolAtLocation(n.symbol,t);for(let e of r.getConstructSignatures())if(e.getReturnType().getProperty(`render`))return e}return null}function F(e){return!!L(e)}const I=new Set([`Element`,`ReactElement`]);function L(e){if(e.isUnion()){for(let t of e.types)if(L(t))return!0}return I.has(e.symbol?.getEscapedName().toString())}const R=d({Component:y,ComponentClass:C(y),FunctionComponent:C(y),ReactElement:y,ReactNode:y,ReactNodeArray:x(y),ReactPortal:y}),z=o({typeAnalyzerOptions:({reader:e,rootDir:i})=>{let a=r.fileURLToPath(new URL(`.`,import.meta.url)),o=n.join(a,`..`,`types`),u=n.join(a,`..`,`node_modules`),d=t.existsSync(o)?o:u;return{reader:c([e,s({mapping:{from:d,to:n.join(i,`node_modules`)},watch:!1}),...d===o?[s({mapping:{from:o,to:n.join(i,`node_modules`,`@types`)},watch:!1})]:[]]),specialTypes:R,tsCompilerOptions:{jsx:l.JsxEmit.ReactJSX,jsxImportSource:`react`}}},crawlFile:({logger:e,resolver:t,rootDir:n,absoluteFilePath:r})=>M(e,t,n,r)}),B={line:`data-inspector-line`,column:`data-inspector-column`,relativePath:`data-inspector-relative-path`};function V(e){return e.includes(B.relativePath)}function H(e,t){if(!e.includes(`<`)||V(e))return;let n=l.createSourceFile(t.relativePath,e,l.ScriptTarget.Latest,!0,l.ScriptKind.TSX),r=[],i=e=>{if(l.isJsxElement(e)||l.isJsxSelfClosingElement(e)){let i=l.isJsxElement(e)?e.openingElement:e,a=i.tagName.getStart(n),{line:o,character:s}=n.getLineAndCharacterOfPosition(a),c=i.tagName.getEnd(),u=` ${B.line}="${o+1}" ${B.column}="${s+1}" ${B.relativePath}="${U(t.relativePath)}"`;r.push({at:c,text:u})}l.forEachChild(e,i)};if(i(n),r.length===0)return;r.sort((e,t)=>t.at-e.at);let a=e;for(let{at:e,text:t}of r)a=a.slice(0,e)+t+a.slice(e);return a}function U(e){return e.replace(/&/g,`&`).replace(/"/g,`"`)}const W=/import (\* as )?React[ ,]/;function G(){return{name:`previewjs:react-js-imports`,async transform(e,t){return t.includes(`node_modules`)||n.extname(t)!==`.js`||W.test(e)?e:`import React from 'react';${e}`}}}const K=e(import.meta.url),q=[`fsevents`];function J(e,t){if(t.react)return{};let r={};for(let t of[`react`,`react-dom`])try{let i=K.resolve(`${t}/package.json`,{paths:[e]});r[t]=n.dirname(i)}catch{}return r}function Y(e,r){try{let i=K.resolve(e,{paths:[r]}),a=n.dirname(i);for(;a!==n.dirname(a);){let e=n.join(a,`package.json`);if(t.existsSync(e)){let n=JSON.parse(t.readFileSync(e,`utf8`));return typeof n.version==`string`?n.version:null}a=n.dirname(a)}return null}catch{return null}}const X=[`src/.umi/**`,`src/.umi-production/**`,`src/.umi-test/**`,`src/.umi-tmp/**`];function Z(){let e=``;return{name:`previewjs:inspector-attrs`,enforce:`pre`,configResolved(t){e=t.root},transform(t,r){let i=r.split(`?`)[0]??r;if(!/\.[jt]sx$/.test(i)||i.includes(`node_modules`)||i.includes(`__previewjs__`)||i.includes(`__previewjs_internal__`))return;let a=H(t,{relativePath:n.relative(e,i).split(n.sep).join(`/`)});if(a!==void 0)return{code:a,map:null}}}}const Q={info:{apiVersion:5,name:`@cfp-studio/plugin-react`},isCompatible:async(e,t)=>{let n=await e.react?.readInstalledVersion();if(!n&&t&&(n=Y(`react`,t)),!n)return!1;let[r,i]=n.split(`.`).map(e=>Number.parseInt(e));return Number.isNaN(r)||Number.isNaN(i)?!1:r>=17||r===16&&i>=14},async create({rootDir:e,reader:o,logger:s,dependencies:c}){let l=r.fileURLToPath(new URL(`.`,import.meta.url)),u=await c.umi?.readInstalledVersion()??await c[`@umijs/max`]?.readInstalledVersion(),d=u?Number.parseInt(u.split(`.`)[0]??`0`)>=4:!1,f=z({rootDir:e,reader:o,logger:s}),p=n.join(l,`..`,`preview`),m=J(e,c),h=`/__previewjs_internal__/renderer/shims/umi.tsx`,g=n.join(e,`src`),_={};if(d){t.existsSync(g)&&(_[`@`]=g);let r=n.join(g,`.umi`);t.existsSync(r)?_[`@@`]=r:s.debug(`umi: 跳过 @@ 别名,src/.umi 不存在(dev server 未启动)`);let i=(()=>{try{return n.dirname(K.resolve(`umi`,{paths:[e]}))}catch{try{return n.dirname(K.resolve(`@umijs/max`,{paths:[e]}))}catch{return e}}})();for(let e of[`react-router`,`react-router-dom`])try{let t=K.resolve(`${e}/package.json`,{paths:[i]});_[e]=n.dirname(t)}catch{s.debug(`umi: 跳过 ${e} 别名,解析不到实际安装位置`)}}return{defaultWrapperPath:`__previewjs__/Wrapper.tsx`,previewDirPath:p,ignoredPaths:d?X:[],...f,viteConfig:e=>{let t=e.find(e=>e.name.startsWith(`vite:react-`));return{optimizeDeps:{...d?{exclude:q}:{},esbuildOptions:{plugins:c.next?[a()]:[]}},resolve:{alias:{"react-native":`react-native-web`,...m,...d?{umi:h,"@umijs/max":h,..._}:{}}},plugins:[Z(),G(),...e,...t?[]:[i()],{name:`previewjs:update-react-import`,async transform(e,t){if(!t.endsWith(`__previewjs_internal__/renderer/index.tsx`))return;let n=Number.parseInt(await c.react?.readInstalledVersion()||`0`);return e.replace(/__PREVIEWJS_PLUGIN_REACT_IMPORT_PATH__/g,n>=18?`./render-18`:`./render-16`)}}]}}}}};export{X as UMI_GENERATED_DIR_GLOBS,q as UMI_NATIVE_MODULES_TO_EXCLUDE,Q as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cfp-studio/plugin-react",
|
|
3
|
-
"version": "17.3.
|
|
3
|
+
"version": "17.3.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"esbuild-plugin-polyfill-node": "0.3.0",
|
|
17
17
|
"typescript": "5.9.3",
|
|
18
18
|
"@cfp-studio/analyzer-api": "0.1.13",
|
|
19
|
-
"@cfp-studio/type-analyzer": "10.1.8",
|
|
20
|
-
"@cfp-studio/serializable-values": "9.0.8",
|
|
21
19
|
"@cfp-studio/storybook-helpers": "6.0.2",
|
|
22
|
-
"@cfp-studio/
|
|
20
|
+
"@cfp-studio/serializable-values": "9.0.8",
|
|
21
|
+
"@cfp-studio/type-analyzer": "10.1.8",
|
|
22
|
+
"@cfp-studio/vfs": "2.1.8"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@playwright/test": "1.64.1",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"react-dom": "18.3.1",
|
|
34
34
|
"vite": "6.4.5",
|
|
35
35
|
"vitest": "5.0.5",
|
|
36
|
-
"@cfp-studio/core": "1.0.
|
|
36
|
+
"@cfp-studio/core": "1.0.8",
|
|
37
37
|
"@cfp-studio/testing": "14.0.1",
|
|
38
|
-
"@cfp-studio/iframe": "18.
|
|
38
|
+
"@cfp-studio/iframe": "18.4.3"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=20"
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"typecheck": "tsc --noEmit && tsc -p preview/tsconfig.json",
|
|
52
52
|
"test": "node scripts/prepare-types.mjs && vitest run",
|
|
53
53
|
"test:watch": "vitest watch",
|
|
54
|
-
"e2e-test": "pnpm --dir tests/apps/react16 install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/react17 install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/react18 install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/react-sass install --ignore-workspace --ignore-scripts && playwright test",
|
|
54
|
+
"e2e-test": "pnpm --dir tests/apps/react16 install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/react17 install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/react18 install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/react-sass install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/umi4 install --ignore-workspace --ignore-scripts && pnpm --dir tests/apps/umi-max install --ignore-workspace --ignore-scripts && tsx scripts/link-shared-umi-fixtures.ts && playwright test",
|
|
55
55
|
"lint": "oxlint",
|
|
56
56
|
"format": "oxfmt --write ."
|
|
57
57
|
}
|
|
@@ -2,48 +2,42 @@ import type { ReactNode } from "react";
|
|
|
2
2
|
import { Component } from "react";
|
|
3
3
|
|
|
4
4
|
type ErrorBoundaryProps = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
renderId: number;
|
|
6
|
+
children: ReactNode;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
let errorBoundaryInstance: ErrorBoundary | null = null;
|
|
10
10
|
|
|
11
|
-
export async function expectErrorBoundary(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
20
|
-
if (shouldAbortRender()) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return errorBoundaryInstance;
|
|
11
|
+
export async function expectErrorBoundary(renderId: number, shouldAbortRender: () => boolean) {
|
|
12
|
+
while (!errorBoundaryInstance || errorBoundaryInstance.props.renderId !== renderId) {
|
|
13
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
14
|
+
if (shouldAbortRender()) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return errorBoundaryInstance;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
export class ErrorBoundary extends Component<ErrorBoundaryProps> {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
22
|
+
state = {
|
|
23
|
+
error: null as Error | null,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
constructor(props: ErrorBoundaryProps) {
|
|
27
|
+
super(props);
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
29
|
+
errorBoundaryInstance = this;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static getDerivedStateFromError(error: Error) {
|
|
33
|
+
return { error };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
render() {
|
|
37
|
+
if (this.state.error) {
|
|
38
|
+
return <></>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return this.props.children;
|
|
42
|
+
}
|
|
49
43
|
}
|