@yarch/create-admin 0.1.0 → 0.2.1
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 +94 -26
- package/bin/create-admin.mjs +115 -20
- package/bin/gen-registry-snapshot.mjs +32 -0
- package/bin/registry-snapshot.json +15 -0
- package/bin/registry.mjs +19 -0
- package/package.json +2 -2
- package/templates/admin-antd/AGENTS.md +33 -0
- package/templates/admin-antd/CLAUDE.md +1 -0
- package/templates/admin-antd/GEMINI.md +1 -0
- package/templates/admin-antd/archetype.json +48 -7
- package/templates/admin-antd/package.json +1 -0
- package/templates/admin-arco/AGENTS.md +33 -0
- package/templates/admin-arco/CLAUDE.md +1 -0
- package/templates/admin-arco/GEMINI.md +1 -0
- package/templates/admin-semi/AGENTS.md +33 -0
- package/templates/admin-semi/CLAUDE.md +1 -0
- package/templates/admin-semi/GEMINI.md +1 -0
- package/templates/base-semi/AGENTS.md +30 -0
- package/templates/base-semi/CLAUDE.md +1 -0
- package/templates/base-semi/GEMINI.md +1 -0
- package/templates/base-semi/README.md +35 -0
- package/templates/base-semi/archetype.json +13 -0
- package/templates/base-semi/conf/nginx.conf.example +19 -0
- package/templates/base-semi/index.html +11 -0
- package/templates/base-semi/package.json +29 -0
- package/templates/base-semi/src/app/micro-apps.config.ts +16 -0
- package/templates/base-semi/src/app/router.tsx +44 -0
- package/templates/base-semi/src/layouts/basic-layout.tsx +51 -0
- package/templates/base-semi/src/main.tsx +22 -0
- package/templates/base-semi/src/micro/shared.ts +43 -0
- package/templates/base-semi/src/micro/sub-app.tsx +65 -0
- package/templates/base-semi/src/pages/dashboard.tsx +16 -0
- package/templates/base-semi/src/pages/login.tsx +18 -0
- package/templates/base-semi/src/pages/not-found.tsx +13 -0
- package/templates/base-semi/src/stores/auth.ts +13 -0
- package/templates/base-semi/src/ui/theme.ts +7 -0
- package/templates/base-semi/src/vite-env.d.ts +1 -0
- package/templates/base-semi/tsconfig.json +16 -0
- package/templates/base-semi/vite.config.ts +17 -0
- package/templates/sub-semi/AGENTS.md +33 -0
- package/templates/sub-semi/CLAUDE.md +1 -0
- package/templates/sub-semi/GEMINI.md +1 -0
- package/templates/sub-semi/README.md +28 -0
- package/templates/sub-semi/archetype.json +13 -0
- package/templates/sub-semi/index.html +11 -0
- package/templates/sub-semi/package.json +34 -0
- package/templates/sub-semi/src/app/root.tsx +23 -0
- package/templates/sub-semi/src/app/router.tsx +26 -0
- package/templates/sub-semi/src/events.ts +4 -0
- package/templates/sub-semi/src/features/invoices/api.ts +19 -0
- package/templates/sub-semi/src/features/invoices/hooks/use-invoices.ts +18 -0
- package/templates/sub-semi/src/layouts/sub-layout.tsx +12 -0
- package/templates/sub-semi/src/main.tsx +28 -0
- package/templates/sub-semi/src/pages/invoices.tsx +49 -0
- package/templates/sub-semi/src/supply/integrated.ts +48 -0
- package/templates/sub-semi/src/supply/shims/contract.ts +25 -0
- package/templates/sub-semi/src/supply/standalone.ts +38 -0
- package/templates/sub-semi/src/supply/types.ts +18 -0
- package/templates/sub-semi/src/types/index.ts +6 -0
- package/templates/sub-semi/src/ui/popup.ts +13 -0
- package/templates/sub-semi/src/vite-env.d.ts +1 -0
- package/templates/sub-semi/tsconfig.json +20 -0
- package/templates/sub-semi/vite.config.ts +96 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "{{description}}",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "vite",
|
|
9
|
+
"dev:micro": "vite --mode integrated",
|
|
10
|
+
"build": "tsc --noEmit && vite build",
|
|
11
|
+
"build:micro": "tsc --noEmit && vite build --mode integrated",
|
|
12
|
+
"preview": "vite preview"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@yarch/contract": "{{yarchContractDep}}",
|
|
16
|
+
"@yarch/react": "{{yarchReactDep}}",
|
|
17
|
+
"@douyinfe/semi-ui": "^2"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "^19",
|
|
21
|
+
"react-dom": "^19",
|
|
22
|
+
"react-router-dom": "^7"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"react": "^19",
|
|
26
|
+
"react-dom": "^19",
|
|
27
|
+
"react-router-dom": "^7",
|
|
28
|
+
"@types/react": "^19",
|
|
29
|
+
"@types/react-dom": "^19",
|
|
30
|
+
"@vitejs/plugin-react": "^4",
|
|
31
|
+
"typescript": "^5.6",
|
|
32
|
+
"vite": "^6"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { StrictMode } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { BrowserRouter } from "react-router-dom";
|
|
4
|
+
import type { SubAppMountProps } from "@yarch/contract";
|
|
5
|
+
import SubRouter from "./router";
|
|
6
|
+
import { setPopupContainer } from "../ui/popup";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* mount 工厂(五-1):容器与路由 base 由供给层注入(四-2——子应用不感知完整 URL);
|
|
10
|
+
* 返回清理函数供 unmount 成对调用(五-2)。
|
|
11
|
+
*/
|
|
12
|
+
export function mountApp(props: SubAppMountProps): () => void {
|
|
13
|
+
setPopupContainer(props.container); // 六-3:弹层挂载容器指向本应用容器
|
|
14
|
+
const root = createRoot(props.container);
|
|
15
|
+
root.render(
|
|
16
|
+
<StrictMode>
|
|
17
|
+
<BrowserRouter basename={props.base}>
|
|
18
|
+
<SubRouter />
|
|
19
|
+
</BrowserRouter>
|
|
20
|
+
</StrictMode>
|
|
21
|
+
);
|
|
22
|
+
return () => root.unmount();
|
|
23
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { Routes, Route, useNavigate } from "react-router-dom";
|
|
3
|
+
import { supply } from "@supply";
|
|
4
|
+
import SubLayout from "../layouts/sub-layout";
|
|
5
|
+
import Invoices from "../pages/invoices";
|
|
6
|
+
|
|
7
|
+
function NavigatorSetup() {
|
|
8
|
+
const navigate = useNavigate();
|
|
9
|
+
useEffect(() => { supply.setupAppNavigator(navigate); }, [navigate]); // 独立=注册;集成=noop(十-2)
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function SubRouter() {
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
<NavigatorSetup />
|
|
17
|
+
<Routes>
|
|
18
|
+
<Route path="/" element={<SubLayout />}>
|
|
19
|
+
<Route index element={<Invoices />} />
|
|
20
|
+
{/* 基座 manifest 菜单 path="/invoices" 指向的内部路由(四-2:子应用自治路由表) */}
|
|
21
|
+
<Route path="invoices" element={<Invoices />} />
|
|
22
|
+
</Route>
|
|
23
|
+
</Routes>
|
|
24
|
+
</>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createClient, type PageData } from "@yarch/contract";
|
|
2
|
+
import { supply } from "@supply";
|
|
3
|
+
import type { Invoice } from "../../types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 域内 API(十-1/十-3):token 经供给层取(独立=mock,集成=基座下发函数),
|
|
7
|
+
* 401 时只抛 ApiError——跳登录唯一归基座(十-2),本应用不做任何跳转。
|
|
8
|
+
*/
|
|
9
|
+
const api = createClient({
|
|
10
|
+
baseUrl: "/api/v1",
|
|
11
|
+
getHeaders: (): Record<string, string> => {
|
|
12
|
+
const token = supply.getAccessToken();
|
|
13
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export function fetchInvoices(page = 1, pageSize = 20): Promise<PageData<Invoice>> {
|
|
18
|
+
return api.get<PageData<Invoice>>(`/invoices?page=${page}&pageSize=${pageSize}`);
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { fetchInvoices } from "../api";
|
|
3
|
+
import type { Invoice } from "../../../types";
|
|
4
|
+
|
|
5
|
+
export function useInvoices() {
|
|
6
|
+
const [invoices, setInvoices] = useState<Invoice[]>([]);
|
|
7
|
+
const [loading, setLoading] = useState(true);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
let alive = true; // 异步回写防卸载后 setState(五-2 清理纪律的邻接面)
|
|
11
|
+
fetchInvoices()
|
|
12
|
+
.then((page) => { if (alive) { setInvoices(page.list); setLoading(false); } })
|
|
13
|
+
.catch(() => { if (alive) setLoading(false); }); // 十-2:错误只上抛不跳转,列表态由页面兜底
|
|
14
|
+
return () => { alive = false; };
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
return { invoices, loading };
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Typography } from "@douyinfe/semi-ui";
|
|
2
|
+
import { Outlet } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
/** 域内布局(一-1:子应用只做域内页面;外壳/菜单/登录态全部归基座,三-2) */
|
|
5
|
+
export default function SubLayout() {
|
|
6
|
+
return (
|
|
7
|
+
<div style={{ minHeight: "100vh" }}>
|
|
8
|
+
<Typography.Title heading={5}>{{appName}}</Typography.Title>
|
|
9
|
+
<Outlet />
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { supply } from "@supply";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { semiGlobal } from "@douyinfe/semi-ui";
|
|
4
|
+
// React 19 下 Semi 命令式渲染(Toast/Notification 等)须显式注入 createRoot,否则弹层静默不渲染
|
|
5
|
+
semiGlobal.config.createRoot = createRoot;
|
|
6
|
+
import { mountApp } from "./app/root";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 双模式唯一入口(十一-2):挂载参数全部来自供给层,业务代码零分支。
|
|
10
|
+
* 独立模式:vite dev 直开浏览器;集成模式:被基座经 micro-app 加载,supply 已切 integrated 实现。
|
|
11
|
+
*/
|
|
12
|
+
let unmount: (() => void) | null = null;
|
|
13
|
+
|
|
14
|
+
function mount() {
|
|
15
|
+
const props = supply.getMountProps();
|
|
16
|
+
unmount = mountApp(props);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function unmountApp() {
|
|
20
|
+
unmount?.(); // 五-2:unmount 彻底清理(定时器/监听/动态 DOM——root.unmount 卸掉整棵树)
|
|
21
|
+
unmount = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
mount();
|
|
25
|
+
supply.onUnmount(unmountApp); // 五-2:卸载清理成对
|
|
26
|
+
supply.onRemount(() => { // 五-3:mount/unmount 幂等——来回切换不崩不重挂
|
|
27
|
+
if (unmount === null) mount();
|
|
28
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Button, Modal, Table, Typography } from "@douyinfe/semi-ui";
|
|
3
|
+
import { useInvoices } from "../features/invoices/hooks/use-invoices";
|
|
4
|
+
import { appEvents } from "../events";
|
|
5
|
+
import { getPopupContainer } from "../ui/popup";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 域内示例页:列表 + 导出。
|
|
9
|
+
* 导出完成后事件上行(九-1/九-3):appEvents.emit("export-done", …) → 基座 Toast;
|
|
10
|
+
* Modal 指定挂载容器为本应用容器(六-3)。
|
|
11
|
+
*/
|
|
12
|
+
export default function Invoices() {
|
|
13
|
+
const { invoices, loading } = useInvoices();
|
|
14
|
+
const [open, setOpen] = useState(false);
|
|
15
|
+
|
|
16
|
+
const columns = [
|
|
17
|
+
{ title: "账单号", dataIndex: "id" },
|
|
18
|
+
{ title: "金额", dataIndex: "amount" },
|
|
19
|
+
{ title: "状态", dataIndex: "status" },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div>
|
|
24
|
+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
|
|
25
|
+
<h3>账单列表</h3>
|
|
26
|
+
<Button theme="solid" onClick={() => setOpen(true)}>导出</Button>
|
|
27
|
+
</div>
|
|
28
|
+
<Table
|
|
29
|
+
columns={columns}
|
|
30
|
+
dataSource={invoices}
|
|
31
|
+
loading={loading}
|
|
32
|
+
pagination={{ pageSize: 10 }}
|
|
33
|
+
empty={<Typography.Text type="tertiary">暂无数据(后端未起或未登录——独立模式为 mock token)</Typography.Text>}
|
|
34
|
+
/>
|
|
35
|
+
<Modal
|
|
36
|
+
title="确认导出"
|
|
37
|
+
visible={open}
|
|
38
|
+
getPopupContainer={getPopupContainer}
|
|
39
|
+
onOk={() => {
|
|
40
|
+
setOpen(false);
|
|
41
|
+
appEvents.emit("export-done", { count: invoices.length }); // 九-3:{应用名}:export-done
|
|
42
|
+
}}
|
|
43
|
+
onCancel={() => setOpen(false)}
|
|
44
|
+
>
|
|
45
|
+
将导出 {invoices.length} 条账单,完成后基座将收到事件上行通知。
|
|
46
|
+
</Modal>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 集成运行供给(十一-2):登录态/主题/token 全部由基座经 __YARCH_SUPPLY__ 下发(五-5/十-1);
|
|
3
|
+
* 路由 base = 应用名前缀(四-1/四-2 由基座口径注入,本档直接取同值)。
|
|
4
|
+
* 仅存在于集成构建(vite --mode integrated alias 到此)。
|
|
5
|
+
*/
|
|
6
|
+
import type { SubAppMountProps, SubAppUser } from "@yarch/contract";
|
|
7
|
+
import type { Supply } from "./types";
|
|
8
|
+
|
|
9
|
+
interface BaseSupply {
|
|
10
|
+
getToken(): string | null;
|
|
11
|
+
user: SubAppUser | null;
|
|
12
|
+
themeToken?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function baseSupply(): BaseSupply {
|
|
16
|
+
const s = (globalThis as Record<string, unknown>).__YARCH_SUPPLY__ as BaseSupply | undefined;
|
|
17
|
+
if (!s) {
|
|
18
|
+
throw new Error("基座未注册 __YARCH_SUPPLY__(五-5 props 下行)——集成构建必须在基座内运行");
|
|
19
|
+
}
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const supply: Supply = {
|
|
24
|
+
getMountProps(): SubAppMountProps {
|
|
25
|
+
const s = baseSupply();
|
|
26
|
+
return {
|
|
27
|
+
container: document.getElementById("root")!, // micro-app 沙箱内自取(五-1 形式由模板档固化)
|
|
28
|
+
base: "/{{appName}}", // 四-1:路由前缀 = 应用名
|
|
29
|
+
user: s.user,
|
|
30
|
+
getToken: () => s.getToken(),
|
|
31
|
+
themeToken: s.themeToken,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
getAccessToken() {
|
|
35
|
+
return baseSupply().getToken();
|
|
36
|
+
},
|
|
37
|
+
setupAppNavigator() {
|
|
38
|
+
// 十-2:导航端口与 401 跳转唯一归基座(contract 单例已由基座注册),子应用不注册
|
|
39
|
+
},
|
|
40
|
+
onUnmount(cb) {
|
|
41
|
+
window.addEventListener("unmount", cb); // micro-app 卸载事件(五-2:定时器/监听/DOM 彻底清理)
|
|
42
|
+
return () => window.removeEventListener("unmount", cb);
|
|
43
|
+
},
|
|
44
|
+
onRemount(cb) {
|
|
45
|
+
window.addEventListener("mounted", cb); // 五-3:脚本缓存复用下的重挂渲染
|
|
46
|
+
return () => window.removeEventListener("mounted", cb);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// @yarch/contract shim(集成构建专用):contract 运行时单例由基座共享(八-1)——
|
|
2
|
+
// 两份实例 = 两个 ApiError 类,instanceof 全失效;本 shim 保证子应用取到的就是基座那份。
|
|
3
|
+
const ns = (globalThis as Record<string, any>).__YARCH_CONTRACT__;
|
|
4
|
+
if (!ns) throw new Error("集成构建须运行在基座内:__YARCH_CONTRACT__ 未注册(八-1 contract 单例由基座共享)");
|
|
5
|
+
|
|
6
|
+
export const CODE_SUCCESS = ns.CODE_SUCCESS;
|
|
7
|
+
export const errorCodes = ns.errorCodes;
|
|
8
|
+
export const TRACE_ID_HEADER = ns.TRACE_ID_HEADER;
|
|
9
|
+
export const newTraceId = ns.newTraceId;
|
|
10
|
+
export const ApiError = ns.ApiError;
|
|
11
|
+
export const unwrap = ns.unwrap;
|
|
12
|
+
export const unwrapAllowNull = ns.unwrapAllowNull;
|
|
13
|
+
export const setNavigator = ns.setNavigator;
|
|
14
|
+
export const navigateTo = ns.navigateTo;
|
|
15
|
+
export const navigateToLogin = ns.navigateToLogin;
|
|
16
|
+
export const createClient = ns.createClient;
|
|
17
|
+
export const createAppEventBus = ns.createAppEventBus;
|
|
18
|
+
export const getAppEventBus = ns.getAppEventBus;
|
|
19
|
+
export const defineAppEvents = ns.defineAppEvents;
|
|
20
|
+
export const assertAppEventName = ns.assertAppEventName;
|
|
21
|
+
export const createAppStorage = ns.createAppStorage;
|
|
22
|
+
export const subAppRoutePrefix = ns.subAppRoutePrefix;
|
|
23
|
+
export const SHARED_CONTRACT_KEY = ns.SHARED_CONTRACT_KEY;
|
|
24
|
+
export const setSharedContract = ns.setSharedContract;
|
|
25
|
+
export const getSharedContract = ns.getSharedContract;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 独立运行供给(十一-1/十-6):登录态 mock/开发态注入,路由 base 从环境取同值(四-2)。
|
|
3
|
+
* 本模块(含 mock 外壳)不进集成产物——集成构建经 vite alias 指向 integrated.ts。
|
|
4
|
+
*/
|
|
5
|
+
import { createAppStorage, type SubAppMountProps } from "@yarch/contract";
|
|
6
|
+
import { setupReactNavigator } from "@yarch/react";
|
|
7
|
+
import type { Supply } from "./types";
|
|
8
|
+
|
|
9
|
+
const storage = createAppStorage("{{appName}}"); // 二-2:独立模式 storage 同样带 {应用名}: 前缀,无例外
|
|
10
|
+
if (storage.get<string>("mock-token") === null) {
|
|
11
|
+
storage.set("mock-token", "standalone-dev"); // 十-6:独立模式登录态以开发态注入
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// vite base = "/{{appName}}/",与集成模式基座注入的路由前缀同值(四-2 同值口径)
|
|
15
|
+
const baseURL = import.meta.env.BASE_URL.replace(/\/$/, "");
|
|
16
|
+
|
|
17
|
+
export const supply: Supply = {
|
|
18
|
+
getMountProps(): SubAppMountProps {
|
|
19
|
+
return {
|
|
20
|
+
container: document.getElementById("root")!,
|
|
21
|
+
base: baseURL,
|
|
22
|
+
user: { id: "dev", name: "独立模式" },
|
|
23
|
+
getToken: () => storage.get<string>("mock-token"),
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
getAccessToken() {
|
|
27
|
+
return storage.get<string>("mock-token");
|
|
28
|
+
},
|
|
29
|
+
setupAppNavigator(navigate) {
|
|
30
|
+
setupReactNavigator(navigate); // 独立模式由本应用路由实现导航端口(401→/login)
|
|
31
|
+
},
|
|
32
|
+
onUnmount() {
|
|
33
|
+
return () => {}; // 独立模式无卸载方
|
|
34
|
+
},
|
|
35
|
+
onRemount() {
|
|
36
|
+
return () => {};
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SubAppMountProps } from "@yarch/contract";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 环境供给层端口(micro-frontend.md 十一-2):独立/集成双模式差异的唯一容身处——
|
|
5
|
+
* 业务代码只消费本接口,出现 `if (isMicro)` 分支即设计缺陷。
|
|
6
|
+
*/
|
|
7
|
+
export interface Supply {
|
|
8
|
+
/** 挂载参数(五-5 最小集:容器/base/用户上下文/取 token/主题 token) */
|
|
9
|
+
getMountProps(): SubAppMountProps;
|
|
10
|
+
/** 取 token(十-1:独立=开发态 mock 注入;集成=基座下发函数引用) */
|
|
11
|
+
getAccessToken(): string | null;
|
|
12
|
+
/** 导航端口注册(四-3/十-2:独立=注册本应用路由跳转;集成=noop——导航权与 401 跳转唯一归基座) */
|
|
13
|
+
setupAppNavigator(navigate: (path: string) => void): void;
|
|
14
|
+
/** 卸载清理钩(五-2:micro-app unmount 事件触发;返回取消函数,七-4 成对) */
|
|
15
|
+
onUnmount(cb: () => void): () => void;
|
|
16
|
+
/** 重新挂载钩(五-3 mount/unmount 幂等:micro-app 复用脚本缓存重挂时重渲染) */
|
|
17
|
+
onRemount(cb: () => void): () => void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 弹层容器(六-3):Modal/Select/Tooltip 等默认挂 body 的组件必须指定挂载容器为本应用容器,
|
|
3
|
+
* 否则微前端沙箱下弹层逃逸到基座 DOM。页面用法:`<Modal getPopupContainer={getPopupContainer}>`。
|
|
4
|
+
*/
|
|
5
|
+
let container: HTMLElement | null = null;
|
|
6
|
+
|
|
7
|
+
export function setPopupContainer(el: HTMLElement): void {
|
|
8
|
+
container = el;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getPopupContainer(): HTMLElement {
|
|
12
|
+
return container ?? document.body;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"jsx": "react-jsx",
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"baseUrl": ".",
|
|
13
|
+
"paths": {
|
|
14
|
+
"@supply": ["./src/supply/standalone.ts"]
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"include": [
|
|
18
|
+
"src"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import react from "@vitejs/plugin-react";
|
|
3
|
+
import { defineConfig, type Plugin } from "vite";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 双构建模式(十一-1/十一-2):
|
|
7
|
+
* 独立(默认) = 全量自足,pnpm dev / build;
|
|
8
|
+
* 集成(integrated)= 供给层切 __YARCH_SUPPLY__ 实现;react 系与 @yarch/contract
|
|
9
|
+
* 由基座经 window 全局提供(八-1/八-2)——集成产物 dist-micro/ 中框架
|
|
10
|
+
* 与 contract 体积消失(十三表八-1/八-2 机检的构建口径)。
|
|
11
|
+
* 集成模式不挂 plugin-react(fast-refresh preamble 与 micro-app 沙箱互扰;esbuild 按 tsconfig 自动转 JSX)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 框架运行时 shim(集成构建专用,八-2):构建期从真实安装的包内省全部导出名,
|
|
16
|
+
* 生成 `export const X = globalThis.__YARCH_REACT__["<pkg>"].X` 虚拟模块——
|
|
17
|
+
* 覆盖面与所装 react 系版本逐名全等(三方库如 semi-ui 的任意具名 import 均可解析)。
|
|
18
|
+
* 类型检查不受影响(tsconfig 仍指向真实包类型)。
|
|
19
|
+
*/
|
|
20
|
+
const RUNTIME_SPECS: Record<string, string> = {
|
|
21
|
+
// import 说明符 → 基座 __YARCH_REACT__ 下注册名(见 base-semi src/micro/shared.ts)
|
|
22
|
+
react: "react",
|
|
23
|
+
"react-dom": "react-dom",
|
|
24
|
+
"react-dom/client": "react-dom/client",
|
|
25
|
+
"react/jsx-runtime": "react/jsx-runtime",
|
|
26
|
+
"react/jsx-dev-runtime": "react/jsx-dev-runtime",
|
|
27
|
+
"react-router": "react-router",
|
|
28
|
+
"react-router-dom": "react-router-dom",
|
|
29
|
+
};
|
|
30
|
+
const VIRTUAL_PREFIX = "\0yarch-runtime-shim:";
|
|
31
|
+
|
|
32
|
+
function yarchRuntimeShims(): Plugin {
|
|
33
|
+
return {
|
|
34
|
+
name: "yarch-runtime-shims",
|
|
35
|
+
// 解析不走插件 resolveId(bare import 在 alias 之后已被 vite 内置解析器接管)——
|
|
36
|
+
// 由下方 resolve.alias 把说明符改写为虚拟 id,本插件只负责 load 生成完整导出面。
|
|
37
|
+
async load(id) {
|
|
38
|
+
if (!id.startsWith(VIRTUAL_PREFIX)) return null;
|
|
39
|
+
const spec = id.slice(VIRTUAL_PREFIX.length);
|
|
40
|
+
const globalName = RUNTIME_SPECS[spec];
|
|
41
|
+
const exported = await import(spec);
|
|
42
|
+
// CJS 互操作会泄漏 "module.exports" 等非标识符键(cjs-module-lexer 伪影),只保留合法导出名
|
|
43
|
+
const names = Object.keys(exported).filter((n) => n !== "default" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(n));
|
|
44
|
+
const lines = [
|
|
45
|
+
`const ns = (globalThis).__YARCH_REACT__?.[${JSON.stringify(globalName)}];`,
|
|
46
|
+
`if (!ns) throw new Error(${JSON.stringify(
|
|
47
|
+
`集成构建须运行在基座内:__YARCH_REACT__[${globalName}] 未注册(八-2 框架运行时由基座统一提供)`,
|
|
48
|
+
)});`,
|
|
49
|
+
...names.map((n) => `export const ${n} = ns[${JSON.stringify(n)}];`),
|
|
50
|
+
...("default" in exported ? ["export default ns.default;"] : []),
|
|
51
|
+
];
|
|
52
|
+
return lines.join("\n");
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const contractShim = fileURLToPath(new URL("./src/supply/shims/contract.ts", import.meta.url));
|
|
58
|
+
|
|
59
|
+
export default defineConfig(({ mode }) => ({
|
|
60
|
+
plugins: mode === "integrated" ? [yarchRuntimeShims()] : [react()],
|
|
61
|
+
// 十二-4:产物资源挂在本应用前缀下(独立模式 URL 形态与集成一致,四-1 全站无方言)
|
|
62
|
+
base: "/{{appName}}/",
|
|
63
|
+
server: {
|
|
64
|
+
host: "127.0.0.1", // 钉死 IPv4 回环:与默认入口 URL/CI 同口径(vite 默认 localhost 在 macOS 为 ::1 only)
|
|
65
|
+
port: {{port}},
|
|
66
|
+
cors: true, // 基座跨域加载本应用 dev 产物(micro-app fetch 入口与资源)
|
|
67
|
+
proxy: {
|
|
68
|
+
"/api/v1": {
|
|
69
|
+
target: "{{proxyTarget}}",
|
|
70
|
+
changeOrigin: true,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
resolve: {
|
|
75
|
+
alias: [
|
|
76
|
+
// 供给层实现按构建模式切换(vite 不读 tsconfig paths,须显式 alias;tsconfig paths 仅供类型检查)
|
|
77
|
+
{
|
|
78
|
+
find: /^@supply$/,
|
|
79
|
+
replacement: fileURLToPath(
|
|
80
|
+
new URL(mode === "integrated" ? "./src/supply/integrated.ts" : "./src/supply/standalone.ts", import.meta.url),
|
|
81
|
+
),
|
|
82
|
+
},
|
|
83
|
+
...(mode === "integrated"
|
|
84
|
+
? [
|
|
85
|
+
...Object.keys(RUNTIME_SPECS).map((spec) => ({
|
|
86
|
+
find: new RegExp(`^${spec.replace("/", "\\/")}$`),
|
|
87
|
+
replacement: VIRTUAL_PREFIX + spec,
|
|
88
|
+
})),
|
|
89
|
+
{ find: /^@yarch\/contract$/, replacement: contractShim },
|
|
90
|
+
]
|
|
91
|
+
: []),
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
build: { outDir: mode === "integrated" ? "dist-micro" : "dist" },
|
|
95
|
+
preview: { cors: true }, // 基座跨域加载集成产物(micro-app fetch,与 dev server.cors 同口径)
|
|
96
|
+
}));
|