@pgpmjs/ast 0.1.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/LICENSE +23 -0
- package/README.md +99 -0
- package/esm/files/extension/index.js +3 -0
- package/esm/files/extension/reader.js +79 -0
- package/esm/files/extension/writer.js +99 -0
- package/esm/files/index.js +6 -0
- package/esm/files/plan/generator.js +82 -0
- package/esm/files/plan/index.js +5 -0
- package/esm/files/plan/parser.js +297 -0
- package/esm/files/plan/validators.js +181 -0
- package/esm/files/plan/writer.js +142 -0
- package/esm/files/sql/header.js +259 -0
- package/esm/files/sql/index.js +2 -0
- package/esm/files/sql/writer.js +107 -0
- package/esm/files/sql-scripts/index.js +2 -0
- package/esm/files/sql-scripts/reader.js +19 -0
- package/esm/files/types/index.js +1 -0
- package/esm/files/types/package.js +1 -0
- package/esm/hash.js +9 -0
- package/esm/index.js +18 -0
- package/esm/module/index.js +12 -0
- package/esm/module/parse.js +27 -0
- package/esm/module/reader.js +64 -0
- package/esm/module/roundtrip.js +65 -0
- package/esm/module/types.js +1 -0
- package/esm/module/writer.js +45 -0
- package/esm/plan-rename.js +28 -0
- package/esm/utils/author.js +11 -0
- package/files/extension/index.d.ts +2 -0
- package/files/extension/index.js +19 -0
- package/files/extension/reader.d.ts +24 -0
- package/files/extension/reader.js +86 -0
- package/files/extension/writer.d.ts +48 -0
- package/files/extension/writer.js +107 -0
- package/files/index.d.ts +5 -0
- package/files/index.js +22 -0
- package/files/plan/generator.d.ts +23 -0
- package/files/plan/generator.js +90 -0
- package/files/plan/index.d.ts +4 -0
- package/files/plan/index.js +21 -0
- package/files/plan/parser.d.ts +28 -0
- package/files/plan/parser.js +304 -0
- package/files/plan/validators.d.ts +52 -0
- package/files/plan/validators.js +187 -0
- package/files/plan/writer.d.ts +27 -0
- package/files/plan/writer.js +152 -0
- package/files/sql/header.d.ts +114 -0
- package/files/sql/header.js +266 -0
- package/files/sql/index.d.ts +2 -0
- package/files/sql/index.js +18 -0
- package/files/sql/writer.d.ts +12 -0
- package/files/sql/writer.js +114 -0
- package/files/sql-scripts/index.d.ts +1 -0
- package/files/sql-scripts/index.js +18 -0
- package/files/sql-scripts/reader.d.ts +8 -0
- package/files/sql-scripts/reader.js +23 -0
- package/files/types/index.d.ts +46 -0
- package/files/types/index.js +17 -0
- package/files/types/package.d.ts +20 -0
- package/files/types/package.js +2 -0
- package/hash.d.ts +6 -0
- package/hash.js +12 -0
- package/index.d.ts +19 -0
- package/index.js +36 -0
- package/module/index.d.ts +12 -0
- package/module/index.js +28 -0
- package/module/parse.d.ts +23 -0
- package/module/parse.js +31 -0
- package/module/reader.d.ts +13 -0
- package/module/reader.js +67 -0
- package/module/roundtrip.d.ts +22 -0
- package/module/roundtrip.js +68 -0
- package/module/types.d.ts +72 -0
- package/module/types.js +2 -0
- package/module/writer.d.ts +30 -0
- package/module/writer.js +50 -0
- package/package.json +48 -0
- package/plan-rename.d.ts +8 -0
- package/plan-rename.js +31 -0
- package/utils/author.d.ts +5 -0
- package/utils/author.js +14 -0
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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);
|
|
@@ -0,0 +1,24 @@
|
|
|
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[];
|
|
@@ -0,0 +1,86 @@
|
|
|
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;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write the Makefile for the extension.
|
|
3
|
+
*/
|
|
4
|
+
export declare const writeExtensionMakefile: (outputPath: string, extname: string, version: string) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Generate content for a .control file
|
|
7
|
+
* https://www.postgresql.org/docs/current/extend-extensions.html
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateControlFileContent(options: {
|
|
10
|
+
name: string;
|
|
11
|
+
version: string;
|
|
12
|
+
comment?: string;
|
|
13
|
+
requires?: string[];
|
|
14
|
+
default_version?: string;
|
|
15
|
+
relocatable?: boolean;
|
|
16
|
+
superuser?: boolean;
|
|
17
|
+
schema?: string;
|
|
18
|
+
module_pathname?: string;
|
|
19
|
+
}): string;
|
|
20
|
+
/**
|
|
21
|
+
* Update (or insert/remove) the `requires =` line of an existing .control file
|
|
22
|
+
* in place, preserving every other field and the file's formatting. Used when
|
|
23
|
+
* changing a module's dependencies after init (e.g. via `pgpm extension`) so
|
|
24
|
+
* hand-tuned fields (comment, schema, relocatable, module_pathname) survive.
|
|
25
|
+
*/
|
|
26
|
+
export declare function upsertRequiresLine(content: string, requires: string[]): string;
|
|
27
|
+
/**
|
|
28
|
+
* Write the control file for the extension.
|
|
29
|
+
* If the file already exists, only its `requires` line is updated so that any
|
|
30
|
+
* custom fields are preserved; otherwise a fresh file is generated.
|
|
31
|
+
*/
|
|
32
|
+
export declare const writeExtensionControlFile: (outputPath: string, extname: string, extensions: string[], version: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Interface for extension information
|
|
35
|
+
*/
|
|
36
|
+
export interface ExtensionInfo {
|
|
37
|
+
extname: string;
|
|
38
|
+
packageDir: string;
|
|
39
|
+
version: string;
|
|
40
|
+
Makefile: string;
|
|
41
|
+
controlFile: string;
|
|
42
|
+
sqlFile: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Write control and Makefile for the extension with given data.
|
|
46
|
+
* If extInfo is not provided, it will be generated from packageDir.
|
|
47
|
+
*/
|
|
48
|
+
export declare const writeExtensions: (packageDir: string, extensions: string[], extInfo?: ExtensionInfo) => void;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeExtensions = exports.writeExtensionControlFile = exports.writeExtensionMakefile = void 0;
|
|
4
|
+
exports.generateControlFileContent = generateControlFileContent;
|
|
5
|
+
exports.upsertRequiresLine = upsertRequiresLine;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const reader_1 = require("./reader");
|
|
8
|
+
/**
|
|
9
|
+
* Write the Makefile for the extension.
|
|
10
|
+
*/
|
|
11
|
+
const writeExtensionMakefile = (outputPath, extname, version) => {
|
|
12
|
+
const content = `EXTENSION = ${extname}
|
|
13
|
+
DATA = sql/${extname}--${version}.sql
|
|
14
|
+
|
|
15
|
+
PG_CONFIG = pg_config
|
|
16
|
+
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
|
17
|
+
include $(PGXS)
|
|
18
|
+
`;
|
|
19
|
+
(0, fs_1.writeFileSync)(outputPath, content);
|
|
20
|
+
};
|
|
21
|
+
exports.writeExtensionMakefile = writeExtensionMakefile;
|
|
22
|
+
/**
|
|
23
|
+
* Generate content for a .control file
|
|
24
|
+
* https://www.postgresql.org/docs/current/extend-extensions.html
|
|
25
|
+
*/
|
|
26
|
+
function generateControlFileContent(options) {
|
|
27
|
+
const { name, version, comment = `${name} extension`, requires = [], default_version = version, relocatable = false, superuser = false, schema, module_pathname } = options;
|
|
28
|
+
let content = `# ${name} extension
|
|
29
|
+
comment = '${comment}'
|
|
30
|
+
default_version = '${default_version}'
|
|
31
|
+
`;
|
|
32
|
+
if (module_pathname) {
|
|
33
|
+
content += `module_pathname = '${module_pathname}'\n`;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
content += `module_pathname = '$libdir/${name}'\n`;
|
|
37
|
+
}
|
|
38
|
+
if (requires.length > 0) {
|
|
39
|
+
content += `requires = '${requires.join(',')}'\n`;
|
|
40
|
+
}
|
|
41
|
+
content += `relocatable = ${relocatable}
|
|
42
|
+
superuser = ${superuser}\n`;
|
|
43
|
+
if (schema) {
|
|
44
|
+
content += `schema = ${schema}\n`;
|
|
45
|
+
}
|
|
46
|
+
return content;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Update (or insert/remove) the `requires =` line of an existing .control file
|
|
50
|
+
* in place, preserving every other field and the file's formatting. Used when
|
|
51
|
+
* changing a module's dependencies after init (e.g. via `pgpm extension`) so
|
|
52
|
+
* hand-tuned fields (comment, schema, relocatable, module_pathname) survive.
|
|
53
|
+
*/
|
|
54
|
+
function upsertRequiresLine(content, requires) {
|
|
55
|
+
const lines = content.split('\n');
|
|
56
|
+
const idx = lines.findIndex((line) => /^\s*requires\s*=/.test(line));
|
|
57
|
+
if (requires.length === 0) {
|
|
58
|
+
if (idx !== -1)
|
|
59
|
+
lines.splice(idx, 1);
|
|
60
|
+
return lines.join('\n');
|
|
61
|
+
}
|
|
62
|
+
const requiresLine = `requires = '${requires.join(',')}'`;
|
|
63
|
+
if (idx !== -1) {
|
|
64
|
+
lines[idx] = requiresLine;
|
|
65
|
+
return lines.join('\n');
|
|
66
|
+
}
|
|
67
|
+
// Insert to match generated ordering (before relocatable/superuser/schema).
|
|
68
|
+
const insertAt = lines.findIndex((line) => /^\s*(relocatable|superuser|schema)\s*=/.test(line));
|
|
69
|
+
if (insertAt !== -1) {
|
|
70
|
+
lines.splice(insertAt, 0, requiresLine);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
lines.push(requiresLine);
|
|
74
|
+
}
|
|
75
|
+
return lines.join('\n');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Write the control file for the extension.
|
|
79
|
+
* If the file already exists, only its `requires` line is updated so that any
|
|
80
|
+
* custom fields are preserved; otherwise a fresh file is generated.
|
|
81
|
+
*/
|
|
82
|
+
const writeExtensionControlFile = (outputPath, extname, extensions, version) => {
|
|
83
|
+
if ((0, fs_1.existsSync)(outputPath)) {
|
|
84
|
+
const existing = (0, fs_1.readFileSync)(outputPath, 'utf-8');
|
|
85
|
+
(0, fs_1.writeFileSync)(outputPath, upsertRequiresLine(existing, extensions));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const content = generateControlFileContent({
|
|
89
|
+
name: extname,
|
|
90
|
+
version,
|
|
91
|
+
requires: extensions
|
|
92
|
+
});
|
|
93
|
+
(0, fs_1.writeFileSync)(outputPath, content);
|
|
94
|
+
};
|
|
95
|
+
exports.writeExtensionControlFile = writeExtensionControlFile;
|
|
96
|
+
/**
|
|
97
|
+
* Write control and Makefile for the extension with given data.
|
|
98
|
+
* If extInfo is not provided, it will be generated from packageDir.
|
|
99
|
+
*/
|
|
100
|
+
const writeExtensions = (packageDir, extensions, extInfo) => {
|
|
101
|
+
// If extInfo is not provided, get it from packageDir
|
|
102
|
+
const info = extInfo || (0, reader_1.getExtensionInfo)(packageDir);
|
|
103
|
+
const { controlFile, Makefile, extname, version } = info;
|
|
104
|
+
(0, exports.writeExtensionControlFile)(controlFile, extname, extensions, version);
|
|
105
|
+
(0, exports.writeExtensionMakefile)(Makefile, extname, version);
|
|
106
|
+
};
|
|
107
|
+
exports.writeExtensions = writeExtensions;
|
package/files/index.d.ts
ADDED
package/files/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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 functionality from package-files
|
|
18
|
+
__exportStar(require("./extension"), exports);
|
|
19
|
+
__exportStar(require("./plan"), exports);
|
|
20
|
+
__exportStar(require("./sql"), exports);
|
|
21
|
+
__exportStar(require("./sql-scripts"), exports);
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get a timestamp for the plan file.
|
|
3
|
+
* Uses the PGPM_FROZEN_TIME env var if present, otherwise the fixed epoch.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getNow(): string;
|
|
6
|
+
export interface PlanEntry {
|
|
7
|
+
change: string;
|
|
8
|
+
dependencies: string[];
|
|
9
|
+
comment?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface GeneratePlanOptions {
|
|
12
|
+
moduleName: string;
|
|
13
|
+
uri?: string;
|
|
14
|
+
entries: PlanEntry[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate a plan file content from structured data
|
|
18
|
+
*/
|
|
19
|
+
export declare function generatePlan(options: GeneratePlanOptions): string;
|
|
20
|
+
/**
|
|
21
|
+
* Write a generated plan file to disk
|
|
22
|
+
*/
|
|
23
|
+
export declare function writePlan(planPath: string, plan: string): void;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getNow = getNow;
|
|
7
|
+
exports.generatePlan = generatePlan;
|
|
8
|
+
exports.writePlan = writePlan;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
/**
|
|
11
|
+
* Fixed epoch timestamp used for deterministic plan generation.
|
|
12
|
+
* Can be overridden with the PGPM_FROZEN_TIME environment variable.
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_FROZEN_TIME = '2017-08-11T08:11:51Z';
|
|
15
|
+
/**
|
|
16
|
+
* Get a UTC timestamp string from a Date object.
|
|
17
|
+
*/
|
|
18
|
+
function getUTCTimestamp(d) {
|
|
19
|
+
return (d.getUTCFullYear() +
|
|
20
|
+
'-' + String(d.getUTCMonth() + 1).padStart(2, '0') +
|
|
21
|
+
'-' + String(d.getUTCDate()).padStart(2, '0') +
|
|
22
|
+
'T' + String(d.getUTCHours()).padStart(2, '0') +
|
|
23
|
+
':' + String(d.getUTCMinutes()).padStart(2, '0') +
|
|
24
|
+
':' + String(d.getUTCSeconds()).padStart(2, '0') +
|
|
25
|
+
'Z');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get a timestamp for the plan file.
|
|
29
|
+
* Uses the PGPM_FROZEN_TIME env var if present, otherwise the fixed epoch.
|
|
30
|
+
*/
|
|
31
|
+
function getNow() {
|
|
32
|
+
const frozen = process.env.PGPM_FROZEN_TIME;
|
|
33
|
+
if (frozen) {
|
|
34
|
+
return getUTCTimestamp(new Date(frozen));
|
|
35
|
+
}
|
|
36
|
+
return DEFAULT_FROZEN_TIME;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Sort dependencies by their order in the supplied plan, so prerequisites
|
|
40
|
+
* (parent migrations) appear before their dependents in the bracket.
|
|
41
|
+
* Internal dependencies are sorted by their plan index, and external
|
|
42
|
+
* dependencies (not in the plan) preserve their original order.
|
|
43
|
+
*/
|
|
44
|
+
const sortDependencies = (deps, order) => {
|
|
45
|
+
const originalOrder = new Map(deps.map((dep, index) => [dep, index]));
|
|
46
|
+
return [...deps].sort((a, b) => {
|
|
47
|
+
const aIndex = order.get(a);
|
|
48
|
+
const bIndex = order.get(b);
|
|
49
|
+
const aOriginal = originalOrder.get(a);
|
|
50
|
+
const bOriginal = originalOrder.get(b);
|
|
51
|
+
if (aIndex !== undefined && bIndex !== undefined)
|
|
52
|
+
return aIndex - bIndex || aOriginal - bOriginal;
|
|
53
|
+
if (aIndex !== undefined)
|
|
54
|
+
return -1;
|
|
55
|
+
if (bIndex !== undefined)
|
|
56
|
+
return 1;
|
|
57
|
+
return aOriginal - bOriginal;
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Generate a plan file content from structured data
|
|
62
|
+
*/
|
|
63
|
+
function generatePlan(options) {
|
|
64
|
+
const { moduleName, uri, entries } = options;
|
|
65
|
+
const now = getNow();
|
|
66
|
+
const planfile = [
|
|
67
|
+
`%syntax-version=1.0.0`,
|
|
68
|
+
`%project=${moduleName}`,
|
|
69
|
+
`%uri=${uri || moduleName}`
|
|
70
|
+
];
|
|
71
|
+
// Build an order map from the entry list so brackets can be sorted by plan order
|
|
72
|
+
const entryOrder = new Map(entries.map((entry, index) => [entry.change, index]));
|
|
73
|
+
// Generate the plan entries
|
|
74
|
+
entries.forEach(entry => {
|
|
75
|
+
const deps = sortDependencies(entry.dependencies || [], entryOrder);
|
|
76
|
+
if (deps.length > 0) {
|
|
77
|
+
planfile.push(`${entry.change} [${deps.join(' ')}] ${now} constructive <constructive@5b0c196eeb62>${entry.comment ? ` # ${entry.comment}` : ''}`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
planfile.push(`${entry.change} ${now} constructive <constructive@5b0c196eeb62>${entry.comment ? ` # ${entry.comment}` : ''}`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return planfile.join('\n');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Write a generated plan file to disk
|
|
87
|
+
*/
|
|
88
|
+
function writePlan(planPath, plan) {
|
|
89
|
+
fs_1.default.writeFileSync(planPath, plan);
|
|
90
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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 plan functionality
|
|
18
|
+
__exportStar(require("./generator"), exports);
|
|
19
|
+
__exportStar(require("./parser"), exports);
|
|
20
|
+
__exportStar(require("./validators"), exports);
|
|
21
|
+
__exportStar(require("./writer"), exports);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ExtendedPlanFile, ParseResult, PlanFile } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a Sqitch plan file with full validation
|
|
4
|
+
* Supports both changes and tags
|
|
5
|
+
*/
|
|
6
|
+
export declare function parsePlanFile(planPath: string): ParseResult<ExtendedPlanFile>;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a reference within a plan file context
|
|
9
|
+
* Handles symbolic references (HEAD, ROOT) and relative references
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveReference(ref: string, plan: ExtendedPlanFile, currentPackage?: string): {
|
|
12
|
+
change?: string;
|
|
13
|
+
tag?: string;
|
|
14
|
+
error?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Simple plan file parser without validation.
|
|
18
|
+
* Returns a PlanFile (without tags) for callers that don't need tag data.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parsePlanFileSimple(planPath: string): PlanFile;
|
|
21
|
+
/**
|
|
22
|
+
* Get all change names from a plan file
|
|
23
|
+
*/
|
|
24
|
+
export declare function getChanges(planPath: string): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Get the latest (last) change from a plan file
|
|
27
|
+
*/
|
|
28
|
+
export declare function getLatestChange(planPath: string): string;
|