create-projx 1.6.2 → 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 +12 -0
- package/dist/{baseline-KTCFW2FK.js → baseline-RXPDDEDD.js} +2 -6
- package/dist/{chunk-LTIJPVRZ.js → chunk-LYPPFXGK.js} +123 -34
- package/dist/{chunk-D33FXCNT.js → chunk-OBYYB6PR.js} +289 -150
- package/dist/index.js +708 -172
- package/dist/{utils-VY5BBJBQ.js → utils-BXHJP6HF.js} +1 -1
- package/package.json +1 -1
- package/src/templates/ci.yml.ejs +63 -63
- package/src/templates/pre-commit.ejs +52 -52
- package/src/templates/setup.sh.ejs +16 -16
|
@@ -13,11 +13,18 @@ import {
|
|
|
13
13
|
toSnake,
|
|
14
14
|
upsertComponentMarker,
|
|
15
15
|
writeProjxConfig
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-LYPPFXGK.js";
|
|
17
17
|
|
|
18
18
|
// src/baseline.ts
|
|
19
19
|
import { existsSync, writeFileSync, unlinkSync } from "fs";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
chmod,
|
|
22
|
+
mkdir,
|
|
23
|
+
writeFile,
|
|
24
|
+
rm,
|
|
25
|
+
readFile as readFile2,
|
|
26
|
+
copyFile
|
|
27
|
+
} from "fs/promises";
|
|
21
28
|
import { execSync } from "child_process";
|
|
22
29
|
import { join as join2, dirname } from "path";
|
|
23
30
|
import { tmpdir } from "os";
|
|
@@ -25,30 +32,57 @@ import { tmpdir } from "os";
|
|
|
25
32
|
// src/generators/index.ts
|
|
26
33
|
import { readFile } from "fs/promises";
|
|
27
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
|
+
}
|
|
28
65
|
async function renderShared(filename, vars) {
|
|
29
|
-
const tpl = await readFile(
|
|
30
|
-
join(sharedTemplateDir(), filename),
|
|
31
|
-
"utf-8"
|
|
32
|
-
);
|
|
66
|
+
const tpl = await readFile(join(sharedTemplateDir(), filename), "utf-8");
|
|
33
67
|
return render(tpl, vars);
|
|
34
68
|
}
|
|
35
69
|
async function generateDockerCompose(vars) {
|
|
36
|
-
return renderShared("docker-compose.yml.ejs", vars);
|
|
70
|
+
return renderShared("docker-compose.yml.ejs", withInstances(vars));
|
|
37
71
|
}
|
|
38
72
|
async function generateDockerComposeDev(vars) {
|
|
39
|
-
return renderShared("docker-compose.dev.yml.ejs", vars);
|
|
73
|
+
return renderShared("docker-compose.dev.yml.ejs", withInstances(vars));
|
|
40
74
|
}
|
|
41
75
|
async function generatePreCommit(vars) {
|
|
42
|
-
return renderShared("pre-commit.ejs", vars);
|
|
76
|
+
return renderShared("pre-commit.ejs", withInstances(vars));
|
|
43
77
|
}
|
|
44
78
|
async function generateSetupSh(vars) {
|
|
45
|
-
return renderShared("setup.sh.ejs", vars);
|
|
79
|
+
return renderShared("setup.sh.ejs", withInstances(vars));
|
|
46
80
|
}
|
|
47
81
|
async function generateCiYml(vars) {
|
|
48
|
-
return renderShared("ci.yml.ejs", vars);
|
|
82
|
+
return renderShared("ci.yml.ejs", withInstances(vars));
|
|
49
83
|
}
|
|
50
84
|
async function generateReadme(vars) {
|
|
51
|
-
return renderShared("README.md.ejs", vars);
|
|
85
|
+
return renderShared("README.md.ejs", withInstances(vars));
|
|
52
86
|
}
|
|
53
87
|
function generateVscodeSettings(vars) {
|
|
54
88
|
const settings = {};
|
|
@@ -58,9 +92,15 @@ function generateVscodeSettings(vars) {
|
|
|
58
92
|
"editor.codeActionsOnSave": { "source.fixAll.ruff": "explicit" }
|
|
59
93
|
};
|
|
60
94
|
}
|
|
61
|
-
settings["[typescript]"] = {
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
};
|
|
64
104
|
settings["[json]"] = { "editor.defaultFormatter": "esbenp.prettier-vscode" };
|
|
65
105
|
settings["[css]"] = { "editor.defaultFormatter": "esbenp.prettier-vscode" };
|
|
66
106
|
settings["[yaml]"] = { "editor.defaultFormatter": "esbenp.prettier-vscode" };
|
|
@@ -82,32 +122,9 @@ function generateVscodeSettings(vars) {
|
|
|
82
122
|
}
|
|
83
123
|
|
|
84
124
|
// src/baseline.ts
|
|
85
|
-
function buildPathsUpper(paths) {
|
|
86
|
-
const result = {};
|
|
87
|
-
for (const [component, dir] of Object.entries(paths)) {
|
|
88
|
-
result[component] = dir.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
89
|
-
}
|
|
90
|
-
return result;
|
|
91
|
-
}
|
|
92
|
-
var CANONICAL_DISPLAY_NAMES = {
|
|
93
|
-
fastapi: "FastAPI",
|
|
94
|
-
fastify: "Fastify",
|
|
95
|
-
frontend: "Frontend",
|
|
96
|
-
mobile: "Flutter",
|
|
97
|
-
e2e: "E2E",
|
|
98
|
-
infra: "Terraform"
|
|
99
|
-
};
|
|
100
|
-
function buildDisplayNames(paths) {
|
|
101
|
-
const result = {};
|
|
102
|
-
for (const [component, dir] of Object.entries(paths)) {
|
|
103
|
-
const canonical = component;
|
|
104
|
-
result[canonical] = dir === canonical ? CANONICAL_DISPLAY_NAMES[canonical] : dir;
|
|
105
|
-
}
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
125
|
var BASELINE_REF = "refs/projx/baseline";
|
|
109
126
|
async function migrateComponentMarkers(cwd, components, componentPaths, applyDefaults) {
|
|
110
|
-
const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-
|
|
127
|
+
const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-BXHJP6HF.js");
|
|
111
128
|
for (const component of components) {
|
|
112
129
|
const dir = componentPaths[component];
|
|
113
130
|
const markerDir = join2(cwd, dir);
|
|
@@ -175,11 +192,17 @@ function saveBaselineRef(cwd) {
|
|
|
175
192
|
}
|
|
176
193
|
function getBaselineRef(cwd) {
|
|
177
194
|
try {
|
|
178
|
-
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();
|
|
179
199
|
} catch {
|
|
180
200
|
}
|
|
181
201
|
try {
|
|
182
|
-
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();
|
|
183
206
|
if (sha) return sha;
|
|
184
207
|
} catch {
|
|
185
208
|
}
|
|
@@ -187,7 +210,10 @@ function getBaselineRef(cwd) {
|
|
|
187
210
|
}
|
|
188
211
|
function getFileAtRef(cwd, ref, filePath) {
|
|
189
212
|
try {
|
|
190
|
-
return execSync(`git show ${ref}:"${filePath}"`, {
|
|
213
|
+
return execSync(`git show ${ref}:"${filePath}"`, {
|
|
214
|
+
cwd,
|
|
215
|
+
stdio: "pipe"
|
|
216
|
+
}).toString();
|
|
191
217
|
} catch {
|
|
192
218
|
return null;
|
|
193
219
|
}
|
|
@@ -258,15 +284,22 @@ async function tryThreeWayMerge(cwd, templateDir, baselineRef, componentPaths) {
|
|
|
258
284
|
const pathFallbacks = buildPathFallbacks(componentPaths);
|
|
259
285
|
for (const file of templateFiles) {
|
|
260
286
|
if (file === ".projx") continue;
|
|
261
|
-
|
|
287
|
+
const isMarker = file.endsWith("/.projx-component") || file === ".projx-component";
|
|
262
288
|
const oursPath = join2(cwd, file);
|
|
289
|
+
if (isMarker && existsSync(oursPath)) continue;
|
|
263
290
|
if (!existsSync(oursPath)) {
|
|
264
291
|
await mkdir(dirname(oursPath), { recursive: true });
|
|
265
292
|
await copyFile(join2(templateDir, file), oursPath);
|
|
266
293
|
merged.push(file);
|
|
267
294
|
continue;
|
|
268
295
|
}
|
|
269
|
-
|
|
296
|
+
if (isMarker) continue;
|
|
297
|
+
const baseContent = lookupBaseContent(
|
|
298
|
+
cwd,
|
|
299
|
+
baselineRef,
|
|
300
|
+
file,
|
|
301
|
+
pathFallbacks
|
|
302
|
+
);
|
|
270
303
|
if (baseContent === null) continue;
|
|
271
304
|
let theirsContent;
|
|
272
305
|
try {
|
|
@@ -307,7 +340,10 @@ function createOrphanWorktree(cwd) {
|
|
|
307
340
|
}
|
|
308
341
|
function cleanupWorktree(cwd, worktree, branch) {
|
|
309
342
|
try {
|
|
310
|
-
execSync(`git worktree remove "${worktree}" --force`, {
|
|
343
|
+
execSync(`git worktree remove "${worktree}" --force`, {
|
|
344
|
+
cwd,
|
|
345
|
+
stdio: "pipe"
|
|
346
|
+
});
|
|
311
347
|
} catch {
|
|
312
348
|
try {
|
|
313
349
|
rm(worktree, { recursive: true, force: true });
|
|
@@ -332,7 +368,8 @@ async function removeSkippedFiles(dir, skipPatterns, realDir) {
|
|
|
332
368
|
await walk(full, base);
|
|
333
369
|
} else if (entry.name !== ".projx-component") {
|
|
334
370
|
const targetRel = rel.endsWith(".ejs") ? rel.slice(0, -".ejs".length) : rel;
|
|
335
|
-
if (!matchesSkip(targetRel, skipPatterns) && !matchesSkip(rel, skipPatterns))
|
|
371
|
+
if (!matchesSkip(targetRel, skipPatterns) && !matchesSkip(rel, skipPatterns))
|
|
372
|
+
continue;
|
|
336
373
|
if (realDir && !existsSync(join2(realDir, targetRel))) continue;
|
|
337
374
|
await unlink(full);
|
|
338
375
|
}
|
|
@@ -341,41 +378,35 @@ async function removeSkippedFiles(dir, skipPatterns, realDir) {
|
|
|
341
378
|
await walk(dir, dir);
|
|
342
379
|
}
|
|
343
380
|
async function writeTemplateToDir(dest, repoDir, components, componentPaths, vars, version, options = {}) {
|
|
344
|
-
const {
|
|
381
|
+
const {
|
|
382
|
+
componentSkips,
|
|
383
|
+
rootSkip,
|
|
384
|
+
applyDefaults = false,
|
|
385
|
+
realCwd = dest,
|
|
386
|
+
extraInstances = [],
|
|
387
|
+
instancesToScaffold
|
|
388
|
+
} = options;
|
|
345
389
|
const name = vars.projectName;
|
|
346
390
|
const nameSnake = toSnake(name);
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (existsSync(srcDir)) {
|
|
366
|
-
await cp(srcDir, outDir, { recursive: true, force: true });
|
|
367
|
-
}
|
|
368
|
-
await rm(tmpDir, { recursive: true, force: true });
|
|
369
|
-
await renderEjsInDir(outDir, vars);
|
|
370
|
-
await upsertComponentMarker(join2(dest, targetDir), component, skipPatterns.length > 0 ? skipPatterns : void 0);
|
|
371
|
-
}
|
|
372
|
-
if (!vars.pathsUpper) {
|
|
373
|
-
vars.pathsUpper = buildPathsUpper(componentPaths);
|
|
374
|
-
}
|
|
375
|
-
if (!vars.displayNames) {
|
|
376
|
-
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
|
+
});
|
|
377
409
|
}
|
|
378
|
-
await substituteNames(dest, components, componentPaths, name, nameSnake, vars.nameOverrides);
|
|
379
410
|
const hasBackend = components.includes("fastapi") || components.includes("fastify");
|
|
380
411
|
const userSkip = rootSkip ?? [];
|
|
381
412
|
const defaultRootSkip = applyDefaults ? DEFAULT_ROOT_SKIP_PATTERNS : [];
|
|
@@ -386,20 +417,32 @@ async function writeTemplateToDir(dest, repoDir, components, componentPaths, var
|
|
|
386
417
|
};
|
|
387
418
|
if (hasBackend || components.includes("frontend")) {
|
|
388
419
|
if (shouldWrite("docker-compose.yml"))
|
|
389
|
-
await writeFile(
|
|
420
|
+
await writeFile(
|
|
421
|
+
join2(dest, "docker-compose.yml"),
|
|
422
|
+
await generateDockerCompose(vars)
|
|
423
|
+
);
|
|
390
424
|
if (shouldWrite("docker-compose.dev.yml"))
|
|
391
|
-
await writeFile(
|
|
425
|
+
await writeFile(
|
|
426
|
+
join2(dest, "docker-compose.dev.yml"),
|
|
427
|
+
await generateDockerComposeDev(vars)
|
|
428
|
+
);
|
|
392
429
|
}
|
|
393
430
|
if (shouldWrite("README.md"))
|
|
394
431
|
await writeFile(join2(dest, "README.md"), await generateReadme(vars));
|
|
395
432
|
if (shouldWrite(".githooks/pre-commit")) {
|
|
396
433
|
await mkdir(join2(dest, ".githooks"), { recursive: true });
|
|
397
|
-
await writeFile(
|
|
434
|
+
await writeFile(
|
|
435
|
+
join2(dest, ".githooks/pre-commit"),
|
|
436
|
+
await generatePreCommit(vars)
|
|
437
|
+
);
|
|
398
438
|
await chmod(join2(dest, ".githooks/pre-commit"), 493);
|
|
399
439
|
}
|
|
400
440
|
if (shouldWrite(".github/workflows/ci.yml")) {
|
|
401
441
|
await mkdir(join2(dest, ".github/workflows"), { recursive: true });
|
|
402
|
-
await writeFile(
|
|
442
|
+
await writeFile(
|
|
443
|
+
join2(dest, ".github/workflows/ci.yml"),
|
|
444
|
+
await generateCiYml(vars)
|
|
445
|
+
);
|
|
403
446
|
}
|
|
404
447
|
if (shouldWrite("setup.sh")) {
|
|
405
448
|
await writeFile(join2(dest, "setup.sh"), await generateSetupSh(vars));
|
|
@@ -408,31 +451,68 @@ async function writeTemplateToDir(dest, repoDir, components, componentPaths, var
|
|
|
408
451
|
await copyStaticFiles(repoDir, dest);
|
|
409
452
|
if (shouldWrite(".vscode/settings.json")) {
|
|
410
453
|
await mkdir(join2(dest, ".vscode"), { recursive: true });
|
|
411
|
-
await writeFile(
|
|
454
|
+
await writeFile(
|
|
455
|
+
join2(dest, ".vscode/settings.json"),
|
|
456
|
+
generateVscodeSettings(vars)
|
|
457
|
+
);
|
|
412
458
|
}
|
|
413
459
|
await writeManagedProjx(dest, version, vars, applyDefaults);
|
|
414
460
|
}
|
|
415
|
-
async function
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
await
|
|
435
|
-
|
|
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
|
+
);
|
|
436
516
|
}
|
|
437
517
|
}
|
|
438
518
|
async function detectPackageNameOverrides(cwd, components, componentPaths) {
|
|
@@ -484,7 +564,7 @@ async function readPubspecName(file) {
|
|
|
484
564
|
return null;
|
|
485
565
|
}
|
|
486
566
|
}
|
|
487
|
-
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) {
|
|
488
568
|
const hasHead = (() => {
|
|
489
569
|
try {
|
|
490
570
|
execSync("git rev-parse HEAD", { cwd, stdio: "pipe" });
|
|
@@ -494,24 +574,47 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
494
574
|
}
|
|
495
575
|
})();
|
|
496
576
|
if (!hasHead) {
|
|
497
|
-
await writeTemplateToDir(
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
+
);
|
|
503
593
|
return { status: "clean" };
|
|
504
594
|
}
|
|
505
595
|
const { worktree, branch } = createOrphanWorktree(cwd);
|
|
506
596
|
try {
|
|
507
|
-
await writeTemplateToDir(
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
+
);
|
|
513
613
|
execSync("git add -A", { cwd: worktree, stdio: "pipe" });
|
|
514
|
-
const diff = execSync("git diff --cached --stat", {
|
|
614
|
+
const diff = execSync("git diff --cached --stat", {
|
|
615
|
+
cwd: worktree,
|
|
616
|
+
stdio: "pipe"
|
|
617
|
+
}).toString().trim();
|
|
515
618
|
if (!diff) {
|
|
516
619
|
cleanupWorktree(cwd, worktree, branch);
|
|
517
620
|
return { status: "clean" };
|
|
@@ -521,7 +624,10 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
521
624
|
{ cwd: worktree, stdio: "pipe" }
|
|
522
625
|
);
|
|
523
626
|
try {
|
|
524
|
-
execSync(`git worktree remove "${worktree}" --force`, {
|
|
627
|
+
execSync(`git worktree remove "${worktree}" --force`, {
|
|
628
|
+
cwd,
|
|
629
|
+
stdio: "pipe"
|
|
630
|
+
});
|
|
525
631
|
} catch {
|
|
526
632
|
try {
|
|
527
633
|
await rm(worktree, { recursive: true, force: true });
|
|
@@ -547,7 +653,12 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
547
653
|
} catch {
|
|
548
654
|
}
|
|
549
655
|
if (mergeClean) {
|
|
550
|
-
await migrateComponentMarkers(
|
|
656
|
+
await migrateComponentMarkers(
|
|
657
|
+
cwd,
|
|
658
|
+
components,
|
|
659
|
+
componentPaths,
|
|
660
|
+
applyDefaults
|
|
661
|
+
);
|
|
551
662
|
saveBaselineRef(cwd);
|
|
552
663
|
return { status: "clean" };
|
|
553
664
|
}
|
|
@@ -555,32 +666,47 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
555
666
|
if (baselineRef) {
|
|
556
667
|
const tmpTemplate = join2(tmpdir(), `projx-tpl-${Date.now()}`);
|
|
557
668
|
await mkdir(tmpTemplate, { recursive: true });
|
|
558
|
-
await writeTemplateToDir(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
+
);
|
|
565
691
|
await rm(tmpTemplate, { recursive: true, force: true });
|
|
566
|
-
await migrateComponentMarkers(
|
|
692
|
+
await migrateComponentMarkers(
|
|
693
|
+
cwd,
|
|
694
|
+
components,
|
|
695
|
+
componentPaths,
|
|
696
|
+
applyDefaults
|
|
697
|
+
);
|
|
567
698
|
if (result.conflicted.length === 0) {
|
|
568
699
|
await writeManagedProjx(cwd, version, vars, applyDefaults);
|
|
569
700
|
execSync("git add -A", { cwd, stdio: "pipe" });
|
|
570
|
-
const staged = execSync("git diff --cached --stat", {
|
|
701
|
+
const staged = execSync("git diff --cached --stat", {
|
|
702
|
+
cwd,
|
|
703
|
+
stdio: "pipe"
|
|
704
|
+
}).toString().trim();
|
|
571
705
|
if (staged) {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
);
|
|
577
|
-
} catch (err) {
|
|
578
|
-
throw new Error(
|
|
579
|
-
`Pre-commit hook rejected the merged template content. Resolve the issues and commit manually:
|
|
580
|
-
git commit -m "projx: update to template v${version} (3-way merge)"`,
|
|
581
|
-
{ cause: err }
|
|
582
|
-
);
|
|
583
|
-
}
|
|
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
|
+
);
|
|
584
710
|
}
|
|
585
711
|
saveBaselineRef(cwd);
|
|
586
712
|
return result.merged.length > 0 ? { status: "merged", mergedFiles: result.merged } : { status: "clean" };
|
|
@@ -609,13 +735,28 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
609
735
|
conflictedFiles: result.conflicted
|
|
610
736
|
};
|
|
611
737
|
}
|
|
612
|
-
await writeTemplateToDir(
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
+
);
|
|
619
760
|
return { status: "conflicts" };
|
|
620
761
|
} catch (err) {
|
|
621
762
|
cleanupWorktree(cwd, worktree, branch);
|
|
@@ -624,8 +765,6 @@ async function applyTemplate(cwd, repoDir, components, componentPaths, vars, ver
|
|
|
624
765
|
}
|
|
625
766
|
|
|
626
767
|
export {
|
|
627
|
-
buildPathsUpper,
|
|
628
|
-
buildDisplayNames,
|
|
629
768
|
BASELINE_REF,
|
|
630
769
|
matchesSkip,
|
|
631
770
|
saveBaselineRef,
|