pgpm 5.19.1 → 5.21.0
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/commands/cache.js +1 -1
- package/commands/diff.d.ts +3 -0
- package/commands/diff.js +359 -0
- package/commands/dump.js +3 -3
- package/commands/export.js +24 -5
- package/commands/import.d.ts +3 -0
- package/commands/import.js +235 -0
- package/commands/init/boilerplate.js +1 -1
- package/commands/init/index.js +3 -3
- package/commands/package.js +0 -1
- package/commands/slice.js +1 -1
- package/commands/test-packages.js +1 -1
- package/commands/transform.d.ts +13 -0
- package/commands/transform.js +317 -0
- package/commands/update.js +1 -1
- package/commands.js +7 -0
- package/esm/commands/cache.js +1 -1
- package/esm/commands/diff.js +324 -0
- package/esm/commands/dump.js +3 -3
- package/esm/commands/export.js +22 -3
- package/esm/commands/import.js +200 -0
- package/esm/commands/init/boilerplate.js +1 -1
- package/esm/commands/init/index.js +3 -3
- package/esm/commands/package.js +0 -1
- package/esm/commands/slice.js +1 -1
- package/esm/commands/test-packages.js +1 -1
- package/esm/commands/transform.js +278 -0
- package/esm/commands/update.js +1 -1
- package/esm/commands.js +7 -0
- package/esm/utils/display.js +3 -0
- package/esm/utils/driver.js +1 -1
- package/esm/utils/emit-package.js +1 -0
- package/esm/utils/engine.js +1 -1
- package/esm/utils/module-projections.js +82 -0
- package/esm/utils/scratch-db.js +61 -0
- package/package.json +14 -12
- package/utils/display.d.ts +1 -1
- package/utils/display.js +3 -0
- package/utils/driver.js +1 -1
- package/utils/emit-package.d.ts +9 -0
- package/utils/emit-package.js +7 -0
- package/utils/engine.js +1 -1
- package/utils/module-projections.d.ts +36 -0
- package/utils/module-projections.js +123 -0
- package/utils/scratch-db.d.ts +16 -0
- package/utils/scratch-db.js +66 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const import_1 = require("@pgpmjs/import");
|
|
37
|
+
const logger_1 = require("@pgpmjs/logger");
|
|
38
|
+
const transform_1 = require("@pgpmjs/transform");
|
|
39
|
+
const inquirerer_1 = require("inquirerer");
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const emit_package_1 = require("../utils/emit-package");
|
|
42
|
+
const module_projections_1 = require("../utils/module-projections");
|
|
43
|
+
const log = new logger_1.Logger('import');
|
|
44
|
+
const importUsageText = `
|
|
45
|
+
Import Command:
|
|
46
|
+
|
|
47
|
+
pgpm import <dump.sql | dir-of-sql-files> --pkg <module-name> [OPTIONS]
|
|
48
|
+
|
|
49
|
+
pgpm-itize an arbitrary SQL dump (e.g. pg_dump --schema-only output): parse
|
|
50
|
+
it, classify every statement, and emit a complete deployable pgpm module —
|
|
51
|
+
module dir, pgpm.plan, and deploy/revert/verify trees with spec-derived
|
|
52
|
+
change paths, graph-derived requires, and generated revert/verify scripts.
|
|
53
|
+
|
|
54
|
+
Directory inputs concatenate their .sql files in sorted (lexicographic)
|
|
55
|
+
filename order.
|
|
56
|
+
|
|
57
|
+
Dump handling:
|
|
58
|
+
- pg_dump preamble noise (SET ..., SELECT set_config(...), psql backslash
|
|
59
|
+
commands) is skipped; COMMENT ON and GRANT statements are kept, attached
|
|
60
|
+
to their host object's change.
|
|
61
|
+
- CREATE EXTENSION statements become .control requires (pgpm owns
|
|
62
|
+
extension creation at deploy time).
|
|
63
|
+
- COPY ... FROM stdin blocks and INSERTs are skipped with a warning unless
|
|
64
|
+
--with-data is passed, which emits them as seed fixture changes (COPY
|
|
65
|
+
data is converted to INSERTs).
|
|
66
|
+
- Statements that classify to no object land in a misc/statements change
|
|
67
|
+
with a warning — never dropped silently.
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--help, -h Show this help message
|
|
71
|
+
--pkg <name> Module name for the generated package (required)
|
|
72
|
+
--granularity <level> Granularity dial: atomic | object | consolidated
|
|
73
|
+
(default: object)
|
|
74
|
+
--change-granularity <level>
|
|
75
|
+
Change-level distribution: alteration | object | single
|
|
76
|
+
(default: object)
|
|
77
|
+
--naming <style> Change path naming style: directory | flat (default: directory)
|
|
78
|
+
--out <dir> Output base directory (default: current directory);
|
|
79
|
+
the module is written to <out>/<pkg>
|
|
80
|
+
--with-data Import COPY/INSERT data as seed fixture changes
|
|
81
|
+
--partition <file> Partition config (JSON: rules/defaultPackage/splitRiders)
|
|
82
|
+
splitting the import into multiple pgpm packages with
|
|
83
|
+
derived cross-package requires
|
|
84
|
+
--write Allow overwriting an existing module directory
|
|
85
|
+
--emit-sql <file|-> Also project the imported module into a single linear
|
|
86
|
+
SQL script (- for stdout). Requires a single output
|
|
87
|
+
package (not supported with --partition).
|
|
88
|
+
--emit-bundle <file> Also project the imported module into a
|
|
89
|
+
content-addressed .bundle.tar.gz. Same single-package
|
|
90
|
+
requirement as --emit-sql.
|
|
91
|
+
--cwd <directory> Working directory (default: current directory)
|
|
92
|
+
--dry-run Print the resulting plan/paths without writing
|
|
93
|
+
|
|
94
|
+
The imported module is the canonical artifact; --emit-sql and --emit-bundle are
|
|
95
|
+
pure projections of it (the same machinery pgpm package and pgpm diff use).
|
|
96
|
+
|
|
97
|
+
Examples:
|
|
98
|
+
pgpm import dump.sql --pkg my-app
|
|
99
|
+
pgpm import dump.sql --pkg my-app --granularity consolidated --naming flat
|
|
100
|
+
pgpm import ./sql-files --pkg my-app --out ./packages --with-data
|
|
101
|
+
pgpm import dump.sql --pkg my-app --partition partition.json
|
|
102
|
+
pgpm import dump.sql --pkg my-app --emit-sql my-app.sql
|
|
103
|
+
`;
|
|
104
|
+
const NAMING_STYLES = ['directory', 'flat'];
|
|
105
|
+
exports.default = async (argv, prompter, _options) => {
|
|
106
|
+
if (argv.help || argv.h) {
|
|
107
|
+
console.log(importUsageText);
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
const cwd = argv.cwd || process.cwd();
|
|
111
|
+
const input = argv._?.filter(Boolean)[0];
|
|
112
|
+
if (!input) {
|
|
113
|
+
await (0, inquirerer_1.cliExitWithError)('Missing input: pgpm import <dump.sql | dir-of-sql-files> --pkg <name>');
|
|
114
|
+
}
|
|
115
|
+
const pkgName = argv.pkg;
|
|
116
|
+
if (!pkgName || typeof pkgName !== 'string') {
|
|
117
|
+
await (0, inquirerer_1.cliExitWithError)('--pkg <module-name> is required.');
|
|
118
|
+
}
|
|
119
|
+
const granularityRaw = argv.granularity ?? 'object';
|
|
120
|
+
if (!(0, transform_1.isExportGranularity)(granularityRaw)) {
|
|
121
|
+
await (0, inquirerer_1.cliExitWithError)(`Invalid --granularity "${granularityRaw}". Expected one of: ${transform_1.EXPORT_GRANULARITIES.join(', ')}.`);
|
|
122
|
+
}
|
|
123
|
+
const granularity = granularityRaw;
|
|
124
|
+
const changeGranularityRaw = argv['change-granularity'] ?? argv.changeGranularity ?? 'object';
|
|
125
|
+
if (!(0, transform_1.isChangeGranularity)(changeGranularityRaw)) {
|
|
126
|
+
await (0, inquirerer_1.cliExitWithError)(`Invalid --change-granularity "${changeGranularityRaw}". Expected one of: ${transform_1.CHANGE_GRANULARITIES.join(', ')}.`);
|
|
127
|
+
}
|
|
128
|
+
const changeGranularity = changeGranularityRaw;
|
|
129
|
+
const namingRaw = argv.naming ?? 'directory';
|
|
130
|
+
if (!NAMING_STYLES.includes(namingRaw)) {
|
|
131
|
+
await (0, inquirerer_1.cliExitWithError)(`Invalid --naming "${namingRaw}". Expected one of: ${NAMING_STYLES.join(', ')}.`);
|
|
132
|
+
}
|
|
133
|
+
const naming = namingRaw;
|
|
134
|
+
let partition;
|
|
135
|
+
if (typeof argv.partition === 'string' && argv.partition) {
|
|
136
|
+
try {
|
|
137
|
+
partition = (0, transform_1.parsePartitionConfig)(path.resolve(cwd, argv.partition));
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
await (0, inquirerer_1.cliExitWithError)(err instanceof Error ? err.message : String(err));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const outBase = typeof argv.out === 'string' && argv.out ? path.resolve(cwd, argv.out) : path.resolve(cwd);
|
|
144
|
+
const withData = Boolean(argv['with-data'] ?? argv.withData);
|
|
145
|
+
const write = Boolean(argv.write);
|
|
146
|
+
const dryRun = Boolean(argv['dry-run'] ?? argv.dryRun);
|
|
147
|
+
const emit = (0, module_projections_1.parseEmitProjectionTargets)(argv, cwd);
|
|
148
|
+
const emitRequested = (0, module_projections_1.hasEmitProjection)(emit);
|
|
149
|
+
const sqlToStdout = emit.emitSql === module_projections_1.STDOUT_TARGET;
|
|
150
|
+
if (emitRequested && dryRun) {
|
|
151
|
+
await (0, inquirerer_1.cliExitWithError)('--emit-sql/--emit-bundle cannot be combined with --dry-run.');
|
|
152
|
+
}
|
|
153
|
+
let source;
|
|
154
|
+
try {
|
|
155
|
+
source = (0, import_1.loadDumpSource)(path.resolve(cwd, input));
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
await (0, inquirerer_1.cliExitWithError)(err instanceof Error ? err.message : String(err));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (source.files.length > 1 && !sqlToStdout) {
|
|
162
|
+
log.info(`concatenated ${source.files.length} .sql files in sorted order`);
|
|
163
|
+
}
|
|
164
|
+
for (const warning of (0, import_1.dumpCompatibilityWarnings)(source)) {
|
|
165
|
+
console.warn(`\nWARNING: ${warning}\n`);
|
|
166
|
+
}
|
|
167
|
+
const result = await (0, import_1.importDumpRows)(source, { granularity, changeGranularity, naming, withData });
|
|
168
|
+
let packages;
|
|
169
|
+
if (partition) {
|
|
170
|
+
try {
|
|
171
|
+
const partitioned = await (0, transform_1.partitionExportRows)(result.rows, partition);
|
|
172
|
+
result.warnings.push(...partitioned.warnings.map(w => `partition: ${w}`));
|
|
173
|
+
packages = partitioned.packages.map(pkg => ({
|
|
174
|
+
name: pkg.name,
|
|
175
|
+
requires: [...result.controlRequires, ...pkg.requires],
|
|
176
|
+
rows: (0, import_1.linkTextualDeps)(pkg.rows)
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
if (err instanceof transform_1.PartitionCycleError) {
|
|
181
|
+
await (0, inquirerer_1.cliExitWithError)(`Partition failed: ${err.message}`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
packages = [{ name: pkgName, requires: result.controlRequires, rows: result.rows }];
|
|
189
|
+
}
|
|
190
|
+
for (const warning of result.warnings) {
|
|
191
|
+
console.warn(`import: ${warning}`);
|
|
192
|
+
}
|
|
193
|
+
if (dryRun) {
|
|
194
|
+
for (const pkg of packages) {
|
|
195
|
+
console.log(`package ${pkg.name} -> ${path.join(outBase, pkg.name)}`);
|
|
196
|
+
if (pkg.requires.length) {
|
|
197
|
+
console.log(` requires: ${pkg.requires.join(', ')}`);
|
|
198
|
+
}
|
|
199
|
+
for (const row of pkg.rows) {
|
|
200
|
+
const deps = row.deps?.length ? ` [${row.deps.join(' ')}]` : '';
|
|
201
|
+
console.log(` ${row.deploy}${deps}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
if (emitRequested && packages.length !== 1) {
|
|
207
|
+
await (0, inquirerer_1.cliExitWithError)('--emit-sql/--emit-bundle require a single output package; a --partition import emits multiple packages.');
|
|
208
|
+
}
|
|
209
|
+
for (const pkg of packages) {
|
|
210
|
+
const guard = (0, emit_package_1.checkOverwrite)(path.join(outBase, pkg.name), source.files[0], write);
|
|
211
|
+
if (guard) {
|
|
212
|
+
await (0, inquirerer_1.cliExitWithError)(guard);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
let firstDir;
|
|
216
|
+
for (const pkg of packages) {
|
|
217
|
+
const dir = (0, emit_package_1.writePackage)(outBase, pkg);
|
|
218
|
+
firstDir ??= dir;
|
|
219
|
+
if (!sqlToStdout) {
|
|
220
|
+
log.success(`wrote ${pkg.rows.length} changes to ${dir}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (emitRequested && firstDir) {
|
|
224
|
+
await (0, module_projections_1.projectModule)(firstDir, emit, sqlToStdout ? undefined : msg => log.success(msg));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const { summary } = result;
|
|
228
|
+
if (!sqlToStdout) {
|
|
229
|
+
log.success(`import: ${summary.statements} statement(s) -> ${summary.changes} change(s), ` +
|
|
230
|
+
`${summary.skippedPreamble} preamble skipped, ${summary.skippedData} data skipped, ` +
|
|
231
|
+
`${summary.misc} in misc, ${result.warnings.length} warning(s)`);
|
|
232
|
+
}
|
|
233
|
+
prompter.close();
|
|
234
|
+
return argv;
|
|
235
|
+
};
|
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.resolveInitTemplateRepo = resolveInitTemplateRepo;
|
|
7
7
|
exports.persistBoilerplateSource = persistBoilerplateSource;
|
|
8
8
|
exports.readBoilerplateSource = readBoilerplateSource;
|
|
9
|
+
const core_1 = require("@pgpmjs/core");
|
|
9
10
|
const fs_1 = __importDefault(require("fs"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const core_1 = require("@pgpmjs/core");
|
|
12
12
|
/**
|
|
13
13
|
* Resolve the template repo for an `init` invocation from its flags.
|
|
14
14
|
*
|
package/commands/init/index.js
CHANGED
|
@@ -4,13 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createInitUsageText = void 0;
|
|
7
|
-
const child_process_1 = require("child_process");
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
7
|
const core_1 = require("@pgpmjs/core");
|
|
11
8
|
const env_1 = require("@pgpmjs/env");
|
|
12
9
|
const types_1 = require("@pgpmjs/types");
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
12
|
const inquirerer_1 = require("inquirerer");
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
14
|
const utils_1 = require("../../utils");
|
|
15
15
|
const boilerplate_1 = require("./boilerplate");
|
|
16
16
|
const DEFAULT_MOTD = `
|
package/commands/package.js
CHANGED
|
@@ -118,7 +118,6 @@ exports.default = async (argv, prompter, _options) => {
|
|
|
118
118
|
const project = new core_1.PgpmPackage(cwd);
|
|
119
119
|
project.ensureModule();
|
|
120
120
|
const info = project.getModuleInfo();
|
|
121
|
-
info.version;
|
|
122
121
|
await (0, core_1.writePackage)({
|
|
123
122
|
version: info.version,
|
|
124
123
|
extension: true,
|
package/commands/slice.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const core_1 = require("@pgpmjs/core");
|
|
4
4
|
const slice_1 = require("@pgpmjs/slice");
|
|
5
5
|
const types_1 = require("@pgpmjs/types");
|
|
6
|
-
const path_1 = require("path");
|
|
7
6
|
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
8
|
const sliceUsageText = `
|
|
9
9
|
Slice Command:
|
|
10
10
|
|
|
@@ -7,8 +7,8 @@ const core_1 = require("@pgpmjs/core");
|
|
|
7
7
|
const env_1 = require("@pgpmjs/env");
|
|
8
8
|
const logger_1 = require("@pgpmjs/logger");
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const pg_env_1 = require("pg-env");
|
|
11
10
|
const pg_cache_1 = require("pg-cache");
|
|
11
|
+
const pg_env_1 = require("pg-env");
|
|
12
12
|
const log = new logger_1.Logger('test-packages');
|
|
13
13
|
// ANSI color codes
|
|
14
14
|
const RED = '\x1b[0;31m';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PartitionedPackageRows } from '@pgpmjs/transform';
|
|
2
|
+
import { CLIOptions, Inquirerer, ParsedArgs } from 'inquirerer';
|
|
3
|
+
export { checkOverwrite } from '../utils/emit-package';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the base directory package dir(s) are created in. Without --out,
|
|
6
|
+
* packages are written as siblings of the source module — a plain transform
|
|
7
|
+
* of `my-mod` at granularity `object` lands in `../my-mod-object`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const resolveOutBase: (modulePath: string, out?: string) => string;
|
|
10
|
+
/** Order partition packages so prerequisites come before dependents. */
|
|
11
|
+
export declare const orderPackages: (packages: PartitionedPackageRows[]) => PartitionedPackageRows[];
|
|
12
|
+
declare const _default: (argv: Partial<ParsedArgs>, prompter: Inquirerer, _options: CLIOptions) => Promise<Partial<ParsedArgs>>;
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.orderPackages = exports.resolveOutBase = exports.checkOverwrite = void 0;
|
|
37
|
+
const core_1 = require("@pgpmjs/core");
|
|
38
|
+
const logger_1 = require("@pgpmjs/logger");
|
|
39
|
+
const transform_1 = require("@pgpmjs/transform");
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const inquirerer_1 = require("inquirerer");
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const pg_env_1 = require("pg-env");
|
|
44
|
+
const emit_package_1 = require("../utils/emit-package");
|
|
45
|
+
const module_projections_1 = require("../utils/module-projections");
|
|
46
|
+
const scratch_db_1 = require("../utils/scratch-db");
|
|
47
|
+
var emit_package_2 = require("../utils/emit-package");
|
|
48
|
+
Object.defineProperty(exports, "checkOverwrite", { enumerable: true, get: function () { return emit_package_2.checkOverwrite; } });
|
|
49
|
+
const log = new logger_1.Logger('transform');
|
|
50
|
+
const transformUsageText = `
|
|
51
|
+
Transform Command:
|
|
52
|
+
|
|
53
|
+
pgpm transform --granularity <atomic|object|consolidated> [OPTIONS]
|
|
54
|
+
|
|
55
|
+
Re-dial an existing pgpm module (or every module in a workspace) through the
|
|
56
|
+
dials pipeline: flatten the deploy scripts in plan order, re-project them at
|
|
57
|
+
the requested granularity with spec-derived change paths, graph-derived
|
|
58
|
+
requires, and generated revert/verify scripts.
|
|
59
|
+
|
|
60
|
+
Options:
|
|
61
|
+
--help, -h Show this help message
|
|
62
|
+
--granularity <level> Target granularity: atomic | object | consolidated (required)
|
|
63
|
+
--change-granularity <level>
|
|
64
|
+
Change-level distribution: alteration | object | single
|
|
65
|
+
(default: object). With alteration, every ADD COLUMN /
|
|
66
|
+
ADD CONSTRAINT becomes its own change with its own
|
|
67
|
+
deploy/revert/verify and requires. With single, the
|
|
68
|
+
whole module becomes one change.
|
|
69
|
+
--partition <file> Partition config (JSON: rules/defaultPackage/splitRiders)
|
|
70
|
+
splitting the module into multiple pgpm packages with
|
|
71
|
+
derived cross-package requires.
|
|
72
|
+
--naming <style> Change path naming style: directory | flat (default: directory)
|
|
73
|
+
--cwd <directory> Module or workspace directory (default: current directory)
|
|
74
|
+
--out <dir> Output directory (default: sibling <module>-<granularity>)
|
|
75
|
+
--write Allow writing over an existing/module directory
|
|
76
|
+
--emit-sql <file|-> Also project the transformed module into a single
|
|
77
|
+
linear SQL script (- for stdout). Requires a single
|
|
78
|
+
output package (no workspace/partition fan-out).
|
|
79
|
+
--emit-bundle <file> Also project the transformed module into a
|
|
80
|
+
content-addressed .bundle.tar.gz. Same single-package
|
|
81
|
+
requirement as --emit-sql.
|
|
82
|
+
--check Deploy original and transformed output into scratch
|
|
83
|
+
databases and assert the catalogs are equivalent
|
|
84
|
+
--dry-run Print the resulting plan/paths without writing
|
|
85
|
+
|
|
86
|
+
The transformed module is the canonical artifact; --emit-sql and --emit-bundle
|
|
87
|
+
are pure projections of it (the same machinery pgpm package and pgpm diff use).
|
|
88
|
+
|
|
89
|
+
Examples:
|
|
90
|
+
pgpm transform --granularity object
|
|
91
|
+
pgpm transform --granularity atomic --naming flat --out ./out
|
|
92
|
+
pgpm transform --granularity object --partition partition.json --check
|
|
93
|
+
pgpm transform --granularity consolidated --emit-sql migration.sql
|
|
94
|
+
pgpm transform --granularity atomic --change-granularity alteration
|
|
95
|
+
`;
|
|
96
|
+
const NAMING_STYLES = ['directory', 'flat'];
|
|
97
|
+
/**
|
|
98
|
+
* Resolve the base directory package dir(s) are created in. Without --out,
|
|
99
|
+
* packages are written as siblings of the source module — a plain transform
|
|
100
|
+
* of `my-mod` at granularity `object` lands in `../my-mod-object`.
|
|
101
|
+
*/
|
|
102
|
+
const resolveOutBase = (modulePath, out) => {
|
|
103
|
+
if (out)
|
|
104
|
+
return path.resolve(out);
|
|
105
|
+
return path.dirname(modulePath);
|
|
106
|
+
};
|
|
107
|
+
exports.resolveOutBase = resolveOutBase;
|
|
108
|
+
/** Order partition packages so prerequisites come before dependents. */
|
|
109
|
+
const orderPackages = (packages) => {
|
|
110
|
+
const byName = new Map(packages.map(pkg => [pkg.name, pkg]));
|
|
111
|
+
const ordered = [];
|
|
112
|
+
const seen = new Set();
|
|
113
|
+
const visit = (pkg) => {
|
|
114
|
+
if (seen.has(pkg.name))
|
|
115
|
+
return;
|
|
116
|
+
seen.add(pkg.name);
|
|
117
|
+
for (const req of pkg.requires) {
|
|
118
|
+
const dep = byName.get(req);
|
|
119
|
+
if (dep)
|
|
120
|
+
visit(dep);
|
|
121
|
+
}
|
|
122
|
+
ordered.push(pkg);
|
|
123
|
+
};
|
|
124
|
+
for (const pkg of packages)
|
|
125
|
+
visit(pkg);
|
|
126
|
+
return ordered;
|
|
127
|
+
};
|
|
128
|
+
exports.orderPackages = orderPackages;
|
|
129
|
+
const readControlRequires = (modulePath, name) => {
|
|
130
|
+
const controlPath = path.join(modulePath, `${name}.control`);
|
|
131
|
+
if (!fs.existsSync(controlPath))
|
|
132
|
+
return [];
|
|
133
|
+
const match = fs.readFileSync(controlPath, 'utf-8').match(/^requires\s*=\s*'([^']*)'\s*$/m);
|
|
134
|
+
if (!match)
|
|
135
|
+
return [];
|
|
136
|
+
return match[1].split(',').map(s => s.trim()).filter(Boolean);
|
|
137
|
+
};
|
|
138
|
+
const deployModules = async (config, modulePaths) => {
|
|
139
|
+
const client = new core_1.PgpmMigrate(config);
|
|
140
|
+
for (const modulePath of modulePaths) {
|
|
141
|
+
const result = await client.deploy({ modulePath });
|
|
142
|
+
if (result.failed) {
|
|
143
|
+
throw new Error(`deploy failed at change ${result.failed} (module ${modulePath})`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Prove the transform is structurally lossless: deploy the original module and
|
|
149
|
+
* the transformed package(s) into two scratch databases and compare catalogs.
|
|
150
|
+
*/
|
|
151
|
+
const runCheck = async (transformed) => {
|
|
152
|
+
const config = (0, pg_env_1.getPgEnvOptions)();
|
|
153
|
+
const stamp = Date.now();
|
|
154
|
+
const names = [`pgpm_transform_check_a_${stamp}`, `pgpm_transform_check_b_${stamp}`];
|
|
155
|
+
return (0, scratch_db_1.withScratchDatabases)(config, names, async ([cfgOriginal, cfgTransformed]) => {
|
|
156
|
+
await deployModules(cfgOriginal, [transformed.modulePath]);
|
|
157
|
+
const orderedDirs = (0, exports.orderPackages)(transformed.packages).map(pkg => path.join(transformed.outBase, pkg.name));
|
|
158
|
+
await deployModules(cfgTransformed, orderedDirs);
|
|
159
|
+
return (0, scratch_db_1.catalogDifferences)(cfgOriginal, cfgTransformed);
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
const transformModule = async (modulePath, granularity, changeGranularity, naming, partition, out) => {
|
|
163
|
+
const source = (0, transform_1.loadModuleSource)(modulePath);
|
|
164
|
+
const warnings = [...source.warnings];
|
|
165
|
+
const rows = source.changes.map(change => ({
|
|
166
|
+
name: change.name,
|
|
167
|
+
deploy: change.name,
|
|
168
|
+
deps: change.dependencies,
|
|
169
|
+
content: change.deploy
|
|
170
|
+
}));
|
|
171
|
+
const restructured = await (0, transform_1.restructureExportRows)(rows, granularity, { naming, changeGranularity });
|
|
172
|
+
warnings.push(...restructured.warnings.map(w => `restructure (${granularity}): ${w}`));
|
|
173
|
+
const outBase = (0, exports.resolveOutBase)(modulePath, out);
|
|
174
|
+
let packages;
|
|
175
|
+
if (partition) {
|
|
176
|
+
const result = await (0, transform_1.partitionExportRows)(restructured.rows, partition);
|
|
177
|
+
warnings.push(...result.warnings.map(w => `partition: ${w}`));
|
|
178
|
+
packages = result.packages;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Distinct package name so the output can live beside the source module
|
|
182
|
+
// in the same workspace without colliding.
|
|
183
|
+
packages = [{ name: `${source.name}-${granularity}`, requires: [], rows: restructured.rows }];
|
|
184
|
+
}
|
|
185
|
+
return { modulePath, name: source.name, outBase, packages, warnings };
|
|
186
|
+
};
|
|
187
|
+
const printDryRun = (transformed) => {
|
|
188
|
+
console.log(`module ${transformed.name} (${transformed.modulePath})`);
|
|
189
|
+
for (const pkg of transformed.packages) {
|
|
190
|
+
console.log(` package ${pkg.name} -> ${path.join(transformed.outBase, pkg.name)}`);
|
|
191
|
+
for (const row of pkg.rows) {
|
|
192
|
+
const deps = row.deps?.length ? ` [${row.deps.join(' ')}]` : '';
|
|
193
|
+
console.log(` ${row.deploy}${deps}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
exports.default = async (argv, prompter, _options) => {
|
|
198
|
+
if (argv.help || argv.h) {
|
|
199
|
+
console.log(transformUsageText);
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
const granularityRaw = argv.granularity;
|
|
203
|
+
if (granularityRaw === undefined) {
|
|
204
|
+
await (0, inquirerer_1.cliExitWithError)(`--granularity is required. Expected one of: ${transform_1.EXPORT_GRANULARITIES.join(', ')}.`);
|
|
205
|
+
}
|
|
206
|
+
if (!(0, transform_1.isExportGranularity)(granularityRaw)) {
|
|
207
|
+
await (0, inquirerer_1.cliExitWithError)(`Invalid --granularity "${granularityRaw}". Expected one of: ${transform_1.EXPORT_GRANULARITIES.join(', ')}.`);
|
|
208
|
+
}
|
|
209
|
+
const granularity = granularityRaw;
|
|
210
|
+
const changeGranularityRaw = argv['change-granularity'] ?? argv.changeGranularity ?? 'object';
|
|
211
|
+
if (!(0, transform_1.isChangeGranularity)(changeGranularityRaw)) {
|
|
212
|
+
await (0, inquirerer_1.cliExitWithError)(`Invalid --change-granularity "${changeGranularityRaw}". Expected one of: ${transform_1.CHANGE_GRANULARITIES.join(', ')}.`);
|
|
213
|
+
}
|
|
214
|
+
const changeGranularity = changeGranularityRaw;
|
|
215
|
+
const namingRaw = argv.naming ?? 'directory';
|
|
216
|
+
if (!NAMING_STYLES.includes(namingRaw)) {
|
|
217
|
+
await (0, inquirerer_1.cliExitWithError)(`Invalid --naming "${namingRaw}". Expected one of: ${NAMING_STYLES.join(', ')}.`);
|
|
218
|
+
}
|
|
219
|
+
const naming = namingRaw;
|
|
220
|
+
const cwd = argv.cwd || process.cwd();
|
|
221
|
+
let partition;
|
|
222
|
+
if (typeof argv.partition === 'string' && argv.partition) {
|
|
223
|
+
try {
|
|
224
|
+
partition = (0, transform_1.parsePartitionConfig)(path.resolve(cwd, argv.partition));
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
await (0, inquirerer_1.cliExitWithError)(err instanceof Error ? err.message : String(err));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const out = typeof argv.out === 'string' && argv.out ? path.resolve(cwd, argv.out) : undefined;
|
|
231
|
+
const write = Boolean(argv.write);
|
|
232
|
+
const check = Boolean(argv.check);
|
|
233
|
+
const dryRun = Boolean(argv['dry-run'] ?? argv.dryRun);
|
|
234
|
+
const emit = (0, module_projections_1.parseEmitProjectionTargets)(argv, cwd);
|
|
235
|
+
const emitRequested = (0, module_projections_1.hasEmitProjection)(emit);
|
|
236
|
+
const sqlToStdout = emit.emitSql === module_projections_1.STDOUT_TARGET;
|
|
237
|
+
const pkg = new core_1.PgpmPackage(path.resolve(cwd));
|
|
238
|
+
let modulePaths;
|
|
239
|
+
if (pkg.isInModule()) {
|
|
240
|
+
modulePaths = [pkg.modulePath];
|
|
241
|
+
}
|
|
242
|
+
else if (pkg.workspacePath) {
|
|
243
|
+
const moduleMap = pkg.getModuleMap();
|
|
244
|
+
modulePaths = Object.values(moduleMap).map(mod => path.resolve(pkg.workspacePath, mod.path));
|
|
245
|
+
if (modulePaths.length === 0) {
|
|
246
|
+
await (0, inquirerer_1.cliExitWithError)('No modules found in workspace.');
|
|
247
|
+
}
|
|
248
|
+
if (out && modulePaths.length > 1) {
|
|
249
|
+
await (0, inquirerer_1.cliExitWithError)('--out is not supported when transforming a multi-module workspace.');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
await (0, inquirerer_1.cliExitWithError)('Not inside a pgpm module or workspace (pass --cwd <dir>).');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (emitRequested && modulePaths.length > 1) {
|
|
257
|
+
await (0, inquirerer_1.cliExitWithError)('--emit-sql/--emit-bundle target a single module; they are not supported when transforming a multi-module workspace.');
|
|
258
|
+
}
|
|
259
|
+
if (emitRequested && dryRun) {
|
|
260
|
+
await (0, inquirerer_1.cliExitWithError)('--emit-sql/--emit-bundle cannot be combined with --dry-run.');
|
|
261
|
+
}
|
|
262
|
+
for (const modulePath of modulePaths) {
|
|
263
|
+
let transformed;
|
|
264
|
+
try {
|
|
265
|
+
transformed = await transformModule(modulePath, granularity, changeGranularity, naming, partition, out);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
if (err instanceof transform_1.PartitionCycleError) {
|
|
269
|
+
await (0, inquirerer_1.cliExitWithError)(`Partition failed: ${err.message}`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
throw err;
|
|
273
|
+
}
|
|
274
|
+
for (const warning of transformed.warnings) {
|
|
275
|
+
console.warn(`transform: ${warning}`);
|
|
276
|
+
}
|
|
277
|
+
if (dryRun) {
|
|
278
|
+
printDryRun(transformed);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
for (const pkgRows of transformed.packages) {
|
|
282
|
+
const targetDir = path.join(transformed.outBase, pkgRows.name);
|
|
283
|
+
const guard = (0, emit_package_1.checkOverwrite)(targetDir, modulePath, write);
|
|
284
|
+
if (guard) {
|
|
285
|
+
await (0, inquirerer_1.cliExitWithError)(guard);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (emitRequested && transformed.packages.length !== 1) {
|
|
289
|
+
await (0, inquirerer_1.cliExitWithError)('--emit-sql/--emit-bundle require a single output package; a --partition transform emits multiple packages.');
|
|
290
|
+
}
|
|
291
|
+
const sourceRequires = readControlRequires(modulePath, transformed.name);
|
|
292
|
+
let firstDir;
|
|
293
|
+
for (const pkgRows of transformed.packages) {
|
|
294
|
+
const dir = (0, emit_package_1.writePackage)(transformed.outBase, pkgRows, sourceRequires);
|
|
295
|
+
firstDir ??= dir;
|
|
296
|
+
if (!sqlToStdout) {
|
|
297
|
+
log.success(`wrote ${pkgRows.rows.length} changes to ${dir}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (emitRequested && firstDir) {
|
|
301
|
+
await (0, module_projections_1.projectModule)(firstDir, emit, sqlToStdout ? undefined : msg => log.success(msg));
|
|
302
|
+
}
|
|
303
|
+
if (check) {
|
|
304
|
+
log.info('running --check: deploying original and transformed output to scratch databases...');
|
|
305
|
+
const diffs = await runCheck(transformed);
|
|
306
|
+
if (diffs.length) {
|
|
307
|
+
console.error(`--check failed: catalogs differ (${diffs.length} differences):`);
|
|
308
|
+
for (const diff of diffs)
|
|
309
|
+
console.error(` ${diff}`);
|
|
310
|
+
await (0, inquirerer_1.cliExitWithError)('Transform is not structurally lossless.');
|
|
311
|
+
}
|
|
312
|
+
log.success('--check passed: catalogs are structurally equivalent.');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
prompter.close();
|
|
316
|
+
return argv;
|
|
317
|
+
};
|
package/commands/update.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const utils_1 = require("@inquirerer/utils");
|
|
4
4
|
const logger_1 = require("@pgpmjs/logger");
|
|
5
|
-
const inquirerer_1 = require("inquirerer");
|
|
6
5
|
const child_process_1 = require("child_process");
|
|
6
|
+
const inquirerer_1 = require("inquirerer");
|
|
7
7
|
const npm_version_1 = require("../utils/npm-version");
|
|
8
8
|
const log = new logger_1.Logger('update');
|
|
9
9
|
const updateUsageText = `
|