@zukhruf/nx 0.3.57 → 0.3.58

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 CHANGED
@@ -6,11 +6,15 @@ The words in these documents have one meaning each. See the glossary in [CONTEXT
6
6
 
7
7
  ## Areas
8
8
 
9
- | Area | Import path | Status |
10
- | ---------- | -------------------- | ---------------------------------------------------------- |
11
- | format | `@zukhruf/nx/format` | This README |
12
- | test | `@zukhruf/nx/test` | Planned (#3328): one `node:test` target, copied in 5 repos |
13
- | generators | — | Later, when their own count is due |
9
+ | Area | Import path | Status |
10
+ | ---------------- | ---------------------- | --------------------------------------------------------------------------------------------- |
11
+ | format | `@zukhruf/nx/format` | This README |
12
+ | test | `@zukhruf/nx/test` | This README |
13
+ | portless | `@zukhruf/nx/portless` | Candidate (#3336): 14 projects in 3 repos |
14
+ | opentofu, prisma | — | Candidates (#3339): 5 places each |
15
+ | generators | — | Candidates: the workspace generator of `@factory/foundation` (#3337); app kinds later (#3338) |
16
+
17
+ The lint inputs (7 copies) are #3335; their home (`@zukhruf/eslint` or an area here) is still open.
14
18
 
15
19
  ## format
16
20
 
@@ -131,3 +135,212 @@ context-compiler already does this for `@nx/eslint/plugin`. See [ADR 0003](./doc
131
135
  | deepagents | #3323 |
132
136
 
133
137
  The umbrella item is #3317.
138
+
139
+ ## test
140
+
141
+ > Status: shipped (#3328).
142
+
143
+ Seven workspaces run their tests with `node --test` through Nx. Each one copies the `test` target, and the flags drifted. The `test` area is an Nx inference plugin. It gives the `test` target to each project that has `node:test` files. You list the plugin one time in `nx.json`. You do not copy the target, and you do not tag the project.
144
+
145
+ ### Use the test area
146
+
147
+ You need Nx 23.2.1 or later. The peer range of `nx` and `@nx/devkit` becomes `>=23.2.1 <25`. See [Prerequisites of the test area](#prerequisites-of-the-test-area).
148
+
149
+ Add the plugin to `plugins` in `nx.json`. The options are optional. They apply to each project of the workspace:
150
+
151
+ ```json
152
+ {
153
+ "plugins": [
154
+ "@zukhruf/nx/format",
155
+ {
156
+ "plugin": "@zukhruf/nx/test",
157
+ "options": { "timeout": 60000, "flags": [] }
158
+ }
159
+ ]
160
+ }
161
+ ```
162
+
163
+ zukhruf builds this package, so it lists the plugin by path, as it does for `format`: `"./packages/nx/src/test/plugin.ts"`.
164
+
165
+ ### Signature of the test area
166
+
167
+ ```ts
168
+ // @zukhruf/nx/test
169
+ export const createNodes: CreateNodes<{
170
+ timeout?: number;
171
+ flags?: string[];
172
+ }>;
173
+ ```
174
+
175
+ Unlike `format`, this plugin takes options. The repos use different flags, so each difference is a parameter:
176
+
177
+ | Option | Default | Effect |
178
+ | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
179
+ | `timeout` | `60000` | the value of `--test-timeout`, in milliseconds |
180
+ | `flags` | `[]` | more Node flags for each project of the workspace, for example `["--test-force-exit"]` or `["--experimental-test-module-mocks"]` |
181
+
182
+ ### Which projects get the test target
183
+
184
+ The plugin finds the test files. It does not read tags or imports. A project root gets the `test` target when all of these are true:
185
+
186
+ - The root owns at least one test file: `*.test.ts`, `*.test.mts`, `*.test.cts`, `*.test.js` or `*.test.mjs`. A file belongs to its nearest project root.
187
+ - The root has no `vitest.config.*`.
188
+ - The root has no `vite.config.*` with a `test` key. `@nx/vitest` uses the same condition (`@nx/vitest/dist/src/plugins/plugin.js:132`).
189
+ - The root is not the workspace root `.`.
190
+
191
+ The project roots are the same as for `format` (see [ADR 0003](./docs/adr/0003-which-roots-get-targets.md)). The plugin adds `**/*.test.{ts,mts,cts,js,mjs}` and `**/{vite,vitest}.config.{js,ts,mjs,mts,cjs,cts}` to the files that it reads. Thus Nx runs the plugin again when a test file or a Vite config changes. The plugin loads Vite from the workspace only for a root with a `vite.config.*`. Nx does not give the plugin the paths in `.gitignore`, so the plugin does not see `dist`.
192
+
193
+ A project with Vitest files and `node:test` files gets no `test` target from this plugin. `@nx/vitest` gives it its target, as today.
194
+
195
+ The 2026-10-29 audit of the 7 repos measured the effect. The rule matches each `node:test` target in use. 11 targets that test nothing go: in context-compiler the connectors `call-history`, `contacts`, `messages`, `slack` and `teams`; in text2sql `backend/inputs`, `harness/runs`, `toolkit/dashboards`, `reports`, `rpc` and `threads`. In text2sql, `.agents` and `.claude` get a target, because they own test files. The `test:node` tag is not necessary. A repo can delete it. See [ADR 0004](./docs/adr/0004-node-test-target-from-test-files.md).
196
+
197
+ ### What the test target runs
198
+
199
+ | Part | Value |
200
+ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
201
+ | Command | `node --test --no-warnings --test-timeout=<timeout> <flags> --test-reporter=spec --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=test-results.xml {args}` |
202
+ | `cwd` | `{projectRoot}` |
203
+ | `args` | the test selection (see below) |
204
+ | `outputs` | `["{projectRoot}/test-results.xml"]` |
205
+ | `cache` | `false` |
206
+ | `dependsOn` | `["^build"]` |
207
+
208
+ - `--no-warnings`: 5 of the 7 repos use it.
209
+ - `--test-timeout`: zukhruf and deepagents use 60 s, so the default is 60 s. The other repos have no time limit, and that is drift.
210
+ - Two reporters: `spec` to the terminal and `junit` to `test-results.xml`. zukhruf, deepagents and text2sql use both.
211
+ - `^build`: a test imports the built output of the packages that the project depends on. factory, whatisgoingon and text2sql test from the source, so `^build` is sufficient for them. A repo whose tests also need the project's own `build` adds it (see [Prerequisites of the test area](#prerequisites-of-the-test-area)).
212
+ - No cache: no repo caches a `node:test` run today.
213
+
214
+ The base command has no `--test-force-exit` and no `--experimental-test-module-mocks`. Only Docker tests and tests with open handles need `--test-force-exit`. Only deepagents and the text2sql electron app use module mocks. A repo adds these flags with the `flags` option. One project adds them in its own `args` (see [Override the test target of a project](#override-the-test-target-of-a-project)).
215
+
216
+ The test selection is a list of globs:
217
+
218
+ - one glob for each top-level folder that has test files, for example `"src/**/*.test.ts" "eval/**/*.test.ts"`,
219
+ - one glob with no `**` for the test files directly in the project root, for example `"*.test.ts"`, only when such files exist.
220
+
221
+ Thus a file outside these globs does not run, for example a build output or a `test/helpers/*` file with no test file next to it. Node's own default pattern, `**/{test,test/**/*,test-*,*[._-]test}.{js,mjs,cjs,ts,mts,cts}`, matches too much: `dist/spec/test/*`, `test/helpers/*` and `*.e2e.test.ts`.
222
+
223
+ ### Run one file or one test
224
+
225
+ `--args` replaces the test selection. Use it to run one file:
226
+
227
+ ```sh
228
+ nx run mutex:test --args="src/mutex/mutex.test.ts"
229
+ ```
230
+
231
+ A Node flag on the CLI keeps the test selection. Use it to run the tests with a name:
232
+
233
+ ```sh
234
+ nx run mutex:test --test-name-pattern=login
235
+ ```
236
+
237
+ The command ends with `{args}`. For a command with `{args}`, Nx puts the CLI flags and the positional words first, then `args` (`nx/dist/src/executors/run-commands/run-commands.impl.js:136-142`). Thus `--test-name-pattern=login` comes before the globs, and Node reads it as a flag.
238
+
239
+ Without `{args}`, the order is different. Nx puts a CLI flag after `args` (`run-commands.impl.js:117-119` and `145-162`): only an unknown option that is not on the CLI comes before `args`. Then `nx run mutex:test --test-name-pattern=login` puts the flag after the globs, and Node reads it as a file name.
240
+
241
+ A positional file does not replace the selection. In `nx run mutex:test src/mutex/mutex.test.ts`, Nx adds the file before the selection, and all the tests run. Use `--args`.
242
+
243
+ A probe on Nx 23.2.1 (2026-10-29) showed this, with `args` `DEFAULT` and a command that prints its arguments:
244
+
245
+ | CLI | With `{args}` | Without `{args}` |
246
+ | ------------------------------------ | ----------------------------------- | ----------------------------------- |
247
+ | `--args=X` | `X` | `X` |
248
+ | `--test-name-pattern=login` | `--test-name-pattern=login DEFAULT` | `DEFAULT --test-name-pattern=login` |
249
+ | `pos.test.ts` | `pos.test.ts DEFAULT` | `DEFAULT pos.test.ts` |
250
+ | `--args=X --test-name-pattern=login` | `--test-name-pattern=login X` | — |
251
+
252
+ The selection is in `args` and not in the command, because a flag after a glob does not work. `node --test "x.test.ts" --test-timeout=1` reads the flag as a file name. It stops with exit code 1: `Could not find 'x.test.ts, --test-timeout=1'`. zukhruf has `"src/**/*.test.ts"` at the end of its command today, so a zukhruf run cannot narrow. Also, a glob that matches no file gives exit code 0. Thus a wrong narrowing passes with no test.
253
+
254
+ ### Docker tests
255
+
256
+ A project with the tag `test:docker` starts Docker containers in its tests. zukhruf and deepagents already use this tag. The plugin adds three items to the `test` target of such a project:
257
+
258
+ - `--test-global-setup=<path>`. The global setup of `@zukhruf/testing` makes one run of all the test processes, and it removes what a killed run leaves.
259
+ - `--test-force-exit`. The teardown of the global setup is synchronous on purpose, so it runs also under `--test-force-exit` (`packages/testing/src/scripts/docker-global-setup.ts:99-104`).
260
+ - `cache: false`. The result depends on the engine, not only on the files.
261
+
262
+ `cache: false` is the default, not a guarantee. The base target has no cache too. `targetDefaults` and the project files come after the plugin, so a repo can set `cache: true` on both.
263
+
264
+ The plugin selects the path in this order:
265
+
266
+ 1. The project's own `src/testing/docker-global-setup.ts`, if it exists. mutex has one: it starts the shared servers before the run. `@zukhruf/testing` has one: its tests use its own source.
267
+ 2. Else, `@zukhruf/testing/docker-global-setup`, resolved from the project root with the default conditions. This gives `dist/scripts/docker-global-setup.js`. The plugin writes the path relative to the `cwd` of the target.
268
+ 3. If the plugin cannot resolve the package, it stops with an error. It does not run the tests with no global setup.
269
+
270
+ The value must be a path. Node resolves it with `path.resolve(cwd, value)`, so a package name does not work. That is why 5 repos keep a file that only re-exports the global setup. Each repo deletes this file when it migrates. The global setup runs one time for each `node --test` process.
271
+
272
+ The plugin does not select the Docker engine. It never sets or deletes `DOCKER_CONTEXT` or `DOCKER_HOST`, and it never calls the Docker CLI. The user, CI or the repo selects the engine. See [ADR 0005](./docs/adr/0005-docker-tests-get-the-global-setup.md).
273
+
274
+ On an SSH engine, a repo adds `--test-concurrency=1` with `flags`, or `"parallelism": false` in `targetDefaults`. The plugin does not add them.
275
+
276
+ ### Override the test target of a project
277
+
278
+ Nx applies the plugin first, then `targetDefaults`, then `project.json` and `package.json`. A later source wins.
279
+
280
+ A flag for each project of the workspace goes into the `flags` option. A flag for one project goes into the `args` of that project, before its own selection. `args` comes after the command, and a flag must come before a glob. For example, the text2sql backends have a time limit of 120 s:
281
+
282
+ ```json
283
+ {
284
+ "targets": {
285
+ "test": {
286
+ "options": { "args": "--test-timeout=120000 \"src/**/*.test.ts\"" }
287
+ }
288
+ }
289
+ }
290
+ ```
291
+
292
+ Node uses the last `--test-timeout`, so the value in `args` wins. A probe on Node 26.10.0 showed this on 2026-10-29: `--test-timeout=50 --test-timeout=60000` passed a test of 300 ms, and the reverse order failed it.
293
+
294
+ text2sql leaves the integration tests out of `test` in the same way: `"args": "\"src/**/!(*.integration).test.ts\""`. Other flags for one project: a time limit of 10 s, `--experimental-test-module-mocks`, `--test-force-exit`, `--env-file`.
295
+
296
+ Do not override the `command`. A command in a project file is a new copy of the target.
297
+
298
+ These rules stay in their repo, because they are of a different kind:
299
+
300
+ - the script of deepagents that selects the Docker context,
301
+ - the tests of elt and context-compiler that run from the workspace root,
302
+ - the list of e2e files of the context-compiler desktop app.
303
+
304
+ A `scripts.test` in a `package.json` replaces the inferred target. Nx runs it with `nx:run-script`. The 4 demo projects of deepagents keep their scripts.
305
+
306
+ ### Prerequisites of the test area
307
+
308
+ - Nx 23.2.1 or later. In Nx 23.2.1, a target default with its own `command` replaces the command of a plugin (`nx/dist/src/project-graph/utils/project-configuration/target-defaults.js:201-229`). Nx 23.1.0 does not do this. text2sql uses 23.1.0 today.
309
+ - Delete the `command` of `targetDefaults.test` in `nx.json`. If you keep it, it replaces the command of the plugin.
310
+ - Write each extra dependency with `"..."`, so that `^build` stays. For example, factory writes `["...", "^sdk"]`.
311
+ - If the tests of a project use its own built output, add `build`: `targetDefaults.test.dependsOn: ["...", "build"]`. zukhruf, deepagents, elt and context-compiler need this. For example, the tests of `@zukhruf/nx` resolve `@zukhruf/nx/format` to `dist`. Nx skips a dependency on a target that the project does not have, so this is safe for a project with no `build`.
312
+
313
+ ### Migrate a workspace to the test area
314
+
315
+ 1. Update `nx` and `@nx/devkit` to 23.2.1 or later.
316
+ 2. Add `@zukhruf/nx/test` to `plugins` in `nx.json`. Set `timeout` and `flags` if the repo needs other values.
317
+ 3. Delete the `command` of `targetDefaults.test`, and its tag filters. Write each extra dependency with `"..."`.
318
+ 4. Delete each `test` target in a `project.json` or `package.json` that the plugin now gives. Move the flags of one project into its `args`.
319
+ 5. Add the tag `test:docker` to each project with Docker tests.
320
+ 6. Delete the file that only re-exports `@zukhruf/testing/docker-global-setup`. Keep a file that does more, for example the mutex file.
321
+ 7. Delete the `test:node` tags.
322
+ 8. Compare `nx show projects --withTarget test` before and after. The differences must be the ones in the table.
323
+
324
+ | Workspace | Today | Changes |
325
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
326
+ | zukhruf | `targetDefaults.test` with filters `tag:test:node` and `tag:test:docker`; `dependsOn` `["build", "^build"]`; `--test-force-exit` for each project | 30 tagged projects; 2 Docker projects (mutex, testing) keep their own global setup; `dependsOn` `["...", "build"]`; `flags` `["--test-force-exit"]` if it keeps force-exit for each project |
327
+ | factory | 2 targets: `api` with Docker arguments; `foundation` with its tests in `tests/` | delete `apps/factory/api/src/testing/docker-global-setup.ts`; `dependsOn` `["...", "^sdk"]` |
328
+ | whatisgoingon | 1 target | 1 |
329
+ | deepagents | 11 tagged projects, 3 Docker projects, 4 demo scripts | delete `tools/src/docker-global-setup.ts`; `dependsOn` `["...", "build"]`; module mocks in `flags` or in the `args` of a project; the 4 demo scripts and the Docker context script stay |
330
+ | elt | 55 commands in the projects, 8 with the global setup | delete `docker-global-setup.ts`; `dependsOn` `["...", "build"]`; the runs from the workspace root stay as overrides |
331
+ | context-compiler | 17 targets; the `plugin` project has a no-op `test` that depends on `server:test` | 5 connector targets that test nothing go; `dependsOn` `["...", "build"]`; the desktop e2e files stay as an override; if `plugin` owns no test file, the plugin gives it nothing and its own `test` in `project.json` stays |
332
+ | text2sql | about 24 commands in the projects, and 13 projects on the shared default; Nx 23.1.0 | update Nx; delete `packages/adapters/src/testing/docker-global-setup.ts` and `apps/desktop/backend/src/testing/docker-global-setup.ts`; 6 targets that test nothing go; `.agents` and `.claude` get a target; the backends set `--test-timeout=120000` in `args`; the daemon keeps `["image"]` or uses `"..."` |
333
+
334
+ ### Who uses the test area
335
+
336
+ | Workspace | Backlog item |
337
+ | ---------------- | ------------------------------------------- |
338
+ | zukhruf | uses it: `nx.json` lists the plugin by path |
339
+ | factory | #3363 |
340
+ | whatisgoingon | #3364 |
341
+ | deepagents | #3365 |
342
+ | elt | #3366 |
343
+ | context-compiler | #3367 |
344
+ | text2sql | #3368 |
345
+
346
+ The umbrella item is #3328.
@@ -0,0 +1,14 @@
1
+ import { type CreateNodes } from '@nx/devkit';
2
+ export interface TestOptions {
3
+ timeout?: number;
4
+ flags?: string[];
5
+ }
6
+ /**
7
+ * Gives the `test` target, a `node --test` run, to each project root that
8
+ * owns test files and is not a Vitest project (ADR 0004). A project with the
9
+ * tag `test:docker` also gets the global setup of `@zukhruf/testing` (ADR
10
+ * 0005). The project roots are the roots of `./format`: a copy of its rule,
11
+ * on the record in docs/copied-code.md.
12
+ */
13
+ export declare const createNodes: CreateNodes<TestOptions>;
14
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/test/plugin.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,YAAY,CAAC;AAMpB,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,WAAW,CAwFhD,CAAC"}
@@ -0,0 +1,151 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync } from 'node:fs';
10
+ import { createRequire } from 'node:module';
11
+ import { basename, dirname, extname, join, relative } from 'node:path';
12
+ import { pathToFileURL } from 'node:url';
13
+ import { createNodesFromFiles, readJsonFile, } from '@nx/devkit';
14
+ import { buildPackageJsonPatterns, buildPackageJsonWorkspacesMatcher, } from '@nx/devkit/internal';
15
+ const testFile = /\.test\.(?:ts|mts|cts|js|mjs)$/;
16
+ const viteConfig = /^vite\.config\.(?:js|ts|mjs|mts|cjs|cts)$/;
17
+ const vitestConfig = /^vitest\.config\.(?:js|ts|mjs|mts|cjs|cts)$/;
18
+ const ownGlobalSetup = 'src/testing/docker-global-setup.ts';
19
+ /**
20
+ * Gives the `test` target, a `node --test` run, to each project root that
21
+ * owns test files and is not a Vitest project (ADR 0004). A project with the
22
+ * tag `test:docker` also gets the global setup of `@zukhruf/testing` (ADR
23
+ * 0005). The project roots are the roots of `./format`: a copy of its rule,
24
+ * on the record in docs/copied-code.md.
25
+ */
26
+ export const createNodes = [
27
+ '{package.json,**/package.json,project.json,**/project.json,**/*.test.{ts,mts,cts,js,mjs},**/{vite,vitest}.config.{js,ts,mjs,mts,cjs,cts}}',
28
+ async (files, options, context) => {
29
+ // The defaults of ADR 0004: 60 s is the time limit of zukhruf and
30
+ // deepagents, and no repo adds a flag for each project.
31
+ const { timeout = 60_000, flags = [] } = options ?? {};
32
+ const configFiles = files.filter((file) => ['package.json', 'project.json'].includes(basename(file)));
33
+ const projectJsonRoots = new Set(configFiles
34
+ .filter((file) => basename(file) === 'project.json')
35
+ .map((file) => dirname(file)));
36
+ const isInWorkspaces = buildPackageJsonWorkspacesMatcher(buildPackageJsonPatterns(context.workspaceRoot, (file) => readJsonFile(join(context.workspaceRoot, file))));
37
+ const rootFiles = configFiles.filter((file) => basename(file) === 'project.json'
38
+ ? true
39
+ : !projectJsonRoots.has(dirname(file)) && isInWorkspaces(file));
40
+ const roots = new Set(rootFiles.map((file) => dirname(file)));
41
+ const owned = new Map();
42
+ for (const file of files.filter((file) => testFile.test(file))) {
43
+ const root = nearestRoot(roots, dirname(file));
44
+ if (root !== undefined && root !== '.') {
45
+ owned.set(root, [...(owned.get(root) ?? []), relative(root, file)]);
46
+ }
47
+ }
48
+ const vitestRoots = new Set(files
49
+ .filter((file) => vitestConfig.test(basename(file)))
50
+ .map((file) => dirname(file)));
51
+ const viteConfigs = new Map(files
52
+ .filter((file) => viteConfig.test(basename(file)))
53
+ .map((file) => [dirname(file), file]));
54
+ return createNodesFromFiles(async (file) => {
55
+ const root = dirname(file);
56
+ const tests = owned.get(root);
57
+ if (tests === undefined ||
58
+ vitestRoots.has(root) ||
59
+ (await hasViteTest(context.workspaceRoot, viteConfigs.get(root)))) {
60
+ return {};
61
+ }
62
+ const docker = tags(context.workspaceRoot, root, new Set(configFiles)).includes('test:docker');
63
+ const command = [
64
+ 'node --test --no-warnings',
65
+ `--test-timeout=${timeout}`,
66
+ ...new Set([
67
+ ...flags,
68
+ ...(docker
69
+ ? [
70
+ `--test-global-setup=${globalSetup(context.workspaceRoot, root)}`,
71
+ '--test-force-exit',
72
+ ]
73
+ : []),
74
+ ]),
75
+ '--test-reporter=spec --test-reporter-destination=stdout',
76
+ '--test-reporter=junit --test-reporter-destination=test-results.xml',
77
+ '{args}',
78
+ ].join(' ');
79
+ const test = {
80
+ executor: 'nx:run-commands',
81
+ options: { command, cwd: '{projectRoot}', args: selection(tests) },
82
+ outputs: ['{projectRoot}/test-results.xml'],
83
+ cache: false,
84
+ dependsOn: ['^build'],
85
+ };
86
+ return { projects: { [root]: { targets: { test } } } };
87
+ }, rootFiles, options, context);
88
+ },
89
+ ];
90
+ function nearestRoot(roots, folder) {
91
+ if (roots.has(folder)) {
92
+ return folder;
93
+ }
94
+ return folder === '.' ? undefined : nearestRoot(roots, dirname(folder));
95
+ }
96
+ // One glob for each top-level folder with test files, and one glob with no
97
+ // `**` for the test files directly in the root (ADR 0004).
98
+ function selection(tests) {
99
+ const extensions = new Map();
100
+ for (const test of tests) {
101
+ const slash = test.indexOf('/');
102
+ const folder = slash === -1 ? '' : test.slice(0, slash);
103
+ extensions.set(folder, (extensions.get(folder) ?? new Set()).add(extname(test).slice(1)));
104
+ }
105
+ return [...extensions]
106
+ .sort(([a], [b]) => a.localeCompare(b))
107
+ .map(([folder, found]) => {
108
+ const extension = found.size === 1
109
+ ? [...found].join('')
110
+ : `{${[...found].sort().join(',')}}`;
111
+ return `"${folder === '' ? '' : `${folder}/**/`}*.test.${extension}"`;
112
+ })
113
+ .join(' ');
114
+ }
115
+ // The rule of `@nx/vitest`: a `vite.config.*` whose resolved config has
116
+ // `test` makes a Vitest project (`@nx/vitest/dist/src/plugins/plugin.js:
117
+ // 120-138`). Vite is loaded from the workspace, and only when a root has a
118
+ // `vite.config.*`.
119
+ async function hasViteTest(workspaceRoot, file) {
120
+ if (file === undefined) {
121
+ return false;
122
+ }
123
+ const vite = await import(__rewriteRelativeImportExtension(pathToFileURL(createRequire(join(workspaceRoot, 'package.json')).resolve('vite')).href));
124
+ const config = await vite.resolveConfig({ configFile: join(workspaceRoot, file), mode: 'development' }, 'build');
125
+ return 'test' in config && Boolean(config.test);
126
+ }
127
+ function tags(workspaceRoot, root, configFiles) {
128
+ const projectJson = join(root, 'project.json');
129
+ const packageJson = join(root, 'package.json');
130
+ return [
131
+ ...(configFiles.has(projectJson)
132
+ ? (readJsonFile(join(workspaceRoot, projectJson))
133
+ .tags ?? [])
134
+ : []),
135
+ ...(configFiles.has(packageJson)
136
+ ? (readJsonFile(join(workspaceRoot, packageJson)).nx?.tags ?? [])
137
+ : []),
138
+ ];
139
+ }
140
+ // The project's own global setup wins; else the one of `@zukhruf/testing`,
141
+ // resolved from the project root with the default conditions and written
142
+ // relative to the `cwd` of the target (ADR 0005). Node reads the value as a
143
+ // path, so a package name does not work.
144
+ function globalSetup(workspaceRoot, root) {
145
+ const projectRoot = join(workspaceRoot, root);
146
+ if (existsSync(join(projectRoot, ownGlobalSetup))) {
147
+ return ownGlobalSetup;
148
+ }
149
+ return relative(projectRoot, createRequire(join(projectRoot, 'package.json')).resolve('@zukhruf/testing/docker-global-setup'));
150
+ }
151
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/test/plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zukhruf/nx",
3
- "version": "0.3.57",
4
- "description": "The Nx part of the monorepo standard. Each area is a subpath. @zukhruf/nx/format is an Nx inference plugin that gives each project two targets: format, which writes the files with Prettier, and format:check, which only reads them and is cached.",
3
+ "version": "0.3.58",
4
+ "description": "The Nx part of the monorepo standard, one area per subpath. @zukhruf/nx/format gives each project format and a cached format:check, both with Prettier. @zukhruf/nx/test gives each project that has node:test files a test target, a node --test run.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,7 +20,9 @@
20
20
  "monorepo",
21
21
  "prettier",
22
22
  "format",
23
- "check"
23
+ "check",
24
+ "node-test",
25
+ "test"
24
26
  ],
25
27
  "type": "module",
26
28
  "exports": {
@@ -30,6 +32,12 @@
30
32
  "types": "./dist/format/plugin.d.ts",
31
33
  "import": "./dist/format/plugin.js",
32
34
  "default": "./dist/format/plugin.js"
35
+ },
36
+ "./test": {
37
+ "@zukhruf/source": "./src/test/plugin.ts",
38
+ "types": "./dist/test/plugin.d.ts",
39
+ "import": "./dist/test/plugin.js",
40
+ "default": "./dist/test/plugin.js"
33
41
  }
34
42
  },
35
43
  "files": [
@@ -42,11 +50,11 @@
42
50
  "access": "public"
43
51
  },
44
52
  "peerDependencies": {
45
- "@nx/devkit": ">=23.0.0 <25.0.0",
46
- "nx": ">=23.0.0 <25.0.0",
53
+ "@nx/devkit": ">=23.2.1 <25.0.0",
54
+ "nx": ">=23.2.1 <25.0.0",
47
55
  "prettier": "^3.0.0"
48
56
  },
49
57
  "devDependencies": {
50
- "@zukhruf/testing": "^0.3.57"
58
+ "@zukhruf/testing": "^0.3.58"
51
59
  }
52
60
  }