nx 23.1.1 → 23.1.3
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/release/changelog-renderer/index.js +4 -5
- package/dist/release/index.d.ts +1 -1
- package/dist/src/command-line/add/add.js +5 -0
- package/dist/src/command-line/affected/affected.js +1 -0
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
- package/dist/src/command-line/import/import.js +1 -2
- package/dist/src/command-line/init/init-v2.js +4 -5
- package/dist/src/command-line/migrate/migrate.d.ts +4 -0
- package/dist/src/command-line/migrate/migrate.js +89 -26
- package/dist/src/command-line/release/changelog.js +5 -0
- package/dist/src/command-line/release/index.d.ts +1 -1
- package/dist/src/command-line/release/utils/git.js +1 -1
- package/dist/src/command-line/release/utils/release-graph.d.ts +17 -0
- package/dist/src/command-line/release/utils/release-graph.js +71 -19
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +6 -0
- package/dist/src/command-line/release/version/release-group-processor.d.ts +24 -0
- package/dist/src/command-line/release/version/release-group-processor.js +80 -6
- package/dist/src/command-line/release/version/version-actions.d.ts +27 -2
- package/dist/src/command-line/release/version/version-actions.js +9 -1
- package/dist/src/command-line/release/version.js +9 -5
- package/dist/src/command-line/reset/reset.js +6 -1
- package/dist/src/command-line/run/run-one.js +1 -0
- package/dist/src/command-line/run-many/run-many.js +1 -0
- package/dist/src/command-line/watch/watch.js +13 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +52 -1
- package/dist/src/daemon/client/client.js +160 -36
- package/dist/src/daemon/client/daemon-environment.d.ts +17 -0
- package/dist/src/daemon/client/daemon-environment.js +110 -2
- package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
- package/dist/src/daemon/message-types/daemon-message.d.ts +33 -0
- package/dist/src/daemon/message-types/daemon-message.js +48 -0
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +5 -0
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +16 -0
- package/dist/src/daemon/server/latest-nx.js +5 -2
- package/dist/src/daemon/server/server.js +24 -0
- package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
- package/dist/src/daemon/server/shutdown-utils.js +11 -4
- package/dist/src/daemon/socket-utils.d.ts +1 -0
- package/dist/src/daemon/socket-utils.js +24 -3
- package/dist/src/daemon/tmp-dir.d.ts +43 -3
- package/dist/src/daemon/tmp-dir.js +437 -15
- package/dist/src/devkit-internals.d.ts +94 -4
- package/dist/src/devkit-internals.js +194 -5
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
- package/dist/src/generators/tree.d.ts +4 -2
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
- package/dist/src/native/index.d.ts +10 -0
- package/dist/src/native/index.js +71 -28
- package/dist/src/native/native-bindings.js +1 -0
- package/dist/src/native/native-file-cache-location.d.ts +17 -1
- package/dist/src/native/native-file-cache-location.js +86 -12
- 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/utilities/get-cloud-options.js +5 -2
- package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
- package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +60 -0
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +14 -8
- package/dist/src/plugins/js/project-graph/affected/npm-packages.js +51 -20
- package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +2 -0
- package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
- package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
- package/dist/src/project-graph/error-types.d.ts +12 -1
- package/dist/src/project-graph/error-types.js +27 -2
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +18 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
- package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
- package/dist/src/project-graph/project-graph.js +20 -0
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +5 -1
- package/dist/src/project-graph/utils/project-configuration/target-normalization.js +133 -3
- package/dist/src/tasks-runner/task-env.js +5 -2
- package/dist/src/tasks-runner/utils.js +2 -6
- package/dist/src/utils/assert-workspace-validity.js +7 -2
- package/dist/src/utils/fileutils.d.ts +5 -0
- package/dist/src/utils/fileutils.js +7 -2
- package/dist/src/utils/git-worktrees.d.ts +30 -0
- package/dist/src/utils/git-worktrees.js +212 -0
- package/dist/src/utils/ignore.d.ts +128 -1
- package/dist/src/utils/ignore.js +182 -12
- package/dist/src/utils/long-running-target.d.ts +10 -0
- package/dist/src/utils/long-running-target.js +19 -0
- package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
- package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
- package/dist/src/utils/min-release-age/packument.js +1 -1
- package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
- package/dist/src/utils/nx-tmp-dir.js +40 -0
- package/dist/src/utils/owned-private-dir.d.ts +132 -0
- package/dist/src/utils/owned-private-dir.js +323 -0
- package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
- package/dist/src/utils/package-manager-config/bunfig.js +49 -0
- package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
- package/dist/src/utils/package-manager-config/npmrc.js +124 -0
- package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
- package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
- package/dist/src/utils/package-manager.d.ts +21 -2
- package/dist/src/utils/package-manager.js +244 -47
- package/dist/src/utils/provenance.js +6 -14
- package/dist/src/utils/registry-config/bun.d.ts +2 -0
- package/dist/src/utils/registry-config/bun.js +257 -0
- package/dist/src/utils/registry-config/index.d.ts +13 -0
- package/dist/src/utils/registry-config/index.js +102 -0
- package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
- package/dist/src/utils/registry-config/pnpm.js +1551 -0
- package/dist/src/utils/registry-config/utils.d.ts +195 -0
- package/dist/src/utils/registry-config/utils.js +496 -0
- package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-berry.js +612 -0
- package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
- package/dist/src/utils/safe-spawn.d.ts +24 -0
- package/dist/src/utils/safe-spawn.js +104 -0
- package/dist/src/utils/shell-quoting.d.ts +15 -0
- package/dist/src/utils/shell-quoting.js +35 -0
- package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
- package/dist/src/utils/wait-for-socket-connection.js +14 -8
- package/migrations.json +6 -0
- package/package.json +12 -12
- package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
- package/dist/src/utils/min-release-age/npmrc.js +0 -45
|
@@ -412,6 +412,16 @@ export declare function getEventDimensions(): EventDimensions
|
|
|
412
412
|
*/
|
|
413
413
|
export declare function getFilesForOutputsBatch(directory: string, entriesBatch: Array<Array<string>>): Array<Array<string>>
|
|
414
414
|
|
|
415
|
+
/**
|
|
416
|
+
* The same list, for JavaScript callers that walk a tree rather than the
|
|
417
|
+
* filesystem - `visitNotIgnoredFiles` - so both sides apply one baseline
|
|
418
|
+
* instead of maintaining a second copy that drifts.
|
|
419
|
+
*
|
|
420
|
+
* The patterns are gitignore-shaped, so they read the same to the `ignore`
|
|
421
|
+
* crate here and the `ignore` npm package there.
|
|
422
|
+
*/
|
|
423
|
+
export declare function getHardcodedIgnorePatterns(): Array<string>
|
|
424
|
+
|
|
415
425
|
/**
|
|
416
426
|
* If `workspace_root` is inside a git worktree, returns the main repo root.
|
|
417
427
|
* Returns `None` when already in the main repo (or not in a git repo at all).
|
package/dist/src/native/index.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
const { join, basename } = require('path');
|
|
2
|
-
const {
|
|
3
|
-
|
|
4
|
-
existsSync,
|
|
5
|
-
mkdirSync,
|
|
6
|
-
renameSync,
|
|
7
|
-
statSync,
|
|
8
|
-
unlinkSync,
|
|
9
|
-
} = require('fs');
|
|
2
|
+
const { copyFileSync, renameSync, statSync, unlinkSync } = require('fs');
|
|
3
|
+
const { createHash } = require('crypto');
|
|
10
4
|
const Module = require('module');
|
|
11
5
|
const { nxVersion } = require('../utils/versions');
|
|
12
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
ensureSecureNativeFileCacheLocation,
|
|
8
|
+
} = require('./native-file-cache-location');
|
|
13
9
|
|
|
14
10
|
const MAX_COPY_RETRIES = 3;
|
|
15
11
|
|
|
@@ -73,6 +69,14 @@ function statsOrNull(path) {
|
|
|
73
69
|
}
|
|
74
70
|
}
|
|
75
71
|
|
|
72
|
+
function removeIfPresent(path) {
|
|
73
|
+
try {
|
|
74
|
+
unlinkSync(path);
|
|
75
|
+
} catch {
|
|
76
|
+
// Ignore cleanup errors
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
function isNoExecError(e) {
|
|
77
81
|
return e.code === 'EACCES' || e.code === 'EPERM';
|
|
78
82
|
}
|
|
@@ -95,20 +99,44 @@ Module._load = function (request, parent, isMain) {
|
|
|
95
99
|
const nativeLocation = require.resolve(modulePath);
|
|
96
100
|
const fileName = basename(nativeLocation);
|
|
97
101
|
|
|
98
|
-
//
|
|
99
|
-
|
|
102
|
+
// null when the cache dir cannot be created *and trusted*; loading in place
|
|
103
|
+
// from node_modules is safer than loading out of a directory we don't own.
|
|
104
|
+
const nativeFileCacheLocation = ensureSecureNativeFileCacheLocation();
|
|
105
|
+
if (!nativeFileCacheLocation) {
|
|
106
|
+
return originalLoad.apply(this, [nativeLocation, parent, isMain]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// nxVersion alone is not enough: in a source checkout it is the placeholder
|
|
110
|
+
// 0.0.1 for every worktree, so hashing the resolved binding path keeps each
|
|
111
|
+
// checkout in its own entry.
|
|
112
|
+
const cacheKey =
|
|
113
|
+
nxVersion +
|
|
114
|
+
'-' +
|
|
115
|
+
createHash('sha256')
|
|
116
|
+
.update(nativeLocation)
|
|
117
|
+
.digest('hex')
|
|
118
|
+
.substring(0, 12);
|
|
119
|
+
|
|
100
120
|
// This is a path to copy to, not the one that gets loaded
|
|
101
121
|
const tmpTmpFile = join(
|
|
102
122
|
nativeFileCacheLocation,
|
|
103
|
-
|
|
123
|
+
cacheKey + '-' + Math.random() + fileName
|
|
104
124
|
);
|
|
105
125
|
// This is the path that will get loaded
|
|
106
|
-
const tmpFile = join(nativeFileCacheLocation,
|
|
107
|
-
const
|
|
126
|
+
const tmpFile = join(nativeFileCacheLocation, cacheKey + '-' + fileName);
|
|
127
|
+
const sourceStats = statSync(nativeLocation);
|
|
128
|
+
const expectedFileSize = sourceStats.size;
|
|
108
129
|
const existingFileStats = statsOrNull(tmpFile);
|
|
109
130
|
|
|
110
|
-
//
|
|
111
|
-
|
|
131
|
+
// Size alone does not detect a rebuilt binding: a small Rust edit routinely
|
|
132
|
+
// produces a byte-identical size. The copy is taken after the build, so a
|
|
133
|
+
// cache entry older than the source means the source was rebuilt since.
|
|
134
|
+
const isFresh =
|
|
135
|
+
existingFileStats?.size === expectedFileSize &&
|
|
136
|
+
existingFileStats.mtimeMs >= sourceStats.mtimeMs;
|
|
137
|
+
|
|
138
|
+
// If the file to be loaded already exists and is current, just load it
|
|
139
|
+
if (isFresh) {
|
|
112
140
|
try {
|
|
113
141
|
return originalLoad.apply(this, [tmpFile, parent, isMain]);
|
|
114
142
|
} catch (e) {
|
|
@@ -119,20 +147,36 @@ Module._load = function (request, parent, isMain) {
|
|
|
119
147
|
throw e;
|
|
120
148
|
}
|
|
121
149
|
}
|
|
122
|
-
if (!existsSync(nativeFileCacheLocation)) {
|
|
123
|
-
mkdirSync(nativeFileCacheLocation, { recursive: true });
|
|
124
|
-
}
|
|
125
150
|
|
|
126
151
|
// Retry copying up to 3 times, validating after each copy
|
|
152
|
+
let attemptsMade = 0;
|
|
153
|
+
let cacheError = null;
|
|
127
154
|
for (let attempt = 1; attempt <= MAX_COPY_RETRIES; attempt++) {
|
|
155
|
+
attemptsMade = attempt;
|
|
128
156
|
// First copy to a unique location for each process
|
|
129
|
-
|
|
157
|
+
try {
|
|
158
|
+
copyFileSync(nativeLocation, tmpTmpFile);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
// Permission errors won't heal on retry, and a throwing copy can still
|
|
161
|
+
// have left a partial file behind.
|
|
162
|
+
cacheError = e;
|
|
163
|
+
removeIfPresent(tmpTmpFile);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
130
166
|
|
|
131
167
|
// Validate the copy - check file size matches expected
|
|
132
168
|
const copiedFileStats = statsOrNull(tmpTmpFile);
|
|
133
169
|
if (copiedFileStats?.size === expectedFileSize) {
|
|
134
170
|
// Copy succeeded, rename to final location and load
|
|
135
|
-
|
|
171
|
+
try {
|
|
172
|
+
renameSync(tmpTmpFile, tmpFile);
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// Unguarded, this throws out of `require` and native-bindings.js
|
|
175
|
+
// reports it as a missing optional dependency, which it is not.
|
|
176
|
+
cacheError = e;
|
|
177
|
+
removeIfPresent(tmpTmpFile);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
136
180
|
try {
|
|
137
181
|
return originalLoad.apply(this, [tmpFile, parent, isMain]);
|
|
138
182
|
} catch (e) {
|
|
@@ -145,16 +189,15 @@ Module._load = function (request, parent, isMain) {
|
|
|
145
189
|
}
|
|
146
190
|
|
|
147
191
|
// Copy failed validation, clean up the malformed file
|
|
148
|
-
|
|
149
|
-
unlinkSync(tmpTmpFile);
|
|
150
|
-
} catch {
|
|
151
|
-
// Ignore cleanup errors
|
|
152
|
-
}
|
|
192
|
+
removeIfPresent(tmpTmpFile);
|
|
153
193
|
}
|
|
154
194
|
|
|
155
|
-
//
|
|
195
|
+
// Name the errno: ENOSPC, EROFS, EACCES and EDQUOT are all actionable and
|
|
196
|
+
// otherwise indistinguishable.
|
|
156
197
|
console.warn(
|
|
157
|
-
`Warning: Failed to copy native module to cache after ${
|
|
198
|
+
`Warning: Failed to copy native module to cache after ${attemptsMade} attempt${
|
|
199
|
+
attemptsMade === 1 ? '' : 's'
|
|
200
|
+
}${cacheError ? ` (${cacheError.code ?? cacheError.message})` : ''}. ` +
|
|
158
201
|
`Loading from original location instead. ` +
|
|
159
202
|
`This may cause file locking issues on Windows.`
|
|
160
203
|
);
|
|
@@ -610,6 +610,7 @@ module.exports.getBinaryTarget = nativeBinding.getBinaryTarget
|
|
|
610
610
|
module.exports.getDefaultMaxCacheSize = nativeBinding.getDefaultMaxCacheSize
|
|
611
611
|
module.exports.getEventDimensions = nativeBinding.getEventDimensions
|
|
612
612
|
module.exports.getFilesForOutputsBatch = nativeBinding.getFilesForOutputsBatch
|
|
613
|
+
module.exports.getHardcodedIgnorePatterns = nativeBinding.getHardcodedIgnorePatterns
|
|
613
614
|
module.exports.getMainWorktreeRoot = nativeBinding.getMainWorktreeRoot
|
|
614
615
|
module.exports.getTransformableOutputs = nativeBinding.getTransformableOutputs
|
|
615
616
|
module.exports.GroupType = nativeBinding.GroupType
|
|
@@ -1 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Path of the current user's native binary cache. Nothing about the constant is
|
|
3
|
+
* owner-only — `ensureSecureNativeFileCacheLocation` is what establishes that,
|
|
4
|
+
* and on Windows nothing does, since the OS temp dir is already per-account.
|
|
5
|
+
*/
|
|
6
|
+
export declare const NATIVE_CACHE_ROOT: string;
|
|
7
|
+
export declare function getNativeFileCacheLocationToDelete(): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Create the native file cache dir, or return `null` if it cannot be created
|
|
10
|
+
* *securely* — in which case the caller loads the binding in place.
|
|
11
|
+
*
|
|
12
|
+
* The stable top-level container is verified as safe for private children. The
|
|
13
|
+
* uid directory and every directory loaded through are owner-only.
|
|
14
|
+
* `ensureOwnedPrivateDir` refuses a directory or symlink another local user
|
|
15
|
+
* planted before us.
|
|
16
|
+
*/
|
|
17
|
+
export declare function ensureSecureNativeFileCacheLocation(cacheRoot?: string): string | null;
|
|
@@ -1,23 +1,97 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
3
|
+
exports.NATIVE_CACHE_ROOT = void 0;
|
|
4
|
+
exports.getNativeFileCacheLocationToDelete = getNativeFileCacheLocationToDelete;
|
|
5
|
+
exports.ensureSecureNativeFileCacheLocation = ensureSecureNativeFileCacheLocation;
|
|
5
6
|
const path_1 = require("path");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const nx_tmp_dir_1 = require("../utils/nx-tmp-dir");
|
|
8
9
|
const versions_1 = require("../utils/versions");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const owned_private_dir_1 = require("../utils/owned-private-dir");
|
|
11
|
+
/**
|
|
12
|
+
* Path of the current user's native binary cache. Nothing about the constant is
|
|
13
|
+
* owner-only — `ensureSecureNativeFileCacheLocation` is what establishes that,
|
|
14
|
+
* and on Windows nothing does, since the OS temp dir is already per-account.
|
|
15
|
+
*/
|
|
16
|
+
exports.NATIVE_CACHE_ROOT = (0, path_1.join)(nx_tmp_dir_1.NX_USER_TMP_DIR, 'native-cache');
|
|
17
|
+
/**
|
|
18
|
+
* The configured cache dir, normalized. `resolve` strips a trailing slash, which
|
|
19
|
+
* would otherwise defeat the guards downstream: `lstat` on a path ending in `/`
|
|
20
|
+
* resolves a symlink rather than reporting it, and `O_NOFOLLOW` then opens the
|
|
21
|
+
* target. `NX_SOCKET_DIR` is normalized for the same reason.
|
|
22
|
+
*/
|
|
23
|
+
function configuredCacheDir() {
|
|
24
|
+
const dir = process.env.NX_NATIVE_FILE_CACHE_DIRECTORY;
|
|
25
|
+
return dir ? (0, path_1.resolve)(dir) : undefined;
|
|
26
|
+
}
|
|
27
|
+
function getNativeFileCacheLocationToDelete() {
|
|
28
|
+
const configured = configuredCacheDir();
|
|
29
|
+
if (configured !== undefined) {
|
|
30
|
+
// Checked for ownership before it is handed to a recursive delete: being
|
|
31
|
+
// configured does not make it ours. Weaker than the load path below, which
|
|
32
|
+
// also enforces mode and validates the version directory.
|
|
33
|
+
return (0, owned_private_dir_1.isOwnedRealDirectory)(configured) ? configured : null;
|
|
12
34
|
}
|
|
13
|
-
|
|
14
|
-
|
|
35
|
+
return (0, owned_private_dir_1.isSafeSharedRoot)(nx_tmp_dir_1.NX_TMP_DIR).status === 'ok' &&
|
|
36
|
+
(0, owned_private_dir_1.isOwnedRealDirectory)(nx_tmp_dir_1.NX_USER_TMP_DIR) &&
|
|
37
|
+
(0, owned_private_dir_1.isOwnedRealDirectory)(exports.NATIVE_CACHE_ROOT)
|
|
38
|
+
? (0, path_1.join)(exports.NATIVE_CACHE_ROOT, versions_1.nxVersion)
|
|
39
|
+
: null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create the native file cache dir, or return `null` if it cannot be created
|
|
43
|
+
* *securely* — in which case the caller loads the binding in place.
|
|
44
|
+
*
|
|
45
|
+
* The stable top-level container is verified as safe for private children. The
|
|
46
|
+
* uid directory and every directory loaded through are owner-only.
|
|
47
|
+
* `ensureOwnedPrivateDir` refuses a directory or symlink another local user
|
|
48
|
+
* planted before us.
|
|
49
|
+
*/
|
|
50
|
+
function ensureSecureNativeFileCacheLocation(
|
|
51
|
+
// Test seam: lets a spec plant a hostile directory under a root it controls.
|
|
52
|
+
cacheRoot = exports.NATIVE_CACHE_ROOT) {
|
|
53
|
+
const configured = configuredCacheDir();
|
|
54
|
+
if (configured !== undefined) {
|
|
55
|
+
const dir = configured;
|
|
15
56
|
try {
|
|
16
|
-
|
|
57
|
+
// Held to the same 0700 bar as the default location: a `.node` is loaded
|
|
58
|
+
// out of here. Unlike NX_SOCKET_DIR it is not additionally refused for
|
|
59
|
+
// naming one of Nx's own roots, which is the only case that throws.
|
|
60
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(dir), { recursive: true });
|
|
61
|
+
const established = (0, owned_private_dir_1.ensureOwnedPrivateDir)(dir);
|
|
62
|
+
if (established.status !== 'ok') {
|
|
63
|
+
// The guard's own reason rather than a fixed sentence: `not-created
|
|
64
|
+
// (EACCES)` and `foreign-owner` are exactly what the catch below
|
|
65
|
+
// promises to tell apart, and one string for all of them cannot.
|
|
66
|
+
throw new owned_private_dir_1.DirectoryRefusedError(established.refusal);
|
|
67
|
+
}
|
|
68
|
+
return dir;
|
|
17
69
|
}
|
|
18
70
|
catch (e) {
|
|
19
|
-
//
|
|
71
|
+
// Never discard a configured directory silently — the socket directory
|
|
72
|
+
// follows the same rule. A typo, EACCES or EROFS is otherwise
|
|
73
|
+
// indistinguishable from a working cache.
|
|
74
|
+
console.warn(`Nx could not use the configured native file cache directory ${dir} (${e?.code ?? e?.message ?? e}). Loading the native binding in place instead.`);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const userRoot = (0, path_1.dirname)(cacheRoot);
|
|
79
|
+
const sharedRoot = (0, path_1.dirname)(userRoot);
|
|
80
|
+
// Outermost first: the stable shared container must either belong to root or
|
|
81
|
+
// to us, and must be sticky if peers can write there. The uid directory then
|
|
82
|
+
// becomes the owner-only boundary for sockets and native-cache alike.
|
|
83
|
+
if ((0, owned_private_dir_1.ensureSafeSharedRoot)(sharedRoot).status !== 'ok') {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
for (const root of [userRoot, cacheRoot]) {
|
|
87
|
+
if ((0, owned_private_dir_1.ensureOwnedPrivateDir)(root).status !== 'ok') {
|
|
88
|
+
return null;
|
|
20
89
|
}
|
|
21
|
-
return (0, path_1.join)((0, os_1.tmpdir)(), `nx-native-file-cache-${hash.digest('hex').substring(0, 7)}`);
|
|
22
90
|
}
|
|
91
|
+
// Verified, not assumed: this is the directory we load a `.node` out of.
|
|
92
|
+
const versionDir = (0, path_1.join)(cacheRoot, versions_1.nxVersion);
|
|
93
|
+
if ((0, owned_private_dir_1.ensureOwnedPrivateDir)(versionDir).status !== 'ok') {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return versionDir;
|
|
23
97
|
}
|
|
Binary file
|
|
Binary file
|
|
@@ -5,12 +5,15 @@ exports.getCloudUrl = getCloudUrl;
|
|
|
5
5
|
exports.removeTrailingSlash = removeTrailingSlash;
|
|
6
6
|
exports.isNxCloudId = isNxCloudId;
|
|
7
7
|
const nx_json_1 = require("../../config/nx-json");
|
|
8
|
-
const run_command_1 = require("../../tasks-runner/run-command");
|
|
9
8
|
const workspace_root_1 = require("../../utils/workspace-root");
|
|
10
9
|
function getCloudOptions(directory = workspace_root_1.workspaceRoot) {
|
|
10
|
+
// Required lazily: this module is reachable from the @nx/devkit/internal
|
|
11
|
+
// barrel that plugin workers load, and run-command eagerly pulls in the whole
|
|
12
|
+
// task-execution subsystem.
|
|
13
|
+
const { getRunnerOptions, } = require('../../tasks-runner/run-command');
|
|
11
14
|
const nxJson = (0, nx_json_1.readNxJson)(directory);
|
|
12
15
|
// TODO: The default is not always cloud? But it's not handled at the moment
|
|
13
|
-
return
|
|
16
|
+
return getRunnerOptions('default', nxJson, {}, true);
|
|
14
17
|
}
|
|
15
18
|
function getCloudUrl() {
|
|
16
19
|
return removeTrailingSlash(process.env.NX_CLOUD_API || process.env.NRWL_API || `https://cloud.nx.app`);
|
|
@@ -5,7 +5,6 @@ exports.getNxCloudAppOnBoardingUrl = getNxCloudAppOnBoardingUrl;
|
|
|
5
5
|
exports.readNxCloudToken = readNxCloudToken;
|
|
6
6
|
const is_workspace_claimed_1 = require("./is-workspace-claimed");
|
|
7
7
|
const url_shorten_1 = require("./url-shorten");
|
|
8
|
-
const run_command_1 = require("../../tasks-runner/run-command");
|
|
9
8
|
const nx_json_1 = require("../../generators/utils/nx-json");
|
|
10
9
|
async function createNxCloudOnboardingURLForWelcomeApp(tree, token) {
|
|
11
10
|
token = token || readNxCloudToken(tree);
|
|
@@ -22,7 +21,11 @@ async function getNxCloudAppOnBoardingUrl(token) {
|
|
|
22
21
|
return onboardingUrl;
|
|
23
22
|
}
|
|
24
23
|
function readNxCloudToken(tree) {
|
|
24
|
+
// Required lazily: this module is re-exported from the @nx/devkit/internal
|
|
25
|
+
// barrel that plugin workers load, and run-command eagerly pulls in the whole
|
|
26
|
+
// task-execution subsystem.
|
|
27
|
+
const { getRunnerOptions, } = require('../../tasks-runner/run-command');
|
|
25
28
|
const nxJson = (0, nx_json_1.readNxJson)(tree);
|
|
26
|
-
const { accessToken, nxCloudId } =
|
|
29
|
+
const { accessToken, nxCloudId } = getRunnerOptions('default', nxJson, {}, true);
|
|
27
30
|
return accessToken || nxCloudId;
|
|
28
31
|
}
|
|
@@ -12,6 +12,16 @@ const project_graph_1 = require("../../../config/project-graph");
|
|
|
12
12
|
const file_hasher_1 = require("../../../hasher/file-hasher");
|
|
13
13
|
const project_graph_builder_1 = require("../../../project-graph/project-graph-builder");
|
|
14
14
|
const json_1 = require("../../../utils/json");
|
|
15
|
+
// Highest bun.lock version this parser has been verified against:
|
|
16
|
+
// - 1: workspace packages are no longer listed with their dependencies
|
|
17
|
+
// - 2: identical content, stricter validation in Bun's own reader
|
|
18
|
+
// - 3: values in "overrides" may be objects holding rules scoped to a parent
|
|
19
|
+
// package (`"parent": { "child": "1.0.0" }`) and keys may carry a version
|
|
20
|
+
// selector (`"parent@1"`, `"child@<2"`). This parser never reads
|
|
21
|
+
// "overrides"; the resolved packages are listed in "packages" as before.
|
|
22
|
+
// Newer versions are parsed on a best-effort basis instead of being rejected
|
|
23
|
+
// outright.
|
|
24
|
+
const MAX_KNOWN_LOCKFILE_VERSION = 3;
|
|
15
25
|
const DEPENDENCY_TYPES = [
|
|
16
26
|
'dependencies',
|
|
17
27
|
'devDependencies',
|
|
@@ -356,9 +366,12 @@ function parseLockFile(lockFileContent, lockFileHash) {
|
|
|
356
366
|
if (typeof result.lockfileVersion !== 'number') {
|
|
357
367
|
throw new Error(`Lockfile version must be a number, got ${typeof result.lockfileVersion}`);
|
|
358
368
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
throw new Error(`Unsupported lockfile version ${result.lockfileVersion}.
|
|
369
|
+
if (!Number.isInteger(result.lockfileVersion) ||
|
|
370
|
+
result.lockfileVersion < 0) {
|
|
371
|
+
throw new Error(`Unsupported lockfile version ${result.lockfileVersion}. Lockfile version must be a non-negative integer`);
|
|
372
|
+
}
|
|
373
|
+
if (result.lockfileVersion > MAX_KNOWN_LOCKFILE_VERSION) {
|
|
374
|
+
console.warn(`bun.lock has lockfileVersion ${result.lockfileVersion}, which is newer than the version ${MAX_KNOWN_LOCKFILE_VERSION} this version of Nx was tested with. Attempting to parse it anyway.`);
|
|
362
375
|
}
|
|
363
376
|
}
|
|
364
377
|
if (!result.packages || typeof result.packages !== 'object') {
|
|
@@ -616,7 +629,7 @@ function processPackageForDependencies(packageKey, packageData, lockFile, index,
|
|
|
616
629
|
const deps = packageDependencies[depType];
|
|
617
630
|
if (!deps || typeof deps !== 'object')
|
|
618
631
|
continue;
|
|
619
|
-
const depDependencies = processDependencyEntries(deps, sourceNodeName, index, ctx, workspacePackages
|
|
632
|
+
const depDependencies = processDependencyEntries(deps, packageKey, sourceNodeName, lockFile, index, ctx, workspacePackages);
|
|
620
633
|
dependencies.push(...depDependencies);
|
|
621
634
|
}
|
|
622
635
|
return dependencies;
|
|
@@ -634,12 +647,12 @@ function extractPackageDependencies(packageData) {
|
|
|
634
647
|
}
|
|
635
648
|
return undefined;
|
|
636
649
|
}
|
|
637
|
-
function processDependencyEntries(deps, sourceNodeName, index, ctx, workspacePackages
|
|
650
|
+
function processDependencyEntries(deps, sourcePackageKey, sourceNodeName, lockFile, index, ctx, workspacePackages) {
|
|
638
651
|
const dependencies = [];
|
|
639
652
|
const depsEntries = Object.entries(deps);
|
|
640
653
|
for (const [packageName, versionSpec] of depsEntries) {
|
|
641
654
|
try {
|
|
642
|
-
const dependency = processSingleDependency(packageName, versionSpec, sourceNodeName, index, ctx, workspacePackages
|
|
655
|
+
const dependency = processSingleDependency(packageName, versionSpec, sourcePackageKey, sourceNodeName, lockFile, index, ctx, workspacePackages);
|
|
643
656
|
if (dependency) {
|
|
644
657
|
dependencies.push(dependency);
|
|
645
658
|
}
|
|
@@ -650,7 +663,7 @@ function processDependencyEntries(deps, sourceNodeName, index, ctx, workspacePac
|
|
|
650
663
|
}
|
|
651
664
|
return dependencies;
|
|
652
665
|
}
|
|
653
|
-
function processSingleDependency(packageName, versionSpec, sourceNodeName, index, ctx, workspacePackages
|
|
666
|
+
function processSingleDependency(packageName, versionSpec, sourcePackageKey, sourceNodeName, lockFile, index, ctx, workspacePackages) {
|
|
654
667
|
if (typeof packageName !== 'string' || typeof versionSpec !== 'string') {
|
|
655
668
|
return null;
|
|
656
669
|
}
|
|
@@ -663,14 +676,22 @@ function processSingleDependency(packageName, versionSpec, sourceNodeName, index
|
|
|
663
676
|
}
|
|
664
677
|
let targetPackageName = packageName;
|
|
665
678
|
let targetVersion = versionSpec;
|
|
666
|
-
|
|
667
|
-
|
|
679
|
+
// A "<parent>/<name>" entry records what this parent actually resolved the
|
|
680
|
+
// dependency to. Overrides scoped to a parent (bun.lock v3) can pick a
|
|
681
|
+
// version outside the declared range, so it takes precedence over matching
|
|
682
|
+
// the range against the versions in the lockfile.
|
|
683
|
+
const nestedVersion = resolveNestedVersion(sourcePackageKey, packageName, lockFile);
|
|
684
|
+
const aliasTarget = nestedVersion ? null : resolveAliasTarget(versionSpec);
|
|
685
|
+
if (nestedVersion) {
|
|
686
|
+
targetVersion = nestedVersion;
|
|
687
|
+
}
|
|
688
|
+
else if (aliasTarget) {
|
|
668
689
|
targetPackageName = aliasTarget.packageName;
|
|
669
690
|
targetVersion = aliasTarget.version;
|
|
670
691
|
}
|
|
671
692
|
else {
|
|
672
693
|
// O(1) lookup using index instead of O(n) scan
|
|
673
|
-
const resolvedVersion = findResolvedVersion(packageName, versionSpec, index, manifests);
|
|
694
|
+
const resolvedVersion = findResolvedVersion(packageName, versionSpec, index, lockFile.manifests);
|
|
674
695
|
if (!resolvedVersion) {
|
|
675
696
|
return null;
|
|
676
697
|
}
|
|
@@ -693,6 +714,18 @@ function processSingleDependency(packageName, versionSpec, sourceNodeName, index
|
|
|
693
714
|
return null;
|
|
694
715
|
}
|
|
695
716
|
}
|
|
717
|
+
function resolveNestedVersion(sourcePackageKey, packageName, lockFile) {
|
|
718
|
+
const nestedEntry = lockFile.packages[`${sourcePackageKey}/${packageName}`];
|
|
719
|
+
if (!Array.isArray(nestedEntry) || typeof nestedEntry[0] !== 'string') {
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
const { name, version } = getCachedSpecInfo(nestedEntry[0]);
|
|
723
|
+
// aliases ("alias@npm:real@1.0.0") are resolved to the real package by the caller
|
|
724
|
+
if (name !== packageName || !version || version.startsWith('npm:')) {
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
return version;
|
|
728
|
+
}
|
|
696
729
|
function resolveTargetNodeName(targetPackageName, targetVersion, ctx) {
|
|
697
730
|
const hoistedNodeName = `npm:${targetPackageName}`;
|
|
698
731
|
const versionedNodeName = `npm:${targetPackageName}@${targetVersion}`;
|
|
@@ -12,6 +12,7 @@ const file_hasher_1 = require("../../../hasher/file-hasher");
|
|
|
12
12
|
const catalog_1 = require("../../../utils/catalog");
|
|
13
13
|
const project_graph_pruning_1 = require("./project-graph-pruning");
|
|
14
14
|
const path_1 = require("path");
|
|
15
|
+
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
15
16
|
const get_workspace_packages_from_graph_1 = require("../utils/get-workspace-packages-from-graph");
|
|
16
17
|
const semver_1 = require("semver");
|
|
17
18
|
const WORKSPACE_DEP_TYPES = [
|
|
@@ -133,6 +134,17 @@ function findPatchHash(patchEntriesByPackage, packageName, version) {
|
|
|
133
134
|
const nameOnlyMatch = entries.find((entry) => entry.versionSpecifier === null);
|
|
134
135
|
return nameOnlyMatch?.hash;
|
|
135
136
|
}
|
|
137
|
+
// Segment-aware: `..` and `../x` escape, a directory literally named `..cache`
|
|
138
|
+
// does not. Absolute targets are resolved against the workspace root.
|
|
139
|
+
function linkTargetEscapesWorkspace(importerPath, depVersion) {
|
|
140
|
+
const rawTarget = depVersion.slice('link:'.length);
|
|
141
|
+
if (path_1.posix.isAbsolute(rawTarget) || (0, path_1.isAbsolute)(rawTarget)) {
|
|
142
|
+
const rel = (0, path_1.relative)(workspace_root_1.workspaceRoot, rawTarget);
|
|
143
|
+
return rel === '..' || rel.startsWith(`..${path_1.sep}`) || (0, path_1.isAbsolute)(rel);
|
|
144
|
+
}
|
|
145
|
+
const combined = path_1.posix.normalize(path_1.posix.join(importerPath === '.' ? '' : importerPath, rawTarget));
|
|
146
|
+
return combined === '..' || combined.startsWith('../');
|
|
147
|
+
}
|
|
136
148
|
function getNodes(data, isV5) {
|
|
137
149
|
cachedInvertedRecords.clear();
|
|
138
150
|
const keyMap = new Map();
|
|
@@ -345,6 +357,54 @@ function getNodes(data, isV5) {
|
|
|
345
357
|
results[node.name] = node;
|
|
346
358
|
});
|
|
347
359
|
}
|
|
360
|
+
// `link:` dependencies pointing outside the workspace never appear in the
|
|
361
|
+
// packages section, so nothing above minted a node for them — and a task
|
|
362
|
+
// input naming one via `externalDependencies` fails hashing with "could not
|
|
363
|
+
// be found". Mint a node under the bare `npm:<name>`, hashed on the link
|
|
364
|
+
// path: the hash changes only when the path does, not when the linked
|
|
365
|
+
// content does, which is how linked dependencies behave everywhere else in
|
|
366
|
+
// Nx. Links that resolve inside the workspace are workspace projects, not
|
|
367
|
+
// externals, and are skipped.
|
|
368
|
+
//
|
|
369
|
+
// One node per package name, chosen deterministically by visiting importers
|
|
370
|
+
// root-first, then lexicographically. An `externalDependencies` input names
|
|
371
|
+
// a package, not a link target, so it could not distinguish two linked
|
|
372
|
+
// targets anyway.
|
|
373
|
+
const importerPaths = Object.keys(data.importers ?? {}).sort((a, b) => a === '.' ? -1 : b === '.' ? 1 : a.localeCompare(b));
|
|
374
|
+
for (const importerPath of importerPaths) {
|
|
375
|
+
const importer = data.importers[importerPath];
|
|
376
|
+
for (const depType of [
|
|
377
|
+
'dependencies',
|
|
378
|
+
'devDependencies',
|
|
379
|
+
'optionalDependencies',
|
|
380
|
+
]) {
|
|
381
|
+
const deps = importer[depType];
|
|
382
|
+
if (!deps) {
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
for (const [depName, depVersion] of Object.entries(deps)) {
|
|
386
|
+
if (typeof depVersion !== 'string' || !depVersion.startsWith('link:')) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (!linkTargetEscapesWorkspace(importerPath, depVersion)) {
|
|
390
|
+
continue; // inside the workspace -> a workspace project
|
|
391
|
+
}
|
|
392
|
+
const bareName = `npm:${depName}`;
|
|
393
|
+
if (results[bareName]) {
|
|
394
|
+
continue; // a hoisted registry version or an earlier link wins
|
|
395
|
+
}
|
|
396
|
+
results[bareName] = {
|
|
397
|
+
type: 'npm',
|
|
398
|
+
name: bareName,
|
|
399
|
+
data: {
|
|
400
|
+
version: depVersion,
|
|
401
|
+
packageName: depName,
|
|
402
|
+
hash: (0, file_hasher_1.hashArray)([depName, depVersion]),
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
348
408
|
return { nodes: results, keyMap };
|
|
349
409
|
}
|
|
350
410
|
function getHoistedVersion(packageName, isV5, hoistedKeysByPackage) {
|
|
@@ -177,17 +177,23 @@ function switchNodeToHoisted(node, builder, invBuilder) {
|
|
|
177
177
|
const sources = Object.keys(builder.graph.dependencies).filter((name) => builder.graph.dependencies[name].some((d) => d.target === previousName));
|
|
178
178
|
builder.removeNode(node.name);
|
|
179
179
|
invBuilder.removeNode(node.name);
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
// Re-add under the hoisted name as a new object. The node is shared by
|
|
181
|
+
// reference with the caller's graph, so renaming it in place would leave that
|
|
182
|
+
// graph with a node keyed `npm:<pkg>@<version>` but named `npm:<pkg>`, and
|
|
183
|
+
// every later prune of the same graph would fail to resolve the edge.
|
|
184
|
+
const hoistedNode = {
|
|
185
|
+
...node,
|
|
186
|
+
name: `npm:${node.data.packageName}`,
|
|
187
|
+
};
|
|
188
|
+
builder.addExternalNode(hoistedNode);
|
|
189
|
+
invBuilder.addExternalNode(hoistedNode);
|
|
184
190
|
targets.forEach((target) => {
|
|
185
|
-
builder.addStaticDependency(
|
|
186
|
-
invBuilder.addStaticDependency(target,
|
|
191
|
+
builder.addStaticDependency(hoistedNode.name, target);
|
|
192
|
+
invBuilder.addStaticDependency(target, hoistedNode.name);
|
|
187
193
|
});
|
|
188
194
|
sources.forEach((source) => {
|
|
189
|
-
builder.addStaticDependency(source,
|
|
190
|
-
invBuilder.addStaticDependency(
|
|
195
|
+
builder.addStaticDependency(source, hoistedNode.name);
|
|
196
|
+
invBuilder.addStaticDependency(hoistedNode.name, source);
|
|
191
197
|
});
|
|
192
198
|
}
|
|
193
199
|
// BFS to find the shortest path to a dependency specified in package.json
|