pgpm 1.4.2 → 2.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/commands/init/index.d.ts +2 -2
- package/commands/init/index.js +269 -16
- package/commands/init/module.js +2 -0
- package/commands/init/workspace.js +5 -3
- package/esm/commands/init/index.js +269 -16
- package/esm/commands/init/module.js +2 -0
- package/esm/commands/init/workspace.js +5 -3
- package/package.json +5 -5
package/commands/init/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CLIOptions, Inquirerer } from 'inquirerer';
|
|
2
2
|
export declare const createInitUsageText: (binaryName: string, productLabel?: string) => string;
|
|
3
3
|
declare const _default: (argv: Partial<Record<string, any>>, prompter: Inquirerer, _options: CLIOptions) => Promise<{
|
|
4
|
-
[x: string]: any;
|
|
5
|
-
} | {
|
|
6
4
|
cwd: string;
|
|
5
|
+
} | {
|
|
6
|
+
[x: string]: any;
|
|
7
7
|
}>;
|
|
8
8
|
export default _default;
|
package/commands/init/index.js
CHANGED
|
@@ -4,29 +4,42 @@ 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
|
|
8
|
-
const
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const core_1 = require("@pgpmjs/core");
|
|
10
|
+
const types_1 = require("@pgpmjs/types");
|
|
11
|
+
const inquirerer_1 = require("inquirerer");
|
|
12
|
+
const DEFAULT_MOTD = `
|
|
13
|
+
| _ _
|
|
14
|
+
=== |.===. '\\-//\`
|
|
15
|
+
(o o) {}o o{} (o o)
|
|
16
|
+
ooO--(_)--Ooo-ooO--(_)--Ooo-ooO--(_)--Ooo-
|
|
17
|
+
`;
|
|
9
18
|
const createInitUsageText = (binaryName, productLabel) => {
|
|
10
19
|
const displayName = productLabel ?? binaryName;
|
|
11
20
|
return `
|
|
12
21
|
Init Command:
|
|
13
22
|
|
|
14
|
-
${binaryName} init [OPTIONS] [
|
|
23
|
+
${binaryName} init [OPTIONS] [<fromPath>]
|
|
15
24
|
|
|
16
|
-
Initialize ${displayName}
|
|
25
|
+
Initialize ${displayName} from a template. The template's type (workspace/module)
|
|
26
|
+
determines the behavior automatically.
|
|
17
27
|
|
|
18
28
|
Options:
|
|
19
29
|
--help, -h Show this help message
|
|
20
30
|
--cwd <directory> Working directory (default: current directory)
|
|
21
31
|
--repo <repo> Template repo (default: https://github.com/constructive-io/pgpm-boilerplates.git)
|
|
22
|
-
--template-path <path> Template sub-path (default: workspace/module) or local path override
|
|
23
32
|
--from-branch <branch> Branch/tag to use when cloning repo
|
|
33
|
+
--dir <variant> Template variant directory (e.g., supabase, drizzle)
|
|
34
|
+
--boilerplate Prompt to select from available boilerplates
|
|
24
35
|
|
|
25
36
|
Examples:
|
|
26
|
-
${binaryName} init Initialize new module
|
|
37
|
+
${binaryName} init Initialize new module (default)
|
|
27
38
|
${binaryName} init workspace Initialize new workspace
|
|
39
|
+
${binaryName} init module Initialize new module explicitly
|
|
40
|
+
${binaryName} init workspace --dir <variant> Use variant templates
|
|
41
|
+
${binaryName} init --boilerplate Select from available boilerplates
|
|
28
42
|
${binaryName} init --repo owner/repo Use templates from GitHub repository
|
|
29
|
-
${binaryName} init --template-path ./custom-templates Use templates from local path
|
|
30
43
|
${binaryName} init --repo owner/repo --from-branch develop Use specific branch
|
|
31
44
|
`;
|
|
32
45
|
};
|
|
@@ -37,16 +50,256 @@ exports.default = async (argv, prompter, _options) => {
|
|
|
37
50
|
console.log((0, exports.createInitUsageText)('pgpm'));
|
|
38
51
|
process.exit(0);
|
|
39
52
|
}
|
|
40
|
-
return
|
|
53
|
+
return handleInit(argv, prompter);
|
|
41
54
|
};
|
|
42
|
-
async function
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
async function handleInit(argv, prompter) {
|
|
56
|
+
const { cwd = process.cwd() } = argv;
|
|
57
|
+
const templateRepo = argv.repo ?? core_1.DEFAULT_TEMPLATE_REPO;
|
|
58
|
+
const branch = argv.fromBranch;
|
|
59
|
+
const dir = argv.dir;
|
|
60
|
+
const noTty = Boolean(argv.noTty || argv['no-tty'] || process.env.CI === 'true');
|
|
61
|
+
const useBoilerplatePrompt = Boolean(argv.boilerplate);
|
|
62
|
+
// Get fromPath from first positional arg
|
|
63
|
+
const positionalFromPath = argv._?.[0];
|
|
64
|
+
// Handle --boilerplate flag: separate path from regular init
|
|
65
|
+
if (useBoilerplatePrompt) {
|
|
66
|
+
return handleBoilerplateInit(argv, prompter, {
|
|
67
|
+
positionalFromPath,
|
|
68
|
+
templateRepo,
|
|
69
|
+
branch,
|
|
70
|
+
dir,
|
|
71
|
+
noTty,
|
|
72
|
+
cwd,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Regular init path: default to 'module' if no fromPath provided
|
|
76
|
+
const fromPath = positionalFromPath || 'module';
|
|
77
|
+
// Inspect the template to get its type
|
|
78
|
+
const inspection = (0, core_1.inspectTemplate)({
|
|
79
|
+
fromPath,
|
|
80
|
+
templateRepo,
|
|
81
|
+
branch,
|
|
82
|
+
dir,
|
|
83
|
+
toolName: core_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
84
|
+
cwd,
|
|
85
|
+
});
|
|
86
|
+
const templateType = inspection.config?.type;
|
|
87
|
+
// Branch based on template type
|
|
88
|
+
if (templateType === 'workspace') {
|
|
89
|
+
return handleWorkspaceInit(argv, prompter, {
|
|
90
|
+
fromPath,
|
|
91
|
+
templateRepo,
|
|
92
|
+
branch,
|
|
93
|
+
dir,
|
|
94
|
+
noTty,
|
|
95
|
+
cwd,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// Default to module init (for 'module' type or unknown types)
|
|
99
|
+
return handleModuleInit(argv, prompter, {
|
|
100
|
+
fromPath,
|
|
101
|
+
templateRepo,
|
|
102
|
+
branch,
|
|
103
|
+
dir,
|
|
104
|
+
noTty,
|
|
105
|
+
cwd,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async function handleBoilerplateInit(argv, prompter, ctx) {
|
|
109
|
+
let fromPath;
|
|
110
|
+
if (ctx.positionalFromPath) {
|
|
111
|
+
// If a positional fromPath was provided with --boilerplate, use it directly
|
|
112
|
+
fromPath = ctx.positionalFromPath;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// No positional arg: prompt user to select from available boilerplates
|
|
116
|
+
if (ctx.noTty) {
|
|
117
|
+
throw new Error('Cannot use --boilerplate without a <fromPath> argument in non-interactive mode. ' +
|
|
118
|
+
'Please specify a boilerplate explicitly, e.g., `pgpm init workspace --boilerplate`');
|
|
119
|
+
}
|
|
120
|
+
// Inspect without fromPath to get the template directory for scanning
|
|
121
|
+
const initialInspection = (0, core_1.inspectTemplate)({
|
|
122
|
+
templateRepo: ctx.templateRepo,
|
|
123
|
+
branch: ctx.branch,
|
|
124
|
+
dir: ctx.dir,
|
|
125
|
+
toolName: core_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
126
|
+
cwd: ctx.cwd,
|
|
127
|
+
});
|
|
128
|
+
// Resolve the base directory for scanning boilerplates:
|
|
129
|
+
// - If --dir is specified, use the resolvedTemplatePath (bypasses .boilerplates.json)
|
|
130
|
+
// - Otherwise, use .boilerplates.json's dir (defaults to repo root if missing)
|
|
131
|
+
const baseDir = ctx.dir
|
|
132
|
+
? initialInspection.resolvedTemplatePath
|
|
133
|
+
: (0, core_1.resolveBoilerplateBaseDir)(initialInspection.templateDir);
|
|
134
|
+
const boilerplates = (0, core_1.scanBoilerplates)(baseDir);
|
|
135
|
+
if (boilerplates.length === 0) {
|
|
136
|
+
throw new Error(`No boilerplates found in the template repository.\n` +
|
|
137
|
+
`Checked directory: ${baseDir}\n` +
|
|
138
|
+
`Make sure the repository contains boilerplate directories with .boilerplate.json files.`);
|
|
139
|
+
}
|
|
140
|
+
const boilerplateQuestion = [
|
|
141
|
+
{
|
|
142
|
+
name: 'selectedBoilerplate',
|
|
143
|
+
message: 'Select a boilerplate',
|
|
144
|
+
type: 'autocomplete',
|
|
145
|
+
options: boilerplates.map((bp) => bp.name),
|
|
146
|
+
required: true,
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
const boilerplateAnswer = await prompter.prompt(argv, boilerplateQuestion);
|
|
150
|
+
fromPath = boilerplateAnswer.selectedBoilerplate;
|
|
151
|
+
}
|
|
152
|
+
// Inspect the selected template to get its type
|
|
153
|
+
const inspection = (0, core_1.inspectTemplate)({
|
|
154
|
+
fromPath,
|
|
155
|
+
templateRepo: ctx.templateRepo,
|
|
156
|
+
branch: ctx.branch,
|
|
157
|
+
dir: ctx.dir,
|
|
158
|
+
toolName: core_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
159
|
+
cwd: ctx.cwd,
|
|
160
|
+
});
|
|
161
|
+
const templateType = inspection.config?.type;
|
|
162
|
+
// Branch based on template type
|
|
163
|
+
if (templateType === 'workspace') {
|
|
164
|
+
return handleWorkspaceInit(argv, prompter, {
|
|
165
|
+
fromPath,
|
|
166
|
+
templateRepo: ctx.templateRepo,
|
|
167
|
+
branch: ctx.branch,
|
|
168
|
+
dir: ctx.dir,
|
|
169
|
+
noTty: ctx.noTty,
|
|
170
|
+
cwd: ctx.cwd,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// Default to module init (for 'module' type or unknown types)
|
|
174
|
+
return handleModuleInit(argv, prompter, {
|
|
175
|
+
fromPath,
|
|
176
|
+
templateRepo: ctx.templateRepo,
|
|
177
|
+
branch: ctx.branch,
|
|
178
|
+
dir: ctx.dir,
|
|
179
|
+
noTty: ctx.noTty,
|
|
180
|
+
cwd: ctx.cwd,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async function handleWorkspaceInit(argv, prompter, ctx) {
|
|
184
|
+
const workspaceQuestions = [
|
|
185
|
+
{
|
|
186
|
+
name: 'name',
|
|
187
|
+
message: 'Enter workspace name',
|
|
188
|
+
required: true,
|
|
189
|
+
type: 'text'
|
|
190
|
+
}
|
|
191
|
+
];
|
|
192
|
+
const answers = await prompter.prompt(argv, workspaceQuestions);
|
|
193
|
+
const targetPath = path_1.default.join(ctx.cwd, (0, core_1.sluggify)(answers.name));
|
|
194
|
+
// Register workspace.dirname resolver so boilerplate templates can use it via defaultFrom/setFrom
|
|
195
|
+
const dirName = path_1.default.basename(targetPath);
|
|
196
|
+
(0, inquirerer_1.registerDefaultResolver)('workspace.dirname', () => dirName);
|
|
197
|
+
await (0, core_1.scaffoldTemplate)({
|
|
198
|
+
fromPath: ctx.fromPath,
|
|
199
|
+
outputDir: targetPath,
|
|
200
|
+
templateRepo: ctx.templateRepo,
|
|
201
|
+
branch: ctx.branch,
|
|
202
|
+
dir: ctx.dir,
|
|
203
|
+
answers: {
|
|
46
204
|
...argv,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
205
|
+
...answers,
|
|
206
|
+
workspaceName: answers.name
|
|
207
|
+
},
|
|
208
|
+
toolName: core_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
209
|
+
noTty: ctx.noTty,
|
|
210
|
+
cwd: ctx.cwd,
|
|
211
|
+
prompter
|
|
212
|
+
});
|
|
213
|
+
// Check for .motd file and print it, or use default ASCII art
|
|
214
|
+
const motdPath = path_1.default.join(targetPath, '.motd');
|
|
215
|
+
let motd = DEFAULT_MOTD;
|
|
216
|
+
if (fs_1.default.existsSync(motdPath)) {
|
|
217
|
+
try {
|
|
218
|
+
motd = fs_1.default.readFileSync(motdPath, 'utf8');
|
|
219
|
+
fs_1.default.unlinkSync(motdPath);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Ignore errors reading/deleting .motd
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
process.stdout.write(motd);
|
|
226
|
+
if (!motd.endsWith('\n')) {
|
|
227
|
+
process.stdout.write('\n');
|
|
228
|
+
}
|
|
229
|
+
process.stdout.write(`\n✨ Enjoy!\n\ncd ./${dirName}\n`);
|
|
230
|
+
return { ...argv, ...answers, cwd: targetPath };
|
|
231
|
+
}
|
|
232
|
+
async function handleModuleInit(argv, prompter, ctx) {
|
|
233
|
+
const project = new core_1.PgpmPackage(ctx.cwd);
|
|
234
|
+
if (!project.workspacePath) {
|
|
235
|
+
process.stderr.write('Not inside a PGPM workspace.\n');
|
|
236
|
+
throw types_1.errors.NOT_IN_WORKSPACE({});
|
|
237
|
+
}
|
|
238
|
+
if (!project.isInsideAllowedDirs(ctx.cwd) && !project.isInWorkspace() && !project.isParentOfAllowedDirs(ctx.cwd)) {
|
|
239
|
+
process.stderr.write('You must be inside the workspace root or a parent directory of modules (like packages/).\n');
|
|
240
|
+
throw types_1.errors.NOT_IN_WORKSPACE_MODULE({});
|
|
241
|
+
}
|
|
242
|
+
const availExtensions = project.getAvailableModules();
|
|
243
|
+
const moduleQuestions = [
|
|
244
|
+
{
|
|
245
|
+
name: 'moduleName',
|
|
246
|
+
message: 'Enter the module name',
|
|
247
|
+
required: true,
|
|
248
|
+
type: 'text',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'extensions',
|
|
252
|
+
message: 'Which extensions?',
|
|
253
|
+
options: availExtensions,
|
|
254
|
+
type: 'checkbox',
|
|
255
|
+
allowCustomOptions: true,
|
|
256
|
+
required: true,
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
const answers = await prompter.prompt(argv, moduleQuestions);
|
|
260
|
+
const modName = (0, core_1.sluggify)(answers.moduleName);
|
|
261
|
+
const extensions = answers.extensions
|
|
262
|
+
.filter((opt) => opt.selected)
|
|
263
|
+
.map((opt) => opt.name);
|
|
264
|
+
const templateAnswers = {
|
|
265
|
+
...argv,
|
|
266
|
+
...answers,
|
|
267
|
+
moduleName: modName,
|
|
268
|
+
packageIdentifier: argv.packageIdentifier || modName
|
|
269
|
+
};
|
|
270
|
+
await project.initModule({
|
|
271
|
+
name: modName,
|
|
272
|
+
description: answers.description || modName,
|
|
273
|
+
author: answers.author || modName,
|
|
274
|
+
extensions,
|
|
275
|
+
templateRepo: ctx.templateRepo,
|
|
276
|
+
templatePath: ctx.fromPath,
|
|
277
|
+
branch: ctx.branch,
|
|
278
|
+
dir: ctx.dir,
|
|
279
|
+
toolName: core_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
280
|
+
answers: templateAnswers,
|
|
281
|
+
noTty: ctx.noTty
|
|
282
|
+
});
|
|
283
|
+
const isRoot = path_1.default.resolve(project.getWorkspacePath()) === path_1.default.resolve(ctx.cwd);
|
|
284
|
+
const modulePath = isRoot
|
|
285
|
+
? path_1.default.join(ctx.cwd, 'packages', modName)
|
|
286
|
+
: path_1.default.join(ctx.cwd, modName);
|
|
287
|
+
const motdPath = path_1.default.join(modulePath, '.motd');
|
|
288
|
+
let motd = DEFAULT_MOTD;
|
|
289
|
+
if (fs_1.default.existsSync(motdPath)) {
|
|
290
|
+
try {
|
|
291
|
+
motd = fs_1.default.readFileSync(motdPath, 'utf8');
|
|
292
|
+
fs_1.default.unlinkSync(motdPath);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
// Ignore errors reading/deleting .motd
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
process.stdout.write(motd);
|
|
299
|
+
if (!motd.endsWith('\n')) {
|
|
300
|
+
process.stdout.write('\n');
|
|
50
301
|
}
|
|
51
|
-
|
|
302
|
+
const relPath = isRoot ? `packages/${modName}` : modName;
|
|
303
|
+
process.stdout.write(`\n✨ Enjoy!\n\ncd ./${relPath}\n`);
|
|
304
|
+
return { ...argv, ...answers };
|
|
52
305
|
}
|
package/commands/init/module.js
CHANGED
|
@@ -51,6 +51,7 @@ async function runModuleSetup(argv, prompter) {
|
|
|
51
51
|
.map((opt) => opt.name);
|
|
52
52
|
const templateRepo = argv.repo ?? core_1.DEFAULT_TEMPLATE_REPO;
|
|
53
53
|
const templatePath = argv.templatePath;
|
|
54
|
+
const dir = argv.dir;
|
|
54
55
|
const templateAnswers = {
|
|
55
56
|
...argv,
|
|
56
57
|
...answers,
|
|
@@ -65,6 +66,7 @@ async function runModuleSetup(argv, prompter) {
|
|
|
65
66
|
templateRepo,
|
|
66
67
|
templatePath,
|
|
67
68
|
branch: argv.fromBranch,
|
|
69
|
+
dir,
|
|
68
70
|
toolName: core_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
69
71
|
answers: templateAnswers,
|
|
70
72
|
noTty: Boolean(argv.noTty || argv['no-tty'] || process.env.CI === 'true')
|
|
@@ -35,12 +35,13 @@ async function runWorkspaceSetup(argv, prompter) {
|
|
|
35
35
|
// This provides the intended workspace directory name before the folder is created
|
|
36
36
|
const dirName = path_1.default.basename(targetPath);
|
|
37
37
|
(0, inquirerer_1.registerDefaultResolver)('workspace.dirname', () => dirName);
|
|
38
|
+
const dir = argv.dir;
|
|
38
39
|
await (0, core_1.scaffoldTemplate)({
|
|
39
|
-
|
|
40
|
+
fromPath: templatePath ?? 'workspace',
|
|
40
41
|
outputDir: targetPath,
|
|
41
42
|
templateRepo,
|
|
42
43
|
branch: argv.fromBranch,
|
|
43
|
-
|
|
44
|
+
dir,
|
|
44
45
|
answers: {
|
|
45
46
|
...argv,
|
|
46
47
|
...answers,
|
|
@@ -48,7 +49,8 @@ async function runWorkspaceSetup(argv, prompter) {
|
|
|
48
49
|
},
|
|
49
50
|
toolName: core_1.DEFAULT_TEMPLATE_TOOL_NAME,
|
|
50
51
|
noTty: Boolean(argv.noTty || argv['no-tty'] || process.env.CI === 'true'),
|
|
51
|
-
cwd
|
|
52
|
+
cwd,
|
|
53
|
+
prompter
|
|
52
54
|
});
|
|
53
55
|
// Check for .motd file and print it, or use default ASCII art
|
|
54
56
|
const motdPath = path_1.default.join(targetPath, '.motd');
|
|
@@ -1,26 +1,39 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { DEFAULT_TEMPLATE_REPO, DEFAULT_TEMPLATE_TOOL_NAME, inspectTemplate, PgpmPackage, resolveBoilerplateBaseDir, scaffoldTemplate, scanBoilerplates, sluggify, } from '@pgpmjs/core';
|
|
4
|
+
import { errors } from '@pgpmjs/types';
|
|
5
|
+
import { registerDefaultResolver } from 'inquirerer';
|
|
6
|
+
const DEFAULT_MOTD = `
|
|
7
|
+
| _ _
|
|
8
|
+
=== |.===. '\\-//\`
|
|
9
|
+
(o o) {}o o{} (o o)
|
|
10
|
+
ooO--(_)--Ooo-ooO--(_)--Ooo-ooO--(_)--Ooo-
|
|
11
|
+
`;
|
|
3
12
|
export const createInitUsageText = (binaryName, productLabel) => {
|
|
4
13
|
const displayName = productLabel ?? binaryName;
|
|
5
14
|
return `
|
|
6
15
|
Init Command:
|
|
7
16
|
|
|
8
|
-
${binaryName} init [OPTIONS] [
|
|
17
|
+
${binaryName} init [OPTIONS] [<fromPath>]
|
|
9
18
|
|
|
10
|
-
Initialize ${displayName}
|
|
19
|
+
Initialize ${displayName} from a template. The template's type (workspace/module)
|
|
20
|
+
determines the behavior automatically.
|
|
11
21
|
|
|
12
22
|
Options:
|
|
13
23
|
--help, -h Show this help message
|
|
14
24
|
--cwd <directory> Working directory (default: current directory)
|
|
15
25
|
--repo <repo> Template repo (default: https://github.com/constructive-io/pgpm-boilerplates.git)
|
|
16
|
-
--template-path <path> Template sub-path (default: workspace/module) or local path override
|
|
17
26
|
--from-branch <branch> Branch/tag to use when cloning repo
|
|
27
|
+
--dir <variant> Template variant directory (e.g., supabase, drizzle)
|
|
28
|
+
--boilerplate Prompt to select from available boilerplates
|
|
18
29
|
|
|
19
30
|
Examples:
|
|
20
|
-
${binaryName} init Initialize new module
|
|
31
|
+
${binaryName} init Initialize new module (default)
|
|
21
32
|
${binaryName} init workspace Initialize new workspace
|
|
33
|
+
${binaryName} init module Initialize new module explicitly
|
|
34
|
+
${binaryName} init workspace --dir <variant> Use variant templates
|
|
35
|
+
${binaryName} init --boilerplate Select from available boilerplates
|
|
22
36
|
${binaryName} init --repo owner/repo Use templates from GitHub repository
|
|
23
|
-
${binaryName} init --template-path ./custom-templates Use templates from local path
|
|
24
37
|
${binaryName} init --repo owner/repo --from-branch develop Use specific branch
|
|
25
38
|
`;
|
|
26
39
|
};
|
|
@@ -30,16 +43,256 @@ export default async (argv, prompter, _options) => {
|
|
|
30
43
|
console.log(createInitUsageText('pgpm'));
|
|
31
44
|
process.exit(0);
|
|
32
45
|
}
|
|
33
|
-
return
|
|
46
|
+
return handleInit(argv, prompter);
|
|
34
47
|
};
|
|
35
|
-
async function
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
async function handleInit(argv, prompter) {
|
|
49
|
+
const { cwd = process.cwd() } = argv;
|
|
50
|
+
const templateRepo = argv.repo ?? DEFAULT_TEMPLATE_REPO;
|
|
51
|
+
const branch = argv.fromBranch;
|
|
52
|
+
const dir = argv.dir;
|
|
53
|
+
const noTty = Boolean(argv.noTty || argv['no-tty'] || process.env.CI === 'true');
|
|
54
|
+
const useBoilerplatePrompt = Boolean(argv.boilerplate);
|
|
55
|
+
// Get fromPath from first positional arg
|
|
56
|
+
const positionalFromPath = argv._?.[0];
|
|
57
|
+
// Handle --boilerplate flag: separate path from regular init
|
|
58
|
+
if (useBoilerplatePrompt) {
|
|
59
|
+
return handleBoilerplateInit(argv, prompter, {
|
|
60
|
+
positionalFromPath,
|
|
61
|
+
templateRepo,
|
|
62
|
+
branch,
|
|
63
|
+
dir,
|
|
64
|
+
noTty,
|
|
65
|
+
cwd,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Regular init path: default to 'module' if no fromPath provided
|
|
69
|
+
const fromPath = positionalFromPath || 'module';
|
|
70
|
+
// Inspect the template to get its type
|
|
71
|
+
const inspection = inspectTemplate({
|
|
72
|
+
fromPath,
|
|
73
|
+
templateRepo,
|
|
74
|
+
branch,
|
|
75
|
+
dir,
|
|
76
|
+
toolName: DEFAULT_TEMPLATE_TOOL_NAME,
|
|
77
|
+
cwd,
|
|
78
|
+
});
|
|
79
|
+
const templateType = inspection.config?.type;
|
|
80
|
+
// Branch based on template type
|
|
81
|
+
if (templateType === 'workspace') {
|
|
82
|
+
return handleWorkspaceInit(argv, prompter, {
|
|
83
|
+
fromPath,
|
|
84
|
+
templateRepo,
|
|
85
|
+
branch,
|
|
86
|
+
dir,
|
|
87
|
+
noTty,
|
|
88
|
+
cwd,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// Default to module init (for 'module' type or unknown types)
|
|
92
|
+
return handleModuleInit(argv, prompter, {
|
|
93
|
+
fromPath,
|
|
94
|
+
templateRepo,
|
|
95
|
+
branch,
|
|
96
|
+
dir,
|
|
97
|
+
noTty,
|
|
98
|
+
cwd,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async function handleBoilerplateInit(argv, prompter, ctx) {
|
|
102
|
+
let fromPath;
|
|
103
|
+
if (ctx.positionalFromPath) {
|
|
104
|
+
// If a positional fromPath was provided with --boilerplate, use it directly
|
|
105
|
+
fromPath = ctx.positionalFromPath;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// No positional arg: prompt user to select from available boilerplates
|
|
109
|
+
if (ctx.noTty) {
|
|
110
|
+
throw new Error('Cannot use --boilerplate without a <fromPath> argument in non-interactive mode. ' +
|
|
111
|
+
'Please specify a boilerplate explicitly, e.g., `pgpm init workspace --boilerplate`');
|
|
112
|
+
}
|
|
113
|
+
// Inspect without fromPath to get the template directory for scanning
|
|
114
|
+
const initialInspection = inspectTemplate({
|
|
115
|
+
templateRepo: ctx.templateRepo,
|
|
116
|
+
branch: ctx.branch,
|
|
117
|
+
dir: ctx.dir,
|
|
118
|
+
toolName: DEFAULT_TEMPLATE_TOOL_NAME,
|
|
119
|
+
cwd: ctx.cwd,
|
|
120
|
+
});
|
|
121
|
+
// Resolve the base directory for scanning boilerplates:
|
|
122
|
+
// - If --dir is specified, use the resolvedTemplatePath (bypasses .boilerplates.json)
|
|
123
|
+
// - Otherwise, use .boilerplates.json's dir (defaults to repo root if missing)
|
|
124
|
+
const baseDir = ctx.dir
|
|
125
|
+
? initialInspection.resolvedTemplatePath
|
|
126
|
+
: resolveBoilerplateBaseDir(initialInspection.templateDir);
|
|
127
|
+
const boilerplates = scanBoilerplates(baseDir);
|
|
128
|
+
if (boilerplates.length === 0) {
|
|
129
|
+
throw new Error(`No boilerplates found in the template repository.\n` +
|
|
130
|
+
`Checked directory: ${baseDir}\n` +
|
|
131
|
+
`Make sure the repository contains boilerplate directories with .boilerplate.json files.`);
|
|
132
|
+
}
|
|
133
|
+
const boilerplateQuestion = [
|
|
134
|
+
{
|
|
135
|
+
name: 'selectedBoilerplate',
|
|
136
|
+
message: 'Select a boilerplate',
|
|
137
|
+
type: 'autocomplete',
|
|
138
|
+
options: boilerplates.map((bp) => bp.name),
|
|
139
|
+
required: true,
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
const boilerplateAnswer = await prompter.prompt(argv, boilerplateQuestion);
|
|
143
|
+
fromPath = boilerplateAnswer.selectedBoilerplate;
|
|
144
|
+
}
|
|
145
|
+
// Inspect the selected template to get its type
|
|
146
|
+
const inspection = inspectTemplate({
|
|
147
|
+
fromPath,
|
|
148
|
+
templateRepo: ctx.templateRepo,
|
|
149
|
+
branch: ctx.branch,
|
|
150
|
+
dir: ctx.dir,
|
|
151
|
+
toolName: DEFAULT_TEMPLATE_TOOL_NAME,
|
|
152
|
+
cwd: ctx.cwd,
|
|
153
|
+
});
|
|
154
|
+
const templateType = inspection.config?.type;
|
|
155
|
+
// Branch based on template type
|
|
156
|
+
if (templateType === 'workspace') {
|
|
157
|
+
return handleWorkspaceInit(argv, prompter, {
|
|
158
|
+
fromPath,
|
|
159
|
+
templateRepo: ctx.templateRepo,
|
|
160
|
+
branch: ctx.branch,
|
|
161
|
+
dir: ctx.dir,
|
|
162
|
+
noTty: ctx.noTty,
|
|
163
|
+
cwd: ctx.cwd,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// Default to module init (for 'module' type or unknown types)
|
|
167
|
+
return handleModuleInit(argv, prompter, {
|
|
168
|
+
fromPath,
|
|
169
|
+
templateRepo: ctx.templateRepo,
|
|
170
|
+
branch: ctx.branch,
|
|
171
|
+
dir: ctx.dir,
|
|
172
|
+
noTty: ctx.noTty,
|
|
173
|
+
cwd: ctx.cwd,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async function handleWorkspaceInit(argv, prompter, ctx) {
|
|
177
|
+
const workspaceQuestions = [
|
|
178
|
+
{
|
|
179
|
+
name: 'name',
|
|
180
|
+
message: 'Enter workspace name',
|
|
181
|
+
required: true,
|
|
182
|
+
type: 'text'
|
|
183
|
+
}
|
|
184
|
+
];
|
|
185
|
+
const answers = await prompter.prompt(argv, workspaceQuestions);
|
|
186
|
+
const targetPath = path.join(ctx.cwd, sluggify(answers.name));
|
|
187
|
+
// Register workspace.dirname resolver so boilerplate templates can use it via defaultFrom/setFrom
|
|
188
|
+
const dirName = path.basename(targetPath);
|
|
189
|
+
registerDefaultResolver('workspace.dirname', () => dirName);
|
|
190
|
+
await scaffoldTemplate({
|
|
191
|
+
fromPath: ctx.fromPath,
|
|
192
|
+
outputDir: targetPath,
|
|
193
|
+
templateRepo: ctx.templateRepo,
|
|
194
|
+
branch: ctx.branch,
|
|
195
|
+
dir: ctx.dir,
|
|
196
|
+
answers: {
|
|
39
197
|
...argv,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
198
|
+
...answers,
|
|
199
|
+
workspaceName: answers.name
|
|
200
|
+
},
|
|
201
|
+
toolName: DEFAULT_TEMPLATE_TOOL_NAME,
|
|
202
|
+
noTty: ctx.noTty,
|
|
203
|
+
cwd: ctx.cwd,
|
|
204
|
+
prompter
|
|
205
|
+
});
|
|
206
|
+
// Check for .motd file and print it, or use default ASCII art
|
|
207
|
+
const motdPath = path.join(targetPath, '.motd');
|
|
208
|
+
let motd = DEFAULT_MOTD;
|
|
209
|
+
if (fs.existsSync(motdPath)) {
|
|
210
|
+
try {
|
|
211
|
+
motd = fs.readFileSync(motdPath, 'utf8');
|
|
212
|
+
fs.unlinkSync(motdPath);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Ignore errors reading/deleting .motd
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
process.stdout.write(motd);
|
|
219
|
+
if (!motd.endsWith('\n')) {
|
|
220
|
+
process.stdout.write('\n');
|
|
221
|
+
}
|
|
222
|
+
process.stdout.write(`\n✨ Enjoy!\n\ncd ./${dirName}\n`);
|
|
223
|
+
return { ...argv, ...answers, cwd: targetPath };
|
|
224
|
+
}
|
|
225
|
+
async function handleModuleInit(argv, prompter, ctx) {
|
|
226
|
+
const project = new PgpmPackage(ctx.cwd);
|
|
227
|
+
if (!project.workspacePath) {
|
|
228
|
+
process.stderr.write('Not inside a PGPM workspace.\n');
|
|
229
|
+
throw errors.NOT_IN_WORKSPACE({});
|
|
230
|
+
}
|
|
231
|
+
if (!project.isInsideAllowedDirs(ctx.cwd) && !project.isInWorkspace() && !project.isParentOfAllowedDirs(ctx.cwd)) {
|
|
232
|
+
process.stderr.write('You must be inside the workspace root or a parent directory of modules (like packages/).\n');
|
|
233
|
+
throw errors.NOT_IN_WORKSPACE_MODULE({});
|
|
234
|
+
}
|
|
235
|
+
const availExtensions = project.getAvailableModules();
|
|
236
|
+
const moduleQuestions = [
|
|
237
|
+
{
|
|
238
|
+
name: 'moduleName',
|
|
239
|
+
message: 'Enter the module name',
|
|
240
|
+
required: true,
|
|
241
|
+
type: 'text',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'extensions',
|
|
245
|
+
message: 'Which extensions?',
|
|
246
|
+
options: availExtensions,
|
|
247
|
+
type: 'checkbox',
|
|
248
|
+
allowCustomOptions: true,
|
|
249
|
+
required: true,
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
const answers = await prompter.prompt(argv, moduleQuestions);
|
|
253
|
+
const modName = sluggify(answers.moduleName);
|
|
254
|
+
const extensions = answers.extensions
|
|
255
|
+
.filter((opt) => opt.selected)
|
|
256
|
+
.map((opt) => opt.name);
|
|
257
|
+
const templateAnswers = {
|
|
258
|
+
...argv,
|
|
259
|
+
...answers,
|
|
260
|
+
moduleName: modName,
|
|
261
|
+
packageIdentifier: argv.packageIdentifier || modName
|
|
262
|
+
};
|
|
263
|
+
await project.initModule({
|
|
264
|
+
name: modName,
|
|
265
|
+
description: answers.description || modName,
|
|
266
|
+
author: answers.author || modName,
|
|
267
|
+
extensions,
|
|
268
|
+
templateRepo: ctx.templateRepo,
|
|
269
|
+
templatePath: ctx.fromPath,
|
|
270
|
+
branch: ctx.branch,
|
|
271
|
+
dir: ctx.dir,
|
|
272
|
+
toolName: DEFAULT_TEMPLATE_TOOL_NAME,
|
|
273
|
+
answers: templateAnswers,
|
|
274
|
+
noTty: ctx.noTty
|
|
275
|
+
});
|
|
276
|
+
const isRoot = path.resolve(project.getWorkspacePath()) === path.resolve(ctx.cwd);
|
|
277
|
+
const modulePath = isRoot
|
|
278
|
+
? path.join(ctx.cwd, 'packages', modName)
|
|
279
|
+
: path.join(ctx.cwd, modName);
|
|
280
|
+
const motdPath = path.join(modulePath, '.motd');
|
|
281
|
+
let motd = DEFAULT_MOTD;
|
|
282
|
+
if (fs.existsSync(motdPath)) {
|
|
283
|
+
try {
|
|
284
|
+
motd = fs.readFileSync(motdPath, 'utf8');
|
|
285
|
+
fs.unlinkSync(motdPath);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// Ignore errors reading/deleting .motd
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
process.stdout.write(motd);
|
|
292
|
+
if (!motd.endsWith('\n')) {
|
|
293
|
+
process.stdout.write('\n');
|
|
43
294
|
}
|
|
44
|
-
|
|
295
|
+
const relPath = isRoot ? `packages/${modName}` : modName;
|
|
296
|
+
process.stdout.write(`\n✨ Enjoy!\n\ncd ./${relPath}\n`);
|
|
297
|
+
return { ...argv, ...answers };
|
|
45
298
|
}
|
|
@@ -45,6 +45,7 @@ export default async function runModuleSetup(argv, prompter) {
|
|
|
45
45
|
.map((opt) => opt.name);
|
|
46
46
|
const templateRepo = argv.repo ?? DEFAULT_TEMPLATE_REPO;
|
|
47
47
|
const templatePath = argv.templatePath;
|
|
48
|
+
const dir = argv.dir;
|
|
48
49
|
const templateAnswers = {
|
|
49
50
|
...argv,
|
|
50
51
|
...answers,
|
|
@@ -59,6 +60,7 @@ export default async function runModuleSetup(argv, prompter) {
|
|
|
59
60
|
templateRepo,
|
|
60
61
|
templatePath,
|
|
61
62
|
branch: argv.fromBranch,
|
|
63
|
+
dir,
|
|
62
64
|
toolName: DEFAULT_TEMPLATE_TOOL_NAME,
|
|
63
65
|
answers: templateAnswers,
|
|
64
66
|
noTty: Boolean(argv.noTty || argv['no-tty'] || process.env.CI === 'true')
|
|
@@ -29,12 +29,13 @@ export default async function runWorkspaceSetup(argv, prompter) {
|
|
|
29
29
|
// This provides the intended workspace directory name before the folder is created
|
|
30
30
|
const dirName = path.basename(targetPath);
|
|
31
31
|
registerDefaultResolver('workspace.dirname', () => dirName);
|
|
32
|
+
const dir = argv.dir;
|
|
32
33
|
await scaffoldTemplate({
|
|
33
|
-
|
|
34
|
+
fromPath: templatePath ?? 'workspace',
|
|
34
35
|
outputDir: targetPath,
|
|
35
36
|
templateRepo,
|
|
36
37
|
branch: argv.fromBranch,
|
|
37
|
-
|
|
38
|
+
dir,
|
|
38
39
|
answers: {
|
|
39
40
|
...argv,
|
|
40
41
|
...answers,
|
|
@@ -42,7 +43,8 @@ export default async function runWorkspaceSetup(argv, prompter) {
|
|
|
42
43
|
},
|
|
43
44
|
toolName: DEFAULT_TEMPLATE_TOOL_NAME,
|
|
44
45
|
noTty: Boolean(argv.noTty || argv['no-tty'] || process.env.CI === 'true'),
|
|
45
|
-
cwd
|
|
46
|
+
cwd,
|
|
47
|
+
prompter
|
|
46
48
|
});
|
|
47
49
|
// Check for .motd file and print it, or use default ASCII art
|
|
48
50
|
const motdPath = path.join(targetPath, '.motd');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgpm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PostgreSQL Package Manager - Database migration and package management CLI",
|
|
6
6
|
"main": "index.js",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"ts-node": "^10.9.2"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@pgpmjs/core": "^
|
|
48
|
+
"@pgpmjs/core": "^4.0.1",
|
|
49
49
|
"@pgpmjs/env": "^2.8.8",
|
|
50
50
|
"@pgpmjs/logger": "^1.3.5",
|
|
51
51
|
"@pgpmjs/types": "^2.12.6",
|
|
52
52
|
"appstash": "^0.2.6",
|
|
53
|
-
"create-gen-app": "^0.
|
|
53
|
+
"create-gen-app": "^0.8.1",
|
|
54
54
|
"find-and-require-package-json": "^0.8.2",
|
|
55
|
-
"inquirerer": "^2.
|
|
55
|
+
"inquirerer": "^2.4.0",
|
|
56
56
|
"js-yaml": "^4.1.0",
|
|
57
57
|
"minimist": "^1.2.8",
|
|
58
58
|
"pg-cache": "^1.6.9",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"pg",
|
|
74
74
|
"pgsql"
|
|
75
75
|
],
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "b4dd6e24bd84d79f3057d8555b850481b7d4e19a"
|
|
77
77
|
}
|