@zukhruf/nx 0.0.0-stage → 0.3.57

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ezzabuzaid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,133 @@
1
- # Temporary Holding Version
1
+ # @zukhruf/nx
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ This package is the Nx part of the monorepo standard. Each workspace copied the same Nx configuration into each project. This package gives that configuration one time, in areas. Each area has its own import path, and the package has no root entry point. Lint stays in `@zukhruf/eslint`, and the Git hooks stay in `@zukhruf/git-hooks`.
4
+
5
+ The words in these documents have one meaning each. See the glossary in [CONTEXT.md](./CONTEXT.md).
6
+
7
+ ## Areas
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 |
14
+
15
+ ## format
16
+
17
+ An Nx workspace formats its files with Prettier. The `format` area is an Nx inference plugin. It gives each project of the workspace two targets: `format`, which writes the files, and `format:check`, which only reads them. You list the plugin one time in `nx.json`. You do not copy targets into each project.
18
+
19
+ ## Use it
20
+
21
+ You need Nx 23 or 24 and Prettier 3. `nx`, `@nx/devkit` and `prettier` are peer dependencies.
22
+
23
+ Add the plugin to `plugins` in `nx.json`. Then add `format:check` to the `dependsOn` of `typecheck` that the workspace already has:
24
+
25
+ ```jsonc
26
+ {
27
+ "plugins": ["@zukhruf/nx/format"],
28
+ "targetDefaults": {
29
+ "typecheck": {
30
+ "dependsOn": ["lint", "format:check", "..."],
31
+ },
32
+ },
33
+ }
34
+ ```
35
+
36
+ Keep the entries that are already in the list. A `dependsOn` array replaces the array of an earlier source, and only `"..."` keeps the dependencies that `@nx/js/typescript` gives `typecheck`.
37
+
38
+ zukhruf builds this package, so it lists the plugin by path: `"plugins": ["./packages/nx/src/format/plugin.ts"]`. The package name resolves to `dist/`, and a fresh clone has no build before Nx makes the project graph.
39
+
40
+ The plugin does not change `typecheck`. That target belongs to `@nx/js/typescript`, and each workspace adds its own dependencies to it, for example `^sdk`. See [ADR 0001](./docs/adr/0001-inferred-targets-not-copied-defaults.md).
41
+
42
+ ## Signature
43
+
44
+ ```ts
45
+ // @zukhruf/nx/format
46
+ export const createNodes: CreateNodes;
47
+ ```
48
+
49
+ The plugin takes no options. The target names and the commands are the same in each of the 7 copies that this package replaces, so no option is necessary.
50
+
51
+ ## What each target runs
52
+
53
+ | Target | Command | Writes? | Cached | Inputs |
54
+ | -------------- | -------------------------------- | ------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
55
+ | `format` | `prettier --write {projectRoot}` | yes | no | — |
56
+ | `format:check` | `prettier --check {projectRoot}` | no | yes | the files of the project; the root `.prettierignore` and `.editorconfig`; the config file that Prettier finds from the workspace root; `prettier` and each Prettier plugin that the config names (`externalDependencies`) |
57
+
58
+ - `format` writes. It runs only when you run it on purpose: `nx run <project>:format`. No target depends on it.
59
+ - `format:check` only reads. It fails when a file is not formatted.
60
+ - A project with no file that Prettier can format passes `format:check`. Prettier 3.9.9 passes a folder whose files are all ignored or of an unknown type. It stops with an error only for a folder with no file, and a project root always has a `project.json` or a `package.json`. Thus the command has no `--no-error-on-unmatched-pattern`.
61
+ - The plugin finds the config file with `resolveConfigFile` of Prettier, as the Prettier CLI does. It can be `.prettierrc`, `prettier.config.mjs` or the `prettier` key of the root `package.json`. That file is an input, so a change to it runs the check again. The Prettier CLI also reads `.editorconfig`, so that file is an input too. When Prettier finds no config file, no config file is an input.
62
+
63
+ To format only your own files, give their paths: `npx prettier --write <files>`. See [ADR 0002](./docs/adr/0002-a-check-never-writes.md).
64
+
65
+ ## Override a project
66
+
67
+ A target in the `project.json` of a project replaces the inferred target. Nx applies the plugin first, then `targetDefaults`, then `project.json`. For example, the `infra` project of whatisgoingon formats OpenTofu files:
68
+
69
+ ```json
70
+ {
71
+ "targets": {
72
+ "format": { "command": "tofu fmt -recursive" },
73
+ "format:check": { "command": "tofu fmt -check -recursive" }
74
+ }
75
+ }
76
+ ```
77
+
78
+ ## Leave fixtures out
79
+
80
+ The plugin gives targets only to the projects that Nx finds, by the rule and the matcher of Nx itself:
81
+
82
+ - Each folder with a `project.json` is a project.
83
+ - A folder with a `package.json` is a project only when the workspace globs of the package manager match its path, or when it has a `project.json` too. The globs are `workspaces` in the root `package.json`, `pnpm-workspace.yaml` and `lerna.json`, with their `!` negations.
84
+ - The workspace root is a project only when it has a `project.json`, or its `package.json` has an `nx` key.
85
+
86
+ Thus a fixture outside the workspace globs gets no targets, and you do not add it to `exclude`. The root files stay checked by the `Format` step of `@zukhruf/git-hooks` and by `npx prettier --check .` in CI.
87
+
88
+ A fixture inside the workspace globs is a project of Nx. Use the standard `exclude` of an Nx plugin entry to give it no format targets:
89
+
90
+ ```json
91
+ {
92
+ "plugins": [
93
+ {
94
+ "plugin": "@zukhruf/nx/format",
95
+ "exclude": ["packages/connectors/manifest/src/fixtures/**"]
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ context-compiler already does this for `@nx/eslint/plugin`. See [ADR 0003](./docs/adr/0003-which-roots-get-targets.md).
102
+
103
+ ## Migrate a workspace
104
+
105
+ 1. Add the plugin to `plugins` in `nx.json`.
106
+ 2. Delete the `format` and `format:check` entries from `targetDefaults`.
107
+ 3. Delete each opt-in: each `"format": {}` and each copied `format` or `format:check` target in a `project.json` or in `nx.targets` of a `package.json`. Keep a target only when it is an override.
108
+ 4. Delete `"format"` from `lint.dependsOn` and from `typecheck.dependsOn`.
109
+ 5. Add `"format:check"` to `typecheck.dependsOn`.
110
+
111
+ | Workspace | Today | Opt-ins to delete |
112
+ | ---------------- | -------------------------------------------------------------------------------------------- | ---------------------------------- |
113
+ | zukhruf | targetDefault `format` = `--write`; `lint` depends on `format` | 32 `"format": {}` |
114
+ | elt | same as zukhruf | 69 |
115
+ | context-compiler | same as zukhruf | 27 |
116
+ | text2sql | a full `format` target in each project; `typecheck` depends on `format` | 75 |
117
+ | factory | targetDefaults `format` and `format:check`; `typecheck` depends on `lint` and `format:check` | 9 `nx.targets` in `package.json` |
118
+ | whatisgoingon | same as factory; `infra` overrides `format` with `tofu fmt` | 3, and `infra` adds `format:check` |
119
+ | deepagents | one stray `format` target in `packages/compaction` | 1 |
120
+
121
+ ## Who uses it
122
+
123
+ | Workspace | Backlog item |
124
+ | ---------------- | ------------------------------- |
125
+ | zukhruf | #3306 (phase 2, first consumer) |
126
+ | elt | #3318 |
127
+ | context-compiler | #3319 |
128
+ | factory | #3320 |
129
+ | whatisgoingon | #3321 |
130
+ | text2sql | #3322 |
131
+ | deepagents | #3323 |
132
+
133
+ The umbrella item is #3317.
@@ -0,0 +1,12 @@
1
+ import { type CreateNodes } from '@nx/devkit';
2
+ /**
3
+ * Gives each Nx project `format` (writes) and `format:check` (only reads,
4
+ * cached). Any root that a plugin returns becomes a project, and no plugin
5
+ * hook receives the projects that Nx found. So the plugin selects roots with
6
+ * the rule and the matcher of Nx's own package.json plugin: each folder with a
7
+ * `project.json`, and a `package.json` folder only when the package-manager
8
+ * workspace globs match it. It returns targets only; the default plugins name
9
+ * the project.
10
+ */
11
+ export declare const createNodes: CreateNodes;
12
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/format/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,YAAY,CAAC;AAOpB;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,EAAE,WA4CzB,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { basename, dirname, isAbsolute, join, relative } from 'node:path';
2
+ import { createNodesFromFiles, readJsonFile, } from '@nx/devkit';
3
+ import { buildPackageJsonPatterns, buildPackageJsonWorkspacesMatcher, } from '@nx/devkit/internal';
4
+ import { resolveConfig, resolveConfigFile } from 'prettier';
5
+ /**
6
+ * Gives each Nx project `format` (writes) and `format:check` (only reads,
7
+ * cached). Any root that a plugin returns becomes a project, and no plugin
8
+ * hook receives the projects that Nx found. So the plugin selects roots with
9
+ * the rule and the matcher of Nx's own package.json plugin: each folder with a
10
+ * `project.json`, and a `package.json` folder only when the package-manager
11
+ * workspace globs match it. It returns targets only; the default plugins name
12
+ * the project.
13
+ */
14
+ export const createNodes = [
15
+ '{package.json,**/package.json,project.json,**/project.json}',
16
+ async (configFiles, options, context) => {
17
+ const projectJsonRoots = new Set(configFiles
18
+ .filter((file) => basename(file) === 'project.json')
19
+ .map((file) => dirname(file)));
20
+ const isInWorkspaces = buildPackageJsonWorkspacesMatcher(buildPackageJsonPatterns(context.workspaceRoot, (file) => readJsonFile(join(context.workspaceRoot, file))));
21
+ const rootFiles = configFiles.filter((file) => basename(file) === 'project.json'
22
+ ? true
23
+ : !projectJsonRoots.has(dirname(file)) && isInWorkspaces(file));
24
+ const formatCheck = {
25
+ command: 'prettier --check {projectRoot}',
26
+ cache: true,
27
+ inputs: [
28
+ 'default',
29
+ '{workspaceRoot}/.prettierignore',
30
+ '{workspaceRoot}/.editorconfig',
31
+ ...(await prettierConfigInputs(context.workspaceRoot)),
32
+ ],
33
+ };
34
+ return createNodesFromFiles((file) => ({
35
+ projects: {
36
+ [dirname(file)]: {
37
+ targets: {
38
+ format: { command: 'prettier --write {projectRoot}' },
39
+ 'format:check': formatCheck,
40
+ },
41
+ },
42
+ },
43
+ }), rootFiles, options, context);
44
+ },
45
+ ];
46
+ async function prettierConfigInputs(workspaceRoot) {
47
+ // `resolveConfigFile` gives null when no config file exists
48
+ // (prettier/index.d.ts:661).
49
+ const file = await resolveConfigFile(join(workspaceRoot, 'package.json'));
50
+ if (file === null) {
51
+ return [{ externalDependencies: ['prettier'] }];
52
+ }
53
+ // `resolveConfig` is typed as nullable (prettier/index.d.ts:649-652), and
54
+ // `Options.plugins` is optional (prettier/index.d.ts:807).
55
+ const plugins = (await resolveConfig(file, { config: file }))?.plugins ?? [];
56
+ return [
57
+ `{workspaceRoot}/${relative(workspaceRoot, file)}`,
58
+ {
59
+ externalDependencies: [
60
+ 'prettier',
61
+ ...plugins
62
+ .filter((plugin) => typeof plugin === 'string' &&
63
+ !plugin.startsWith('.') &&
64
+ !isAbsolute(plugin))
65
+ .map((plugin) => plugin
66
+ .split('/')
67
+ .slice(0, plugin.startsWith('@') ? 2 : 1)
68
+ .join('/')),
69
+ ],
70
+ },
71
+ ];
72
+ }
73
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/format/plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "@zukhruf/nx",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
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.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/JanuaryLabs/zukhruf",
9
+ "directory": "packages/nx"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/JanuaryLabs/zukhruf/issues"
13
+ },
14
+ "homepage": "https://github.com/JanuaryLabs/zukhruf/tree/main/packages/nx#readme",
15
+ "keywords": [
16
+ "nx",
17
+ "nx-plugin",
18
+ "inference",
19
+ "createNodes",
20
+ "monorepo",
21
+ "prettier",
22
+ "format",
23
+ "check"
24
+ ],
25
+ "type": "module",
26
+ "exports": {
27
+ "./package.json": "./package.json",
28
+ "./format": {
29
+ "@zukhruf/source": "./src/format/plugin.ts",
30
+ "types": "./dist/format/plugin.d.ts",
31
+ "import": "./dist/format/plugin.js",
32
+ "default": "./dist/format/plugin.js"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "!dist/spec/**",
38
+ "!**/*.tsbuildinfo",
39
+ "!**/*.test.*"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "peerDependencies": {
45
+ "@nx/devkit": ">=23.0.0 <25.0.0",
46
+ "nx": ">=23.0.0 <25.0.0",
47
+ "prettier": "^3.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@zukhruf/testing": "^0.3.57"
51
+ }
52
+ }