create-berna-stencil 2.6.0 → 2.6.2

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 (199) hide show
  1. package/README.md +1 -1
  2. package/_tools/assistant.js +92 -88
  3. package/_tools/modules/constants.js +23 -0
  4. package/_tools/modules/pageComponents.js +77 -0
  5. package/_tools/modules/updateData.js +25 -44
  6. package/_tools/modules/updateOutputPath.js +59 -63
  7. package/_tools/modules/updatePage.js +131 -104
  8. package/_tools/modules/utils.js +16 -19
  9. package/_tools/modules/validation.js +20 -0
  10. package/bin/create.js +74 -12
  11. package/out/.htaccess +16 -0
  12. package/out/404.html +163 -0
  13. package/out/assets/brand/favicon.svg +37 -0
  14. package/out/assets/brand/logo.svg +37 -0
  15. package/out/backend/.htaccess +7 -0
  16. package/out/backend/_core/composer.json +5 -0
  17. package/out/backend/_core/composer.lock +492 -0
  18. package/out/backend/_core/index.php +148 -0
  19. package/out/backend/_core/init.php +34 -0
  20. package/out/backend/_core/modules/RateLimiter.php +31 -0
  21. package/out/backend/_core/modules/Response.php +49 -0
  22. package/{src → out}/backend/_core/vendor/composer/autoload_static.php +10 -10
  23. package/{src → out}/backend/_core/vendor/composer/installed.json +6 -6
  24. package/{src → out}/backend/_core/vendor/composer/installed.php +2 -2
  25. package/out/backend/_core/vendor/graham-campbell/result-type/CHANGELOG.md +53 -0
  26. package/{src → out}/backend/_core/vendor/graham-campbell/result-type/LICENSE +21 -21
  27. package/out/backend/_core/vendor/graham-campbell/result-type/README.md +42 -0
  28. package/{src → out}/backend/_core/vendor/graham-campbell/result-type/composer.json +33 -33
  29. package/out/backend/_core/vendor/graham-campbell/result-type/phpunit.xml.dist +13 -0
  30. package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Error.php +121 -121
  31. package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Result.php +69 -69
  32. package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Success.php +120 -120
  33. package/out/backend/_core/vendor/graham-campbell/result-type/tests/ResultTest.php +95 -0
  34. package/{src → out}/backend/_core/vendor/phpoption/phpoption/LICENSE +200 -200
  35. package/out/backend/_core/vendor/phpoption/phpoption/Makefile +17 -0
  36. package/out/backend/_core/vendor/phpoption/phpoption/README.md +201 -0
  37. package/{src → out}/backend/_core/vendor/phpoption/phpoption/composer.json +50 -50
  38. package/out/backend/_core/vendor/phpoption/phpoption/phpstan-baseline.neon +44 -0
  39. package/out/backend/_core/vendor/phpoption/phpoption/phpstan.neon.dist +7 -0
  40. package/out/backend/_core/vendor/phpoption/phpoption/phpunit.xml.dist +13 -0
  41. package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php +175 -175
  42. package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/None.php +136 -136
  43. package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Option.php +434 -434
  44. package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Some.php +169 -169
  45. package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/EnsureTest.php +72 -0
  46. package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/LazyOptionTest.php +357 -0
  47. package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/NoneTest.php +153 -0
  48. package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/OptionTest.php +166 -0
  49. package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/SomeTest.php +194 -0
  50. package/out/backend/_core/vendor/phpoption/phpoption/tests/bootstrap.php +8 -0
  51. package/out/backend/_core/vendor/phpoption/phpoption/vendor-bin/phpstan/composer.json +8 -0
  52. package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/Ctype.php +232 -232
  53. package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/LICENSE +19 -19
  54. package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/README.md +12 -12
  55. package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/bootstrap.php +50 -50
  56. package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/bootstrap80.php +46 -46
  57. package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/composer.json +38 -38
  58. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/LICENSE +19 -19
  59. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Mbstring.php +1077 -1077
  60. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/README.md +13 -13
  61. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php +119 -119
  62. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php +1397 -1397
  63. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php +5 -5
  64. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php +1489 -1489
  65. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap.php +171 -171
  66. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap80.php +167 -167
  67. package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/composer.json +39 -39
  68. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/LICENSE +19 -19
  69. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Php80.php +115 -115
  70. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/PhpToken.php +106 -106
  71. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/README.md +25 -25
  72. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php +31 -31
  73. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php +16 -16
  74. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php +20 -20
  75. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php +16 -16
  76. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php +16 -16
  77. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/bootstrap.php +42 -42
  78. package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/composer.json +37 -37
  79. package/out/backend/_core/vendor/vlucas/phpdotenv/.editorconfig +15 -0
  80. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/LICENSE +30 -30
  81. package/out/backend/_core/vendor/vlucas/phpdotenv/Makefile +17 -0
  82. package/out/backend/_core/vendor/vlucas/phpdotenv/README.md +370 -0
  83. package/out/backend/_core/vendor/vlucas/phpdotenv/UPGRADING.md +196 -0
  84. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/composer.json +60 -60
  85. package/out/backend/_core/vendor/vlucas/phpdotenv/phpstan-baseline.neon +157 -0
  86. package/out/backend/_core/vendor/vlucas/phpdotenv/phpstan.neon.dist +7 -0
  87. package/out/backend/_core/vendor/vlucas/phpdotenv/phpunit.xml.dist +13 -0
  88. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Dotenv.php +267 -267
  89. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php +12 -12
  90. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php +12 -12
  91. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php +12 -12
  92. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php +12 -12
  93. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ValidationException.php +12 -12
  94. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Loader.php +48 -48
  95. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php +20 -20
  96. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Resolver.php +65 -65
  97. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Entry.php +59 -59
  98. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php +299 -299
  99. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lexer.php +58 -58
  100. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lines.php +127 -127
  101. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Parser.php +53 -53
  102. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php +19 -19
  103. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Value.php +88 -88
  104. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php +15 -15
  105. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php +89 -89
  106. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php +80 -80
  107. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php +88 -88
  108. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php +85 -85
  109. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php +110 -110
  110. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php +48 -48
  111. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php +64 -64
  112. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php +91 -91
  113. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php +17 -17
  114. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php +104 -104
  115. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php +88 -88
  116. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php +27 -27
  117. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php +107 -107
  118. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php +272 -272
  119. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php +51 -51
  120. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Paths.php +44 -44
  121. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Reader.php +81 -81
  122. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/FileStore.php +72 -72
  123. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php +141 -141
  124. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php +17 -17
  125. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StringStore.php +37 -37
  126. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Util/Regex.php +112 -112
  127. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Util/Str.php +108 -108
  128. package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Validator.php +207 -207
  129. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/DotenvTest.php +387 -0
  130. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Loader/LoaderTest.php +86 -0
  131. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/EntryParserTest.php +234 -0
  132. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LexerTest.php +40 -0
  133. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LinesTest.php +53 -0
  134. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/ParserTest.php +98 -0
  135. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php +57 -0
  136. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php +75 -0
  137. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php +52 -0
  138. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php +75 -0
  139. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/RepositoryTest.php +305 -0
  140. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Store/StoreTest.php +141 -0
  141. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/ValidatorTest.php +479 -0
  142. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/.env +5 -0
  143. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/assertions.env +18 -0
  144. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/booleans.env +33 -0
  145. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/commented.env +15 -0
  146. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/empty.env +1 -0
  147. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/example.env +1 -0
  148. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/exported.env +7 -0
  149. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/immutable.env +1 -0
  150. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/integers.env +17 -0
  151. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/large.env +2 -0
  152. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multibyte.env +3 -0
  153. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiline.env +14 -0
  154. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiple.env +4 -0
  155. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/mutable.env +1 -0
  156. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/nested.env +15 -0
  157. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/quoted.env +11 -0
  158. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/specialchars.env +8 -0
  159. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/unicodevarnames.env +2 -0
  160. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/utf8-with-bom-encoding.env +3 -0
  161. package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/windows.env +1 -0
  162. package/out/backend/_core/vendor/vlucas/phpdotenv/vendor-bin/phpstan/composer.json +15 -0
  163. package/out/backend/api/protected/example-protected.php +17 -0
  164. package/out/backend/api/public/example-public.php +17 -0
  165. package/out/backend/database/Database.php +24 -0
  166. package/out/backend/database/migrations/create_example_db.sql +1 -0
  167. package/out/backend/web.config +17 -0
  168. package/out/css/fonts/bootstrap-icons.woff +0 -0
  169. package/out/css/fonts/bootstrap-icons.woff2 +0 -0
  170. package/out/css/github-markdown-dark.css +1124 -0
  171. package/out/css/github-markdown-light.css +1124 -0
  172. package/out/css/pages/404.css +21517 -0
  173. package/out/css/pages/homepage.css +21508 -0
  174. package/out/data/site.json +44 -0
  175. package/out/index.html +1147 -0
  176. package/out/js/bootstrap.bundle.min.js +7 -0
  177. package/out/js/pages/js/pages/404.js +6 -0
  178. package/out/js/pages/js/pages/homepage.js +6 -0
  179. package/out/js/pages/ts/pages/404.js +6 -0
  180. package/out/js/pages/ts/pages/homepage.js +6 -0
  181. package/out/llms.txt +14 -0
  182. package/out/robots.txt +4 -0
  183. package/out/sitemap.xml +15 -0
  184. package/out/web.config +27 -0
  185. package/package.json +14 -47
  186. package/src/frontend/data/site.json +1 -1
  187. package/.gitignore +0 -4
  188. package/_tools/modules/updateIncludes.js +0 -81
  189. package/src/backend/config.example.php +0 -28
  190. /package/{src → out}/backend/_core/vendor/autoload.php +0 -0
  191. /package/{src → out}/backend/_core/vendor/composer/ClassLoader.php +0 -0
  192. /package/{src → out}/backend/_core/vendor/composer/InstalledVersions.php +0 -0
  193. /package/{src → out}/backend/_core/vendor/composer/LICENSE +0 -0
  194. /package/{src → out}/backend/_core/vendor/composer/autoload_classmap.php +0 -0
  195. /package/{src → out}/backend/_core/vendor/composer/autoload_files.php +0 -0
  196. /package/{src → out}/backend/_core/vendor/composer/autoload_namespaces.php +0 -0
  197. /package/{src → out}/backend/_core/vendor/composer/autoload_psr4.php +0 -0
  198. /package/{src → out}/backend/_core/vendor/composer/autoload_real.php +0 -0
  199. /package/{src → out}/backend/_core/vendor/composer/platform_check.php +0 -0
package/README.md CHANGED
@@ -16,7 +16,7 @@ Because it keeps you close to the fundamentals instead of hiding them, you spend
16
16
  - 📁 **Scalable structure** — a clean, opinionated project layout that grows with your needs
17
17
  - 🌍 **Open source** — free to use, free to modify, free to share
18
18
 
19
- ![Version](https://img.shields.io/badge/version-2.6.0-blue)
19
+ ![Version](https://img.shields.io/badge/version-2.6.2-blue)
20
20
  ![License](https://img.shields.io/badge/license-Apache--2.0-blue)
21
21
  ![Eleventy](https://img.shields.io/badge/11ty-v3.1.2-black)
22
22
 
@@ -1,146 +1,150 @@
1
1
  const readline = require('readline');
2
- const { addPage, removePage, renamePage } = require('./modules/updatePage');
3
- const { updateOutputPath, getCurrentOutputPath } = require('./modules/updateOutputPath');
4
2
 
5
- const c = {
6
- reset: "\x1b[0m",
7
- bold: "\x1b[1m",
8
- dim: "\x1b[2m",
9
- red: "\x1b[31m",
10
- green: "\x1b[32m",
11
- yellow: "\x1b[33m",
12
- magenta: "\x1b[35m",
13
- cyan: "\x1b[36m"
3
+ const { addPage, removePage, renamePage, pageExists } = require('./modules/updatePage');
4
+ const { updateOutputPath, getCurrentOutputPath } = require('./modules/updateOutputPath');
5
+ const { validatePageName, validateOutputPath } = require('./modules/validation');
6
+ const { toKebabCase } = require('./modules/utils');
7
+
8
+ const color = {
9
+ reset: '\x1b[0m',
10
+ bold: '\x1b[1m',
11
+ dim: '\x1b[2m',
12
+ red: '\x1b[31m',
13
+ green: '\x1b[32m',
14
+ yellow: '\x1b[33m',
15
+ magenta: '\x1b[35m',
16
+ cyan: '\x1b[36m',
14
17
  };
15
18
 
16
- const readerInterface = readline.createInterface({
19
+ const rl = readline.createInterface({
17
20
  input: process.stdin,
18
21
  output: process.stdout,
19
- terminal: true
22
+ terminal: true,
20
23
  });
21
24
 
22
- const PROTECTED_PAGES = ['homepage', '404'];
23
- const MAX_NAME_LENGTH = 50;
24
-
25
- function toKebabCase(str) {
26
- return str.trim().toLowerCase()
27
- .replace(/[^a-z0-9\s_-]/g, '')
28
- .replace(/[\s_]+/g, '-')
29
- .replace(/-+/g, '-')
30
- .replace(/^-+|-+$/g, '');
31
- }
32
-
33
- function validatePageName(name) {
34
- if (!name) return 'Invalid name.';
35
- if (name.length > MAX_NAME_LENGTH) return `Name must be ${MAX_NAME_LENGTH} characters or fewer.`;
36
- if (!/^[a-z0-9-]+$/.test(name)) return 'Page name can only contain lowercase letters, numbers, and hyphens.';
37
- if (/^\d/.test(name)) return 'Page name cannot start with a number.';
38
- if (PROTECTED_PAGES.includes(name)) return `"${name}" is a protected page name.`;
39
- return null;
40
- }
41
-
42
- function validateOutputPath(input) {
43
- if (!input.trim()) return 'Invalid path.';
44
- if (input.includes('..')) return 'Path cannot contain "..".';
45
- if (/[<>|?*"']/.test(input)) return 'Path contains invalid characters.';
46
- return null;
25
+ function sanitizeInput(value) {
26
+ return (value ?? '').replace(/[\x00-\x1F\x7F]/g, '').trim();
47
27
  }
48
28
 
49
- function sanitizeInput(str) {
50
- return str.replace(/[\x00-\x1F\x7F]/g, '').trim();
29
+ function ask(prompt) {
30
+ return new Promise((resolve) => {
31
+ const onClose = () => resolve(null);
32
+ rl.once('close', onClose);
33
+ rl.question(prompt, (answer) => {
34
+ rl.off('close', onClose);
35
+ resolve(sanitizeInput(answer));
36
+ });
37
+ });
51
38
  }
52
39
 
53
- function ask(prompt) {
54
- return new Promise(resolve =>
55
- readerInterface.question(prompt, answer => resolve(sanitizeInput(answer)))
56
- );
40
+ async function confirm(prompt) {
41
+ const answer = await ask(`${prompt} ${color.dim}[y/N]${color.reset} `);
42
+ return /^y(es)?$/i.test((answer ?? '').trim());
57
43
  }
58
44
 
59
45
  async function askPageName(prompt) {
60
46
  const raw = await ask(prompt);
47
+ if (raw === null) return null;
48
+
61
49
  const name = toKebabCase(raw);
62
50
  const error = validatePageName(name);
63
51
  if (error) {
64
- console.log(`\n${c.red}✖ ${error}${c.reset}`);
52
+ console.log(`\n${color.red}✖ ${error}${color.reset}`);
65
53
  return null;
66
54
  }
67
55
  return name;
68
56
  }
69
57
 
70
- async function handleCreateRequest() {
71
- const name = await askPageName(`\n${c.green}❯${c.reset} Enter the name of the new page: `);
72
- if (name) addPage(name, null);
58
+ async function handleCreate() {
59
+ const name = await askPageName(`\n${color.green}❯${color.reset} Name of the new page: `);
60
+ if (name) addPage(name);
73
61
  }
74
62
 
75
- async function handleRemoveRequest() {
76
- const name = await askPageName(`\n${c.red}❯${c.reset} Enter the name of the page to remove: `);
77
- if (name) removePage(name);
63
+ async function handleRemove() {
64
+ const name = await askPageName(`\n${color.red}❯${color.reset} Name of the page to remove: `);
65
+ if (!name) return;
66
+
67
+ if (!pageExists(name)) {
68
+ console.log(`\n${color.yellow}⚠ Page "${name}" does not exist.${color.reset}`);
69
+ return;
70
+ }
71
+
72
+ const confirmed = await confirm(`This permanently deletes all files for "${name}".`);
73
+ if (!confirmed) {
74
+ console.log(`\n${color.dim}Cancelled.${color.reset}`);
75
+ return;
76
+ }
77
+ removePage(name);
78
78
  }
79
79
 
80
- async function handleRenameRequest() {
81
- const oldName = await askPageName(`\n${c.yellow}❯${c.reset} Enter the name of the page to rename: `);
80
+ async function handleRename() {
81
+ const oldName = await askPageName(`\n${color.yellow}❯${color.reset} Page to rename: `);
82
82
  if (!oldName) return;
83
83
 
84
- const newName = await askPageName(`${c.yellow}❯${c.reset} Enter the new name: `);
84
+ const newName = await askPageName(`${color.yellow}❯${color.reset} New name: `);
85
85
  if (!newName) return;
86
86
 
87
87
  if (oldName === newName) {
88
- console.log(`\n${c.yellow}⚠ Old and new name are the same.${c.reset}`);
88
+ console.log(`\n${color.yellow}⚠ Old and new name are the same.${color.reset}`);
89
89
  return;
90
90
  }
91
-
92
91
  renamePage(oldName, newName);
93
92
  }
94
93
 
95
- async function handleOutputPathRequest() {
94
+ async function handleOutputPath() {
96
95
  const current = getCurrentOutputPath();
97
- const label = current ? `\n${c.dim}Current path: "${current}"${c.reset}\n` : '\n';
98
- const input = await ask(`${label}${c.magenta}❯${c.reset} Enter the new output path: `);
96
+ const label = current ? `\n${color.dim}Current path: "${current}"${color.reset}\n` : '\n';
97
+
98
+ const input = await ask(`${label}${color.magenta}❯${color.reset} New output path: `);
99
+ if (input === null) return;
99
100
 
100
101
  const error = validateOutputPath(input);
101
102
  if (error) {
102
- console.log(`\n${c.red}✖ ${error}${c.reset}`);
103
- } else {
104
- updateOutputPath(input);
103
+ console.log(`\n${color.red}✖ ${error}${color.reset}`);
104
+ return;
105
105
  }
106
+ updateOutputPath(input);
106
107
  }
107
108
 
108
109
  const MENU_ACTIONS = {
109
- '1': handleCreateRequest,
110
- '2': handleRemoveRequest,
111
- '3': handleRenameRequest,
112
- '4': handleOutputPathRequest,
110
+ '1': handleCreate,
111
+ '2': handleRemove,
112
+ '3': handleRename,
113
+ '4': handleOutputPath,
113
114
  };
114
115
 
115
- async function displayMainMenu() {
116
- console.log(`\n${c.cyan}${c.bold}╭────────────────────────╮`);
116
+ function renderMenu() {
117
+ console.log(`\n${color.cyan}${color.bold}╭────────────────────────╮`);
117
118
  console.log(`│ Berna-Stencil CLI │`);
118
- console.log(`╰────────────────────────╯${c.reset}\n`);
119
- console.log(` ${c.green}1.${c.reset} Create page`);
120
- console.log(` ${c.red}2.${c.reset} Remove page`);
121
- console.log(` ${c.yellow}3.${c.reset} Rename page`);
122
- console.log(` ${c.magenta}4.${c.reset} Configure output path`);
123
- console.log(`\n ${c.dim}CTRL/CMD + C to exit${c.reset}\n`);
124
-
125
- const choice = (await ask(`${c.cyan}❯${c.reset} Choose an option: `)).trim();
126
-
127
- if (choice === '0') {
128
- readerInterface.close();
129
- process.exit(0);
130
- }
119
+ console.log(`╰────────────────────────╯${color.reset}\n`);
120
+ console.log(` ${color.green}1.${color.reset} Create page`);
121
+ console.log(` ${color.red}2.${color.reset} Remove page`);
122
+ console.log(` ${color.yellow}3.${color.reset} Rename page`);
123
+ console.log(` ${color.magenta}4.${color.reset} Configure output path`);
124
+ console.log(` ${color.dim}CTRL + C to exit\n`);
125
+ }
126
+
127
+ async function main() {
128
+ while (true) {
129
+ renderMenu();
130
+
131
+ const choice = await ask(`${color.cyan}❯${color.reset} Choose an option: `);
132
+
133
+ const action = MENU_ACTIONS[choice];
134
+ if (!action) {
135
+ console.log(`\n${color.red}✖ Invalid option.${color.reset}`);
136
+ continue;
137
+ }
131
138
 
132
- const action = MENU_ACTIONS[choice];
133
- if (action) {
134
139
  try {
135
140
  await action();
136
141
  } catch (err) {
137
- console.log(`\n${c.red}✖ Unexpected error: ${err.message}${c.reset}`);
142
+ console.log(`\n${color.red}✖ Unexpected error: ${err.message}${color.reset}`);
138
143
  }
139
- } else {
140
- console.log(`\n${c.red}✖ Invalid option.${c.reset}`);
141
144
  }
142
145
 
143
- displayMainMenu();
146
+ rl.close();
147
+ process.exit(0);
144
148
  }
145
149
 
146
- displayMainMenu();
150
+ main();
@@ -0,0 +1,23 @@
1
+ const path = require('path');
2
+
3
+ const ROOT = path.resolve(__dirname, '..', '..');
4
+ const FRONTEND_DIR = path.join(ROOT, 'src', 'frontend');
5
+
6
+ const MAX_PAGE_NAME_LENGTH = 50;
7
+ const PROTECTED_PAGES = Object.freeze(['homepage', '404']);
8
+
9
+ const PATHS = Object.freeze({
10
+ root: ROOT,
11
+ routes: path.join(FRONTEND_DIR, '_routes'),
12
+ scssPages: path.join(FRONTEND_DIR, 'scss', 'pages'),
13
+ jsPages: path.join(FRONTEND_DIR, 'js', 'pages'),
14
+ tsPages: path.join(FRONTEND_DIR, 'ts', 'pages'),
15
+ siteData: path.join(FRONTEND_DIR, 'data', 'site.json'),
16
+ pageComponents: path.join(FRONTEND_DIR, 'layouts', 'pageComponents.njk'),
17
+ templates: path.join(ROOT, '_tools', 'res', 'templates'),
18
+ eleventyConfig: path.join(ROOT, '.eleventy.js'),
19
+ packageJson: path.join(ROOT, 'package.json'),
20
+ tsconfig: path.join(ROOT, 'tsconfig.json'),
21
+ });
22
+
23
+ module.exports = { PATHS, MAX_PAGE_NAME_LENGTH, PROTECTED_PAGES };
@@ -0,0 +1,77 @@
1
+ const fs = require('fs');
2
+ const { PATHS } = require('./constants');
3
+ const { toCamelCase } = require('./utils');
4
+
5
+ function readPageComponents() {
6
+ if (!fs.existsSync(PATHS.pageComponents)) {
7
+ console.log(`[skip] not found: ${PATHS.pageComponents}`);
8
+ return null;
9
+ }
10
+ return fs.readFileSync(PATHS.pageComponents, 'utf8');
11
+ }
12
+
13
+ function writePageComponents(content) {
14
+ fs.writeFileSync(PATHS.pageComponents, content);
15
+ }
16
+
17
+ function elifBlockRegex(camelName) {
18
+ return new RegExp(
19
+ `[ \\t]*\\{%\\s*elif\\s+title\\s*==\\s*"${camelName}"\\s*%\\}[\\s\\S]*?(?=[ \\t]*\\{%\\s*(?:elif|else|endif))`,
20
+ );
21
+ }
22
+
23
+ function addPageBlock(pageName) {
24
+ const content = readPageComponents();
25
+ if (!content) return;
26
+
27
+ const camelName = toCamelCase(pageName);
28
+ if (content.includes(`{% elif title == "${camelName}" %}`)) return;
29
+
30
+ if (!content.includes('{% else %}')) {
31
+ console.log('[skip] no {% else %} anchor in pageComponents.njk');
32
+ return;
33
+ }
34
+
35
+ const block =
36
+ `{% elif title == "${camelName}" %}\n` +
37
+ ` {#{% include "component.njk" %}#}\n\n`;
38
+
39
+ writePageComponents(content.replace('{% else %}', `${block}{% else %}`));
40
+ console.log(`[updated] page block added for "${camelName}"`);
41
+ }
42
+
43
+ function removePageBlock(pageName) {
44
+ const content = readPageComponents();
45
+ if (!content) return;
46
+
47
+ const camelName = toCamelCase(pageName);
48
+ if (!elifBlockRegex(camelName).test(content)) {
49
+ console.log(`[skip] page block for "${camelName}" not found`);
50
+ return;
51
+ }
52
+
53
+ const updated = content
54
+ .replace(elifBlockRegex(camelName), '')
55
+ .replace(/\n\s*\n\s*\n/g, '\n\n');
56
+
57
+ writePageComponents(updated);
58
+ console.log(`[cleaned] page block removed for "${camelName}"`);
59
+ }
60
+
61
+ function renamePageBlock(oldName, newName) {
62
+ const content = readPageComponents();
63
+ if (!content) return;
64
+
65
+ const oldLine = `{% elif title == "${toCamelCase(oldName)}" %}`;
66
+ const newLine = `{% elif title == "${toCamelCase(newName)}" %}`;
67
+
68
+ if (!content.includes(oldLine)) {
69
+ console.log(`[skip] page block for "${toCamelCase(oldName)}" not found`);
70
+ return;
71
+ }
72
+
73
+ writePageComponents(content.replace(oldLine, newLine));
74
+ console.log(`[renamed] page block "${toCamelCase(oldName)}" → "${toCamelCase(newName)}"`);
75
+ }
76
+
77
+ module.exports = { addPageBlock, removePageBlock, renamePageBlock };
@@ -1,72 +1,60 @@
1
- const fileSystem = require('fs');
1
+ const fs = require('fs');
2
+ const { PATHS } = require('./constants');
2
3
  const { toCamelCase, toNiceTitle } = require('./utils');
3
4
 
4
- const SITE_DATA_PATH = 'src/frontend/data/site.json';
5
-
6
- // Returns the parsed site.json content, or null if the file doesn't exist
7
5
  function readSiteData() {
8
- if (!fileSystem.existsSync(SITE_DATA_PATH)) return null;
9
- return JSON.parse(fileSystem.readFileSync(SITE_DATA_PATH, 'utf8'));
6
+ if (!fs.existsSync(PATHS.siteData)) {
7
+ console.log(`[skip] not found: ${PATHS.siteData}`);
8
+ return null;
9
+ }
10
+ try {
11
+ const data = JSON.parse(fs.readFileSync(PATHS.siteData, 'utf8'));
12
+ if (!data.pages || typeof data.pages !== 'object') data.pages = {};
13
+ return data;
14
+ } catch (err) {
15
+ console.log(`[error] cannot parse site.json: ${err.message}`);
16
+ return null;
17
+ }
10
18
  }
11
19
 
12
- // Serializes and writes the data object back to site.json
13
20
  function writeSiteData(data) {
14
- fileSystem.writeFileSync(SITE_DATA_PATH, JSON.stringify(data, null, 2));
21
+ fs.writeFileSync(PATHS.siteData, `${JSON.stringify(data, null, 2)}\n`);
15
22
  }
16
23
 
17
- // --- Public API ---
18
-
19
- // Adds a new page record to site.json
20
- // Skips silently if the file doesn't exist or the record is already present
21
24
  function addSiteData(pageName) {
22
25
  const data = readSiteData();
23
26
  if (!data) return;
24
27
 
25
28
  const camelName = toCamelCase(pageName);
26
-
27
29
  if (data.pages[camelName]) {
28
- console.log(`[SKIP] Record "${camelName}" already exists.`);
30
+ console.log(`[skip] record "${camelName}" already exists`);
29
31
  return;
30
32
  }
31
33
 
32
- // Build the default page record with SEO metadata and empty CDN arrays
33
34
  data.pages[camelName] = {
34
- seo: {
35
- title: toNiceTitle(pageName),
36
- description: 'description',
37
- },
38
- cdn: {
39
- css: [],
40
- js: []
41
- }
35
+ seo: { title: toNiceTitle(pageName), description: 'description' },
36
+ cdn: { css: [], js: [] },
42
37
  };
43
38
 
44
39
  writeSiteData(data);
45
- console.log(`[UPDATED] Record "${camelName}" added.`);
40
+ console.log(`[updated] record "${camelName}" added`);
46
41
  }
47
42
 
48
- // Removes a page record from site.json
49
- // Skips silently if the file doesn't exist or the record is not found
50
43
  function removeSiteData(pageName) {
51
44
  const data = readSiteData();
52
45
  if (!data) return;
53
46
 
54
47
  const camelName = toCamelCase(pageName);
55
-
56
48
  if (!data.pages[camelName]) {
57
- console.log(`[SKIP] Record "${camelName}" not found.`);
49
+ console.log(`[skip] record "${camelName}" not found`);
58
50
  return;
59
51
  }
60
52
 
61
53
  delete data.pages[camelName];
62
-
63
54
  writeSiteData(data);
64
- console.log(`[CLEANED] Record "${camelName}" removed.`);
55
+ console.log(`[cleaned] record "${camelName}" removed`);
65
56
  }
66
57
 
67
- // Renames a page record in site.json
68
- // Preserves all existing fields (cdn, etc.) and only updates the SEO title
69
- // Skips if the source record doesn't exist or the target name is already taken
70
58
  function renameSiteData(oldName, newName) {
71
59
  const data = readSiteData();
72
60
  if (!data) return;
@@ -75,29 +63,22 @@ function renameSiteData(oldName, newName) {
75
63
  const newCamel = toCamelCase(newName);
76
64
 
77
65
  if (!data.pages[oldCamel]) {
78
- console.log(`[SKIP] Record "${oldCamel}" not found.`);
66
+ console.log(`[skip] record "${oldCamel}" not found`);
79
67
  return;
80
68
  }
81
-
82
69
  if (data.pages[newCamel]) {
83
- console.log(`[SKIP] Record "${newCamel}" already exists.`);
70
+ console.log(`[skip] record "${newCamel}" already exists`);
84
71
  return;
85
72
  }
86
73
 
87
- // Spread the existing record to preserve cdn and any future fields,
88
- // then override only the seo.title with the new page name
89
74
  data.pages[newCamel] = {
90
75
  ...data.pages[oldCamel],
91
- seo: {
92
- ...data.pages[oldCamel].seo,
93
- title: toNiceTitle(newName),
94
- }
76
+ seo: { ...data.pages[oldCamel].seo, title: toNiceTitle(newName) },
95
77
  };
96
-
97
78
  delete data.pages[oldCamel];
98
79
 
99
80
  writeSiteData(data);
100
- console.log(`[UPDATED] Record "${oldCamel}" renamed to "${newCamel}".`);
81
+ console.log(`[updated] record "${oldCamel}" renamed to "${newCamel}"`);
101
82
  }
102
83
 
103
84
  module.exports = { addSiteData, removeSiteData, renameSiteData };
@@ -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
  }