@pgpmjs/core 7.1.0 → 7.2.1
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/bundle/apply.d.ts +93 -0
- package/bundle/apply.js +137 -0
- package/bundle/index.d.ts +12 -0
- package/bundle/index.js +31 -0
- package/core/class/pgpm.d.ts +2 -2
- package/core/class/pgpm.js +6 -6
- package/esm/bundle/apply.js +131 -0
- package/esm/bundle/index.js +15 -0
- package/esm/core/class/pgpm.js +6 -6
- package/esm/index.js +5 -3
- package/esm/migrate/client.js +1 -1
- package/esm/migrate/utils/hash.js +3 -6
- package/esm/modules/modules.js +1 -1
- package/esm/packaging/package.js +1 -1
- package/esm/rebundle/module.js +1 -1
- package/esm/rebundle/rebundle.js +2 -2
- package/esm/rebundle/workspace.js +1 -1
- package/esm/refactor/rename.js +5 -27
- package/esm/resolution/deps.js +2 -2
- package/esm/resolution/resolve.js +2 -2
- package/esm/slice/slice.js +7 -32
- package/index.d.ts +4 -2
- package/index.js +6 -4
- package/migrate/client.js +1 -1
- package/migrate/utils/hash.d.ts +2 -4
- package/migrate/utils/hash.js +4 -8
- package/modules/modules.d.ts +1 -1
- package/modules/modules.js +1 -1
- package/package.json +4 -2
- package/packaging/package.js +1 -1
- package/rebundle/module.js +1 -1
- package/rebundle/rebundle.js +2 -2
- package/rebundle/types.d.ts +1 -1
- package/rebundle/workspace.js +1 -1
- package/refactor/rename.d.ts +2 -8
- package/refactor/rename.js +6 -29
- package/resolution/deps.d.ts +1 -1
- package/resolution/deps.js +2 -2
- package/resolution/resolve.js +2 -2
- package/slice/slice.d.ts +1 -1
- package/slice/slice.js +7 -32
- package/slice/types.d.ts +1 -1
- package/workspace/minimal.d.ts +1 -1
- package/esm/files/extension/index.js +0 -3
- package/esm/files/extension/reader.js +0 -79
- package/esm/files/extension/writer.js +0 -99
- package/esm/files/index.js +0 -6
- package/esm/files/plan/generator.js +0 -82
- package/esm/files/plan/index.js +0 -5
- package/esm/files/plan/parser.js +0 -297
- package/esm/files/plan/validators.js +0 -181
- package/esm/files/plan/writer.js +0 -142
- package/esm/files/sql/header.js +0 -259
- package/esm/files/sql/index.js +0 -2
- package/esm/files/sql/writer.js +0 -107
- package/esm/files/sql-scripts/index.js +0 -2
- package/esm/files/sql-scripts/reader.js +0 -19
- package/esm/files/types/index.js +0 -1
- package/esm/files/types/package.js +0 -1
- package/esm/utils/author.js +0 -11
- package/files/extension/index.d.ts +0 -2
- package/files/extension/index.js +0 -19
- package/files/extension/reader.d.ts +0 -24
- package/files/extension/reader.js +0 -86
- package/files/extension/writer.d.ts +0 -48
- package/files/extension/writer.js +0 -107
- package/files/index.d.ts +0 -5
- package/files/index.js +0 -22
- package/files/plan/generator.d.ts +0 -23
- package/files/plan/generator.js +0 -90
- package/files/plan/index.d.ts +0 -4
- package/files/plan/index.js +0 -21
- package/files/plan/parser.d.ts +0 -28
- package/files/plan/parser.js +0 -304
- package/files/plan/validators.d.ts +0 -52
- package/files/plan/validators.js +0 -187
- package/files/plan/writer.d.ts +0 -27
- package/files/plan/writer.js +0 -152
- package/files/sql/header.d.ts +0 -114
- package/files/sql/header.js +0 -266
- package/files/sql/index.d.ts +0 -2
- package/files/sql/index.js +0 -18
- package/files/sql/writer.d.ts +0 -12
- package/files/sql/writer.js +0 -114
- package/files/sql-scripts/index.d.ts +0 -1
- package/files/sql-scripts/index.js +0 -18
- package/files/sql-scripts/reader.d.ts +0 -8
- package/files/sql-scripts/reader.js +0 -23
- package/files/types/index.d.ts +0 -46
- package/files/types/index.js +0 -17
- package/files/types/package.d.ts +0 -20
- package/files/types/package.js +0 -2
- package/utils/author.d.ts +0 -5
- package/utils/author.js +0 -14
package/esm/files/plan/writer.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { parseAuthor } from '../../utils/author';
|
|
4
|
-
/**
|
|
5
|
-
* Sort dependencies by their order in the supplied plan, so prerequisites
|
|
6
|
-
* (parent migrations) appear before their dependents in the bracket.
|
|
7
|
-
* Internal dependencies are sorted by their plan index, and external
|
|
8
|
-
* dependencies (not in the plan) preserve their original order.
|
|
9
|
-
*/
|
|
10
|
-
const sortDependencies = (deps, order) => {
|
|
11
|
-
const originalOrder = new Map(deps.map((dep, index) => [dep, index]));
|
|
12
|
-
return [...deps].sort((a, b) => {
|
|
13
|
-
const aIndex = order.get(a);
|
|
14
|
-
const bIndex = order.get(b);
|
|
15
|
-
const aOriginal = originalOrder.get(a);
|
|
16
|
-
const bOriginal = originalOrder.get(b);
|
|
17
|
-
if (aIndex !== undefined && bIndex !== undefined)
|
|
18
|
-
return aIndex - bIndex || aOriginal - bOriginal;
|
|
19
|
-
if (aIndex !== undefined)
|
|
20
|
-
return -1;
|
|
21
|
-
if (bIndex !== undefined)
|
|
22
|
-
return 1;
|
|
23
|
-
return aOriginal - bOriginal;
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Write a PGPM plan file based on the provided rows
|
|
28
|
-
*/
|
|
29
|
-
export function writePgpmPlan(rows, opts) {
|
|
30
|
-
const dir = path.resolve(path.join(opts.outdir, opts.name));
|
|
31
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
32
|
-
const date = () => '2017-08-11T08:11:51Z'; // stubbed timestamp
|
|
33
|
-
const { fullName, email } = parseAuthor(opts.author || 'constructive');
|
|
34
|
-
const authorName = fullName;
|
|
35
|
-
const authorEmail = email || `${fullName}@5b0c196eeb62`;
|
|
36
|
-
const duplicates = {};
|
|
37
|
-
// Build an order map from the row list so brackets can be sorted by plan order
|
|
38
|
-
const rowOrder = new Map(rows.map((row, index) => [row.deploy, index]));
|
|
39
|
-
const plan = opts.replacer(`%syntax-version=1.0.0
|
|
40
|
-
%project=constructive-extension-name
|
|
41
|
-
%uri=constructive-extension-name
|
|
42
|
-
|
|
43
|
-
${rows
|
|
44
|
-
.map((row) => {
|
|
45
|
-
if (duplicates[row.deploy]) {
|
|
46
|
-
console.log('DUPLICATE ' + row.deploy);
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
duplicates[row.deploy] = true;
|
|
50
|
-
if (row.deps?.length) {
|
|
51
|
-
return `${row.deploy} [${sortDependencies(row.deps, rowOrder).join(' ')}] ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
52
|
-
}
|
|
53
|
-
return `${row.deploy} ${date()} ${authorName} <${authorEmail}> # add ${row.name}`;
|
|
54
|
-
})
|
|
55
|
-
.join('\n')}
|
|
56
|
-
`);
|
|
57
|
-
fs.writeFileSync(path.join(dir, 'pgpm.plan'), plan);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Write a plan file with the provided content
|
|
61
|
-
*/
|
|
62
|
-
export function writePlanFile(planPath, plan) {
|
|
63
|
-
const content = generatePlanFileContent(plan);
|
|
64
|
-
fs.writeFileSync(planPath, content);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Generate content for a plan file
|
|
68
|
-
*/
|
|
69
|
-
export function generatePlanFileContent(plan) {
|
|
70
|
-
const { package: packageName, uri, changes, tags } = plan;
|
|
71
|
-
let content = `%syntax-version=1.0.0\n`;
|
|
72
|
-
content += `%project=${packageName}\n`;
|
|
73
|
-
if (uri) {
|
|
74
|
-
content += `%uri=${uri}\n`;
|
|
75
|
-
}
|
|
76
|
-
content += `\n`;
|
|
77
|
-
// Build an order map from the change list so brackets can be sorted by plan order
|
|
78
|
-
const changeOrder = new Map(changes.map((change, index) => [change.name, index]));
|
|
79
|
-
// Add changes and their associated tags
|
|
80
|
-
for (const change of changes) {
|
|
81
|
-
content += generateChangeLineContent(change, changeOrder);
|
|
82
|
-
content += `\n`;
|
|
83
|
-
const associatedTags = tags.filter(tag => tag.change === change.name);
|
|
84
|
-
for (const tag of associatedTags) {
|
|
85
|
-
content += generateTagLineContent(tag);
|
|
86
|
-
content += `\n`;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return content;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Generate a line for a change in a plan file
|
|
93
|
-
*/
|
|
94
|
-
export function generateChangeLineContent(change, changeOrder) {
|
|
95
|
-
const { name, dependencies, timestamp, planner, email, comment } = change;
|
|
96
|
-
let line = name;
|
|
97
|
-
// Add dependencies if present, sorted by plan order
|
|
98
|
-
if (dependencies && dependencies.length > 0) {
|
|
99
|
-
line += ` [${sortDependencies(dependencies, changeOrder || new Map()).join(' ')}]`;
|
|
100
|
-
}
|
|
101
|
-
// Add timestamp if present
|
|
102
|
-
if (timestamp) {
|
|
103
|
-
line += ` ${timestamp}`;
|
|
104
|
-
// Add planner if present
|
|
105
|
-
if (planner) {
|
|
106
|
-
line += ` ${planner}`;
|
|
107
|
-
// Add email if present
|
|
108
|
-
if (email) {
|
|
109
|
-
line += ` <${email}>`;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Add comment if present
|
|
114
|
-
if (comment) {
|
|
115
|
-
line += ` # ${comment}`;
|
|
116
|
-
}
|
|
117
|
-
return line;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Generate a line for a tag in a plan file
|
|
121
|
-
*/
|
|
122
|
-
export function generateTagLineContent(tag) {
|
|
123
|
-
const { name, timestamp, planner, email, comment } = tag;
|
|
124
|
-
let line = `@${name}`;
|
|
125
|
-
// Add timestamp if present
|
|
126
|
-
if (timestamp) {
|
|
127
|
-
line += ` ${timestamp}`;
|
|
128
|
-
// Add planner if present
|
|
129
|
-
if (planner) {
|
|
130
|
-
line += ` ${planner}`;
|
|
131
|
-
// Add email if present
|
|
132
|
-
if (email) {
|
|
133
|
-
line += ` <${email}>`;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// Add comment if present
|
|
138
|
-
if (comment) {
|
|
139
|
-
line += ` # ${comment}`;
|
|
140
|
-
}
|
|
141
|
-
return line;
|
|
142
|
-
}
|
package/esm/files/sql/header.js
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { parsePlanFile } from '../plan/parser';
|
|
4
|
-
import { parseReference } from '../plan/validators';
|
|
5
|
-
const HEADER_LINE = /^--\s*(Deploy|Revert|Verify)\b(:)?\s*(.*?)\s*$/;
|
|
6
|
-
const REQUIRES_LINE = /^--\s*requires:\s*(.*?)\s*$/;
|
|
7
|
-
const COMMENT_LINE = /^\s*--/;
|
|
8
|
-
const TO_PG_SUFFIX = /\s+to\s+pg\s*$/;
|
|
9
|
-
/**
|
|
10
|
-
* Parse the header block of a pgpm SQL script into a structured model.
|
|
11
|
-
*
|
|
12
|
-
* Recognizes both header formats:
|
|
13
|
-
* `-- Deploy <project>:<change> to pg` (legacy sqitch-compatible)
|
|
14
|
-
* `-- Deploy <change>` / `-- Deploy: <change>` (new)
|
|
15
|
-
* along with `-- requires: <ref>` lines where `<ref>` may be a plain change
|
|
16
|
-
* name, `{package}:{change}`, `@tag`, or `{package}:@tag`.
|
|
17
|
-
*
|
|
18
|
-
* The parse is lossless: `header.lines` plus `body` reassemble the original
|
|
19
|
-
* content byte-exactly (see {@link writePgpmScript}).
|
|
20
|
-
*/
|
|
21
|
-
export function parsePgpmHeader(content) {
|
|
22
|
-
const header = {
|
|
23
|
-
verb: null,
|
|
24
|
-
project: null,
|
|
25
|
-
change: null,
|
|
26
|
-
toPg: false,
|
|
27
|
-
requires: [],
|
|
28
|
-
lines: []
|
|
29
|
-
};
|
|
30
|
-
const lines = content.split('\n');
|
|
31
|
-
let lastHeaderIdx = -1;
|
|
32
|
-
for (let i = 0; i < lines.length; i++) {
|
|
33
|
-
const line = lines[i];
|
|
34
|
-
if (line.trim() === '') {
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (!COMMENT_LINE.test(line)) {
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
const headerMatch = line.match(HEADER_LINE);
|
|
41
|
-
if (headerMatch && header.verb === null) {
|
|
42
|
-
header.verb = headerMatch[1];
|
|
43
|
-
let target = headerMatch[3];
|
|
44
|
-
if (TO_PG_SUFFIX.test(target)) {
|
|
45
|
-
header.toPg = true;
|
|
46
|
-
target = target.replace(TO_PG_SUFFIX, '');
|
|
47
|
-
}
|
|
48
|
-
const colonIndex = target.indexOf(':');
|
|
49
|
-
if (colonIndex > 0 && /^[a-zA-Z0-9_-]+$/.test(target.substring(0, colonIndex))) {
|
|
50
|
-
header.project = target.substring(0, colonIndex);
|
|
51
|
-
header.change = target.substring(colonIndex + 1).trim();
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
header.change = target.trim() || null;
|
|
55
|
-
}
|
|
56
|
-
lastHeaderIdx = i;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const requiresMatch = line.match(REQUIRES_LINE);
|
|
60
|
-
if (requiresMatch) {
|
|
61
|
-
const raw = requiresMatch[1];
|
|
62
|
-
header.requires.push({ raw, ref: parseReference(raw) });
|
|
63
|
-
lastHeaderIdx = i;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
// Other comment lines (e.g. attribution) before/between header lines are
|
|
67
|
-
// tolerated but only absorbed into the header when a later header line
|
|
68
|
-
// follows; trailing comments belong to the body.
|
|
69
|
-
}
|
|
70
|
-
header.lines = lines.slice(0, lastHeaderIdx + 1);
|
|
71
|
-
return {
|
|
72
|
-
header,
|
|
73
|
-
body: lines.slice(lastHeaderIdx + 1).join('\n')
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Serialize a header back to its raw lines. When the structured fields have
|
|
78
|
-
* been modified via {@link renameInHeader}, the raw lines already reflect the
|
|
79
|
-
* change; this simply joins header and body losslessly.
|
|
80
|
-
*/
|
|
81
|
-
export function writePgpmScript(script) {
|
|
82
|
-
if (script.header.lines.length === 0)
|
|
83
|
-
return script.body;
|
|
84
|
-
return script.header.lines.join('\n') + '\n' + script.body;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Rewrite change names in a script header according to a rename map
|
|
88
|
-
* (old change name -> new change name). Rewrites both the `-- Deploy` line's
|
|
89
|
-
* change identity and any `-- requires:` lines referencing renamed changes,
|
|
90
|
-
* preserving format (project qualifier, ` to pg` suffix, tag refs untouched).
|
|
91
|
-
*
|
|
92
|
-
* Returns the number of substitutions applied; mutates `script` in place.
|
|
93
|
-
*/
|
|
94
|
-
export function renameInHeader(script, renames) {
|
|
95
|
-
const { header } = script;
|
|
96
|
-
let count = 0;
|
|
97
|
-
const renameRef = (raw) => {
|
|
98
|
-
const ref = parseReference(raw);
|
|
99
|
-
if (!ref) {
|
|
100
|
-
const to = renames.get(raw);
|
|
101
|
-
return to ?? null;
|
|
102
|
-
}
|
|
103
|
-
if (ref.tag || ref.symbolic || ref.relative)
|
|
104
|
-
return null;
|
|
105
|
-
const change = ref.change;
|
|
106
|
-
if (!change)
|
|
107
|
-
return null;
|
|
108
|
-
const to = renames.get(change);
|
|
109
|
-
if (!to)
|
|
110
|
-
return null;
|
|
111
|
-
return ref.package ? `${ref.package}:${to}` : to;
|
|
112
|
-
};
|
|
113
|
-
header.lines = header.lines.map(line => {
|
|
114
|
-
const headerMatch = line.match(HEADER_LINE);
|
|
115
|
-
if (headerMatch && header.change && renames.has(header.change)) {
|
|
116
|
-
const to = renames.get(header.change);
|
|
117
|
-
const oldTarget = header.project ? `${header.project}:${header.change}` : header.change;
|
|
118
|
-
const newTarget = header.project ? `${header.project}:${to}` : to;
|
|
119
|
-
const rewritten = line.replace(oldTarget, newTarget);
|
|
120
|
-
if (rewritten !== line) {
|
|
121
|
-
header.change = to;
|
|
122
|
-
count++;
|
|
123
|
-
return rewritten;
|
|
124
|
-
}
|
|
125
|
-
return line;
|
|
126
|
-
}
|
|
127
|
-
const requiresMatch = line.match(REQUIRES_LINE);
|
|
128
|
-
if (requiresMatch) {
|
|
129
|
-
const raw = requiresMatch[1];
|
|
130
|
-
const renamed = renameRef(raw);
|
|
131
|
-
if (renamed !== null) {
|
|
132
|
-
count++;
|
|
133
|
-
return line.replace(raw, renamed);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return line;
|
|
137
|
-
});
|
|
138
|
-
header.requires = header.requires.map(req => {
|
|
139
|
-
const renamed = renameRef(req.raw);
|
|
140
|
-
if (renamed === null)
|
|
141
|
-
return req;
|
|
142
|
-
return { raw: renamed, ref: parseReference(renamed) };
|
|
143
|
-
});
|
|
144
|
-
return count;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Scan a deploy script the way dependency resolution historically has:
|
|
148
|
-
* line-by-line over the whole file, collecting `-- requires: <ref>` lines
|
|
149
|
-
* verbatim and validating any `-- Deploy <project>:<change>[ to pg]` line
|
|
150
|
-
* against the script's path identity and project name.
|
|
151
|
-
*
|
|
152
|
-
* Kept strictly line-regex-compatible with the original resolver behavior
|
|
153
|
-
* (requires lines are honored anywhere in the file, and the legacy
|
|
154
|
-
* `-- Deploy: <change>` colon form is not identity-validated).
|
|
155
|
-
*
|
|
156
|
-
* @throws when the Deploy line's project or change identity mismatches.
|
|
157
|
-
*/
|
|
158
|
-
export function scanDeployScript(content, options) {
|
|
159
|
-
const { key, extname, makeKey } = options;
|
|
160
|
-
const requires = [];
|
|
161
|
-
for (const line of content.split('\n')) {
|
|
162
|
-
const requiresMatch = line.match(/^-- requires: (.*)/);
|
|
163
|
-
if (requiresMatch) {
|
|
164
|
-
requires.push(requiresMatch[1].trim());
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (/:/.test(line)) {
|
|
168
|
-
const m2 = line.match(/^-- Deploy ([^:]*):([\w\/]+)(?:\s+to\s+pg)?/);
|
|
169
|
-
if (m2) {
|
|
170
|
-
const actualProject = m2[1];
|
|
171
|
-
const keyToTest = m2[2];
|
|
172
|
-
if (extname !== actualProject) {
|
|
173
|
-
throw new Error(`Mismatched project name in deploy file:
|
|
174
|
-
Expected project: ${extname}
|
|
175
|
-
Found in line : ${actualProject}
|
|
176
|
-
Line : ${line}`);
|
|
177
|
-
}
|
|
178
|
-
const expectedKey = makeKey(keyToTest);
|
|
179
|
-
if (key !== expectedKey) {
|
|
180
|
-
throw new Error(`Deployment script path or internal name mismatch:
|
|
181
|
-
Expected key : ${key}
|
|
182
|
-
Found in line : ${expectedKey}
|
|
183
|
-
Line : ${line}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
const m2 = line.match(/^-- Deploy (.*?)(?:\s+to\s+pg)?\s*$/);
|
|
189
|
-
if (m2) {
|
|
190
|
-
const keyToTest = m2[1].trim();
|
|
191
|
-
if (key !== makeKey(keyToTest)) {
|
|
192
|
-
throw new Error('deployment script in wrong place or is named wrong internally\n' + line);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return { requires };
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Verify that every change in a module's pgpm.plan has a deploy script whose
|
|
201
|
-
* header identity matches its path, and report dependency drift between the
|
|
202
|
-
* plan's dep lists and the scripts' `-- requires:` lines.
|
|
203
|
-
*
|
|
204
|
-
* Read-only diagnostic; returns issues rather than throwing.
|
|
205
|
-
*/
|
|
206
|
-
export function verifyPlanMatchesHeaders(moduleDir) {
|
|
207
|
-
const issues = [];
|
|
208
|
-
const planPath = join(moduleDir, 'pgpm.plan');
|
|
209
|
-
const parsed = parsePlanFile(planPath);
|
|
210
|
-
if (!parsed.data) {
|
|
211
|
-
throw new Error(`Failed to parse plan file: ${planPath}`);
|
|
212
|
-
}
|
|
213
|
-
for (const planChange of parsed.data.changes) {
|
|
214
|
-
const change = planChange.name;
|
|
215
|
-
const file = join(moduleDir, 'deploy', `${change}.sql`);
|
|
216
|
-
let content;
|
|
217
|
-
try {
|
|
218
|
-
content = readFileSync(file, 'utf-8');
|
|
219
|
-
}
|
|
220
|
-
catch {
|
|
221
|
-
issues.push({
|
|
222
|
-
change,
|
|
223
|
-
file,
|
|
224
|
-
kind: 'missing-file',
|
|
225
|
-
message: `deploy script not found for plan change "${change}"`
|
|
226
|
-
});
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
const { header } = parsePgpmHeader(content);
|
|
230
|
-
if (header.change && header.change !== change) {
|
|
231
|
-
issues.push({
|
|
232
|
-
change,
|
|
233
|
-
file,
|
|
234
|
-
kind: 'identity-mismatch',
|
|
235
|
-
message: `header declares "${header.change}" but path identity is "${change}"`
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
const planDeps = new Set(planChange.dependencies || []);
|
|
239
|
-
const headerDeps = new Set(header.requires.map(r => r.raw));
|
|
240
|
-
const missingInHeader = [...planDeps].filter(d => !headerDeps.has(d));
|
|
241
|
-
const missingInPlan = [...headerDeps].filter(d => !planDeps.has(d));
|
|
242
|
-
if (missingInHeader.length > 0 || missingInPlan.length > 0) {
|
|
243
|
-
const parts = [];
|
|
244
|
-
if (missingInHeader.length > 0) {
|
|
245
|
-
parts.push(`in plan but not in header requires: ${missingInHeader.join(', ')}`);
|
|
246
|
-
}
|
|
247
|
-
if (missingInPlan.length > 0) {
|
|
248
|
-
parts.push(`in header requires but not in plan: ${missingInPlan.join(', ')}`);
|
|
249
|
-
}
|
|
250
|
-
issues.push({
|
|
251
|
-
change,
|
|
252
|
-
file,
|
|
253
|
-
kind: 'requires-drift',
|
|
254
|
-
message: parts.join('; ')
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return issues;
|
|
259
|
-
}
|
package/esm/files/sql/index.js
DELETED
package/esm/files/sql/writer.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { getEnvOptions } from '@pgpmjs/env';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
/**
|
|
5
|
-
* Write SQL files for PGPM migrations (deploy, revert, verify)
|
|
6
|
-
*/
|
|
7
|
-
export const writePgpmFiles = (rows, opts) => {
|
|
8
|
-
rows.forEach((row) => writeVerify(row, opts));
|
|
9
|
-
rows.forEach((row) => writeRevert(row, opts));
|
|
10
|
-
rows.forEach((row) => writeDeploy(row, opts));
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Sort dependencies in a consistent order
|
|
14
|
-
*/
|
|
15
|
-
const ordered = (arr) => {
|
|
16
|
-
if (!arr)
|
|
17
|
-
return [];
|
|
18
|
-
return arr.sort((a, b) => a.length - b.length || a.localeCompare(b));
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Write a deploy SQL file for a PGPM change
|
|
22
|
-
*/
|
|
23
|
-
const writeDeploy = (row, opts) => {
|
|
24
|
-
const globalOpts = getEnvOptions({
|
|
25
|
-
migrations: {
|
|
26
|
-
codegen: {
|
|
27
|
-
useTx: opts.useTx
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
const useTx = globalOpts.migrations.codegen.useTx;
|
|
32
|
-
const deploy = opts.replacer(row.deploy);
|
|
33
|
-
const dir = path.dirname(deploy);
|
|
34
|
-
const prefix = path.join(opts.outdir, opts.name, 'deploy');
|
|
35
|
-
const actualDir = path.resolve(prefix, dir);
|
|
36
|
-
const actualFile = path.resolve(prefix, `${deploy}.sql`);
|
|
37
|
-
fs.mkdirSync(actualDir, { recursive: true });
|
|
38
|
-
const sqlContent = opts.replacer(row.content);
|
|
39
|
-
const content = `-- Deploy: ${deploy}
|
|
40
|
-
-- made with <3 @ constructive.io
|
|
41
|
-
|
|
42
|
-
${opts.replacer(ordered(row?.deps)
|
|
43
|
-
.map((dep) => `-- requires: ${dep}`)
|
|
44
|
-
.join('\n') || '')}
|
|
45
|
-
|
|
46
|
-
${useTx ? 'BEGIN;' : ''}
|
|
47
|
-
${sqlContent}
|
|
48
|
-
${useTx ? 'COMMIT;' : ''}
|
|
49
|
-
`;
|
|
50
|
-
fs.writeFileSync(actualFile, content);
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* Write a verify SQL file for a PGPM change
|
|
54
|
-
*/
|
|
55
|
-
const writeVerify = (row, opts) => {
|
|
56
|
-
const globalOpts = getEnvOptions({
|
|
57
|
-
migrations: {
|
|
58
|
-
codegen: {
|
|
59
|
-
useTx: opts.useTx
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
const useTx = globalOpts.migrations.codegen.useTx;
|
|
64
|
-
const deploy = opts.replacer(row.deploy);
|
|
65
|
-
const dir = path.dirname(deploy);
|
|
66
|
-
const prefix = path.join(opts.outdir, opts.name, 'verify');
|
|
67
|
-
const actualDir = path.resolve(prefix, dir);
|
|
68
|
-
const actualFile = path.resolve(prefix, `${deploy}.sql`);
|
|
69
|
-
fs.mkdirSync(actualDir, { recursive: true });
|
|
70
|
-
const sqlContent = opts.replacer(row.verify);
|
|
71
|
-
const content = opts.replacer(`-- Verify: ${deploy}
|
|
72
|
-
|
|
73
|
-
${useTx ? 'BEGIN;' : ''}
|
|
74
|
-
${sqlContent}
|
|
75
|
-
${useTx ? 'COMMIT;' : ''}
|
|
76
|
-
|
|
77
|
-
`);
|
|
78
|
-
fs.writeFileSync(actualFile, content);
|
|
79
|
-
};
|
|
80
|
-
/**
|
|
81
|
-
* Write a revert SQL file for a PGPM change
|
|
82
|
-
*/
|
|
83
|
-
const writeRevert = (row, opts) => {
|
|
84
|
-
const globalOpts = getEnvOptions({
|
|
85
|
-
migrations: {
|
|
86
|
-
codegen: {
|
|
87
|
-
useTx: opts.useTx
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
const useTx = globalOpts.migrations.codegen.useTx;
|
|
92
|
-
const deploy = opts.replacer(row.deploy);
|
|
93
|
-
const dir = path.dirname(deploy);
|
|
94
|
-
const prefix = path.join(opts.outdir, opts.name, 'revert');
|
|
95
|
-
const actualDir = path.resolve(prefix, dir);
|
|
96
|
-
const actualFile = path.resolve(prefix, `${deploy}.sql`);
|
|
97
|
-
fs.mkdirSync(actualDir, { recursive: true });
|
|
98
|
-
const sqlContent = opts.replacer(row.revert);
|
|
99
|
-
const content = `-- Revert: ${deploy}
|
|
100
|
-
|
|
101
|
-
${useTx ? 'BEGIN;' : ''}
|
|
102
|
-
${sqlContent}
|
|
103
|
-
${useTx ? 'COMMIT;' : ''}
|
|
104
|
-
|
|
105
|
-
`;
|
|
106
|
-
fs.writeFileSync(actualFile, content);
|
|
107
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
/**
|
|
4
|
-
* Read a SQL script file, return empty string if not found
|
|
5
|
-
*/
|
|
6
|
-
export function readScript(basePath, scriptType, changeName) {
|
|
7
|
-
const scriptPath = join(basePath, scriptType, `${changeName}.sql`);
|
|
8
|
-
if (!existsSync(scriptPath)) {
|
|
9
|
-
return '';
|
|
10
|
-
}
|
|
11
|
-
return readFileSync(scriptPath, 'utf-8');
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Check if a script file exists
|
|
15
|
-
*/
|
|
16
|
-
export function scriptExists(basePath, scriptType, changeName) {
|
|
17
|
-
const scriptPath = join(basePath, scriptType, `${changeName}.sql`);
|
|
18
|
-
return existsSync(scriptPath);
|
|
19
|
-
}
|
package/esm/files/types/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './package';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/esm/utils/author.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export function parseAuthor(author) {
|
|
2
|
-
const trimmed = (author || '').trim();
|
|
3
|
-
const match = trimmed.match(/^(.+?)\s*<([^>]+)>$/);
|
|
4
|
-
if (match) {
|
|
5
|
-
return {
|
|
6
|
-
fullName: match[1].trim(),
|
|
7
|
-
email: match[2].trim()
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
return { fullName: trimmed };
|
|
11
|
-
}
|
package/files/extension/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
// Re-export all extension functionality
|
|
18
|
-
__exportStar(require("./reader"), exports);
|
|
19
|
-
__exportStar(require("./writer"), exports);
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ExtensionInfo } from './writer';
|
|
2
|
-
export interface Module {
|
|
3
|
-
path: string;
|
|
4
|
-
requires: string[];
|
|
5
|
-
version: string;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Parse a .control file and extract its metadata.
|
|
9
|
-
* https://www.postgresql.org/docs/current/extend-extensions.html
|
|
10
|
-
*/
|
|
11
|
-
export declare function parseControlFile(filePath: string, basePath: string): Module;
|
|
12
|
-
/**
|
|
13
|
-
* Parse the pgpm.plan file to get the extension name.
|
|
14
|
-
*/
|
|
15
|
-
export declare const getExtensionName: (packageDir: string) => string;
|
|
16
|
-
/**
|
|
17
|
-
* Get detailed information about an extension in the specified directory.
|
|
18
|
-
*/
|
|
19
|
-
export declare const getExtensionInfo: (packageDir: string) => ExtensionInfo;
|
|
20
|
-
/**
|
|
21
|
-
* Get a list of extensions required by an extension from its control file.
|
|
22
|
-
* Returns an empty array if the file doesn't exist or has no requires line.
|
|
23
|
-
*/
|
|
24
|
-
export declare const getInstalledExtensions: (controlFilePath: string) => string[];
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getInstalledExtensions = exports.getExtensionInfo = exports.getExtensionName = void 0;
|
|
4
|
-
exports.parseControlFile = parseControlFile;
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
const plan_1 = require("../plan");
|
|
8
|
-
/**
|
|
9
|
-
* Parse a .control file and extract its metadata.
|
|
10
|
-
* https://www.postgresql.org/docs/current/extend-extensions.html
|
|
11
|
-
*/
|
|
12
|
-
function parseControlFile(filePath, basePath) {
|
|
13
|
-
const contents = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
14
|
-
const key = (0, path_1.basename)(filePath).split('.control')[0];
|
|
15
|
-
const requires = contents
|
|
16
|
-
.split('\n')
|
|
17
|
-
.find((line) => /^requires/.test(line))
|
|
18
|
-
?.split('=')[1]
|
|
19
|
-
.split(',')
|
|
20
|
-
.map((req) => req.replace(/[\'\s]*/g, '').trim()) || [];
|
|
21
|
-
const version = contents
|
|
22
|
-
.split('\n')
|
|
23
|
-
.find((line) => /^default_version/.test(line))
|
|
24
|
-
?.split('=')[1]
|
|
25
|
-
.replace(/[\']*/g, '')
|
|
26
|
-
.trim() || '';
|
|
27
|
-
return {
|
|
28
|
-
path: (0, path_1.dirname)((0, path_1.relative)(basePath, filePath)),
|
|
29
|
-
requires,
|
|
30
|
-
version,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Parse the pgpm.plan file to get the extension name.
|
|
35
|
-
*/
|
|
36
|
-
const getExtensionName = (packageDir) => {
|
|
37
|
-
const planPath = `${packageDir}/pgpm.plan`;
|
|
38
|
-
const plan = (0, plan_1.parsePlanFileSimple)(planPath);
|
|
39
|
-
if (!plan.package) {
|
|
40
|
-
throw new Error('No package name found in pgpm.plan!');
|
|
41
|
-
}
|
|
42
|
-
return plan.package;
|
|
43
|
-
};
|
|
44
|
-
exports.getExtensionName = getExtensionName;
|
|
45
|
-
/**
|
|
46
|
-
* Get detailed information about an extension in the specified directory.
|
|
47
|
-
*/
|
|
48
|
-
const getExtensionInfo = (packageDir) => {
|
|
49
|
-
const pkgPath = `${packageDir}/package.json`;
|
|
50
|
-
const pkg = require(pkgPath);
|
|
51
|
-
const extname = (0, exports.getExtensionName)(packageDir);
|
|
52
|
-
const version = pkg.version;
|
|
53
|
-
const Makefile = `${packageDir}/Makefile`;
|
|
54
|
-
const controlFile = `${packageDir}/${extname}.control`;
|
|
55
|
-
const sqlFile = `sql/${extname}--${version}.sql`;
|
|
56
|
-
return { extname, packageDir, version, Makefile, controlFile, sqlFile };
|
|
57
|
-
};
|
|
58
|
-
exports.getExtensionInfo = getExtensionInfo;
|
|
59
|
-
/**
|
|
60
|
-
* Get a list of extensions required by an extension from its control file.
|
|
61
|
-
* Returns an empty array if the file doesn't exist or has no requires line.
|
|
62
|
-
*/
|
|
63
|
-
const getInstalledExtensions = (controlFilePath) => {
|
|
64
|
-
try {
|
|
65
|
-
const contents = (0, fs_1.readFileSync)(controlFilePath, 'utf-8');
|
|
66
|
-
const match = contents.match(/^\s*requires\s*=\s*'([^']*)'/m);
|
|
67
|
-
if (!match) {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
const requiresValue = match[1];
|
|
71
|
-
if (!requiresValue || requiresValue.trim() === '') {
|
|
72
|
-
return [];
|
|
73
|
-
}
|
|
74
|
-
return requiresValue
|
|
75
|
-
.split(',')
|
|
76
|
-
.map((ext) => ext.trim())
|
|
77
|
-
.filter((ext) => ext.length > 0);
|
|
78
|
-
}
|
|
79
|
-
catch (e) {
|
|
80
|
-
if (e.code === 'ENOENT') {
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
throw new Error(`Error reading control file at ${controlFilePath}: ${e.message}`);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
exports.getInstalledExtensions = getInstalledExtensions;
|