@ykdz/template 0.0.41 → 0.0.42
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/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +111 -99
- package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.js +29 -4
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +11 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.js +14 -4
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.js +13 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +44 -10
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +4 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +19 -8
- package/node_modules/@ykdz/template-builtin-presets/package.json +23 -10
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +30 -29
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +8 -21
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-diagnostics.yml +21 -5
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.yml +4 -2
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/AGENTS.md.template +7 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/gitignore +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/pnpm-workspace.yaml +3 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/scripts/node-test-count-reporter.ts +31 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/scripts/run-rust-tests.ts +100 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/scripts/run-turbo-boundaries.ts +74 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/scripts/task-gates.ts +589 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo-projections/database-preparation.json +17 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo-projections/deployment.json +13 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo-projections/rust-toolchain.json +5 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo-projections/vue-hono-joint-e2e.json +13 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo.json +14 -15
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/typescript-config/turbo.json +30 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/tsconfig.scripts.json +12 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/turbo.json +19 -4
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/TESTING.md +7 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/src/components/CounterButton.vue +18 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/test/counter.browser.test.ts +16 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/test/{app.test.ts → counter.node.test.ts} +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/tsconfig.test.json +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/vitest.config.ts +50 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/check-artifact.ts +16 -5
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/release.yml +5 -2
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/turbo.json +5 -2
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/bridge.ts +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/cli-command-identity.ts +0 -45
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/e2e/run-journeys.test.ts +11 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/integration/command.test.ts +43 -39
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/unit/cli-command-identity.test.ts +52 -52
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/tsconfig.json +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/turbo.json +58 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/test/greet.test.ts +20 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/tsconfig.json +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/turbo.json +44 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/todos.test.ts +8 -11
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/turbo.json +38 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/turbo.json +44 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +18 -2
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/test/counter.browser.test.ts +14 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.test.json +8 -2
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/turbo.json +61 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/vitest.config.ts +29 -2
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/src/App.vue +2 -12
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/turbo.json +60 -2
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/api/test/app.test.ts +7 -6
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/api/tsconfig.json +2 -7
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/api/turbo.json +46 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/src/App.vue +2 -11
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/turbo.json +57 -1
- package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.d.ts +1 -1
- package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.js +6 -3
- package/node_modules/@ykdz/template-core/dist/module-graph.d.ts +0 -9
- package/node_modules/@ykdz/template-core/dist/module-graph.js +0 -40
- package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +2 -2
- package/node_modules/@ykdz/template-core/dist/package-contribution.js +2 -44
- package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +3 -0
- package/node_modules/@ykdz/template-core/dist/project-github.js +7 -3
- package/node_modules/@ykdz/template-core/dist/renderer.d.ts +1 -0
- package/node_modules/@ykdz/template-core/dist/renderer.js +39 -1
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +34 -16
- package/node_modules/@ykdz/template-core/package.json +0 -5
- package/package.json +4 -10
- package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/.github/workflows/check.yml +0 -23
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/.github/workflows/check.yml +0 -19
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/.github/workflows/check.yml +0 -20
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/vitest.config.ts +0 -8
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/api/vitest.config.ts +0 -15
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/vitest.config.ts +0 -10
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": ["//"],
|
|
3
|
-
"tags": ["library"]
|
|
3
|
+
"tags": ["library"],
|
|
4
|
+
"tasks": {
|
|
5
|
+
"build": {
|
|
6
|
+
"dependsOn": ["^build"],
|
|
7
|
+
"inputs": [],
|
|
8
|
+
"outputs": []
|
|
9
|
+
},
|
|
10
|
+
"format:check": {
|
|
11
|
+
"inputs": [],
|
|
12
|
+
"outputs": [],
|
|
13
|
+
"tags": ["quality:light"]
|
|
14
|
+
},
|
|
15
|
+
"format:write": {
|
|
16
|
+
"dependsOn": ["lint:fix"],
|
|
17
|
+
"inputs": [],
|
|
18
|
+
"outputs": [],
|
|
19
|
+
"cache": false,
|
|
20
|
+
"tags": ["work:repair"]
|
|
21
|
+
},
|
|
22
|
+
"lint:fix": {
|
|
23
|
+
"inputs": [],
|
|
24
|
+
"outputs": [],
|
|
25
|
+
"cache": false
|
|
26
|
+
},
|
|
27
|
+
"typecheck": {
|
|
28
|
+
"dependsOn": ["^typecheck"],
|
|
29
|
+
"inputs": [],
|
|
30
|
+
"outputs": []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
4
33
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "{{TYPESCRIPT_CONFIG_PACKAGE}}/base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"customConditions": ["source"],
|
|
5
|
+
"module": "nodenext",
|
|
6
|
+
"moduleResolution": "nodenext",
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"target": "es2024",
|
|
9
|
+
"types": ["node"]
|
|
10
|
+
},
|
|
11
|
+
"include": ["scripts/**/*.ts"]
|
|
12
|
+
}
|
|
@@ -4,19 +4,34 @@
|
|
|
4
4
|
"tasks": {
|
|
5
5
|
"format:check": {
|
|
6
6
|
"dependsOn": ["//#toolchain:prepare"],
|
|
7
|
-
"
|
|
7
|
+
"inputs": [],
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"tags": ["quality:light"]
|
|
8
10
|
},
|
|
9
11
|
"format:write": {
|
|
10
12
|
"dependsOn": ["//#toolchain:prepare"],
|
|
11
|
-
"
|
|
13
|
+
"inputs": [],
|
|
14
|
+
"outputs": [],
|
|
15
|
+
"cache": false,
|
|
16
|
+
"tags": ["work:repair"]
|
|
12
17
|
},
|
|
13
18
|
"lint": {
|
|
14
19
|
"dependsOn": ["//#toolchain:prepare"],
|
|
15
|
-
"
|
|
20
|
+
"inputs": [],
|
|
21
|
+
"outputs": [],
|
|
22
|
+
"tags": ["quality:light"]
|
|
16
23
|
},
|
|
17
24
|
"test": {
|
|
18
25
|
"dependsOn": ["//#toolchain:prepare"],
|
|
19
|
-
"
|
|
26
|
+
"inputs": [],
|
|
27
|
+
"outputs": [],
|
|
28
|
+
"tags": ["quality:light"]
|
|
29
|
+
},
|
|
30
|
+
"typecheck": {
|
|
31
|
+
"dependsOn": ["^typecheck"],
|
|
32
|
+
"inputs": [],
|
|
33
|
+
"outputs": [],
|
|
34
|
+
"tags": ["quality:light"]
|
|
20
35
|
}
|
|
21
36
|
}
|
|
22
37
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 测试约定
|
|
2
|
+
|
|
3
|
+
前端包的 `pnpm test` 使用 Vitest 的 `node` 项目运行逻辑测试;`pnpm test:browser` 使用 `browser` 项目在 Chromium 中运行组件测试。Vitest Browser Mode 通过 Playwright provider 启动真实浏览器,测试文件按运行环境命名:`*.node.test.ts` 和 `*.browser.test.ts`。
|
|
4
|
+
|
|
5
|
+
可复用的业务逻辑、状态管理和纯函数放在 Node 测试中。组件测试通过 `vitest-browser-vue` 渲染真实 Vue 组件,优先按角色和可访问名称查找控件,再执行点击、输入等用户操作并检查可见结果。默认 `render` 会在下个测试开始前清理组件;按组件真实需要配置插件,若使用 Pinia,每个测试都应创建独立实例,避免测试之间共享状态。
|
|
6
|
+
|
|
7
|
+
`pnpm test` 与 `pnpm test:browser` 选择互斥项目。Playwright Test 的 `pnpm test:e2e` 用于覆盖跨页面、服务或持久化流程中的关键用户旅程;组件内部的交互由 Browser Mode 验证,避免在端到端测试中重复这些细节。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { storeToRefs } from "pinia";
|
|
3
|
+
|
|
4
|
+
import { useCounterStore } from "#/stores/counter";
|
|
5
|
+
|
|
6
|
+
const counter = useCounterStore();
|
|
7
|
+
const { count } = storeToRefs(counter);
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<button
|
|
12
|
+
class="rounded bg-cyan-300 px-4 py-2 font-semibold text-slate-950 hover:bg-cyan-200"
|
|
13
|
+
type="button"
|
|
14
|
+
@click="counter.increment()"
|
|
15
|
+
>
|
|
16
|
+
计数:{{ count }}
|
|
17
|
+
</button>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createPinia } from "pinia";
|
|
2
|
+
import { expect, it } from "vitest";
|
|
3
|
+
import { render } from "vitest-browser-vue";
|
|
4
|
+
|
|
5
|
+
import CounterButton from "../src/components/CounterButton.vue";
|
|
6
|
+
|
|
7
|
+
it("increments the Pinia counter when clicked", async () => {
|
|
8
|
+
const screen = await render(CounterButton, {
|
|
9
|
+
global: { plugins: [createPinia()] },
|
|
10
|
+
});
|
|
11
|
+
const button = screen.getByRole("button", { name: /计数/ });
|
|
12
|
+
|
|
13
|
+
await expect.element(button).toHaveTextContent("计数:0");
|
|
14
|
+
await button.click();
|
|
15
|
+
await expect.element(button).toHaveTextContent("计数:1");
|
|
16
|
+
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"customConditions": ["source"],
|
|
5
5
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
6
6
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.test.tsbuildinfo",
|
|
7
|
-
"types": ["node", "
|
|
7
|
+
"types": ["node", "web-bluetooth"]
|
|
8
8
|
},
|
|
9
9
|
"include": ["env.d.ts", "src/**/*.ts", "src/**/*.vue", "test/**/*.ts"]
|
|
10
10
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { playwright } from "@vitest/browser-playwright";
|
|
2
|
+
import type { ConfigEnv, UserConfig, UserConfigExport } from "vite";
|
|
3
|
+
import { defineConfig, mergeConfig } from "vitest/config";
|
|
4
|
+
|
|
5
|
+
import viteConfig from "./vite.config.ts";
|
|
6
|
+
|
|
7
|
+
function resolveViteConfig(
|
|
8
|
+
config: UserConfigExport,
|
|
9
|
+
configEnv: ConfigEnv,
|
|
10
|
+
): UserConfig | Promise<UserConfig> {
|
|
11
|
+
return typeof config === "function" ? config(configEnv) : config;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default defineConfig(async (configEnv) => {
|
|
15
|
+
const baseConfig = await resolveViteConfig(viteConfig, configEnv);
|
|
16
|
+
|
|
17
|
+
return mergeConfig(
|
|
18
|
+
baseConfig,
|
|
19
|
+
defineConfig({
|
|
20
|
+
test: {
|
|
21
|
+
attachmentsDir: ".vitest/attachments",
|
|
22
|
+
exclude: ["test/e2e/**", "node_modules/**", "dist/**"],
|
|
23
|
+
projects: [
|
|
24
|
+
{
|
|
25
|
+
extends: true,
|
|
26
|
+
test: {
|
|
27
|
+
name: "node",
|
|
28
|
+
include: ["test/**/*.node.test.ts"],
|
|
29
|
+
environment: "node",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
extends: true,
|
|
34
|
+
test: {
|
|
35
|
+
name: "browser",
|
|
36
|
+
include: ["test/**/*.browser.test.ts"],
|
|
37
|
+
browser: {
|
|
38
|
+
enabled: true,
|
|
39
|
+
provider: playwright(),
|
|
40
|
+
headless: true,
|
|
41
|
+
screenshotFailures: true,
|
|
42
|
+
instances: [{ browser: "chromium" }],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
});
|
package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/check-artifact.ts
CHANGED
|
@@ -14,6 +14,8 @@ function oneLine(value: string): string {
|
|
|
14
14
|
const rawArguments = process.argv.slice(2);
|
|
15
15
|
const arguments_ =
|
|
16
16
|
rawArguments[0] === "--" ? rawArguments.slice(1) : rawArguments;
|
|
17
|
+
const requiredSetting = process.env.PUBLICATION_ARTIFACT_REQUIRED;
|
|
18
|
+
const required = requiredSetting === "true";
|
|
17
19
|
const outputDirectoryFromEnvironment =
|
|
18
20
|
process.env.PUBLICATION_ARTIFACT_OUTPUT_DIRECTORY;
|
|
19
21
|
const explicitOutputDirectory =
|
|
@@ -30,9 +32,10 @@ const environmentOutputDirectory =
|
|
|
30
32
|
? null
|
|
31
33
|
: outputDirectoryFromEnvironment;
|
|
32
34
|
const validArguments =
|
|
33
|
-
(
|
|
34
|
-
(
|
|
35
|
-
|
|
35
|
+
(requiredSetting === undefined || required) &&
|
|
36
|
+
((arguments_.length === 0 && environmentOutputDirectory !== null) ||
|
|
37
|
+
(explicitOutputDirectory !== undefined &&
|
|
38
|
+
environmentOutputDirectory === undefined));
|
|
36
39
|
if (!validArguments) {
|
|
37
40
|
console.error("ERROR publication-artifact-usage");
|
|
38
41
|
console.error(`Observed: ${JSON.stringify(arguments_)}`);
|
|
@@ -57,7 +60,16 @@ if (!validArguments) {
|
|
|
57
60
|
outputDirectory,
|
|
58
61
|
});
|
|
59
62
|
if (result.kind === "blocked") {
|
|
60
|
-
|
|
63
|
+
if (result.readiness.mode === "safe-unconfigured") {
|
|
64
|
+
console.log("发布制品验收未执行:候选尚未满足公开发布条件。");
|
|
65
|
+
if (required) {
|
|
66
|
+
console.error("ERROR publication-artifact-required");
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
console.log("npm publication artifact: blocked");
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
}
|
|
61
73
|
console.log(`Mode: ${result.readiness.mode}`);
|
|
62
74
|
for (const item of result.readiness.blockers) {
|
|
63
75
|
console.log(`BLOCKER ${item.code}`);
|
|
@@ -66,7 +78,6 @@ if (!validArguments) {
|
|
|
66
78
|
console.log(`Expected: ${oneLine(item.expected)}`);
|
|
67
79
|
console.log(`Next action: ${oneLine(item.nextAction)}`);
|
|
68
80
|
}
|
|
69
|
-
if (result.readiness.mode === "public-intent") process.exitCode = 1;
|
|
70
81
|
} else if (result.kind === "failed") {
|
|
71
82
|
console.error(`ERROR ${result.failure.code}`);
|
|
72
83
|
console.error(`Observed: ${oneLine(result.failure.observed)}`);
|
package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/release.yml
CHANGED
|
@@ -45,10 +45,13 @@ jobs:
|
|
|
45
45
|
run: |
|
|
46
46
|
mkdir -p "$RUNNER_TEMP/npm-publication-artifact"
|
|
47
47
|
test -z "$(find "$RUNNER_TEMP/npm-publication-artifact" -mindepth 1 -print -quit)"
|
|
48
|
-
- name: Run
|
|
49
|
-
run:
|
|
48
|
+
- name: Run Full Check once
|
|
49
|
+
run: node --conditions=source scripts/task-gates.ts check:full
|
|
50
|
+
- name: Verify publication artifact
|
|
51
|
+
run: pnpm run publication:artifact
|
|
50
52
|
env:
|
|
51
53
|
PUBLICATION_ARTIFACT_OUTPUT_DIRECTORY: ${{ runner.temp }}/npm-publication-artifact
|
|
54
|
+
PUBLICATION_ARTIFACT_REQUIRED: "true"
|
|
52
55
|
- name: Upload verified npm artifact
|
|
53
56
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
54
57
|
with:
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tasks": {
|
|
3
|
-
"//#publication:artifact": {
|
|
4
|
-
|
|
3
|
+
"//#publication:artifact": {
|
|
4
|
+
"cache": false,
|
|
5
|
+
"tags": ["quality:conditional"]
|
|
6
|
+
},
|
|
7
|
+
"//#publication:readiness": { "cache": false, "tags": ["work:release"] }
|
|
5
8
|
}
|
|
6
9
|
}
|
package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/cli-command-identity.ts
CHANGED
|
@@ -3,46 +3,6 @@ export type CliCommandIdentity = {
|
|
|
3
3
|
readonly version: string;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
-
const reservedSystemToolNames = new Set([
|
|
7
|
-
"aux",
|
|
8
|
-
"bash",
|
|
9
|
-
"bun",
|
|
10
|
-
"cmd",
|
|
11
|
-
"com1",
|
|
12
|
-
"com2",
|
|
13
|
-
"com3",
|
|
14
|
-
"com4",
|
|
15
|
-
"com5",
|
|
16
|
-
"com6",
|
|
17
|
-
"com7",
|
|
18
|
-
"com8",
|
|
19
|
-
"com9",
|
|
20
|
-
"con",
|
|
21
|
-
"corepack",
|
|
22
|
-
"deno",
|
|
23
|
-
"git",
|
|
24
|
-
"lpt1",
|
|
25
|
-
"lpt2",
|
|
26
|
-
"lpt3",
|
|
27
|
-
"lpt4",
|
|
28
|
-
"lpt5",
|
|
29
|
-
"lpt6",
|
|
30
|
-
"lpt7",
|
|
31
|
-
"lpt8",
|
|
32
|
-
"lpt9",
|
|
33
|
-
"node",
|
|
34
|
-
"nul",
|
|
35
|
-
"npm",
|
|
36
|
-
"npx",
|
|
37
|
-
"pnpm",
|
|
38
|
-
"powershell",
|
|
39
|
-
"prn",
|
|
40
|
-
"pwsh",
|
|
41
|
-
"sh",
|
|
42
|
-
"yarn",
|
|
43
|
-
"zsh",
|
|
44
|
-
]);
|
|
45
|
-
|
|
46
6
|
export function validateCliCommandName(
|
|
47
7
|
commandName: string,
|
|
48
8
|
occupiedCommandNames: readonly string[] = [],
|
|
@@ -72,11 +32,6 @@ export function validateCliCommandName(
|
|
|
72
32
|
`CLI command name must use lowercase ASCII letters, digits, and single hyphens; received ${JSON.stringify(commandName)}`,
|
|
73
33
|
);
|
|
74
34
|
}
|
|
75
|
-
if (reservedSystemToolNames.has(commandName)) {
|
|
76
|
-
throw new Error(
|
|
77
|
-
`CLI command name is a reserved system tool; received ${JSON.stringify(commandName)}`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
35
|
if (occupiedCommandNames.includes(commandName)) {
|
|
81
36
|
throw new Error(
|
|
82
37
|
`CLI command name is already used by another workspace package; received ${JSON.stringify(commandName)}`,
|
package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/e2e/run-journeys.test.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
|
|
4
|
+
import { runJourneys } from "./run-journeys.ts";
|
|
5
|
+
|
|
6
|
+
for (const mode of ["source", "distribution"] as const) {
|
|
7
|
+
void test(`runs all ${mode} CLI journeys`, { timeout: 180_000 }, async () => {
|
|
8
|
+
const passed = await runJourneys({ mode });
|
|
9
|
+
assert.notStrictEqual(passed.length, 0);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { describe, it } from "node:test";
|
|
4
4
|
|
|
5
5
|
import { cliCommandIdentity } from "../../src/cli-command-identity.ts";
|
|
6
6
|
import { runCli, type CliRuntime } from "../../src/main.ts";
|
|
@@ -23,62 +23,63 @@ function testRuntime(args: readonly string[]) {
|
|
|
23
23
|
return { runtime, stdout: () => stdout, stderr: () => stderr };
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
describe("CLI 契约", () => {
|
|
27
|
-
it("查询成功默认返回结构化数据,也支持中文文本", async () => {
|
|
26
|
+
void describe("CLI 契约", () => {
|
|
27
|
+
void it("查询成功默认返回结构化数据,也支持中文文本", async () => {
|
|
28
28
|
const output = testRuntime(["lookup", "ada"]);
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
assert.equal(await runCli(output.runtime), 0);
|
|
30
|
+
assert.deepStrictEqual(JSON.parse(output.stdout()), {
|
|
31
31
|
schemaVersion: "1",
|
|
32
32
|
command: "lookup",
|
|
33
33
|
kind: "data",
|
|
34
34
|
variant: "found",
|
|
35
35
|
data: { name: "ada", title: "Ada Lovelace" },
|
|
36
36
|
});
|
|
37
|
-
|
|
37
|
+
assert.equal(output.stderr(), "");
|
|
38
38
|
const human = testRuntime(["lookup", "ada", "--output-format", "text"]);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
assert.equal(await runCli(human.runtime), 0);
|
|
40
|
+
assert.ok(human.stdout().includes("找到记录"));
|
|
41
|
+
assert.ok(human.stdout().includes("Ada Lovelace"));
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
it("未找到记录是已声明失败,非法名称是用法错误", async () => {
|
|
44
|
+
void it("未找到记录是已声明失败,非法名称是用法错误", async () => {
|
|
45
45
|
const missing = testRuntime(["lookup", "missing"]);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
assert.equal(await runCli(missing.runtime), 1);
|
|
47
|
+
assert.equal(missing.stdout(), "");
|
|
48
|
+
assert.partialDeepStrictEqual(JSON.parse(missing.stderr()), {
|
|
49
49
|
kind: "failure",
|
|
50
50
|
variant: "notFound",
|
|
51
51
|
data: { name: "missing" },
|
|
52
52
|
});
|
|
53
53
|
for (const args of [["lookup"], ["lookup", " "]]) {
|
|
54
54
|
const invalid = testRuntime([...args, "--output-format", "text"]);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
assert.equal(await runCli(invalid.runtime), 2);
|
|
56
|
+
assert.equal(invalid.stdout(), "");
|
|
57
|
+
assert.partialDeepStrictEqual(JSON.parse(invalid.stderr()), {
|
|
58
58
|
kind: "usageFailure",
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it("帮助和版本使用 manifest 身份,schema 导出完整契约", async () => {
|
|
63
|
+
void it("帮助和版本使用 manifest 身份,schema 导出完整契约", async () => {
|
|
64
64
|
const version = testRuntime(["--version"]);
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
assert.equal(await runCli(version.runtime), 0);
|
|
66
|
+
assert.equal(
|
|
67
|
+
version.stdout(),
|
|
67
68
|
`${identity.commandName} ${identity.version}\n`,
|
|
68
69
|
);
|
|
69
70
|
const help = testRuntime(["--help"]);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
assert.equal(await runCli(help.runtime), 0);
|
|
72
|
+
assert.ok(help.stdout().includes(identity.commandName));
|
|
73
|
+
assert.ok(help.stdout().includes("lookup"));
|
|
74
|
+
assert.ok(help.stdout().includes("schema"));
|
|
74
75
|
const schema = testRuntime(["schema"]);
|
|
75
|
-
|
|
76
|
+
assert.equal(await runCli(schema.runtime), 0);
|
|
76
77
|
const humanSchema = testRuntime(["schema", "--output-format", "text"]);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
assert.equal(await runCli(humanSchema.runtime), 0);
|
|
79
|
+
assert.ok(humanSchema.stdout().includes("CLI 契约"));
|
|
80
|
+
assert.ok(humanSchema.stdout().includes('"commands"'));
|
|
80
81
|
const manifest = JSON.parse(schema.stdout()).data.manifest;
|
|
81
|
-
|
|
82
|
+
assert.partialDeepStrictEqual(manifest, {
|
|
82
83
|
commands: {
|
|
83
84
|
lookup: {
|
|
84
85
|
input: {
|
|
@@ -102,41 +103,44 @@ describe("CLI 契约", () => {
|
|
|
102
103
|
});
|
|
103
104
|
});
|
|
104
105
|
|
|
105
|
-
it("等待输出完成,仅 stdout EPIPE 安静成功", async () => {
|
|
106
|
+
void it("等待输出完成,仅 stdout EPIPE 安静成功", async () => {
|
|
106
107
|
const output = testRuntime(["lookup", "ada"]);
|
|
107
|
-
|
|
108
|
+
assert.equal(
|
|
108
109
|
await runCli({
|
|
109
110
|
...output.runtime,
|
|
110
111
|
write: async () => {
|
|
111
112
|
throw Object.assign(new Error("pipe"), { code: "EPIPE" });
|
|
112
113
|
},
|
|
113
114
|
}),
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
0,
|
|
116
|
+
);
|
|
117
|
+
assert.equal(
|
|
116
118
|
await runCli({
|
|
117
119
|
...output.runtime,
|
|
118
120
|
write: async () => {
|
|
119
121
|
throw new Error("disk");
|
|
120
122
|
},
|
|
121
123
|
}),
|
|
122
|
-
|
|
124
|
+
70,
|
|
125
|
+
);
|
|
123
126
|
const failure = testRuntime(["lookup", "missing"]);
|
|
124
|
-
|
|
127
|
+
assert.equal(
|
|
125
128
|
await runCli({
|
|
126
129
|
...failure.runtime,
|
|
127
130
|
write: async () => {
|
|
128
131
|
throw Object.assign(new Error("pipe"), { code: "EPIPE" });
|
|
129
132
|
},
|
|
130
133
|
}),
|
|
131
|
-
|
|
134
|
+
70,
|
|
135
|
+
);
|
|
132
136
|
const argv = ["node", identity.commandName];
|
|
133
137
|
Object.defineProperty(argv, "slice", {
|
|
134
138
|
value() {
|
|
135
139
|
throw new Error("内部细节");
|
|
136
140
|
},
|
|
137
141
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
assert.equal(await runCli({ ...output.runtime, argv }), 70);
|
|
143
|
+
assert.ok(output.stderr().includes("命令执行失败"));
|
|
144
|
+
assert.ok(!output.stderr().includes("内部细节"));
|
|
141
145
|
});
|
|
142
146
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
cliCommandIdentity,
|
|
5
6
|
validateCliCommandName,
|
|
6
7
|
} from "../../src/cli-command-identity.ts";
|
|
7
8
|
|
|
8
|
-
describe("CLI command identity", () => {
|
|
9
|
-
|
|
9
|
+
void describe("CLI command identity", () => {
|
|
10
|
+
for (const { name, command, message } of [
|
|
10
11
|
{ name: "path separator", command: "tools/release", message: "path" },
|
|
11
12
|
{ name: "backslash path", command: "tools\\release", message: "path" },
|
|
12
13
|
{ name: "whitespace", command: "release tool", message: "whitespace" },
|
|
@@ -14,63 +15,62 @@ describe("CLI command identity", () => {
|
|
|
14
15
|
{ name: "leading hyphen", command: "-release", message: "leading hyphen" },
|
|
15
16
|
{ name: "uppercase ASCII", command: "Release", message: "lowercase ASCII" },
|
|
16
17
|
{ name: "non-ASCII", command: "发布", message: "lowercase ASCII" },
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
name: "Windows printer device",
|
|
31
|
-
command: "lpt9",
|
|
32
|
-
message: "reserved system tool",
|
|
33
|
-
},
|
|
34
|
-
])("rejects an unsafe command name: $name", ({ command, message }) => {
|
|
35
|
-
expect(() => validateCliCommandName(command)).toThrow(message);
|
|
36
|
-
});
|
|
18
|
+
]) {
|
|
19
|
+
void it(`拒绝格式不合法的命令名:${name}`, () => {
|
|
20
|
+
assert.throws(
|
|
21
|
+
() => validateCliCommandName(command),
|
|
22
|
+
(error: unknown) => {
|
|
23
|
+
assert.ok(error instanceof Error);
|
|
24
|
+
assert.ok(error.message.includes(message));
|
|
25
|
+
return true;
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
37
30
|
|
|
38
|
-
it("
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
validateCliCommandName("release-tool", ["release-tool"]),
|
|
42
|
-
|
|
31
|
+
void it("接受符合格式的命令名,并拒绝工作区中已使用的名称", () => {
|
|
32
|
+
assert.equal(validateCliCommandName("release-tool", []), "release-tool");
|
|
33
|
+
assert.throws(
|
|
34
|
+
() => validateCliCommandName("release-tool", ["release-tool"]),
|
|
35
|
+
/already used by another workspace package/,
|
|
36
|
+
);
|
|
43
37
|
});
|
|
44
38
|
|
|
45
|
-
it("derives command and unpublished version only from manifest facts", () => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
version: "unpublished",
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
void it("derives command and unpublished version only from manifest facts", () => {
|
|
40
|
+
assert.deepStrictEqual(
|
|
41
|
+
cliCommandIdentity({ bin: { release: "./dist/cli.js" } }),
|
|
42
|
+
{ commandName: "release", version: "unpublished" },
|
|
43
|
+
);
|
|
44
|
+
assert.deepStrictEqual(
|
|
51
45
|
cliCommandIdentity({
|
|
52
46
|
bin: { ship: "./dist/cli.js" },
|
|
53
47
|
version: "2.3.4",
|
|
54
48
|
}),
|
|
55
|
-
|
|
49
|
+
{ commandName: "ship", version: "2.3.4" },
|
|
50
|
+
);
|
|
56
51
|
});
|
|
57
52
|
|
|
58
|
-
it("rejects malformed manifest identity facts", () => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
53
|
+
void it("rejects malformed manifest identity facts", () => {
|
|
54
|
+
assert.throws(() => cliCommandIdentity(null), /must be an object/);
|
|
55
|
+
assert.throws(() => cliCommandIdentity({ bin: {} }), /exactly one bin/);
|
|
56
|
+
assert.throws(
|
|
57
|
+
() =>
|
|
58
|
+
cliCommandIdentity({
|
|
59
|
+
bin: { release: "./dist/cli.js", ship: "./dist/cli.js" },
|
|
60
|
+
}),
|
|
61
|
+
/exactly one bin/,
|
|
62
|
+
);
|
|
63
|
+
assert.throws(
|
|
64
|
+
() => cliCommandIdentity({ bin: { release: "./src/cli.ts" } }),
|
|
65
|
+
/must execute ".\/dist\/cli\.js"/,
|
|
66
|
+
);
|
|
67
|
+
assert.throws(
|
|
68
|
+
() =>
|
|
69
|
+
cliCommandIdentity({
|
|
70
|
+
bin: { release: "./dist/cli.js" },
|
|
71
|
+
version: 1,
|
|
72
|
+
}),
|
|
73
|
+
/version must be a non-empty string/,
|
|
74
|
+
);
|
|
75
75
|
});
|
|
76
76
|
});
|