create-berna-stencil 2.6.0 → 2.6.1

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.
Files changed (179) hide show
  1. package/.gitignore +5 -4
  2. package/README.md +1 -6
  3. package/_tools/assistant.js +92 -88
  4. package/_tools/modules/constants.js +23 -0
  5. package/_tools/modules/pageComponents.js +77 -0
  6. package/_tools/modules/updateData.js +25 -44
  7. package/_tools/modules/updateOutputPath.js +59 -63
  8. package/_tools/modules/updatePage.js +131 -104
  9. package/_tools/modules/utils.js +16 -19
  10. package/_tools/modules/validation.js +20 -0
  11. package/bin/create.js +75 -13
  12. package/package.json +50 -65
  13. package/src/backend/_core/vendor/composer/autoload_static.php +10 -10
  14. package/src/backend/_core/vendor/composer/installed.json +6 -6
  15. package/src/backend/_core/vendor/composer/installed.php +2 -2
  16. package/src/backend/_core/vendor/graham-campbell/result-type/.gitattributes +9 -0
  17. package/src/backend/_core/vendor/graham-campbell/result-type/.github/CODE_OF_CONDUCT.md +132 -0
  18. package/src/backend/_core/vendor/graham-campbell/result-type/.github/CONTRIBUTING.md +31 -0
  19. package/src/backend/_core/vendor/graham-campbell/result-type/.github/FUNDING.yml +2 -0
  20. package/src/backend/_core/vendor/graham-campbell/result-type/.github/SECURITY.md +14 -0
  21. package/src/backend/_core/vendor/graham-campbell/result-type/.github/workflows/stale.yml +11 -0
  22. package/src/backend/_core/vendor/graham-campbell/result-type/.github/workflows/tests.yml +40 -0
  23. package/src/backend/_core/vendor/graham-campbell/result-type/CHANGELOG.md +53 -0
  24. package/src/backend/_core/vendor/graham-campbell/result-type/LICENSE +21 -21
  25. package/src/backend/_core/vendor/graham-campbell/result-type/README.md +42 -0
  26. package/src/backend/_core/vendor/graham-campbell/result-type/composer.json +33 -33
  27. package/src/backend/_core/vendor/graham-campbell/result-type/phpunit.xml.dist +13 -0
  28. package/src/backend/_core/vendor/graham-campbell/result-type/src/Error.php +121 -121
  29. package/src/backend/_core/vendor/graham-campbell/result-type/src/Result.php +69 -69
  30. package/src/backend/_core/vendor/graham-campbell/result-type/src/Success.php +120 -120
  31. package/src/backend/_core/vendor/graham-campbell/result-type/tests/ResultTest.php +95 -0
  32. package/src/backend/_core/vendor/phpoption/phpoption/.gitattributes +13 -0
  33. package/src/backend/_core/vendor/phpoption/phpoption/.github/CODE_OF_CONDUCT.md +132 -0
  34. package/src/backend/_core/vendor/phpoption/phpoption/.github/CONTRIBUTING.md +30 -0
  35. package/src/backend/_core/vendor/phpoption/phpoption/.github/FUNDING.yml +2 -0
  36. package/src/backend/_core/vendor/phpoption/phpoption/.github/SECURITY.md +14 -0
  37. package/src/backend/_core/vendor/phpoption/phpoption/.github/workflows/static.yml +40 -0
  38. package/src/backend/_core/vendor/phpoption/phpoption/.github/workflows/tests.yml +40 -0
  39. package/src/backend/_core/vendor/phpoption/phpoption/LICENSE +200 -200
  40. package/src/backend/_core/vendor/phpoption/phpoption/Makefile +17 -0
  41. package/src/backend/_core/vendor/phpoption/phpoption/README.md +201 -0
  42. package/src/backend/_core/vendor/phpoption/phpoption/composer.json +50 -50
  43. package/src/backend/_core/vendor/phpoption/phpoption/phpstan-baseline.neon +44 -0
  44. package/src/backend/_core/vendor/phpoption/phpoption/phpstan.neon.dist +7 -0
  45. package/src/backend/_core/vendor/phpoption/phpoption/phpunit.xml.dist +13 -0
  46. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php +175 -175
  47. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/None.php +136 -136
  48. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Option.php +434 -434
  49. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Some.php +169 -169
  50. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/EnsureTest.php +72 -0
  51. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/LazyOptionTest.php +357 -0
  52. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/NoneTest.php +153 -0
  53. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/OptionTest.php +166 -0
  54. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/SomeTest.php +194 -0
  55. package/src/backend/_core/vendor/phpoption/phpoption/tests/bootstrap.php +8 -0
  56. package/src/backend/_core/vendor/phpoption/phpoption/vendor-bin/phpstan/composer.json +8 -0
  57. package/src/backend/_core/vendor/symfony/polyfill-ctype/Ctype.php +232 -232
  58. package/src/backend/_core/vendor/symfony/polyfill-ctype/LICENSE +19 -19
  59. package/src/backend/_core/vendor/symfony/polyfill-ctype/README.md +12 -12
  60. package/src/backend/_core/vendor/symfony/polyfill-ctype/bootstrap.php +50 -50
  61. package/src/backend/_core/vendor/symfony/polyfill-ctype/bootstrap80.php +46 -46
  62. package/src/backend/_core/vendor/symfony/polyfill-ctype/composer.json +38 -38
  63. package/src/backend/_core/vendor/symfony/polyfill-mbstring/LICENSE +19 -19
  64. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Mbstring.php +1077 -1077
  65. package/src/backend/_core/vendor/symfony/polyfill-mbstring/README.md +13 -13
  66. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php +119 -119
  67. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php +1397 -1397
  68. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php +5 -5
  69. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php +1489 -1489
  70. package/src/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap.php +171 -171
  71. package/src/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap80.php +167 -167
  72. package/src/backend/_core/vendor/symfony/polyfill-mbstring/composer.json +39 -39
  73. package/src/backend/_core/vendor/symfony/polyfill-php80/LICENSE +19 -19
  74. package/src/backend/_core/vendor/symfony/polyfill-php80/Php80.php +115 -115
  75. package/src/backend/_core/vendor/symfony/polyfill-php80/PhpToken.php +106 -106
  76. package/src/backend/_core/vendor/symfony/polyfill-php80/README.md +25 -25
  77. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php +31 -31
  78. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php +16 -16
  79. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php +20 -20
  80. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php +16 -16
  81. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php +16 -16
  82. package/src/backend/_core/vendor/symfony/polyfill-php80/bootstrap.php +42 -42
  83. package/src/backend/_core/vendor/symfony/polyfill-php80/composer.json +37 -37
  84. package/src/backend/_core/vendor/vlucas/phpdotenv/.editorconfig +15 -0
  85. package/src/backend/_core/vendor/vlucas/phpdotenv/.gitattributes +15 -0
  86. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/CODE_OF_CONDUCT.md +132 -0
  87. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/CONTRIBUTING.md +30 -0
  88. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/FUNDING.yml +2 -0
  89. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/SECURITY.md +14 -0
  90. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/workflows/static.yml +40 -0
  91. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/workflows/tests.yml +70 -0
  92. package/src/backend/_core/vendor/vlucas/phpdotenv/LICENSE +30 -30
  93. package/src/backend/_core/vendor/vlucas/phpdotenv/Makefile +17 -0
  94. package/src/backend/_core/vendor/vlucas/phpdotenv/README.md +370 -0
  95. package/src/backend/_core/vendor/vlucas/phpdotenv/UPGRADING.md +196 -0
  96. package/src/backend/_core/vendor/vlucas/phpdotenv/composer.json +60 -60
  97. package/src/backend/_core/vendor/vlucas/phpdotenv/phpstan-baseline.neon +157 -0
  98. package/src/backend/_core/vendor/vlucas/phpdotenv/phpstan.neon.dist +7 -0
  99. package/src/backend/_core/vendor/vlucas/phpdotenv/phpunit.xml.dist +13 -0
  100. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Dotenv.php +267 -267
  101. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php +12 -12
  102. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php +12 -12
  103. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php +12 -12
  104. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php +12 -12
  105. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ValidationException.php +12 -12
  106. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Loader.php +48 -48
  107. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php +20 -20
  108. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Resolver.php +65 -65
  109. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Entry.php +59 -59
  110. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php +299 -299
  111. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lexer.php +58 -58
  112. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lines.php +127 -127
  113. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Parser.php +53 -53
  114. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php +19 -19
  115. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Value.php +88 -88
  116. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php +15 -15
  117. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php +89 -89
  118. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php +80 -80
  119. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php +88 -88
  120. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php +85 -85
  121. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php +110 -110
  122. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php +48 -48
  123. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php +64 -64
  124. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php +91 -91
  125. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php +17 -17
  126. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php +104 -104
  127. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php +88 -88
  128. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php +27 -27
  129. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php +107 -107
  130. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php +272 -272
  131. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php +51 -51
  132. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Paths.php +44 -44
  133. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Reader.php +81 -81
  134. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/FileStore.php +72 -72
  135. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php +141 -141
  136. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php +17 -17
  137. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/StringStore.php +37 -37
  138. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Util/Regex.php +112 -112
  139. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Util/Str.php +108 -108
  140. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Validator.php +207 -207
  141. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/DotenvTest.php +387 -0
  142. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Loader/LoaderTest.php +86 -0
  143. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/EntryParserTest.php +234 -0
  144. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LexerTest.php +40 -0
  145. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LinesTest.php +53 -0
  146. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/ParserTest.php +98 -0
  147. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php +57 -0
  148. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php +75 -0
  149. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php +52 -0
  150. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php +75 -0
  151. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/RepositoryTest.php +305 -0
  152. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Store/StoreTest.php +141 -0
  153. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/ValidatorTest.php +479 -0
  154. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/.env +5 -0
  155. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/assertions.env +18 -0
  156. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/booleans.env +33 -0
  157. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/commented.env +15 -0
  158. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/empty.env +1 -0
  159. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/example.env +1 -0
  160. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/exported.env +7 -0
  161. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/immutable.env +1 -0
  162. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/integers.env +17 -0
  163. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/large.env +2 -0
  164. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multibyte.env +3 -0
  165. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiline.env +14 -0
  166. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiple.env +4 -0
  167. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/mutable.env +1 -0
  168. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/nested.env +15 -0
  169. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/quoted.env +11 -0
  170. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/specialchars.env +8 -0
  171. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/unicodevarnames.env +2 -0
  172. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/utf8-with-bom-encoding.env +3 -0
  173. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/windows.env +1 -0
  174. package/src/backend/_core/vendor/vlucas/phpdotenv/vendor-bin/phpstan/composer.json +15 -0
  175. package/src/backend/{config.example.php → config.php} +4 -13
  176. package/src/frontend/data/site.json +44 -44
  177. package/_tools/buildJs.js +0 -28
  178. package/_tools/cleanOutput.js +0 -20
  179. package/_tools/modules/updateIncludes.js +0 -81
@@ -1,113 +1,109 @@
1
- const fs = require('fs');
1
+ const fs = require('fs');
2
2
  const path = require('path');
3
+ const { PATHS } = require('./constants');
3
4
  const { isTypeScriptProject } = require('./utils');
4
5
 
5
- const ELEVENTY_CONFIG = path.resolve(__dirname, '../../.eleventy.js');
6
- const PACKAGE_JSON = path.resolve(__dirname, '../../package.json');
7
- const TSCONFIG = path.resolve(__dirname, '../../tsconfig.json');
8
-
9
6
  const OUTPUT_DIR_REGEX = /const OUTPUT_DIR\s*=\s*['"`]([^'"`]*)['"`]/;
10
-
11
- // --- Helpers ---
7
+ const TSCONFIG_OUTDIR_REGEX = /"outDir"\s*:\s*"[^"]*"/;
12
8
 
13
9
  function parseOutputDir(content) {
14
10
  const match = content.match(OUTPUT_DIR_REGEX);
15
11
  return match ? match[1] : null;
16
12
  }
17
13
 
18
- // --- Updaters ---
19
-
20
14
  function updateEleventyConfig(newPath) {
21
- const content = fs.readFileSync(ELEVENTY_CONFIG, 'utf-8');
22
-
23
- const updated = content.replace(
24
- OUTPUT_DIR_REGEX,
25
- `const OUTPUT_DIR = "${newPath}"`
26
- );
15
+ const content = fs.readFileSync(PATHS.eleventyConfig, 'utf8');
16
+ const updated = content.replace(OUTPUT_DIR_REGEX, `const OUTPUT_DIR = "${newPath}"`);
27
17
 
28
18
  if (content === updated) {
29
- console.log('(!) OUTPUT_DIR not found in .eleventy.js');
30
- return false;
19
+ console.log('[skip] OUTPUT_DIR not found in .eleventy.js');
20
+ return;
31
21
  }
32
-
33
- fs.writeFileSync(ELEVENTY_CONFIG, updated, 'utf-8');
34
- console.log(`(✓) .eleventy.js updated → ${newPath}`);
35
- return true;
22
+ fs.writeFileSync(PATHS.eleventyConfig, updated);
23
+ console.log(`[updated] .eleventy.js → ${newPath}`);
36
24
  }
37
25
 
38
26
  function updatePackageJson(newPath) {
39
- const pkg = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf-8'));
27
+ const pkg = JSON.parse(fs.readFileSync(PATHS.packageJson, 'utf8'));
28
+ const scriptGlob = isTypeScriptProject()
29
+ ? 'src/frontend/ts/pages/*.ts'
30
+ : 'src/frontend/js/pages/*.js';
40
31
 
41
32
  pkg.outputDir = newPath;
42
-
43
- const usesTs = isTypeScriptProject();
44
-
33
+ pkg.scripts = pkg.scripts || {};
45
34
  pkg.scripts['build:css'] = `sass src/frontend/scss:${newPath}/css --no-source-map --style=compressed --quiet`;
46
35
  pkg.scripts['serve:css'] = `sass --watch src/frontend/scss:${newPath}/css --no-source-map --quiet`;
36
+ pkg.scripts['build:js'] = `esbuild "${scriptGlob}" --bundle --outdir=${newPath}/js/pages --minify`;
37
+ pkg.scripts['serve:js'] = `esbuild "${scriptGlob}" --bundle --outdir=${newPath}/js/pages --watch`;
47
38
 
48
- if (usesTs) {
49
- pkg.scripts['build:js'] = `esbuild "src/frontend/ts/pages/*.ts" --bundle --outdir=${newPath}/js/pages --minify`;
50
- pkg.scripts['serve:js'] = `esbuild "src/frontend/ts/pages/*.ts" --bundle --outdir=${newPath}/js/pages --watch`;
51
- } else {
52
- pkg.scripts['build:js'] = `esbuild "src/frontend/js/pages/*.js" --bundle --outdir=${newPath}/js/pages --minify`;
53
- pkg.scripts['serve:js'] = `esbuild "src/frontend/js/pages/*.js" --bundle --outdir=${newPath}/js/pages --watch`;
54
- }
55
-
56
- fs.writeFileSync(PACKAGE_JSON, JSON.stringify(pkg, null, 2), 'utf-8');
57
- console.log(`(✓) package.json updated → ${newPath}`);
58
- return true;
39
+ fs.writeFileSync(PATHS.packageJson, `${JSON.stringify(pkg, null, 2)}\n`);
40
+ console.log(`[updated] package.json ${newPath}`);
59
41
  }
60
42
 
61
- const TSCONFIG_OUTDIR_REGEX = /"outDir"\s*:\s*"[^"]*"/;
62
-
63
43
  function updateTsConfig(newPath) {
64
44
  if (!isTypeScriptProject()) return;
65
45
 
66
- const content = fs.readFileSync(TSCONFIG, 'utf-8');
46
+ const content = fs.readFileSync(PATHS.tsconfig, 'utf8');
67
47
  const updated = content.replace(TSCONFIG_OUTDIR_REGEX, `"outDir": "./${newPath}/ts"`);
68
48
 
69
49
  if (content === updated) {
70
- console.log('(!) outDir not found in tsconfig.json');
50
+ console.log('[skip] outDir not found in tsconfig.json');
71
51
  return;
72
52
  }
73
-
74
- fs.writeFileSync(TSCONFIG, updated, 'utf-8');
75
- console.log(`(✓) tsconfig.json updated → ${newPath}/ts`);
53
+ fs.writeFileSync(PATHS.tsconfig, updated);
54
+ console.log(`[updated] tsconfig.json → ${newPath}/ts`);
76
55
  }
77
56
 
78
- // --- Public API ---
57
+ function deleteOldOutput(oldPath) {
58
+ if (!oldPath) return;
79
59
 
80
- function updateOutputPath(newPath) {
81
- const trimmed = newPath.trim().replace(/\\/g, '/');
60
+ const oldAbsPath = path.resolve(PATHS.root, oldPath);
61
+ const insideProject = oldAbsPath.startsWith(PATHS.root + path.sep);
62
+
63
+ if (!insideProject || oldAbsPath === PATHS.root) {
64
+ console.log(`[skip] refusing to delete "${oldAbsPath}" (outside project)`);
65
+ return;
66
+ }
67
+ if (fs.existsSync(oldAbsPath)) {
68
+ fs.rmSync(oldAbsPath, { recursive: true, force: true });
69
+ console.log(`[deleted] ${oldAbsPath}`);
70
+ }
71
+ }
72
+
73
+ function updateOutputPath(rawPath) {
74
+ const trimmed = (rawPath ?? '').trim().replace(/\\/g, '/');
75
+ if (!trimmed) {
76
+ console.log('[skip] empty output path');
77
+ return;
78
+ }
82
79
 
83
80
  const normalizedPath = trimmed === '.'
84
81
  ? 'out'
85
82
  : `${trimmed.replace(/\/$/, '')}/${path.basename(process.cwd())}-out`;
86
83
 
87
- const eleventyContent = fs.readFileSync(ELEVENTY_CONFIG, 'utf-8');
88
- const oldPath = parseOutputDir(eleventyContent);
89
-
90
- if (oldPath) {
91
- const oldAbsPath = path.resolve(__dirname, '../../', oldPath);
92
- if (fs.existsSync(oldAbsPath)) {
93
- fs.rmSync(oldAbsPath, { recursive: true, force: true });
94
- console.log(`(✓) folder deleted → ${oldAbsPath}`);
95
- } else {
96
- console.log(`(i) folder not found, nothing to delete → ${oldAbsPath}`);
97
- }
84
+ let oldPath = null;
85
+ try {
86
+ oldPath = parseOutputDir(fs.readFileSync(PATHS.eleventyConfig, 'utf8'));
87
+ } catch (err) {
88
+ console.log(`[error] cannot read .eleventy.js: ${err.message}`);
89
+ return;
98
90
  }
99
91
 
100
- console.log(`\nupdating output path → "${normalizedPath}"...`);
92
+ deleteOldOutput(oldPath);
101
93
 
102
- updatePackageJson(normalizedPath);
103
- updateEleventyConfig(normalizedPath);
104
- updateTsConfig(normalizedPath);
94
+ console.log(`\nupdating output path → "${normalizedPath}"`);
95
+ try {
96
+ updatePackageJson(normalizedPath);
97
+ updateEleventyConfig(normalizedPath);
98
+ updateTsConfig(normalizedPath);
99
+ } catch (err) {
100
+ console.log(`[error] could not update output path: ${err.message}`);
101
+ }
105
102
  }
106
103
 
107
104
  function getCurrentOutputPath() {
108
105
  try {
109
- const content = fs.readFileSync(ELEVENTY_CONFIG, 'utf-8');
110
- return parseOutputDir(content);
106
+ return parseOutputDir(fs.readFileSync(PATHS.eleventyConfig, 'utf8'));
111
107
  } catch {
112
108
  return null;
113
109
  }
@@ -1,135 +1,162 @@
1
- const fileSystem = require('fs');
1
+ const fs = require('fs');
2
2
  const path = require('path');
3
3
 
4
+ const { PATHS } = require('./constants');
5
+ const { toCamelCase, isTypeScriptProject } = require('./utils');
4
6
  const { addSiteData, removeSiteData, renameSiteData } = require('./updateData');
5
- const { addLayout, removeLayout, renameLayout } = require('./updateIncludes');
7
+ const { addPageBlock, removePageBlock, renamePageBlock } = require('./pageComponents');
6
8
  const { getCurrentOutputPath } = require('./updateOutputPath');
7
- const { toCamelCase, isTypeScriptProject } = require('./utils');
8
-
9
- const TEMPLATES_DIR = path.join(__dirname, '..', 'res', 'templates');
10
-
11
- function getPageTargets(pageName) {
12
- const camelName = toCamelCase(pageName);
13
- const usesTs = isTypeScriptProject();
14
9
 
15
- return [
16
- {
17
- folder: 'src/frontend/scss/pages',
18
- templateFile: 'template.scss',
19
- fileName: `${camelName}.scss`,
20
- },
21
- {
22
- folder: usesTs ? 'src/frontend/ts/pages' : 'src/frontend/js/pages',
23
- templateFile: usesTs ? 'template.ts' : 'template.js',
24
- fileName: usesTs ? `${camelName}.ts` : `${camelName}.js`,
10
+ function getPageArtifacts(pageName) {
11
+ const camelName = toCamelCase(pageName);
12
+ const usesTs = isTypeScriptProject();
13
+ const scriptDir = usesTs ? PATHS.tsPages : PATHS.jsPages;
14
+ const scriptExt = usesTs ? 'ts' : 'js';
15
+ const outputRoot = path.join(PATHS.root, getCurrentOutputPath() || 'out');
16
+
17
+ return {
18
+ camelName,
19
+ source: {
20
+ scss: path.join(PATHS.scssPages, `${camelName}.scss`),
21
+ script: path.join(scriptDir, `${camelName}.${scriptExt}`),
22
+ route: path.join(PATHS.routes, `${pageName}.njk`),
25
23
  },
26
- {
27
- folder: 'src/frontend/_routes',
28
- templateFile: 'template.njk',
29
- fileName: `${pageName}.njk`,
24
+ output: {
25
+ files: [
26
+ path.join(outputRoot, 'js', 'pages', `${camelName}.js`),
27
+ path.join(outputRoot, 'css', 'pages', `${camelName}.css`),
28
+ path.join(outputRoot, `${pageName}.html`),
29
+ path.join(outputRoot, 'pages', `${pageName}.html`),
30
+ ],
31
+ folders: [
32
+ path.join(outputRoot, pageName),
33
+ path.join(outputRoot, 'pages', pageName),
34
+ ],
30
35
  },
31
- ];
36
+ };
32
37
  }
33
38
 
34
- function addPage(pageName) {
35
- const camelName = toCamelCase(pageName);
36
-
37
- getPageTargets(pageName).forEach(({ folder, templateFile, fileName }) => {
38
- const destPath = path.join(folder, fileName);
39
- fileSystem.mkdirSync(folder, { recursive: true });
40
-
41
- if (fileSystem.existsSync(destPath)) return;
42
-
43
- const srcPath = path.join(TEMPLATES_DIR, templateFile);
39
+ function pageExists(pageName) {
40
+ return fs.existsSync(path.join(PATHS.routes, `${pageName}.njk`));
41
+ }
44
42
 
45
- if (templateFile === 'template.njk') {
46
- const content = fileSystem.readFileSync(srcPath, 'utf8')
47
- .replace(/^title:.*$/m, `title: "${camelName}"`)
48
- .replace(/^permalink:.*$/m, `permalink: "/${pageName}/"`);
49
- fileSystem.writeFileSync(destPath, content);
50
- } else {
51
- fileSystem.copyFileSync(srcPath, destPath);
52
- }
43
+ function applyRouteFrontMatter(filePath, camelName, pageName) {
44
+ const content = fs.readFileSync(filePath, 'utf8')
45
+ .replace(/^title:.*$/m, `title: "${camelName}"`)
46
+ .replace(/^permalink:.*$/m, `permalink: "/${pageName}/"`);
47
+ fs.writeFileSync(filePath, content);
48
+ }
53
49
 
54
- console.log(`[created file] ${destPath}`);
55
- });
50
+ function addPage(pageName) {
51
+ if (pageExists(pageName)) {
52
+ console.log(`[skip] page "${pageName}" already exists`);
53
+ return;
54
+ }
55
+
56
+ const { camelName, source } = getPageArtifacts(pageName);
57
+ const usesTs = isTypeScriptProject();
58
+
59
+ const creations = [
60
+ { dest: source.scss, template: 'template.scss', isRoute: false },
61
+ { dest: source.script, template: usesTs ? 'template.ts' : 'template.js', isRoute: false },
62
+ { dest: source.route, template: 'template.njk', isRoute: true },
63
+ ];
56
64
 
57
- addLayout(pageName);
65
+ try {
66
+ creations.forEach(({ dest, template, isRoute }) => {
67
+ const templatePath = path.join(PATHS.templates, template);
68
+ if (!fs.existsSync(templatePath)) {
69
+ console.log(`[skip] template not found: ${templatePath}`);
70
+ return;
71
+ }
72
+
73
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
74
+ fs.copyFileSync(templatePath, dest);
75
+ if (isRoute) applyRouteFrontMatter(dest, camelName, pageName);
76
+
77
+ console.log(`[created] ${dest}`);
78
+ });
79
+ } catch (err) {
80
+ console.log(`[error] could not create page files: ${err.message}`);
81
+ return;
82
+ }
83
+
84
+ addPageBlock(pageName);
58
85
  addSiteData(pageName);
59
86
  }
60
87
 
61
88
  function renamePage(oldName, newName) {
62
- const oldCamel = toCamelCase(oldName);
63
- const newCamel = toCamelCase(newName);
64
- const usesTs = isTypeScriptProject();
65
- const ext = usesTs ? 'ts' : 'js';
66
- const jsFolder = usesTs ? 'src/frontend/ts/pages' : 'src/frontend/js/pages';
67
-
68
- const filesToRename = [
69
- { src: `src/frontend/scss/pages/${oldCamel}.scss`, dest: `src/frontend/scss/pages/${newCamel}.scss` },
70
- { src: `${jsFolder}/${oldCamel}.${ext}`, dest: `${jsFolder}/${newCamel}.${ext}` },
71
- { src: `src/frontend/_routes/${oldName}.njk`, dest: `src/frontend/_routes/${newName}.njk` },
89
+ if (!pageExists(oldName)) {
90
+ console.log(`[skip] page "${oldName}" does not exist`);
91
+ return;
92
+ }
93
+ if (pageExists(newName)) {
94
+ console.log(`[skip] target page "${newName}" already exists`);
95
+ return;
96
+ }
97
+
98
+ const oldArtifacts = getPageArtifacts(oldName);
99
+ const newArtifacts = getPageArtifacts(newName);
100
+
101
+ const moves = [
102
+ { src: oldArtifacts.source.scss, dest: newArtifacts.source.scss },
103
+ { src: oldArtifacts.source.script, dest: newArtifacts.source.script },
104
+ { src: oldArtifacts.source.route, dest: newArtifacts.source.route },
72
105
  ];
73
106
 
74
- filesToRename.forEach(({ src, dest }) => {
75
- if (!fileSystem.existsSync(src)) {
76
- console.log(`[skip] not found: ${src}`);
77
- return;
78
- }
79
-
80
- fileSystem.renameSync(src, dest);
81
- console.log(`[renamed] ${src} → ${dest}`);
82
-
83
- if (dest.endsWith('.njk')) {
84
- const content = fileSystem.readFileSync(dest, 'utf8')
85
- .replace(/^title:.*$/m, `title: "${newCamel}"`)
86
- .replace(/^permalink:.*$/m, `permalink: "/${newName}/"`);
87
- fileSystem.writeFileSync(dest, content);
88
- }
89
- });
90
-
91
- renameLayout(oldName, newName);
107
+ try {
108
+ moves.forEach(({ src, dest }) => {
109
+ if (!fs.existsSync(src)) {
110
+ console.log(`[skip] not found: ${src}`);
111
+ return;
112
+ }
113
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
114
+ fs.renameSync(src, dest);
115
+ console.log(`[renamed] ${src} → ${dest}`);
116
+
117
+ if (dest.endsWith('.njk')) applyRouteFrontMatter(dest, newArtifacts.camelName, newName);
118
+ });
119
+ } catch (err) {
120
+ console.log(`[error] could not rename page files: ${err.message}`);
121
+ return;
122
+ }
123
+
124
+ renamePageBlock(oldName, newName);
92
125
  renameSiteData(oldName, newName);
93
126
  }
94
127
 
95
128
  function removePage(pageName) {
96
- const camelName = toCamelCase(pageName);
97
- const usesTs = isTypeScriptProject();
98
- const ext = usesTs ? 'ts' : 'js';
99
- const jsFolder = usesTs ? 'src/frontend/ts/pages' : 'src/frontend/js/pages';
100
- const OUTPUT_DIR = getCurrentOutputPath() || 'out';
101
-
102
- const filesToDelete = [
103
- `src/frontend/scss/pages/${camelName}.scss`,
104
- `${jsFolder}/${camelName}.${ext}`,
105
- `src/frontend/_routes/${pageName}.njk`,
106
- path.join(OUTPUT_DIR, 'js/pages', `${camelName}.js`),
107
- path.join(OUTPUT_DIR, 'css/pages', `${camelName}.css`),
108
- path.join(OUTPUT_DIR, `${pageName}.html`),
109
- path.join(OUTPUT_DIR, 'pages', `${pageName}.html`),
110
- ];
111
-
112
- const foldersToDelete = [
113
- path.join(OUTPUT_DIR, pageName),
114
- path.join(OUTPUT_DIR, 'pages', pageName),
115
- ];
116
-
117
- filesToDelete.forEach(f => {
118
- if (fileSystem.existsSync(f)) {
119
- fileSystem.unlinkSync(f);
120
- console.log(`[deleted file] ${f}`);
129
+ if (!pageExists(pageName)) {
130
+ console.log(`[skip] page "${pageName}" does not exist`);
131
+ }
132
+
133
+ const { source, output } = getPageArtifacts(pageName);
134
+ const filesToDelete = [source.scss, source.script, source.route, ...output.files];
135
+
136
+ filesToDelete.forEach((file) => {
137
+ try {
138
+ if (fs.existsSync(file)) {
139
+ fs.unlinkSync(file);
140
+ console.log(`[deleted] ${file}`);
141
+ }
142
+ } catch (err) {
143
+ console.log(`[error] ${file}: ${err.message}`);
121
144
  }
122
145
  });
123
146
 
124
- foldersToDelete.forEach(folder => {
125
- if (fileSystem.existsSync(folder)) {
126
- fileSystem.rmSync(folder, { recursive: true, force: true });
127
- console.log(`[deleted folder] ${folder}`);
147
+ output.folders.forEach((folder) => {
148
+ try {
149
+ if (fs.existsSync(folder)) {
150
+ fs.rmSync(folder, { recursive: true, force: true });
151
+ console.log(`[deleted] ${folder}`);
152
+ }
153
+ } catch (err) {
154
+ console.log(`[error] ${folder}: ${err.message}`);
128
155
  }
129
156
  });
130
157
 
131
- removeLayout(pageName);
158
+ removePageBlock(pageName);
132
159
  removeSiteData(pageName);
133
160
  }
134
161
 
135
- module.exports = { addPage, removePage, renamePage };
162
+ module.exports = { addPage, removePage, renamePage, pageExists };
@@ -1,30 +1,27 @@
1
- const fs = require('fs');
2
- const path = require('path');
1
+ const fs = require('fs');
2
+ const { PATHS } = require('./constants');
3
3
 
4
- const TSCONFIG = path.resolve(__dirname, '../../tsconfig.json');
5
-
6
- // Shared utility functions used across modules
4
+ function toCamelCase(value) {
5
+ return value.toLowerCase().replace(/[-_][a-z0-9]/g, (group) => group.slice(1).toUpperCase());
6
+ }
7
7
 
8
- // Converts a kebab-case or snake_case string to camelCase.
9
- // Uses slice(1) to remove the delimiter, avoiding the double-replace bug
10
- // that occurs when using .replace('-', '') without the /g flag.
11
- function toCamelCase(str) {
12
- return str.toLowerCase().replace(/[-_][a-z0-9]/g, (group) =>
13
- group.slice(1).toUpperCase()
14
- );
8
+ function toKebabCase(value) {
9
+ return value.trim().toLowerCase()
10
+ .replace(/[^a-z0-9\s_-]/g, '')
11
+ .replace(/[\s_]+/g, '-')
12
+ .replace(/-+/g, '-')
13
+ .replace(/^-+|-+$/g, '');
15
14
  }
16
15
 
17
- // Converts a kebab-case page name to a human-readable title
18
- // e.g. "about-us" → "About Us"
19
16
  function toNiceTitle(pageName) {
20
- return pageName
21
- .split('-')
22
- .map(w => w.charAt(0).toUpperCase() + w.slice(1))
17
+ return pageName.split('-')
18
+ .filter(Boolean)
19
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
23
20
  .join(' ');
24
21
  }
25
22
 
26
23
  function isTypeScriptProject() {
27
- return fs.existsSync(TSCONFIG);
24
+ return fs.existsSync(PATHS.tsconfig);
28
25
  }
29
26
 
30
- module.exports = { toCamelCase, toNiceTitle, isTypeScriptProject };
27
+ module.exports = { toCamelCase, toKebabCase, toNiceTitle, isTypeScriptProject };
@@ -0,0 +1,20 @@
1
+ const { MAX_PAGE_NAME_LENGTH, PROTECTED_PAGES } = require('./constants');
2
+
3
+ function validatePageName(name) {
4
+ if (!name) return 'Invalid name.';
5
+ if (name.length > MAX_PAGE_NAME_LENGTH) return `Name must be ${MAX_PAGE_NAME_LENGTH} characters or fewer.`;
6
+ if (!/^[a-z0-9-]+$/.test(name)) return 'Page name can only contain lowercase letters, numbers, and hyphens.';
7
+ if (/^\d/.test(name)) return 'Page name cannot start with a number.';
8
+ if (PROTECTED_PAGES.includes(name)) return `"${name}" is a protected page name.`;
9
+ return null;
10
+ }
11
+
12
+ function validateOutputPath(input) {
13
+ const value = (input ?? '').trim();
14
+ if (!value) return 'Invalid path.';
15
+ if (value.includes('..')) return 'Path cannot contain "..".';
16
+ if (/[<>|?*"']/.test(value)) return 'Path contains invalid characters.';
17
+ return null;
18
+ }
19
+
20
+ module.exports = { validatePageName, validateOutputPath };