@trackunit/migrations 0.2.58 → 0.2.59
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,14 @@
|
|
|
1
|
+
## 0.2.59 (2026-08-25)
|
|
2
|
+
|
|
3
|
+
### 🩹 Fixes
|
|
4
|
+
|
|
5
|
+
- **migrations:** load migrations from source inside the manager monorepo ([3c22bb2527f](https://github.com/Trackunit/manager/commit/3c22bb2527f))
|
|
6
|
+
|
|
7
|
+
### ❤️ Thank You
|
|
8
|
+
|
|
9
|
+
- Cursor @cursoragent
|
|
10
|
+
- Mikkel Thorbjørn Andersen
|
|
11
|
+
|
|
1
12
|
## 0.2.58 (2026-08-25)
|
|
2
13
|
|
|
3
14
|
### 🩹 Fixes
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = function extensionlessMigration() {};
|
|
@@ -3,8 +3,64 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runMigrations = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const devkit_1 = require("@nx/devkit");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const module_1 = require("module");
|
|
6
8
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
9
|
+
const url_1 = require("url");
|
|
7
10
|
const discover_packages_1 = require("../utils/discover-packages");
|
|
11
|
+
const requireMigration = (0, module_1.createRequire)(__filename);
|
|
12
|
+
const MODULE_EXTENSIONS = [".js", ".cjs", ".mjs", ".ts", ".tsx"];
|
|
13
|
+
const isFile = (candidate) => {
|
|
14
|
+
try {
|
|
15
|
+
return (0, fs_1.statSync)(candidate).isFile();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* `trackunit-migrations.json` stores implementation paths without a file
|
|
23
|
+
* extension. Node's own resolution only probes `.js`/`.json`/`.node`, and
|
|
24
|
+
* only for `require` (never for `import()`), so the stored path has to be
|
|
25
|
+
* expanded to a concrete file before loading.
|
|
26
|
+
*/
|
|
27
|
+
const resolveModuleFile = (basePath) => {
|
|
28
|
+
if (isFile(basePath))
|
|
29
|
+
return basePath;
|
|
30
|
+
for (const ext of MODULE_EXTENSIONS) {
|
|
31
|
+
const candidate = `${basePath}${ext}`;
|
|
32
|
+
if (isFile(candidate))
|
|
33
|
+
return candidate;
|
|
34
|
+
}
|
|
35
|
+
for (const ext of MODULE_EXTENSIONS) {
|
|
36
|
+
const candidate = path_1.default.join(basePath, `index${ext}`);
|
|
37
|
+
if (isFile(candidate))
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
return basePath;
|
|
41
|
+
};
|
|
42
|
+
let typeScriptLoaderRegistered = false;
|
|
43
|
+
/**
|
|
44
|
+
* Running inside the manager monorepo discovers migrations as TypeScript
|
|
45
|
+
* source, whose sibling imports are extensionless and whose `@trackunit/*`
|
|
46
|
+
* imports resolve through workspace tsconfig path aliases. Nx's generator
|
|
47
|
+
* runtime transpiles the explicitly-named generator files but does not add
|
|
48
|
+
* a require hook that resolves either, so a real migration and everything
|
|
49
|
+
* it imports must be loaded through a TypeScript register plus tsconfig
|
|
50
|
+
* path resolution. Consumers run compiled `.js` and never reach this branch,
|
|
51
|
+
* so the loaders stay dev-only dependencies of the manager monorepo.
|
|
52
|
+
*/
|
|
53
|
+
const ensureTypeScriptLoader = () => {
|
|
54
|
+
if (typeScriptLoaderRegistered)
|
|
55
|
+
return;
|
|
56
|
+
const tsConfigPaths = requireMigration("tsconfig-paths");
|
|
57
|
+
const configResult = tsConfigPaths.loadConfig(path_1.default.join(process.cwd(), "tsconfig.base.json"));
|
|
58
|
+
if (configResult.resultType === "success") {
|
|
59
|
+
tsConfigPaths.register({ baseUrl: configResult.absoluteBaseUrl, paths: configResult.paths });
|
|
60
|
+
}
|
|
61
|
+
requireMigration("@swc-node/register");
|
|
62
|
+
typeScriptLoaderRegistered = true;
|
|
63
|
+
};
|
|
8
64
|
/**
|
|
9
65
|
* Resolves a stored implementation path. Relative paths in
|
|
10
66
|
* `trackunit-migrations.json` are interpreted as relative to the
|
|
@@ -13,11 +69,37 @@ const discover_packages_1 = require("../utils/discover-packages");
|
|
|
13
69
|
* preserving back-compat with absolute paths.
|
|
14
70
|
*/
|
|
15
71
|
const resolveImplementationPath = (storedPath) => path_1.default.isAbsolute(storedPath) ? storedPath : path_1.default.resolve(process.cwd(), storedPath);
|
|
72
|
+
const isEsmRequireError = (error) => error instanceof Error && "code" in error && error.code === "ERR_REQUIRE_ESM";
|
|
73
|
+
/**
|
|
74
|
+
* Loads a migration module from an implementation path that is stored
|
|
75
|
+
* without a file extension (see `portableImplementationPath` in the
|
|
76
|
+
* `migrate` generator). The path is expanded to a concrete file, then
|
|
77
|
+
* loaded with `require`, which resolves `.js` for the built/published
|
|
78
|
+
* output consumers run and `.ts` for source when running inside the
|
|
79
|
+
* manager monorepo (a TypeScript loader is registered on demand for that
|
|
80
|
+
* case). Native `import()` performs no extension resolution and cannot load
|
|
81
|
+
* these paths at all; it is kept only as a fallback for genuine ESM output
|
|
82
|
+
* (rare — the migrations build emits CommonJS).
|
|
83
|
+
*/
|
|
84
|
+
const loadMigrationModule = async (resolvedPath) => {
|
|
85
|
+
const file = resolveModuleFile(resolvedPath);
|
|
86
|
+
if (file.endsWith(".ts") || file.endsWith(".tsx")) {
|
|
87
|
+
ensureTypeScriptLoader();
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return requireMigration(file);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (!isEsmRequireError(error))
|
|
94
|
+
throw error;
|
|
95
|
+
return Promise.resolve(`${(0, url_1.pathToFileURL)(file).href}`).then(s => tslib_1.__importStar(require(s)));
|
|
96
|
+
}
|
|
97
|
+
};
|
|
16
98
|
const TRACKUNIT_MIGRATIONS_FILE = "trackunit-migrations.json";
|
|
17
99
|
const runMigration = async (tree, migration, verbose) => {
|
|
18
100
|
try {
|
|
19
101
|
const resolvedPath = resolveImplementationPath(migration.implementation);
|
|
20
|
-
const migrationModule = await
|
|
102
|
+
const migrationModule = await loadMigrationModule(resolvedPath);
|
|
21
103
|
const migrationFn = migrationModule.default ?? migrationModule;
|
|
22
104
|
if (typeof migrationFn !== "function") {
|
|
23
105
|
devkit_1.logger.warn(` "${migration.name}" does not export a function, skipping`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../libs/migrations/src/generators/run-migrations/generator.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Tree, formatFiles, logger, readJson } from \"@nx/devkit\";\nimport path from \"path\";\nimport { compareVersions } from \"../utils/discover-packages\";\n\ntype RunMigrationsSchema = {\n readonly verbose?: boolean;\n};\n\n/**\n * Resolves a stored implementation path. Relative paths in\n * `trackunit-migrations.json` are interpreted as relative to the\n * workspace root (`process.cwd()`); absolute paths are used as-is.\n * This keeps the on-disk file portable between checkouts while\n * preserving back-compat with absolute paths.\n */\nconst resolveImplementationPath = (storedPath: string): string =>\n path.isAbsolute(storedPath) ? storedPath : path.resolve(process.cwd(), storedPath);\n\ntype PendingMigration = {\n readonly package: string;\n readonly name: string;\n readonly version: string;\n readonly description: string;\n readonly implementation: string;\n};\n\ntype TrackunitMigrationsFile = {\n readonly migratedVersions: Record<string, string>;\n readonly migrations: ReadonlyArray<PendingMigration>;\n};\n\nconst TRACKUNIT_MIGRATIONS_FILE = \"trackunit-migrations.json\";\n\nconst runMigration = async (tree: Tree, migration: PendingMigration, verbose: boolean): Promise<boolean> => {\n try {\n const resolvedPath = resolveImplementationPath(migration.implementation);\n const migrationModule: Record<string, unknown> = await import(resolvedPath);\n const migrationFn = migrationModule.default ?? migrationModule;\n\n if (typeof migrationFn !== \"function\") {\n logger.warn(` \"${migration.name}\" does not export a function, skipping`);\n return false;\n }\n\n if (verbose) {\n logger.info(` Running: ${migration.name} (v${migration.version}) from ${migration.package}`);\n }\n\n migrationFn(tree);\n return true;\n } catch (error) {\n logger.error(` Failed \"${migration.name}\": ${error instanceof Error ? error.message : String(error)}`);\n return false;\n }\n};\n\n/**\n * Step 2: Reads trackunit-migrations.json and executes each pending\n * migration. Successful migrations are removed from the pending list\n * and bump `migratedVersions[package]` to their version. Failures\n * stay in the list so they remain retryable on the next run without\n * having to go through `migrate` again.\n *\n * Usage: nx g @trackunit/migrations:run-migrations\n */\nexport const runMigrations = async (tree: Tree, options: RunMigrationsSchema): Promise<void> => {\n if (!tree.exists(TRACKUNIT_MIGRATIONS_FILE)) {\n logger.error(`${TRACKUNIT_MIGRATIONS_FILE} not found. Run \"nx g @trackunit/migrations:migrate\" first.`);\n return;\n }\n\n const file: TrackunitMigrationsFile = readJson(tree, TRACKUNIT_MIGRATIONS_FILE);\n\n if (file.migrations.length === 0) {\n logger.info(\"No pending migrations to run.\");\n return;\n }\n\n logger.info(`Running ${file.migrations.length} migration(s)...\\n`);\n\n let succeeded = 0;\n const remaining: Array<PendingMigration> = [];\n const migratedVersions: Record<string, string> = { ...file.migratedVersions };\n\n for (const migration of file.migrations) {\n logger.info(`${migration.package}: ${migration.name} - ${migration.description}`);\n\n const ok = await runMigration(tree, migration, options.verbose ?? false);\n if (ok) {\n succeeded++;\n const current = migratedVersions[migration.package];\n if (current === undefined || compareVersions(migration.version, current) > 0) {\n migratedVersions[migration.package] = migration.version;\n }\n } else {\n remaining.push(migration);\n }\n }\n\n const updated: TrackunitMigrationsFile = {\n migratedVersions,\n migrations: remaining,\n };\n tree.write(TRACKUNIT_MIGRATIONS_FILE, JSON.stringify(updated, null, 2));\n\n await formatFiles(tree);\n\n logger.info(`\\n${succeeded} migration(s) applied successfully.`);\n if (remaining.length > 0) {\n logger.warn(\n `${remaining.length} migration(s) failed and remain in ${TRACKUNIT_MIGRATIONS_FILE}. Fix the cause and re-run \"nx g @trackunit/migrations:run-migrations\".`\n );\n } else {\n logger.info(`\\nYou can delete ${TRACKUNIT_MIGRATIONS_FILE} or keep it for version tracking.`);\n }\n};\n\nexport default runMigrations;\n"]}
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../libs/migrations/src/generators/run-migrations/generator.ts"],"names":[],"mappings":";;;;AAAA,uCAAsE;AACtE,2BAA8B;AAC9B,mCAAuC;AACvC,wDAAwB;AACxB,6BAAoC;AACpC,kEAA6D;AAM7D,MAAM,gBAAgB,GAAG,IAAA,sBAAa,EAAC,UAAU,CAAC,CAAC;AAEnD,MAAM,iBAAiB,GAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAExF,MAAM,MAAM,GAAG,CAAC,SAAiB,EAAW,EAAE;IAC5C,IAAI,CAAC;QACH,OAAO,IAAA,aAAQ,EAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CAAC,QAAgB,EAAU,EAAE;IACrD,IAAI,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,GAAG,QAAQ,GAAG,GAAG,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC1C,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,EAAE,CAAC,CAAC;QACrD,IAAI,MAAM,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC1C,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAaF,IAAI,0BAA0B,GAAG,KAAK,CAAC;AAEvC;;;;;;;;;GASG;AACH,MAAM,sBAAsB,GAAG,GAAS,EAAE;IACxC,IAAI,0BAA0B;QAAE,OAAO;IAEvC,MAAM,aAAa,GAAwB,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAC9F,IAAI,YAAY,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAC1C,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,eAAe,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/F,CAAC;IAED,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;IACvC,0BAA0B,GAAG,IAAI,CAAC;AACpC,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,yBAAyB,GAAG,CAAC,UAAkB,EAAU,EAAE,CAC/D,cAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;AAErF,MAAM,iBAAiB,GAAG,CAAC,KAAc,EAAW,EAAE,CACpD,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,MAAM,mBAAmB,GAAG,KAAK,EAAE,YAAoB,EAAoC,EAAE;IAC3F,MAAM,IAAI,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAE7C,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,sBAAsB,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC;QACH,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QAC3C,0BAAc,IAAA,mBAAa,EAAC,IAAI,CAAC,CAAC,IAAI,gDAAE;IAC1C,CAAC;AACH,CAAC,CAAC;AAeF,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAE9D,MAAM,YAAY,GAAG,KAAK,EAAE,IAAU,EAAE,SAA2B,EAAE,OAAgB,EAAoB,EAAE;IACzG,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,yBAAyB,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACzE,MAAM,eAAe,GAAG,MAAM,mBAAmB,CAAC,YAAY,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,IAAI,eAAe,CAAC;QAE/D,IAAI,OAAO,WAAW,KAAK,UAAU,EAAE,CAAC;YACtC,eAAM,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,IAAI,wCAAwC,CAAC,CAAC;YAC1E,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,eAAM,CAAC,IAAI,CAAC,cAAc,SAAS,CAAC,IAAI,MAAM,SAAS,CAAC,OAAO,UAAU,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;QAChG,CAAC;QAED,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,aAAa,SAAS,CAAC,IAAI,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxG,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACI,MAAM,aAAa,GAAG,KAAK,EAAE,IAAU,EAAE,OAA4B,EAAiB,EAAE;IAC7F,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC5C,eAAM,CAAC,KAAK,CAAC,GAAG,yBAAyB,6DAA6D,CAAC,CAAC;QACxG,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAA4B,IAAA,iBAAQ,EAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC;IAEhF,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,eAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,eAAM,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU,CAAC,MAAM,oBAAoB,CAAC,CAAC;IAEnE,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,MAAM,SAAS,GAA4B,EAAE,CAAC;IAC9C,MAAM,gBAAgB,GAA2B,EAAE,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAE9E,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,eAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,IAAI,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAElF,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;QACzE,IAAI,EAAE,EAAE,CAAC;YACP,SAAS,EAAE,CAAC;YACZ,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACpD,IAAI,OAAO,KAAK,SAAS,IAAI,IAAA,mCAAe,EAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7E,gBAAgB,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC;YAC1D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAA4B;QACvC,gBAAgB;QAChB,UAAU,EAAE,SAAS;KACtB,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,yBAAyB,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAExE,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IAExB,eAAM,CAAC,IAAI,CAAC,KAAK,SAAS,qCAAqC,CAAC,CAAC;IACjE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,eAAM,CAAC,IAAI,CACT,GAAG,SAAS,CAAC,MAAM,sCAAsC,yBAAyB,yEAAyE,CAC5J,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,eAAM,CAAC,IAAI,CAAC,oBAAoB,yBAAyB,mCAAmC,CAAC,CAAC;IAChG,CAAC;AACH,CAAC,CAAC;AAlDW,QAAA,aAAa,iBAkDxB;AAEF,kBAAe,qBAAa,CAAC","sourcesContent":["import { type Tree, formatFiles, logger, readJson } from \"@nx/devkit\";\nimport { statSync } from \"fs\";\nimport { createRequire } from \"module\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\nimport { compareVersions } from \"../utils/discover-packages\";\n\ntype RunMigrationsSchema = {\n readonly verbose?: boolean;\n};\n\nconst requireMigration = createRequire(__filename);\n\nconst MODULE_EXTENSIONS: ReadonlyArray<string> = [\".js\", \".cjs\", \".mjs\", \".ts\", \".tsx\"];\n\nconst isFile = (candidate: string): boolean => {\n try {\n return statSync(candidate).isFile();\n } catch {\n return false;\n }\n};\n\n/**\n * `trackunit-migrations.json` stores implementation paths without a file\n * extension. Node's own resolution only probes `.js`/`.json`/`.node`, and\n * only for `require` (never for `import()`), so the stored path has to be\n * expanded to a concrete file before loading.\n */\nconst resolveModuleFile = (basePath: string): string => {\n if (isFile(basePath)) return basePath;\n for (const ext of MODULE_EXTENSIONS) {\n const candidate = `${basePath}${ext}`;\n if (isFile(candidate)) return candidate;\n }\n for (const ext of MODULE_EXTENSIONS) {\n const candidate = path.join(basePath, `index${ext}`);\n if (isFile(candidate)) return candidate;\n }\n return basePath;\n};\n\ntype TsConfigPaths = Record<string, ReadonlyArray<string>>;\n\ntype TsConfigLoadResult =\n | { readonly resultType: \"success\"; readonly absoluteBaseUrl: string; readonly paths: TsConfigPaths }\n | { readonly resultType: \"failed\" };\n\ntype TsConfigPathsModule = {\n readonly loadConfig: (cwd: string) => TsConfigLoadResult;\n readonly register: (options: { readonly baseUrl: string; readonly paths: TsConfigPaths }) => void;\n};\n\nlet typeScriptLoaderRegistered = false;\n\n/**\n * Running inside the manager monorepo discovers migrations as TypeScript\n * source, whose sibling imports are extensionless and whose `@trackunit/*`\n * imports resolve through workspace tsconfig path aliases. Nx's generator\n * runtime transpiles the explicitly-named generator files but does not add\n * a require hook that resolves either, so a real migration and everything\n * it imports must be loaded through a TypeScript register plus tsconfig\n * path resolution. Consumers run compiled `.js` and never reach this branch,\n * so the loaders stay dev-only dependencies of the manager monorepo.\n */\nconst ensureTypeScriptLoader = (): void => {\n if (typeScriptLoaderRegistered) return;\n\n const tsConfigPaths: TsConfigPathsModule = requireMigration(\"tsconfig-paths\");\n const configResult = tsConfigPaths.loadConfig(path.join(process.cwd(), \"tsconfig.base.json\"));\n if (configResult.resultType === \"success\") {\n tsConfigPaths.register({ baseUrl: configResult.absoluteBaseUrl, paths: configResult.paths });\n }\n\n requireMigration(\"@swc-node/register\");\n typeScriptLoaderRegistered = true;\n};\n\n/**\n * Resolves a stored implementation path. Relative paths in\n * `trackunit-migrations.json` are interpreted as relative to the\n * workspace root (`process.cwd()`); absolute paths are used as-is.\n * This keeps the on-disk file portable between checkouts while\n * preserving back-compat with absolute paths.\n */\nconst resolveImplementationPath = (storedPath: string): string =>\n path.isAbsolute(storedPath) ? storedPath : path.resolve(process.cwd(), storedPath);\n\nconst isEsmRequireError = (error: unknown): boolean =>\n error instanceof Error && \"code\" in error && error.code === \"ERR_REQUIRE_ESM\";\n\n/**\n * Loads a migration module from an implementation path that is stored\n * without a file extension (see `portableImplementationPath` in the\n * `migrate` generator). The path is expanded to a concrete file, then\n * loaded with `require`, which resolves `.js` for the built/published\n * output consumers run and `.ts` for source when running inside the\n * manager monorepo (a TypeScript loader is registered on demand for that\n * case). Native `import()` performs no extension resolution and cannot load\n * these paths at all; it is kept only as a fallback for genuine ESM output\n * (rare — the migrations build emits CommonJS).\n */\nconst loadMigrationModule = async (resolvedPath: string): Promise<Record<string, unknown>> => {\n const file = resolveModuleFile(resolvedPath);\n\n if (file.endsWith(\".ts\") || file.endsWith(\".tsx\")) {\n ensureTypeScriptLoader();\n }\n\n try {\n return requireMigration(file);\n } catch (error) {\n if (!isEsmRequireError(error)) throw error;\n return import(pathToFileURL(file).href);\n }\n};\n\ntype PendingMigration = {\n readonly package: string;\n readonly name: string;\n readonly version: string;\n readonly description: string;\n readonly implementation: string;\n};\n\ntype TrackunitMigrationsFile = {\n readonly migratedVersions: Record<string, string>;\n readonly migrations: ReadonlyArray<PendingMigration>;\n};\n\nconst TRACKUNIT_MIGRATIONS_FILE = \"trackunit-migrations.json\";\n\nconst runMigration = async (tree: Tree, migration: PendingMigration, verbose: boolean): Promise<boolean> => {\n try {\n const resolvedPath = resolveImplementationPath(migration.implementation);\n const migrationModule = await loadMigrationModule(resolvedPath);\n const migrationFn = migrationModule.default ?? migrationModule;\n\n if (typeof migrationFn !== \"function\") {\n logger.warn(` \"${migration.name}\" does not export a function, skipping`);\n return false;\n }\n\n if (verbose) {\n logger.info(` Running: ${migration.name} (v${migration.version}) from ${migration.package}`);\n }\n\n migrationFn(tree);\n return true;\n } catch (error) {\n logger.error(` Failed \"${migration.name}\": ${error instanceof Error ? error.message : String(error)}`);\n return false;\n }\n};\n\n/**\n * Step 2: Reads trackunit-migrations.json and executes each pending\n * migration. Successful migrations are removed from the pending list\n * and bump `migratedVersions[package]` to their version. Failures\n * stay in the list so they remain retryable on the next run without\n * having to go through `migrate` again.\n *\n * Usage: nx g @trackunit/migrations:run-migrations\n */\nexport const runMigrations = async (tree: Tree, options: RunMigrationsSchema): Promise<void> => {\n if (!tree.exists(TRACKUNIT_MIGRATIONS_FILE)) {\n logger.error(`${TRACKUNIT_MIGRATIONS_FILE} not found. Run \"nx g @trackunit/migrations:migrate\" first.`);\n return;\n }\n\n const file: TrackunitMigrationsFile = readJson(tree, TRACKUNIT_MIGRATIONS_FILE);\n\n if (file.migrations.length === 0) {\n logger.info(\"No pending migrations to run.\");\n return;\n }\n\n logger.info(`Running ${file.migrations.length} migration(s)...\\n`);\n\n let succeeded = 0;\n const remaining: Array<PendingMigration> = [];\n const migratedVersions: Record<string, string> = { ...file.migratedVersions };\n\n for (const migration of file.migrations) {\n logger.info(`${migration.package}: ${migration.name} - ${migration.description}`);\n\n const ok = await runMigration(tree, migration, options.verbose ?? false);\n if (ok) {\n succeeded++;\n const current = migratedVersions[migration.package];\n if (current === undefined || compareVersions(migration.version, current) > 0) {\n migratedVersions[migration.package] = migration.version;\n }\n } else {\n remaining.push(migration);\n }\n }\n\n const updated: TrackunitMigrationsFile = {\n migratedVersions,\n migrations: remaining,\n };\n tree.write(TRACKUNIT_MIGRATIONS_FILE, JSON.stringify(updated, null, 2));\n\n await formatFiles(tree);\n\n logger.info(`\\n${succeeded} migration(s) applied successfully.`);\n if (remaining.length > 0) {\n logger.warn(\n `${remaining.length} migration(s) failed and remain in ${TRACKUNIT_MIGRATIONS_FILE}. Fix the cause and re-run \"nx g @trackunit/migrations:run-migrations\".`\n );\n } else {\n logger.info(`\\nYou can delete ${TRACKUNIT_MIGRATIONS_FILE} or keep it for version tracking.`);\n }\n};\n\nexport default runMigrations;\n"]}
|