@untestutils/next 0.5.2 → 0.5.4

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/dist/index.d.mts CHANGED
@@ -1,13 +1,23 @@
1
- import { type FrameworkBaseOptions, type Recipe } from "@untestutils/core";
1
+ import { type Driver, type FrameworkBaseOptions, type Recipe } from "@untestutils/core";
2
2
  export type NextRun = "server" | "dev" | "static";
3
+ export type NextConfigOverride = Record<string, unknown>;
3
4
  export interface NextOptions extends FrameworkBaseOptions {
4
5
  run?: NextRun;
6
+ /**
7
+ * Merged onto `next.config.*` via backup/restore (`withMergedConfigOverride`).
8
+ * Next has no `--config` flag — the file is restored after prepare (build)
9
+ * or after the process stops (`dev`). Same role as Nuxt `nuxtConfig`.
10
+ */
11
+ nextConfig?: NextConfigOverride;
5
12
  }
13
+ export type NextMatrixVariant = Partial<Pick<NextOptions, "env" | "hashInputs" | "run" | "readyPath" | "readyTimeoutMs" | "workspaceDeps" | "nextConfig">>;
6
14
  /**
7
15
  * Next.js Recipe factory.
8
16
  * `run: 'server'` (default) — `next build` + `next start`
9
17
  * `run: 'dev'` — `next dev` (never shared)
10
18
  * `run: 'static'` — `next build` with export + staticDir on `out/`
11
19
  */
12
- export declare function next(opts: NextOptions): Recipe;
20
+ export declare const next: Driver<NextOptions>;
21
+ /** Expand one Next recipe base into many recipes with distinct ids. */
22
+ export declare function matrix(base: NextOptions, variants: Record<string, NextMatrixVariant>): Record<string, Recipe>;
13
23
  export default next;
package/dist/index.d.ts CHANGED
@@ -1,14 +1,24 @@
1
- import { type FrameworkBaseOptions, type Recipe } from '@untestutils/core';
1
+ import { type Driver, type FrameworkBaseOptions, type Recipe } from '@untestutils/core';
2
2
  export type NextRun = 'server' | 'dev' | 'static';
3
+ export type NextConfigOverride = Record<string, unknown>;
3
4
  export interface NextOptions extends FrameworkBaseOptions {
4
5
  run?: NextRun;
6
+ /**
7
+ * Merged onto `next.config.*` via backup/restore (`withMergedConfigOverride`).
8
+ * Next has no `--config` flag — the file is restored after prepare (build)
9
+ * or after the process stops (`dev`). Same role as Nuxt `nuxtConfig`.
10
+ */
11
+ nextConfig?: NextConfigOverride;
5
12
  }
13
+ export type NextMatrixVariant = Partial<Pick<NextOptions, 'env' | 'hashInputs' | 'run' | 'readyPath' | 'readyTimeoutMs' | 'workspaceDeps' | 'nextConfig'>>;
6
14
  /**
7
15
  * Next.js Recipe factory.
8
16
  * `run: 'server'` (default) — `next build` + `next start`
9
17
  * `run: 'dev'` — `next dev` (never shared)
10
18
  * `run: 'static'` — `next build` with export + staticDir on `out/`
11
19
  */
12
- export declare function next(opts: NextOptions): Recipe;
20
+ export declare const next: Driver<NextOptions>;
21
+ /** Expand one Next recipe base into many recipes with distinct ids. */
22
+ export declare function matrix(base: NextOptions, variants: Record<string, NextMatrixVariant>): Record<string, Recipe>;
13
23
  export default next;
14
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,oBAAoB,EACzB,KAAK,MAAM,EACZ,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;AAElD,MAAM,WAAW,WAAY,SAAQ,oBAAoB;IACvD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAYD;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAsF9C;AAED,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAgBL,KAAK,MAAM,EACX,KAAK,oBAAoB,EACzB,KAAK,MAAM,EACZ,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;AAElD,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzD,MAAM,WAAW,WAAY,SAAQ,oBAAoB;IACvD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,CACrC,IAAI,CACF,WAAW,EACX,KAAK,GAAG,YAAY,GAAG,KAAK,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,GAAG,YAAY,CAC/F,CACF,CAAC;AAuCF;;;;;GAKG;AACH,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,WAAW,CA4HnC,CAAC;AAEH,uEAAuE;AACvE,wBAAgB,MAAM,CACpB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBxB;AAED,eAAe,IAAI,CAAC"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,13 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join, resolve } from "pathe";
3
- import { assertAppRoot, cliFrameworkRecipe, defineRecipe, frameworkRoots, resolveBin, runCommand, staticDir } from "@untestutils/core";
3
+ import { appendWorkspaceHashInputs, assertAppRoot, cliFrameworkRecipe, configOverrideHashInput, deepMergePlain, defineDriver, defineRecipe, findFirstExistingConfig, frameworkRoots, installMergedConfigOverride, matrixRecipe, resolveBin, runCommand, staticDir, withMergedConfigOverride } from "@untestutils/core";
4
+ const NEXT_CONFIG_NAMES = [
5
+ "next.config.mjs",
6
+ "next.config.js",
7
+ "next.config.cjs",
8
+ "next.config.ts",
9
+ "next.config.mts"
10
+ ];
4
11
  function resolveNextBin(root) {
5
12
  return resolveBin({
6
13
  label: "next",
@@ -10,35 +17,59 @@ function resolveNextBin(root) {
10
17
  binRelative: ["dist/bin/next"]
11
18
  });
12
19
  }
20
+ function hashInputsFor(opts, root, extra) {
21
+ const inputs = [...opts.hashInputs ?? [root], ...extra];
22
+ if (opts.nextConfig) inputs.push(configOverrideHashInput("nextConfig", opts.nextConfig));
23
+ return inputs;
24
+ }
25
+ function resolveNextConfigPath(root) {
26
+ return findFirstExistingConfig(root, NEXT_CONFIG_NAMES) ?? join(root, "next.config.mjs");
27
+ }
28
+ async function withNextConfigOverride(root, overrides, fn) {
29
+ if (!overrides || !Object.keys(overrides).length) return fn();
30
+ return withMergedConfigOverride(resolveNextConfigPath(root), overrides, fn);
31
+ }
13
32
 
14
- export function next(opts) {
33
+ export const next = defineDriver((opts) => {
15
34
  const root = resolve(opts.root);
16
35
  const runMode = opts.run ?? "server";
17
36
  const id = opts.id ?? `next-${runMode}-${root.split("/").pop()}`;
18
37
  const bin = () => resolveNextBin(root);
38
+ const hasOverrides = Boolean(opts.nextConfig && Object.keys(opts.nextConfig).length);
19
39
  if (runMode === "dev") {
40
+ let restore;
20
41
  return cliFrameworkRecipe({
21
42
  id,
22
43
  root,
23
44
  label: "next",
24
45
  share: "never",
25
46
  env: opts.env,
26
- hashInputs: opts.hashInputs ?? [root, "run:dev"],
47
+ hashInputs: hashInputsFor(opts, root, ["run:dev"]),
27
48
  readyPath: opts.readyPath,
28
49
  readyTimeoutMs: opts.readyTimeoutMs,
29
- start: ({ port }) => ({
30
- command: process.execPath,
31
- args: [
32
- bin(),
33
- "dev",
34
- "-H",
35
- "127.0.0.1",
36
- "-p",
37
- String(port)
38
- ],
39
- cwd: root,
40
- env: { PORT: String(port) }
41
- })
50
+ workspaceDeps: opts.workspaceDeps,
51
+ start: async ({ port }) => {
52
+ if (hasOverrides) {
53
+ restore = await installMergedConfigOverride(resolveNextConfigPath(root), opts.nextConfig);
54
+ }
55
+ return {
56
+ command: process.execPath,
57
+ args: [
58
+ bin(),
59
+ "dev",
60
+ "-H",
61
+ "127.0.0.1",
62
+ "-p",
63
+ String(port)
64
+ ],
65
+ cwd: root,
66
+ env: { PORT: String(port) }
67
+ };
68
+ },
69
+ afterStop: async () => {
70
+ await restore?.();
71
+ restore = undefined;
72
+ }
42
73
  });
43
74
  }
44
75
  if (runMode === "static") {
@@ -46,26 +77,29 @@ export function next(opts) {
46
77
  const recipe = defineRecipe({
47
78
  id,
48
79
  share: "always",
49
- hashInputs: async () => [
50
- ...opts.hashInputs ?? [root],
51
- "run:static",
52
- ...opts.env ? [`env:${JSON.stringify(opts.env)}`] : []
53
- ],
80
+ hashInputs: async () => {
81
+ let inputs = hashInputsFor(opts, root, ["run:static"]);
82
+ if (opts.env) inputs = [...inputs, `env:${JSON.stringify(opts.env)}`];
83
+ inputs = await appendWorkspaceHashInputs(inputs, root, opts.workspaceDeps);
84
+ return inputs;
85
+ },
54
86
  ready: async () => {},
55
87
  prepare: async () => {
56
88
  ensureRoot();
57
89
  const nextBin = bin();
58
- const result = await runCommand(process.execPath, [nextBin, "build"], {
59
- cwd: root,
60
- env: {
61
- ...process.env,
62
- ...opts.env
63
- },
64
- timeoutMs: opts.readyTimeoutMs ?? 3e5
90
+ await withNextConfigOverride(root, opts.nextConfig, async () => {
91
+ const result = await runCommand(process.execPath, [nextBin, "build"], {
92
+ cwd: root,
93
+ env: {
94
+ ...process.env,
95
+ ...opts.env
96
+ },
97
+ timeoutMs: opts.readyTimeoutMs ?? 3e5
98
+ });
99
+ if (result.exitCode !== 0) {
100
+ throw new Error(`[untestutils/next] static build failed:\n${result.stderr.slice(-2e3)}`);
101
+ }
65
102
  });
66
- if (result.exitCode !== 0) {
67
- throw new Error(`[untestutils/next] static build failed:\n${result.stderr.slice(-2e3)}`);
68
- }
69
103
  const out = join(root, "out");
70
104
  if (!existsSync(out)) {
71
105
  throw new Error(`[untestutils/next] expected ${out} after static export (set output: 'export' in next.config)`);
@@ -90,14 +124,33 @@ export function next(opts) {
90
124
  label: "next",
91
125
  share: "always",
92
126
  env: opts.env,
93
- hashInputs: opts.hashInputs ?? [root, "run:server"],
127
+ hashInputs: hashInputsFor(opts, root, ["run:server"]),
94
128
  readyPath: opts.readyPath,
95
129
  readyTimeoutMs: opts.readyTimeoutMs,
96
- prepare: () => ({
97
- command: process.execPath,
98
- args: [bin(), "build"],
99
- cwd: root
100
- }),
130
+ workspaceDeps: opts.workspaceDeps,
131
+ prepare: async () => {
132
+ if (!hasOverrides) {
133
+ return {
134
+ command: process.execPath,
135
+ args: [bin(), "build"],
136
+ cwd: root
137
+ };
138
+ }
139
+ const nextBin = bin();
140
+ await withNextConfigOverride(root, opts.nextConfig, async () => {
141
+ const result = await runCommand(process.execPath, [nextBin, "build"], {
142
+ cwd: root,
143
+ env: {
144
+ ...process.env,
145
+ ...opts.env
146
+ },
147
+ timeoutMs: opts.readyTimeoutMs ?? 3e5
148
+ });
149
+ if (result.exitCode !== 0) {
150
+ throw new Error(`[untestutils/next] prepare failed (${result.exitCode}):\n${result.stderr.slice(-2e3)}\n${result.stdout.slice(-1e3)}`);
151
+ }
152
+ });
153
+ },
101
154
  start: ({ port }) => ({
102
155
  command: process.execPath,
103
156
  args: [
@@ -112,5 +165,26 @@ export function next(opts) {
112
165
  env: { PORT: String(port) }
113
166
  })
114
167
  });
168
+ });
169
+
170
+ export function matrix(base, variants) {
171
+ return matrixRecipe(next, base, variants, {
172
+ label: "next",
173
+ merge: (b, patch, id, name) => ({
174
+ ...b,
175
+ ...patch,
176
+ id,
177
+ env: {
178
+ ...b.env ?? {},
179
+ ...patch.env ?? {}
180
+ },
181
+ nextConfig: b.nextConfig || patch.nextConfig ? deepMergePlain(b.nextConfig ?? {}, patch.nextConfig ?? {}) : undefined,
182
+ hashInputs: [
183
+ ...b.hashInputs ?? [b.root],
184
+ ...patch.hashInputs ?? [],
185
+ `variant:${name}`
186
+ ]
187
+ })
188
+ });
115
189
  }
116
190
  export default next;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@untestutils/next",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "homepage": "https://s00d.github.io/untestutils/",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "pathe": "^2.0.3",
31
- "@untestutils/core": "0.5.2"
31
+ "@untestutils/core": "0.5.4"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^26.6.1",