@farris/cli 2.3.7 → 2.3.9
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/lib/commands/add-app.js +5 -5
- package/lib/commands/add-app.js.map +1 -1
- package/lib/commands/add-lib.js +5 -5
- package/lib/commands/add-lib.js.map +1 -1
- package/lib/commands/add-micro.js +12 -0
- package/lib/commands/add-micro.js.map +1 -0
- package/lib/commands/add-project.js +196 -194
- package/lib/commands/add-project.js.map +1 -1
- package/lib/commands/build-components.js +60 -60
- package/lib/commands/build-components.js.map +1 -1
- package/lib/commands/build-css.js +38 -38
- package/lib/commands/build-css.js.map +1 -1
- package/lib/commands/build-lib.js +30 -30
- package/lib/commands/build-lib.js.map +1 -1
- package/lib/commands/build.js +19 -19
- package/lib/commands/build.js.map +1 -1
- package/lib/commands/create-app-options.js +85 -74
- package/lib/commands/create-app-options.js.map +1 -1
- package/lib/commands/create-app.js +131 -131
- package/lib/commands/create-app.js.map +1 -1
- package/lib/commands/create-options.js +58 -32
- package/lib/commands/create-options.js.map +1 -1
- package/lib/commands/create.js +341 -331
- package/lib/commands/create.js.map +1 -1
- package/lib/commands/dev-serve.js +18 -18
- package/lib/commands/extract-i18n.js +74 -74
- package/lib/commands/extract-i18n.js.map +1 -1
- package/lib/commands/generate/api.js +124 -124
- package/lib/commands/generate/api.js.map +1 -1
- package/lib/commands/generate/common.js +101 -101
- package/lib/commands/generate/common.js.map +1 -1
- package/lib/commands/generate/component.js +156 -156
- package/lib/commands/generate/component.js.map +1 -1
- package/lib/commands/generate/view.js +168 -168
- package/lib/commands/generate/view.js.map +1 -1
- package/lib/commands/preview-serve.js +16 -16
- package/lib/commands/workspace/add-app.js +15 -15
- package/lib/commands/workspace/add-app.js.map +1 -1
- package/lib/commands/workspace/create.js +62 -62
- package/lib/commands/workspace/create.js.map +1 -1
- package/lib/commands/workspace/inspect.js +30 -30
- package/lib/commands/workspace/inspect.js.map +1 -1
- package/lib/commands/workspace/verify.js +38 -38
- package/lib/commands/workspace/verify.js.map +1 -1
- package/lib/common/agents-doc.js +33 -33
- package/lib/common/agents-doc.js.map +1 -1
- package/lib/common/apply-catalog-deps.js +15 -15
- package/lib/common/apply-catalog-deps.js.map +1 -1
- package/lib/common/component-file-name.js +3 -3
- package/lib/common/constant.js +85 -84
- package/lib/common/constant.js.map +1 -1
- package/lib/common/create-app-target.js +46 -46
- package/lib/common/create-app-target.js.map +1 -1
- package/lib/common/generate-app.js +106 -109
- package/lib/common/generate-app.js.map +1 -1
- package/lib/common/get-dependencies.js +9 -9
- package/lib/common/get-farris-config.js +25 -25
- package/lib/common/get-farris-config.js.map +1 -1
- package/lib/common/get-version.js +6 -6
- package/lib/common/get-vite-config.js +192 -192
- package/lib/common/get-vite-config.js.map +1 -1
- package/lib/common/lib-package-exports.js +24 -24
- package/lib/common/lib-package-exports.js.map +1 -1
- package/lib/common/list-lib-components.js +23 -23
- package/lib/common/list-lib-components.js.map +1 -1
- package/lib/common/output.js +164 -164
- package/lib/common/output.js.map +1 -1
- package/lib/common/project-layout.js +155 -183
- package/lib/common/project-layout.js.map +1 -1
- package/lib/common/safety.js +172 -173
- package/lib/common/safety.js.map +1 -1
- package/lib/common/template-manifest.js +18 -14
- package/lib/common/template-manifest.js.map +1 -1
- package/lib/common/template-renderer.js +114 -114
- package/lib/common/template-renderer.js.map +1 -1
- package/lib/common/transaction.js +83 -83
- package/lib/common/transaction.js.map +1 -1
- package/lib/common/verifier.js +187 -201
- package/lib/common/verifier.js.map +1 -1
- package/lib/common/workspace-contract.js +191 -157
- package/lib/common/workspace-contract.js.map +1 -1
- package/lib/config/vite-app.js +13 -13
- package/lib/config/vite-common.js +20 -20
- package/lib/config/vite-common.js.map +1 -1
- package/lib/config/vite-component.js +32 -32
- package/lib/config/vite-component.js.map +1 -1
- package/lib/config/vite-lib.js +26 -26
- package/lib/config/vite-lib.js.map +1 -1
- package/lib/index.js +261 -238
- package/lib/index.js.map +1 -1
- package/lib/plugins/create-app-package-json.js +23 -23
- package/lib/plugins/create-component-style.js +44 -44
- package/lib/plugins/create-package-json.js +48 -48
- package/lib/plugins/create-package-json.js.map +1 -1
- package/lib/plugins/dts.js +8 -8
- package/lib/plugins/html-system.js +9 -9
- package/lib/plugins/mock.js +240 -292
- package/lib/plugins/mock.js.map +1 -1
- package/lib/plugins/msw-adapter.js +141 -0
- package/lib/plugins/msw-adapter.js.map +1 -0
- package/lib/plugins/replace.js +17 -17
- package/lib/plugins/systemjs-bundle.js +15 -15
- package/lib/plugins/systemjs-bundle.js.map +1 -1
- package/package.json +6 -1
- package/templates/create/.gitignore.tpl +28 -8
- package/templates/create/AGENTS.md +76 -45
- package/templates/create/README.md +44 -11
- package/templates/create/package.json +2 -1
- package/templates/lib/.gitignore.tpl +31 -1
- package/templates/lib/.gitlab-ci.yml +2 -2
- package/templates/lib/README.md +24 -1
- package/templates/lib/{test → components/__tests__}/index.spec.ts +1 -1
- package/templates/lib/{test → components/__tests__}/index.spec.tsx +2 -2
- package/templates/lib/farris.config.mjs +2 -2
- package/templates/lib/package.json +1 -1
- package/templates/lib/tsconfig.json +1 -2
- package/templates/lib/vitest.config.ts +4 -2
- package/templates/micro/.eslintrc.cjs +15 -0
- package/templates/micro/.gitignore.tpl +33 -0
- package/templates/micro/.gitlab-ci.yml +59 -0
- package/templates/micro/.prettierrc.json +8 -0
- package/templates/micro/farris.config.mjs +34 -0
- package/templates/micro/index.html +12 -0
- package/templates/micro/mock/README.md +53 -0
- package/templates/micro/mock/tsconfig.json +10 -0
- package/templates/micro/package.json +47 -0
- package/templates/micro/src/App.tsx +25 -0
- package/templates/micro/src/locales/index.ts +5 -0
- package/templates/micro/src/locales/zh-CHS.json +1 -0
- package/templates/micro/src/main.ts +47 -0
- package/templates/micro/src/router/index.ts +24 -0
- package/templates/micro/src/views/AboutView.tsx +12 -0
- package/templates/micro/src/views/HomeView.tsx +13 -0
- package/templates/micro/src/views/__tests__/AboutView.spec.tsx +11 -0
- package/templates/micro/src/views/__tests__/HomeView.spec.tsx +11 -0
- package/templates/micro/tsconfig.json +18 -0
- package/templates/micro/vitest.config.ts +12 -0
- package/templates/mobile/.gitignore.tpl +25 -24
- package/templates/mobile/farris.config.mjs +2 -2
- package/templates/mobile/mock/README.md +39 -20
- package/templates/mobile/mock/tsconfig.json +10 -0
- package/templates/mobile/package.json +5 -2
- package/templates/mobile/src/main.ts +5 -40
- package/templates/mobile/src/views/HomeView.tsx +2 -2
- package/templates/mobile/src/views/__tests__/AboutView.spec.tsx +11 -0
- package/templates/mobile/src/views/__tests__/HomeView.spec.tsx +11 -0
- package/templates/mobile/tsconfig.json +1 -2
- package/templates/mobile/vitest.config.ts +4 -2
- package/templates/web/.gitignore.tpl +25 -24
- package/templates/web/farris.config.mjs +2 -2
- package/templates/web/mock/README.md +39 -20
- package/templates/web/mock/tsconfig.json +10 -0
- package/templates/web/package.json +5 -2
- package/templates/web/src/App.tsx +0 -4
- package/templates/web/src/main.ts +5 -40
- package/templates/web/src/views/HomeView.tsx +2 -2
- package/templates/web/src/views/__tests__/AboutView.spec.tsx +11 -0
- package/templates/web/src/views/__tests__/HomeView.spec.tsx +11 -0
- package/templates/web/tsconfig.json +1 -2
- package/templates/web/vitest.config.ts +4 -2
- package/templates/mobile/src/components/TheButton.tsx +0 -9
- package/templates/mobile/test/src/components/TheButton.spec.ts +0 -15
- package/templates/mobile/test/src/components/TheButton.spec.tsx +0 -15
- package/templates/web/src/components/TheButton.tsx +0 -9
- package/templates/web/src/micro-app.d.ts +0 -20
- package/templates/web/src/styles/index.css +0 -0
- package/templates/web/test/src/components/TheButton.spec.ts +0 -15
- package/templates/web/test/src/components/TheButton.spec.tsx +0 -15
- /package/templates/{web → micro}/src/micro/context.ts +0 -0
- /package/templates/{mobile → micro}/src/micro-app.d.ts +0 -0
- /package/templates/{mobile/src/styles/index.css → micro/src/styles/index.scss} +0 -0
|
@@ -1,34 +1,53 @@
|
|
|
1
1
|
# mock 目录
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
开发期接口数据。用 MSW 写法,库本身的用法见 MSW 官方文档。一个领域一个文件,命名 `*.mock.ts`,只导出 handler 数组:
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
import { http, HttpResponse, delay } from 'msw';
|
|
7
|
+
|
|
8
|
+
export const handlers = [
|
|
9
|
+
http.get('/api/v1/users', async ({ request }) => {
|
|
10
|
+
const keyword = new URL(request.url).searchParams.get('keyword') ?? '';
|
|
11
|
+
await delay(300); // 可选:模拟延迟,单位毫秒
|
|
12
|
+
return HttpResponse.json({ data: [], total: 0 }); // 形状跟客户端出口走
|
|
13
|
+
}),
|
|
14
|
+
http.get('/api/v1/users/:id', ({ params }) => HttpResponse.json({ data: { id: params.id } })),
|
|
15
|
+
http.post('/api/v1/users', async ({ request }) => {
|
|
16
|
+
const body = await request.json();
|
|
17
|
+
return HttpResponse.json({ id: 'u-new', ...body }, { status: 201 });
|
|
18
|
+
})
|
|
17
19
|
];
|
|
18
20
|
```
|
|
19
21
|
|
|
22
|
+
## 加一条 mock 并验证
|
|
23
|
+
|
|
24
|
+
1. 先在 `src/api/` 里确认实际请求路径(例如 `/api/v1/users`),逐字抄进 handler 路径,前缀也要带上。
|
|
25
|
+
2. 新建或追加 `mock/<领域>.mock.ts`,按上面的形状写 handler;同一前缀下字面量路径写在 `:参数名` 之前。
|
|
26
|
+
3. 在工程目录执行 `pnpm dev`,打开用到该接口的页面,**验证**返回的是 mock 数据。
|
|
27
|
+
4. 需要联调真实后端时执行 `pnpm dev --no-mock`(或 `MOCK=none pnpm dev`),业务代码不改。
|
|
28
|
+
|
|
20
29
|
规则:
|
|
21
30
|
|
|
22
|
-
- url
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
31
|
+
- url 必须与代码实际发出的请求路径逐字一致,前缀也要带上;请求路径见 `src/api/` 下的接口模块。
|
|
32
|
+
- url 只写路径,以 `/` 开头,不要写协议与主机。写 `/api/v1/users`,不要写 `http://localhost:3000/api/v1/users`;主机或端口不同时不会命中。
|
|
33
|
+
- 路径参数在 url 里用 `:参数名` 表示,例如 `/api/v1/users/:id`。查询参数不写进 url,用 `new URL(request.url).searchParams` 读取。
|
|
34
|
+
- 同一前缀下同时有字面量路径与 `:参数名` 路径时,字面量写在前面。MSW 取第一个匹配的 handler,`/api/v1/users/current` 排在 `/api/v1/users/:id` 后面会被 `:id` 接走。
|
|
35
|
+
- 响应体必须能被工程的 HTTP 客户端正确消费,形状跟着客户端出口走。数据要有业务语义,用中文姓名、机构、单据号这类内容,不要用占位文本。
|
|
36
|
+
- 关键场景要模拟:空态、错误、慢响应。错误用 `HttpResponse.json(..., { status: 500 })`;慢响应用 `delay`。
|
|
37
|
+
- 目录里只放 handler 文件。共享数据或工具函数要放进 `_` 开头的文件,例如 `_data.ts`,加载时会跳过;其他文件会被当作 handler 文件加载,导出内容不是 handler 就会报错。
|
|
38
|
+
- 同一路径不允许在多个 mock 文件中重复定义,重复时开发服务启动或请求即报错。
|
|
39
|
+
- 文件只导出 handler 数组,写 `export const handlers = [...]` 或 `export default [...]`。不需要 `setupServer` / `setupWorker` 接线;接线由脚手架完成。
|
|
40
|
+
- `msw` 已由模板放进应用 `devDependencies`,版本随工作区 catalog 统一。不要把它加到 `dependencies`,也不要在 `mock/` 之外引用它,否则构建期会报错。
|
|
41
|
+
- mock 只用于开发期,不参与构建,也不要在组件里散落假数据。
|
|
27
42
|
|
|
28
43
|
行为:
|
|
29
44
|
|
|
30
|
-
- 命中 mock
|
|
31
|
-
-
|
|
32
|
-
- 未命中且未配代理的 `/api`
|
|
45
|
+
- 命中 mock 时返回 mock 数据。
|
|
46
|
+
- 未命中时按 `farris.config.mjs` 的 `proxy` 配置转发真实后端。
|
|
47
|
+
- 未命中且未配代理的 `/api` 路径返回 404 并提示,便于排查漏配。
|
|
33
48
|
|
|
34
49
|
开关:在工程目录执行 `pnpm dev` 默认生效;`pnpm dev --no-mock` 或 `MOCK=none pnpm dev` 进入真实后端联调。
|
|
50
|
+
|
|
51
|
+
类型:本目录附带独立的 `tsconfig.json`,只服务于编辑器与按需类型检查。应用自身的 `typecheck` 不包含本目录,mock 也不参与构建。
|
|
52
|
+
|
|
53
|
+
约定版本 2026-09。本文件随 `@farris/cli` 模板发布,AI 协作以本文件为准。
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
// mock 目录的独立 TS 工程:只服务于编辑器与按需类型检查(应用自身的 typecheck 不含本目录)。
|
|
3
|
+
// msw 是应用的开发期依赖(见 package.json 的 devDependencies),按常规模块解析即可。
|
|
4
|
+
"extends": "../tsconfig.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"composite": false,
|
|
7
|
+
"noEmit": true
|
|
8
|
+
},
|
|
9
|
+
"include": ["**/*.ts", "**/*.d.ts"]
|
|
10
|
+
}
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "farris-cli dev",
|
|
8
|
-
"build": "
|
|
9
|
-
"build:es": "farris-cli build --format es
|
|
8
|
+
"build": "pnpm run build:es && pnpm run build:umd && pnpm run build:systemjs",
|
|
9
|
+
"build:es": "farris-cli build -ep --format es",
|
|
10
|
+
"build:umd": "farris-cli build -ep --format umd --outDir dist-umd",
|
|
11
|
+
"build:systemjs": "farris-cli build -ep --format systemjs --outDir dist-systemjs",
|
|
10
12
|
"preview": "farris-cli preview",
|
|
11
13
|
"test": "vitest run",
|
|
12
14
|
"test:watch": "vitest",
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
"eslint": "^8.57.0",
|
|
33
35
|
"eslint-plugin-vue": "^9.23.0",
|
|
34
36
|
"jsdom": "^25.0.1",
|
|
37
|
+
"msw": "^2.15.0",
|
|
35
38
|
"prettier": "^3.2.5",
|
|
36
39
|
"typescript": "~5.4.0",
|
|
37
40
|
"vite": "^5.4.19",
|
|
@@ -1,47 +1,12 @@
|
|
|
1
|
-
import { createApp
|
|
1
|
+
import { createApp } from 'vue';
|
|
2
2
|
import FarrisVue from '@farris/mobile-ui-vue';
|
|
3
3
|
import '@farris/mobile-ui-vue/style.css';
|
|
4
4
|
import App from './App';
|
|
5
5
|
import router from './router';<%= styleImport %>
|
|
6
6
|
import { localResources } from './locales';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const app = createApp(App);
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
app = createApp(App);
|
|
16
|
-
app.use(FarrisVue, { basePath: '', locale: 'zh-CHS', localResources });
|
|
17
|
-
app.use(router);
|
|
18
|
-
app.mount('#app');
|
|
19
|
-
|
|
20
|
-
// 微前端环境:按平台下发的 initialPath 定位菜单配置的页面(fullPath)
|
|
21
|
-
// hash 路由必须保留这段兜底;history 路由下平台 defaultPage 已能直接命中,这里是幂等兜底
|
|
22
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
23
|
-
const rawPath = window.microApp?.getData?.()?.initialPath;
|
|
24
|
-
if (typeof rawPath === 'string' && rawPath !== '') {
|
|
25
|
-
// 平台下发的 initialPath 通常不带前导斜杠(如 'form'),这里统一归一化
|
|
26
|
-
const initialPath = rawPath.startsWith('/') ? rawPath : '/' + rawPath;
|
|
27
|
-
if (initialPath !== '/') {
|
|
28
|
-
router.isReady().then(() => router.replace(initialPath)).catch(() => undefined);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** 卸载应用(微前端宿主会调用 window.unmount) */
|
|
35
|
-
function unmount(): void {
|
|
36
|
-
app?.unmount();
|
|
37
|
-
app = null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 微前端(micro-app)生命周期契约:必须暴露到 window
|
|
41
|
-
window.mount = mount;
|
|
42
|
-
window.unmount = unmount;
|
|
43
|
-
|
|
44
|
-
// 独立运行(浏览器直接访问 / iframe 嵌入)时自动挂载
|
|
45
|
-
if (!window.__MICRO_APP_ENVIRONMENT__) {
|
|
46
|
-
mount();
|
|
47
|
-
}
|
|
10
|
+
app.use(FarrisVue, { basePath: '', locale: 'zh-CHS', localResources });
|
|
11
|
+
app.use(router);
|
|
12
|
+
app.mount('#app');
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { defineComponent } from 'vue';
|
|
2
|
-
import TheButton from '../components/TheButton';
|
|
3
2
|
|
|
4
3
|
export default defineComponent({
|
|
5
4
|
name: 'HomeView',
|
|
6
5
|
setup() {
|
|
7
6
|
return () => (
|
|
8
7
|
<main>
|
|
9
|
-
<
|
|
8
|
+
<h1>Home</h1>
|
|
9
|
+
<p>在 src/views/ 下新增页面;通用组件按需创建在 src/components/ 下。</p>
|
|
10
10
|
</main>
|
|
11
11
|
);
|
|
12
12
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import AboutView from '../AboutView';
|
|
4
|
+
|
|
5
|
+
describe('AboutView', () => {
|
|
6
|
+
it('mounts', () => {
|
|
7
|
+
const wrapper = mount(AboutView);
|
|
8
|
+
|
|
9
|
+
expect(wrapper.exists()).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import HomeView from '../HomeView';
|
|
4
|
+
|
|
5
|
+
describe('HomeView', () => {
|
|
6
|
+
it('mounts', () => {
|
|
7
|
+
const wrapper = mount(HomeView);
|
|
8
|
+
|
|
9
|
+
expect(wrapper.exists()).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import vue from '@vitejs/plugin-vue';
|
|
2
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import { configDefaults, defineConfig } from 'vitest/config';
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
plugins: [vue()],
|
|
6
6
|
test: {
|
|
7
7
|
environment: 'jsdom',
|
|
8
|
-
|
|
8
|
+
// 官方默认收集范围(**/*.{test,spec}.*),源码旁的测试才会被收集
|
|
9
|
+
include: [...configDefaults.include],
|
|
10
|
+
exclude: [...configDefaults.exclude, '**/__sandbox_*/**']
|
|
9
11
|
}
|
|
10
12
|
});
|
|
@@ -1,32 +1,33 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
# 依赖与产物
|
|
2
|
+
node_modules/
|
|
3
|
+
dist/
|
|
4
|
+
dist-es/
|
|
5
|
+
dist-ssr/
|
|
6
|
+
coverage/
|
|
7
|
+
*.tsbuildinfo
|
|
8
|
+
.pnpm-store/
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
# 日志与本地文件
|
|
11
|
+
*.log
|
|
12
12
|
.DS_Store
|
|
13
|
-
dist
|
|
14
|
-
dist-es
|
|
15
|
-
dist-ssr
|
|
16
|
-
coverage
|
|
17
13
|
*.local
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
!.env.example
|
|
17
|
+
package-lock.json
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
/cypress/screenshots/
|
|
21
|
-
|
|
22
|
-
# Editor directories and files
|
|
19
|
+
# 编辑器
|
|
23
20
|
.vscode/*
|
|
24
21
|
!.vscode/extensions.json
|
|
25
|
-
.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*.njsproj
|
|
29
|
-
*.sln
|
|
22
|
+
!.vscode/settings.json
|
|
23
|
+
.idea/
|
|
24
|
+
.history
|
|
30
25
|
*.sw?
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
# AI 工具
|
|
28
|
+
.codebuddy/
|
|
29
|
+
.cursor/
|
|
30
|
+
.claude/
|
|
31
|
+
.windsurf/
|
|
32
|
+
.trae/
|
|
33
|
+
.opencode/
|
|
@@ -4,9 +4,9 @@ export default {
|
|
|
4
4
|
// 相对路径 base:微前端(IBP)与 iframe 嵌入场景都适用
|
|
5
5
|
base: './',
|
|
6
6
|
routerMode: 'hash',
|
|
7
|
-
//
|
|
7
|
+
// 默认交付格式;pnpm build 会依次产出 es / umd / systemjs(iife 见 --format iife)
|
|
8
8
|
format: "<%= format %>",
|
|
9
|
-
// 输出目录 App
|
|
9
|
+
// 输出目录 App/Lib 模式默认值均为 './dist'
|
|
10
10
|
// outDir: fileURLToPath(new URL('./dist', import.meta.url)),
|
|
11
11
|
// 最小化 默认值 true
|
|
12
12
|
minify: true,
|
|
@@ -1,34 +1,53 @@
|
|
|
1
1
|
# mock 目录
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
开发期接口数据。用 MSW 写法,库本身的用法见 MSW 官方文档。一个领域一个文件,命名 `*.mock.ts`,只导出 handler 数组:
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
import { http, HttpResponse, delay } from 'msw';
|
|
7
|
+
|
|
8
|
+
export const handlers = [
|
|
9
|
+
http.get('/api/v1/users', async ({ request }) => {
|
|
10
|
+
const keyword = new URL(request.url).searchParams.get('keyword') ?? '';
|
|
11
|
+
await delay(300); // 可选:模拟延迟,单位毫秒
|
|
12
|
+
return HttpResponse.json({ data: [], total: 0 }); // 形状跟客户端出口走
|
|
13
|
+
}),
|
|
14
|
+
http.get('/api/v1/users/:id', ({ params }) => HttpResponse.json({ data: { id: params.id } })),
|
|
15
|
+
http.post('/api/v1/users', async ({ request }) => {
|
|
16
|
+
const body = await request.json();
|
|
17
|
+
return HttpResponse.json({ id: 'u-new', ...body }, { status: 201 });
|
|
18
|
+
})
|
|
17
19
|
];
|
|
18
20
|
```
|
|
19
21
|
|
|
22
|
+
## 加一条 mock 并验证
|
|
23
|
+
|
|
24
|
+
1. 先在 `src/api/` 里确认实际请求路径(例如 `/api/v1/users`),逐字抄进 handler 路径,前缀也要带上。
|
|
25
|
+
2. 新建或追加 `mock/<领域>.mock.ts`,按上面的形状写 handler;同一前缀下字面量路径写在 `:参数名` 之前。
|
|
26
|
+
3. 在工程目录执行 `pnpm dev`,打开用到该接口的页面,**验证**返回的是 mock 数据。
|
|
27
|
+
4. 需要联调真实后端时执行 `pnpm dev --no-mock`(或 `MOCK=none pnpm dev`),业务代码不改。
|
|
28
|
+
|
|
20
29
|
规则:
|
|
21
30
|
|
|
22
|
-
- url
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
31
|
+
- url 必须与代码实际发出的请求路径逐字一致,前缀也要带上;请求路径见 `src/api/` 下的接口模块。
|
|
32
|
+
- url 只写路径,以 `/` 开头,不要写协议与主机。写 `/api/v1/users`,不要写 `http://localhost:3000/api/v1/users`;主机或端口不同时不会命中。
|
|
33
|
+
- 路径参数在 url 里用 `:参数名` 表示,例如 `/api/v1/users/:id`。查询参数不写进 url,用 `new URL(request.url).searchParams` 读取。
|
|
34
|
+
- 同一前缀下同时有字面量路径与 `:参数名` 路径时,字面量写在前面。MSW 取第一个匹配的 handler,`/api/v1/users/current` 排在 `/api/v1/users/:id` 后面会被 `:id` 接走。
|
|
35
|
+
- 响应体必须能被工程的 HTTP 客户端正确消费,形状跟着客户端出口走。数据要有业务语义,用中文姓名、机构、单据号这类内容,不要用占位文本。
|
|
36
|
+
- 关键场景要模拟:空态、错误、慢响应。错误用 `HttpResponse.json(..., { status: 500 })`;慢响应用 `delay`。
|
|
37
|
+
- 目录里只放 handler 文件。共享数据或工具函数要放进 `_` 开头的文件,例如 `_data.ts`,加载时会跳过;其他文件会被当作 handler 文件加载,导出内容不是 handler 就会报错。
|
|
38
|
+
- 同一路径不允许在多个 mock 文件中重复定义,重复时开发服务启动或请求即报错。
|
|
39
|
+
- 文件只导出 handler 数组,写 `export const handlers = [...]` 或 `export default [...]`。不需要 `setupServer` / `setupWorker` 接线;接线由脚手架完成。
|
|
40
|
+
- `msw` 已由模板放进应用 `devDependencies`,版本随工作区 catalog 统一。不要把它加到 `dependencies`,也不要在 `mock/` 之外引用它,否则构建期会报错。
|
|
41
|
+
- mock 只用于开发期,不参与构建,也不要在组件里散落假数据。
|
|
27
42
|
|
|
28
43
|
行为:
|
|
29
44
|
|
|
30
|
-
- 命中 mock
|
|
31
|
-
-
|
|
32
|
-
- 未命中且未配代理的 `/api`
|
|
45
|
+
- 命中 mock 时返回 mock 数据。
|
|
46
|
+
- 未命中时按 `farris.config.mjs` 的 `proxy` 配置转发真实后端。
|
|
47
|
+
- 未命中且未配代理的 `/api` 路径返回 404 并提示,便于排查漏配。
|
|
33
48
|
|
|
34
49
|
开关:在工程目录执行 `pnpm dev` 默认生效;`pnpm dev --no-mock` 或 `MOCK=none pnpm dev` 进入真实后端联调。
|
|
50
|
+
|
|
51
|
+
类型:本目录附带独立的 `tsconfig.json`,只服务于编辑器与按需类型检查。应用自身的 `typecheck` 不包含本目录,mock 也不参与构建。
|
|
52
|
+
|
|
53
|
+
约定版本 2026-09。本文件随 `@farris/cli` 模板发布,AI 协作以本文件为准。
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
// mock 目录的独立 TS 工程:只服务于编辑器与按需类型检查(应用自身的 typecheck 不含本目录)。
|
|
3
|
+
// msw 是应用的开发期依赖(见 package.json 的 devDependencies),按常规模块解析即可。
|
|
4
|
+
"extends": "../tsconfig.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"composite": false,
|
|
7
|
+
"noEmit": true
|
|
8
|
+
},
|
|
9
|
+
"include": ["**/*.ts", "**/*.d.ts"]
|
|
10
|
+
}
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "farris-cli dev",
|
|
8
|
-
"build": "
|
|
9
|
-
"build:es": "farris-cli build --format es
|
|
8
|
+
"build": "pnpm run build:es && pnpm run build:umd && pnpm run build:systemjs",
|
|
9
|
+
"build:es": "farris-cli build -ep --format es",
|
|
10
|
+
"build:umd": "farris-cli build -ep --format umd --outDir dist-umd",
|
|
11
|
+
"build:systemjs": "farris-cli build -ep --format systemjs --outDir dist-systemjs",
|
|
10
12
|
"preview": "farris-cli preview",
|
|
11
13
|
"test": "vitest run",
|
|
12
14
|
"test:watch": "vitest",
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
"eslint": "^8.57.0",
|
|
33
35
|
"eslint-plugin-vue": "^9.23.0",
|
|
34
36
|
"jsdom": "^25.0.1",
|
|
37
|
+
"msw": "^2.15.0",
|
|
35
38
|
"prettier": "^3.2.5",
|
|
36
39
|
"typescript": "~5.4.0",
|
|
37
40
|
"vite": "^5.4.19",
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { defineComponent } from 'vue';
|
|
2
2
|
import { RouterLink, RouterView } from 'vue-router';
|
|
3
|
-
import { setupMicroAppContext } from './micro/context';
|
|
4
3
|
|
|
5
4
|
export default defineComponent({
|
|
6
5
|
name: 'App',
|
|
7
6
|
setup() {
|
|
8
|
-
// 微前端环境:订阅主应用下发的上下文(语言 / 主题)
|
|
9
|
-
setupMicroAppContext();
|
|
10
|
-
|
|
11
7
|
return () => (
|
|
12
8
|
<>
|
|
13
9
|
<header>
|
|
@@ -1,47 +1,12 @@
|
|
|
1
|
-
import { createApp
|
|
1
|
+
import { createApp } from 'vue';
|
|
2
2
|
import FarrisVue from '@farris/ui-vue';
|
|
3
3
|
import '@farris/ui-vue/index.css';
|
|
4
4
|
import App from './App';
|
|
5
5
|
import router from './router';<%= styleImport %>
|
|
6
6
|
import { localResources } from './locales';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const app = createApp(App);
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
app = createApp(App);
|
|
16
|
-
app.use(FarrisVue, { basePath: '', locale: 'zh-CHS', localResources });
|
|
17
|
-
app.use(router);
|
|
18
|
-
app.mount('#app');
|
|
19
|
-
|
|
20
|
-
// 微前端环境:按平台下发的 initialPath 定位菜单配置的页面(fullPath)
|
|
21
|
-
// hash 路由必须保留这段兜底;history 路由下平台 defaultPage 已能直接命中,这里是幂等兜底
|
|
22
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
23
|
-
const rawPath = window.microApp?.getData?.()?.initialPath;
|
|
24
|
-
if (typeof rawPath === 'string' && rawPath !== '') {
|
|
25
|
-
// 平台下发的 initialPath 通常不带前导斜杠(如 'form'),这里统一归一化
|
|
26
|
-
const initialPath = rawPath.startsWith('/') ? rawPath : '/' + rawPath;
|
|
27
|
-
if (initialPath !== '/') {
|
|
28
|
-
router.isReady().then(() => router.replace(initialPath)).catch(() => undefined);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** 卸载应用(微前端宿主会调用 window.unmount) */
|
|
35
|
-
function unmount(): void {
|
|
36
|
-
app?.unmount();
|
|
37
|
-
app = null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 微前端(micro-app)生命周期契约:必须暴露到 window
|
|
41
|
-
window.mount = mount;
|
|
42
|
-
window.unmount = unmount;
|
|
43
|
-
|
|
44
|
-
// 独立运行(浏览器直接访问 / iframe 嵌入)时自动挂载
|
|
45
|
-
if (!window.__MICRO_APP_ENVIRONMENT__) {
|
|
46
|
-
mount();
|
|
47
|
-
}
|
|
10
|
+
app.use(FarrisVue, { basePath: '', locale: 'zh-CHS', localResources });
|
|
11
|
+
app.use(router);
|
|
12
|
+
app.mount('#app');
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { defineComponent } from 'vue';
|
|
2
|
-
import TheButton from '../components/TheButton';
|
|
3
2
|
|
|
4
3
|
export default defineComponent({
|
|
5
4
|
name: 'HomeView',
|
|
6
5
|
setup() {
|
|
7
6
|
return () => (
|
|
8
7
|
<main>
|
|
9
|
-
<
|
|
8
|
+
<h1>Home</h1>
|
|
9
|
+
<p>在 src/views/ 下新增页面;通用组件按需创建在 src/components/ 下。</p>
|
|
10
10
|
</main>
|
|
11
11
|
);
|
|
12
12
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import AboutView from '../AboutView';
|
|
4
|
+
|
|
5
|
+
describe('AboutView', () => {
|
|
6
|
+
it('mounts', () => {
|
|
7
|
+
const wrapper = mount(AboutView);
|
|
8
|
+
|
|
9
|
+
expect(wrapper.exists()).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import HomeView from '../HomeView';
|
|
4
|
+
|
|
5
|
+
describe('HomeView', () => {
|
|
6
|
+
it('mounts', () => {
|
|
7
|
+
const wrapper = mount(HomeView);
|
|
8
|
+
|
|
9
|
+
expect(wrapper.exists()).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import vue from '@vitejs/plugin-vue';
|
|
2
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import { configDefaults, defineConfig } from 'vitest/config';
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
plugins: [vue()],
|
|
6
6
|
test: {
|
|
7
7
|
environment: 'jsdom',
|
|
8
|
-
|
|
8
|
+
// 官方默认收集范围(**/*.{test,spec}.*),源码旁的测试才会被收集
|
|
9
|
+
include: [...configDefaults.include],
|
|
10
|
+
exclude: [...configDefaults.exclude, '**/__sandbox_*/**']
|
|
9
11
|
}
|
|
10
12
|
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import TheButton from '../../../src/components/TheButton';
|
|
4
|
-
|
|
5
|
-
describe('TheButton', () => {
|
|
6
|
-
it('mounts and renders its default slot', () => {
|
|
7
|
-
const wrapper = mount(TheButton, {
|
|
8
|
-
global: {
|
|
9
|
-
stubs: { FmButton: { template: '<button><slot /></button>' } }
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
expect(wrapper.text()).toContain('按钮');
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import TheButton from '../../../src/components/TheButton';
|
|
4
|
-
|
|
5
|
-
describe('TheButton (tsx collection)', () => {
|
|
6
|
-
it('is collected by the unified vitest include', () => {
|
|
7
|
-
const wrapper = mount(TheButton, {
|
|
8
|
-
global: {
|
|
9
|
-
stubs: { FmButton: { template: '<button><slot /></button>' } }
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
expect(wrapper.text()).toContain('按钮');
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export {};
|
|
2
|
-
|
|
3
|
-
declare global {
|
|
4
|
-
interface Window {
|
|
5
|
-
/** micro-app 子应用环境标记(由宿主注入) */
|
|
6
|
-
__MICRO_APP_ENVIRONMENT__?: boolean;
|
|
7
|
-
/** 宿主注入的 micro-app API */
|
|
8
|
-
microApp?: {
|
|
9
|
-
addGlobalDataListener?: (callback: (data: any) => void, immediately?: boolean) => void;
|
|
10
|
-
setGlobalData?: (data: Record<string, any>) => void;
|
|
11
|
-
getData?: () => Record<string, any> | null;
|
|
12
|
-
router?: {
|
|
13
|
-
getBaseAppRouter?: () => any;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
/** 微前端生命周期:宿主会调用这两个方法 */
|
|
17
|
-
mount?: () => void;
|
|
18
|
-
unmount?: () => void;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
File without changes
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import TheButton from '../../../src/components/TheButton';
|
|
4
|
-
|
|
5
|
-
describe('TheButton', () => {
|
|
6
|
-
it('mounts and renders its default slot', () => {
|
|
7
|
-
const wrapper = mount(TheButton, {
|
|
8
|
-
global: {
|
|
9
|
-
stubs: { FButton: { template: '<button><slot /></button>' } }
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
expect(wrapper.text()).toContain('按钮');
|
|
14
|
-
});
|
|
15
|
-
});
|