@elench/testkit 0.1.67 → 0.1.68

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.
@@ -21,6 +21,12 @@ import {
21
21
  const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
22
22
  const ROOT_ENTRY = path.join(PACKAGE_ROOT, "lib", "index.mjs");
23
23
  const SETUP_ENTRY = path.join(PACKAGE_ROOT, "lib", "setup", "index.mjs");
24
+ const SETUP_NEXT_TSCONFIG_ENTRY = path.join(
25
+ PACKAGE_ROOT,
26
+ "lib",
27
+ "setup",
28
+ "next-runtime-tsconfig.mjs"
29
+ );
24
30
  const RUNTIME_ENTRY = path.join(PACKAGE_ROOT, "lib", "runtime", "index.mjs");
25
31
  const KNOWN_FAILURES_ENTRY = path.join(PACKAGE_ROOT, "lib", "known-failures", "index.mjs");
26
32
  const MODULE_RUNNER_ENTRY = path.join(
@@ -225,6 +231,7 @@ function resolvePackageSubpath(specifier) {
225
231
  const subpath = specifier.slice("@elench/testkit".length);
226
232
  if (!subpath) return ROOT_ENTRY;
227
233
  if (subpath === "/setup") return SETUP_ENTRY;
234
+ if (subpath === "/setup/next-runtime-tsconfig") return SETUP_NEXT_TSCONFIG_ENTRY;
228
235
  if (subpath === "/runtime") return RUNTIME_ENTRY;
229
236
  if (subpath === "/known-failures") return KNOWN_FAILURES_ENTRY;
230
237
 
@@ -56,13 +56,21 @@ export interface ScriptBuildConfig {
56
56
  script: string;
57
57
  }
58
58
 
59
+ export interface NextBuildConfig {
60
+ kind: "next";
61
+ cwd?: string;
62
+ distDir?: string;
63
+ inputs?: string[];
64
+ tsconfig?: string;
65
+ }
66
+
59
67
  export interface StepsBuildConfig {
60
68
  kind: "steps";
61
69
  inputs?: string[];
62
70
  steps?: TemplateLifecycleStepConfig[];
63
71
  }
64
72
 
65
- export type BuildConfig = TscBuildConfig | ScriptBuildConfig | StepsBuildConfig;
73
+ export type BuildConfig = TscBuildConfig | ScriptBuildConfig | NextBuildConfig | StepsBuildConfig;
66
74
 
67
75
  export interface LocalDatabaseConfig {
68
76
  provider: "local";
@@ -151,6 +159,7 @@ export interface ServiceConfig {
151
159
  databaseFrom?: string;
152
160
  dependsOn?: string[];
153
161
  discovery?: DiscoveryConfig;
162
+ env?: Record<string, string>;
154
163
  envFile?: string;
155
164
  envFiles?: string[];
156
165
  browser?: BrowserServiceConfig;
@@ -282,7 +291,7 @@ export declare function scriptBuild(
282
291
  options?: Omit<ScriptBuildConfig, "kind" | "script">
283
292
  ): ScriptBuildConfig;
284
293
  export declare function stepsBuild(options?: Omit<StepsBuildConfig, "kind">): StepsBuildConfig;
285
- export declare function nextBuild(options?: Omit<ScriptBuildConfig, "kind" | "script">): ScriptBuildConfig;
294
+ export declare function nextBuild(options?: Omit<NextBuildConfig, "kind">): NextBuildConfig;
286
295
  export declare function nodeApp(options: NodeAppOptions): ServiceConfig;
287
296
  export declare function nextApp(options: NextAppOptions): ServiceConfig;
288
297
  export declare function clerkSessionProfile(options?: {
@@ -172,7 +172,13 @@ export function stepsBuild(options = {}) {
172
172
  }
173
173
 
174
174
  export function nextBuild(options = {}) {
175
- return scriptBuild("./node_modules/.bin/next build", options);
175
+ return {
176
+ kind: "next",
177
+ cwd: options.cwd,
178
+ distDir: options.distDir || "dist",
179
+ tsconfig: options.tsconfig || "tsconfig.json",
180
+ inputs: Array.isArray(options.inputs) ? [...options.inputs] : undefined,
181
+ };
176
182
  }
177
183
 
178
184
  export function nodeApp(options = {}) {
@@ -249,7 +255,12 @@ export function nextApp(options = {}) {
249
255
 
250
256
  const normalizedPort = requiredNumber(port, "nextApp port");
251
257
  const baseUrl = explicitBaseUrl || "http://127.0.0.1:{port}";
252
- const build = mode === "start" ? explicitBuild || nextBuild({ cwd, inputs: buildInputs }) : explicitBuild || null;
258
+ const build =
259
+ explicitBuild === undefined
260
+ ? mode === "start"
261
+ ? nextBuild({ cwd, inputs: buildInputs })
262
+ : null
263
+ : explicitBuild;
253
264
  const start =
254
265
  explicitStart ||
255
266
  (mode === "start"
@@ -273,7 +284,13 @@ export function nextApp(options = {}) {
273
284
  baseUrl,
274
285
  readyUrl: explicitReadyUrl || baseUrl,
275
286
  readyTimeoutMs,
276
- env,
287
+ env: mode === "start"
288
+ ? {
289
+ NEXT_DIST_DIR: env.NEXT_DIST_DIR || "{prepareDir}/dist",
290
+ NEXT_TSCONFIG_PATH: env.NEXT_TSCONFIG_PATH || "{prepareDir}/tsconfig.json",
291
+ ...env,
292
+ }
293
+ : env,
277
294
  },
278
295
  };
279
296
  }
@@ -32,6 +32,33 @@ describe("setup helpers", () => {
32
32
  expect(config.local.start).toBe("./node_modules/.bin/next dev -p {port}");
33
33
  });
34
34
 
35
+ it("builds a Next app preset for start mode with managed runtime env defaults", () => {
36
+ const config = nextApp({ cwd: "frontend", port: 3000, mode: "start" });
37
+
38
+ expect(config.local.start).toBe("./node_modules/.bin/next start --port {port}");
39
+ expect(config.local.env).toMatchObject({
40
+ NEXT_DIST_DIR: "{prepareDir}/dist",
41
+ NEXT_TSCONFIG_PATH: "{prepareDir}/tsconfig.json",
42
+ });
43
+ expect(config.runtime.build).toEqual({
44
+ kind: "next",
45
+ cwd: "frontend",
46
+ distDir: "dist",
47
+ tsconfig: "tsconfig.json",
48
+ inputs: undefined,
49
+ });
50
+ });
51
+
52
+ it("allows Next start apps to disable managed builds explicitly", () => {
53
+ const config = nextApp({ cwd: "frontend", port: 3000, mode: "start", build: null });
54
+
55
+ expect(config.runtime.build).toBeNull();
56
+ expect(config.local.env).toMatchObject({
57
+ NEXT_DIST_DIR: "{prepareDir}/dist",
58
+ NEXT_TSCONFIG_PATH: "{prepareDir}/tsconfig.json",
59
+ });
60
+ });
61
+
35
62
  it("builds a Node app preset with tsc build defaults", () => {
36
63
  const config = nodeApp({ port: 3000, entry: "src/server.ts" });
37
64
 
@@ -64,9 +91,10 @@ describe("setup helpers", () => {
64
91
  inputs: undefined,
65
92
  });
66
93
  expect(nextBuild({ cwd: "frontend" })).toEqual({
67
- kind: "script",
68
- script: "./node_modules/.bin/next build",
94
+ kind: "next",
69
95
  cwd: "frontend",
96
+ distDir: "dist",
97
+ tsconfig: "tsconfig.json",
70
98
  inputs: undefined,
71
99
  });
72
100
  expect(
@@ -0,0 +1,43 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export async function writeNextRuntimeTsconfig(context = {}) {
5
+ const serviceDir = context.cwd || context.productDir;
6
+ const prepareDir = context.prepareDir;
7
+ if (!serviceDir || !prepareDir) {
8
+ throw new Error("writeNextRuntimeTsconfig requires cwd and prepareDir");
9
+ }
10
+
11
+ const outputPath = path.join(prepareDir, "tsconfig.json");
12
+ const outputDir = path.dirname(outputPath);
13
+ const relative = (target) => path.relative(outputDir, target).replaceAll(path.sep, "/");
14
+ const srcDir = path.join(serviceDir, "src");
15
+ const testsDir = path.join(serviceDir, "tests");
16
+
17
+ const config = {
18
+ extends: relative(path.join(serviceDir, "tsconfig.json")),
19
+ compilerOptions: {
20
+ paths: {
21
+ "@/*": [relative(path.join(srcDir, "*"))],
22
+ },
23
+ },
24
+ include: [
25
+ relative(path.join(serviceDir, "next-env.d.ts")),
26
+ `${relative(srcDir)}/**/*.ts`,
27
+ `${relative(srcDir)}/**/*.tsx`,
28
+ `${relative(srcDir)}/**/*.mts`,
29
+ `${relative(path.join(prepareDir, "dist", "types"))}/**/*.ts`,
30
+ `${relative(path.join(prepareDir, "dist", "dev", "types"))}/**/*.ts`,
31
+ ],
32
+ exclude: [
33
+ "node_modules",
34
+ `${relative(srcDir)}/**/__testkit__/**`,
35
+ `${relative(testsDir)}/**`,
36
+ ".next/cache",
37
+ ".next/dev",
38
+ ],
39
+ };
40
+
41
+ fs.mkdirSync(outputDir, { recursive: true });
42
+ fs.writeFileSync(outputPath, `${JSON.stringify(config, null, 2)}\n`);
43
+ }
@@ -0,0 +1,57 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { writeNextRuntimeTsconfig } from "./next-runtime-tsconfig.mjs";
6
+
7
+ const cleanups = [];
8
+
9
+ afterEach(() => {
10
+ while (cleanups.length > 0) {
11
+ cleanups.pop()();
12
+ }
13
+ });
14
+
15
+ describe("next runtime tsconfig helper", () => {
16
+ it("writes a runtime-specific tsconfig for next builds", async () => {
17
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-next-tsconfig-"));
18
+ cleanups.push(() => fs.rmSync(tempRoot, { recursive: true, force: true }));
19
+
20
+ const serviceDir = path.join(tempRoot, "frontend");
21
+ const prepareDir = path.join(tempRoot, ".testkit", "prepared");
22
+ fs.mkdirSync(path.join(serviceDir, "src"), { recursive: true });
23
+ fs.writeFileSync(path.join(serviceDir, "tsconfig.json"), "{}\n");
24
+ fs.writeFileSync(path.join(serviceDir, "next-env.d.ts"), "// next\n");
25
+
26
+ await writeNextRuntimeTsconfig({
27
+ cwd: serviceDir,
28
+ prepareDir,
29
+ productDir: tempRoot,
30
+ });
31
+
32
+ const generated = JSON.parse(fs.readFileSync(path.join(prepareDir, "tsconfig.json"), "utf8"));
33
+ expect(generated).toEqual({
34
+ extends: "../../frontend/tsconfig.json",
35
+ compilerOptions: {
36
+ paths: {
37
+ "@/*": ["../../frontend/src/*"],
38
+ },
39
+ },
40
+ include: [
41
+ "../../frontend/next-env.d.ts",
42
+ "../../frontend/src/**/*.ts",
43
+ "../../frontend/src/**/*.tsx",
44
+ "../../frontend/src/**/*.mts",
45
+ "dist/types/**/*.ts",
46
+ "dist/dev/types/**/*.ts",
47
+ ],
48
+ exclude: [
49
+ "node_modules",
50
+ "../../frontend/src/**/__testkit__/**",
51
+ "../../frontend/tests/**",
52
+ ".next/cache",
53
+ ".next/dev",
54
+ ],
55
+ });
56
+ });
57
+ });
@@ -30,6 +30,16 @@ export function normalizeBuildConfig(value, label) {
30
30
  };
31
31
  }
32
32
 
33
+ if (kind === "next") {
34
+ return {
35
+ kind,
36
+ cwd: normalizeOptionalString(value.cwd),
37
+ distDir: normalizeOptionalString(value.distDir) || "dist",
38
+ tsconfig: normalizeOptionalString(value.tsconfig) || "tsconfig.json",
39
+ inputs: normalizeConfiguredInputs(value.inputs, `${label}.inputs`),
40
+ };
41
+ }
42
+
33
43
  if (kind === "steps") {
34
44
  return {
35
45
  kind,
@@ -38,7 +48,7 @@ export function normalizeBuildConfig(value, label) {
38
48
  };
39
49
  }
40
50
 
41
- throw new Error(`${label}.kind must be one of: tsc, script, steps`);
51
+ throw new Error(`${label}.kind must be one of: tsc, script, next, steps`);
42
52
  }
43
53
 
44
54
  export function finalizeBuildConfig(build, transform) {
@@ -61,6 +71,15 @@ export function finalizeBuildConfig(build, transform) {
61
71
  inputs: build.inputs.map((input) => transform(input)),
62
72
  };
63
73
  }
74
+ if (build.kind === "next") {
75
+ return {
76
+ ...build,
77
+ cwd: build.cwd ? transform(build.cwd) : build.cwd,
78
+ distDir: transform(build.distDir),
79
+ tsconfig: transform(build.tsconfig),
80
+ inputs: build.inputs.map((input) => transform(input)),
81
+ };
82
+ }
64
83
  return {
65
84
  ...build,
66
85
  inputs: build.inputs.map((input) => transform(input)),
@@ -104,6 +123,27 @@ export function buildConfigToPrepare(build) {
104
123
  };
105
124
  }
106
125
 
126
+ if (build.kind === "next") {
127
+ const inputs = build.inputs.length > 0 ? [...build.inputs] : defaultNextInputs(build);
128
+ return {
129
+ inputs,
130
+ steps: [
131
+ {
132
+ kind: "module",
133
+ specifier: "@elench/testkit/setup/next-runtime-tsconfig#writeNextRuntimeTsconfig",
134
+ cwd: build.cwd || undefined,
135
+ inputs,
136
+ },
137
+ {
138
+ kind: "command",
139
+ cmd: "./node_modules/.bin/next build",
140
+ cwd: build.cwd || undefined,
141
+ inputs,
142
+ },
143
+ ],
144
+ };
145
+ }
146
+
107
147
  const inputs = build.inputs.length > 0 ? [...build.inputs] : defaultTscInputs(build);
108
148
  const cmd = `./node_modules/.bin/tsc -p ${build.tsconfig} --outDir "{prepareDir}/${build.outDir}"`;
109
149
  return {
@@ -134,11 +174,35 @@ function sourceEntryToOutputPath(entry) {
134
174
  }
135
175
 
136
176
  function defaultTscInputs(build) {
137
- const inputs = new Set([build.tsconfig, "package.json"]);
138
- const normalizedEntry = String(build.entry).split(path.sep).join("/");
177
+ const inputs = new Set([
178
+ prefixConfiguredPath(build.cwd, build.tsconfig),
179
+ prefixConfiguredPath(build.cwd, "package.json"),
180
+ ]);
181
+ const normalizedEntry = prefixConfiguredPath(build.cwd, String(build.entry).split(path.sep).join("/"));
139
182
  const topLevelDir = normalizedEntry.includes("/") ? normalizedEntry.slice(0, normalizedEntry.indexOf("/")) : normalizedEntry;
140
183
  if (topLevelDir) {
141
184
  inputs.add(topLevelDir);
142
185
  }
143
186
  return [...inputs];
144
187
  }
188
+
189
+ function defaultNextInputs(build) {
190
+ const inputs = new Set([
191
+ prefixConfiguredPath(build.cwd, build.tsconfig),
192
+ prefixConfiguredPath(build.cwd, "package.json"),
193
+ prefixConfiguredPath(build.cwd, "src"),
194
+ prefixConfiguredPath(build.cwd, "app"),
195
+ prefixConfiguredPath(build.cwd, "public"),
196
+ prefixConfiguredPath(build.cwd, "next.config.js"),
197
+ prefixConfiguredPath(build.cwd, "next.config.mjs"),
198
+ prefixConfiguredPath(build.cwd, "next.config.ts"),
199
+ ]);
200
+ return [...inputs];
201
+ }
202
+
203
+ function prefixConfiguredPath(cwd, value) {
204
+ const normalizedCwd = String(cwd || "").split(path.sep).join("/").replace(/^\.\/+/, "").replace(/\/+$/g, "");
205
+ const normalizedValue = String(value).split(path.sep).join("/").replace(/^\.\/+/, "");
206
+ if (!normalizedCwd || normalizedCwd === ".") return normalizedValue;
207
+ return `${normalizedCwd}/${normalizedValue}`.replace(/\/+/g, "/");
208
+ }
@@ -56,6 +56,22 @@ describe("shared build config helpers", () => {
56
56
  inputs: ["frontend/package.json"],
57
57
  });
58
58
 
59
+ expect(
60
+ normalizeBuildConfig(
61
+ {
62
+ kind: "next",
63
+ cwd: "frontend",
64
+ },
65
+ "runtime.build"
66
+ )
67
+ ).toEqual({
68
+ kind: "next",
69
+ cwd: "frontend",
70
+ distDir: "dist",
71
+ tsconfig: "tsconfig.json",
72
+ inputs: [],
73
+ });
74
+
59
75
  expect(
60
76
  normalizeBuildConfig(
61
77
  {
@@ -77,5 +93,58 @@ describe("shared build config helpers", () => {
77
93
  },
78
94
  ],
79
95
  });
96
+
97
+ expect(
98
+ buildConfigToPrepare({
99
+ kind: "next",
100
+ cwd: "frontend",
101
+ distDir: "dist",
102
+ tsconfig: "tsconfig.json",
103
+ inputs: [],
104
+ })
105
+ ).toEqual({
106
+ inputs: [
107
+ "frontend/tsconfig.json",
108
+ "frontend/package.json",
109
+ "frontend/src",
110
+ "frontend/app",
111
+ "frontend/public",
112
+ "frontend/next.config.js",
113
+ "frontend/next.config.mjs",
114
+ "frontend/next.config.ts",
115
+ ],
116
+ steps: [
117
+ {
118
+ kind: "module",
119
+ specifier: "@elench/testkit/setup/next-runtime-tsconfig#writeNextRuntimeTsconfig",
120
+ cwd: "frontend",
121
+ inputs: [
122
+ "frontend/tsconfig.json",
123
+ "frontend/package.json",
124
+ "frontend/src",
125
+ "frontend/app",
126
+ "frontend/public",
127
+ "frontend/next.config.js",
128
+ "frontend/next.config.mjs",
129
+ "frontend/next.config.ts",
130
+ ],
131
+ },
132
+ {
133
+ kind: "command",
134
+ cmd: "./node_modules/.bin/next build",
135
+ cwd: "frontend",
136
+ inputs: [
137
+ "frontend/tsconfig.json",
138
+ "frontend/package.json",
139
+ "frontend/src",
140
+ "frontend/app",
141
+ "frontend/public",
142
+ "frontend/next.config.js",
143
+ "frontend/next.config.mjs",
144
+ "frontend/next.config.ts",
145
+ ],
146
+ },
147
+ ],
148
+ });
80
149
  });
81
150
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/next-analysis",
3
- "version": "0.1.67",
3
+ "version": "0.1.68",
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.67",
3
+ "version": "0.1.68",
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.67"
25
+ "@elench/testkit-protocol": "0.1.68"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.67",
3
+ "version": "0.1.68",
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.67",
3
+ "version": "0.1.68",
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.67",
3
+ "version": "0.1.68",
4
4
  "description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -64,10 +64,10 @@
64
64
  "vitest": "^3.2.4"
65
65
  },
66
66
  "dependencies": {
67
- "@elench/next-analysis": "0.1.67",
68
- "@elench/ts-analysis": "0.1.67",
69
- "@elench/testkit-bridge": "0.1.67",
70
- "@elench/testkit-protocol": "0.1.67",
67
+ "@elench/next-analysis": "0.1.68",
68
+ "@elench/ts-analysis": "0.1.68",
69
+ "@elench/testkit-bridge": "0.1.68",
70
+ "@elench/testkit-protocol": "0.1.68",
71
71
  "@babel/code-frame": "^7.29.0",
72
72
  "@oclif/core": "^4.10.6",
73
73
  "esbuild": "^0.25.11",