nx 23.2.0-beta.8 → 23.2.0-beta.9
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/src/ai/set-up-ai-agents/set-up-ai-agents.js +2 -2
- package/dist/src/command-line/format/format.js +84 -101
- package/dist/src/command-line/import/import.js +12 -0
- package/dist/src/command-line/init/configure-plugins.js +4 -1
- package/dist/src/command-line/init/implementation/add-nx-to-nest.js +9 -0
- package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +3 -17
- package/dist/src/command-line/init/implementation/angular/index.js +4 -0
- package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +2 -0
- package/dist/src/command-line/init/implementation/angular/standalone-workspace.js +8 -2
- package/dist/src/command-line/init/implementation/check-compatible-with-plugins.js +2 -0
- package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +5 -0
- package/dist/src/command-line/init/implementation/format.d.ts +18 -0
- package/dist/src/command-line/init/implementation/format.js +114 -0
- package/dist/src/command-line/init/implementation/utils.js +8 -0
- package/dist/src/command-line/init/init-v1.js +7 -0
- package/dist/src/command-line/init/init-v2.js +11 -0
- package/dist/src/command-line/migrate/migrate.d.ts +4 -0
- package/dist/src/command-line/migrate/migrate.js +64 -24
- package/dist/src/command-line/release/version.js +2 -2
- package/dist/src/daemon/client/client.js +11 -2
- package/dist/src/devkit-internals.d.ts +5 -2
- package/dist/src/devkit-internals.js +14 -6
- package/dist/src/generators/internal-utils/{format-changed-files-with-prettier-if-available.d.ts → format-changed-files.d.ts} +6 -6
- package/dist/src/generators/internal-utils/format-changed-files.js +163 -0
- package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.d.ts +15 -0
- package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.js +21 -3
- package/dist/src/migrations/update-15-0-0/prefix-outputs.js +2 -2
- package/dist/src/migrations/update-16-0-0/remove-nrwl-cli.js +2 -2
- package/dist/src/migrations/update-16-0-0/update-depends-on-to-tokens.js +2 -2
- package/dist/src/migrations/update-16-0-0/update-nx-cloud-runner.js +2 -2
- package/dist/src/migrations/update-16-2-0/remove-run-commands-output-path.js +2 -2
- package/dist/src/migrations/update-17-0-0/rm-default-collection-npm-scope.js +2 -2
- package/dist/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +2 -2
- package/dist/src/migrations/update-17-2-0/move-default-base.js +2 -2
- package/dist/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.js +2 -2
- package/dist/src/migrations/update-19-2-4/set-project-name.js +2 -2
- package/dist/src/migrations/update-20-0-0/move-use-daemon-process.js +2 -2
- package/dist/src/migrations/update-20-0-1/use-legacy-cache.js +2 -2
- package/dist/src/migrations/update-21-0-0/release-changelog-config-changes.js +2 -2
- package/dist/src/migrations/update-21-0-0/release-version-config-changes.js +2 -2
- package/dist/src/migrations/update-21-0-0/remove-custom-tasks-runner.js +2 -2
- package/dist/src/migrations/update-21-0-0/remove-legacy-cache.js +2 -2
- package/dist/src/migrations/update-22-0-0/consolidate-release-tag-config.js +2 -2
- package/dist/src/migrations/update-22-0-0/release-version-config-changes.js +2 -2
- package/dist/src/migrations/update-22-2-0/add-self-healing-to-gitignore.js +2 -2
- package/dist/src/migrations/update-22-6-0/add-claude-settings-local-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-22-6-0/add-claude-worktrees-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-22-7-0/add-polygraph-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +2 -2
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +3 -3
- package/dist/src/utils/chunkify.d.ts +7 -1
- package/dist/src/utils/chunkify.js +10 -3
- package/dist/src/utils/formatters/index.d.ts +13 -0
- package/dist/src/utils/formatters/index.js +74 -0
- package/dist/src/utils/formatters/oxfmt.d.ts +32 -0
- package/dist/src/utils/formatters/oxfmt.js +764 -0
- package/dist/src/utils/formatters/prettier.d.ts +26 -0
- package/dist/src/utils/formatters/prettier.js +201 -0
- package/dist/src/utils/formatters/shared.d.ts +5 -0
- package/dist/src/utils/formatters/shared.js +8 -0
- package/dist/src/utils/ignore.d.ts +47 -5
- package/dist/src/utils/ignore.js +93 -11
- package/dist/src/utils/write-formatted-json-file.d.ts +3 -2
- package/dist/src/utils/write-formatted-json-file.js +5 -4
- package/package.json +11 -11
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +0 -88
- package/dist/src/utils/is-using-prettier.d.ts +0 -3
- package/dist/src/utils/is-using-prettier.js +0 -62
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Tree } from '../../generators/tree';
|
|
2
|
+
/**
|
|
3
|
+
* Config filenames prettier discovers. Exported because generator setup must
|
|
4
|
+
* agree with detection - a format missing from one side gets a second,
|
|
5
|
+
* redundant config written next to it.
|
|
6
|
+
* https://prettier.io/docs/configuration
|
|
7
|
+
*/
|
|
8
|
+
export declare const prettierConfigFiles: string[];
|
|
9
|
+
export declare function isUsingPrettier(root: string): boolean;
|
|
10
|
+
export declare function isUsingPrettierInTree(tree: Tree): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Keeps only the files prettier can format. oxfmt needs no equivalent - it
|
|
13
|
+
* silently skips file types it does not handle.
|
|
14
|
+
*/
|
|
15
|
+
export declare function filterToPrettierSupportedFiles(files: string[]): Promise<string[]>;
|
|
16
|
+
export declare function writeWithPrettier(patterns: string[], cwd?: string): void;
|
|
17
|
+
export declare function checkWithPrettier(patterns: string[]): Promise<string[]>;
|
|
18
|
+
export declare function getPrettierPath(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Quote a pattern for prettier's shell-based exec calls; oxfmt uses execFile
|
|
21
|
+
* and takes raw paths.
|
|
22
|
+
*
|
|
23
|
+
* Exported so `nx format` can size its chunks against the quoted length -
|
|
24
|
+
* patterns are chunked before they get here, and quoting grows each one.
|
|
25
|
+
*/
|
|
26
|
+
export declare function quoteForShell(pattern: string): string;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prettierConfigFiles = void 0;
|
|
4
|
+
exports.isUsingPrettier = isUsingPrettier;
|
|
5
|
+
exports.isUsingPrettierInTree = isUsingPrettierInTree;
|
|
6
|
+
exports.filterToPrettierSupportedFiles = filterToPrettierSupportedFiles;
|
|
7
|
+
exports.writeWithPrettier = writeWithPrettier;
|
|
8
|
+
exports.checkWithPrettier = checkWithPrettier;
|
|
9
|
+
exports.getPrettierPath = getPrettierPath;
|
|
10
|
+
exports.quoteForShell = quoteForShell;
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const semver_1 = require("semver");
|
|
15
|
+
const json_1 = require("../../generators/utils/json");
|
|
16
|
+
const fileutils_1 = require("../fileutils");
|
|
17
|
+
const handle_import_1 = require("../handle-import");
|
|
18
|
+
const package_json_1 = require("../package-json");
|
|
19
|
+
const shared_1 = require("./shared");
|
|
20
|
+
/**
|
|
21
|
+
* Config filenames prettier discovers. Exported because generator setup must
|
|
22
|
+
* agree with detection - a format missing from one side gets a second,
|
|
23
|
+
* redundant config written next to it.
|
|
24
|
+
* https://prettier.io/docs/configuration
|
|
25
|
+
*/
|
|
26
|
+
exports.prettierConfigFiles = [
|
|
27
|
+
'.prettierrc',
|
|
28
|
+
'.prettierrc.json',
|
|
29
|
+
'.prettierrc.yml',
|
|
30
|
+
'.prettierrc.yaml',
|
|
31
|
+
'.prettierrc.json5',
|
|
32
|
+
'.prettierrc.js',
|
|
33
|
+
'prettier.config.js',
|
|
34
|
+
'.prettierrc.ts',
|
|
35
|
+
'prettier.config.ts',
|
|
36
|
+
'.prettierrc.mjs',
|
|
37
|
+
'prettier.config.mjs',
|
|
38
|
+
'.prettierrc.mts',
|
|
39
|
+
'prettier.config.mts',
|
|
40
|
+
'.prettierrc.cjs',
|
|
41
|
+
'prettier.config.cjs',
|
|
42
|
+
'.prettierrc.cts',
|
|
43
|
+
'prettier.config.cts',
|
|
44
|
+
'.prettierrc.toml',
|
|
45
|
+
];
|
|
46
|
+
function isUsingPrettier(root) {
|
|
47
|
+
for (const file of exports.prettierConfigFiles) {
|
|
48
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, file))) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Even if no file is present, it is possible the user is configuring prettier via their package.json
|
|
53
|
+
const packageJsonPath = (0, node_path_1.join)(root, 'package.json');
|
|
54
|
+
if ((0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
55
|
+
const packageJson = (0, fileutils_1.readJsonFile)(packageJsonPath);
|
|
56
|
+
if (packageJson.prettier) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
function isUsingPrettierInTree(tree) {
|
|
63
|
+
for (const file of exports.prettierConfigFiles) {
|
|
64
|
+
if (tree.exists(file)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Even if no file is present, it is possible the user is configuring prettier via their package.json
|
|
69
|
+
if (tree.exists('package.json')) {
|
|
70
|
+
const packageJson = (0, json_1.readJson)(tree, 'package.json');
|
|
71
|
+
if (packageJson.prettier) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Keeps only the files prettier can format. oxfmt needs no equivalent - it
|
|
79
|
+
* silently skips file types it does not handle.
|
|
80
|
+
*/
|
|
81
|
+
async function filterToPrettierSupportedFiles(files) {
|
|
82
|
+
const prettier = await (0, handle_import_1.handleImport)('prettier');
|
|
83
|
+
const supportInfo = await prettier.getSupportInfo();
|
|
84
|
+
const supportedExtensions = new Set(supportInfo.languages
|
|
85
|
+
.flatMap((language) => language.extensions)
|
|
86
|
+
.filter((extension) => !!extension));
|
|
87
|
+
// Prettier matches ~30 files by *name* rather than extension (`.swcrc`,
|
|
88
|
+
// `Jakefile`). It publishes that list next to the extensions, so read it
|
|
89
|
+
// rather than hardcoding.
|
|
90
|
+
const supportedFilenames = new Set(supportInfo.languages.flatMap((language) => language.filenames ?? []));
|
|
91
|
+
return files.filter((file) => supportedExtensions.has((0, node_path_1.extname)(file)) ||
|
|
92
|
+
supportedFilenames.has((0, node_path_1.basename)(file)));
|
|
93
|
+
}
|
|
94
|
+
function writeWithPrettier(patterns,
|
|
95
|
+
// Defaults to the caller's cwd. `nx init` sets it so it can pass paths
|
|
96
|
+
// relative to the repo root rather than absolute ones the user has to read.
|
|
97
|
+
cwd) {
|
|
98
|
+
if (patterns.length === 0) {
|
|
99
|
+
// Prettier with no file arguments reads stdin, and `stdio: [0, 1, 2]` hands
|
|
100
|
+
// it nx's own - from a terminal it blocks forever with nothing on screen. At
|
|
101
|
+
// EOF it exits 0 under `--list-different` and 2 (which `execSync` throws on)
|
|
102
|
+
// without it, so which failure you get depends on `shouldUseListDifferent`.
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const prettierPath = getPrettierPath();
|
|
106
|
+
const listDifferentArg = shouldUseListDifferent() ? '--list-different ' : '';
|
|
107
|
+
// No `--parser json` special case for `.swcrc`: prettier's own language table
|
|
108
|
+
// maps it to the json parser, so it formats correctly on its own (measured).
|
|
109
|
+
// Splitting the batch also meant one of the two spawns could receive zero
|
|
110
|
+
// files, which is the error above.
|
|
111
|
+
(0, node_child_process_1.execSync)(`node ${quoteForShell(prettierPath)} --write ${listDifferentArg}-- ${patterns
|
|
112
|
+
.map(quoteForShell)
|
|
113
|
+
.join(' ')}`, {
|
|
114
|
+
cwd,
|
|
115
|
+
stdio: [0, 1, 2],
|
|
116
|
+
windowsHide: true,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function checkWithPrettier(patterns) {
|
|
120
|
+
const prettierPath = getPrettierPath();
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
(0, node_child_process_1.exec)(`node ${quoteForShell(prettierPath)} --list-different -- ${patterns
|
|
123
|
+
.map(quoteForShell)
|
|
124
|
+
.join(' ')}`, { encoding: 'utf-8', windowsHide: true, maxBuffer: shared_1.FORMATTER_MAX_BUFFER }, (error, stdout) => {
|
|
125
|
+
if (error) {
|
|
126
|
+
// As the oxfmt sibling: a spawn failure, kill or maxBuffer overrun reports a
|
|
127
|
+
// string `code` or none. Prettier signals "files differ" numerically, so
|
|
128
|
+
// treating these as a file list would pass `format:check` on a formatter
|
|
129
|
+
// that never ran.
|
|
130
|
+
if (typeof error['code'] !== 'number') {
|
|
131
|
+
reject(new Error(`prettier could not be run to completion (${error['code'] ?? error.signal ?? 'unknown'}): ${error.message}`));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Only `Mismatch` means "files differ", as in `checkWithOxfmt`. `Failure`
|
|
135
|
+
// still prints the files prettier got through - measured, one differing plus
|
|
136
|
+
// one unparseable exits 2 with the differing file on stdout - so reading
|
|
137
|
+
// stdout there would swallow the syntax error.
|
|
138
|
+
if (error['code'] !== 1 /* PrettierExitCode.Mismatch */ ||
|
|
139
|
+
stdout.length === 0) {
|
|
140
|
+
reject(error);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
resolve(stdout.trim().split('\n'));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
resolve([]);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
let prettierPath;
|
|
152
|
+
function getPrettierPath() {
|
|
153
|
+
if (prettierPath) {
|
|
154
|
+
return prettierPath;
|
|
155
|
+
}
|
|
156
|
+
const { packageJson, path: packageJsonPath } = (0, package_json_1.readModulePackageJson)('prettier');
|
|
157
|
+
const bin = packageJson.bin;
|
|
158
|
+
const binPath = typeof bin === 'string' ? bin : bin?.['prettier'];
|
|
159
|
+
if (!binPath) {
|
|
160
|
+
throw new Error(`Could not find prettier binary in ${packageJsonPath}`);
|
|
161
|
+
}
|
|
162
|
+
prettierPath = (0, node_path_1.resolve)((0, node_path_1.dirname)(packageJsonPath), binPath);
|
|
163
|
+
return prettierPath;
|
|
164
|
+
}
|
|
165
|
+
let useListDifferent;
|
|
166
|
+
/**
|
|
167
|
+
* Determines if --list-different should be used with --write.
|
|
168
|
+
* Prettier 4+ and 3.6.x with experimental CLI don't support combining these flags.
|
|
169
|
+
*/
|
|
170
|
+
function shouldUseListDifferent() {
|
|
171
|
+
if (useListDifferent !== undefined) {
|
|
172
|
+
return useListDifferent;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const { packageJson } = (0, package_json_1.readModulePackageJson)('prettier');
|
|
176
|
+
const prettierMajor = (0, semver_1.major)(packageJson.version);
|
|
177
|
+
const isExperimentalCli = process.env.PRETTIER_EXPERIMENTAL_CLI === '1';
|
|
178
|
+
useListDifferent = prettierMajor < 4 && !isExperimentalCli;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
useListDifferent = false;
|
|
182
|
+
}
|
|
183
|
+
return useListDifferent;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Quote a pattern for prettier's shell-based exec calls; oxfmt uses execFile
|
|
187
|
+
* and takes raw paths.
|
|
188
|
+
*
|
|
189
|
+
* Exported so `nx format` can size its chunks against the quoted length -
|
|
190
|
+
* patterns are chunked before they get here, and quoting grows each one.
|
|
191
|
+
*/
|
|
192
|
+
function quoteForShell(pattern) {
|
|
193
|
+
// Interpolated into a command string, so every character special *inside
|
|
194
|
+
// double quotes* needs escaping, not just `$`: a backtick substitutes, `"`
|
|
195
|
+
// closes the quoting, `\` escapes what follows. One pass over the original,
|
|
196
|
+
// since `String.replace` never re-scans what it inserted.
|
|
197
|
+
const escaped = process.platform !== 'win32'
|
|
198
|
+
? pattern.replace(/([\\"`$])/g, '\\$1')
|
|
199
|
+
: pattern;
|
|
200
|
+
return `"${escaped}"`;
|
|
201
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FORMATTER_MAX_BUFFER = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Formatters list every mismatching path on stdout, which overruns Node's 1MB
|
|
6
|
+
* default on a large workspace.
|
|
7
|
+
*/
|
|
8
|
+
exports.FORMATTER_MAX_BUFFER = 50 * 1024 * 1024;
|
|
@@ -62,7 +62,7 @@ export declare function createIgnoreChainResolver(read: (path: string) => string
|
|
|
62
62
|
* that same note. Here they are simply the entry's matchers: any one excluding
|
|
63
63
|
* wins, and a negation counts only if none excluded.
|
|
64
64
|
*
|
|
65
|
-
* Two preconditions,
|
|
65
|
+
* Two preconditions, neither enforced here:
|
|
66
66
|
*
|
|
67
67
|
* - `filePath` is workspace-relative POSIX and must sit under every `dir` in the
|
|
68
68
|
* chain, which holds when the chain came from that file's own directory.
|
|
@@ -70,11 +70,28 @@ export declare function createIgnoreChainResolver(read: (path: string) => string
|
|
|
70
70
|
* re-include a file inside an excluded directory, and this does not implement
|
|
71
71
|
* that rule: asked directly about `dist/keep.ts` with a root `dist/` and a
|
|
72
72
|
* nested `dist/.gitignore` holding `!keep.ts`, it answers "not ignored" where
|
|
73
|
-
* git says ignored (measured).
|
|
74
|
-
*
|
|
75
|
-
*
|
|
73
|
+
* git says ignored (measured). A pruning walk like `visitNotIgnoredFiles`
|
|
74
|
+
* satisfies it by never asking about anything under `dist/`; a per-file
|
|
75
|
+
* caller cannot, so it goes through `createAncestorAwareIgnoreChecker`,
|
|
76
|
+
* which checks the ancestors before asking the chain.
|
|
76
77
|
*/
|
|
77
78
|
export declare function isIgnoredByChain(chain: ScopedIgnoreMatcher[], filePath: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* The chain's answers plus git's excluded-ancestor rule: nothing inside an
|
|
81
|
+
* ignored directory can be re-included, so a nested negation must not
|
|
82
|
+
* resurrect a file whose ancestor an outer file excluded - the case
|
|
83
|
+
* `isIgnoredByChain` alone gets wrong (see its second precondition). The
|
|
84
|
+
* oxfmt CLI follows the same rule (measured against 0.60.0: a scan skips a
|
|
85
|
+
* nested `!keep.ts` under a root-ignored `dist/`), so every per-file caller -
|
|
86
|
+
* the cascading tree checkers and the disk-backed resolver in
|
|
87
|
+
* `formatters/oxfmt.ts` - goes through here.
|
|
88
|
+
*
|
|
89
|
+
* A directory's own ignore files cannot un-ignore the directory itself, so
|
|
90
|
+
* its verdict comes from its parent's chain - probed with a trailing slash,
|
|
91
|
+
* which is what makes a directory-only pattern like `dist/` match. Verdicts
|
|
92
|
+
* are memoized per directory, so a batch of files shares its ancestor walks.
|
|
93
|
+
*/
|
|
94
|
+
export declare function createAncestorAwareIgnoreChecker(resolve: (dir: string) => ScopedIgnoreMatcher[]): TreeIgnoreChecker;
|
|
78
95
|
/**
|
|
79
96
|
* Directories that should never be walked, whatever the workspace's own ignore
|
|
80
97
|
* files say - `node_modules`, `.git`, the nx caches.
|
|
@@ -88,7 +105,7 @@ export declare function isIgnoredByChain(chain: ScopedIgnoreMatcher[], filePath:
|
|
|
88
105
|
*/
|
|
89
106
|
export declare function isAlwaysIgnored(path: string): boolean;
|
|
90
107
|
/**
|
|
91
|
-
*
|
|
108
|
+
* A chain's answers as predicates over workspace-relative POSIX paths.
|
|
92
109
|
*
|
|
93
110
|
* Files and directories are asked separately because the answers differ: a
|
|
94
111
|
* pattern is only directory-only if it ends in a slash, and `ignore` will not
|
|
@@ -123,6 +140,31 @@ export declare function createGitIgnoreChecker(tree: Tree): TreeIgnoreChecker;
|
|
|
123
140
|
* Reads from the tree rather than disk, as above.
|
|
124
141
|
*/
|
|
125
142
|
export declare function createPrettierIgnoreChecker(tree: Tree): TreeIgnoreChecker;
|
|
143
|
+
/**
|
|
144
|
+
* What oxfmt ignores: prettier's two files, but resolved from each file's own
|
|
145
|
+
* directory upwards rather than the workspace root - measured against the
|
|
146
|
+
* oxfmt 0.60.0 CLI, which differs from prettier on exactly that axis. Still
|
|
147
|
+
* one matcher per file rather than merged.
|
|
148
|
+
*
|
|
149
|
+
* A config's `ignorePatterns` is not an ignore file and is not read here;
|
|
150
|
+
* `formatFilesWithOxfmt` applies it rooted at that config's directory.
|
|
151
|
+
*/
|
|
152
|
+
export declare function createOxfmtIgnoreChecker(tree: Tree): TreeIgnoreChecker;
|
|
153
|
+
/**
|
|
154
|
+
* Exported, unlike git's and prettier's, because oxfmt has two consumers:
|
|
155
|
+
* this tree-backed checker and the disk-backed resolver in
|
|
156
|
+
* `formatters/oxfmt.ts`. A shared value is the only thing that keeps them
|
|
157
|
+
* agreeing, so do not restate these three anywhere.
|
|
158
|
+
*
|
|
159
|
+
* `satisfies` rather than an annotation keeps the values literal - an
|
|
160
|
+
* annotation widens `cascade` and `merge` to `boolean` (measured in the
|
|
161
|
+
* declaration emit).
|
|
162
|
+
*/
|
|
163
|
+
export declare const OXFMT_IGNORE_OPTIONS: {
|
|
164
|
+
filenames: string[];
|
|
165
|
+
cascade: true;
|
|
166
|
+
merge: false;
|
|
167
|
+
};
|
|
126
168
|
/**
|
|
127
169
|
* `path.dirname` for the workspace-relative POSIX paths the chain is keyed by,
|
|
128
170
|
* except that the workspace root is `''` rather than `.` - that is the key
|
package/dist/src/utils/ignore.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OXFMT_IGNORE_OPTIONS = void 0;
|
|
3
4
|
exports.getIgnoreObject = getIgnoreObject;
|
|
4
5
|
exports.createIgnoreChainResolver = createIgnoreChainResolver;
|
|
5
6
|
exports.isIgnoredByChain = isIgnoredByChain;
|
|
7
|
+
exports.createAncestorAwareIgnoreChecker = createAncestorAwareIgnoreChecker;
|
|
6
8
|
exports.isAlwaysIgnored = isAlwaysIgnored;
|
|
7
9
|
exports.createGitIgnoreChecker = createGitIgnoreChecker;
|
|
8
10
|
exports.createPrettierIgnoreChecker = createPrettierIgnoreChecker;
|
|
11
|
+
exports.createOxfmtIgnoreChecker = createOxfmtIgnoreChecker;
|
|
9
12
|
exports.posixDirname = posixDirname;
|
|
10
13
|
exports.addEntryToGitIgnore = addEntryToGitIgnore;
|
|
11
14
|
const ignore = require("ignore");
|
|
@@ -93,7 +96,7 @@ function createIgnoreChainResolver(read, filenames, merge) {
|
|
|
93
96
|
* that same note. Here they are simply the entry's matchers: any one excluding
|
|
94
97
|
* wins, and a negation counts only if none excluded.
|
|
95
98
|
*
|
|
96
|
-
* Two preconditions,
|
|
99
|
+
* Two preconditions, neither enforced here:
|
|
97
100
|
*
|
|
98
101
|
* - `filePath` is workspace-relative POSIX and must sit under every `dir` in the
|
|
99
102
|
* chain, which holds when the chain came from that file's own directory.
|
|
@@ -101,9 +104,10 @@ function createIgnoreChainResolver(read, filenames, merge) {
|
|
|
101
104
|
* re-include a file inside an excluded directory, and this does not implement
|
|
102
105
|
* that rule: asked directly about `dist/keep.ts` with a root `dist/` and a
|
|
103
106
|
* nested `dist/.gitignore` holding `!keep.ts`, it answers "not ignored" where
|
|
104
|
-
* git says ignored (measured).
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
+
* git says ignored (measured). A pruning walk like `visitNotIgnoredFiles`
|
|
108
|
+
* satisfies it by never asking about anything under `dist/`; a per-file
|
|
109
|
+
* caller cannot, so it goes through `createAncestorAwareIgnoreChecker`,
|
|
110
|
+
* which checks the ancestors before asking the chain.
|
|
107
111
|
*/
|
|
108
112
|
function isIgnoredByChain(chain, filePath) {
|
|
109
113
|
for (const { dir, matchers } of chain) {
|
|
@@ -122,6 +126,45 @@ function isIgnoredByChain(chain, filePath) {
|
|
|
122
126
|
}
|
|
123
127
|
return false;
|
|
124
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* The chain's answers plus git's excluded-ancestor rule: nothing inside an
|
|
131
|
+
* ignored directory can be re-included, so a nested negation must not
|
|
132
|
+
* resurrect a file whose ancestor an outer file excluded - the case
|
|
133
|
+
* `isIgnoredByChain` alone gets wrong (see its second precondition). The
|
|
134
|
+
* oxfmt CLI follows the same rule (measured against 0.60.0: a scan skips a
|
|
135
|
+
* nested `!keep.ts` under a root-ignored `dist/`), so every per-file caller -
|
|
136
|
+
* the cascading tree checkers and the disk-backed resolver in
|
|
137
|
+
* `formatters/oxfmt.ts` - goes through here.
|
|
138
|
+
*
|
|
139
|
+
* A directory's own ignore files cannot un-ignore the directory itself, so
|
|
140
|
+
* its verdict comes from its parent's chain - probed with a trailing slash,
|
|
141
|
+
* which is what makes a directory-only pattern like `dist/` match. Verdicts
|
|
142
|
+
* are memoized per directory, so a batch of files shares its ancestor walks.
|
|
143
|
+
*/
|
|
144
|
+
function createAncestorAwareIgnoreChecker(resolve) {
|
|
145
|
+
const ignoredDirs = new Map();
|
|
146
|
+
const isIgnoredDirectory = (dir) => {
|
|
147
|
+
if (dir === '') {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const cached = ignoredDirs.get(dir);
|
|
151
|
+
if (cached !== undefined) {
|
|
152
|
+
return cached;
|
|
153
|
+
}
|
|
154
|
+
const parent = posixDirname(dir);
|
|
155
|
+
const ignored = isIgnoredDirectory(parent) ||
|
|
156
|
+
isIgnoredByChain(resolve(parent), `${dir}/`);
|
|
157
|
+
ignoredDirs.set(dir, ignored);
|
|
158
|
+
return ignored;
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
isIgnoredFile: (filePath) => {
|
|
162
|
+
const dir = posixDirname(filePath);
|
|
163
|
+
return (isIgnoredDirectory(dir) || isIgnoredByChain(resolve(dir), filePath));
|
|
164
|
+
},
|
|
165
|
+
isIgnoredDirectory,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
125
168
|
let alwaysIgnored;
|
|
126
169
|
/**
|
|
127
170
|
* Directories that should never be walked, whatever the workspace's own ignore
|
|
@@ -174,16 +217,55 @@ function createPrettierIgnoreChecker(tree) {
|
|
|
174
217
|
merge: false,
|
|
175
218
|
});
|
|
176
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* What oxfmt ignores: prettier's two files, but resolved from each file's own
|
|
222
|
+
* directory upwards rather than the workspace root - measured against the
|
|
223
|
+
* oxfmt 0.60.0 CLI, which differs from prettier on exactly that axis. Still
|
|
224
|
+
* one matcher per file rather than merged.
|
|
225
|
+
*
|
|
226
|
+
* A config's `ignorePatterns` is not an ignore file and is not read here;
|
|
227
|
+
* `formatFilesWithOxfmt` applies it rooted at that config's directory.
|
|
228
|
+
*/
|
|
229
|
+
function createOxfmtIgnoreChecker(tree) {
|
|
230
|
+
return createTreeIgnoreChecker(tree, exports.OXFMT_IGNORE_OPTIONS);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Exported, unlike git's and prettier's, because oxfmt has two consumers:
|
|
234
|
+
* this tree-backed checker and the disk-backed resolver in
|
|
235
|
+
* `formatters/oxfmt.ts`. A shared value is the only thing that keeps them
|
|
236
|
+
* agreeing, so do not restate these three anywhere.
|
|
237
|
+
*
|
|
238
|
+
* `satisfies` rather than an annotation keeps the values literal - an
|
|
239
|
+
* annotation widens `cascade` and `merge` to `boolean` (measured in the
|
|
240
|
+
* declaration emit).
|
|
241
|
+
*/
|
|
242
|
+
exports.OXFMT_IGNORE_OPTIONS = {
|
|
243
|
+
filenames: ['.gitignore', '.prettierignore'],
|
|
244
|
+
cascade: true,
|
|
245
|
+
merge: false,
|
|
246
|
+
};
|
|
177
247
|
function createTreeIgnoreChecker(tree, { filenames, cascade, merge }) {
|
|
178
248
|
const resolve = createIgnoreChainResolver((path) => tree.read(path, 'utf-8'), filenames, merge);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
249
|
+
if (cascade) {
|
|
250
|
+
// Cascading resolution and the excluded-ancestor rule travel together:
|
|
251
|
+
// both come from git, and the one cascading tool that is not git - oxfmt -
|
|
252
|
+
// applies both (measured, see `createAncestorAwareIgnoreChecker`).
|
|
253
|
+
const checker = createAncestorAwareIgnoreChecker(resolve);
|
|
254
|
+
return {
|
|
255
|
+
isIgnoredFile: (path) => isAlwaysIgnored(path) || checker.isIgnoredFile(path),
|
|
256
|
+
// The trailing slash is what makes a directory-only pattern like `dist/`
|
|
257
|
+
// match - `ignore` will not match it against the bare `dist`.
|
|
258
|
+
isIgnoredDirectory: (path) => isAlwaysIgnored(`${path}/`) || checker.isIgnoredDirectory(path),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
// Root-only: the whole chain is the root's, so no ancestor holds ignore
|
|
262
|
+
// files of its own, and within one matcher `ignore` itself refuses to
|
|
263
|
+
// re-include under an excluded directory (measured). Nothing is left for an
|
|
264
|
+
// ancestor walk to add.
|
|
265
|
+
const check = (probe) => isAlwaysIgnored(probe) || isIgnoredByChain(resolve(''), probe);
|
|
184
266
|
return {
|
|
185
|
-
isIgnoredFile: (path) => check(path
|
|
186
|
-
isIgnoredDirectory: (path) => check(
|
|
267
|
+
isIgnoredFile: (path) => check(path),
|
|
268
|
+
isIgnoredDirectory: (path) => check(`${path}/`),
|
|
187
269
|
};
|
|
188
270
|
}
|
|
189
271
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type JsonWriteOptions } from './fileutils';
|
|
2
2
|
/**
|
|
3
|
-
* Writes a JSON file, formatting with
|
|
4
|
-
* falling back to standard JSON serialization
|
|
3
|
+
* Writes a JSON file, formatting it with whichever formatter the workspace is
|
|
4
|
+
* configured for, and falling back to standard JSON serialization when there
|
|
5
|
+
* is none or it cannot format the file.
|
|
5
6
|
*/
|
|
6
7
|
export declare function writeFormattedJsonFile<T extends object = object>(filePath: string, content: T, options?: JsonWriteOptions): Promise<void>;
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.writeFormattedJsonFile = writeFormattedJsonFile;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
|
-
const
|
|
5
|
+
const format_changed_files_1 = require("../generators/internal-utils/format-changed-files");
|
|
6
6
|
const json_1 = require("./json");
|
|
7
7
|
const fileutils_1 = require("./fileutils");
|
|
8
8
|
const workspace_root_1 = require("./workspace-root");
|
|
9
9
|
/**
|
|
10
|
-
* Writes a JSON file, formatting with
|
|
11
|
-
* falling back to standard JSON serialization
|
|
10
|
+
* Writes a JSON file, formatting it with whichever formatter the workspace is
|
|
11
|
+
* configured for, and falling back to standard JSON serialization when there
|
|
12
|
+
* is none or it cannot format the file.
|
|
12
13
|
*/
|
|
13
14
|
async function writeFormattedJsonFile(filePath, content, options) {
|
|
14
|
-
const formattedContent = await (0,
|
|
15
|
+
const formattedContent = await (0, format_changed_files_1.formatFileContents)([{ path: filePath, content: (0, json_1.serializeJson)(content) }], workspace_root_1.workspaceRoot, { silent: true });
|
|
15
16
|
if (formattedContent.has(filePath)) {
|
|
16
17
|
(0, node_fs_1.writeFileSync)(filePath, formattedContent.get(filePath), {
|
|
17
18
|
encoding: 'utf-8',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nx",
|
|
3
|
-
"version": "23.2.0-beta.
|
|
3
|
+
"version": "23.2.0-beta.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
|
|
@@ -185,16 +185,16 @@
|
|
|
185
185
|
}
|
|
186
186
|
},
|
|
187
187
|
"optionalDependencies": {
|
|
188
|
-
"@nx/nx-darwin-arm64": "23.2.0-beta.
|
|
189
|
-
"@nx/nx-darwin-x64": "23.2.0-beta.
|
|
190
|
-
"@nx/nx-freebsd-x64": "23.2.0-beta.
|
|
191
|
-
"@nx/nx-linux-arm-gnueabihf": "23.2.0-beta.
|
|
192
|
-
"@nx/nx-linux-arm64-gnu": "23.2.0-beta.
|
|
193
|
-
"@nx/nx-linux-arm64-musl": "23.2.0-beta.
|
|
194
|
-
"@nx/nx-linux-x64-gnu": "23.2.0-beta.
|
|
195
|
-
"@nx/nx-linux-x64-musl": "23.2.0-beta.
|
|
196
|
-
"@nx/nx-win32-arm64-msvc": "23.2.0-beta.
|
|
197
|
-
"@nx/nx-win32-x64-msvc": "23.2.0-beta.
|
|
188
|
+
"@nx/nx-darwin-arm64": "23.2.0-beta.9",
|
|
189
|
+
"@nx/nx-darwin-x64": "23.2.0-beta.9",
|
|
190
|
+
"@nx/nx-freebsd-x64": "23.2.0-beta.9",
|
|
191
|
+
"@nx/nx-linux-arm-gnueabihf": "23.2.0-beta.9",
|
|
192
|
+
"@nx/nx-linux-arm64-gnu": "23.2.0-beta.9",
|
|
193
|
+
"@nx/nx-linux-arm64-musl": "23.2.0-beta.9",
|
|
194
|
+
"@nx/nx-linux-x64-gnu": "23.2.0-beta.9",
|
|
195
|
+
"@nx/nx-linux-x64-musl": "23.2.0-beta.9",
|
|
196
|
+
"@nx/nx-win32-arm64-msvc": "23.2.0-beta.9",
|
|
197
|
+
"@nx/nx-win32-x64-msvc": "23.2.0-beta.9"
|
|
198
198
|
},
|
|
199
199
|
"nx-migrations": {
|
|
200
200
|
"migrations": "./migrations.json",
|
package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatChangedFilesWithPrettierIfAvailable = formatChangedFilesWithPrettierIfAvailable;
|
|
4
|
-
exports.formatFilesWithPrettierIfAvailable = formatFilesWithPrettierIfAvailable;
|
|
5
|
-
const tslib_1 = require("tslib");
|
|
6
|
-
const path = tslib_1.__importStar(require("path"));
|
|
7
|
-
const is_using_prettier_1 = require("../../utils/is-using-prettier");
|
|
8
|
-
const path_1 = require("../../utils/path");
|
|
9
|
-
const installation_directory_1 = require("../../utils/installation-directory");
|
|
10
|
-
/**
|
|
11
|
-
* Formats all the created or updated files using Prettier
|
|
12
|
-
* @param tree - the file system tree
|
|
13
|
-
*
|
|
14
|
-
* @remarks
|
|
15
|
-
* Set the environment variable `NX_SKIP_FORMAT` to `true` to skip Prettier
|
|
16
|
-
* formatting. This is useful for repositories that use alternative formatters
|
|
17
|
-
* like Biome, dprint, or have custom formatting requirements.
|
|
18
|
-
*/
|
|
19
|
-
async function formatChangedFilesWithPrettierIfAvailable(tree, options) {
|
|
20
|
-
if (process.env.NX_SKIP_FORMAT === 'true') {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const excludedPaths = options?.excludePaths
|
|
24
|
-
? new Set(Array.from(options.excludePaths, path_1.normalizePath))
|
|
25
|
-
: undefined;
|
|
26
|
-
const files = new Set(tree
|
|
27
|
-
.listChanges()
|
|
28
|
-
.filter((file) => file.type !== 'DELETE' &&
|
|
29
|
-
!excludedPaths?.has((0, path_1.normalizePath)(file.path))));
|
|
30
|
-
const results = await formatFilesWithPrettierIfAvailable(Array.from(files), tree.root, options);
|
|
31
|
-
for (const [path, content] of results) {
|
|
32
|
-
tree.write(path, content);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function formatFilesWithPrettierIfAvailable(files, root, options) {
|
|
36
|
-
const results = new Map();
|
|
37
|
-
// Check here as well for direct callers of this function
|
|
38
|
-
if (process.env.NX_SKIP_FORMAT === 'true' || files.length === 0) {
|
|
39
|
-
return results;
|
|
40
|
-
}
|
|
41
|
-
let prettier;
|
|
42
|
-
try {
|
|
43
|
-
const prettierPath = require.resolve('prettier', {
|
|
44
|
-
paths: [...(0, installation_directory_1.getNxRequirePaths)(root), __dirname],
|
|
45
|
-
});
|
|
46
|
-
prettier = require(prettierPath);
|
|
47
|
-
/**
|
|
48
|
-
* Even after we discovered prettier in node_modules, we need to be sure that the user is intentionally using prettier
|
|
49
|
-
* before proceeding to format with it.
|
|
50
|
-
*/
|
|
51
|
-
if (!(0, is_using_prettier_1.isUsingPrettier)(root)) {
|
|
52
|
-
return results;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch { }
|
|
56
|
-
if (!prettier) {
|
|
57
|
-
return results;
|
|
58
|
-
}
|
|
59
|
-
await Promise.all(Array.from(files).map(async (file) => {
|
|
60
|
-
try {
|
|
61
|
-
const systemPath = path.join(root, file.path);
|
|
62
|
-
let options = {
|
|
63
|
-
filepath: systemPath,
|
|
64
|
-
};
|
|
65
|
-
const resolvedOptions = await prettier.resolveConfig(systemPath, {
|
|
66
|
-
editorconfig: true,
|
|
67
|
-
});
|
|
68
|
-
if (!resolvedOptions) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
options = {
|
|
72
|
-
...options,
|
|
73
|
-
...resolvedOptions,
|
|
74
|
-
};
|
|
75
|
-
const support = await prettier.getFileInfo(systemPath, options);
|
|
76
|
-
if (support.ignored || !support.inferredParser) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
results.set(file.path, await prettier.format(file.content.toString('utf-8'), options));
|
|
80
|
-
}
|
|
81
|
-
catch (e) {
|
|
82
|
-
if (!options?.silent) {
|
|
83
|
-
console.warn(`Could not format ${file.path}. Error: "${e.message}"`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}));
|
|
87
|
-
return results;
|
|
88
|
-
}
|