@trackunit/migrations 0.2.58-alpha-62031fc7c43.0 → 0.2.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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## 0.2.58 (2026-08-25)
|
|
2
|
+
|
|
3
|
+
### 🩹 Fixes
|
|
4
|
+
|
|
5
|
+
- **migrations:** route every directory listing through the tolerant reader [GLU-2029] ([eff5dba9767](https://github.com/Trackunit/manager/commit/eff5dba9767))
|
|
6
|
+
- **migrations:** warn when package discovery skips a directory [GLU-2028] ([09cc451476c](https://github.com/Trackunit/manager/commit/09cc451476c))
|
|
7
|
+
- **migrations:** GLU-2007 make discovery and react codemods work in consumer repos ([3f25bb543f9](https://github.com/Trackunit/manager/commit/3f25bb543f9))
|
|
8
|
+
|
|
9
|
+
### ❤️ Thank You
|
|
10
|
+
|
|
11
|
+
- Cursor @cursoragent
|
|
12
|
+
- Mikkel Thorbjørn Andersen
|
|
13
|
+
|
|
14
|
+
## 0.2.57 (2026-08-25)
|
|
15
|
+
|
|
16
|
+
This was a version bump only for migrations to align it with other projects, there were no code changes.
|
|
17
|
+
|
|
1
18
|
## 0.2.56 (2026-08-20)
|
|
2
19
|
|
|
3
20
|
This was a version bump only for migrations to align it with other projects, there were no code changes.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../libs/migrations/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["export {};\n"]}
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.discoverTrackunitPackages = exports.discoverWorkspaceTrackunitPackages = exports.compareVersions = exports.loadMigrationsJson = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
6
|
const fs_1 = require("fs");
|
|
6
7
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
8
|
const readPackageJson = (pkgJsonPath) => {
|
|
@@ -43,10 +44,31 @@ const semver_1 = require("semver");
|
|
|
43
44
|
*/
|
|
44
45
|
const compareVersions = (a, b) => (0, semver_1.compare)(a, b);
|
|
45
46
|
exports.compareVersions = compareVersions;
|
|
47
|
+
// Swallowing a failed directory read shrinks the discovered package set, which
|
|
48
|
+
// `migrate` then reports as "no pending migrations" -- indistinguishable from
|
|
49
|
+
// the green-run-no-codemods symptom this walker exists to fix. Warning keeps
|
|
50
|
+
// the walk crash-proof while making a truncated result visible.
|
|
51
|
+
const warnSkipped = (dir, error) => {
|
|
52
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
53
|
+
devkit_1.logger.warn(`Skipped "${dir}" while discovering @trackunit packages: ${reason}. Discovered migrations may be incomplete.`);
|
|
54
|
+
};
|
|
55
|
+
// `existsSync` only says the entry resolved, not that it can be listed: an
|
|
56
|
+
// unreadable store directory or a path that turns out to be a file still
|
|
57
|
+
// throws. Every listing in this module goes through here so one bad directory
|
|
58
|
+
// cannot abort discovery.
|
|
59
|
+
const readDirEntries = (dir) => {
|
|
60
|
+
try {
|
|
61
|
+
return (0, fs_1.readdirSync)(dir, { withFileTypes: true });
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
warnSkipped(dir, error);
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
};
|
|
46
68
|
const scanScopeDir = (scopeDir) => {
|
|
47
69
|
if (!(0, fs_1.existsSync)(scopeDir))
|
|
48
70
|
return [];
|
|
49
|
-
const entries = (
|
|
71
|
+
const entries = readDirEntries(scopeDir);
|
|
50
72
|
const found = [];
|
|
51
73
|
for (const entry of entries) {
|
|
52
74
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
@@ -69,6 +91,11 @@ const scanScopeDir = (scopeDir) => {
|
|
|
69
91
|
};
|
|
70
92
|
const MANAGER_REPO_PACKAGE_NAME = "@trackunit/manager";
|
|
71
93
|
const WORKSPACE_SCAN_ROOTS = ["libs", "apps"];
|
|
94
|
+
// Levels to climb from a resolved package directory to reach the
|
|
95
|
+
// `node_modules` holding it: `<store>/node_modules/name` versus
|
|
96
|
+
// `<store>/node_modules/@scope/name`.
|
|
97
|
+
const UNSCOPED_DEPTH = 1;
|
|
98
|
+
const SCOPED_DEPTH = 2;
|
|
72
99
|
const isManagerMonorepo = (cwd) => {
|
|
73
100
|
const rootPkgJson = readPackageJson(path_1.default.join(cwd, "package.json"));
|
|
74
101
|
return rootPkgJson?.name === MANAGER_REPO_PACKAGE_NAME;
|
|
@@ -81,14 +108,7 @@ const walkForPackageJsons = (rootDir, onPackageJson) => {
|
|
|
81
108
|
const dir = stack.pop();
|
|
82
109
|
if (dir === undefined)
|
|
83
110
|
continue;
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
entries = (0, fs_1.readdirSync)(dir, { withFileTypes: true });
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
for (const entry of entries) {
|
|
111
|
+
for (const entry of readDirEntries(dir)) {
|
|
92
112
|
if (entry.name === "node_modules" || entry.name.startsWith("."))
|
|
93
113
|
continue;
|
|
94
114
|
const entryPath = path_1.default.join(dir, entry.name);
|
|
@@ -135,49 +155,90 @@ exports.discoverWorkspaceTrackunitPackages = discoverWorkspaceTrackunitPackages;
|
|
|
135
155
|
* and nested node_modules inside every package. When the same package
|
|
136
156
|
* is found at multiple levels, the highest semver version is kept.
|
|
137
157
|
*
|
|
138
|
-
* Works with
|
|
158
|
+
* Works with hoisted layouts (npm, yarn) and with the isolated layouts
|
|
159
|
+
* where dependencies are symlinks into a content store (Yarn
|
|
160
|
+
* `nodeLinker: pnpm`, pnpm, bun), including pnpm with hoisting disabled,
|
|
161
|
+
* where a package's dependencies are siblings of it inside the store
|
|
162
|
+
* rather than nested beneath it.
|
|
139
163
|
*
|
|
140
164
|
* When invoked inside the manager monorepo (root package.json name
|
|
141
165
|
* is "@trackunit/manager"), additionally scans libs/ and apps/ for
|
|
142
166
|
* workspace packages that ship migrations, since those are not
|
|
143
167
|
* linked under node_modules/@trackunit/.
|
|
168
|
+
*
|
|
169
|
+
* @param cwd - Workspace root to scan; defaults to the current directory
|
|
170
|
+
* @returns {ReadonlyArray<DiscoveredPackage>} One entry per package name, at its highest discovered version
|
|
144
171
|
*/
|
|
145
|
-
const discoverTrackunitPackages = () => {
|
|
146
|
-
const cwd = process.cwd();
|
|
172
|
+
const discoverTrackunitPackages = (cwd = process.cwd()) => {
|
|
147
173
|
const rootNodeModules = path_1.default.resolve(cwd, "node_modules");
|
|
148
174
|
const allFound = [];
|
|
149
175
|
const visited = new Set();
|
|
150
176
|
const walkNodeModules = (nodeModulesDir) => {
|
|
151
177
|
if (!(0, fs_1.existsSync)(nodeModulesDir))
|
|
152
178
|
return;
|
|
153
|
-
|
|
179
|
+
// Isolated layouts (Yarn `nodeLinker: pnpm`, pnpm, bun) symlink every
|
|
180
|
+
// dependency into a content store, so a mutual dependency produces
|
|
181
|
+
// endless distinct logical paths pointing at the same real directories.
|
|
182
|
+
// `path.resolve` normalises but does not follow symlinks, so it cannot
|
|
183
|
+
// recognise the repeat and the walk grows the path until the OS rejects
|
|
184
|
+
// it. Canonicalising through the real path is what makes this terminate.
|
|
185
|
+
let canonical;
|
|
186
|
+
try {
|
|
187
|
+
canonical = (0, fs_1.realpathSync)(nodeModulesDir);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
warnSkipped(nodeModulesDir, error);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
154
193
|
if (visited.has(canonical))
|
|
155
194
|
return;
|
|
156
195
|
visited.add(canonical);
|
|
157
196
|
const trackunitScope = path_1.default.join(nodeModulesDir, "@trackunit");
|
|
158
197
|
allFound.push(...scanScopeDir(trackunitScope));
|
|
159
|
-
const
|
|
160
|
-
withFileTypes: true,
|
|
161
|
-
});
|
|
162
|
-
for (const entry of topEntries) {
|
|
198
|
+
for (const entry of readDirEntries(nodeModulesDir)) {
|
|
163
199
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
164
200
|
continue;
|
|
165
201
|
const entryPath = path_1.default.join(nodeModulesDir, entry.name);
|
|
166
202
|
if (entry.name.startsWith("@")) {
|
|
167
|
-
const
|
|
168
|
-
withFileTypes: true,
|
|
169
|
-
});
|
|
170
|
-
for (const scopeEntry of scopeEntries) {
|
|
203
|
+
for (const scopeEntry of readDirEntries(entryPath)) {
|
|
171
204
|
if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink())
|
|
172
205
|
continue;
|
|
173
|
-
|
|
206
|
+
const scopedPkgDir = path_1.default.join(entryPath, scopeEntry.name);
|
|
207
|
+
walkNodeModules(path_1.default.join(scopedPkgDir, "node_modules"));
|
|
208
|
+
walkStoreSiblingsOf(scopedPkgDir, SCOPED_DEPTH);
|
|
174
209
|
}
|
|
175
210
|
}
|
|
176
211
|
else {
|
|
177
212
|
walkNodeModules(path_1.default.join(entryPath, "node_modules"));
|
|
213
|
+
walkStoreSiblingsOf(entryPath, UNSCOPED_DEPTH);
|
|
178
214
|
}
|
|
179
215
|
}
|
|
180
216
|
};
|
|
217
|
+
/**
|
|
218
|
+
* pnpm with hoisting disabled places a package's dependencies *alongside*
|
|
219
|
+
* it inside the store rather than nested beneath it, so probing only
|
|
220
|
+
* `<entry>/node_modules` never reaches a @trackunit package that is purely
|
|
221
|
+
* transitive. Resolving the entry and walking the store `node_modules` that
|
|
222
|
+
* contains it reaches those siblings. In hoisted layouts this resolves back
|
|
223
|
+
* to a directory the walk has already scanned, so `visited` makes it free.
|
|
224
|
+
*/
|
|
225
|
+
function walkStoreSiblingsOf(pkgDir, depth) {
|
|
226
|
+
if (!(0, fs_1.existsSync)(pkgDir))
|
|
227
|
+
return;
|
|
228
|
+
let real;
|
|
229
|
+
try {
|
|
230
|
+
real = (0, fs_1.realpathSync)(pkgDir);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
warnSkipped(pkgDir, error);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
let candidate = real;
|
|
237
|
+
for (let level = 0; level < depth; level += 1)
|
|
238
|
+
candidate = path_1.default.dirname(candidate);
|
|
239
|
+
if (path_1.default.basename(candidate) === "node_modules")
|
|
240
|
+
walkNodeModules(candidate);
|
|
241
|
+
}
|
|
181
242
|
walkNodeModules(rootNodeModules);
|
|
182
243
|
if (isManagerMonorepo(cwd)) {
|
|
183
244
|
allFound.push(...(0, exports.discoverWorkspaceTrackunitPackages)(cwd));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover-packages.js","sourceRoot":"","sources":["../../../../../../libs/migrations/src/generators/utils/discover-packages.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync } from \"fs\";\nimport path from \"path\";\n\ntype PackageJsonWithMigrations = {\n readonly name?: string;\n readonly version: string;\n readonly migrations?: string | { readonly migrations: string };\n};\n\nexport type DiscoveredPackage = {\n readonly name: string;\n readonly version: string;\n readonly migrationsJsonPath: string;\n};\n\nconst readPackageJson = (pkgJsonPath: string): PackageJsonWithMigrations | undefined => {\n try {\n const raw = readFileSync(pkgJsonPath, \"utf-8\");\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n};\n\nconst resolveMigrationsPath = (pkgDir: string, pkgJson: PackageJsonWithMigrations): string | undefined => {\n const migrations = pkgJson.migrations;\n if (migrations === undefined) return undefined;\n\n const migrationsFile = typeof migrations === \"string\" ? migrations : migrations.migrations;\n const resolved = path.resolve(pkgDir, migrationsFile);\n\n return existsSync(resolved) ? resolved : undefined;\n};\n\n/** Compares two semver strings, returning negative/zero/positive like Array.sort comparators. */\n/** Loads and parses a migrations.json file from disk. */\nexport const loadMigrationsJson = (\n migrationsPath: string\n):\n | {\n readonly generators?: Record<\n string,\n { readonly version: string; readonly description: string; readonly implementation: string }\n >;\n }\n | undefined => {\n try {\n const raw = readFileSync(migrationsPath, \"utf-8\");\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n};\n\nimport { compare } from \"semver\";\n/**\n * Compare two semver strings, returning negative/zero/positive like Array.sort comparators.\n *\n * @param a - The first semver string to compare.\n * @param b - The second semver string to compare.\n * @returns {number} A negative number if a < b, 0 if a === b, and a positive number if a > b.\n */\nexport const compareVersions = (a: string, b: string): number => compare(a, b);\n\nconst scanScopeDir = (scopeDir: string): ReadonlyArray<DiscoveredPackage> => {\n if (!existsSync(scopeDir)) return [];\n\n const entries = readdirSync(scopeDir, { withFileTypes: true });\n const found: Array<DiscoveredPackage> = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\n const pkgDir = path.join(scopeDir, entry.name);\n const pkgJsonPath = path.join(pkgDir, \"package.json\");\n const pkgJson = readPackageJson(pkgJsonPath);\n\n if (!pkgJson) continue;\n\n const migrationsJsonPath = resolveMigrationsPath(pkgDir, pkgJson);\n if (migrationsJsonPath) {\n found.push({\n name: pkgJson.name ?? `@trackunit/${entry.name}`,\n version: pkgJson.version,\n migrationsJsonPath,\n });\n }\n }\n\n return found;\n};\n\nconst MANAGER_REPO_PACKAGE_NAME = \"@trackunit/manager\";\nconst WORKSPACE_SCAN_ROOTS: ReadonlyArray<string> = [\"libs\", \"apps\"];\n\nconst isManagerMonorepo = (cwd: string): boolean => {\n const rootPkgJson = readPackageJson(path.join(cwd, \"package.json\"));\n return rootPkgJson?.name === MANAGER_REPO_PACKAGE_NAME;\n};\n\nconst walkForPackageJsons = (rootDir: string, onPackageJson: (pkgJsonPath: string) => void): void => {\n if (!existsSync(rootDir)) return;\n\n const stack: Array<string> = [rootDir];\n while (stack.length > 0) {\n const dir = stack.pop();\n if (dir === undefined) continue;\n\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n const entryPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n stack.push(entryPath);\n } else if (entry.isFile() && entry.name === \"package.json\") {\n onPackageJson(entryPath);\n }\n }\n }\n};\n\n/**\n * In-monorepo fallback discovery: scans libs/ and apps/ for\n * package.json files whose name starts with \"@trackunit/\" and that\n * declare a \"migrations\" field. Used only when the engine is run\n * inside the manager monorepo (root package.json name is\n * \"@trackunit/manager\"), since internal workspace projects are not\n * symlinked under node_modules/@trackunit/* by Yarn.\n */\nexport const discoverWorkspaceTrackunitPackages = (cwd: string = process.cwd()): ReadonlyArray<DiscoveredPackage> => {\n const found: Array<DiscoveredPackage> = [];\n\n for (const root of WORKSPACE_SCAN_ROOTS) {\n walkForPackageJsons(path.join(cwd, root), pkgJsonPath => {\n const pkgJson = readPackageJson(pkgJsonPath);\n if (!pkgJson?.name || !pkgJson.name.startsWith(\"@trackunit/\")) return;\n\n const migrationsJsonPath = resolveMigrationsPath(path.dirname(pkgJsonPath), pkgJson);\n if (!migrationsJsonPath) return;\n\n found.push({\n name: pkgJson.name,\n version: pkgJson.version,\n migrationsJsonPath,\n });\n });\n }\n\n return found;\n};\n\n/**\n * Recursively walks node_modules directories looking for @trackunit/*\n * packages that declare a \"migrations\" field. Scans root node_modules\n * and nested node_modules inside every package. When the same package\n * is found at multiple levels, the highest semver version is kept.\n *\n * Works with all package managers (npm, yarn, pnpm, bun).\n *\n * When invoked inside the manager monorepo (root package.json name\n * is \"@trackunit/manager\"), additionally scans libs/ and apps/ for\n * workspace packages that ship migrations, since those are not\n * linked under node_modules/@trackunit/.\n */\nexport const discoverTrackunitPackages = (): ReadonlyArray<DiscoveredPackage> => {\n const cwd = process.cwd();\n const rootNodeModules = path.resolve(cwd, \"node_modules\");\n const allFound: Array<DiscoveredPackage> = [];\n const visited = new Set<string>();\n\n const walkNodeModules = (nodeModulesDir: string): void => {\n if (!existsSync(nodeModulesDir)) return;\n\n const canonical = path.resolve(nodeModulesDir);\n if (visited.has(canonical)) return;\n visited.add(canonical);\n\n const trackunitScope = path.join(nodeModulesDir, \"@trackunit\");\n allFound.push(...scanScopeDir(trackunitScope));\n\n const topEntries = readdirSync(nodeModulesDir, {\n withFileTypes: true,\n });\n for (const entry of topEntries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\n const entryPath = path.join(nodeModulesDir, entry.name);\n\n if (entry.name.startsWith(\"@\")) {\n const scopeEntries = readdirSync(entryPath, {\n withFileTypes: true,\n });\n for (const scopeEntry of scopeEntries) {\n if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink()) continue;\n walkNodeModules(path.join(entryPath, scopeEntry.name, \"node_modules\"));\n }\n } else {\n walkNodeModules(path.join(entryPath, \"node_modules\"));\n }\n }\n };\n\n walkNodeModules(rootNodeModules);\n\n if (isManagerMonorepo(cwd)) {\n allFound.push(...discoverWorkspaceTrackunitPackages(cwd));\n }\n\n const byName = new Map<string, DiscoveredPackage>();\n for (const pkg of allFound) {\n const existing = byName.get(pkg.name);\n if (!existing || compareVersions(pkg.version, existing.version) > 0) {\n byName.set(pkg.name, pkg);\n }\n }\n\n return [...byName.values()];\n};\n"]}
|
|
1
|
+
{"version":3,"file":"discover-packages.js","sourceRoot":"","sources":["../../../../../../libs/migrations/src/generators/utils/discover-packages.ts"],"names":[],"mappings":"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","sourcesContent":["import { logger } from \"@nx/devkit\";\nimport { type Dirent, existsSync, readdirSync, readFileSync, realpathSync } from \"fs\";\nimport path from \"path\";\n\ntype PackageJsonWithMigrations = {\n readonly name?: string;\n readonly version: string;\n readonly migrations?: string | { readonly migrations: string };\n};\n\nexport type DiscoveredPackage = {\n readonly name: string;\n readonly version: string;\n readonly migrationsJsonPath: string;\n};\n\nconst readPackageJson = (pkgJsonPath: string): PackageJsonWithMigrations | undefined => {\n try {\n const raw = readFileSync(pkgJsonPath, \"utf-8\");\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n};\n\nconst resolveMigrationsPath = (pkgDir: string, pkgJson: PackageJsonWithMigrations): string | undefined => {\n const migrations = pkgJson.migrations;\n if (migrations === undefined) return undefined;\n\n const migrationsFile = typeof migrations === \"string\" ? migrations : migrations.migrations;\n const resolved = path.resolve(pkgDir, migrationsFile);\n\n return existsSync(resolved) ? resolved : undefined;\n};\n\n/** Compares two semver strings, returning negative/zero/positive like Array.sort comparators. */\n/** Loads and parses a migrations.json file from disk. */\nexport const loadMigrationsJson = (\n migrationsPath: string\n):\n | {\n readonly generators?: Record<\n string,\n { readonly version: string; readonly description: string; readonly implementation: string }\n >;\n }\n | undefined => {\n try {\n const raw = readFileSync(migrationsPath, \"utf-8\");\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n};\n\nimport { compare } from \"semver\";\n/**\n * Compare two semver strings, returning negative/zero/positive like Array.sort comparators.\n *\n * @param a - The first semver string to compare.\n * @param b - The second semver string to compare.\n * @returns {number} A negative number if a < b, 0 if a === b, and a positive number if a > b.\n */\nexport const compareVersions = (a: string, b: string): number => compare(a, b);\n\n// Swallowing a failed directory read shrinks the discovered package set, which\n// `migrate` then reports as \"no pending migrations\" -- indistinguishable from\n// the green-run-no-codemods symptom this walker exists to fix. Warning keeps\n// the walk crash-proof while making a truncated result visible.\nconst warnSkipped = (dir: string, error: unknown): void => {\n const reason = error instanceof Error ? error.message : String(error);\n logger.warn(\n `Skipped \"${dir}\" while discovering @trackunit packages: ${reason}. Discovered migrations may be incomplete.`\n );\n};\n\n// `existsSync` only says the entry resolved, not that it can be listed: an\n// unreadable store directory or a path that turns out to be a file still\n// throws. Every listing in this module goes through here so one bad directory\n// cannot abort discovery.\nconst readDirEntries = (dir: string): ReadonlyArray<Dirent> => {\n try {\n return readdirSync(dir, { withFileTypes: true });\n } catch (error) {\n warnSkipped(dir, error);\n return [];\n }\n};\n\nconst scanScopeDir = (scopeDir: string): ReadonlyArray<DiscoveredPackage> => {\n if (!existsSync(scopeDir)) return [];\n\n const entries = readDirEntries(scopeDir);\n const found: Array<DiscoveredPackage> = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\n const pkgDir = path.join(scopeDir, entry.name);\n const pkgJsonPath = path.join(pkgDir, \"package.json\");\n const pkgJson = readPackageJson(pkgJsonPath);\n\n if (!pkgJson) continue;\n\n const migrationsJsonPath = resolveMigrationsPath(pkgDir, pkgJson);\n if (migrationsJsonPath) {\n found.push({\n name: pkgJson.name ?? `@trackunit/${entry.name}`,\n version: pkgJson.version,\n migrationsJsonPath,\n });\n }\n }\n\n return found;\n};\n\nconst MANAGER_REPO_PACKAGE_NAME = \"@trackunit/manager\";\nconst WORKSPACE_SCAN_ROOTS: ReadonlyArray<string> = [\"libs\", \"apps\"];\n\n// Levels to climb from a resolved package directory to reach the\n// `node_modules` holding it: `<store>/node_modules/name` versus\n// `<store>/node_modules/@scope/name`.\nconst UNSCOPED_DEPTH = 1;\nconst SCOPED_DEPTH = 2;\n\nconst isManagerMonorepo = (cwd: string): boolean => {\n const rootPkgJson = readPackageJson(path.join(cwd, \"package.json\"));\n return rootPkgJson?.name === MANAGER_REPO_PACKAGE_NAME;\n};\n\nconst walkForPackageJsons = (rootDir: string, onPackageJson: (pkgJsonPath: string) => void): void => {\n if (!existsSync(rootDir)) return;\n\n const stack: Array<string> = [rootDir];\n while (stack.length > 0) {\n const dir = stack.pop();\n if (dir === undefined) continue;\n\n for (const entry of readDirEntries(dir)) {\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n const entryPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n stack.push(entryPath);\n } else if (entry.isFile() && entry.name === \"package.json\") {\n onPackageJson(entryPath);\n }\n }\n }\n};\n\n/**\n * In-monorepo fallback discovery: scans libs/ and apps/ for\n * package.json files whose name starts with \"@trackunit/\" and that\n * declare a \"migrations\" field. Used only when the engine is run\n * inside the manager monorepo (root package.json name is\n * \"@trackunit/manager\"), since internal workspace projects are not\n * symlinked under node_modules/@trackunit/* by Yarn.\n */\nexport const discoverWorkspaceTrackunitPackages = (cwd: string = process.cwd()): ReadonlyArray<DiscoveredPackage> => {\n const found: Array<DiscoveredPackage> = [];\n\n for (const root of WORKSPACE_SCAN_ROOTS) {\n walkForPackageJsons(path.join(cwd, root), pkgJsonPath => {\n const pkgJson = readPackageJson(pkgJsonPath);\n if (!pkgJson?.name || !pkgJson.name.startsWith(\"@trackunit/\")) return;\n\n const migrationsJsonPath = resolveMigrationsPath(path.dirname(pkgJsonPath), pkgJson);\n if (!migrationsJsonPath) return;\n\n found.push({\n name: pkgJson.name,\n version: pkgJson.version,\n migrationsJsonPath,\n });\n });\n }\n\n return found;\n};\n\n/**\n * Recursively walks node_modules directories looking for @trackunit/*\n * packages that declare a \"migrations\" field. Scans root node_modules\n * and nested node_modules inside every package. When the same package\n * is found at multiple levels, the highest semver version is kept.\n *\n * Works with hoisted layouts (npm, yarn) and with the isolated layouts\n * where dependencies are symlinks into a content store (Yarn\n * `nodeLinker: pnpm`, pnpm, bun), including pnpm with hoisting disabled,\n * where a package's dependencies are siblings of it inside the store\n * rather than nested beneath it.\n *\n * When invoked inside the manager monorepo (root package.json name\n * is \"@trackunit/manager\"), additionally scans libs/ and apps/ for\n * workspace packages that ship migrations, since those are not\n * linked under node_modules/@trackunit/.\n *\n * @param cwd - Workspace root to scan; defaults to the current directory\n * @returns {ReadonlyArray<DiscoveredPackage>} One entry per package name, at its highest discovered version\n */\nexport const discoverTrackunitPackages = (cwd: string = process.cwd()): ReadonlyArray<DiscoveredPackage> => {\n const rootNodeModules = path.resolve(cwd, \"node_modules\");\n const allFound: Array<DiscoveredPackage> = [];\n const visited = new Set<string>();\n\n const walkNodeModules = (nodeModulesDir: string): void => {\n if (!existsSync(nodeModulesDir)) return;\n\n // Isolated layouts (Yarn `nodeLinker: pnpm`, pnpm, bun) symlink every\n // dependency into a content store, so a mutual dependency produces\n // endless distinct logical paths pointing at the same real directories.\n // `path.resolve` normalises but does not follow symlinks, so it cannot\n // recognise the repeat and the walk grows the path until the OS rejects\n // it. Canonicalising through the real path is what makes this terminate.\n let canonical: string;\n try {\n canonical = realpathSync(nodeModulesDir);\n } catch (error) {\n warnSkipped(nodeModulesDir, error);\n return;\n }\n if (visited.has(canonical)) return;\n visited.add(canonical);\n\n const trackunitScope = path.join(nodeModulesDir, \"@trackunit\");\n allFound.push(...scanScopeDir(trackunitScope));\n\n for (const entry of readDirEntries(nodeModulesDir)) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\n const entryPath = path.join(nodeModulesDir, entry.name);\n\n if (entry.name.startsWith(\"@\")) {\n for (const scopeEntry of readDirEntries(entryPath)) {\n if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink()) continue;\n const scopedPkgDir = path.join(entryPath, scopeEntry.name);\n walkNodeModules(path.join(scopedPkgDir, \"node_modules\"));\n walkStoreSiblingsOf(scopedPkgDir, SCOPED_DEPTH);\n }\n } else {\n walkNodeModules(path.join(entryPath, \"node_modules\"));\n walkStoreSiblingsOf(entryPath, UNSCOPED_DEPTH);\n }\n }\n };\n\n /**\n * pnpm with hoisting disabled places a package's dependencies *alongside*\n * it inside the store rather than nested beneath it, so probing only\n * `<entry>/node_modules` never reaches a @trackunit package that is purely\n * transitive. Resolving the entry and walking the store `node_modules` that\n * contains it reaches those siblings. In hoisted layouts this resolves back\n * to a directory the walk has already scanned, so `visited` makes it free.\n */\n function walkStoreSiblingsOf(pkgDir: string, depth: number): void {\n if (!existsSync(pkgDir)) return;\n\n let real: string;\n try {\n real = realpathSync(pkgDir);\n } catch (error) {\n warnSkipped(pkgDir, error);\n return;\n }\n\n let candidate = real;\n for (let level = 0; level < depth; level += 1) candidate = path.dirname(candidate);\n\n if (path.basename(candidate) === \"node_modules\") walkNodeModules(candidate);\n }\n\n walkNodeModules(rootNodeModules);\n\n if (isManagerMonorepo(cwd)) {\n allFound.push(...discoverWorkspaceTrackunitPackages(cwd));\n }\n\n const byName = new Map<string, DiscoveredPackage>();\n for (const pkg of allFound) {\n const existing = byName.get(pkg.name);\n if (!existing || compareVersions(pkg.version, existing.version) > 0) {\n byName.set(pkg.name, pkg);\n }\n }\n\n return [...byName.values()];\n};\n"]}
|