create-gen-app 0.1.6 → 0.2.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/README.md +35 -31
- package/cache.d.ts +13 -0
- package/cache.js +76 -0
- package/clone.d.ts +6 -0
- package/clone.js +10 -6
- package/esm/cache.js +38 -0
- package/esm/clone.js +14 -11
- package/esm/extract.js +16 -2
- package/esm/index.js +37 -24
- package/esm/replace.js +21 -7
- package/esm/template-cache.js +223 -0
- package/extract.js +16 -2
- package/index.d.ts +8 -6
- package/index.js +27 -14
- package/package.json +4 -10
- package/replace.js +21 -7
- package/template-cache.d.ts +59 -0
- package/template-cache.js +260 -0
- package/types.d.ts +31 -0
- package/cli.d.ts +0 -6
- package/cli.js +0 -239
- package/esm/cli.js +0 -200
|
@@ -0,0 +1,260 @@
|
|
|
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.TemplateCache = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const crypto = __importStar(require("crypto"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const appstash_1 = require("appstash");
|
|
42
|
+
const clone_1 = require("./clone");
|
|
43
|
+
const DEFAULT_TOOL = "pgpm";
|
|
44
|
+
/**
|
|
45
|
+
* Manages template repository caching with TTL support
|
|
46
|
+
*/
|
|
47
|
+
class TemplateCache {
|
|
48
|
+
config;
|
|
49
|
+
reposDir;
|
|
50
|
+
metadataDir;
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.config = this.normalizeConfig(options);
|
|
53
|
+
if (this.config.enabled) {
|
|
54
|
+
const dirs = (0, appstash_1.appstash)(this.config.toolName, {
|
|
55
|
+
ensure: true,
|
|
56
|
+
baseDir: this.config.baseDir,
|
|
57
|
+
});
|
|
58
|
+
this.reposDir = (0, appstash_1.resolve)(dirs, "cache", "repos");
|
|
59
|
+
this.metadataDir = (0, appstash_1.resolve)(dirs, "cache", "metadata");
|
|
60
|
+
if (!fs.existsSync(this.reposDir)) {
|
|
61
|
+
fs.mkdirSync(this.reposDir, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
if (!fs.existsSync(this.metadataDir)) {
|
|
64
|
+
fs.mkdirSync(this.metadataDir, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.reposDir = "";
|
|
69
|
+
this.metadataDir = "";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
normalizeConfig(options) {
|
|
73
|
+
if (options === false) {
|
|
74
|
+
return {
|
|
75
|
+
enabled: false,
|
|
76
|
+
toolName: DEFAULT_TOOL,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const { enabled, toolName, baseDir, ttl, maxAge } = options ?? {};
|
|
80
|
+
return {
|
|
81
|
+
enabled: enabled !== false,
|
|
82
|
+
toolName: toolName ?? DEFAULT_TOOL,
|
|
83
|
+
baseDir,
|
|
84
|
+
ttl: ttl ?? maxAge,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get cached template if it exists and is not expired
|
|
89
|
+
*/
|
|
90
|
+
get(templateUrl, branch) {
|
|
91
|
+
if (!this.config.enabled) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const key = this.createCacheKey(templateUrl, branch);
|
|
95
|
+
const cachePath = path.join(this.reposDir, key);
|
|
96
|
+
const metadataPath = path.join(this.metadataDir, `${key}.json`);
|
|
97
|
+
if (!fs.existsSync(cachePath)) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
if (this.config.ttl && fs.existsSync(metadataPath)) {
|
|
101
|
+
try {
|
|
102
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
|
103
|
+
if (this.isExpired(metadata)) {
|
|
104
|
+
this.clear(templateUrl, branch);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// If metadata is corrupted, treat as expired
|
|
110
|
+
this.clear(templateUrl, branch);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return cachePath;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Cache a template repository
|
|
118
|
+
*/
|
|
119
|
+
set(templateUrl, branch) {
|
|
120
|
+
if (!this.config.enabled) {
|
|
121
|
+
throw new Error("Cache is disabled");
|
|
122
|
+
}
|
|
123
|
+
const key = this.createCacheKey(templateUrl, branch);
|
|
124
|
+
const cachePath = path.join(this.reposDir, key);
|
|
125
|
+
const metadataPath = path.join(this.metadataDir, `${key}.json`);
|
|
126
|
+
// Clone the repository
|
|
127
|
+
this.cloneInto(templateUrl, cachePath, branch);
|
|
128
|
+
// Write metadata
|
|
129
|
+
const metadata = {
|
|
130
|
+
templateUrl,
|
|
131
|
+
branch,
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
gitUrl: (0, clone_1.normalizeGitUrl)(templateUrl),
|
|
134
|
+
};
|
|
135
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
136
|
+
return cachePath;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Clear a specific cached template
|
|
140
|
+
*/
|
|
141
|
+
clear(templateUrl, branch) {
|
|
142
|
+
if (!this.config.enabled) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const key = this.createCacheKey(templateUrl, branch);
|
|
146
|
+
const cachePath = path.join(this.reposDir, key);
|
|
147
|
+
const metadataPath = path.join(this.metadataDir, `${key}.json`);
|
|
148
|
+
if (fs.existsSync(cachePath)) {
|
|
149
|
+
fs.rmSync(cachePath, { recursive: true, force: true });
|
|
150
|
+
}
|
|
151
|
+
if (fs.existsSync(metadataPath)) {
|
|
152
|
+
fs.rmSync(metadataPath, { force: true });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Clear all cached templates
|
|
157
|
+
*/
|
|
158
|
+
clearAll() {
|
|
159
|
+
if (!this.config.enabled) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (fs.existsSync(this.reposDir)) {
|
|
163
|
+
fs.rmSync(this.reposDir, { recursive: true, force: true });
|
|
164
|
+
fs.mkdirSync(this.reposDir, { recursive: true });
|
|
165
|
+
}
|
|
166
|
+
if (fs.existsSync(this.metadataDir)) {
|
|
167
|
+
fs.rmSync(this.metadataDir, { recursive: true, force: true });
|
|
168
|
+
fs.mkdirSync(this.metadataDir, { recursive: true });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check if cache metadata is expired
|
|
173
|
+
*/
|
|
174
|
+
isExpired(metadata) {
|
|
175
|
+
if (!this.config.ttl) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const age = Date.now() - metadata.timestamp;
|
|
179
|
+
return age > this.config.ttl;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get cache metadata for a template
|
|
183
|
+
*/
|
|
184
|
+
getMetadata(templateUrl, branch) {
|
|
185
|
+
if (!this.config.enabled) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const key = this.createCacheKey(templateUrl, branch);
|
|
189
|
+
const metadataPath = path.join(this.metadataDir, `${key}.json`);
|
|
190
|
+
if (!fs.existsSync(metadataPath)) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
return JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* List all cached templates with their metadata
|
|
202
|
+
*/
|
|
203
|
+
listAll() {
|
|
204
|
+
if (!this.config.enabled) {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
if (!fs.existsSync(this.metadataDir)) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
const results = [];
|
|
211
|
+
const files = fs.readdirSync(this.metadataDir);
|
|
212
|
+
for (const file of files) {
|
|
213
|
+
if (!file.endsWith(".json")) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const metadataPath = path.join(this.metadataDir, file);
|
|
217
|
+
try {
|
|
218
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
|
219
|
+
results.push({
|
|
220
|
+
...metadata,
|
|
221
|
+
key: path.basename(file, ".json"),
|
|
222
|
+
expired: this.isExpired(metadata),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Skip corrupted metadata
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return results;
|
|
230
|
+
}
|
|
231
|
+
createCacheKey(templateUrl, branch) {
|
|
232
|
+
const gitUrl = (0, clone_1.normalizeGitUrl)(templateUrl);
|
|
233
|
+
return crypto
|
|
234
|
+
.createHash("md5")
|
|
235
|
+
.update(`${gitUrl}#${branch ?? "default"}`)
|
|
236
|
+
.digest("hex");
|
|
237
|
+
}
|
|
238
|
+
cloneInto(templateUrl, destination, branch) {
|
|
239
|
+
if (fs.existsSync(destination)) {
|
|
240
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
241
|
+
}
|
|
242
|
+
const gitUrl = (0, clone_1.normalizeGitUrl)(templateUrl);
|
|
243
|
+
const branchArgs = branch ? ` --branch ${branch} --single-branch` : "";
|
|
244
|
+
const depthArgs = " --depth 1";
|
|
245
|
+
(0, child_process_1.execSync)(`git clone${branchArgs}${depthArgs} ${gitUrl} ${destination}`, {
|
|
246
|
+
stdio: "inherit",
|
|
247
|
+
});
|
|
248
|
+
const gitDir = path.join(destination, ".git");
|
|
249
|
+
if (fs.existsSync(gitDir)) {
|
|
250
|
+
fs.rmSync(gitDir, { recursive: true, force: true });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
isEnabled() {
|
|
254
|
+
return this.config.enabled;
|
|
255
|
+
}
|
|
256
|
+
getConfig() {
|
|
257
|
+
return { ...this.config };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.TemplateCache = TemplateCache;
|
package/types.d.ts
CHANGED
|
@@ -50,6 +50,37 @@ export interface CreateGenOptions {
|
|
|
50
50
|
* Whether to use TTY for interactive prompts
|
|
51
51
|
*/
|
|
52
52
|
noTty?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Optional caching configuration. Pass `false` to disable caching entirely.
|
|
55
|
+
*/
|
|
56
|
+
cache?: CacheOptions | false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Options that control template caching behavior
|
|
60
|
+
*/
|
|
61
|
+
export interface CacheOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Enable or disable caching. Defaults to true.
|
|
64
|
+
*/
|
|
65
|
+
enabled?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Tool name used for appstash (affects ~/.<tool> dirs). Defaults to `pgpm`.
|
|
68
|
+
*/
|
|
69
|
+
toolName?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Optional base directory for appstash. Useful for tests to avoid touching the real home dir.
|
|
72
|
+
*/
|
|
73
|
+
baseDir?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Time-to-live in milliseconds. If set, cached templates older than this will be invalidated.
|
|
76
|
+
* Defaults to undefined (no expiration).
|
|
77
|
+
*/
|
|
78
|
+
ttl?: number;
|
|
79
|
+
/**
|
|
80
|
+
* Alias for ttl. Maximum age in milliseconds for cached templates.
|
|
81
|
+
* Defaults to undefined (no expiration).
|
|
82
|
+
*/
|
|
83
|
+
maxAge?: number;
|
|
53
84
|
}
|
|
54
85
|
/**
|
|
55
86
|
* Result of extracting variables from a template
|
package/cli.d.ts
DELETED
package/cli.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
-
if (k2 === undefined) k2 = k;
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
-
};
|
|
39
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.runCli = runCli;
|
|
41
|
-
const fs = __importStar(require("fs"));
|
|
42
|
-
const path = __importStar(require("path"));
|
|
43
|
-
const inquirerer_1 = require("inquirerer");
|
|
44
|
-
const minimist_1 = __importDefault(require("minimist"));
|
|
45
|
-
const clone_1 = require("./clone");
|
|
46
|
-
const index_1 = require("./index");
|
|
47
|
-
const package_json_1 = __importDefault(require("../package.json"));
|
|
48
|
-
const DEFAULT_REPO = "https://github.com/launchql/pgpm-boilerplates.git";
|
|
49
|
-
const DEFAULT_PATH = ".";
|
|
50
|
-
const DEFAULT_OUTPUT_FALLBACK = "create-gen-app-output";
|
|
51
|
-
const PACKAGE_VERSION = package_json_1.default.version ?? "0.0.0";
|
|
52
|
-
const RESERVED_ARG_KEYS = new Set([
|
|
53
|
-
"_",
|
|
54
|
-
"repo",
|
|
55
|
-
"r",
|
|
56
|
-
"branch",
|
|
57
|
-
"b",
|
|
58
|
-
"path",
|
|
59
|
-
"p",
|
|
60
|
-
"template",
|
|
61
|
-
"t",
|
|
62
|
-
"output",
|
|
63
|
-
"o",
|
|
64
|
-
"force",
|
|
65
|
-
"f",
|
|
66
|
-
"help",
|
|
67
|
-
"h",
|
|
68
|
-
"version",
|
|
69
|
-
"v",
|
|
70
|
-
"no-tty",
|
|
71
|
-
"n",
|
|
72
|
-
]);
|
|
73
|
-
async function runCli(rawArgv = process.argv.slice(2)) {
|
|
74
|
-
const args = (0, minimist_1.default)(rawArgv, {
|
|
75
|
-
alias: {
|
|
76
|
-
r: "repo",
|
|
77
|
-
b: "branch",
|
|
78
|
-
p: "path",
|
|
79
|
-
t: "template",
|
|
80
|
-
o: "output",
|
|
81
|
-
f: "force",
|
|
82
|
-
h: "help",
|
|
83
|
-
v: "version",
|
|
84
|
-
n: "no-tty",
|
|
85
|
-
},
|
|
86
|
-
string: ["repo", "branch", "path", "template", "output"],
|
|
87
|
-
boolean: ["force", "help", "version", "no-tty"],
|
|
88
|
-
default: {
|
|
89
|
-
repo: DEFAULT_REPO,
|
|
90
|
-
path: DEFAULT_PATH,
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
if (args.help) {
|
|
94
|
-
printHelp();
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
if (args.version) {
|
|
98
|
-
printVersion();
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (!args.output && args._[0]) {
|
|
102
|
-
args.output = args._[0];
|
|
103
|
-
}
|
|
104
|
-
let tempDir = null;
|
|
105
|
-
try {
|
|
106
|
-
console.log(`Cloning template from ${args.repo}...`);
|
|
107
|
-
if (args.branch) {
|
|
108
|
-
console.log(`Using branch ${args.branch}`);
|
|
109
|
-
}
|
|
110
|
-
tempDir = await (0, clone_1.cloneRepo)(args.repo, { branch: args.branch });
|
|
111
|
-
const selectionRoot = path.join(tempDir, args.path);
|
|
112
|
-
if (!fs.existsSync(selectionRoot) || !fs.statSync(selectionRoot).isDirectory()) {
|
|
113
|
-
throw new Error(`Template path "${args.path}" does not exist in ${args.repo}`);
|
|
114
|
-
}
|
|
115
|
-
const templates = fs
|
|
116
|
-
.readdirSync(selectionRoot, { withFileTypes: true })
|
|
117
|
-
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
|
118
|
-
.map((entry) => entry.name)
|
|
119
|
-
.sort();
|
|
120
|
-
if (templates.length === 0) {
|
|
121
|
-
throw new Error("No template folders found in repository");
|
|
122
|
-
}
|
|
123
|
-
let selectedTemplate = args.template;
|
|
124
|
-
if (selectedTemplate) {
|
|
125
|
-
if (!templates.includes(selectedTemplate)) {
|
|
126
|
-
throw new Error(`Template "${selectedTemplate}" not found in ${args.repo}${args.path === "." ? "" : `/${args.path}`}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else if (templates.length === 1) {
|
|
130
|
-
selectedTemplate = templates[0];
|
|
131
|
-
console.log(`Using the only available template: ${selectedTemplate}`);
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
selectedTemplate = await promptForTemplate(templates);
|
|
135
|
-
}
|
|
136
|
-
if (!selectedTemplate) {
|
|
137
|
-
throw new Error("Template selection failed");
|
|
138
|
-
}
|
|
139
|
-
const normalizedBasePath = args.path === "." || args.path === "./"
|
|
140
|
-
? ""
|
|
141
|
-
: args.path.replace(/^[./]+/, "").replace(/\/+$/, "");
|
|
142
|
-
const fromPath = normalizedBasePath
|
|
143
|
-
? path.join(normalizedBasePath, selectedTemplate)
|
|
144
|
-
: selectedTemplate;
|
|
145
|
-
const outputDir = resolveOutputDir(args.output, selectedTemplate);
|
|
146
|
-
ensureOutputDir(outputDir, Boolean(args.force));
|
|
147
|
-
const answerOverrides = extractAnswerOverrides(args);
|
|
148
|
-
const noTty = Boolean(args["no-tty"] ?? args.noTty);
|
|
149
|
-
await (0, index_1.createGen)({
|
|
150
|
-
templateUrl: args.repo,
|
|
151
|
-
fromBranch: args.branch,
|
|
152
|
-
fromPath,
|
|
153
|
-
outputDir,
|
|
154
|
-
argv: answerOverrides,
|
|
155
|
-
noTty,
|
|
156
|
-
});
|
|
157
|
-
console.log(`\n✨ Done! Project ready at ${outputDir}`);
|
|
158
|
-
return { outputDir, template: selectedTemplate };
|
|
159
|
-
}
|
|
160
|
-
finally {
|
|
161
|
-
if (tempDir && fs.existsSync(tempDir)) {
|
|
162
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
function printHelp() {
|
|
167
|
-
console.log(`
|
|
168
|
-
create-gen-app CLI
|
|
169
|
-
|
|
170
|
-
Usage:
|
|
171
|
-
create-gen-app [options] [outputDir]
|
|
172
|
-
cga [options] [outputDir]
|
|
173
|
-
|
|
174
|
-
Options:
|
|
175
|
-
-r, --repo <url> Git repository to clone (default: ${DEFAULT_REPO})
|
|
176
|
-
-b, --branch <name> Branch to use when cloning
|
|
177
|
-
-p, --path <dir> Subdirectory that contains templates (default: .)
|
|
178
|
-
-t, --template <name> Template folder to use (will prompt if omitted)
|
|
179
|
-
-o, --output <dir> Output directory (defaults to ./<template>)
|
|
180
|
-
-f, --force Overwrite the output directory if it exists
|
|
181
|
-
-v, --version Show CLI version
|
|
182
|
-
-n, --no-tty Disable TTY mode for prompts
|
|
183
|
-
-h, --help Show this help message
|
|
184
|
-
|
|
185
|
-
You can also pass variable overrides, e.g.:
|
|
186
|
-
create-gen-app --template module --PROJECT_NAME my-app
|
|
187
|
-
`);
|
|
188
|
-
}
|
|
189
|
-
function printVersion() {
|
|
190
|
-
console.log(`create-gen-app v${PACKAGE_VERSION}`);
|
|
191
|
-
}
|
|
192
|
-
async function promptForTemplate(templates) {
|
|
193
|
-
const prompter = new inquirerer_1.Inquirerer();
|
|
194
|
-
const question = {
|
|
195
|
-
type: "list",
|
|
196
|
-
name: "template",
|
|
197
|
-
message: "Which template would you like to use?",
|
|
198
|
-
options: templates,
|
|
199
|
-
required: true,
|
|
200
|
-
};
|
|
201
|
-
try {
|
|
202
|
-
const answers = (await prompter.prompt({}, [question]));
|
|
203
|
-
return answers.template;
|
|
204
|
-
}
|
|
205
|
-
finally {
|
|
206
|
-
if (typeof prompter.close === "function") {
|
|
207
|
-
prompter.close();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
function resolveOutputDir(outputArg, template) {
|
|
212
|
-
const base = outputArg ?? (template ? path.join(process.cwd(), template) : DEFAULT_OUTPUT_FALLBACK);
|
|
213
|
-
return path.resolve(base);
|
|
214
|
-
}
|
|
215
|
-
function ensureOutputDir(outputDir, force) {
|
|
216
|
-
if (!fs.existsSync(outputDir)) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (!force) {
|
|
220
|
-
throw new Error(`Output directory "${outputDir}" already exists. Use --force to overwrite or choose another path.`);
|
|
221
|
-
}
|
|
222
|
-
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
223
|
-
}
|
|
224
|
-
function extractAnswerOverrides(args) {
|
|
225
|
-
const overrides = {};
|
|
226
|
-
for (const [key, value] of Object.entries(args)) {
|
|
227
|
-
if (RESERVED_ARG_KEYS.has(key)) {
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
overrides[key] = value;
|
|
231
|
-
}
|
|
232
|
-
return overrides;
|
|
233
|
-
}
|
|
234
|
-
if (require.main === module) {
|
|
235
|
-
runCli().catch((error) => {
|
|
236
|
-
console.error(error instanceof Error ? error.message : error);
|
|
237
|
-
process.exitCode = 1;
|
|
238
|
-
});
|
|
239
|
-
}
|