@dsh-workbench/module-tools 0.3.2 → 0.3.3

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.
@@ -3,35 +3,23 @@ import { tmpdir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  import { afterAll, describe, expect, it, vi } from 'vitest';
5
5
 
6
- const shell = vi.hoisted(() => ({
7
- resource: { phase: 'loading' } as {
8
- readonly phase: string;
9
- readonly value?: unknown;
10
- readonly error?: { readonly code: string; readonly message: string };
11
- },
12
- readEnabled: undefined as boolean | undefined,
13
- }));
14
-
15
6
  vi.mock('@dsh-workbench/core/client', () => ({
16
7
  kit: {
17
- hooks: {
18
- useApiResource: (_route: string, _query: unknown, _refreshToken: number, options?: { readonly enabled?: boolean }) => {
19
- shell.readEnabled = options?.enabled;
20
- return shell.resource;
21
- },
22
- },
8
+ hooks: { useApiResource: () => ({ phase: 'loading' }) },
23
9
  components: {
24
10
  Card: () => null,
25
11
  CenteredState: () => null,
26
12
  EmptyState: () => null,
27
13
  StateBlock: () => null,
28
14
  IconPanelScreen: () => null,
15
+ Rows: () => null,
16
+ Row: () => null,
29
17
  },
30
18
  },
31
19
  }));
32
20
 
33
21
  import { apply, inject, name, OVERVIEW_ROUTE, readOverview } from './host';
34
- import { apply as applyClient, formatBytes, inject as injectClient, overviewStateOf } from './client';
22
+ import { apply as applyClient, inject as injectClient } from './client';
35
23
 
36
24
  interface HostFace {
37
25
  readonly unit: string;
@@ -99,42 +87,11 @@ function overview(search: Record<string, string>): Promise<unknown> {
99
87
  return hostFace().mount().routes[OVERVIEW_ROUTE](new URLSearchParams(search));
100
88
  }
101
89
 
102
- function textOf(value: unknown, out: string[] = []): string[] {
103
- if (typeof value === 'string') {
104
- out.push(value);
105
- return out;
106
- }
107
- if (typeof value === 'number') {
108
- out.push(String(value));
109
- return out;
110
- }
111
- if (typeof value === 'boolean' || value === null || value === undefined) return out;
112
- if (Array.isArray(value)) {
113
- for (const one of value) textOf(one, out);
114
- return out;
115
- }
116
- if (typeof value === 'object') {
117
- const record = value as Record<string, unknown>;
118
- const source = 'type' in record && 'props' in record ? (record.props as Record<string, unknown>) : record;
119
- for (const one of Object.values(source)) {
120
- if (typeof one !== 'function') textOf(one, out);
121
- }
122
- }
123
- return out;
124
- }
125
-
126
90
  const scratch = mkdtempSync(join(tmpdir(), 'dsh-wb-diy-'));
127
91
  afterAll(() => {
128
92
  rmSync(scratch, { recursive: true, force: true });
129
93
  });
130
94
 
131
- const cardProps = {
132
- world: { current: { workspace: { path: '/tmp/__UNIT__-workspace' } } },
133
- refreshToken: 1,
134
- onRefresh: () => {},
135
- t: (key: string) => key,
136
- };
137
-
138
95
  describe('__NAME__ 的面', () => {
139
96
  it('插件名就是包名', () => {
140
97
  expect(name).toBe('__NAME__');
@@ -200,96 +157,3 @@ describe('宿主侧的只读路由', () => {
200
157
  expect(readOverview(join(scratch, 'sample'), 5).bytes).toBe(160);
201
158
  });
202
159
  });
203
-
204
- describe('卡片:待机 / 加载 / 就绪 / 就绪但空 / 出错', () => {
205
- it('没有选中工作区时不发请求,画待机那一屏', () => {
206
- const face = clientFace();
207
- shell.resource = { phase: 'idle' };
208
- const withoutProject = { ...cardProps, world: { current: { workspace: null } } };
209
-
210
- expect(textOf(face.card(withoutProject)).join(' ')).toContain('__UNIT__.needProject');
211
- expect(shell.readEnabled).toBe(false);
212
-
213
- shell.resource = { phase: 'loading' };
214
- expect(textOf(face.card(cardProps)).join(' ')).not.toContain('__UNIT__.needProject');
215
- expect(shell.readEnabled).toBe(true);
216
- });
217
-
218
- it('四态各画得出来', () => {
219
- const face = clientFace();
220
-
221
- shell.resource = { phase: 'loading' };
222
- expect(textOf(face.card(cardProps)).join(' ')).toContain('__UNIT__.loading');
223
-
224
- shell.resource = { phase: 'error', error: { code: 'internal', message: 'boom' } };
225
- expect(textOf(face.card(cardProps)).join(' ')).toContain('internal: boom');
226
-
227
- shell.resource = {
228
- phase: 'ready',
229
- value: {
230
- ok: true,
231
- path: '/real/workbench',
232
- files: 2,
233
- dirs: 1,
234
- bytes: 2048,
235
- largest: [{ name: 'big.bin', bytes: 2048, mtime: '2026-01-02T03:04:05.000Z' }],
236
- },
237
- };
238
- const ready = textOf(face.card(cardProps)).join(' ');
239
- expect(ready).toContain('/real/workbench');
240
- expect(ready).toContain('big.bin');
241
- expect(ready).toContain('2.0 KB');
242
- expect(ready).toContain('__UNIT__.fileUnit');
243
- expect(ready).toContain('__UNIT__.largest');
244
-
245
- shell.resource = {
246
- phase: 'ready',
247
- value: { ok: true, path: '/real/empty', files: 0, dirs: 0, bytes: 0, largest: [] },
248
- };
249
- expect(textOf(face.card(cardProps)).join(' ')).toContain('__UNIT__.empty');
250
- });
251
-
252
- it('刷新中旧数据还在(不退回加载态)', () => {
253
- const face = clientFace();
254
- shell.resource = {
255
- phase: 'loading',
256
- value: { ok: true, path: '/real/workbench', files: 1, dirs: 0, bytes: 10, largest: [] },
257
- };
258
- const text = textOf(face.card(cardProps)).join(' ');
259
- expect(text).toContain('/real/workbench');
260
- expect(text).not.toContain('__UNIT__.loading');
261
- });
262
-
263
- });
264
-
265
- describe('状态映射', () => {
266
- it('phase 与读数一起解出那一屏(待机 / 加载 / 就绪 / 就绪但空 / 出错)', () => {
267
- const at = (input: Parameters<typeof overviewStateOf>[0]): string => overviewStateOf(input).kind;
268
-
269
- expect(at({ phase: 'idle', value: undefined, error: undefined })).toBe('idle');
270
- expect(at({ phase: 'loading', value: undefined, error: undefined })).toBe('loading');
271
- expect(
272
- at({
273
- phase: 'ready',
274
- value: { ok: true, path: '/p', files: 2, dirs: 1, bytes: 10, largest: [] },
275
- error: undefined,
276
- }),
277
- ).toBe('ready');
278
- expect(
279
- at({
280
- phase: 'ready',
281
- value: { ok: true, path: '/p', files: 0, dirs: 0, bytes: 0, largest: [] },
282
- error: undefined,
283
- }),
284
- ).toBe('empty');
285
- expect(at({ phase: 'error', value: undefined, error: { code: 'internal', message: 'boom' } })).toBe('error');
286
- });
287
- });
288
-
289
- describe('格式化', () => {
290
- it('字节格式化不炸也不乱', () => {
291
- expect(formatBytes(0)).toBe('0 B');
292
- expect(formatBytes(2048)).toBe('2.0 KB');
293
- expect(formatBytes(3145728)).toBe('3.0 MB');
294
- });
295
- });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 组件样式(CSS Modules)的类名表。
3
+ *
4
+ * 构建时类名会被哈希,`import styles from './X.module.css'` 拿到的是**表**而不是文本
5
+ * (构建让它进产物里的那份内联样式);少了这条声明,`styles.xxx` 会被当成 string 的属性,
6
+ * `pnpm typecheck` 直接红。
7
+ *
8
+ * 单独一个文件:`types/globals.d.ts` 里有 import,是模块,里面的 `declare module`
9
+ * 会变成模块增强而不是环境声明 —— 与外壳 `packages/workbench/types/assets.d.ts` 同一个理由。
10
+ */
11
+ declare module '*.module.css' {
12
+ const classes: Record<string, string>;
13
+ export default classes;
14
+ }