@webstir-io/webstir 0.1.1 → 0.1.2
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 +13 -0
- package/assets/deployment/docker/.dockerignore +7 -0
- package/assets/deployment/docker/Dockerfile +17 -0
- package/assets/deployment/docker/README.md +44 -0
- package/assets/deployment/docker/example.env +3 -0
- package/assets/features/client_nav/client_nav.ts +369 -264
- package/assets/features/client_nav/document_navigation.ts +344 -0
- package/assets/features/client_nav/form_enhancement.ts +275 -0
- package/assets/templates/api/src/backend/index.ts +71 -10
- package/assets/templates/api/src/backend/tsconfig.json +6 -1
- package/assets/templates/full/src/backend/index.ts +71 -10
- package/assets/templates/full/src/backend/module.ts +515 -0
- package/assets/templates/full/src/backend/tests/progressive-enhancement.test.ts +180 -0
- package/assets/templates/full/src/backend/tsconfig.json +6 -1
- package/assets/templates/full/src/frontend/app/scripts/features/client-nav.ts +574 -0
- package/assets/templates/full/src/frontend/app/scripts/features/document-navigation.ts +344 -0
- package/assets/templates/full/src/frontend/app/scripts/features/form-enhancement.ts +275 -0
- package/assets/templates/full/src/frontend/pages/home/index.css +8 -0
- package/assets/templates/full/src/frontend/pages/home/index.html +6 -1
- package/assets/templates/full/src/frontend/pages/home/tests/home.test.ts +12 -2
- package/assets/templates/spa/src/frontend/pages/home/tests/home.test.ts +10 -2
- package/package.json +31 -13
- package/scripts/check-feature-projections.mjs +87 -0
- package/scripts/check-full-demo-sync.mjs +89 -0
- package/scripts/check-package-install.mjs +537 -0
- package/scripts/check-standalone-install.mjs +221 -0
- package/scripts/pack-standalone.mjs +52 -28
- package/scripts/publish.sh +9 -0
- package/scripts/run-tests.mjs +99 -0
- package/scripts/sync-assets.mjs +175 -17
- package/src/add-backend-compat.ts +628 -0
- package/src/add-backend.ts +155 -27
- package/src/add.ts +111 -4
- package/src/agent.ts +393 -0
- package/src/api-watch.ts +7 -4
- package/src/backend-inspect.ts +70 -2
- package/src/backend-runtime.ts +22 -14
- package/src/build.ts +1 -3
- package/src/bun-generated-frontend-watch.ts +209 -0
- package/src/bun-globals.d.ts +23 -0
- package/src/bun-spa-document.ts +310 -0
- package/src/bun-spa-routes.ts +159 -0
- package/src/bun-spa-watch.ts +29 -0
- package/src/bun-ssg-watch.ts +304 -0
- package/src/cli.ts +381 -50
- package/src/compile-tests.ts +37 -29
- package/src/dev-server.ts +214 -143
- package/src/doctor.ts +164 -0
- package/src/enable-assets.ts +18 -1
- package/src/enable.ts +133 -41
- package/src/execute.ts +28 -4
- package/src/external-workspace.ts +178 -0
- package/src/format.ts +296 -17
- package/src/frontend-inspect.ts +32 -0
- package/src/frontend-watch.ts +27 -102
- package/src/full-watch.ts +13 -18
- package/src/index.ts +7 -0
- package/src/init-assets.ts +41 -11
- package/src/init.ts +85 -71
- package/src/inspect.ts +112 -0
- package/src/mcp/run-cli-json.ts +46 -0
- package/src/mcp/server.ts +307 -0
- package/src/operations.ts +176 -0
- package/src/providers.ts +20 -18
- package/src/refresh.ts +29 -3
- package/src/repair.ts +110 -43
- package/src/runtime-filter.ts +41 -0
- package/src/runtime.ts +1 -1
- package/src/smoke.ts +48 -16
- package/src/test.ts +54 -16
- package/src/testing-runtime.ts +273 -0
- package/src/types.ts +1 -4
- package/src/watch-events.ts +46 -17
- package/src/watch.ts +5 -1
- package/src/workspace-watcher.ts +10 -6
- package/src/workspace.ts +4 -2
- package/src/watch-daemon-client.ts +0 -171
package/src/add-backend.ts
CHANGED
|
@@ -1,20 +1,91 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AddJobOptions,
|
|
3
|
+
AddRouteOptions,
|
|
4
|
+
UpdateRouteContractOptions as BackendUpdateRouteContractOptions,
|
|
5
|
+
} from '@webstir-io/webstir-backend';
|
|
6
|
+
|
|
1
7
|
import type { AddCommandResult } from './add.ts';
|
|
2
8
|
|
|
3
|
-
import {
|
|
9
|
+
import { monorepoRoot } from './paths.ts';
|
|
4
10
|
|
|
5
11
|
export interface RunAddBackendOptions {
|
|
6
12
|
readonly workspaceRoot: string;
|
|
7
13
|
readonly rawArgs: readonly string[];
|
|
8
14
|
}
|
|
9
15
|
|
|
16
|
+
export type AddRouteScaffoldOptions = AddRouteOptions;
|
|
17
|
+
export type AddJobScaffoldOptions = AddJobOptions;
|
|
18
|
+
export type UpdateRouteContractOptions = BackendUpdateRouteContractOptions;
|
|
19
|
+
|
|
10
20
|
export async function runAddRouteCommand(options: RunAddBackendOptions): Promise<AddCommandResult> {
|
|
11
|
-
|
|
21
|
+
return await runAddRouteScaffold({
|
|
22
|
+
workspaceRoot: options.workspaceRoot,
|
|
23
|
+
...parseAddRouteScaffoldArgs(options.rawArgs),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function runAddRouteScaffold(
|
|
28
|
+
options: AddRouteScaffoldOptions,
|
|
29
|
+
): Promise<AddCommandResult> {
|
|
30
|
+
const backendAdd = await loadBackendAddModule();
|
|
31
|
+
const result = await backendAdd.runAddRoute(options);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
workspaceRoot: options.workspaceRoot,
|
|
35
|
+
subject: 'route',
|
|
36
|
+
target: result.target,
|
|
37
|
+
changes: result.changes,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function runAddJobCommand(options: RunAddBackendOptions): Promise<AddCommandResult> {
|
|
42
|
+
return await runAddJobScaffold({
|
|
43
|
+
workspaceRoot: options.workspaceRoot,
|
|
44
|
+
...parseAddJobScaffoldArgs(options.rawArgs),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function runAddJobScaffold(options: AddJobScaffoldOptions): Promise<AddCommandResult> {
|
|
49
|
+
const backendAdd = await loadBackendAddModule();
|
|
50
|
+
const result = await backendAdd.runAddJob(options);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
workspaceRoot: options.workspaceRoot,
|
|
54
|
+
subject: 'job',
|
|
55
|
+
target: result.target,
|
|
56
|
+
changes: result.changes,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function runUpdateRouteContract(
|
|
61
|
+
options: UpdateRouteContractOptions,
|
|
62
|
+
): Promise<AddCommandResult> {
|
|
63
|
+
const backendAdd = await loadBackendAddModule();
|
|
64
|
+
const result = await backendAdd.runUpdateRouteContract(options);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
workspaceRoot: options.workspaceRoot,
|
|
68
|
+
subject: 'route',
|
|
69
|
+
target: result.target,
|
|
70
|
+
changes: result.changes,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function parseAddRouteScaffoldArgs(
|
|
75
|
+
rawArgs: readonly string[],
|
|
76
|
+
): Omit<AddRouteScaffoldOptions, 'workspaceRoot'> {
|
|
77
|
+
const parsed = parseBackendCommandArgs(rawArgs, {
|
|
12
78
|
valueFlags: new Set([
|
|
13
79
|
'--method',
|
|
14
80
|
'--path',
|
|
15
81
|
'--summary',
|
|
16
82
|
'--description',
|
|
17
83
|
'--tags',
|
|
84
|
+
'--interaction',
|
|
85
|
+
'--session',
|
|
86
|
+
'--fragment-target',
|
|
87
|
+
'--fragment-selector',
|
|
88
|
+
'--fragment-mode',
|
|
18
89
|
'--params-schema',
|
|
19
90
|
'--query-schema',
|
|
20
91
|
'--body-schema',
|
|
@@ -23,30 +94,37 @@ export async function runAddRouteCommand(options: RunAddBackendOptions): Promise
|
|
|
23
94
|
'--response-status',
|
|
24
95
|
'--response-headers-schema',
|
|
25
96
|
]),
|
|
26
|
-
booleanFlags: new Set(['--
|
|
97
|
+
booleanFlags: new Set(['--session-write', '--form-urlencoded', '--csrf']),
|
|
27
98
|
});
|
|
28
99
|
|
|
29
100
|
const name = parsed.positionals[0];
|
|
30
101
|
if (!name) {
|
|
31
102
|
throw new Error(
|
|
32
|
-
'Usage: webstir add-route <name> --workspace <path> [--method <METHOD>] [--path <path>]
|
|
103
|
+
'Usage: webstir add-route <name> --workspace <path> [--method <METHOD>] [--path <path>].',
|
|
33
104
|
);
|
|
34
105
|
}
|
|
35
106
|
|
|
36
|
-
const tags = parsed.values
|
|
107
|
+
const tags = parsed.values
|
|
108
|
+
.get('--tags')
|
|
37
109
|
?.split(',')
|
|
38
110
|
.map((tag) => tag.trim())
|
|
39
111
|
.filter(Boolean);
|
|
40
112
|
|
|
41
|
-
|
|
42
|
-
workspaceRoot: options.workspaceRoot,
|
|
113
|
+
return {
|
|
43
114
|
name,
|
|
44
115
|
method: parsed.values.get('--method'),
|
|
45
116
|
path: parsed.values.get('--path'),
|
|
46
|
-
fastify: parsed.booleans.has('--fastify'),
|
|
47
117
|
summary: parsed.values.get('--summary'),
|
|
48
118
|
description: parsed.values.get('--description'),
|
|
49
119
|
tags,
|
|
120
|
+
interaction: parsed.values.get('--interaction'),
|
|
121
|
+
sessionMode: parsed.values.get('--session'),
|
|
122
|
+
sessionWrite: parsed.booleans.has('--session-write'),
|
|
123
|
+
formUrlEncoded: parsed.booleans.has('--form-urlencoded'),
|
|
124
|
+
formCsrf: parsed.booleans.has('--csrf'),
|
|
125
|
+
fragmentTarget: parsed.values.get('--fragment-target'),
|
|
126
|
+
fragmentSelector: parsed.values.get('--fragment-selector'),
|
|
127
|
+
fragmentMode: parsed.values.get('--fragment-mode'),
|
|
50
128
|
paramsSchema: parsed.values.get('--params-schema'),
|
|
51
129
|
querySchema: parsed.values.get('--query-schema'),
|
|
52
130
|
bodySchema: parsed.values.get('--body-schema'),
|
|
@@ -54,18 +132,13 @@ export async function runAddRouteCommand(options: RunAddBackendOptions): Promise
|
|
|
54
132
|
responseSchema: parsed.values.get('--response-schema'),
|
|
55
133
|
responseStatus: parsed.values.get('--response-status'),
|
|
56
134
|
responseHeadersSchema: parsed.values.get('--response-headers-schema'),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
workspaceRoot: options.workspaceRoot,
|
|
61
|
-
subject: 'route',
|
|
62
|
-
target: result.target,
|
|
63
|
-
changes: result.changes,
|
|
64
135
|
};
|
|
65
136
|
}
|
|
66
137
|
|
|
67
|
-
export
|
|
68
|
-
|
|
138
|
+
export function parseAddJobScaffoldArgs(
|
|
139
|
+
rawArgs: readonly string[],
|
|
140
|
+
): Omit<AddJobScaffoldOptions, 'workspaceRoot'> {
|
|
141
|
+
const parsed = parseBackendCommandArgs(rawArgs, {
|
|
69
142
|
valueFlags: new Set(['--schedule', '--description', '--priority']),
|
|
70
143
|
booleanFlags: new Set(),
|
|
71
144
|
});
|
|
@@ -75,19 +148,11 @@ export async function runAddJobCommand(options: RunAddBackendOptions): Promise<A
|
|
|
75
148
|
throw new Error('Usage: webstir add-job <name> --workspace <path> [--schedule <expression>].');
|
|
76
149
|
}
|
|
77
150
|
|
|
78
|
-
|
|
79
|
-
workspaceRoot: options.workspaceRoot,
|
|
151
|
+
return {
|
|
80
152
|
name,
|
|
81
153
|
schedule: parsed.values.get('--schedule'),
|
|
82
154
|
description: parsed.values.get('--description'),
|
|
83
155
|
priority: parsed.values.get('--priority'),
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
workspaceRoot: options.workspaceRoot,
|
|
88
|
-
subject: 'job',
|
|
89
|
-
target: result.target,
|
|
90
|
-
changes: result.changes,
|
|
91
156
|
};
|
|
92
157
|
}
|
|
93
158
|
|
|
@@ -102,7 +167,70 @@ interface ParsedBackendCommandArgs {
|
|
|
102
167
|
readonly booleans: ReadonlySet<string>;
|
|
103
168
|
}
|
|
104
169
|
|
|
105
|
-
|
|
170
|
+
interface BackendAddResult {
|
|
171
|
+
readonly target: string;
|
|
172
|
+
readonly changes: readonly string[];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface BackendAddModule {
|
|
176
|
+
readonly runAddRoute: (options: AddRouteOptions) => Promise<BackendAddResult>;
|
|
177
|
+
readonly runAddJob: (options: AddJobOptions) => Promise<BackendAddResult>;
|
|
178
|
+
readonly runUpdateRouteContract: (
|
|
179
|
+
options: UpdateRouteContractOptions,
|
|
180
|
+
) => Promise<BackendAddResult>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let backendAddModulePromise: Promise<BackendAddModule> | null = null;
|
|
184
|
+
|
|
185
|
+
async function loadBackendAddModule(): Promise<BackendAddModule> {
|
|
186
|
+
if (backendAddModulePromise) {
|
|
187
|
+
return await backendAddModulePromise;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
backendAddModulePromise = import('@webstir-io/webstir-backend').then(async (module) => {
|
|
191
|
+
if (
|
|
192
|
+
typeof module.runAddRoute === 'function' &&
|
|
193
|
+
typeof module.runAddJob === 'function' &&
|
|
194
|
+
typeof module.runUpdateRouteContract === 'function'
|
|
195
|
+
) {
|
|
196
|
+
return module as BackendAddModule;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (monorepoRoot) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
'Installed @webstir-io/webstir-backend package does not export runAddRoute/runAddJob/runUpdateRouteContract.',
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const compat = await import('./add-backend-compat.ts');
|
|
206
|
+
return {
|
|
207
|
+
async runAddRoute(options: AddRouteOptions): Promise<BackendAddResult> {
|
|
208
|
+
return await compat.runAddRoute(options);
|
|
209
|
+
},
|
|
210
|
+
async runAddJob(options: AddJobOptions): Promise<BackendAddResult> {
|
|
211
|
+
return await compat.runAddJob({
|
|
212
|
+
workspaceRoot: options.workspaceRoot,
|
|
213
|
+
name: options.name,
|
|
214
|
+
schedule: options.schedule,
|
|
215
|
+
description: options.description,
|
|
216
|
+
...(options.priority !== undefined ? { priority: String(options.priority) } : {}),
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
async runUpdateRouteContract(): Promise<BackendAddResult> {
|
|
220
|
+
throw new Error(
|
|
221
|
+
'Installed @webstir-io/webstir-backend package does not export runUpdateRouteContract.',
|
|
222
|
+
);
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return await backendAddModulePromise;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function parseBackendCommandArgs(
|
|
231
|
+
rawArgs: readonly string[],
|
|
232
|
+
spec: ParseSpec,
|
|
233
|
+
): ParsedBackendCommandArgs {
|
|
106
234
|
const positionals: string[] = [];
|
|
107
235
|
const values = new Map<string, string>();
|
|
108
236
|
const booleans = new Set<string>();
|
package/src/add.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
2
3
|
import { readFile } from 'node:fs/promises';
|
|
3
4
|
import { existsSync } from 'node:fs';
|
|
4
5
|
|
|
5
6
|
import { runAddPage } from '@webstir-io/webstir-frontend';
|
|
6
|
-
import {
|
|
7
|
+
import { monorepoRoot, packageRoot } from './paths.ts';
|
|
7
8
|
|
|
8
9
|
export interface RunAddPageOptions {
|
|
9
10
|
readonly workspaceRoot: string;
|
|
@@ -23,6 +24,12 @@ export interface AddCommandResult {
|
|
|
23
24
|
readonly note?: string;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
interface AddTestInvocationResult {
|
|
28
|
+
readonly normalizedName: string;
|
|
29
|
+
readonly created: boolean;
|
|
30
|
+
readonly relativePath: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
export async function runAddPageCommand(options: RunAddPageOptions): Promise<AddCommandResult> {
|
|
27
34
|
const pageName = options.args[0];
|
|
28
35
|
if (!pageName) {
|
|
@@ -60,6 +67,7 @@ export async function runAddTestCommand(options: RunAddTestOptions): Promise<Add
|
|
|
60
67
|
throw new Error('Usage: webstir add-test <name-or-path> --workspace <path>.');
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
const runAddTest = await loadAddTestRunner();
|
|
63
71
|
const result = await runAddTest({
|
|
64
72
|
workspaceRoot: options.workspaceRoot,
|
|
65
73
|
name: nameArg,
|
|
@@ -70,14 +78,111 @@ export async function runAddTestCommand(options: RunAddTestOptions): Promise<Add
|
|
|
70
78
|
subject: 'test',
|
|
71
79
|
target: result.normalizedName,
|
|
72
80
|
changes: result.created ? [result.relativePath.replaceAll(path.sep, '/')] : [],
|
|
73
|
-
note: result.created
|
|
81
|
+
note: result.created
|
|
82
|
+
? undefined
|
|
83
|
+
: `File already exists: ${result.relativePath.replaceAll(path.sep, '/')}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function loadAddTestRunner(): Promise<
|
|
88
|
+
(options: {
|
|
89
|
+
readonly workspaceRoot: string;
|
|
90
|
+
readonly name: string;
|
|
91
|
+
}) => Promise<AddTestInvocationResult>
|
|
92
|
+
> {
|
|
93
|
+
const mod = (await import('@webstir-io/webstir-testing')) as {
|
|
94
|
+
runAddTest?: (options: {
|
|
95
|
+
readonly workspaceRoot: string;
|
|
96
|
+
readonly name: string;
|
|
97
|
+
}) => Promise<AddTestInvocationResult>;
|
|
98
|
+
};
|
|
99
|
+
if (typeof mod.runAddTest === 'function') {
|
|
100
|
+
return mod.runAddTest;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (monorepoRoot) {
|
|
104
|
+
throw new Error('Installed @webstir-io/webstir-testing package does not export runAddTest.');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// The published regular-install path exposes the add-test binary before it exposes the helper.
|
|
108
|
+
return async (options) => await runPublishedAddTestCli(options.workspaceRoot, options.name);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function runPublishedAddTestCli(
|
|
112
|
+
workspaceRoot: string,
|
|
113
|
+
name: string,
|
|
114
|
+
): Promise<AddTestInvocationResult> {
|
|
115
|
+
const target = resolveAddTestTarget(workspaceRoot, name);
|
|
116
|
+
const existedBefore = existsSync(target.absolutePath);
|
|
117
|
+
const binaryPath = path.join(
|
|
118
|
+
packageRoot,
|
|
119
|
+
'..',
|
|
120
|
+
'..',
|
|
121
|
+
'.bin',
|
|
122
|
+
process.platform === 'win32' ? 'webstir-testing-add.cmd' : 'webstir-testing-add',
|
|
123
|
+
);
|
|
124
|
+
const result = spawnSync(process.execPath, [binaryPath, name, '--workspace', workspaceRoot], {
|
|
125
|
+
cwd: workspaceRoot,
|
|
126
|
+
env: process.env,
|
|
127
|
+
encoding: 'utf8',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (result.error) {
|
|
131
|
+
throw result.error;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (result.status !== 0) {
|
|
135
|
+
const detail = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join('\n');
|
|
136
|
+
throw new Error(
|
|
137
|
+
`Command failed (${result.status}): ${process.execPath} ${binaryPath} ${name} --workspace ${workspaceRoot}${detail ? `\n${detail}` : ''}`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!existsSync(target.absolutePath)) {
|
|
142
|
+
throw new Error(`Expected add-test output at ${target.relativePath}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
normalizedName: target.normalizedName,
|
|
147
|
+
created: !existedBefore,
|
|
148
|
+
relativePath: target.relativePath,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function resolveAddTestTarget(
|
|
153
|
+
workspaceRoot: string,
|
|
154
|
+
rawName: string,
|
|
155
|
+
): {
|
|
156
|
+
readonly normalizedName: string;
|
|
157
|
+
readonly relativePath: string;
|
|
158
|
+
readonly absolutePath: string;
|
|
159
|
+
} {
|
|
160
|
+
const normalizedName = rawName
|
|
161
|
+
.trim()
|
|
162
|
+
.replace(/\\/g, '/')
|
|
163
|
+
.replace(/(\.test\.ts)$/i, '');
|
|
164
|
+
const hasSlash = normalizedName.includes('/');
|
|
165
|
+
|
|
166
|
+
const relativePath = hasSlash
|
|
167
|
+
? path.join(
|
|
168
|
+
'src',
|
|
169
|
+
path.posix.dirname(normalizedName),
|
|
170
|
+
'tests',
|
|
171
|
+
`${path.posix.basename(normalizedName)}.test.ts`,
|
|
172
|
+
)
|
|
173
|
+
: path.join('src', 'tests', `${normalizedName}.test.ts`);
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
normalizedName,
|
|
177
|
+
relativePath,
|
|
178
|
+
absolutePath: path.join(workspaceRoot, relativePath),
|
|
74
179
|
};
|
|
75
180
|
}
|
|
76
181
|
|
|
77
182
|
async function collectChangedFiles(
|
|
78
183
|
workspaceRoot: string,
|
|
79
184
|
absolutePaths: readonly string[],
|
|
80
|
-
before: ReadonlyMap<string, string | null
|
|
185
|
+
before: ReadonlyMap<string, string | null>,
|
|
81
186
|
): Promise<string[]> {
|
|
82
187
|
const changes: string[] = [];
|
|
83
188
|
for (const absolutePath of absolutePaths) {
|
|
@@ -90,7 +195,9 @@ async function collectChangedFiles(
|
|
|
90
195
|
return changes;
|
|
91
196
|
}
|
|
92
197
|
|
|
93
|
-
async function captureFileState(
|
|
198
|
+
async function captureFileState(
|
|
199
|
+
absolutePaths: readonly string[],
|
|
200
|
+
): Promise<Map<string, string | null>> {
|
|
94
201
|
const state = new Map<string, string | null>();
|
|
95
202
|
for (const absolutePath of absolutePaths) {
|
|
96
203
|
state.set(absolutePath, await readFileIfExists(absolutePath));
|