@dsh-workbench/module-tools 1.1.13 → 1.1.14-beta.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.
Files changed (64) hide show
  1. package/README.md +36 -23
  2. package/bin/dsh-wb-module.mjs +18 -4
  3. package/package.json +4 -4
  4. package/src/arch.mjs +36 -25
  5. package/src/check.mjs +50 -3
  6. package/src/checks/arch.mjs +2 -2
  7. package/src/checks/contracts.mjs +88 -41
  8. package/src/checks/kit.mjs +3 -2
  9. package/src/checks/lint.mjs +2 -2
  10. package/src/checks/primitives.mjs +2 -2
  11. package/src/discover.mjs +186 -16
  12. package/src/init.mjs +7 -8
  13. package/src/lint.mjs +4 -16
  14. package/src/market-index.mjs +1 -1
  15. package/src/migrate.mjs +69 -0
  16. package/src/release-journal.mjs +3 -1
  17. package/src/release-target.mjs +1 -1
  18. package/src/release-util.mjs +7 -1
  19. package/template-diy/AGENTS.md.tpl +14 -22
  20. package/template-diy/README.md.tpl +61 -65
  21. package/template-diy/design.json.tpl +19 -21
  22. package/template-diy/package.json.tpl +6 -2
  23. package/template-diy/scripts/smoke.assert.mjs.tpl +28 -0
  24. package/template-diy/scripts/smoke.mjs.tpl +150 -30
  25. package/template-diy/src/__tests__/plugin.test.ts.tpl +453 -0
  26. package/template-diy/src/components/NotesSection.tsx.tpl +74 -0
  27. package/template-diy/src/components/OverviewBody.module.css.tpl +5 -0
  28. package/template-diy/src/components/OverviewBody.tsx.tpl +64 -0
  29. package/template-diy/src/components/OverviewCard.module.css.tpl +9 -1
  30. package/template-diy/src/components/OverviewCard.tsx.tpl +108 -67
  31. package/template-diy/src/components/PrefsSection.tsx.tpl +36 -0
  32. package/template-diy/src/components/RunSection.tsx.tpl +69 -0
  33. package/template-diy/src/components/{OverviewCard.test.ts.tpl → __tests__/OverviewCard.test.ts.tpl} +51 -1
  34. package/template-diy/src/declared-tools.ts.tpl +6 -0
  35. package/template-diy/src/host/notes.ts.tpl +92 -0
  36. package/template-diy/src/host/overview.ts.tpl +57 -0
  37. package/template-diy/src/host/sample.ts.tpl +151 -0
  38. package/template-diy/src/host.ts.tpl +88 -66
  39. package/template-diy/src/messages.ts.tpl +28 -0
  40. package/template-diy/src/model/__tests__/flow.test.ts.tpl +36 -0
  41. package/template-diy/src/model/__tests__/notes.test.ts.tpl +49 -0
  42. package/template-diy/src/model/{overview.test.ts.tpl → __tests__/overview.test.ts.tpl} +1 -1
  43. package/template-diy/src/model/__tests__/prefs.test.ts.tpl +59 -0
  44. package/template-diy/src/model/flow.ts.tpl +47 -0
  45. package/template-diy/src/model/notes.ts.tpl +52 -0
  46. package/template-diy/src/model/overview.ts.tpl +3 -1
  47. package/template-diy/src/model/prefs.ts.tpl +61 -0
  48. package/template/AGENTS.md.tpl +0 -33
  49. package/template/CHANGELOG.md.tpl +0 -7
  50. package/template/README.md.tpl +0 -30
  51. package/template/arch.config.mjs +0 -2
  52. package/template/cordis.patch.yml.tpl +0 -3
  53. package/template/eslint.config.mjs +0 -2
  54. package/template/package.json.tpl +0 -50
  55. package/template/scripts/smoke.mjs.tpl +0 -192
  56. package/template/src/client.tsx.tpl +0 -22
  57. package/template/src/components/HelloCard.tsx.tpl +0 -28
  58. package/template/src/host.ts.tpl +0 -21
  59. package/template/src/messages.ts.tpl +0 -12
  60. package/template/src/plugin.test.ts.tpl +0 -56
  61. package/template/tsconfig.json +0 -20
  62. package/template/types/globals.d.ts +0 -14
  63. package/template/vitest.config.ts +0 -14
  64. package/template-diy/src/plugin.test.ts.tpl +0 -159
@@ -1,159 +0,0 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { afterAll, describe, expect, it, vi } from 'vitest';
5
-
6
- vi.mock('@dsh-workbench/core/client', () => ({
7
- kit: {
8
- hooks: { useApiResource: () => ({ phase: 'loading' }) },
9
- components: {
10
- Card: () => null,
11
- CenteredState: () => null,
12
- EmptyState: () => null,
13
- StateBlock: () => null,
14
- IconPanelScreen: () => null,
15
- Rows: () => null,
16
- Row: () => null,
17
- },
18
- },
19
- }));
20
-
21
- import { apply, inject, name, OVERVIEW_ROUTE, readOverview } from './host';
22
- import { apply as applyClient, inject as injectClient } from './client';
23
-
24
- interface HostFace {
25
- readonly unit: string;
26
- mount(): { readonly routes: Record<string, (search: URLSearchParams) => Promise<unknown>> };
27
- }
28
-
29
- interface ClientFace {
30
- readonly unit: string;
31
- readonly card: (props: {
32
- readonly world: { readonly current: { readonly workspace?: { readonly path: string } | null } };
33
- readonly refreshToken: number;
34
- readonly onRefresh: () => void;
35
- readonly t: (key: string) => string;
36
- }) => unknown;
37
- }
38
-
39
- interface OverviewAnswer {
40
- readonly ok: boolean;
41
- readonly path: string;
42
- readonly files: number;
43
- readonly dirs: number;
44
- readonly bytes: number;
45
- readonly largest: readonly { readonly name: string; readonly bytes: number; readonly mtime: string }[];
46
- readonly error: { readonly code: string; readonly message: string };
47
- }
48
-
49
- function hostFace(): HostFace {
50
- const faces: unknown[] = [];
51
- const ctx = {
52
- get: () => ({
53
- register: (face: unknown) => {
54
- faces.push(face);
55
- return () => {};
56
- },
57
- }),
58
- effect: (effect: () => void | (() => void)) => {
59
- effect();
60
- return () => {};
61
- },
62
- logger: { warn: () => {} },
63
- };
64
- apply(ctx as never);
65
- return faces[0] as HostFace;
66
- }
67
-
68
- function clientFace(): ClientFace {
69
- const faces: unknown[] = [];
70
- const ctx = {
71
- get: () => ({
72
- registerUnit: (face: unknown) => {
73
- faces.push(face);
74
- return () => {};
75
- },
76
- }),
77
- effect: (effect: () => void | (() => void)) => {
78
- effect();
79
- return () => {};
80
- },
81
- };
82
- applyClient(ctx as never);
83
- return faces[0] as ClientFace;
84
- }
85
-
86
- function overview(search: Record<string, string>): Promise<unknown> {
87
- return hostFace().mount().routes[OVERVIEW_ROUTE](new URLSearchParams(search));
88
- }
89
-
90
- const scratch = mkdtempSync(join(tmpdir(), 'dsh-wb-diy-'));
91
- afterAll(() => {
92
- rmSync(scratch, { recursive: true, force: true });
93
- });
94
-
95
- describe('__NAME__ 的面', () => {
96
- it('插件名就是包名', () => {
97
- expect(name).toBe('__NAME__');
98
- });
99
-
100
- it('两侧都只注入外壳交出来的那个服务', () => {
101
- expect(inject).toEqual(['workbenchUnits']);
102
- expect(injectClient).toEqual(['workbenchUnits']);
103
- });
104
-
105
- it('宿主半侧与浏览器半侧都把自己的面注册进外壳', () => {
106
- expect(hostFace().unit).toBe('__UNIT__');
107
- expect(clientFace().unit).toBe('__UNIT__');
108
- });
109
- });
110
-
111
- describe('宿主侧的只读路由', () => {
112
- it('路由住在自己那一格的命名空间里,且是个 async handler', () => {
113
- const routes = hostFace().mount().routes;
114
- expect(Object.keys(routes)).toEqual([OVERVIEW_ROUTE]);
115
- expect(OVERVIEW_ROUTE).toBe('__UNIT__/overview');
116
- expect(typeof routes[OVERVIEW_ROUTE]).toBe('function');
117
- });
118
-
119
- it('真读一个目录:文件数 / 目录数 / 总字节 / 最大的几个文件(名字 + 字节 + mtime)', async () => {
120
- const dir = join(scratch, 'sample');
121
- mkdirSync(join(dir, 'nested'), { recursive: true });
122
- writeFileSync(join(dir, 'small.txt'), 'x'.repeat(10));
123
- writeFileSync(join(dir, 'mid.log'), 'y'.repeat(50));
124
- writeFileSync(join(dir, 'big.bin'), 'z'.repeat(100));
125
-
126
- const answer = (await overview({ path: dir, limit: '2' })) as OverviewAnswer;
127
- expect(answer.ok).toBe(true);
128
- expect(answer.path).toBe(dir);
129
- expect(answer.files).toBe(3);
130
- expect(answer.dirs).toBe(1);
131
- expect(answer.bytes).toBe(160);
132
- expect(answer.largest.map((file) => file.name)).toEqual(['big.bin', 'mid.log']);
133
- expect(answer.largest[0]?.bytes).toBe(100);
134
- expect(answer.largest[0]?.mtime).toMatch(/^\d{4}-\d{2}-\d{2}T/);
135
- });
136
-
137
- it('limit 由查询参数管,越界会夹住', async () => {
138
- const dir = join(scratch, 'sample');
139
- const answer = (await overview({ path: dir, limit: '0' })) as OverviewAnswer;
140
- expect(answer.largest).toHaveLength(1);
141
- });
142
-
143
- it('path 缺省时回 ok:false 的 no-path,而不是偷偷读宿主进程的工作目录', async () => {
144
- const answer = (await overview({})) as OverviewAnswer;
145
- expect(answer.ok).toBe(false);
146
- expect(answer.error.code).toBe('no-path');
147
- });
148
-
149
- it('读不到时交回 ok:false 的 error,而不是抛出去', async () => {
150
- const answer = (await overview({ path: join(scratch, 'nope') })) as OverviewAnswer;
151
- expect(answer.ok).toBe(false);
152
- expect(answer.error.code).toBe('overview-unreadable');
153
- expect(answer.error.message.length).toBeGreaterThan(0);
154
- });
155
-
156
- it('readOverview 与路由返回的是同一份读数', () => {
157
- expect(readOverview(join(scratch, 'sample'), 5).bytes).toBe(160);
158
- });
159
- });