@elench/testkit 0.1.127 → 0.1.128

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.
@@ -0,0 +1,80 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ const DEFAULT_ASSET_EXTENSIONS = new Set([
5
+ ".csv",
6
+ ".css",
7
+ ".gql",
8
+ ".graphql",
9
+ ".html",
10
+ ".json",
11
+ ".md",
12
+ ".sql",
13
+ ".txt",
14
+ ".wasm",
15
+ ".yaml",
16
+ ".yml",
17
+ ]);
18
+
19
+ export async function copyTscRuntimeAssets(context) {
20
+ const sourceDir = normalizeRelativePath(context.args?.sourceDir || "src", "sourceDir");
21
+ const outDir = normalizeRelativePath(context.args?.outDir || "dist", "outDir");
22
+ const extensions = normalizeExtensions(context.args?.extensions);
23
+ const sourceRoot = path.resolve(context.cwd, sourceDir);
24
+ const outputRoot = path.resolve(context.prepareDir, outDir);
25
+
26
+ if (!context.prepareDir) {
27
+ throw new Error("copyTscRuntimeAssets requires a runtime prepare directory");
28
+ }
29
+ if (!fs.existsSync(sourceRoot)) return;
30
+
31
+ for (const filePath of walkFiles(sourceRoot)) {
32
+ const relative = path.relative(sourceRoot, filePath);
33
+ if (shouldSkipAsset(relative, extensions)) continue;
34
+ const destination = path.join(outputRoot, relative);
35
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
36
+ fs.copyFileSync(filePath, destination);
37
+ }
38
+ }
39
+
40
+ function normalizeRelativePath(value, label) {
41
+ const normalized = String(value || "").trim();
42
+ if (!normalized) throw new Error(`${label} must be a non-empty relative path`);
43
+ if (path.isAbsolute(normalized) || normalized.split(path.sep).includes("..")) {
44
+ throw new Error(`${label} must be a relative path inside the service cwd`);
45
+ }
46
+ return normalized;
47
+ }
48
+
49
+ function normalizeExtensions(value) {
50
+ if (value == null) return DEFAULT_ASSET_EXTENSIONS;
51
+ if (!Array.isArray(value)) throw new Error("extensions must be an array");
52
+ return new Set(
53
+ value.map((entry, index) => {
54
+ const normalized = String(entry || "").trim().toLowerCase();
55
+ if (!/^\.[a-z0-9]+$/i.test(normalized)) {
56
+ throw new Error(`extensions[${index}] must look like ".json"`);
57
+ }
58
+ return normalized;
59
+ })
60
+ );
61
+ }
62
+
63
+ function shouldSkipAsset(relativePath, extensions) {
64
+ const normalized = relativePath.split(path.sep).join("/");
65
+ if (normalized.includes("/__testkit__/") || normalized.startsWith("__testkit__/")) return true;
66
+ if (/(^|\/)[^/]+\.test\.[^/]+$/i.test(normalized)) return true;
67
+ if (/(^|\/)[^/]+\.spec\.[^/]+$/i.test(normalized)) return true;
68
+ return !extensions.has(path.extname(normalized).toLowerCase());
69
+ }
70
+
71
+ function* walkFiles(root) {
72
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
73
+ const entryPath = path.join(root, entry.name);
74
+ if (entry.isDirectory()) {
75
+ yield* walkFiles(entryPath);
76
+ continue;
77
+ }
78
+ if (entry.isFile()) yield entryPath;
79
+ }
80
+ }
@@ -29,6 +29,12 @@ const CONFIG_DATABASE_STEPS_ENTRY = path.join(
29
29
  "config-api",
30
30
  "database-steps.mjs"
31
31
  );
32
+ const CONFIG_BUILD_ASSETS_ENTRY = path.join(
33
+ PACKAGE_ROOT,
34
+ "lib",
35
+ "config-api",
36
+ "build-assets.mjs"
37
+ );
32
38
  const CONFIG_NEXT_TSCONFIG_ENTRY = path.join(
33
39
  PACKAGE_ROOT,
34
40
  "lib",
@@ -274,6 +280,7 @@ function resolvePackageSubpath(specifier) {
274
280
  const subpath = specifier.slice("@elench/testkit".length);
275
281
  if (!subpath) return ROOT_ENTRY;
276
282
  if (subpath === "/config") return CONFIG_ENTRY;
283
+ if (subpath === "/config/build-assets") return CONFIG_BUILD_ASSETS_ENTRY;
277
284
  if (subpath === "/config/database-steps") return CONFIG_DATABASE_STEPS_ENTRY;
278
285
  if (subpath === "/config/next-runtime-tsconfig") return CONFIG_NEXT_TSCONFIG_ENTRY;
279
286
  if (subpath === "/drizzle") return DRIZZLE_ENTRY;
@@ -146,6 +146,7 @@ export function buildConfigToPrepare(build) {
146
146
 
147
147
  const inputs = build.inputs.length > 0 ? [...build.inputs] : defaultTscInputs(build);
148
148
  const cmd = `./node_modules/.bin/tsc -p ${build.tsconfig} --outDir "{prepareDir}/${build.outDir}"`;
149
+ const sourceDir = sourceAssetRootFromEntry(build.entry);
149
150
  return {
150
151
  inputs,
151
152
  steps: [
@@ -155,6 +156,16 @@ export function buildConfigToPrepare(build) {
155
156
  cwd: build.cwd || undefined,
156
157
  inputs: [],
157
158
  },
159
+ {
160
+ kind: "module",
161
+ specifier: "@elench/testkit/config/build-assets#copyTscRuntimeAssets",
162
+ cwd: build.cwd || undefined,
163
+ inputs: [],
164
+ args: {
165
+ sourceDir,
166
+ outDir: build.outDir,
167
+ },
168
+ },
158
169
  ],
159
170
  };
160
171
  }
@@ -173,6 +184,14 @@ function sourceEntryToOutputPath(entry) {
173
184
  return withoutExtension;
174
185
  }
175
186
 
187
+ function sourceAssetRootFromEntry(entry) {
188
+ const normalized = String(entry).split(path.sep).join("/");
189
+ if (normalized.startsWith("src/")) return "src";
190
+ const parts = normalized.split("/").filter(Boolean);
191
+ if (parts.length > 1) return parts[0];
192
+ return ".";
193
+ }
194
+
176
195
  function defaultTscInputs(build) {
177
196
  const inputs = new Set([
178
197
  prefixConfiguredPath(build.cwd, build.tsconfig),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/next-analysis",
3
- "version": "0.1.127",
3
+ "version": "0.1.128",
4
4
  "description": "SWC-backed Next.js source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-bridge",
3
- "version": "0.1.127",
3
+ "version": "0.1.128",
4
4
  "description": "Browser bridge helpers for testkit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "typecheck": "tsc -p tsconfig.json --noEmit"
23
23
  },
24
24
  "dependencies": {
25
- "@elench/testkit-protocol": "0.1.127"
25
+ "@elench/testkit-protocol": "0.1.128"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.127",
3
+ "version": "0.1.128",
4
4
  "description": "Shared browser protocol for testkit bridge and extension consumers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/ts-analysis",
3
- "version": "0.1.127",
3
+ "version": "0.1.128",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.127",
3
+ "version": "0.1.128",
4
4
  "description": "Assistant-first CLI for running, inspecting, and debugging local testkit suites",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -95,10 +95,10 @@
95
95
  },
96
96
  "dependencies": {
97
97
  "@babel/code-frame": "^7.29.0",
98
- "@elench/next-analysis": "0.1.127",
99
- "@elench/testkit-bridge": "0.1.127",
100
- "@elench/testkit-protocol": "0.1.127",
101
- "@elench/ts-analysis": "0.1.127",
98
+ "@elench/next-analysis": "0.1.128",
99
+ "@elench/testkit-bridge": "0.1.128",
100
+ "@elench/testkit-protocol": "0.1.128",
101
+ "@elench/ts-analysis": "0.1.128",
102
102
  "@oclif/core": "^4.10.6",
103
103
  "@playwright/test": "^1.52.0",
104
104
  "esbuild": "^0.25.11",