create-projx 1.6.1 → 1.6.3
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 +17 -3
- package/dist/{baseline-5XAJJ457.js → baseline-RXPDDEDD.js} +2 -6
- package/dist/{chunk-FTHX7ILT.js → chunk-LYPPFXGK.js} +140 -34
- package/dist/{chunk-TNI4XBVS.js → chunk-OBYYB6PR.js} +293 -150
- package/dist/index.js +708 -172
- package/dist/{utils-OOY5OZDX.js → utils-BXHJP6HF.js} +3 -1
- package/package.json +8 -9
- package/src/templates/README.md.ejs +1 -1
- package/src/templates/ci.yml.ejs +83 -66
- package/src/templates/pre-commit.ejs +53 -52
- package/src/templates/setup.sh.ejs +16 -16
|
@@ -6,17 +6,25 @@ import {
|
|
|
6
6
|
readComponentMarker,
|
|
7
7
|
readProjxConfig,
|
|
8
8
|
render,
|
|
9
|
+
renderEjsInDir,
|
|
9
10
|
replaceInDir,
|
|
10
11
|
replaceInFile,
|
|
11
12
|
sharedTemplateDir,
|
|
12
13
|
toSnake,
|
|
13
14
|
upsertComponentMarker,
|
|
14
15
|
writeProjxConfig
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-LYPPFXGK.js";
|
|
16
17
|
|
|
17
18
|
// src/baseline.ts
|
|
18
19
|
import { existsSync, writeFileSync, unlinkSync } from "fs";
|
|
19
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
chmod,
|
|
22
|
+
mkdir,
|
|
23
|
+
writeFile,
|
|
24
|
+
rm,
|
|
25
|
+
readFile as readFile2,
|
|
26
|
+
copyFile
|
|
27
|
+
} from "fs/promises";
|
|
20
28
|
import { execSync } from "child_process";
|
|
21
29
|
import { join as join2, dirname } from "path";
|
|
22
30
|
import { tmpdir } from "os";
|
|
@@ -24,30 +32,57 @@ import { tmpdir } from "os";
|
|
|
24
32
|
// src/generators/index.ts
|
|
25
33
|
import { readFile } from "fs/promises";
|
|
26
34
|
import { join } from "path";
|
|
35
|
+
function shellSafeUpper(s) {
|
|
36
|
+
return s.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
37
|
+
}
|
|
38
|
+
var CANONICAL_DISPLAY = {
|
|
39
|
+
fastapi: "FastAPI",
|
|
40
|
+
fastify: "Fastify",
|
|
41
|
+
frontend: "Frontend",
|
|
42
|
+
mobile: "Flutter",
|
|
43
|
+
e2e: "E2E",
|
|
44
|
+
infra: "Terraform"
|
|
45
|
+
};
|
|
46
|
+
function withInstances(vars) {
|
|
47
|
+
const base = vars.instances && vars.instances.length > 0 ? vars.instances : vars.components.map((type) => ({ type, path: vars.paths[type] ?? type }));
|
|
48
|
+
const enriched = base.map((inst) => ({
|
|
49
|
+
...inst,
|
|
50
|
+
upper: shellSafeUpper(inst.path),
|
|
51
|
+
display: inst.path === inst.type ? CANONICAL_DISPLAY[inst.type] : inst.path
|
|
52
|
+
}));
|
|
53
|
+
const byType = (type) => enriched.filter((i) => i.type === type).sort((a, b) => a.path.localeCompare(b.path));
|
|
54
|
+
return {
|
|
55
|
+
...vars,
|
|
56
|
+
instances: enriched,
|
|
57
|
+
fastapiInstances: byType("fastapi"),
|
|
58
|
+
fastifyInstances: byType("fastify"),
|
|
59
|
+
frontendInstances: byType("frontend"),
|
|
60
|
+
mobileInstances: byType("mobile"),
|
|
61
|
+
e2eInstances: byType("e2e"),
|
|
62
|
+
infraInstances: byType("infra")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
27
65
|
async function renderShared(filename, vars) {
|
|
28
|
-
const tpl = await readFile(
|
|
29
|
-
join(sharedTemplateDir(), filename),
|
|
30
|
-
"utf-8"
|
|
31
|
-
);
|
|
66
|
+
const tpl = await readFile(join(sharedTemplateDir(), filename), "utf-8");
|
|
32
67
|
return render(tpl, vars);
|
|
33
68
|
}
|
|
34
69
|
async function generateDockerCompose(vars) {
|
|
35
|
-
return renderShared("docker-compose.yml.ejs", vars);
|
|
70
|
+
return renderShared("docker-compose.yml.ejs", withInstances(vars));
|
|
36
71
|
}
|
|
37
72
|
async function generateDockerComposeDev(vars) {
|
|
38
|
-
return renderShared("docker-compose.dev.yml.ejs", vars);
|
|
73
|
+
return renderShared("docker-compose.dev.yml.ejs", withInstances(vars));
|
|
39
74
|
}
|
|
40
75
|
async function generatePreCommit(vars) {
|
|
41
|
-
return renderShared("pre-commit.ejs", vars);
|
|
76
|
+
return renderShared("pre-commit.ejs", withInstances(vars));
|
|
42
77
|
}
|
|
43
78
|
async function generateSetupSh(vars) {
|
|
44
|
-
return renderShared("setup.sh.ejs", vars);
|
|
79
|
+
return renderShared("setup.sh.ejs", withInstances(vars));
|
|
45
80
|
}
|
|
46
81
|
async function generateCiYml(vars) {
|
|
47
|
-
return renderShared("ci.yml.ejs", vars);
|
|
82
|
+
return renderShared("ci.yml.ejs", withInstances(vars));
|
|
48
83
|
}
|
|
49
84
|
async function generateReadme(vars) {
|
|
50
|
-
return renderShared("README.md.ejs", vars);
|
|
85
|
+
return renderShared("README.md.ejs", withInstances(vars));
|
|
51
86
|
}
|
|
52
87
|
function generateVscodeSettings(vars) {
|
|
53
88
|
const settings = {};
|
|
@@ -57,9 +92,15 @@ function generateVscodeSettings(vars) {
|
|
|
57
92
|
"editor.codeActionsOnSave": { "source.fixAll.ruff": "explicit" }
|
|
58
93
|
};
|
|
59
94
|
}
|
|
60
|
-
settings["[typescript]"] = {
|
|
61
|
-
|
|
62
|
-
|
|
95
|
+
settings["[typescript]"] = {
|
|
96
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
97
|
+
};
|
|
98
|
+
settings["[typescriptreact]"] = {
|
|
99
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
100
|
+
};
|
|
101
|
+
settings["[javascript]"] = {
|
|
102
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
103
|
+
};
|
|
63
104
|
settings["[json]"] = { "editor.defaultFormatter": "esbenp.prettier-vscode" };
|
|
64
105
|
settings["[css]"] = { "editor.defaultFormatter": "esbenp.prettier-vscode" };
|
|
65
106
|
settings["[yaml]"] = { "editor.defaultFormatter": "esbenp.prettier-vscode" };
|
|
@@ -81,32 +122,9 @@ function generateVscodeSettings(vars) {
|
|
|
81
122
|
}
|
|
82
123
|
|
|
83
124
|
// src/baseline.ts
|
|
84
|
-
function buildPathsUpper(paths) {
|
|
85
|
-
const result = {};
|
|
86
|
-
for (const [component, dir] of Object.entries(paths)) {
|
|
87
|
-
result[component] = dir.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
88
|
-
}
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
var CANONICAL_DISPLAY_NAMES = {
|
|
92
|
-
fastapi: "FastAPI",
|
|
93
|
-
fastify: "Fastify",
|
|
94
|
-
frontend: "Frontend",
|
|
95
|
-
mobile: "Flutter",
|
|
96
|
-
e2e: "E2E",
|
|
97
|
-
infra: "Terraform"
|
|
98
|
-
};
|
|
99
|
-
function buildDisplayNames(paths) {
|
|
100
|
-
const result = {};
|
|
101
|
-
for (const [component, dir] of Object.entries(paths)) {
|
|
102
|
-
const canonical = component;
|
|
103
|
-
result[canonical] = dir === canonical ? CANONICAL_DISPLAY_NAMES[canonical] : dir;
|
|
104
|
-
}
|
|
105
|
-
return result;
|
|
106
|
-
}
|
|
107
125
|
var BASELINE_REF = "refs/projx/baseline";
|
|
108
126
|
async function migrateComponentMarkers(cwd, components, componentPaths, applyDefaults) {
|
|
109
|
-
const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-
|
|
127
|
+
const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-BXHJP6HF.js");
|
|
110
128
|
for (const component of components) {
|
|
111
129
|
const dir = componentPaths[component];
|
|
112
130
|
const markerDir = join2(cwd, dir);
|
|
@@ -174,11 +192,17 @@ function saveBaselineRef(cwd) {
|
|
|
174
192
|
}
|
|
175
193
|
function getBaselineRef(cwd) {
|
|
176
194
|
try {
|
|
177
|
-
return execSync(`git rev-parse --verify ${BASELINE_REF}`, {
|
|
195
|
+
return execSync(`git rev-parse --verify ${BASELINE_REF}`, {
|
|
196
|
+
cwd,
|
|
197
|
+
stdio: "pipe"
|
|
198
|
+
}).toString().trim();
|
|
178
199
|
} catch {
|
|
179
200
|
}
|
|
180
201
|
try {
|
|
181
|
-
const sha = execSync("git log -1 --format=%H -- .projx", {
|
|
202
|
+
const sha = execSync("git log -1 --format=%H -- .projx", {
|
|
203
|
+
cwd,
|
|
204
|
+
stdio: "pipe"
|
|
205
|
+
}).toString().trim();
|
|
182
206
|
if (sha) return sha;
|
|
183
207
|
} catch {
|
|
184
208
|
}
|
|
@@ -186,7 +210,10 @@ function getBaselineRef(cwd) {
|
|
|
186
210
|
}
|
|
187
211
|
function getFileAtRef(cwd, ref, filePath) {
|
|
188
212
|
try {
|
|
189
|
-
return execSync(`git show ${ref}:"${filePath}"`, {
|
|
213
|
+
return execSync(`git show ${ref}:"${filePath}"`, {
|
|
214
|
+
cwd,
|
|
215
|
+
stdio: "pipe"
|
|
216
|
+
}).toString();
|
|
190
217
|
} catch {
|
|
191
218
|
return null;
|
|
192
219
|
}
|
|
@@ -257,15 +284,22 @@ async function tryThreeWayMerge(cwd, templateDir, baselineRef, componentPaths) {
|
|
|
257
284
|
const pathFallbacks = buildPathFallbacks(componentPaths);
|
|
258
285
|
for (const file of templateFiles) {
|
|
259
286
|
if (file === ".projx") continue;
|
|
260
|
-
|
|
287
|
+
const isMarker = file.endsWith("/.projx-component") || file === ".projx-component";
|
|
261
288
|
const oursPath = join2(cwd, file);
|
|
289
|
+
if (isMarker && existsSync(oursPath)) continue;
|
|
262
290
|
if (!existsSync(oursPath)) {
|
|
263
291
|
await mkdir(dirname(oursPath), { recursive: true });
|
|
264
292
|
await copyFile(join2(templateDir, file), oursPath);
|
|
265
293
|
merged.push(file);
|
|
266
294
|
continue;
|
|
267
295
|
}
|
|
268
|
-
|
|
296
|
+
if (isMarker) continue;
|
|
297
|
+
const baseContent = lookupBaseContent(
|
|
298
|
+
cwd,
|
|
299
|
+
baselineRef,
|
|
300
|
+
file,
|
|
301
|
+
pathFallbacks
|
|
302
|
+
);
|
|
269
303
|
if (baseContent === null) continue;
|
|
270
304
|
let theirsContent;
|
|
271
305
|
try {
|
|
@@ -306,7 +340,10 @@ function createOrphanWorktree(cwd) {
|
|
|
306
340
|
}
|
|
307
341
|
function cleanupWorktree(cwd, worktree, branch) {
|
|
308
342
|
try {
|
|
309
|
-
execSync(`git worktree remove "${worktree}" --force`, {
|
|
343
|
+
execSync(`git worktree remove "${worktree}" --force`, {
|
|
344
|
+
cwd,
|
|
345
|
+
stdio: "pipe"
|
|
346
|
+
});
|
|
310
347
|
} catch {
|
|
311
348
|
try {
|
|
312
349
|
rm(worktree, { recursive: true, force: true });
|
|
@@ -329,8 +366,11 @@ async function removeSkippedFiles(dir, skipPatterns, realDir) {
|
|
|
329
366
|
const rel = full.slice(base.length + 1);
|
|
330
367
|
if (entry.isDirectory()) {
|
|
331
368
|
await walk(full, base);
|
|
332
|
-
} else if (entry.name !== ".projx-component"
|
|
333
|
-
|
|
369
|
+
} else if (entry.name !== ".projx-component") {
|
|
370
|
+
const targetRel = rel.endsWith(".ejs") ? rel.slice(0, -".ejs".length) : rel;
|
|
371
|
+
if (!matchesSkip(targetRel, skipPatterns) && !matchesSkip(rel, skipPatterns))
|
|
372
|
+
continue;
|
|
373
|
+
if (realDir && !existsSync(join2(realDir, targetRel))) continue;
|
|
334
374
|
await unlink(full);
|
|
335
375
|
}
|
|
336
376
|
}
|
|
@@ -338,40 +378,35 @@ async function removeSkippedFiles(dir, skipPatterns, realDir) {
|
|
|
338
378
|
await walk(dir, dir);
|
|
339
379
|
}
|
|
340
380
|
async function writeTemplateToDir(dest, repoDir, components, componentPaths, vars, version, options = {}) {
|
|
341
|
-
const {
|
|
381
|
+
const {
|
|
382
|
+
componentSkips,
|
|
383
|
+
rootSkip,
|
|
384
|
+
applyDefaults = false,
|
|
385
|
+
realCwd = dest,
|
|
386
|
+
extraInstances = [],
|
|
387
|
+
instancesToScaffold
|
|
388
|
+
} = options;
|
|
342
389
|
const name = vars.projectName;
|
|
343
390
|
const nameSnake = toSnake(name);
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (existsSync(srcDir)) {
|
|
363
|
-
await cp(srcDir, outDir, { recursive: true, force: true });
|
|
364
|
-
}
|
|
365
|
-
await rm(tmpDir, { recursive: true, force: true });
|
|
366
|
-
await upsertComponentMarker(join2(dest, targetDir), component, skipPatterns.length > 0 ? skipPatterns : void 0);
|
|
367
|
-
}
|
|
368
|
-
if (!vars.pathsUpper) {
|
|
369
|
-
vars.pathsUpper = buildPathsUpper(componentPaths);
|
|
370
|
-
}
|
|
371
|
-
if (!vars.displayNames) {
|
|
372
|
-
vars.displayNames = buildDisplayNames(componentPaths);
|
|
391
|
+
const primaryInstances = components.map((type) => ({
|
|
392
|
+
type,
|
|
393
|
+
path: componentPaths[type]
|
|
394
|
+
}));
|
|
395
|
+
const allInstances = [...primaryInstances, ...extraInstances];
|
|
396
|
+
const toScaffold = instancesToScaffold ?? allInstances;
|
|
397
|
+
for (const inst of toScaffold) {
|
|
398
|
+
await writeOneInstance(inst, {
|
|
399
|
+
dest,
|
|
400
|
+
repoDir,
|
|
401
|
+
vars,
|
|
402
|
+
componentPaths,
|
|
403
|
+
realCwd,
|
|
404
|
+
applyDefaults,
|
|
405
|
+
baseSkip: componentSkips?.[inst.type] ?? [],
|
|
406
|
+
projectName: name,
|
|
407
|
+
nameSnake
|
|
408
|
+
});
|
|
373
409
|
}
|
|
374
|
-
await substituteNames(dest, components, componentPaths, name, nameSnake, vars.nameOverrides);
|
|
375
410
|
const hasBackend = components.includes("fastapi") || components.includes("fastify");
|
|
376
411
|
const userSkip = rootSkip ?? [];
|
|
377
412
|
const defaultRootSkip = applyDefaults ? DEFAULT_ROOT_SKIP_PATTERNS : [];
|
|
@@ -382,20 +417,32 @@ async function writeTemplateToDir(dest, repoDir, components, componentPaths, var
|
|
|
382
417
|
};
|
|
383
418
|
if (hasBackend || components.includes("frontend")) {
|
|
384
419
|
if (shouldWrite("docker-compose.yml"))
|
|
385
|
-
await writeFile(
|
|
420
|
+
await writeFile(
|
|
421
|
+
join2(dest, "docker-compose.yml"),
|
|
422
|
+
await generateDockerCompose(vars)
|
|
423
|
+
);
|
|
386
424
|
if (shouldWrite("docker-compose.dev.yml"))
|
|
387
|
-
await writeFile(
|
|
425
|
+
await writeFile(
|
|
426
|
+
join2(dest, "docker-compose.dev.yml"),
|
|
427
|
+
await generateDockerComposeDev(vars)
|
|
428
|
+
);
|
|
388
429
|
}
|
|
389
430
|
if (shouldWrite("README.md"))
|
|
390
431
|
await writeFile(join2(dest, "README.md"), await generateReadme(vars));
|
|
391
432
|
if (shouldWrite(".githooks/pre-commit")) {
|
|
392
433
|
await mkdir(join2(dest, ".githooks"), { recursive: true });
|
|
393
|
-
await writeFile(
|
|
434
|
+
await writeFile(
|
|
435
|
+
join2(dest, ".githooks/pre-commit"),
|
|
436
|
+
await generatePreCommit(vars)
|
|
437
|
+
);
|
|
394
438
|
await chmod(join2(dest, ".githooks/pre-commit"), 493);
|
|
395
439
|
}
|
|
396
440
|
if (shouldWrite(".github/workflows/ci.yml")) {
|
|
397
441
|
await mkdir(join2(dest, ".github/workflows"), { recursive: true });
|
|
398
|
-
await writeFile(
|
|
442
|
+
await writeFile(
|
|
443
|
+
join2(dest, ".github/workflows/ci.yml"),
|
|
444
|
+
await generateCiYml(vars)
|
|
445
|
+
);
|
|
399
446
|
}
|
|
400
447
|
if (shouldWrite("setup.sh")) {
|
|
401
448
|
await writeFile(join2(dest, "setup.sh"), await generateSetupSh(vars));
|
|
@@ -404,31 +451,68 @@ async function writeTemplateToDir(dest, repoDir, components, componentPaths, var
|
|
|
404
451
|
await copyStaticFiles(repoDir, dest);
|
|
405
452
|
if (shouldWrite(".vscode/settings.json")) {
|
|
406
453
|
await mkdir(join2(dest, ".vscode"), { recursive: true });
|
|
407
|
-
await writeFile(
|
|
454
|
+
await writeFile(
|
|
455
|
+
join2(dest, ".vscode/settings.json"),
|
|
456
|
+
generateVscodeSettings(vars)
|
|
457
|
+
);
|
|
408
458
|
}
|
|
409
459
|
await writeManagedProjx(dest, version, vars, applyDefaults);
|
|
410
460
|
}
|
|
411
|
-
async function
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
await
|
|
431
|
-
|
|
461
|
+
async function writeOneInstance(inst, opts) {
|
|
462
|
+
const { dest, repoDir, vars, componentPaths, realCwd, applyDefaults, baseSkip, projectName, nameSnake } = opts;
|
|
463
|
+
const { type, path: targetDir } = inst;
|
|
464
|
+
const realMarker = await readComponentMarker(join2(realCwd, targetDir));
|
|
465
|
+
const isNewMarker = !realMarker;
|
|
466
|
+
const shouldApplyComponentDefault = isNewMarker || applyDefaults;
|
|
467
|
+
const markerSkip = realMarker?.skip ?? [];
|
|
468
|
+
const defaultSkip = shouldApplyComponentDefault ? DEFAULT_COMPONENT_SKIP_PATTERNS[type] ?? [] : [];
|
|
469
|
+
const skipPatterns = [.../* @__PURE__ */ new Set([...baseSkip, ...markerSkip, ...defaultSkip])];
|
|
470
|
+
const tmpDir = join2(dest, "__cptmp__");
|
|
471
|
+
await copyComponent(repoDir, type, tmpDir);
|
|
472
|
+
const srcDir = join2(tmpDir, type);
|
|
473
|
+
if (skipPatterns.length > 0) {
|
|
474
|
+
await removeSkippedFiles(srcDir, skipPatterns, join2(realCwd, targetDir));
|
|
475
|
+
}
|
|
476
|
+
const outDir = join2(dest, targetDir);
|
|
477
|
+
await mkdir(outDir, { recursive: true });
|
|
478
|
+
const { cp } = await import("fs/promises");
|
|
479
|
+
if (existsSync(srcDir)) {
|
|
480
|
+
await cp(srcDir, outDir, { recursive: true, force: true });
|
|
481
|
+
}
|
|
482
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
483
|
+
const instancePaths = { ...componentPaths, [type]: targetDir };
|
|
484
|
+
await renderEjsInDir(outDir, { ...vars, paths: instancePaths });
|
|
485
|
+
await upsertComponentMarker(
|
|
486
|
+
join2(dest, targetDir),
|
|
487
|
+
type,
|
|
488
|
+
skipPatterns.length > 0 ? skipPatterns : void 0
|
|
489
|
+
);
|
|
490
|
+
await substituteNamesForInstance(inst, dest, projectName, nameSnake, vars.nameOverrides);
|
|
491
|
+
}
|
|
492
|
+
async function substituteNamesForInstance(inst, dest, name, nameSnake, overrides) {
|
|
493
|
+
const { type, path } = inst;
|
|
494
|
+
const isCanonical = path === type;
|
|
495
|
+
if (type === "fastapi") {
|
|
496
|
+
const target = isCanonical ? overrides?.fastapi ?? `${name}-fastapi` : `${name}-${path}`;
|
|
497
|
+
await replaceInFile(join2(dest, `${path}/pyproject.toml`), "projx-fastapi", target);
|
|
498
|
+
} else if (type === "fastify") {
|
|
499
|
+
const target = isCanonical ? overrides?.fastify ?? `${name}-fastify` : `${name}-${path}`;
|
|
500
|
+
await replaceInFile(join2(dest, `${path}/package.json`), "projx-fastify", target);
|
|
501
|
+
} else if (type === "frontend") {
|
|
502
|
+
const target = isCanonical ? overrides?.frontend ?? `${name}-frontend` : `${name}-${path}`;
|
|
503
|
+
await replaceInFile(join2(dest, `${path}/package.json`), "projx-frontend", target);
|
|
504
|
+
} else if (type === "e2e") {
|
|
505
|
+
const target = isCanonical ? overrides?.e2e ?? `${name}-e2e` : `${name}-${path}`;
|
|
506
|
+
await replaceInFile(join2(dest, `${path}/package.json`), "projx-e2e", target);
|
|
507
|
+
} else if (type === "mobile") {
|
|
508
|
+
const target = isCanonical ? overrides?.mobile ?? `${nameSnake}_mobile` : toSnake(`${nameSnake}_${path}`);
|
|
509
|
+
await replaceInFile(join2(dest, `${path}/pubspec.yaml`), "projx_mobile", target);
|
|
510
|
+
await replaceInDir(
|
|
511
|
+
join2(dest, path),
|
|
512
|
+
"package:projx_mobile/",
|
|
513
|
+
`package:${target}/`,
|
|
514
|
+
".dart"
|
|
515
|
+
);
|
|
432
516
|
}
|
|
433
517
|
}
|
|
434
518
|
async function detectPackageNameOverrides(cwd, components, componentPaths) {
|
|
@@ -480,7 +564,7 @@ async function readPubspecName(file) {
|
|
|
480
564
|
return null;
|
|
481
565
|
}
|
|
482
566
|
}
|
|
483
|
-
async function applyTemplate(cwd, repoDir, components, componentPaths, vars, version, componentSkips, rootSkip, applyDefaults = false) {
|
|
567
|
+
async function applyTemplate(cwd, repoDir, components, componentPaths, vars, version, componentSkips, rootSkip, applyDefaults = false, extraInstances = [], instancesToScaffold) {
|
|
484
568
|
const hasHead = (() => {
|
|
485
569
|
try {
|
|
486
570
|
execSync("git rev-parse HEAD", { cwd, stdio: "pipe" });
|
|
@@ -490,24 +574,47 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
490
574
|
}
|
|
491
575
|
})();
|
|
492
576
|
if (!hasHead) {
|
|
493
|
-
await writeTemplateToDir(
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
577
|
+
await writeTemplateToDir(
|
|
578
|
+
cwd,
|
|
579
|
+
repoDir,
|
|
580
|
+
components,
|
|
581
|
+
componentPaths,
|
|
582
|
+
vars,
|
|
583
|
+
version,
|
|
584
|
+
{
|
|
585
|
+
componentSkips,
|
|
586
|
+
rootSkip,
|
|
587
|
+
applyDefaults,
|
|
588
|
+
realCwd: cwd,
|
|
589
|
+
extraInstances,
|
|
590
|
+
instancesToScaffold
|
|
591
|
+
}
|
|
592
|
+
);
|
|
499
593
|
return { status: "clean" };
|
|
500
594
|
}
|
|
501
595
|
const { worktree, branch } = createOrphanWorktree(cwd);
|
|
502
596
|
try {
|
|
503
|
-
await writeTemplateToDir(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
597
|
+
await writeTemplateToDir(
|
|
598
|
+
worktree,
|
|
599
|
+
repoDir,
|
|
600
|
+
components,
|
|
601
|
+
componentPaths,
|
|
602
|
+
vars,
|
|
603
|
+
version,
|
|
604
|
+
{
|
|
605
|
+
componentSkips,
|
|
606
|
+
rootSkip,
|
|
607
|
+
applyDefaults,
|
|
608
|
+
realCwd: cwd,
|
|
609
|
+
extraInstances,
|
|
610
|
+
instancesToScaffold
|
|
611
|
+
}
|
|
612
|
+
);
|
|
509
613
|
execSync("git add -A", { cwd: worktree, stdio: "pipe" });
|
|
510
|
-
const diff = execSync("git diff --cached --stat", {
|
|
614
|
+
const diff = execSync("git diff --cached --stat", {
|
|
615
|
+
cwd: worktree,
|
|
616
|
+
stdio: "pipe"
|
|
617
|
+
}).toString().trim();
|
|
511
618
|
if (!diff) {
|
|
512
619
|
cleanupWorktree(cwd, worktree, branch);
|
|
513
620
|
return { status: "clean" };
|
|
@@ -517,7 +624,10 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
517
624
|
{ cwd: worktree, stdio: "pipe" }
|
|
518
625
|
);
|
|
519
626
|
try {
|
|
520
|
-
execSync(`git worktree remove "${worktree}" --force`, {
|
|
627
|
+
execSync(`git worktree remove "${worktree}" --force`, {
|
|
628
|
+
cwd,
|
|
629
|
+
stdio: "pipe"
|
|
630
|
+
});
|
|
521
631
|
} catch {
|
|
522
632
|
try {
|
|
523
633
|
await rm(worktree, { recursive: true, force: true });
|
|
@@ -543,7 +653,12 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
543
653
|
} catch {
|
|
544
654
|
}
|
|
545
655
|
if (mergeClean) {
|
|
546
|
-
await migrateComponentMarkers(
|
|
656
|
+
await migrateComponentMarkers(
|
|
657
|
+
cwd,
|
|
658
|
+
components,
|
|
659
|
+
componentPaths,
|
|
660
|
+
applyDefaults
|
|
661
|
+
);
|
|
547
662
|
saveBaselineRef(cwd);
|
|
548
663
|
return { status: "clean" };
|
|
549
664
|
}
|
|
@@ -551,32 +666,47 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
551
666
|
if (baselineRef) {
|
|
552
667
|
const tmpTemplate = join2(tmpdir(), `projx-tpl-${Date.now()}`);
|
|
553
668
|
await mkdir(tmpTemplate, { recursive: true });
|
|
554
|
-
await writeTemplateToDir(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
669
|
+
await writeTemplateToDir(
|
|
670
|
+
tmpTemplate,
|
|
671
|
+
repoDir,
|
|
672
|
+
components,
|
|
673
|
+
componentPaths,
|
|
674
|
+
vars,
|
|
675
|
+
version,
|
|
676
|
+
{
|
|
677
|
+
componentSkips,
|
|
678
|
+
rootSkip,
|
|
679
|
+
applyDefaults,
|
|
680
|
+
realCwd: cwd,
|
|
681
|
+
extraInstances,
|
|
682
|
+
instancesToScaffold
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
const result = await tryThreeWayMerge(
|
|
686
|
+
cwd,
|
|
687
|
+
tmpTemplate,
|
|
688
|
+
baselineRef,
|
|
689
|
+
componentPaths
|
|
690
|
+
);
|
|
561
691
|
await rm(tmpTemplate, { recursive: true, force: true });
|
|
562
|
-
await migrateComponentMarkers(
|
|
692
|
+
await migrateComponentMarkers(
|
|
693
|
+
cwd,
|
|
694
|
+
components,
|
|
695
|
+
componentPaths,
|
|
696
|
+
applyDefaults
|
|
697
|
+
);
|
|
563
698
|
if (result.conflicted.length === 0) {
|
|
564
699
|
await writeManagedProjx(cwd, version, vars, applyDefaults);
|
|
565
700
|
execSync("git add -A", { cwd, stdio: "pipe" });
|
|
566
|
-
const staged = execSync("git diff --cached --stat", {
|
|
701
|
+
const staged = execSync("git diff --cached --stat", {
|
|
702
|
+
cwd,
|
|
703
|
+
stdio: "pipe"
|
|
704
|
+
}).toString().trim();
|
|
567
705
|
if (staged) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
);
|
|
573
|
-
} catch (err) {
|
|
574
|
-
throw new Error(
|
|
575
|
-
`Pre-commit hook rejected the merged template content. Resolve the issues and commit manually:
|
|
576
|
-
git commit -m "projx: update to template v${version} (3-way merge)"`,
|
|
577
|
-
{ cause: err }
|
|
578
|
-
);
|
|
579
|
-
}
|
|
706
|
+
execSync(
|
|
707
|
+
`git -c core.hooksPath=/dev/null commit -m "projx: update to template v${version} (3-way merge)"`,
|
|
708
|
+
{ cwd, stdio: "pipe" }
|
|
709
|
+
);
|
|
580
710
|
}
|
|
581
711
|
saveBaselineRef(cwd);
|
|
582
712
|
return result.merged.length > 0 ? { status: "merged", mergedFiles: result.merged } : { status: "clean" };
|
|
@@ -605,13 +735,28 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
605
735
|
conflictedFiles: result.conflicted
|
|
606
736
|
};
|
|
607
737
|
}
|
|
608
|
-
await writeTemplateToDir(
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
738
|
+
await writeTemplateToDir(
|
|
739
|
+
cwd,
|
|
740
|
+
repoDir,
|
|
741
|
+
components,
|
|
742
|
+
componentPaths,
|
|
743
|
+
vars,
|
|
744
|
+
version,
|
|
745
|
+
{
|
|
746
|
+
componentSkips,
|
|
747
|
+
rootSkip,
|
|
748
|
+
applyDefaults,
|
|
749
|
+
realCwd: cwd,
|
|
750
|
+
extraInstances,
|
|
751
|
+
instancesToScaffold
|
|
752
|
+
}
|
|
753
|
+
);
|
|
754
|
+
await migrateComponentMarkers(
|
|
755
|
+
cwd,
|
|
756
|
+
components,
|
|
757
|
+
componentPaths,
|
|
758
|
+
applyDefaults
|
|
759
|
+
);
|
|
615
760
|
return { status: "conflicts" };
|
|
616
761
|
} catch (err) {
|
|
617
762
|
cleanupWorktree(cwd, worktree, branch);
|
|
@@ -620,8 +765,6 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
620
765
|
}
|
|
621
766
|
|
|
622
767
|
export {
|
|
623
|
-
buildPathsUpper,
|
|
624
|
-
buildDisplayNames,
|
|
625
768
|
BASELINE_REF,
|
|
626
769
|
matchesSkip,
|
|
627
770
|
saveBaselineRef,
|