@rm-industries/create-forge 1.0.2 → 1.0.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 +23 -6
- package/dist/index.mjs +105 -27
- package/dist/index.mjs.map +1 -1
- package/dist/template/.github/actions/setup-project/action.yml +6 -1
- package/dist/template/.github/dependabot.yml +1 -1
- package/dist/template/.github/workflows/automation.yml +0 -20
- package/dist/template/.github/workflows/project.yml +213 -33
- package/dist/template/.github/workflows/security.yml +7 -0
- package/dist/template/README.md +30 -33
- package/dist/template/docs/github-pages.md +4 -2
- package/dist/template/docs/guides/quality-and-troubleshooting.md +13 -7
- package/dist/template/package-lock.json +783 -848
- package/dist/template/package.json +15 -14
- package/dist/template/playwright.config.ts +2 -1
- package/dist/template/playwright.smoke.config.ts +17 -0
- package/dist/template/tests/smoke/deployment.spec.ts +26 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,6 +13,21 @@ complete input contract. Unless disabled with `--no-install` or `--no-git`, it
|
|
|
13
13
|
runs `npm install` and initializes a `main`-branch Git repository after files are
|
|
14
14
|
created. It never stages files, reads Git identity, or creates a commit.
|
|
15
15
|
|
|
16
|
+
By default, the destination is both the generated project root and repository
|
|
17
|
+
root. To add a Forge site to an existing repository without replacing unrelated
|
|
18
|
+
files, pass the site directory as the destination and identify the repository
|
|
19
|
+
root separately:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm create @rm-industries/forge website -- --repository-root .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Forge writes the application into `website/`, places repository-owned GitHub
|
|
26
|
+
configuration in the root `.github/` directory, installs dependencies inside
|
|
27
|
+
`website/`, and initializes Git at the repository root when needed. Generated
|
|
28
|
+
workflow filters, working directories, artifact paths, and Dependabot settings
|
|
29
|
+
are adjusted for the nested project automatically.
|
|
30
|
+
|
|
16
31
|
The selected template is copied with dotfiles and file modes intact. Forge
|
|
17
32
|
customizes only reviewed metadata files and refuses parent-traversal paths,
|
|
18
33
|
symbolic-link collisions, filesystem roots, and unconfirmed non-empty
|
|
@@ -54,12 +69,14 @@ To exercise the packed CLI across the complete generator fixture matrix:
|
|
|
54
69
|
npm run test:generator:e2e
|
|
55
70
|
```
|
|
56
71
|
|
|
57
|
-
This generates default, fully specified, scoped-package-name,
|
|
58
|
-
no-install, and conflict fixtures in an OS
|
|
59
|
-
|
|
60
|
-
outside the Forge workspace. The
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
This generates default, fully specified, scoped-package-name,
|
|
73
|
+
current-directory, nested-project, no-install, and conflict fixtures in an OS
|
|
74
|
+
temporary directory. The default and nested fixtures install dependencies
|
|
75
|
+
outside the Forge workspace. The nested fixture also runs the generated
|
|
76
|
+
project's core quality and production-build pipeline, verifies its Pages
|
|
77
|
+
artifact location, and checks its repository-level CI and Dependabot paths. The
|
|
78
|
+
conflict fixture proves that a failed invocation leaves existing files
|
|
79
|
+
unchanged. Every fixture is removed after the run.
|
|
63
80
|
|
|
64
81
|
The release compatibility matrix runs the same packed fixtures with the
|
|
65
82
|
generated project's core quality gate on every supported Node line on Ubuntu
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { chmod, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { confirm, isCancel, text } from "@clack/prompts";
|
|
4
4
|
import { Command, CommanderError, Option } from "commander";
|
|
5
|
-
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
|
|
5
|
+
import { basename, dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
6
6
|
import validateNpmPackageName from "validate-npm-package-name";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -102,6 +102,7 @@ const resolveOptions = async (provided, { interactive, prompts }) => {
|
|
|
102
102
|
initialValue: ".",
|
|
103
103
|
validate: textValidation(validateDestination)
|
|
104
104
|
})));
|
|
105
|
+
const repositoryRoot = validateDestination(provided.repositoryRoot ?? destination);
|
|
105
106
|
const derivedPackageName = derivePackageName(destination);
|
|
106
107
|
const packageName = validatePackageName(provided.packageName ?? (useDefaults ? derivedPackageName : await promptText(prompts, {
|
|
107
108
|
message: "What should the npm package be named?",
|
|
@@ -111,6 +112,7 @@ const resolveOptions = async (provided, { interactive, prompts }) => {
|
|
|
111
112
|
const defaultSiteName = humanizePackageName(packageName);
|
|
112
113
|
return {
|
|
113
114
|
destination,
|
|
115
|
+
repositoryRoot,
|
|
114
116
|
packageName,
|
|
115
117
|
siteName: validatePlainText("--site-name", provided.siteName ?? (useDefaults ? defaultSiteName : await promptText(prompts, {
|
|
116
118
|
message: "What should the site be called?",
|
|
@@ -146,7 +148,7 @@ const resolveOptions = async (provided, { interactive, prompts }) => {
|
|
|
146
148
|
const hasBoth = (args, positive, negative) => args.includes(positive) && args.includes(negative);
|
|
147
149
|
const parseArguments = (args, version) => {
|
|
148
150
|
let output = "";
|
|
149
|
-
const program = new Command().name("create-forge").description("Create a content-driven Forge website").argument("[destination]", "directory where the project will be created").option("--name <name>", "npm package name (defaults to the directory basename)").option("--site-name <name>", "site name").option("--description <text>", "site description").option("--author <name>", "site author").option("--url <url>", "absolute canonical HTTP(S) URL").option("--repository <owner/repository>", "GitHub repository or an empty value").addOption(new Option("--install", "install dependencies")).addOption(new Option("--no-install", "do not install dependencies")).addOption(new Option("--git", "initialize a Git repository")).addOption(new Option("--no-git", "do not initialize a Git repository")).option("-y, --yes", "use documented defaults without prompting", false).version(version, "-v, --version", "show the installed package version").helpOption("-h, --help", "show this help message").showHelpAfterError().allowExcessArguments(false).exitOverride().configureOutput({
|
|
151
|
+
const program = new Command().name("create-forge").description("Create a content-driven Forge website").argument("[destination]", "directory where the project will be created").option("--repository-root <path>", "repository root (defaults to the project directory)").option("--name <name>", "npm package name (defaults to the directory basename)").option("--site-name <name>", "site name").option("--description <text>", "site description").option("--author <name>", "site author").option("--url <url>", "absolute canonical HTTP(S) URL").option("--repository <owner/repository>", "GitHub repository or an empty value").addOption(new Option("--install", "install dependencies")).addOption(new Option("--no-install", "do not install dependencies")).addOption(new Option("--git", "initialize a Git repository")).addOption(new Option("--no-git", "do not initialize a Git repository")).option("-y, --yes", "use documented defaults without prompting", false).version(version, "-v, --version", "show the installed package version").helpOption("-h, --help", "show this help message").showHelpAfterError().allowExcessArguments(false).exitOverride().configureOutput({
|
|
150
152
|
writeOut: (value) => {
|
|
151
153
|
output += value;
|
|
152
154
|
},
|
|
@@ -174,6 +176,7 @@ const parseArguments = (args, version) => {
|
|
|
174
176
|
const raw = program.opts();
|
|
175
177
|
return { provided: {
|
|
176
178
|
destination: program.args[0],
|
|
179
|
+
repositoryRoot: raw.repositoryRoot,
|
|
177
180
|
packageName: raw.name,
|
|
178
181
|
siteName: raw.siteName,
|
|
179
182
|
description: raw.description,
|
|
@@ -230,7 +233,10 @@ const templateTokens = Object.freeze({
|
|
|
230
233
|
url: `${prefix}URL__`,
|
|
231
234
|
repository: `${prefix}REPOSITORY__`,
|
|
232
235
|
securityScheduleMinute: `${prefix}SECURITY_SCHEDULE_MINUTE__`,
|
|
233
|
-
automationScheduleMinute: `${prefix}AUTOMATION_SCHEDULE_MINUTE__
|
|
236
|
+
automationScheduleMinute: `${prefix}AUTOMATION_SCHEDULE_MINUTE__`,
|
|
237
|
+
projectDirectory: `${prefix}PROJECT_DIRECTORY__`,
|
|
238
|
+
projectPathFilter: `${prefix}PROJECT_PATH_FILTER__`,
|
|
239
|
+
dependabotDirectory: `${prefix}DEPENDABOT_DIRECTORY__`
|
|
234
240
|
});
|
|
235
241
|
const templateTokenPrefix = prefix;
|
|
236
242
|
//#endregion
|
|
@@ -304,9 +310,12 @@ const replaceRawToken = (source, token, value, field) => {
|
|
|
304
310
|
if (!source.includes(token)) throw new Error(`Packaged template is missing the ${field} token.`);
|
|
305
311
|
return source.replaceAll(token, value);
|
|
306
312
|
};
|
|
307
|
-
const customizeTemplate = async (
|
|
308
|
-
const
|
|
309
|
-
const
|
|
313
|
+
const customizeTemplate = async (projectRoot, repositoryRoot, options) => {
|
|
314
|
+
const projectDirectory = relative(repositoryRoot, projectRoot).split(sep).join("/") || ".";
|
|
315
|
+
const projectPathFilter = projectDirectory === "." ? "**" : `${projectDirectory}/**`;
|
|
316
|
+
const dependabotDirectory = projectDirectory === "." ? "/" : `/${projectDirectory}`;
|
|
317
|
+
const packagePath = join(projectRoot, "package.json");
|
|
318
|
+
const packageLockPath = join(projectRoot, "package-lock.json");
|
|
310
319
|
const packageMetadata = JSON.parse(await readFile(packagePath, "utf8"));
|
|
311
320
|
const packageLock = JSON.parse(await readFile(packageLockPath, "utf8"));
|
|
312
321
|
if (packageMetadata.name !== templateTokens.packageName || packageLock.name !== templateTokens.packageName || packageLock.packages?.[""]?.name !== templateTokens.packageName) throw new Error("Packaged template contains unexpected package metadata tokens.");
|
|
@@ -319,7 +328,7 @@ const customizeTemplate = async (destination, options) => {
|
|
|
319
328
|
packageLock.packages[""].version = "0.0.0";
|
|
320
329
|
await writeFile(packagePath, `${JSON.stringify(packageMetadata, void 0, 2)}\n`);
|
|
321
330
|
await writeFile(packageLockPath, `${JSON.stringify(packageLock, void 0, 2)}\n`);
|
|
322
|
-
const siteConfigPath = join(
|
|
331
|
+
const siteConfigPath = join(projectRoot, "src", "config", "site.ts");
|
|
323
332
|
let siteConfig = await readFile(siteConfigPath, "utf8");
|
|
324
333
|
siteConfig = replaceToken(siteConfig, templateTokens.siteName, options.siteName, "site name");
|
|
325
334
|
siteConfig = replaceToken(siteConfig, templateTokens.description, options.description, "description");
|
|
@@ -329,39 +338,98 @@ const customizeTemplate = async (destination, options) => {
|
|
|
329
338
|
if (siteConfig.includes("__RM_INDUSTRIES_FORGE_TEMPLATE_V1_")) throw new Error("Packaged template contains unresolved tokens.");
|
|
330
339
|
await writeFile(siteConfigPath, siteConfig);
|
|
331
340
|
const scheduleMinutes = deriveScheduleMinutes(options.packageName);
|
|
332
|
-
const securityWorkflowPath = join(
|
|
333
|
-
const automationWorkflowPath = join(
|
|
341
|
+
const securityWorkflowPath = join(repositoryRoot, ".github", "workflows", "security.yml");
|
|
342
|
+
const automationWorkflowPath = join(repositoryRoot, ".github", "workflows", "automation.yml");
|
|
334
343
|
let securityWorkflow = await readFile(securityWorkflowPath, "utf8");
|
|
335
344
|
let automationWorkflow = await readFile(automationWorkflowPath, "utf8");
|
|
336
345
|
securityWorkflow = replaceRawToken(securityWorkflow, templateTokens.securityScheduleMinute, String(scheduleMinutes.security), "security schedule minute");
|
|
337
346
|
automationWorkflow = replaceRawToken(automationWorkflow, templateTokens.automationScheduleMinute, String(scheduleMinutes.automation), "automation schedule minute");
|
|
338
347
|
await writeFile(securityWorkflowPath, securityWorkflow);
|
|
339
348
|
await writeFile(automationWorkflowPath, automationWorkflow);
|
|
349
|
+
const tokenizedFiles = [
|
|
350
|
+
{
|
|
351
|
+
path: join(repositoryRoot, ".github", "workflows", "project.yml"),
|
|
352
|
+
replacements: [[
|
|
353
|
+
templateTokens.projectDirectory,
|
|
354
|
+
projectDirectory,
|
|
355
|
+
"project directory"
|
|
356
|
+
], [
|
|
357
|
+
templateTokens.projectPathFilter,
|
|
358
|
+
projectPathFilter,
|
|
359
|
+
"project path filter"
|
|
360
|
+
]]
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
path: securityWorkflowPath,
|
|
364
|
+
replacements: [[
|
|
365
|
+
templateTokens.projectPathFilter,
|
|
366
|
+
projectPathFilter,
|
|
367
|
+
"project path filter"
|
|
368
|
+
]]
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
path: join(repositoryRoot, ".github", "actions", "setup-project", "action.yml"),
|
|
372
|
+
replacements: [[
|
|
373
|
+
templateTokens.projectDirectory,
|
|
374
|
+
projectDirectory,
|
|
375
|
+
"project directory"
|
|
376
|
+
]]
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
path: join(repositoryRoot, ".github", "dependabot.yml"),
|
|
380
|
+
replacements: [[
|
|
381
|
+
templateTokens.dependabotDirectory,
|
|
382
|
+
dependabotDirectory,
|
|
383
|
+
"Dependabot directory"
|
|
384
|
+
]]
|
|
385
|
+
}
|
|
386
|
+
];
|
|
387
|
+
for (const file of tokenizedFiles) {
|
|
388
|
+
let source = await readFile(file.path, "utf8");
|
|
389
|
+
for (const [token, value, field] of file.replacements) source = replaceRawToken(source, token, value, field);
|
|
390
|
+
await writeFile(file.path, source);
|
|
391
|
+
}
|
|
340
392
|
};
|
|
341
393
|
const materializeProject = async (options, context) => {
|
|
342
394
|
const cwd = context.cwd ?? process.cwd();
|
|
343
|
-
const
|
|
395
|
+
const projectRoot = resolveDestination(options.destination, cwd);
|
|
396
|
+
const repositoryRoot = resolveDestination(options.repositoryRoot, cwd);
|
|
397
|
+
const projectPath = relative(repositoryRoot, projectRoot);
|
|
398
|
+
if (projectPath.startsWith("..") || isAbsolute(projectPath)) throw new MaterializationError(`Project root ${projectRoot} must be inside repository root ${repositoryRoot}.`);
|
|
344
399
|
const templateDirectory = resolveTemplateDirectory(context.templateDirectory);
|
|
345
|
-
const
|
|
400
|
+
const repositoryStat = await getPathStat(repositoryRoot);
|
|
401
|
+
if (repositoryStat && (repositoryStat.isSymbolicLink() || !repositoryStat.isDirectory())) throw new MaterializationError(`Unsafe repository root ${repositoryRoot}: expected a regular directory.`);
|
|
402
|
+
const files = await listTemplateFiles(templateDirectory);
|
|
403
|
+
const destinationExisted = await pathExists(projectRoot);
|
|
346
404
|
if (destinationExisted) {
|
|
347
|
-
const destinationStat = await lstat(
|
|
348
|
-
if (destinationStat.isSymbolicLink() || !destinationStat.isDirectory()) throw new MaterializationError(`Unsafe destination ${
|
|
405
|
+
const destinationStat = await lstat(projectRoot);
|
|
406
|
+
if (destinationStat.isSymbolicLink() || !destinationStat.isDirectory()) throw new MaterializationError(`Unsafe destination ${projectRoot}: expected a regular directory.`);
|
|
349
407
|
}
|
|
350
|
-
if (destinationExisted && !await isDirectoryEmpty(
|
|
351
|
-
if (!await context.confirmOverwrite?.(
|
|
408
|
+
if (destinationExisted && !await isDirectoryEmpty(projectRoot)) {
|
|
409
|
+
if (!await context.confirmOverwrite?.(projectRoot)) throw new MaterializationError(`Destination ${projectRoot} is not empty. No files were changed.`);
|
|
410
|
+
}
|
|
411
|
+
if (projectRoot !== repositoryRoot) {
|
|
412
|
+
const repositoryFiles = files.filter((file) => file.relativePath.startsWith(`.github${sep}`));
|
|
413
|
+
let hasRepositoryConflict = false;
|
|
414
|
+
for (const file of repositoryFiles) if (await pathExists(join(repositoryRoot, file.relativePath))) {
|
|
415
|
+
hasRepositoryConflict = true;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
if (hasRepositoryConflict && !await context.confirmOverwrite?.(repositoryRoot)) throw new MaterializationError(`Repository root ${repositoryRoot} contains conflicting files. No files were changed.`);
|
|
352
419
|
}
|
|
353
|
-
const files = await listTemplateFiles(templateDirectory);
|
|
354
420
|
const backupDirectory = await mkdtemp(join(tmpdir(), "create-forge-backup-"));
|
|
355
421
|
const createdFiles = /* @__PURE__ */ new Set();
|
|
356
422
|
const createdDirectories = /* @__PURE__ */ new Set();
|
|
357
423
|
const backups = /* @__PURE__ */ new Map();
|
|
358
424
|
try {
|
|
359
425
|
assertNotAborted(context.signal);
|
|
360
|
-
await ensureDirectory(
|
|
426
|
+
await ensureDirectory(repositoryRoot, createdDirectories);
|
|
427
|
+
await ensureDirectory(projectRoot, createdDirectories);
|
|
361
428
|
for (const file of files) {
|
|
362
429
|
assertNotAborted(context.signal);
|
|
363
|
-
const
|
|
364
|
-
const
|
|
430
|
+
const targetRoot = file.relativePath.startsWith(`.github${sep}`) ? repositoryRoot : projectRoot;
|
|
431
|
+
const target = join(targetRoot, file.relativePath);
|
|
432
|
+
const targetRelative = relative(targetRoot, target);
|
|
365
433
|
if (targetRelative.startsWith("..") || isAbsolute(targetRelative)) throw new Error(`Unsafe template path ${file.relativePath}.`);
|
|
366
434
|
await ensureDirectory(dirname(target), createdDirectories);
|
|
367
435
|
const targetStat = await getPathStat(target);
|
|
@@ -377,14 +445,17 @@ const materializeProject = async (options, context) => {
|
|
|
377
445
|
context.onFileCopied?.(target);
|
|
378
446
|
}
|
|
379
447
|
assertNotAborted(context.signal);
|
|
380
|
-
await customizeTemplate(
|
|
448
|
+
await customizeTemplate(projectRoot, repositoryRoot, options);
|
|
381
449
|
const tokenBytes = Buffer.from(templateTokenPrefix);
|
|
382
450
|
for (const file of files) {
|
|
383
451
|
assertNotAborted(context.signal);
|
|
384
|
-
|
|
452
|
+
const targetRoot = file.relativePath.startsWith(`.github${sep}`) ? repositoryRoot : projectRoot;
|
|
453
|
+
if ((await readFile(join(targetRoot, file.relativePath))).includes(tokenBytes)) throw new Error(`Generated file ${file.relativePath} contains an unresolved template token.`);
|
|
385
454
|
}
|
|
386
455
|
return {
|
|
387
|
-
destination,
|
|
456
|
+
destination: projectRoot,
|
|
457
|
+
projectRoot,
|
|
458
|
+
repositoryRoot,
|
|
388
459
|
filesCopied: files.length
|
|
389
460
|
};
|
|
390
461
|
} catch (error) {
|
|
@@ -404,7 +475,7 @@ const materializeProject = async (options, context) => {
|
|
|
404
475
|
} catch (cleanupError) {
|
|
405
476
|
if (!["ENOENT", "ENOTEMPTY"].includes(cleanupError.code ?? "")) cleanupErrors.push(cleanupError);
|
|
406
477
|
}
|
|
407
|
-
throw new MaterializationError(`${error instanceof Error ? error.message : String(error)} ${cleanupErrors.length ? `Recovery: review ${
|
|
478
|
+
throw new MaterializationError(`${error instanceof Error ? error.message : String(error)} ${cleanupErrors.length ? `Recovery: review ${repositoryRoot}; some changes could not be rolled back automatically.` : `Recovery: changes from this invocation were rolled back; resolve the error and retry.`}`, { cause: error });
|
|
408
479
|
} finally {
|
|
409
480
|
await rm(backupDirectory, {
|
|
410
481
|
recursive: true,
|
|
@@ -450,12 +521,18 @@ const runProjectSetup = async (options, context) => {
|
|
|
450
521
|
const command = (executable, args) => execute({
|
|
451
522
|
executable,
|
|
452
523
|
arguments: args,
|
|
453
|
-
cwd: context.
|
|
524
|
+
cwd: context.projectRoot,
|
|
454
525
|
...context.signal ? { signal: context.signal } : {},
|
|
455
526
|
...context.environment ? { environment: context.environment } : {}
|
|
456
527
|
});
|
|
457
528
|
if (options.install) await command("npm", ["install"]);
|
|
458
|
-
if (options.git) await
|
|
529
|
+
if (options.git) await execute({
|
|
530
|
+
executable: "git",
|
|
531
|
+
arguments: ["init", "--initial-branch=main"],
|
|
532
|
+
cwd: context.repositoryRoot,
|
|
533
|
+
...context.signal ? { signal: context.signal } : {},
|
|
534
|
+
...context.environment ? { environment: context.environment } : {}
|
|
535
|
+
});
|
|
459
536
|
};
|
|
460
537
|
//#endregion
|
|
461
538
|
//#region src/reporter.ts
|
|
@@ -519,10 +596,11 @@ if (result.options) {
|
|
|
519
596
|
} } : {}
|
|
520
597
|
});
|
|
521
598
|
await runProjectSetup(result.options, {
|
|
522
|
-
|
|
599
|
+
projectRoot: materialized.projectRoot,
|
|
600
|
+
repositoryRoot: materialized.repositoryRoot,
|
|
523
601
|
signal: controller.signal
|
|
524
602
|
});
|
|
525
|
-
result.output = formatCompletion(result.options, { destination: materialized.
|
|
603
|
+
result.output = formatCompletion(result.options, { destination: materialized.projectRoot });
|
|
526
604
|
} catch (error) {
|
|
527
605
|
result.output = `error: ${error instanceof Error ? error.message : String(error)}\n`;
|
|
528
606
|
result.exitCode = interruptedExitCode ?? (error instanceof ProcessStepError ? error.exitCode : 1);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/prompts.ts","../src/options.ts","../src/validation.ts","../src/resolve-options.ts","../src/cli.ts","../src/schedule.ts","../src/template-tokens.ts","../src/materialize.ts","../src/process.ts","../src/reporter.ts","../src/index.ts"],"sourcesContent":["import { confirm, isCancel, text } from '@clack/prompts';\n\nexport type TextPrompt = {\n message: string;\n initialValue?: string;\n placeholder?: string;\n validate?: (value: string | undefined) => string | Error | undefined;\n};\n\nexport type ConfirmPrompt = { message: string; initialValue: boolean };\n\nexport type PromptAdapter = {\n text(options: TextPrompt): Promise<string | symbol>;\n confirm(options: ConfirmPrompt): Promise<boolean | symbol>;\n isCancel(value: unknown): boolean;\n};\n\nexport const clackPrompts: PromptAdapter = {\n text: (options) =>\n text({\n message: options.message,\n ...(options.initialValue === undefined ? {} : { initialValue: options.initialValue }),\n ...(options.placeholder === undefined ? {} : { placeholder: options.placeholder }),\n ...(options.validate === undefined ? {} : { validate: options.validate }),\n }),\n confirm,\n isCancel,\n};\n","export type GeneratorOptions = {\n destination: string;\n packageName: string;\n siteName: string;\n description: string;\n author: string;\n url: string;\n repository: string;\n install: boolean;\n git: boolean;\n};\n\nexport type ProvidedOptions = {\n [Key in keyof GeneratorOptions]?: GeneratorOptions[Key] | undefined;\n} & { yes: boolean };\n\nexport const defaultDescription = 'A content-driven website built with Forge';\nexport const defaultUrl = 'https://example.com';\n\nexport const derivePackageName = (destination: string) => {\n return basename(resolve(destination)) || 'forge-site';\n};\n\nexport const humanizePackageName = (packageName: string) => {\n const unscopedName = packageName.split('/').at(-1) ?? packageName;\n return unscopedName\n .split(/[-_\\s]+/u)\n .filter(Boolean)\n .map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)\n .join(' ');\n};\nimport { basename, resolve } from 'node:path';\n","import validateNpmPackageName from 'validate-npm-package-name';\n\nexport class InputError extends Error {}\n\nconst invalid = (option: string, value: string, reason: string): never => {\n throw new InputError(`Invalid ${option} value ${JSON.stringify(value)}: ${reason}`);\n};\n\nexport const validateDestination = (value: string) => {\n const destination = value.trim();\n if (!destination) invalid('destination', value, 'provide a non-empty path');\n if (destination.includes('\\0')) invalid('destination', value, 'paths cannot contain null bytes');\n return destination;\n};\n\nexport const validatePackageName = (value: string) => {\n const packageName = value.trim();\n const result = validateNpmPackageName(packageName);\n if (!result.validForNewPackages) {\n const reason = [...(result.errors ?? []), ...(result.warnings ?? [])].join('; ');\n invalid('--name', value, reason || 'provide a valid npm package name');\n }\n return packageName;\n};\n\nexport const validatePlainText = (option: string, value: string, required = false) => {\n const text = value.trim();\n if (required && !text) invalid(option, value, 'provide non-empty text');\n if (/\\p{Cc}/u.test(text)) invalid(option, value, 'control characters are not allowed');\n return text;\n};\n\nexport const validateUrl = (value: string) => {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n return url.toString();\n};\n\nexport const validateRepository = (value: string) => {\n const repository = value.trim();\n if (!repository) return repository;\n if (!/^[\\w.-]+\\/[\\w.-]+$/u.test(repository)) {\n invalid('--repository', value, 'use the GitHub owner/repository format or leave it empty');\n }\n return repository;\n};\n","import {\n defaultDescription,\n defaultUrl,\n derivePackageName,\n humanizePackageName,\n type GeneratorOptions,\n type ProvidedOptions,\n} from './options';\nimport type { PromptAdapter, TextPrompt } from './prompts';\nimport {\n InputError,\n validateDestination,\n validatePackageName,\n validatePlainText,\n validateRepository,\n validateUrl,\n} from './validation';\n\nexport class CancellationError extends Error {}\n\ntype ResolutionContext = { interactive: boolean; prompts: PromptAdapter };\n\nconst textValidation = (validator: (value: string) => string) => (value: string | undefined) => {\n try {\n validator(value ?? '');\n return undefined;\n } catch (error) {\n return error instanceof Error ? error.message : 'Invalid value';\n }\n};\n\nconst promptText = async (prompts: PromptAdapter, options: TextPrompt) => {\n const value = await prompts.text(options);\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return String(value);\n};\n\nconst promptConfirm = async (prompts: PromptAdapter, message: string) => {\n const value = await prompts.confirm({ message, initialValue: true });\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return Boolean(value);\n};\n\nconst hasEveryExplicitValue = (options: ProvidedOptions) =>\n options.destination !== undefined &&\n options.packageName !== undefined &&\n options.siteName !== undefined &&\n options.description !== undefined &&\n options.author !== undefined &&\n options.url !== undefined &&\n options.repository !== undefined &&\n options.install !== undefined &&\n options.git !== undefined;\n\nexport const resolveOptions = async (\n provided: ProvidedOptions,\n { interactive, prompts }: ResolutionContext,\n): Promise<GeneratorOptions> => {\n const useDefaults = provided.yes;\n if (!interactive && !useDefaults && !hasEveryExplicitValue(provided)) {\n throw new InputError(\n 'Non-interactive use requires --yes or explicit values for every generator option. Run create-forge --help for details.',\n );\n }\n\n const destination = validateDestination(\n provided.destination ??\n (useDefaults\n ? '.'\n : await promptText(prompts, {\n message: 'Where should we create your project?',\n placeholder: '.',\n initialValue: '.',\n validate: textValidation(validateDestination),\n })),\n );\n const derivedPackageName = derivePackageName(destination);\n const packageName = validatePackageName(\n provided.packageName ??\n (useDefaults\n ? derivedPackageName\n : await promptText(prompts, {\n message: 'What should the npm package be named?',\n initialValue: derivedPackageName,\n validate: textValidation(validatePackageName),\n })),\n );\n const defaultSiteName = humanizePackageName(packageName);\n const siteName = validatePlainText(\n '--site-name',\n provided.siteName ??\n (useDefaults\n ? defaultSiteName\n : await promptText(prompts, {\n message: 'What should the site be called?',\n initialValue: defaultSiteName,\n validate: textValidation((value) => validatePlainText('--site-name', value, true)),\n })),\n true,\n );\n const description = validatePlainText(\n '--description',\n provided.description ??\n (useDefaults\n ? defaultDescription\n : await promptText(prompts, {\n message: 'How would you describe the site?',\n initialValue: defaultDescription,\n validate: textValidation((value) => validatePlainText('--description', value)),\n })),\n );\n const author = validatePlainText(\n '--author',\n provided.author ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'Who is the site author?',\n placeholder: 'Optional',\n validate: textValidation((value) => validatePlainText('--author', value)),\n })),\n );\n const url = validateUrl(\n provided.url ??\n (useDefaults\n ? defaultUrl\n : await promptText(prompts, {\n message: 'What is the canonical site URL?',\n initialValue: defaultUrl,\n validate: textValidation(validateUrl),\n })),\n );\n const repository = validateRepository(\n provided.repository ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'What is the GitHub repository?',\n placeholder: 'owner/repository (optional)',\n validate: textValidation(validateRepository),\n })),\n );\n const install = provided.install ?? (useDefaults ? true : await promptConfirm(prompts, 'Install dependencies?'));\n const git = provided.git ?? (useDefaults ? true : await promptConfirm(prompts, 'Initialize a Git repository?'));\n\n return { destination, packageName, siteName, description, author, url, repository, install, git };\n};\n","import { Command, CommanderError, Option } from 'commander';\n\nimport type { GeneratorOptions, ProvidedOptions } from './options';\nimport { clackPrompts, type PromptAdapter } from './prompts';\nimport { CancellationError, resolveOptions } from './resolve-options';\n\nexport type CliResult = { output: string; exitCode: number; options?: GeneratorOptions };\nexport type CliContext = { interactive?: boolean; prompts?: PromptAdapter };\n\nconst hasBoth = (args: readonly string[], positive: string, negative: string) =>\n args.includes(positive) && args.includes(negative);\n\nconst parseArguments = (args: readonly string[], version: string) => {\n let output = '';\n const program = new Command()\n .name('create-forge')\n .description('Create a content-driven Forge website')\n .argument('[destination]', 'directory where the project will be created')\n .option('--name <name>', 'npm package name (defaults to the directory basename)')\n .option('--site-name <name>', 'site name')\n .option('--description <text>', 'site description')\n .option('--author <name>', 'site author')\n .option('--url <url>', 'absolute canonical HTTP(S) URL')\n .option('--repository <owner/repository>', 'GitHub repository or an empty value')\n .addOption(new Option('--install', 'install dependencies'))\n .addOption(new Option('--no-install', 'do not install dependencies'))\n .addOption(new Option('--git', 'initialize a Git repository'))\n .addOption(new Option('--no-git', 'do not initialize a Git repository'))\n .option('-y, --yes', 'use documented defaults without prompting', false)\n .version(version, '-v, --version', 'show the installed package version')\n .helpOption('-h, --help', 'show this help message')\n .showHelpAfterError()\n .allowExcessArguments(false)\n .exitOverride()\n .configureOutput({\n writeOut: (value) => {\n output += value;\n },\n writeErr: (value) => {\n output += value;\n },\n });\n\n try {\n program.parse(args, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return { output, exitCode: error.exitCode } as const;\n throw error;\n }\n\n if (hasBoth(args, '--install', '--no-install')) {\n return { output: 'error: --install cannot be used with --no-install\\n', exitCode: 1 } as const;\n }\n if (hasBoth(args, '--git', '--no-git')) {\n return { output: 'error: --git cannot be used with --no-git\\n', exitCode: 1 } as const;\n }\n\n const raw = program.opts();\n const provided: ProvidedOptions = {\n destination: program.args[0],\n packageName: raw.name,\n siteName: raw.siteName,\n description: raw.description,\n author: raw.author,\n url: raw.url,\n repository: raw.repository,\n install: raw.install,\n git: raw.git,\n yes: raw.yes,\n };\n return { provided } as const;\n};\n\nexport const runCli = async (\n args: readonly string[],\n version: string,\n context: CliContext = {},\n): Promise<CliResult> => {\n const parsed = parseArguments(args, version);\n if (!('provided' in parsed)) return parsed;\n\n try {\n const options = await resolveOptions(parsed.provided, {\n interactive: context.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),\n prompts: context.prompts ?? clackPrompts,\n });\n return { output: '', exitCode: 0, options };\n } catch (error) {\n if (error instanceof CancellationError) return { output: `${error.message}\\n`, exitCode: 1 };\n if (error instanceof Error) return { output: `error: ${error.message}\\n`, exitCode: 1 };\n throw error;\n }\n};\n","import { createHash } from 'node:crypto';\n\nexport const deriveScheduleMinutes = (packageName: string) => {\n const digest = createHash('sha256').update(packageName).digest();\n const security = 1 + (digest.readUInt8(0) % 59);\n const automation = 1 + ((security - 1 + 17 + (digest.readUInt8(1) % 29)) % 59);\n return { security, automation };\n};\n","const prefix = '__RM_INDUSTRIES_FORGE_TEMPLATE_V1_';\n\nexport const templateTokens = Object.freeze({\n packageName: `${prefix}PACKAGE_NAME__`,\n siteName: `${prefix}SITE_NAME__`,\n description: `${prefix}DESCRIPTION__`,\n author: `${prefix}AUTHOR__`,\n url: `${prefix}URL__`,\n repository: `${prefix}REPOSITORY__`,\n securityScheduleMinute: `${prefix}SECURITY_SCHEDULE_MINUTE__`,\n automationScheduleMinute: `${prefix}AUTOMATION_SCHEDULE_MINUTE__`,\n});\n\nexport const templateTokenPrefix = prefix;\n","import {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rm,\n rmdir,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { GeneratorOptions } from './options';\nimport { deriveScheduleMinutes } from './schedule';\nimport { templateTokenPrefix, templateTokens } from './template-tokens';\n\nexport class MaterializationError extends Error {}\n\ntype MaterializationContext = {\n templateDirectory: string | URL;\n cwd?: string;\n signal?: AbortSignal;\n confirmOverwrite?: (destination: string) => Promise<boolean>;\n onFileCopied?: (path: string) => void;\n};\n\ntype TemplateFile = { source: string; relativePath: string; mode: number };\n\nconst pathExists = async (path: string) => {\n try {\n await lstat(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n};\n\nconst getPathStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n};\n\nconst assertNotAborted = (signal?: AbortSignal) => {\n if (signal?.aborted) throw new Error('Project creation was interrupted.');\n};\n\nconst resolveTemplateDirectory = (directory: string | URL) =>\n directory instanceof URL ? fileURLToPath(directory) : resolve(directory);\n\nconst resolveDestination = (destination: string, cwd: string) => {\n const segments = destination.split(/[\\\\/]/u);\n if (segments.includes('..')) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: parent traversal is not allowed.`,\n );\n }\n const resolved = isAbsolute(destination) ? resolve(destination) : resolve(cwd, destination);\n if (resolved === parse(resolved).root) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: a filesystem root is not allowed.`,\n );\n }\n return resolved;\n};\n\nconst listTemplateFiles = async (root: string, directory = root): Promise<TemplateFile[]> => {\n const entries = await readdir(directory, { withFileTypes: true });\n const files: TemplateFile[] = [];\n for (const entry of entries) {\n const source = join(directory, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listTemplateFiles(root, source)));\n continue;\n }\n if (!entry.isFile()) throw new MaterializationError(`Template entry ${source} is not a regular file.`);\n const relativePath = relative(root, source);\n files.push({\n source,\n relativePath: relativePath === '.gitignore.template' ? '.gitignore' : relativePath,\n mode: (await stat(source)).mode,\n });\n }\n return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));\n};\n\nconst isDirectoryEmpty = async (directory: string) => (await readdir(directory)).length === 0;\n\nconst ensureDirectory = async (directory: string, created: Set<string>) => {\n const existing = await getPathStat(directory);\n if (existing) {\n if (existing.isSymbolicLink() || !existing.isDirectory()) {\n throw new Error(`Unsafe destination entry ${directory}: expected a regular directory.`);\n }\n return;\n }\n const parent = dirname(directory);\n if (parent !== directory) await ensureDirectory(parent, created);\n await mkdir(directory);\n created.add(directory);\n};\n\nconst quoteTypeScriptString = (value: string) =>\n `'${value\n .replaceAll('\\\\', '\\\\\\\\')\n .replaceAll(\"'\", \"\\\\'\")\n .replaceAll('\\u2028', '\\\\u2028')\n .replaceAll('\\u2029', '\\\\u2029')}'`;\n\nconst replaceToken = (source: string, token: string, value: string, field: string) => {\n const quotedToken = `'${token}'`;\n if (!source.includes(quotedToken)) throw new Error(`Packaged template is missing the ${field} token.`);\n return source.replaceAll(quotedToken, quoteTypeScriptString(value));\n};\n\nconst replaceRawToken = (source: string, token: string, value: string, field: string) => {\n if (!source.includes(token)) throw new Error(`Packaged template is missing the ${field} token.`);\n return source.replaceAll(token, value);\n};\n\nconst customizeTemplate = async (destination: string, options: GeneratorOptions) => {\n const packagePath = join(destination, 'package.json');\n const packageLockPath = join(destination, 'package-lock.json');\n const packageMetadata = JSON.parse(await readFile(packagePath, 'utf8')) as Record<string, unknown>;\n const packageLock = JSON.parse(await readFile(packageLockPath, 'utf8')) as {\n name?: string;\n packages?: Record<string, Record<string, unknown>>;\n };\n if (\n packageMetadata.name !== templateTokens.packageName ||\n packageLock.name !== templateTokens.packageName ||\n packageLock.packages?.['']?.name !== templateTokens.packageName\n ) {\n throw new Error('Packaged template contains unexpected package metadata tokens.');\n }\n packageMetadata.name = options.packageName;\n packageMetadata.version = '0.0.0';\n packageMetadata.private = true;\n packageLock.name = options.packageName;\n if (!packageLock.packages?.['']) throw new Error('Packaged template lockfile is missing its root package metadata.');\n packageLock.packages[''].name = options.packageName;\n packageLock.packages[''].version = '0.0.0';\n await writeFile(packagePath, `${JSON.stringify(packageMetadata, undefined, 2)}\\n`);\n await writeFile(packageLockPath, `${JSON.stringify(packageLock, undefined, 2)}\\n`);\n\n const siteConfigPath = join(destination, 'src', 'config', 'site.ts');\n let siteConfig = await readFile(siteConfigPath, 'utf8');\n siteConfig = replaceToken(siteConfig, templateTokens.siteName, options.siteName, 'site name');\n siteConfig = replaceToken(siteConfig, templateTokens.description, options.description, 'description');\n siteConfig = replaceToken(siteConfig, templateTokens.author, options.author, 'author');\n siteConfig = replaceToken(siteConfig, templateTokens.url, options.url, 'URL');\n siteConfig = replaceToken(siteConfig, templateTokens.repository, options.repository, 'repository');\n if (siteConfig.includes(templateTokenPrefix)) throw new Error('Packaged template contains unresolved tokens.');\n await writeFile(siteConfigPath, siteConfig);\n\n const scheduleMinutes = deriveScheduleMinutes(options.packageName);\n const securityWorkflowPath = join(destination, '.github', 'workflows', 'security.yml');\n const automationWorkflowPath = join(destination, '.github', 'workflows', 'automation.yml');\n let securityWorkflow = await readFile(securityWorkflowPath, 'utf8');\n let automationWorkflow = await readFile(automationWorkflowPath, 'utf8');\n securityWorkflow = replaceRawToken(\n securityWorkflow,\n templateTokens.securityScheduleMinute,\n String(scheduleMinutes.security),\n 'security schedule minute',\n );\n automationWorkflow = replaceRawToken(\n automationWorkflow,\n templateTokens.automationScheduleMinute,\n String(scheduleMinutes.automation),\n 'automation schedule minute',\n );\n await writeFile(securityWorkflowPath, securityWorkflow);\n await writeFile(automationWorkflowPath, automationWorkflow);\n};\n\nexport const materializeProject = async (options: GeneratorOptions, context: MaterializationContext) => {\n const cwd = context.cwd ?? process.cwd();\n const destination = resolveDestination(options.destination, cwd);\n const templateDirectory = resolveTemplateDirectory(context.templateDirectory);\n const destinationExisted = await pathExists(destination);\n if (destinationExisted) {\n const destinationStat = await lstat(destination);\n if (destinationStat.isSymbolicLink() || !destinationStat.isDirectory()) {\n throw new MaterializationError(`Unsafe destination ${destination}: expected a regular directory.`);\n }\n }\n if (destinationExisted && !(await isDirectoryEmpty(destination))) {\n const confirmed = await context.confirmOverwrite?.(destination);\n if (!confirmed) {\n throw new MaterializationError(`Destination ${destination} is not empty. No files were changed.`);\n }\n }\n\n const files = await listTemplateFiles(templateDirectory);\n const backupDirectory = await mkdtemp(join(tmpdir(), 'create-forge-backup-'));\n const createdFiles = new Set<string>();\n const createdDirectories = new Set<string>();\n const backups = new Map<string, string>();\n\n try {\n assertNotAborted(context.signal);\n await ensureDirectory(destination, createdDirectories);\n for (const file of files) {\n assertNotAborted(context.signal);\n const target = join(destination, file.relativePath);\n const targetRelative = relative(destination, target);\n if (targetRelative.startsWith('..') || isAbsolute(targetRelative))\n throw new Error(`Unsafe template path ${file.relativePath}.`);\n await ensureDirectory(dirname(target), createdDirectories);\n const targetStat = await getPathStat(target);\n if (targetStat) {\n if (targetStat.isSymbolicLink() || !targetStat.isFile()) {\n throw new Error(`Unsafe destination entry ${target}: expected a regular file.`);\n }\n const backup = join(backupDirectory, file.relativePath);\n await mkdir(dirname(backup), { recursive: true });\n await copyFile(target, backup);\n backups.set(target, backup);\n } else {\n createdFiles.add(target);\n }\n await copyFile(file.source, target);\n await chmod(target, file.mode);\n context.onFileCopied?.(target);\n }\n assertNotAborted(context.signal);\n await customizeTemplate(destination, options);\n const tokenBytes = Buffer.from(templateTokenPrefix);\n for (const file of files) {\n assertNotAborted(context.signal);\n if ((await readFile(join(destination, file.relativePath))).includes(tokenBytes)) {\n throw new Error(`Generated file ${file.relativePath} contains an unresolved template token.`);\n }\n }\n return { destination, filesCopied: files.length };\n } catch (error) {\n const cleanupErrors: unknown[] = [];\n for (const [target, backup] of backups) {\n try {\n await copyFile(backup, target);\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const file of createdFiles) {\n try {\n await rm(file, { force: true });\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const directory of [...createdDirectories].sort((left, right) => right.length - left.length)) {\n try {\n await rmdir(directory);\n } catch (cleanupError) {\n if (!['ENOENT', 'ENOTEMPTY'].includes((cleanupError as NodeJS.ErrnoException).code ?? '')) {\n cleanupErrors.push(cleanupError);\n }\n }\n }\n const reason = error instanceof Error ? error.message : String(error);\n const recovery = cleanupErrors.length\n ? `Recovery: review ${destination}; some changes could not be rolled back automatically.`\n : `Recovery: changes from this invocation were rolled back; resolve the error and retry.`;\n throw new MaterializationError(`${reason} ${recovery}`, { cause: error });\n } finally {\n await rm(backupDirectory, { recursive: true, force: true });\n }\n};\n","import { spawn } from 'node:child_process';\n\nimport type { GeneratorOptions } from './options';\n\nexport class ProcessStepError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1, options?: ErrorOptions) {\n super(message, options);\n this.exitCode = exitCode;\n }\n}\n\nexport type Command = {\n executable: string;\n arguments: readonly string[];\n cwd: string;\n signal?: AbortSignal;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport type CommandExecutor = (command: Command) => Promise<void>;\n\nconst signalExitCode = (signal: NodeJS.Signals | null) => {\n if (signal === 'SIGINT') return 130;\n if (signal === 'SIGTERM') return 143;\n return 1;\n};\n\nexport const executeCommand: CommandExecutor = ({ executable, arguments: args, cwd, signal, environment }) =>\n new Promise((resolve, reject) => {\n const child = spawn(executable, args, {\n cwd,\n stdio: 'inherit',\n ...(signal ? { signal } : {}),\n ...(environment ? { env: environment } : {}),\n });\n\n child.once('error', (error) => {\n const interrupted = signal?.aborted;\n reject(\n new ProcessStepError(\n interrupted\n ? `${executable} was interrupted. The generated project remains at ${cwd}.`\n : `Could not start ${executable}: ${error.message}. The generated project remains at ${cwd}.`,\n interrupted ? 130 : 1,\n { cause: error },\n ),\n );\n });\n child.once('close', (code, processSignal) => {\n if (code === 0) {\n resolve();\n return;\n }\n const detail = processSignal ? `signal ${processSignal}` : `exit code ${code ?? 'unknown'}`;\n reject(\n new ProcessStepError(\n `${executable} failed with ${detail}. The generated project remains at ${cwd}; review the output above and retry the command there.`,\n processSignal ? signalExitCode(processSignal) : (code ?? 1),\n ),\n );\n });\n });\n\ntype ProjectSetupContext = {\n destination: string;\n signal?: AbortSignal;\n execute?: CommandExecutor;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport const runProjectSetup = async (options: GeneratorOptions, context: ProjectSetupContext) => {\n const execute = context.execute ?? executeCommand;\n const command = (executable: string, args: readonly string[]) =>\n execute({\n executable,\n arguments: args,\n cwd: context.destination,\n ...(context.signal ? { signal: context.signal } : {}),\n ...(context.environment ? { environment: context.environment } : {}),\n });\n\n if (options.install) await command('npm', ['install']);\n if (options.git) await command('git', ['init', '--initial-branch=main']);\n};\n","import { isAbsolute, relative } from 'node:path';\n\nimport type { GeneratorOptions } from './options';\n\ntype CompletionContext = {\n destination: string;\n cwd?: string;\n color?: boolean;\n};\n\nconst ansi = {\n bold: (value: string) => `\\u001B[1m${value}\\u001B[22m`,\n green: (value: string) => `\\u001B[32m${value}\\u001B[39m`,\n};\n\nconst commandPath = (path: string) => {\n if (/^[\\w./@-]+$/u.test(path)) return path;\n return `\"${path.replaceAll('\"', '\\\\\"')}\"`;\n};\n\nconst displayDestination = (destination: string, cwd: string) => {\n const path = relative(cwd, destination);\n if (!path) return '.';\n return isAbsolute(path) ? destination : path;\n};\n\nexport const supportsColor = (environment: NodeJS.ProcessEnv = process.env, terminal = Boolean(process.stdout.isTTY)) =>\n terminal && environment.NO_COLOR === undefined;\n\nexport const formatCompletion = (options: GeneratorOptions, context: CompletionContext) => {\n const cwd = context.cwd ?? process.cwd();\n const color = context.color ?? supportsColor();\n const decorate = color ? ansi : { bold: (value: string) => value, green: (value: string) => value };\n const destination = displayDestination(context.destination, cwd);\n const commands = [\n ...(destination === '.' ? [] : [`cd ${commandPath(destination)}`]),\n ...(options.install ? [] : ['npm install']),\n 'npm run dev',\n ];\n\n return `${decorate.green('✓')} Created ${options.siteName} in ${destination}\\n\\n${decorate.bold('Setup')}\\n Dependencies: ${options.install ? 'installed' : 'skipped'}\\n Git repository: ${options.git ? 'initialized' : 'skipped'}\\n\\n${decorate.bold('Next steps')}\\n${commands.map((command) => ` ${command}`).join('\\n')}\\n`;\n};\n","#!/usr/bin/env node\n\nimport { readFile } from 'node:fs/promises';\n\nimport { confirm, isCancel } from '@clack/prompts';\n\nimport { runCli } from './cli';\nimport { materializeProject } from './materialize';\nimport { ProcessStepError, runProjectSetup } from './process';\nimport { formatCompletion } from './reporter';\n\ntype PackageMetadata = {\n version: string;\n};\n\nconst packageJson = new URL('../package.json', import.meta.url);\nconst metadata = JSON.parse(await readFile(packageJson, 'utf8')) as PackageMetadata;\nconst interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);\nconst result = await runCli(process.argv.slice(2), metadata.version, { interactive });\n\nif (result.options) {\n const controller = new AbortController();\n let interruptedExitCode: number | undefined;\n const interrupt = () => {\n interruptedExitCode = 130;\n controller.abort();\n };\n const terminate = () => {\n interruptedExitCode = 143;\n controller.abort();\n };\n process.once('SIGINT', interrupt);\n process.once('SIGTERM', terminate);\n try {\n const materialized = await materializeProject(result.options, {\n templateDirectory: new URL('./template/', import.meta.url),\n signal: controller.signal,\n ...(interactive\n ? {\n confirmOverwrite: async (destination: string) => {\n const answer = await confirm({\n message: `${destination} is not empty. Continue and overwrite conflicting template files?`,\n initialValue: false,\n });\n return !isCancel(answer) && answer;\n },\n }\n : {}),\n });\n await runProjectSetup(result.options, {\n destination: materialized.destination,\n signal: controller.signal,\n });\n result.output = formatCompletion(result.options, { destination: materialized.destination });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n result.output = `error: ${message}\\n`;\n result.exitCode = interruptedExitCode ?? (error instanceof ProcessStepError ? error.exitCode : 1);\n } finally {\n process.off('SIGINT', interrupt);\n process.off('SIGTERM', terminate);\n }\n}\n\nprocess.stdout.write(result.output);\nprocess.exitCode = result.exitCode;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/prompts.ts","../src/options.ts","../src/validation.ts","../src/resolve-options.ts","../src/cli.ts","../src/schedule.ts","../src/template-tokens.ts","../src/materialize.ts","../src/process.ts","../src/reporter.ts","../src/index.ts"],"sourcesContent":["import { confirm, isCancel, text } from '@clack/prompts';\n\nexport type TextPrompt = {\n message: string;\n initialValue?: string;\n placeholder?: string;\n validate?: (value: string | undefined) => string | Error | undefined;\n};\n\nexport type ConfirmPrompt = { message: string; initialValue: boolean };\n\nexport type PromptAdapter = {\n text(options: TextPrompt): Promise<string | symbol>;\n confirm(options: ConfirmPrompt): Promise<boolean | symbol>;\n isCancel(value: unknown): boolean;\n};\n\nexport const clackPrompts: PromptAdapter = {\n text: (options) =>\n text({\n message: options.message,\n ...(options.initialValue === undefined ? {} : { initialValue: options.initialValue }),\n ...(options.placeholder === undefined ? {} : { placeholder: options.placeholder }),\n ...(options.validate === undefined ? {} : { validate: options.validate }),\n }),\n confirm,\n isCancel,\n};\n","export type GeneratorOptions = {\n destination: string;\n repositoryRoot: string;\n packageName: string;\n siteName: string;\n description: string;\n author: string;\n url: string;\n repository: string;\n install: boolean;\n git: boolean;\n};\n\nexport type ProvidedOptions = {\n [Key in keyof GeneratorOptions]?: GeneratorOptions[Key] | undefined;\n} & { yes: boolean };\n\nexport const defaultDescription = 'A content-driven website built with Forge';\nexport const defaultUrl = 'https://example.com';\n\nexport const derivePackageName = (destination: string) => {\n return basename(resolve(destination)) || 'forge-site';\n};\n\nexport const humanizePackageName = (packageName: string) => {\n const unscopedName = packageName.split('/').at(-1) ?? packageName;\n return unscopedName\n .split(/[-_\\s]+/u)\n .filter(Boolean)\n .map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)\n .join(' ');\n};\nimport { basename, resolve } from 'node:path';\n","import validateNpmPackageName from 'validate-npm-package-name';\n\nexport class InputError extends Error {}\n\nconst invalid = (option: string, value: string, reason: string): never => {\n throw new InputError(`Invalid ${option} value ${JSON.stringify(value)}: ${reason}`);\n};\n\nexport const validateDestination = (value: string) => {\n const destination = value.trim();\n if (!destination) invalid('destination', value, 'provide a non-empty path');\n if (destination.includes('\\0')) invalid('destination', value, 'paths cannot contain null bytes');\n return destination;\n};\n\nexport const validatePackageName = (value: string) => {\n const packageName = value.trim();\n const result = validateNpmPackageName(packageName);\n if (!result.validForNewPackages) {\n const reason = [...(result.errors ?? []), ...(result.warnings ?? [])].join('; ');\n invalid('--name', value, reason || 'provide a valid npm package name');\n }\n return packageName;\n};\n\nexport const validatePlainText = (option: string, value: string, required = false) => {\n const text = value.trim();\n if (required && !text) invalid(option, value, 'provide non-empty text');\n if (/\\p{Cc}/u.test(text)) invalid(option, value, 'control characters are not allowed');\n return text;\n};\n\nexport const validateUrl = (value: string) => {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n return url.toString();\n};\n\nexport const validateRepository = (value: string) => {\n const repository = value.trim();\n if (!repository) return repository;\n if (!/^[\\w.-]+\\/[\\w.-]+$/u.test(repository)) {\n invalid('--repository', value, 'use the GitHub owner/repository format or leave it empty');\n }\n return repository;\n};\n","import {\n defaultDescription,\n defaultUrl,\n derivePackageName,\n humanizePackageName,\n type GeneratorOptions,\n type ProvidedOptions,\n} from './options';\nimport type { PromptAdapter, TextPrompt } from './prompts';\nimport {\n InputError,\n validateDestination,\n validatePackageName,\n validatePlainText,\n validateRepository,\n validateUrl,\n} from './validation';\n\nexport class CancellationError extends Error {}\n\ntype ResolutionContext = { interactive: boolean; prompts: PromptAdapter };\n\nconst textValidation = (validator: (value: string) => string) => (value: string | undefined) => {\n try {\n validator(value ?? '');\n return undefined;\n } catch (error) {\n return error instanceof Error ? error.message : 'Invalid value';\n }\n};\n\nconst promptText = async (prompts: PromptAdapter, options: TextPrompt) => {\n const value = await prompts.text(options);\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return String(value);\n};\n\nconst promptConfirm = async (prompts: PromptAdapter, message: string) => {\n const value = await prompts.confirm({ message, initialValue: true });\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return Boolean(value);\n};\n\nconst hasEveryExplicitValue = (options: ProvidedOptions) =>\n options.destination !== undefined &&\n options.packageName !== undefined &&\n options.siteName !== undefined &&\n options.description !== undefined &&\n options.author !== undefined &&\n options.url !== undefined &&\n options.repository !== undefined &&\n options.install !== undefined &&\n options.git !== undefined;\n\nexport const resolveOptions = async (\n provided: ProvidedOptions,\n { interactive, prompts }: ResolutionContext,\n): Promise<GeneratorOptions> => {\n const useDefaults = provided.yes;\n if (!interactive && !useDefaults && !hasEveryExplicitValue(provided)) {\n throw new InputError(\n 'Non-interactive use requires --yes or explicit values for every generator option. Run create-forge --help for details.',\n );\n }\n\n const destination = validateDestination(\n provided.destination ??\n (useDefaults\n ? '.'\n : await promptText(prompts, {\n message: 'Where should we create your project?',\n placeholder: '.',\n initialValue: '.',\n validate: textValidation(validateDestination),\n })),\n );\n const repositoryRoot = validateDestination(provided.repositoryRoot ?? destination);\n const derivedPackageName = derivePackageName(destination);\n const packageName = validatePackageName(\n provided.packageName ??\n (useDefaults\n ? derivedPackageName\n : await promptText(prompts, {\n message: 'What should the npm package be named?',\n initialValue: derivedPackageName,\n validate: textValidation(validatePackageName),\n })),\n );\n const defaultSiteName = humanizePackageName(packageName);\n const siteName = validatePlainText(\n '--site-name',\n provided.siteName ??\n (useDefaults\n ? defaultSiteName\n : await promptText(prompts, {\n message: 'What should the site be called?',\n initialValue: defaultSiteName,\n validate: textValidation((value) => validatePlainText('--site-name', value, true)),\n })),\n true,\n );\n const description = validatePlainText(\n '--description',\n provided.description ??\n (useDefaults\n ? defaultDescription\n : await promptText(prompts, {\n message: 'How would you describe the site?',\n initialValue: defaultDescription,\n validate: textValidation((value) => validatePlainText('--description', value)),\n })),\n );\n const author = validatePlainText(\n '--author',\n provided.author ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'Who is the site author?',\n placeholder: 'Optional',\n validate: textValidation((value) => validatePlainText('--author', value)),\n })),\n );\n const url = validateUrl(\n provided.url ??\n (useDefaults\n ? defaultUrl\n : await promptText(prompts, {\n message: 'What is the canonical site URL?',\n initialValue: defaultUrl,\n validate: textValidation(validateUrl),\n })),\n );\n const repository = validateRepository(\n provided.repository ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'What is the GitHub repository?',\n placeholder: 'owner/repository (optional)',\n validate: textValidation(validateRepository),\n })),\n );\n const install = provided.install ?? (useDefaults ? true : await promptConfirm(prompts, 'Install dependencies?'));\n const git = provided.git ?? (useDefaults ? true : await promptConfirm(prompts, 'Initialize a Git repository?'));\n\n return { destination, repositoryRoot, packageName, siteName, description, author, url, repository, install, git };\n};\n","import { Command, CommanderError, Option } from 'commander';\n\nimport type { GeneratorOptions, ProvidedOptions } from './options';\nimport { clackPrompts, type PromptAdapter } from './prompts';\nimport { CancellationError, resolveOptions } from './resolve-options';\n\nexport type CliResult = { output: string; exitCode: number; options?: GeneratorOptions };\nexport type CliContext = { interactive?: boolean; prompts?: PromptAdapter };\n\nconst hasBoth = (args: readonly string[], positive: string, negative: string) =>\n args.includes(positive) && args.includes(negative);\n\nconst parseArguments = (args: readonly string[], version: string) => {\n let output = '';\n const program = new Command()\n .name('create-forge')\n .description('Create a content-driven Forge website')\n .argument('[destination]', 'directory where the project will be created')\n .option('--repository-root <path>', 'repository root (defaults to the project directory)')\n .option('--name <name>', 'npm package name (defaults to the directory basename)')\n .option('--site-name <name>', 'site name')\n .option('--description <text>', 'site description')\n .option('--author <name>', 'site author')\n .option('--url <url>', 'absolute canonical HTTP(S) URL')\n .option('--repository <owner/repository>', 'GitHub repository or an empty value')\n .addOption(new Option('--install', 'install dependencies'))\n .addOption(new Option('--no-install', 'do not install dependencies'))\n .addOption(new Option('--git', 'initialize a Git repository'))\n .addOption(new Option('--no-git', 'do not initialize a Git repository'))\n .option('-y, --yes', 'use documented defaults without prompting', false)\n .version(version, '-v, --version', 'show the installed package version')\n .helpOption('-h, --help', 'show this help message')\n .showHelpAfterError()\n .allowExcessArguments(false)\n .exitOverride()\n .configureOutput({\n writeOut: (value) => {\n output += value;\n },\n writeErr: (value) => {\n output += value;\n },\n });\n\n try {\n program.parse(args, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return { output, exitCode: error.exitCode } as const;\n throw error;\n }\n\n if (hasBoth(args, '--install', '--no-install')) {\n return { output: 'error: --install cannot be used with --no-install\\n', exitCode: 1 } as const;\n }\n if (hasBoth(args, '--git', '--no-git')) {\n return { output: 'error: --git cannot be used with --no-git\\n', exitCode: 1 } as const;\n }\n\n const raw = program.opts();\n const provided: ProvidedOptions = {\n destination: program.args[0],\n repositoryRoot: raw.repositoryRoot,\n packageName: raw.name,\n siteName: raw.siteName,\n description: raw.description,\n author: raw.author,\n url: raw.url,\n repository: raw.repository,\n install: raw.install,\n git: raw.git,\n yes: raw.yes,\n };\n return { provided } as const;\n};\n\nexport const runCli = async (\n args: readonly string[],\n version: string,\n context: CliContext = {},\n): Promise<CliResult> => {\n const parsed = parseArguments(args, version);\n if (!('provided' in parsed)) return parsed;\n\n try {\n const options = await resolveOptions(parsed.provided, {\n interactive: context.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),\n prompts: context.prompts ?? clackPrompts,\n });\n return { output: '', exitCode: 0, options };\n } catch (error) {\n if (error instanceof CancellationError) return { output: `${error.message}\\n`, exitCode: 1 };\n if (error instanceof Error) return { output: `error: ${error.message}\\n`, exitCode: 1 };\n throw error;\n }\n};\n","import { createHash } from 'node:crypto';\n\nexport const deriveScheduleMinutes = (packageName: string) => {\n const digest = createHash('sha256').update(packageName).digest();\n const security = 1 + (digest.readUInt8(0) % 59);\n const automation = 1 + ((security - 1 + 17 + (digest.readUInt8(1) % 29)) % 59);\n return { security, automation };\n};\n","const prefix = '__RM_INDUSTRIES_FORGE_TEMPLATE_V1_';\n\nexport const templateTokens = Object.freeze({\n packageName: `${prefix}PACKAGE_NAME__`,\n siteName: `${prefix}SITE_NAME__`,\n description: `${prefix}DESCRIPTION__`,\n author: `${prefix}AUTHOR__`,\n url: `${prefix}URL__`,\n repository: `${prefix}REPOSITORY__`,\n securityScheduleMinute: `${prefix}SECURITY_SCHEDULE_MINUTE__`,\n automationScheduleMinute: `${prefix}AUTOMATION_SCHEDULE_MINUTE__`,\n projectDirectory: `${prefix}PROJECT_DIRECTORY__`,\n projectPathFilter: `${prefix}PROJECT_PATH_FILTER__`,\n dependabotDirectory: `${prefix}DEPENDABOT_DIRECTORY__`,\n});\n\nexport const templateTokenPrefix = prefix;\n","import {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rm,\n rmdir,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, relative, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { GeneratorOptions } from './options';\nimport { deriveScheduleMinutes } from './schedule';\nimport { templateTokenPrefix, templateTokens } from './template-tokens';\n\nexport class MaterializationError extends Error {}\n\ntype MaterializationContext = {\n templateDirectory: string | URL;\n cwd?: string;\n signal?: AbortSignal;\n confirmOverwrite?: (destination: string) => Promise<boolean>;\n onFileCopied?: (path: string) => void;\n};\n\ntype TemplateFile = { source: string; relativePath: string; mode: number };\n\nconst pathExists = async (path: string) => {\n try {\n await lstat(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n};\n\nconst getPathStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n};\n\nconst assertNotAborted = (signal?: AbortSignal) => {\n if (signal?.aborted) throw new Error('Project creation was interrupted.');\n};\n\nconst resolveTemplateDirectory = (directory: string | URL) =>\n directory instanceof URL ? fileURLToPath(directory) : resolve(directory);\n\nconst resolveDestination = (destination: string, cwd: string) => {\n const segments = destination.split(/[\\\\/]/u);\n if (segments.includes('..')) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: parent traversal is not allowed.`,\n );\n }\n const resolved = isAbsolute(destination) ? resolve(destination) : resolve(cwd, destination);\n if (resolved === parse(resolved).root) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: a filesystem root is not allowed.`,\n );\n }\n return resolved;\n};\n\nconst listTemplateFiles = async (root: string, directory = root): Promise<TemplateFile[]> => {\n const entries = await readdir(directory, { withFileTypes: true });\n const files: TemplateFile[] = [];\n for (const entry of entries) {\n const source = join(directory, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listTemplateFiles(root, source)));\n continue;\n }\n if (!entry.isFile()) throw new MaterializationError(`Template entry ${source} is not a regular file.`);\n const relativePath = relative(root, source);\n files.push({\n source,\n relativePath: relativePath === '.gitignore.template' ? '.gitignore' : relativePath,\n mode: (await stat(source)).mode,\n });\n }\n return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));\n};\n\nconst isDirectoryEmpty = async (directory: string) => (await readdir(directory)).length === 0;\n\nconst ensureDirectory = async (directory: string, created: Set<string>) => {\n const existing = await getPathStat(directory);\n if (existing) {\n if (existing.isSymbolicLink() || !existing.isDirectory()) {\n throw new Error(`Unsafe destination entry ${directory}: expected a regular directory.`);\n }\n return;\n }\n const parent = dirname(directory);\n if (parent !== directory) await ensureDirectory(parent, created);\n await mkdir(directory);\n created.add(directory);\n};\n\nconst quoteTypeScriptString = (value: string) =>\n `'${value\n .replaceAll('\\\\', '\\\\\\\\')\n .replaceAll(\"'\", \"\\\\'\")\n .replaceAll('\\u2028', '\\\\u2028')\n .replaceAll('\\u2029', '\\\\u2029')}'`;\n\nconst replaceToken = (source: string, token: string, value: string, field: string) => {\n const quotedToken = `'${token}'`;\n if (!source.includes(quotedToken)) throw new Error(`Packaged template is missing the ${field} token.`);\n return source.replaceAll(quotedToken, quoteTypeScriptString(value));\n};\n\nconst replaceRawToken = (source: string, token: string, value: string, field: string) => {\n if (!source.includes(token)) throw new Error(`Packaged template is missing the ${field} token.`);\n return source.replaceAll(token, value);\n};\n\nconst customizeTemplate = async (projectRoot: string, repositoryRoot: string, options: GeneratorOptions) => {\n const relativeProjectDirectory = relative(repositoryRoot, projectRoot);\n const projectDirectory = relativeProjectDirectory.split(sep).join('/') || '.';\n const projectPathFilter = projectDirectory === '.' ? '**' : `${projectDirectory}/**`;\n const dependabotDirectory = projectDirectory === '.' ? '/' : `/${projectDirectory}`;\n const packagePath = join(projectRoot, 'package.json');\n const packageLockPath = join(projectRoot, 'package-lock.json');\n const packageMetadata = JSON.parse(await readFile(packagePath, 'utf8')) as Record<string, unknown>;\n const packageLock = JSON.parse(await readFile(packageLockPath, 'utf8')) as {\n name?: string;\n packages?: Record<string, Record<string, unknown>>;\n };\n if (\n packageMetadata.name !== templateTokens.packageName ||\n packageLock.name !== templateTokens.packageName ||\n packageLock.packages?.['']?.name !== templateTokens.packageName\n ) {\n throw new Error('Packaged template contains unexpected package metadata tokens.');\n }\n packageMetadata.name = options.packageName;\n packageMetadata.version = '0.0.0';\n packageMetadata.private = true;\n packageLock.name = options.packageName;\n if (!packageLock.packages?.['']) throw new Error('Packaged template lockfile is missing its root package metadata.');\n packageLock.packages[''].name = options.packageName;\n packageLock.packages[''].version = '0.0.0';\n await writeFile(packagePath, `${JSON.stringify(packageMetadata, undefined, 2)}\\n`);\n await writeFile(packageLockPath, `${JSON.stringify(packageLock, undefined, 2)}\\n`);\n\n const siteConfigPath = join(projectRoot, 'src', 'config', 'site.ts');\n let siteConfig = await readFile(siteConfigPath, 'utf8');\n siteConfig = replaceToken(siteConfig, templateTokens.siteName, options.siteName, 'site name');\n siteConfig = replaceToken(siteConfig, templateTokens.description, options.description, 'description');\n siteConfig = replaceToken(siteConfig, templateTokens.author, options.author, 'author');\n siteConfig = replaceToken(siteConfig, templateTokens.url, options.url, 'URL');\n siteConfig = replaceToken(siteConfig, templateTokens.repository, options.repository, 'repository');\n if (siteConfig.includes(templateTokenPrefix)) throw new Error('Packaged template contains unresolved tokens.');\n await writeFile(siteConfigPath, siteConfig);\n\n const scheduleMinutes = deriveScheduleMinutes(options.packageName);\n const securityWorkflowPath = join(repositoryRoot, '.github', 'workflows', 'security.yml');\n const automationWorkflowPath = join(repositoryRoot, '.github', 'workflows', 'automation.yml');\n let securityWorkflow = await readFile(securityWorkflowPath, 'utf8');\n let automationWorkflow = await readFile(automationWorkflowPath, 'utf8');\n securityWorkflow = replaceRawToken(\n securityWorkflow,\n templateTokens.securityScheduleMinute,\n String(scheduleMinutes.security),\n 'security schedule minute',\n );\n automationWorkflow = replaceRawToken(\n automationWorkflow,\n templateTokens.automationScheduleMinute,\n String(scheduleMinutes.automation),\n 'automation schedule minute',\n );\n await writeFile(securityWorkflowPath, securityWorkflow);\n await writeFile(automationWorkflowPath, automationWorkflow);\n\n const tokenizedFiles = [\n {\n path: join(repositoryRoot, '.github', 'workflows', 'project.yml'),\n replacements: [\n [templateTokens.projectDirectory, projectDirectory, 'project directory'],\n [templateTokens.projectPathFilter, projectPathFilter, 'project path filter'],\n ],\n },\n {\n path: securityWorkflowPath,\n replacements: [[templateTokens.projectPathFilter, projectPathFilter, 'project path filter']],\n },\n {\n path: join(repositoryRoot, '.github', 'actions', 'setup-project', 'action.yml'),\n replacements: [[templateTokens.projectDirectory, projectDirectory, 'project directory']],\n },\n {\n path: join(repositoryRoot, '.github', 'dependabot.yml'),\n replacements: [[templateTokens.dependabotDirectory, dependabotDirectory, 'Dependabot directory']],\n },\n ] as const;\n for (const file of tokenizedFiles) {\n let source = await readFile(file.path, 'utf8');\n for (const [token, value, field] of file.replacements) source = replaceRawToken(source, token, value, field);\n await writeFile(file.path, source);\n }\n};\n\nexport const materializeProject = async (options: GeneratorOptions, context: MaterializationContext) => {\n const cwd = context.cwd ?? process.cwd();\n const projectRoot = resolveDestination(options.destination, cwd);\n const repositoryRoot = resolveDestination(options.repositoryRoot, cwd);\n const projectPath = relative(repositoryRoot, projectRoot);\n if (projectPath.startsWith('..') || isAbsolute(projectPath)) {\n throw new MaterializationError(`Project root ${projectRoot} must be inside repository root ${repositoryRoot}.`);\n }\n const templateDirectory = resolveTemplateDirectory(context.templateDirectory);\n const repositoryStat = await getPathStat(repositoryRoot);\n if (repositoryStat && (repositoryStat.isSymbolicLink() || !repositoryStat.isDirectory())) {\n throw new MaterializationError(`Unsafe repository root ${repositoryRoot}: expected a regular directory.`);\n }\n const files = await listTemplateFiles(templateDirectory);\n const destinationExisted = await pathExists(projectRoot);\n if (destinationExisted) {\n const destinationStat = await lstat(projectRoot);\n if (destinationStat.isSymbolicLink() || !destinationStat.isDirectory()) {\n throw new MaterializationError(`Unsafe destination ${projectRoot}: expected a regular directory.`);\n }\n }\n if (destinationExisted && !(await isDirectoryEmpty(projectRoot))) {\n const confirmed = await context.confirmOverwrite?.(projectRoot);\n if (!confirmed) {\n throw new MaterializationError(`Destination ${projectRoot} is not empty. No files were changed.`);\n }\n }\n if (projectRoot !== repositoryRoot) {\n const repositoryFiles = files.filter((file) => file.relativePath.startsWith(`.github${sep}`));\n let hasRepositoryConflict = false;\n for (const file of repositoryFiles) {\n if (await pathExists(join(repositoryRoot, file.relativePath))) {\n hasRepositoryConflict = true;\n break;\n }\n }\n if (hasRepositoryConflict && !(await context.confirmOverwrite?.(repositoryRoot))) {\n throw new MaterializationError(\n `Repository root ${repositoryRoot} contains conflicting files. No files were changed.`,\n );\n }\n }\n const backupDirectory = await mkdtemp(join(tmpdir(), 'create-forge-backup-'));\n const createdFiles = new Set<string>();\n const createdDirectories = new Set<string>();\n const backups = new Map<string, string>();\n\n try {\n assertNotAborted(context.signal);\n await ensureDirectory(repositoryRoot, createdDirectories);\n await ensureDirectory(projectRoot, createdDirectories);\n for (const file of files) {\n assertNotAborted(context.signal);\n const targetRoot = file.relativePath.startsWith(`.github${sep}`) ? repositoryRoot : projectRoot;\n const target = join(targetRoot, file.relativePath);\n const targetRelative = relative(targetRoot, target);\n if (targetRelative.startsWith('..') || isAbsolute(targetRelative))\n throw new Error(`Unsafe template path ${file.relativePath}.`);\n await ensureDirectory(dirname(target), createdDirectories);\n const targetStat = await getPathStat(target);\n if (targetStat) {\n if (targetStat.isSymbolicLink() || !targetStat.isFile()) {\n throw new Error(`Unsafe destination entry ${target}: expected a regular file.`);\n }\n const backup = join(backupDirectory, file.relativePath);\n await mkdir(dirname(backup), { recursive: true });\n await copyFile(target, backup);\n backups.set(target, backup);\n } else {\n createdFiles.add(target);\n }\n await copyFile(file.source, target);\n await chmod(target, file.mode);\n context.onFileCopied?.(target);\n }\n assertNotAborted(context.signal);\n await customizeTemplate(projectRoot, repositoryRoot, options);\n const tokenBytes = Buffer.from(templateTokenPrefix);\n for (const file of files) {\n assertNotAborted(context.signal);\n const targetRoot = file.relativePath.startsWith(`.github${sep}`) ? repositoryRoot : projectRoot;\n if ((await readFile(join(targetRoot, file.relativePath))).includes(tokenBytes)) {\n throw new Error(`Generated file ${file.relativePath} contains an unresolved template token.`);\n }\n }\n return { destination: projectRoot, projectRoot, repositoryRoot, filesCopied: files.length };\n } catch (error) {\n const cleanupErrors: unknown[] = [];\n for (const [target, backup] of backups) {\n try {\n await copyFile(backup, target);\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const file of createdFiles) {\n try {\n await rm(file, { force: true });\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const directory of [...createdDirectories].sort((left, right) => right.length - left.length)) {\n try {\n await rmdir(directory);\n } catch (cleanupError) {\n if (!['ENOENT', 'ENOTEMPTY'].includes((cleanupError as NodeJS.ErrnoException).code ?? '')) {\n cleanupErrors.push(cleanupError);\n }\n }\n }\n const reason = error instanceof Error ? error.message : String(error);\n const recovery = cleanupErrors.length\n ? `Recovery: review ${repositoryRoot}; some changes could not be rolled back automatically.`\n : `Recovery: changes from this invocation were rolled back; resolve the error and retry.`;\n throw new MaterializationError(`${reason} ${recovery}`, { cause: error });\n } finally {\n await rm(backupDirectory, { recursive: true, force: true });\n }\n};\n","import { spawn } from 'node:child_process';\n\nimport type { GeneratorOptions } from './options';\n\nexport class ProcessStepError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1, options?: ErrorOptions) {\n super(message, options);\n this.exitCode = exitCode;\n }\n}\n\nexport type Command = {\n executable: string;\n arguments: readonly string[];\n cwd: string;\n signal?: AbortSignal;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport type CommandExecutor = (command: Command) => Promise<void>;\n\nconst signalExitCode = (signal: NodeJS.Signals | null) => {\n if (signal === 'SIGINT') return 130;\n if (signal === 'SIGTERM') return 143;\n return 1;\n};\n\nexport const executeCommand: CommandExecutor = ({ executable, arguments: args, cwd, signal, environment }) =>\n new Promise((resolve, reject) => {\n const child = spawn(executable, args, {\n cwd,\n stdio: 'inherit',\n ...(signal ? { signal } : {}),\n ...(environment ? { env: environment } : {}),\n });\n\n child.once('error', (error) => {\n const interrupted = signal?.aborted;\n reject(\n new ProcessStepError(\n interrupted\n ? `${executable} was interrupted. The generated project remains at ${cwd}.`\n : `Could not start ${executable}: ${error.message}. The generated project remains at ${cwd}.`,\n interrupted ? 130 : 1,\n { cause: error },\n ),\n );\n });\n child.once('close', (code, processSignal) => {\n if (code === 0) {\n resolve();\n return;\n }\n const detail = processSignal ? `signal ${processSignal}` : `exit code ${code ?? 'unknown'}`;\n reject(\n new ProcessStepError(\n `${executable} failed with ${detail}. The generated project remains at ${cwd}; review the output above and retry the command there.`,\n processSignal ? signalExitCode(processSignal) : (code ?? 1),\n ),\n );\n });\n });\n\ntype ProjectSetupContext = {\n projectRoot: string;\n repositoryRoot: string;\n signal?: AbortSignal;\n execute?: CommandExecutor;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport const runProjectSetup = async (options: GeneratorOptions, context: ProjectSetupContext) => {\n const execute = context.execute ?? executeCommand;\n const command = (executable: string, args: readonly string[]) =>\n execute({\n executable,\n arguments: args,\n cwd: context.projectRoot,\n ...(context.signal ? { signal: context.signal } : {}),\n ...(context.environment ? { environment: context.environment } : {}),\n });\n\n if (options.install) await command('npm', ['install']);\n if (options.git) {\n await execute({\n executable: 'git',\n arguments: ['init', '--initial-branch=main'],\n cwd: context.repositoryRoot,\n ...(context.signal ? { signal: context.signal } : {}),\n ...(context.environment ? { environment: context.environment } : {}),\n });\n }\n};\n","import { isAbsolute, relative } from 'node:path';\n\nimport type { GeneratorOptions } from './options';\n\ntype CompletionContext = {\n destination: string;\n cwd?: string;\n color?: boolean;\n};\n\nconst ansi = {\n bold: (value: string) => `\\u001B[1m${value}\\u001B[22m`,\n green: (value: string) => `\\u001B[32m${value}\\u001B[39m`,\n};\n\nconst commandPath = (path: string) => {\n if (/^[\\w./@-]+$/u.test(path)) return path;\n return `\"${path.replaceAll('\"', '\\\\\"')}\"`;\n};\n\nconst displayDestination = (destination: string, cwd: string) => {\n const path = relative(cwd, destination);\n if (!path) return '.';\n return isAbsolute(path) ? destination : path;\n};\n\nexport const supportsColor = (environment: NodeJS.ProcessEnv = process.env, terminal = Boolean(process.stdout.isTTY)) =>\n terminal && environment.NO_COLOR === undefined;\n\nexport const formatCompletion = (options: GeneratorOptions, context: CompletionContext) => {\n const cwd = context.cwd ?? process.cwd();\n const color = context.color ?? supportsColor();\n const decorate = color ? ansi : { bold: (value: string) => value, green: (value: string) => value };\n const destination = displayDestination(context.destination, cwd);\n const commands = [\n ...(destination === '.' ? [] : [`cd ${commandPath(destination)}`]),\n ...(options.install ? [] : ['npm install']),\n 'npm run dev',\n ];\n\n return `${decorate.green('✓')} Created ${options.siteName} in ${destination}\\n\\n${decorate.bold('Setup')}\\n Dependencies: ${options.install ? 'installed' : 'skipped'}\\n Git repository: ${options.git ? 'initialized' : 'skipped'}\\n\\n${decorate.bold('Next steps')}\\n${commands.map((command) => ` ${command}`).join('\\n')}\\n`;\n};\n","#!/usr/bin/env node\n\nimport { readFile } from 'node:fs/promises';\n\nimport { confirm, isCancel } from '@clack/prompts';\n\nimport { runCli } from './cli';\nimport { materializeProject } from './materialize';\nimport { ProcessStepError, runProjectSetup } from './process';\nimport { formatCompletion } from './reporter';\n\ntype PackageMetadata = {\n version: string;\n};\n\nconst packageJson = new URL('../package.json', import.meta.url);\nconst metadata = JSON.parse(await readFile(packageJson, 'utf8')) as PackageMetadata;\nconst interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);\nconst result = await runCli(process.argv.slice(2), metadata.version, { interactive });\n\nif (result.options) {\n const controller = new AbortController();\n let interruptedExitCode: number | undefined;\n const interrupt = () => {\n interruptedExitCode = 130;\n controller.abort();\n };\n const terminate = () => {\n interruptedExitCode = 143;\n controller.abort();\n };\n process.once('SIGINT', interrupt);\n process.once('SIGTERM', terminate);\n try {\n const materialized = await materializeProject(result.options, {\n templateDirectory: new URL('./template/', import.meta.url),\n signal: controller.signal,\n ...(interactive\n ? {\n confirmOverwrite: async (destination: string) => {\n const answer = await confirm({\n message: `${destination} is not empty. Continue and overwrite conflicting template files?`,\n initialValue: false,\n });\n return !isCancel(answer) && answer;\n },\n }\n : {}),\n });\n await runProjectSetup(result.options, {\n projectRoot: materialized.projectRoot,\n repositoryRoot: materialized.repositoryRoot,\n signal: controller.signal,\n });\n result.output = formatCompletion(result.options, { destination: materialized.projectRoot });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n result.output = `error: ${message}\\n`;\n result.exitCode = interruptedExitCode ?? (error instanceof ProcessStepError ? error.exitCode : 1);\n } finally {\n process.off('SIGINT', interrupt);\n process.off('SIGTERM', terminate);\n }\n}\n\nprocess.stdout.write(result.output);\nprocess.exitCode = result.exitCode;\n"],"mappings":"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"}
|
|
@@ -7,6 +7,10 @@ inputs:
|
|
|
7
7
|
description: Node.js version to install
|
|
8
8
|
required: false
|
|
9
9
|
default: '26.x'
|
|
10
|
+
project-directory:
|
|
11
|
+
description: Project directory relative to the repository root
|
|
12
|
+
required: false
|
|
13
|
+
default: __RM_INDUSTRIES_FORGE_TEMPLATE_V1_PROJECT_DIRECTORY__
|
|
10
14
|
|
|
11
15
|
runs:
|
|
12
16
|
using: composite
|
|
@@ -17,8 +21,9 @@ runs:
|
|
|
17
21
|
with:
|
|
18
22
|
node-version: ${{ inputs.node-version }}
|
|
19
23
|
cache: npm
|
|
20
|
-
cache-dependency-path: package-lock.json
|
|
24
|
+
cache-dependency-path: ${{ inputs.project-directory }}/package-lock.json
|
|
21
25
|
|
|
22
26
|
- name: Install dependencies
|
|
23
27
|
shell: bash
|
|
24
28
|
run: npm ci
|
|
29
|
+
working-directory: ${{ inputs.project-directory }}
|
|
@@ -57,23 +57,3 @@ jobs:
|
|
|
57
57
|
|
|
58
58
|
- name: Validate workflows
|
|
59
59
|
uses: raven-actions/actionlint@3d39aea434753780c3b3d4a1a31c854b4dbf49d7 # v2
|
|
60
|
-
|
|
61
|
-
automation:
|
|
62
|
-
name: Automation
|
|
63
|
-
needs:
|
|
64
|
-
- security
|
|
65
|
-
- syntax
|
|
66
|
-
if: always()
|
|
67
|
-
runs-on: ubuntu-latest
|
|
68
|
-
|
|
69
|
-
steps:
|
|
70
|
-
- name: Confirm automation checks
|
|
71
|
-
env:
|
|
72
|
-
SECURITY_RESULT: ${{ needs.security.result }}
|
|
73
|
-
SYNTAX_RESULT: ${{ needs.syntax.result }}
|
|
74
|
-
run: |
|
|
75
|
-
if [[ "$SECURITY_RESULT" != 'success' || "$SYNTAX_RESULT" != 'success' ]]; then
|
|
76
|
-
echo '::error title=Automation validation failed::Workflow security and syntax checks must both pass.'
|
|
77
|
-
exit 1
|
|
78
|
-
fi
|
|
79
|
-
echo 'Workflow security and syntax checks passed.'
|