@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 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 '@cfp-studio/plugin-react';
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.8/node_modules/@types/estree/index.d.ts
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 | PrivateIdentifier;
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.52.0/node_modules/rollup/dist/rollup.d.ts
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: string;
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; // note if plugins use new this.cache to opt-out auto transform cache
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) => LoadResult;
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) => TransformResult$2;
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 will be awaited in parallel while rejections are tracked via
1322
- * Promise.all.
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 that will be awaited before Rollup continues.
1327
- * All listeners will be awaited in parallel while rejections are tracked via
1328
- * Promise.all.
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.19.2_jiti@2.6.1_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/types/hmrPayload.d.ts
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.19.2_jiti@2.6.1_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/types/customEvent.d.ts
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.19.2_jiti@2.6.1_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/dist/node/moduleRunnerTransport.d-DJ_mE5sf.d.ts
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.19.2_jiti@2.6.1_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/types/internal/lightningcssOptions.d.ts
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.19.2_jiti@2.6.1_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/types/internal/cssPreprocessorOptions.d.ts
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.19.2_jiti@2.6.1_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/types/metadata.d.ts
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.19.2_jiti@2.6.1_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/dist/node/index.d.ts
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
- * 定义一个名为 reactFrameworkPlugin 的框架插件工厂对象
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{createAnalyzer as e}from"@cfp-studio/analyzer-api/analyzer";import{createFileSystemReader as t,createStackedReader as n}from"@cfp-studio/vfs";import r from"node:fs";import i from"node:path";import a from"typescript";import o from"node:url";import{decodePreviewableId as s,defineSpecialTypes as c,generatePreviewableId as l}from"@cfp-studio/analyzer-api";import{parseSerializableValue as u}from"@cfp-studio/serializable-values";import{extractArgs as d,extractCsf3Stories as f,extractStoriesInfo as p,resolvePreviewableId as m}from"@cfp-studio/storybook-helpers";import{EMPTY_OBJECT_TYPE as h,NODE_TYPE as g,UNKNOWN_TYPE as _,arrayType as v,dereferenceType as y,functionType as b,helpers as x,maybeOptionalType as S,objectType as C,stripUnusedTypes as w}from"@cfp-studio/type-analyzer";import T from"@vitejs/plugin-react";import{polyfillNode as E}from"esbuild-plugin-polyfill-node";function D(e,t){for(let n of e.statements)if(a.isExpressionStatement(n)&&a.isBinaryExpression(n.expression)&&n.expression.operatorToken.kind===a.SyntaxKind.EqualsToken&&a.isPropertyAccessExpression(n.expression.left)&&a.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(a.isClassDeclaration(n)&&n.name){if(n.name.text!==t)continue;for(let e of n.members)if(a.isPropertyDeclaration(e)&&e.initializer&&e.name.getText()===`propTypes`)return e.initializer}return null}function O(e,t,n,r,i){let a=t.sourceFile(n),o=null;a&&(o=D(a,r));let s=null;return s=o?A(t,o):k(e,t,i),{props:s.type,types:{...s.collected}}}function k(e,t,n){let r=n.getParameters()[0];if(!r)return{type:h,collected:{}};if(!r.valueDeclaration)return{type:_,collected:{}};let i=t.checker.getTypeOfSymbolAtLocation(r,r.valueDeclaration);try{let{type:e,collected:n}=t.resolveType(i);if([e]=y(e,n,[]),e.kind===`object`&&a.isParameter(r.valueDeclaration)&&a.isObjectBindingPattern(r.valueDeclaration.name)){let t=r.valueDeclaration.name,n=new Set,i=new Set,o=!1;for(let e of t.elements){if(e.dotDotDotToken){o=!0;break}let t=e.propertyName||e.name;if(!a.isIdentifier(t)){o=!0;break}n.add(t.text),e.initializer&&i.add(t.text)}o||(e=C(Object.fromEntries(Object.entries(e.fields).filter(([e])=>n.has(e)).map(([e,t])=>[e,S(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:_,collected:{}}}function A(e,t){let n=e.checker.getTypeAtLocation(t),r={},i={};for(let t of n.getProperties())r[t.name]=(()=>{if(!t.valueDeclaration)return _;let n=e.checker.getTypeAtLocation(t.valueDeclaration),r=e.resolveTypeArguments(n),a=r.types[0];return a?(i={...i,...r.collected},S(a,n.symbol.name===`Requireable`)):_})();let a=C(r);return{type:a,collected:w(i,[a])}}async function j(e,t,n,r){let o=t.sourceFile(r);if(!o)return[];let c=[];for(let e of o.statements)if(a.isExportAssignment(e)){if(a.isIdentifier(e.expression))continue;c.push([`default`,e,e.expression])}else if(a.isVariableStatement(e))for(let t of e.declarationList.declarations)!a.isIdentifier(t.name)||!t.initializer||c.push([t.name.text,e,t.initializer]);else if(a.isFunctionDeclaration(e)){let t=!!e.modifiers?.find(e=>e.kind===a.SyntaxKind.ExportKeyword)&&!!e.modifiers?.find(e=>e.kind===a.SyntaxKind.DefaultKeyword),n=e.name?.text;(t||n)&&c.push([n||`default`,e,e])}else a.isClassDeclaration(e)&&e.name&&c.push([e.name.text,e,e]);let m=p(o);e.debug(`**storiresInfo: ${m}`);let h=[],g=d(o),v=x.extractExportedNames(o);async function y(i,a,o){if(o===`default`&&m)return null;let s=g[o],c=o===`default`||!!v[o],l=N(t.checker,a);if(m&&c&&(s||l?.parameters.length===0)){let r=M(e,t,n,m.component);return{...i,analyze:async()=>({args:s?{sourcePosition:{start:s.getStart(),end:s.getEnd()},value:await u(s)}:null}),associatedComponent:r}}return l?{...i,exported:c,analyze:async()=>O(e,t,r,o,l)}:null}for(let[e,t,a]of c){let o=await y({id:l({filePath:i.relative(n,r),name:e}),sourcePosition:{start:t.getStart(),end:t.getEnd()}},a,e);o&&h.push(o)}return[...h,...await f(n,t,o,async r=>{let{filePath:a}=s(r),o=(await j(e,t,n,i.join(n,a))).find(e=>e.id===r);return!o||!(`exported`in o)?{props:_,types:{}}:o.analyze()})]}function M(e,t,n,r){let a=m(n,t.checker,r);return r&&a?{id:a,analyze:async()=>{let o=N(t.checker,r);if(!o)return{props:_,types:{}};let{filePath:c,name:l}=s(a);return O(e,t,i.join(n,c),l,o)}}:null}function N(e,t){let n=e.getTypeAtLocation(t);if(n.flags===a.TypeFlags.Any&&a.isCallExpression(t)&&a.isPropertyAccessExpression(t.expression)&&a.isIdentifier(t.expression.name)&&t.expression.name.text===`bind`){let r=e.getSymbolAtLocation(t.expression.expression);r?.valueDeclaration&&a.isVariableDeclaration(r.valueDeclaration)&&r.valueDeclaration.initializer&&(n=e.getTypeAtLocation(r.valueDeclaration.initializer))}for(let e of n.getCallSignatures())if(P(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 P(e){return!!I(e)}const F=new Set([`Element`,`ReactElement`]);function I(e){if(e.isUnion()){for(let t of e.types)if(I(t))return!0}return F.has(e.symbol?.getEscapedName().toString())}const L=c({Component:g,ComponentClass:b(g),FunctionComponent:b(g),ReactElement:g,ReactNode:g,ReactNodeArray:v(g),ReactPortal:g}),R=e({typeAnalyzerOptions:({reader:e,rootDir:s})=>{let c=o.fileURLToPath(new URL(`.`,import.meta.url)),l=i.join(c,`..`,`types`),u=i.join(c,`..`,`node_modules`),d=r.existsSync(l)?l:u;return{reader:n([e,t({mapping:{from:d,to:i.join(s,`node_modules`)},watch:!1}),...d===l?[t({mapping:{from:l,to:i.join(s,`node_modules`,`@types`)},watch:!1})]:[]]),specialTypes:L,tsCompilerOptions:{jsx:a.JsxEmit.ReactJSX,jsxImportSource:`react`}}},crawlFile:({logger:e,resolver:t,rootDir:n,absoluteFilePath:r})=>j(e,t,n,r)}),z=/import (\* as )?React[ ,]/;function B(){return{name:`previewjs:react-js-imports`,async transform(e,t){return t.includes(`node_modules`)||i.extname(t)!==`.js`||z.test(e)?e:`import React from 'react';${e}`}}}const V={info:{apiVersion:5,name:`@cfp-studio/plugin-react`},isCompatible:async e=>{let t=await e.react?.readInstalledVersion();if(!t)return!1;let[n,r]=t.split(`.`).map(e=>Number.parseInt(e));return Number.isNaN(n)||Number.isNaN(r)?!1:n>=17||n===16&&r>=14},async create({rootDir:e,reader:t,logger:n,dependencies:r}){let a=o.fileURLToPath(new URL(`.`,import.meta.url)),s=R({rootDir:e,reader:t,logger:n});return{defaultWrapperPath:`__previewjs__/Wrapper.tsx`,previewDirPath:i.join(a,`..`,`preview`),...s,viteConfig:e=>{let t=e.find(e=>e.name.startsWith(`vite:react-`));return{optimizeDeps:{esbuildOptions:{plugins:r.next?[E()]:[]}},resolve:{alias:{"react-native":`react-native-web`}},plugins:[B(),...e,...t?[]:[T()],{name:`previewjs:update-react-import`,async transform(e,t){if(!t.endsWith(`__previewjs_internal__/renderer/index.tsx`))return;let n=Number.parseInt(await r.react?.readInstalledVersion()||`0`);return e.replace(/__PREVIEWJS_PLUGIN_REACT_IMPORT_PATH__/g,n>=18?`./render-18`:`./render-16`)}}]}}}}};export{V as default};
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,`&amp;`).replace(/"/g,`&quot;`)}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.0",
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/vfs": "2.1.6"
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.6",
36
+ "@cfp-studio/core": "1.0.8",
37
37
  "@cfp-studio/testing": "14.0.1",
38
- "@cfp-studio/iframe": "18.2.0"
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
- renderId: number;
6
- children: ReactNode;
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
- renderId: number,
13
- shouldAbortRender: () => boolean
14
- ) {
15
- while (
16
- !errorBoundaryInstance ||
17
- errorBoundaryInstance.props.renderId !== renderId
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
- state = {
29
- error: null as Error | null,
30
- };
31
-
32
- constructor(props: ErrorBoundaryProps) {
33
- super(props);
34
- // eslint-disable-next-line @typescript-eslint/no-this-alias
35
- errorBoundaryInstance = this;
36
- }
37
-
38
- static getDerivedStateFromError(error: Error) {
39
- return { error };
40
- }
41
-
42
- render() {
43
- if (this.state.error) {
44
- return <></>;
45
- }
46
-
47
- return this.props.children;
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
  }