@memberjunction/cli 6.1.0-edge.2 → 6.1.0-edge.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +180 -4
- package/README.md +1 -1
- package/dist/commands/codegen/manifest.d.ts +14 -0
- package/dist/commands/codegen/manifest.d.ts.map +1 -1
- package/dist/commands/codegen/manifest.js +26 -7
- package/dist/commands/codegen/manifest.js.map +1 -1
- package/dist/commands/dev/index.d.ts +7 -0
- package/dist/commands/dev/index.d.ts.map +1 -0
- package/dist/commands/dev/index.js +11 -0
- package/dist/commands/dev/index.js.map +1 -0
- package/dist/commands/dev/usage.d.ts +7 -0
- package/dist/commands/dev/usage.d.ts.map +1 -0
- package/dist/commands/dev/usage.js +10 -0
- package/dist/commands/dev/usage.js.map +1 -0
- package/dist/commands/dev/workspace/clean.d.ts +27 -0
- package/dist/commands/dev/workspace/clean.d.ts.map +1 -0
- package/dist/commands/dev/workspace/clean.js +87 -0
- package/dist/commands/dev/workspace/clean.js.map +1 -0
- package/dist/commands/dev/workspace/doctor.d.ts +28 -0
- package/dist/commands/dev/workspace/doctor.d.ts.map +1 -0
- package/dist/commands/dev/workspace/doctor.js +62 -0
- package/dist/commands/dev/workspace/doctor.js.map +1 -0
- package/dist/commands/dev/workspace/index.d.ts +60 -0
- package/dist/commands/dev/workspace/index.d.ts.map +1 -0
- package/dist/commands/dev/workspace/index.js +279 -0
- package/dist/commands/dev/workspace/index.js.map +1 -0
- package/dist/commands/dev/workspace/status.d.ts +19 -0
- package/dist/commands/dev/workspace/status.d.ts.map +1 -0
- package/dist/commands/dev/workspace/status.js +46 -0
- package/dist/commands/dev/workspace/status.js.map +1 -0
- package/dist/commands/migrate/convert.d.ts +25 -0
- package/dist/commands/migrate/convert.d.ts.map +1 -1
- package/dist/commands/migrate/convert.js +61 -8
- package/dist/commands/migrate/convert.js.map +1 -1
- package/dist/commands/migrate/index.d.ts +9 -0
- package/dist/commands/migrate/index.d.ts.map +1 -1
- package/dist/commands/migrate/index.js +41 -1
- package/dist/commands/migrate/index.js.map +1 -1
- package/dist/lib/cli-plugins.d.ts.map +1 -1
- package/dist/lib/cli-plugins.js +5 -0
- package/dist/lib/cli-plugins.js.map +1 -1
- package/dist/lib/dev-workspace/build.d.ts +180 -0
- package/dist/lib/dev-workspace/build.d.ts.map +1 -0
- package/dist/lib/dev-workspace/build.js +570 -0
- package/dist/lib/dev-workspace/build.js.map +1 -0
- package/dist/lib/dev-workspace/clean.d.ts +40 -0
- package/dist/lib/dev-workspace/clean.d.ts.map +1 -0
- package/dist/lib/dev-workspace/clean.js +193 -0
- package/dist/lib/dev-workspace/clean.js.map +1 -0
- package/dist/lib/dev-workspace/detect.d.ts +56 -0
- package/dist/lib/dev-workspace/detect.d.ts.map +1 -0
- package/dist/lib/dev-workspace/detect.js +343 -0
- package/dist/lib/dev-workspace/detect.js.map +1 -0
- package/dist/lib/dev-workspace/dir-flag.d.ts +23 -0
- package/dist/lib/dev-workspace/dir-flag.d.ts.map +1 -0
- package/dist/lib/dev-workspace/dir-flag.js +24 -0
- package/dist/lib/dev-workspace/dir-flag.js.map +1 -0
- package/dist/lib/dev-workspace/doctor.d.ts +96 -0
- package/dist/lib/dev-workspace/doctor.d.ts.map +1 -0
- package/dist/lib/dev-workspace/doctor.js +400 -0
- package/dist/lib/dev-workspace/doctor.js.map +1 -0
- package/dist/lib/dev-workspace/lockfile.d.ts +62 -0
- package/dist/lib/dev-workspace/lockfile.d.ts.map +1 -0
- package/dist/lib/dev-workspace/lockfile.js +456 -0
- package/dist/lib/dev-workspace/lockfile.js.map +1 -0
- package/dist/lib/dev-workspace/member-installs.d.ts +35 -0
- package/dist/lib/dev-workspace/member-installs.d.ts.map +1 -0
- package/dist/lib/dev-workspace/member-installs.js +139 -0
- package/dist/lib/dev-workspace/member-installs.js.map +1 -0
- package/dist/lib/dev-workspace/pnpm.d.ts +29 -0
- package/dist/lib/dev-workspace/pnpm.d.ts.map +1 -0
- package/dist/lib/dev-workspace/pnpm.js +59 -0
- package/dist/lib/dev-workspace/pnpm.js.map +1 -0
- package/dist/lib/dev-workspace/status.d.ts +16 -0
- package/dist/lib/dev-workspace/status.d.ts.map +1 -0
- package/dist/lib/dev-workspace/status.js +185 -0
- package/dist/lib/dev-workspace/status.js.map +1 -0
- package/dist/lib/dev-workspace/types.d.ts +312 -0
- package/dist/lib/dev-workspace/types.d.ts.map +1 -0
- package/dist/lib/dev-workspace/types.js +14 -0
- package/dist/lib/dev-workspace/types.js.map +1 -0
- package/dist/lib/dev-workspace/usage.d.ts +30 -0
- package/dist/lib/dev-workspace/usage.d.ts.map +1 -0
- package/dist/lib/dev-workspace/usage.js +177 -0
- package/dist/lib/dev-workspace/usage.js.map +1 -0
- package/dist/lib/dev-workspace/write.d.ts +25 -0
- package/dist/lib/dev-workspace/write.d.ts.map +1 -0
- package/dist/lib/dev-workspace/write.js +75 -0
- package/dist/lib/dev-workspace/write.js.map +1 -0
- package/dist/light-commands.d.ts.map +1 -1
- package/dist/light-commands.js +7 -0
- package/dist/light-commands.js.map +1 -1
- package/oclif.manifest.json +1228 -969
- package/package.json +22 -22
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* CLI command: `mj dev workspace`.
|
|
4
|
+
*
|
|
5
|
+
* Generates the four ephemeral files that join sibling repo checkouts into a
|
|
6
|
+
* single pnpm workspace at their common parent directory (pnpm-workspace.yaml,
|
|
7
|
+
* .npmrc, package.json, turbo.json), then runs `pnpm install` there.
|
|
8
|
+
*
|
|
9
|
+
* Automates what was previously a hand-run setup: writing those files yourself and
|
|
10
|
+
* keeping them in step as repos come and go.
|
|
11
|
+
* Linking only — app REGISTRATION into a running host is deliberately phase 2.
|
|
12
|
+
*/
|
|
13
|
+
export default class DevWorkspace extends Command {
|
|
14
|
+
static description: string;
|
|
15
|
+
static examples: string[];
|
|
16
|
+
static flags: {
|
|
17
|
+
dir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
18
|
+
include: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[], import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
19
|
+
exclude: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[], import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
20
|
+
install: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
21
|
+
force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
22
|
+
'clean-members': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
23
|
+
verbose: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
24
|
+
};
|
|
25
|
+
run(): Promise<void>;
|
|
26
|
+
/** Resolves the member set: detected candidates plus --include, minus --exclude. */
|
|
27
|
+
private selectMembers;
|
|
28
|
+
/**
|
|
29
|
+
* Builds every file's content (pure builders) and logs each resolution decision.
|
|
30
|
+
* The sentinel comes last and records the names of the files alongside it, so its
|
|
31
|
+
* inventory is true by construction rather than by a constant kept in step.
|
|
32
|
+
*/
|
|
33
|
+
private buildFiles;
|
|
34
|
+
/**
|
|
35
|
+
* Logs where a member's workspace globs came from — and warns LOUDLY when the
|
|
36
|
+
* member has its own pnpm-workspace.yaml that yielded nothing usable, because a
|
|
37
|
+
* silent packages/* fallback is exactly the #3795 failure: an install that
|
|
38
|
+
* succeeds while the member's real packages resolve from the registry.
|
|
39
|
+
*/
|
|
40
|
+
private reportMemberGlobs;
|
|
41
|
+
/**
|
|
42
|
+
* Detects members carrying standalone installs (root or nested node_modules —
|
|
43
|
+
* depth-independent, per the field's 290-trees lesson on #3795) and removes
|
|
44
|
+
* them before the parent install, honoring --clean-members/--no-clean-members
|
|
45
|
+
* or asking interactively. A skip always states the exact consequence.
|
|
46
|
+
*/
|
|
47
|
+
private handleStandaloneInstalls;
|
|
48
|
+
/** Decides one member's cleanup: an explicit flag wins; otherwise ask when interactive, skip loudly when not. */
|
|
49
|
+
private shouldCleanMember;
|
|
50
|
+
/**
|
|
51
|
+
* Reports every absorption decision the manifest build made: pins, hoists,
|
|
52
|
+
* patches, family overrides, drops and skips — loud conflicts, dim counts,
|
|
53
|
+
* itemized detail behind --verbose. A derivation that dropped something and
|
|
54
|
+
* said nothing is the #3795 disease; this method is why it cannot recur.
|
|
55
|
+
*/
|
|
56
|
+
private reportManifestAssembly;
|
|
57
|
+
/** The dropped/skipped half of the assembly report: @types, workspace: drops, superseded pins, lockfile skips. */
|
|
58
|
+
private reportManifestDrops;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/dev/workspace/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAwB7C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;IAC/C,MAAM,CAAC,WAAW,SAKgF;IAElG,MAAM,CAAC,QAAQ,WAMb;IAEF,MAAM,CAAC,KAAK;;;;;;;;MA8BV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAuC1B,oFAAoF;IACpF,OAAO,CAAC,aAAa;IA4BrB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAuBlB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;;;;OAKG;YACW,wBAAwB;IA6BtC,iHAAiH;YACnG,iBAAiB;IAkB/B;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAyB9B,kHAAkH;IAClH,OAAO,CAAC,mBAAmB;CAmB5B"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import { Command, Flags } from '@oclif/core';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { BuildNpmrc, BuildRootPackageJson, BuildSentinel, BuildShellPeerGuidance, BuildWorkspaceYaml, PickTurboJson, } from '../../../lib/dev-workspace/build.js';
|
|
6
|
+
import { DetectCandidates, LoadRepo } from '../../../lib/dev-workspace/detect.js';
|
|
7
|
+
import { WORKSPACE_DIR_ENV_VAR } from '../../../lib/dev-workspace/dir-flag.js';
|
|
8
|
+
import { FindMemberInstallTrees, IsInsideDirectory, RemoveMemberInstallTrees } from '../../../lib/dev-workspace/member-installs.js';
|
|
9
|
+
import { RunPnpmInstall } from '../../../lib/dev-workspace/pnpm.js';
|
|
10
|
+
import { AssertParentDirSafe, SENTINEL_FILE_NAME, WriteWorkspaceFiles } from '../../../lib/dev-workspace/write.js';
|
|
11
|
+
/** Renders one conflict's decision for a warn line. */
|
|
12
|
+
function describeConflict(conflict) {
|
|
13
|
+
const losers = conflict.Losers.map((l) => `${l.Version} (${l.Repo})`).join(', ');
|
|
14
|
+
return `kept ${conflict.Winner.Version} (${conflict.Winner.Repo}), dropped ${losers}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* CLI command: `mj dev workspace`.
|
|
18
|
+
*
|
|
19
|
+
* Generates the four ephemeral files that join sibling repo checkouts into a
|
|
20
|
+
* single pnpm workspace at their common parent directory (pnpm-workspace.yaml,
|
|
21
|
+
* .npmrc, package.json, turbo.json), then runs `pnpm install` there.
|
|
22
|
+
*
|
|
23
|
+
* Automates what was previously a hand-run setup: writing those files yourself and
|
|
24
|
+
* keeping them in step as repos come and go.
|
|
25
|
+
* Linking only — app REGISTRATION into a running host is deliberately phase 2.
|
|
26
|
+
*/
|
|
27
|
+
export default class DevWorkspace extends Command {
|
|
28
|
+
static { this.description = 'Join sibling repo clones into one pnpm workspace: writes pnpm-workspace.yaml, .npmrc, package.json, ' +
|
|
29
|
+
'turbo.json and the .mj-dev-workspace.json sentinel at their common parent, then runs pnpm install there. ' +
|
|
30
|
+
'Members are the parent\'s immediate subdirectories carrying an mj-app.json, a @mj-biz-apps package, or the ' +
|
|
31
|
+
'MJ monorepo root name. Existing files are never overwritten without --force (which keeps a .bak of each). ' +
|
|
32
|
+
'Generated files are ephemeral — never commit them; tear them down with `dev workspace clean`.'; }
|
|
33
|
+
static { this.examples = [
|
|
34
|
+
'<%= config.bin %> dev workspace --dir ~/code/bluecypress',
|
|
35
|
+
'<%= config.bin %> dev workspace --dir ~/code/bluecypress --exclude bizapps-sonar',
|
|
36
|
+
'<%= config.bin %> dev workspace --dir ~/code/bluecypress --include bizapps-common --include bizapps-tasks',
|
|
37
|
+
'<%= config.bin %> dev workspace --dir ~/code/bluecypress --no-install --force',
|
|
38
|
+
'MJ_DEV_WORKSPACE_DIR=~/code/bluecypress <%= config.bin %> dev workspace',
|
|
39
|
+
]; }
|
|
40
|
+
static { this.flags = {
|
|
41
|
+
dir: Flags.string({
|
|
42
|
+
description: `Parent directory holding the sibling repo clones (must NOT itself be a git repo; env: ${WORKSPACE_DIR_ENV_VAR})`,
|
|
43
|
+
env: WORKSPACE_DIR_ENV_VAR,
|
|
44
|
+
default: '.',
|
|
45
|
+
}),
|
|
46
|
+
include: Flags.string({
|
|
47
|
+
multiple: true,
|
|
48
|
+
description: 'Repo directory name to include as a member (repeatable; adds to the detected set)',
|
|
49
|
+
}),
|
|
50
|
+
exclude: Flags.string({
|
|
51
|
+
multiple: true,
|
|
52
|
+
description: 'Repo directory name to exclude from the members (repeatable)',
|
|
53
|
+
}),
|
|
54
|
+
install: Flags.boolean({
|
|
55
|
+
description: 'Run `pnpm install` at the parent after generating (disable with --no-install)',
|
|
56
|
+
default: true,
|
|
57
|
+
allowNo: true,
|
|
58
|
+
}),
|
|
59
|
+
force: Flags.boolean({
|
|
60
|
+
description: 'Overwrite existing workspace files at the parent (a .bak copy of each is kept)',
|
|
61
|
+
default: false,
|
|
62
|
+
}),
|
|
63
|
+
'clean-members': Flags.boolean({
|
|
64
|
+
description: 'Remove members\' standalone node_modules trees (root AND nested, depth-independent) before the parent ' +
|
|
65
|
+
'install; --no-clean-members keeps them. Without either flag, an interactive run asks per member.',
|
|
66
|
+
allowNo: true,
|
|
67
|
+
}),
|
|
68
|
+
verbose: Flags.boolean({ char: 'v', description: 'Show detailed output' }),
|
|
69
|
+
}; }
|
|
70
|
+
async run() {
|
|
71
|
+
const { flags } = await this.parse(DevWorkspace);
|
|
72
|
+
const parentDir = path.resolve(flags.dir);
|
|
73
|
+
try {
|
|
74
|
+
AssertParentDirSafe(parentDir);
|
|
75
|
+
const members = this.selectMembers(parentDir, flags.include ?? [], flags.exclude ?? []);
|
|
76
|
+
await this.handleStandaloneInstalls(parentDir, members, flags['clean-members']);
|
|
77
|
+
const files = this.buildFiles(parentDir, members, flags.verbose);
|
|
78
|
+
const result = WriteWorkspaceFiles(parentDir, files, flags.force);
|
|
79
|
+
for (const backup of result.BackedUp) {
|
|
80
|
+
this.log(chalk.yellow(`Backed up existing file to ${backup}`));
|
|
81
|
+
}
|
|
82
|
+
this.log(chalk.green(`Wrote ${result.Written.join(', ')} at ${parentDir}`));
|
|
83
|
+
for (const line of BuildShellPeerGuidance()) {
|
|
84
|
+
this.log(chalk.dim(line));
|
|
85
|
+
}
|
|
86
|
+
if (flags.install) {
|
|
87
|
+
this.log(chalk.bold('\nRunning pnpm install...'));
|
|
88
|
+
await RunPnpmInstall(parentDir);
|
|
89
|
+
this.log(chalk.green('pnpm install completed'));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this.log(chalk.dim('Skipped pnpm install (--no-install); run `pnpm install` at the parent when ready.'));
|
|
93
|
+
}
|
|
94
|
+
this.log(chalk.dim(`\nBuild any package from the parent: pnpm --filter <package-name> run build`));
|
|
95
|
+
this.log(chalk.dim(`(members whose package.json pins npm refuse in-place pnpm runs — drive those from the parent with --filter until they migrate)`));
|
|
96
|
+
this.log(chalk.dim(`Check the result any time with: ${this.config.bin} dev workspace status --dir ${parentDir}`));
|
|
97
|
+
this.log(chalk.dim(`Tear it back down with: ${this.config.bin} dev workspace clean --dir ${parentDir}`));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
this.error(message);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Resolves the member set: detected candidates plus --include, minus --exclude. */
|
|
105
|
+
selectMembers(parentDir, include, exclude) {
|
|
106
|
+
const members = new Map();
|
|
107
|
+
for (const candidate of DetectCandidates(parentDir)) {
|
|
108
|
+
members.set(candidate.Name, candidate);
|
|
109
|
+
}
|
|
110
|
+
for (const name of include) {
|
|
111
|
+
if (members.has(name))
|
|
112
|
+
continue;
|
|
113
|
+
const repo = LoadRepo(parentDir, name);
|
|
114
|
+
if (repo === null) {
|
|
115
|
+
throw new Error(`--include ${name}: ${path.join(parentDir, name)} has no package.json — not a repo checkout`);
|
|
116
|
+
}
|
|
117
|
+
this.warn(`${name} was not detected as a candidate — including it anyway (--include)`);
|
|
118
|
+
members.set(name, repo);
|
|
119
|
+
}
|
|
120
|
+
for (const name of exclude) {
|
|
121
|
+
if (!members.delete(name))
|
|
122
|
+
this.warn(`--exclude ${name}: not in the member set, ignoring`);
|
|
123
|
+
}
|
|
124
|
+
if (members.size === 0) {
|
|
125
|
+
throw new Error(`No member repos at ${parentDir}. Candidates are sibling dirs with an mj-app.json, @mj-biz-apps packages, or the MJ monorepo; use --include to add others.`);
|
|
126
|
+
}
|
|
127
|
+
const selected = [...members.values()].sort((a, b) => a.Name.localeCompare(b.Name));
|
|
128
|
+
for (const member of selected) {
|
|
129
|
+
const why = member.Reasons.length > 0 ? member.Reasons.join(', ') : 'included via --include';
|
|
130
|
+
this.log(`${chalk.green('member')} ${member.Name} ${chalk.dim(`(${why})`)}`);
|
|
131
|
+
}
|
|
132
|
+
return selected;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Builds every file's content (pure builders) and logs each resolution decision.
|
|
136
|
+
* The sentinel comes last and records the names of the files alongside it, so its
|
|
137
|
+
* inventory is true by construction rather than by a constant kept in step.
|
|
138
|
+
*/
|
|
139
|
+
buildFiles(parentDir, members, verbose) {
|
|
140
|
+
const memberNames = members.map((m) => m.Name);
|
|
141
|
+
const rootPkg = BuildRootPackageJson(path.basename(parentDir), members);
|
|
142
|
+
for (const conflict of rootPkg.Conflicts) {
|
|
143
|
+
this.warn(`devDependency conflict on ${conflict.Package}: ${describeConflict(conflict)}`);
|
|
144
|
+
}
|
|
145
|
+
this.log(chalk.dim(`packageManager pin ${rootPkg.Pin} from ${rootPkg.PinSource}`));
|
|
146
|
+
const turbo = PickTurboJson(members);
|
|
147
|
+
this.log(chalk.dim(`turbo.json copied from ${turbo.Source}`));
|
|
148
|
+
for (const member of members) {
|
|
149
|
+
this.reportMemberGlobs(member);
|
|
150
|
+
}
|
|
151
|
+
this.reportManifestAssembly(rootPkg.Report, verbose);
|
|
152
|
+
const workspaceFiles = [
|
|
153
|
+
{ Name: 'pnpm-workspace.yaml', Content: BuildWorkspaceYaml(members) },
|
|
154
|
+
{ Name: '.npmrc', Content: BuildNpmrc() },
|
|
155
|
+
{ Name: 'package.json', Content: rootPkg.Content },
|
|
156
|
+
{ Name: 'turbo.json', Content: turbo.Content },
|
|
157
|
+
];
|
|
158
|
+
const written = [...workspaceFiles.map((f) => f.Name), SENTINEL_FILE_NAME];
|
|
159
|
+
return [...workspaceFiles, { Name: SENTINEL_FILE_NAME, Content: BuildSentinel(written, memberNames) }];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Logs where a member's workspace globs came from — and warns LOUDLY when the
|
|
163
|
+
* member has its own pnpm-workspace.yaml that yielded nothing usable, because a
|
|
164
|
+
* silent packages/* fallback is exactly the #3795 failure: an install that
|
|
165
|
+
* succeeds while the member's real packages resolve from the registry.
|
|
166
|
+
*/
|
|
167
|
+
reportMemberGlobs(member) {
|
|
168
|
+
for (const glob of member.UnsupportedGlobs) {
|
|
169
|
+
this.warn(`${member.Name} glob '${glob}' has an unsupported shape (only fixed paths, trailing /* and trailing /** ` +
|
|
170
|
+
`are expanded) — its packages are NOT enumerated for workspace:* overrides`);
|
|
171
|
+
}
|
|
172
|
+
if (member.WorkspaceGlobsSource === 'workspace-yaml-without-packages-globs') {
|
|
173
|
+
this.warn(`${member.Name} has its own pnpm-workspace.yaml but it yielded NO globs rooted under packages/ — ` +
|
|
174
|
+
`assuming ${member.Name}/packages/*. If that repo keeps packages anywhere else, they will NOT be ` +
|
|
175
|
+
`linked into this workspace (they would resolve from the registry instead). Check ${member.Name}/pnpm-workspace.yaml.`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (member.WorkspaceGlobsSource === 'member-workspace-yaml') {
|
|
179
|
+
this.log(chalk.dim(`${member.Name} contributes ${member.WorkspaceGlobs.length} workspace globs from its own pnpm-workspace.yaml`));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Detects members carrying standalone installs (root or nested node_modules —
|
|
184
|
+
* depth-independent, per the field's 290-trees lesson on #3795) and removes
|
|
185
|
+
* them before the parent install, honoring --clean-members/--no-clean-members
|
|
186
|
+
* or asking interactively. A skip always states the exact consequence.
|
|
187
|
+
*/
|
|
188
|
+
async handleStandaloneInstalls(parentDir, members, cleanFlag) {
|
|
189
|
+
for (const member of members) {
|
|
190
|
+
if (!IsInsideDirectory(parentDir, member.Path)) {
|
|
191
|
+
// possible via --include with a path-like name; never enumerate such a member for deletion
|
|
192
|
+
this.warn(`${member.Name} lives outside the parent directory (${member.Path}) — excluded from standalone-install cleanup entirely.`);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const scan = FindMemberInstallTrees(member.Path);
|
|
196
|
+
for (const dir of scan.UnreadableDirs) {
|
|
197
|
+
this.warn(`${member.Name}: could not read ${dir} while scanning for standalone installs — skipped, anything under it was not scanned`);
|
|
198
|
+
}
|
|
199
|
+
if (scan.Trees.length === 0)
|
|
200
|
+
continue;
|
|
201
|
+
const clean = await this.shouldCleanMember(member.Name, scan.Trees.length, cleanFlag);
|
|
202
|
+
if (!clean) {
|
|
203
|
+
this.warn(`${member.Name}: keeping its standalone install (${scan.Trees.length} node_modules tree(s)). After the parent ` +
|
|
204
|
+
`install those trees hold symlinks into a store the parent does not manage — expect dangling links and ` +
|
|
205
|
+
`TS2307 'Cannot find module' errors that look like missing dependencies. Re-run with --clean-members to remove them.`);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const result = RemoveMemberInstallTrees(scan.Trees);
|
|
209
|
+
for (const skip of result.Skipped) {
|
|
210
|
+
this.warn(`${member.Name}: NOT removed — ${skip.Path}: ${skip.Reason}`);
|
|
211
|
+
}
|
|
212
|
+
this.log(chalk.green(`${member.Name}: removed ${result.Removed.length} standalone node_modules tree(s)`));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** Decides one member's cleanup: an explicit flag wins; otherwise ask when interactive, skip loudly when not. */
|
|
216
|
+
async shouldCleanMember(name, treeCount, cleanFlag) {
|
|
217
|
+
if (cleanFlag !== undefined)
|
|
218
|
+
return cleanFlag;
|
|
219
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
220
|
+
this.warn(`${name} has a standalone install (${treeCount} node_modules tree(s)) and this run is non-interactive ` +
|
|
221
|
+
`with no --clean-members/--no-clean-members given — leaving it in place.`);
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
this.log(`${name} has a standalone install (${treeCount} node_modules tree${treeCount === 1 ? '' : 's'}).`);
|
|
225
|
+
this.log(` keep it → its old trees resolve against a store the parent install does not manage: two copies of shared packages, dangling symlinks, phantom 'Cannot find module' errors`);
|
|
226
|
+
this.log(` remove it → ${name} builds only through this workspace until you run a plain install inside it again (one command to switch back)`);
|
|
227
|
+
return confirm({
|
|
228
|
+
message: `Remove ${name}'s standalone install before the parent install?`,
|
|
229
|
+
default: true,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Reports every absorption decision the manifest build made: pins, hoists,
|
|
234
|
+
* patches, family overrides, drops and skips — loud conflicts, dim counts,
|
|
235
|
+
* itemized detail behind --verbose. A derivation that dropped something and
|
|
236
|
+
* said nothing is the #3795 disease; this method is why it cannot recur.
|
|
237
|
+
*/
|
|
238
|
+
reportManifestAssembly(report, verbose) {
|
|
239
|
+
this.log(chalk.dim(`pnpm.overrides: ${report.LockfilePinCount} lockfile-derived pin(s), ${report.HoistedOverrideCount} hoisted ` +
|
|
240
|
+
`member override(s), ${report.FamilyOverrideCount} workspace:* family override(s); ${report.Patches.length} patch(es) hoisted`));
|
|
241
|
+
for (const patch of report.Patches) {
|
|
242
|
+
this.log(chalk.dim(` patch ${patch.Package} -> ${patch.Path} (from ${patch.Repo})`));
|
|
243
|
+
}
|
|
244
|
+
for (const conflict of [...report.PinConflicts, ...report.BlockConflicts]) {
|
|
245
|
+
this.warn(`override conflict on ${conflict.Package}: ${describeConflict(conflict)}`);
|
|
246
|
+
}
|
|
247
|
+
for (const unsupported of report.UnsupportedLockfiles) {
|
|
248
|
+
this.warn(`${unsupported.Repo} commits ${unsupported.File} with lockfileVersion ${unsupported.Version} — a format this ` +
|
|
249
|
+
`derivation cannot read, so that member contributes NO pins; its ranges resolve freshly at the parent`);
|
|
250
|
+
}
|
|
251
|
+
for (const dup of report.DuplicateFamilyPackages) {
|
|
252
|
+
this.warn(`package ${dup.Package} is provided by ${dup.Repos.join(' AND ')} — the link target is decided by sort order; use --exclude to drop one`);
|
|
253
|
+
}
|
|
254
|
+
this.reportManifestDrops(report, verbose);
|
|
255
|
+
}
|
|
256
|
+
/** The dropped/skipped half of the assembly report: @types, workspace: drops, superseded pins, lockfile skips. */
|
|
257
|
+
reportManifestDrops(report, verbose) {
|
|
258
|
+
if (report.SkippedTypesDevDeps.length > 0) {
|
|
259
|
+
const detail = verbose ? `: ${report.SkippedTypesDevDeps.join(', ')}` : '';
|
|
260
|
+
this.log(chalk.dim(`devDependency union: skipped ${report.SkippedTypesDevDeps.length} @types/* package(s) — duplicate @types are a nominal-type break${detail}`));
|
|
261
|
+
}
|
|
262
|
+
for (const dropped of report.DroppedWorkspaceDevDeps) {
|
|
263
|
+
this.warn(`devDependency ${dropped.Package} (${dropped.Repo}) uses workspace: but NO member provides it — dropped from the parent manifest`);
|
|
264
|
+
}
|
|
265
|
+
if (report.SupersededPins.length > 0) {
|
|
266
|
+
const detail = verbose ? `: ${report.SupersededPins.join(', ')}` : '';
|
|
267
|
+
this.log(chalk.dim(`${report.SupersededPins.length} lockfile-derived pin(s) superseded by member overrides or workspace:* family overrides${detail}`));
|
|
268
|
+
}
|
|
269
|
+
if (report.LockfileSkips.length === 0)
|
|
270
|
+
return;
|
|
271
|
+
this.log(chalk.dim(`lockfile pin derivation skipped ${report.LockfileSkips.length} entr(y/ies) (workspace links / non-semver)${verbose ? ':' : ' — --verbose lists them'}`));
|
|
272
|
+
if (!verbose)
|
|
273
|
+
return;
|
|
274
|
+
for (const { Repo, Skip } of report.LockfileSkips) {
|
|
275
|
+
this.log(chalk.dim(` ${Repo}: ${Skip.Name}@${Skip.Version} — ${Skip.Reason}`));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/dev/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,aAAa,EACb,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,GACd,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AACpI,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAGnH,uDAAuD;AACvD,SAAS,gBAAgB,CAAC,QAAwB;IAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjF,OAAO,QAAQ,QAAQ,CAAC,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,MAAM,CAAC,IAAI,cAAc,MAAM,EAAE,CAAC;AACxF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;aACxC,gBAAW,GAChB,sGAAsG;QACtG,2GAA2G;QAC3G,6GAA6G;QAC7G,4GAA4G;QAC5G,+FAA+F,CAAC;aAE3F,aAAQ,GAAG;QAChB,0DAA0D;QAC1D,kFAAkF;QAClF,2GAA2G;QAC3G,+EAA+E;QAC/E,yEAAyE;KAC1E,CAAC;aAEK,UAAK,GAAG;QACb,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;YAChB,WAAW,EAAE,yFAAyF,qBAAqB,GAAG;YAC9H,GAAG,EAAE,qBAAqB;YAC1B,OAAO,EAAE,GAAG;SACb,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC;YACpB,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,mFAAmF;SACjG,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC;YACpB,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,8DAA8D;SAC5E,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;YACrB,WAAW,EAAE,+EAA+E;YAC5F,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd,CAAC;QACF,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC;YACnB,WAAW,EAAE,gFAAgF;YAC7F,OAAO,EAAE,KAAK;SACf,CAAC;QACF,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC;YAC7B,WAAW,EACT,wGAAwG;gBACxG,kGAAkG;YACpG,OAAO,EAAE,IAAI;SACd,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;KAC3E,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE1C,IAAI,CAAC;YACH,mBAAmB,CAAC,SAAS,CAAC,CAAC;YAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YACxF,MAAM,IAAI,CAAC,wBAAwB,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;YAChF,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAClE,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACrC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,8BAA8B,MAAM,EAAE,CAAC,CAAC,CAAC;YACjE,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,SAAS,EAAE,CAAC,CAAC,CAAC;YAC5E,KAAK,MAAM,IAAI,IAAI,sBAAsB,EAAE,EAAE,CAAC;gBAC5C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5B,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBAClD,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;gBAChC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;YAClD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mFAAmF,CAAC,CAAC,CAAC;YAC3G,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,6EAA6E,CAAC,CAAC,CAAC;YACnG,IAAI,CAAC,GAAG,CACN,KAAK,CAAC,GAAG,CACP,gIAAgI,CACjI,CACF,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,oCAAoC,IAAI,CAAC,MAAM,CAAC,GAAG,+BAA+B,SAAS,EAAE,CAAC,CAAC,CAAC;YACnH,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,oCAAoC,IAAI,CAAC,MAAM,CAAC,GAAG,8BAA8B,SAAS,EAAE,CAAC,CAAC,CAAC;QACpH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,oFAAoF;IAC5E,aAAa,CAAC,SAAiB,EAAE,OAAiB,EAAE,OAAiB;QAC3E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;QACjD,KAAK,MAAM,SAAS,IAAI,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC;YACpD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACzC,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YAChC,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACvC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,4CAA4C,CAAC,CAAC;YAChH,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,oEAAoE,CAAC,CAAC;YACvF,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,mCAAmC,CAAC,CAAC;QAC7F,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,sBAAsB,SAAS,4HAA4H,CAAC,CAAC;QAC/K,CAAC;QACD,MAAM,QAAQ,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpF,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAC;YAC7F,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACK,UAAU,CAAC,SAAiB,EAAE,OAAwB,EAAE,OAAgB;QAC9E,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,6BAA6B,QAAQ,CAAC,OAAO,KAAK,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sBAAsB,OAAO,CAAC,GAAG,SAAS,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,cAAc,GAAoB;YACtC,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE;YACrE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE;YACzC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE;YAClD,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;SAC/C,CAAC;QACF,MAAM,OAAO,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,cAAc,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACzG,CAAC;IAED;;;;;OAKG;IACK,iBAAiB,CAAC,MAAqB;QAC7C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC3C,IAAI,CAAC,IAAI,CACP,GAAG,MAAM,CAAC,IAAI,UAAU,IAAI,6EAA6E;gBACvG,2EAA2E,CAC9E,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,oBAAoB,KAAK,uCAAuC,EAAE,CAAC;YAC5E,IAAI,CAAC,IAAI,CACP,GAAG,MAAM,CAAC,IAAI,oFAAoF;gBAChG,YAAY,MAAM,CAAC,IAAI,2EAA2E;gBAClG,oFAAoF,MAAM,CAAC,IAAI,uBAAuB,CACzH,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC,oBAAoB,KAAK,uBAAuB,EAAE,CAAC;YAC5D,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,gBAAgB,MAAM,CAAC,cAAc,CAAC,MAAM,mDAAmD,CAAC,CAAC,CAAC;QACrI,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,wBAAwB,CAAC,SAAiB,EAAE,OAAwB,EAAE,SAA8B;QAChH,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/C,2FAA2F;gBAC3F,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,wCAAwC,MAAM,CAAC,IAAI,wDAAwD,CAAC,CAAC;gBACrI,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACjD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACtC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,oBAAoB,GAAG,sFAAsF,CAAC,CAAC;YACzI,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACtC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACtF,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,IAAI,CAAC,IAAI,CACP,GAAG,MAAM,CAAC,IAAI,qCAAqC,IAAI,CAAC,KAAK,CAAC,MAAM,2CAA2C;oBAC7G,wGAAwG;oBACxG,qHAAqH,CACxH,CAAC;gBACF,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,mBAAmB,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YAC1E,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,aAAa,MAAM,CAAC,OAAO,CAAC,MAAM,kCAAkC,CAAC,CAAC,CAAC;QAC5G,CAAC;IACH,CAAC;IAED,iHAAiH;IACzG,KAAK,CAAC,iBAAiB,CAAC,IAAY,EAAE,SAAiB,EAAE,SAA8B;QAC7F,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC9C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClD,IAAI,CAAC,IAAI,CACP,GAAG,IAAI,8BAA8B,SAAS,yDAAyD;gBACrG,yEAAyE,CAC5E,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,8BAA8B,SAAS,qBAAqB,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5G,IAAI,CAAC,GAAG,CAAC,+KAA+K,CAAC,CAAC;QAC1L,IAAI,CAAC,GAAG,CAAC,iBAAiB,IAAI,gHAAgH,CAAC,CAAC;QAChJ,OAAO,OAAO,CAAC;YACb,OAAO,EAAE,UAAU,IAAI,kDAAkD;YACzE,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,sBAAsB,CAAC,MAA4B,EAAE,OAAgB;QAC3E,IAAI,CAAC,GAAG,CACN,KAAK,CAAC,GAAG,CACP,mBAAmB,MAAM,CAAC,gBAAgB,6BAA6B,MAAM,CAAC,oBAAoB,WAAW;YAC3G,uBAAuB,MAAM,CAAC,mBAAmB,oCAAoC,MAAM,CAAC,OAAO,CAAC,MAAM,oBAAoB,CACjI,CACF,CAAC;QACF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,OAAO,OAAO,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACxF,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,MAAM,CAAC,YAAY,EAAE,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1E,IAAI,CAAC,IAAI,CAAC,wBAAwB,QAAQ,CAAC,OAAO,KAAK,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;QACD,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,oBAAoB,EAAE,CAAC;YACtD,IAAI,CAAC,IAAI,CACP,GAAG,WAAW,CAAC,IAAI,YAAY,WAAW,CAAC,IAAI,yBAAyB,WAAW,CAAC,OAAO,mBAAmB;gBAC5G,sGAAsG,CACzG,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,uBAAuB,EAAE,CAAC;YACjD,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,OAAO,mBAAmB,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,wEAAwE,CAAC,CAAC;QACtJ,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,kHAAkH;IAC1G,mBAAmB,CAAC,MAA4B,EAAE,OAAgB;QACxE,IAAI,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,MAAM,CAAC,mBAAmB,CAAC,MAAM,mEAAmE,MAAM,EAAE,CAAC,CAAC,CAAC;QACpK,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,uBAAuB,EAAE,CAAC;YACrD,IAAI,CAAC,IAAI,CAAC,iBAAiB,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,gFAAgF,CAAC,CAAC;QAC/I,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,0FAA0F,MAAM,EAAE,CAAC,CAAC,CAAC;QACzJ,CAAC;QACD,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC9C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mCAAmC,MAAM,CAAC,aAAa,CAAC,MAAM,8CAA8C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,yBAAyB,EAAE,CAAC,CAAC,CAAC;QAC7K,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YAClD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC;IACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* CLI command: `mj dev workspace status`.
|
|
4
|
+
*
|
|
5
|
+
* Reports the state of a generated cross-repo workspace at the parent
|
|
6
|
+
* directory: which of the four generated files exist, whether a lockfile and
|
|
7
|
+
* node_modules exist, whether the sentinel manifest is present, the member list
|
|
8
|
+
* versus the candidates detected on disk, how `--dir` was resolved, and whether
|
|
9
|
+
* the active pnpm version matches the generated pin. Read-only — writes nothing.
|
|
10
|
+
*/
|
|
11
|
+
export default class DevWorkspaceStatus extends Command {
|
|
12
|
+
static description: string;
|
|
13
|
+
static examples: string[];
|
|
14
|
+
static flags: {
|
|
15
|
+
dir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../../src/commands/dev/workspace/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAM7C;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,OAAO;IACrD,MAAM,CAAC,WAAW,SAE2F;IAE7G,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;MAMV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAe3B"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ResolveDirSource, WORKSPACE_DIR_ENV_VAR } from '../../../lib/dev-workspace/dir-flag.js';
|
|
4
|
+
import { GetPnpmVersion } from '../../../lib/dev-workspace/pnpm.js';
|
|
5
|
+
import { CollectWorkspaceStatus, RenderStatus } from '../../../lib/dev-workspace/status.js';
|
|
6
|
+
/**
|
|
7
|
+
* CLI command: `mj dev workspace status`.
|
|
8
|
+
*
|
|
9
|
+
* Reports the state of a generated cross-repo workspace at the parent
|
|
10
|
+
* directory: which of the four generated files exist, whether a lockfile and
|
|
11
|
+
* node_modules exist, whether the sentinel manifest is present, the member list
|
|
12
|
+
* versus the candidates detected on disk, how `--dir` was resolved, and whether
|
|
13
|
+
* the active pnpm version matches the generated pin. Read-only — writes nothing.
|
|
14
|
+
*/
|
|
15
|
+
export default class DevWorkspaceStatus extends Command {
|
|
16
|
+
static { this.description = 'Report the state of the generated cross-repo pnpm workspace at a parent directory: generated files, ' +
|
|
17
|
+
'lockfile, node_modules, sentinel presence, members vs. detected candidates, and the pnpm pin. Read-only.'; }
|
|
18
|
+
static { this.examples = [
|
|
19
|
+
'<%= config.bin %> dev workspace status',
|
|
20
|
+
'<%= config.bin %> dev workspace status --dir ~/code/bluecypress',
|
|
21
|
+
`MJ_DEV_WORKSPACE_DIR=~/code/bluecypress <%= config.bin %> dev workspace status`,
|
|
22
|
+
]; }
|
|
23
|
+
static { this.flags = {
|
|
24
|
+
dir: Flags.string({
|
|
25
|
+
description: `Parent directory holding the sibling repo clones (env: ${WORKSPACE_DIR_ENV_VAR})`,
|
|
26
|
+
env: WORKSPACE_DIR_ENV_VAR,
|
|
27
|
+
default: '.',
|
|
28
|
+
}),
|
|
29
|
+
}; }
|
|
30
|
+
async run() {
|
|
31
|
+
const { flags } = await this.parse(DevWorkspaceStatus);
|
|
32
|
+
const parentDir = path.resolve(flags.dir);
|
|
33
|
+
const dirSource = ResolveDirSource(this.argv, process.env[WORKSPACE_DIR_ENV_VAR]);
|
|
34
|
+
try {
|
|
35
|
+
const activePnpm = await GetPnpmVersion(parentDir);
|
|
36
|
+
const status = CollectWorkspaceStatus(parentDir, activePnpm, dirSource);
|
|
37
|
+
this.log('');
|
|
38
|
+
this.log(RenderStatus(status));
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42
|
+
this.error(message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/commands/dev/workspace/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAE5F;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,OAAO;aAC9C,gBAAW,GAChB,sGAAsG;QACtG,0GAA0G,CAAC;aAEtG,aAAQ,GAAG;QAChB,wCAAwC;QACxC,iEAAiE;QACjE,gFAAgF;KACjF,CAAC;aAEK,UAAK,GAAG;QACb,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;YAChB,WAAW,EAAE,0DAA0D,qBAAqB,GAAG;YAC/F,GAAG,EAAE,qBAAqB;YAC1B,OAAO,EAAE,GAAG;SACb,CAAC;KACH,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAElF,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;YACxE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACH,CAAC"}
|
|
@@ -44,6 +44,31 @@ export declare function decideConvertWrite(result: Pick<ConvertedShape, 'status'
|
|
|
44
44
|
* "preserved DDL below" banner over nothing.
|
|
45
45
|
*/
|
|
46
46
|
export declare function buildConversionFailureArtifact(sourceFile: string, message: string, preservedBody: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Decide whether a legacy (non---split) convert run fails, and with what message (issue #3857).
|
|
49
|
+
* Pure — no I/O — so the exit policy is unit-testable apart from the oclif command.
|
|
50
|
+
*
|
|
51
|
+
* A GAP is a marker comment the converter wrote into the output where it knowingly could not
|
|
52
|
+
* produce SQL (`-- Could not parse: @RoleID …`). Unlike an ERROR — a throw the converter caught —
|
|
53
|
+
* a gap leaves the batch counted as converted, so before this gate the legacy path reported
|
|
54
|
+
* `Files: 1 (1 OK, 0 errors)` and exited 0 over a file PostgreSQL rejects, and nothing short of
|
|
55
|
+
* applying it to a real PG database caught it.
|
|
56
|
+
*
|
|
57
|
+
* Errors always fail. Gaps fail too, unless the caller explicitly accepts them with --allow-gaps
|
|
58
|
+
* (which until now had effect only on the --split path).
|
|
59
|
+
*/
|
|
60
|
+
export declare function decideLegacyConvertExit(args: {
|
|
61
|
+
errorCount: number;
|
|
62
|
+
gapCount: number;
|
|
63
|
+
gapFileCount: number;
|
|
64
|
+
allowGaps: boolean;
|
|
65
|
+
}): {
|
|
66
|
+
fail: true;
|
|
67
|
+
message: string;
|
|
68
|
+
} | {
|
|
69
|
+
fail: false;
|
|
70
|
+
message: null;
|
|
71
|
+
};
|
|
47
72
|
/**
|
|
48
73
|
* CLI command to discover and convert T-SQL migrations to PostgreSQL.
|
|
49
74
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAa7C,OAAO,KAAK,EAIV,yBAAyB,EACzB,oBAAoB,EAErB,MAAM,+BAA+B,CAAC;AAMvC;;;;;GAKG;AACH,KAAK,cAAc,GAAG,IAAI,CAAC,yBAAyB,EAAE,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,gBAAgB,CAAC,GAAG;IAC3G,IAAI,CAAC,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACpC,cAAc,CAAC,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAIhC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAC,EAC7D,WAAW,EAAE,OAAO,GACnB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAKlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAUjH;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,OAAO;IACjD,MAAM,CAAC,WAAW,SAA4C;IAE9D,MAAM,CAAC,QAAQ,WAMb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAa7C,OAAO,KAAK,EAIV,yBAAyB,EACzB,oBAAoB,EAErB,MAAM,+BAA+B,CAAC;AAMvC;;;;;GAKG;AACH,KAAK,cAAc,GAAG,IAAI,CAAC,yBAAyB,EAAE,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,gBAAgB,CAAC,GAAG;IAC3G,IAAI,CAAC,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACpC,cAAc,CAAC,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAIhC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAC,EAC7D,WAAW,EAAE,OAAO,GACnB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAKlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAUjH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAcnE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,OAAO;IACjD,MAAM,CAAC,WAAW,SAA4C;IAE9D,MAAM,CAAC,QAAQ,WAMb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;MAoDV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAgJ1B;;;;;;;;;;;OAWG;YACW,QAAQ;IA6KtB;;;;;;OAMG;YACW,eAAe;IAoC7B;;;;;;;;;OASG;YACW,UAAU;IAsDxB,+EAA+E;IAC/E,OAAO,CAAC,iBAAiB;IAwCzB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,mBAAmB;IA6C3B,OAAO,CAAC,UAAU;CAqBnB"}
|
|
@@ -51,6 +51,36 @@ export function buildConversionFailureArtifact(sourceFile, message, preservedBod
|
|
|
51
51
|
`-- The transpiled DDL below is PRESERVED for hand-finishing; complete it, then rename to .pg.sql.\n\n` +
|
|
52
52
|
`${preserved}\n`);
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Decide whether a legacy (non---split) convert run fails, and with what message (issue #3857).
|
|
56
|
+
* Pure — no I/O — so the exit policy is unit-testable apart from the oclif command.
|
|
57
|
+
*
|
|
58
|
+
* A GAP is a marker comment the converter wrote into the output where it knowingly could not
|
|
59
|
+
* produce SQL (`-- Could not parse: @RoleID …`). Unlike an ERROR — a throw the converter caught —
|
|
60
|
+
* a gap leaves the batch counted as converted, so before this gate the legacy path reported
|
|
61
|
+
* `Files: 1 (1 OK, 0 errors)` and exited 0 over a file PostgreSQL rejects, and nothing short of
|
|
62
|
+
* applying it to a real PG database caught it.
|
|
63
|
+
*
|
|
64
|
+
* Errors always fail. Gaps fail too, unless the caller explicitly accepts them with --allow-gaps
|
|
65
|
+
* (which until now had effect only on the --split path).
|
|
66
|
+
*/
|
|
67
|
+
export function decideLegacyConvertExit(args) {
|
|
68
|
+
const reasons = [];
|
|
69
|
+
if (args.errorCount > 0)
|
|
70
|
+
reasons.push(`errors in ${args.errorCount} file(s)`);
|
|
71
|
+
const gapsFail = args.gapCount > 0 && !args.allowGaps;
|
|
72
|
+
if (gapsFail)
|
|
73
|
+
reasons.push(`${args.gapCount} conversion gap(s) in ${args.gapFileCount} file(s)`);
|
|
74
|
+
if (reasons.length === 0)
|
|
75
|
+
return { fail: false, message: null };
|
|
76
|
+
const advice = gapsFail
|
|
77
|
+
? ' A gap is a marker comment (e.g. "-- Could not parse: …") left where the converter could not ' +
|
|
78
|
+
'produce SQL — PostgreSQL will reject the output. Resolve the gaps (rewrite the source migration ' +
|
|
79
|
+
'declaratively, hand-author the PG form, or fix the converter rule), or pass --allow-gaps to ' +
|
|
80
|
+
'accept them for now.'
|
|
81
|
+
: '';
|
|
82
|
+
return { fail: true, message: `Conversion completed with ${reasons.join(' and ')}.${advice}` };
|
|
83
|
+
}
|
|
54
84
|
/**
|
|
55
85
|
* CLI command to discover and convert T-SQL migrations to PostgreSQL.
|
|
56
86
|
*
|
|
@@ -119,8 +149,9 @@ export default class MigrateConvert extends Command {
|
|
|
119
149
|
default: false,
|
|
120
150
|
}),
|
|
121
151
|
'allow-gaps': Flags.boolean({
|
|
122
|
-
description: '
|
|
123
|
-
'
|
|
152
|
+
description: 'Exit 0 even when migrations have conversion gaps — with --split: hand-authoring needed or ' +
|
|
153
|
+
'unhandled statements; on the legacy path: "-- Could not parse" / "-- ERROR converting batch" ' +
|
|
154
|
+
'markers in the output. Gaps are still embedded as comments and listed in the summary.',
|
|
124
155
|
default: false,
|
|
125
156
|
}),
|
|
126
157
|
'bake-codegen': Flags.boolean({
|
|
@@ -181,6 +212,7 @@ export default class MigrateConvert extends Command {
|
|
|
181
212
|
// Convert each file
|
|
182
213
|
let successCount = 0;
|
|
183
214
|
let errorCount = 0;
|
|
215
|
+
const gapFiles = [];
|
|
184
216
|
const totalStats = createConversionStats();
|
|
185
217
|
for (const migration of unconverted) {
|
|
186
218
|
this.log(`Converting: ${migration.SourceFile}`);
|
|
@@ -205,7 +237,16 @@ export default class MigrateConvert extends Command {
|
|
|
205
237
|
this.logToStderr(` ${err.substring(0, 200)}`);
|
|
206
238
|
}
|
|
207
239
|
}
|
|
208
|
-
|
|
240
|
+
// Gaps are listed the same way errors are — the whole point of issue #3857 is that a file
|
|
241
|
+
// the converter knowingly could not finish must not be reported as "OK".
|
|
242
|
+
if (result.Stats.Gaps > 0) {
|
|
243
|
+
gapFiles.push(migration.SourceFile);
|
|
244
|
+
this.logToStderr(` GAPS: ${result.Stats.Gaps} unconverted statement(s) in ${migration.SourceFile}`);
|
|
245
|
+
for (const gap of result.Stats.GapBatches) {
|
|
246
|
+
this.logToStderr(` ${gap.substring(0, 200)}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (result.Stats.Errors === 0 && result.Stats.Gaps === 0) {
|
|
209
250
|
successCount++;
|
|
210
251
|
this.log(` OK (${result.Stats.Converted} batches, ${result.Stats.TotalBatches - result.Stats.Converted} skipped)`);
|
|
211
252
|
}
|
|
@@ -213,8 +254,8 @@ export default class MigrateConvert extends Command {
|
|
|
213
254
|
// Print summary
|
|
214
255
|
this.log('');
|
|
215
256
|
this.log('=== Conversion Summary ===');
|
|
216
|
-
this.log(` Files: ${unconverted.length} (${successCount} OK, ${errorCount} errors)`);
|
|
217
|
-
this.log(` Batches: ${totalStats.TotalBatches} total, ${totalStats.Converted} converted, ${totalStats.Skipped} skipped, ${totalStats.Errors} errors`);
|
|
257
|
+
this.log(` Files: ${unconverted.length} (${successCount} OK, ${errorCount} errors, ${gapFiles.length} with gaps)`);
|
|
258
|
+
this.log(` Batches: ${totalStats.TotalBatches} total, ${totalStats.Converted} converted, ${totalStats.Skipped} skipped, ${totalStats.Errors} errors, ${totalStats.Gaps} gaps`);
|
|
218
259
|
this.log(` Tables: ${totalStats.TablesCreated}`);
|
|
219
260
|
this.log(` Views: ${totalStats.ViewsCreated}`);
|
|
220
261
|
this.log(` Procedures: ${totalStats.ProceduresConverted}`);
|
|
@@ -223,9 +264,6 @@ export default class MigrateConvert extends Command {
|
|
|
223
264
|
this.log(` Indexes: ${totalStats.IndexesCreated}`);
|
|
224
265
|
this.log(` Inserts: ${totalStats.InsertsConverted}`);
|
|
225
266
|
this.log(` Grants: ${totalStats.GrantsConverted}`);
|
|
226
|
-
if (errorCount > 0) {
|
|
227
|
-
this.error(`Conversion completed with errors in ${errorCount} file(s)`);
|
|
228
|
-
}
|
|
229
267
|
// Post-conversion: deduplicate EntityField Sequence values across files
|
|
230
268
|
// to prevent UQ_EntityField_EntityID_Sequence violations on PG
|
|
231
269
|
this.log('\nRunning EntityField Sequence deduplication...');
|
|
@@ -239,6 +277,19 @@ export default class MigrateConvert extends Command {
|
|
|
239
277
|
else {
|
|
240
278
|
this.log(` No collisions found (${dedupResult.totalInserts} EntityField INSERTs scanned).`);
|
|
241
279
|
}
|
|
280
|
+
// Gate LAST, after the sequence dedup fixup. `this.error` throws, and the files are already
|
|
281
|
+
// written: gating before the fixup would leave every output un-deduped, and a re-run skips
|
|
282
|
+
// them (they now have a .pg.sql counterpart), so the fixup would never get another chance.
|
|
283
|
+
// Errors gate here as they always did; gaps now gate too unless --allow-gaps (issue #3857).
|
|
284
|
+
const exit = decideLegacyConvertExit({
|
|
285
|
+
errorCount,
|
|
286
|
+
gapCount: totalStats.Gaps,
|
|
287
|
+
gapFileCount: gapFiles.length,
|
|
288
|
+
allowGaps: flags['allow-gaps'],
|
|
289
|
+
});
|
|
290
|
+
if (exit.fail) {
|
|
291
|
+
this.error(exit.message);
|
|
292
|
+
}
|
|
242
293
|
}
|
|
243
294
|
/**
|
|
244
295
|
* Split-and-regenerate conversion (the standard path). Classification routes
|
|
@@ -592,8 +643,10 @@ export default class MigrateConvert extends Command {
|
|
|
592
643
|
target.FKConstraints += source.FKConstraints;
|
|
593
644
|
target.CheckConstraints += source.CheckConstraints;
|
|
594
645
|
target.CommentsConverted += source.CommentsConverted;
|
|
646
|
+
target.Gaps += source.Gaps;
|
|
595
647
|
target.ErrorBatches.push(...source.ErrorBatches);
|
|
596
648
|
target.SkippedBatches.push(...source.SkippedBatches);
|
|
649
|
+
target.GapBatches.push(...source.GapBatches);
|
|
597
650
|
}
|
|
598
651
|
}
|
|
599
652
|
//# sourceMappingURL=convert.js.map
|