create-berna-stencil 2.0.15 → 2.2.0

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 (193) hide show
  1. package/.eleventy.js +5 -27
  2. package/README.md +1 -1
  3. package/_tools/buildJs.js +28 -0
  4. package/_tools/cleanOutput.js +9 -13
  5. package/_tools/modules/updateOutputPath.js +36 -14
  6. package/_tools/modules/updatePage.js +31 -11
  7. package/_tools/res/templates/template.js +3 -11
  8. package/_tools/res/templates/template.ts +13 -0
  9. package/bin/create.js +161 -111
  10. package/docs/Assistant CLI.md +2 -0
  11. package/docs/Creating pages.md +2 -0
  12. package/docs/Javascript.md +30 -37
  13. package/package.json +64 -63
  14. package/src/backend/_core/index.php +10 -1
  15. package/src/backend/_core/init.php +4 -3
  16. package/src/backend/_core/modules/RateLimiter.php +31 -0
  17. package/src/backend/_core/vendor/composer/autoload_static.php +10 -10
  18. package/src/backend/_core/vendor/composer/installed.json +6 -6
  19. package/src/backend/_core/vendor/composer/installed.php +2 -2
  20. package/src/backend/_core/vendor/graham-campbell/result-type/.gitattributes +9 -0
  21. package/src/backend/_core/vendor/graham-campbell/result-type/.github/CODE_OF_CONDUCT.md +132 -0
  22. package/src/backend/_core/vendor/graham-campbell/result-type/.github/CONTRIBUTING.md +31 -0
  23. package/src/backend/_core/vendor/graham-campbell/result-type/.github/FUNDING.yml +2 -0
  24. package/src/backend/_core/vendor/graham-campbell/result-type/.github/SECURITY.md +14 -0
  25. package/src/backend/_core/vendor/graham-campbell/result-type/.github/workflows/stale.yml +11 -0
  26. package/src/backend/_core/vendor/graham-campbell/result-type/.github/workflows/tests.yml +40 -0
  27. package/src/backend/_core/vendor/graham-campbell/result-type/CHANGELOG.md +53 -0
  28. package/src/backend/_core/vendor/graham-campbell/result-type/LICENSE +21 -21
  29. package/src/backend/_core/vendor/graham-campbell/result-type/README.md +42 -0
  30. package/src/backend/_core/vendor/graham-campbell/result-type/composer.json +33 -33
  31. package/src/backend/_core/vendor/graham-campbell/result-type/phpunit.xml.dist +13 -0
  32. package/src/backend/_core/vendor/graham-campbell/result-type/src/Error.php +121 -121
  33. package/src/backend/_core/vendor/graham-campbell/result-type/src/Result.php +69 -69
  34. package/src/backend/_core/vendor/graham-campbell/result-type/src/Success.php +120 -120
  35. package/src/backend/_core/vendor/graham-campbell/result-type/tests/ResultTest.php +95 -0
  36. package/src/backend/_core/vendor/phpoption/phpoption/.gitattributes +13 -0
  37. package/src/backend/_core/vendor/phpoption/phpoption/.github/CODE_OF_CONDUCT.md +132 -0
  38. package/src/backend/_core/vendor/phpoption/phpoption/.github/CONTRIBUTING.md +30 -0
  39. package/src/backend/_core/vendor/phpoption/phpoption/.github/FUNDING.yml +2 -0
  40. package/src/backend/_core/vendor/phpoption/phpoption/.github/SECURITY.md +14 -0
  41. package/src/backend/_core/vendor/phpoption/phpoption/.github/workflows/static.yml +40 -0
  42. package/src/backend/_core/vendor/phpoption/phpoption/.github/workflows/tests.yml +40 -0
  43. package/src/backend/_core/vendor/phpoption/phpoption/LICENSE +200 -200
  44. package/src/backend/_core/vendor/phpoption/phpoption/Makefile +17 -0
  45. package/src/backend/_core/vendor/phpoption/phpoption/README.md +201 -0
  46. package/src/backend/_core/vendor/phpoption/phpoption/composer.json +50 -50
  47. package/src/backend/_core/vendor/phpoption/phpoption/phpstan-baseline.neon +44 -0
  48. package/src/backend/_core/vendor/phpoption/phpoption/phpstan.neon.dist +7 -0
  49. package/src/backend/_core/vendor/phpoption/phpoption/phpunit.xml.dist +13 -0
  50. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php +175 -175
  51. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/None.php +136 -136
  52. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Option.php +434 -434
  53. package/src/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Some.php +169 -169
  54. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/EnsureTest.php +72 -0
  55. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/LazyOptionTest.php +357 -0
  56. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/NoneTest.php +153 -0
  57. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/OptionTest.php +166 -0
  58. package/src/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/SomeTest.php +194 -0
  59. package/src/backend/_core/vendor/phpoption/phpoption/tests/bootstrap.php +8 -0
  60. package/src/backend/_core/vendor/phpoption/phpoption/vendor-bin/phpstan/composer.json +8 -0
  61. package/src/backend/_core/vendor/symfony/polyfill-ctype/Ctype.php +232 -232
  62. package/src/backend/_core/vendor/symfony/polyfill-ctype/LICENSE +19 -19
  63. package/src/backend/_core/vendor/symfony/polyfill-ctype/README.md +12 -12
  64. package/src/backend/_core/vendor/symfony/polyfill-ctype/bootstrap.php +50 -50
  65. package/src/backend/_core/vendor/symfony/polyfill-ctype/bootstrap80.php +46 -46
  66. package/src/backend/_core/vendor/symfony/polyfill-ctype/composer.json +38 -38
  67. package/src/backend/_core/vendor/symfony/polyfill-mbstring/LICENSE +19 -19
  68. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Mbstring.php +1077 -1077
  69. package/src/backend/_core/vendor/symfony/polyfill-mbstring/README.md +13 -13
  70. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php +119 -119
  71. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php +1397 -1397
  72. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php +5 -5
  73. package/src/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php +1489 -1489
  74. package/src/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap.php +171 -171
  75. package/src/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap80.php +167 -167
  76. package/src/backend/_core/vendor/symfony/polyfill-mbstring/composer.json +39 -39
  77. package/src/backend/_core/vendor/symfony/polyfill-php80/LICENSE +19 -19
  78. package/src/backend/_core/vendor/symfony/polyfill-php80/Php80.php +115 -115
  79. package/src/backend/_core/vendor/symfony/polyfill-php80/PhpToken.php +106 -106
  80. package/src/backend/_core/vendor/symfony/polyfill-php80/README.md +25 -25
  81. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php +31 -31
  82. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php +16 -16
  83. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php +20 -20
  84. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php +16 -16
  85. package/src/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php +16 -16
  86. package/src/backend/_core/vendor/symfony/polyfill-php80/bootstrap.php +42 -42
  87. package/src/backend/_core/vendor/symfony/polyfill-php80/composer.json +37 -37
  88. package/src/backend/_core/vendor/vlucas/phpdotenv/.editorconfig +15 -0
  89. package/src/backend/_core/vendor/vlucas/phpdotenv/.gitattributes +15 -0
  90. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/CODE_OF_CONDUCT.md +132 -0
  91. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/CONTRIBUTING.md +30 -0
  92. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/FUNDING.yml +2 -0
  93. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/SECURITY.md +14 -0
  94. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/workflows/static.yml +40 -0
  95. package/src/backend/_core/vendor/vlucas/phpdotenv/.github/workflows/tests.yml +70 -0
  96. package/src/backend/_core/vendor/vlucas/phpdotenv/LICENSE +30 -30
  97. package/src/backend/_core/vendor/vlucas/phpdotenv/Makefile +17 -0
  98. package/src/backend/_core/vendor/vlucas/phpdotenv/README.md +370 -0
  99. package/src/backend/_core/vendor/vlucas/phpdotenv/UPGRADING.md +196 -0
  100. package/src/backend/_core/vendor/vlucas/phpdotenv/composer.json +60 -60
  101. package/src/backend/_core/vendor/vlucas/phpdotenv/phpstan-baseline.neon +157 -0
  102. package/src/backend/_core/vendor/vlucas/phpdotenv/phpstan.neon.dist +7 -0
  103. package/src/backend/_core/vendor/vlucas/phpdotenv/phpunit.xml.dist +13 -0
  104. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Dotenv.php +267 -267
  105. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php +12 -12
  106. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php +12 -12
  107. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php +12 -12
  108. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php +12 -12
  109. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ValidationException.php +12 -12
  110. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Loader.php +48 -48
  111. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php +20 -20
  112. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Resolver.php +65 -65
  113. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Entry.php +59 -59
  114. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php +299 -299
  115. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lexer.php +58 -58
  116. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lines.php +127 -127
  117. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Parser.php +53 -53
  118. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php +19 -19
  119. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Value.php +88 -88
  120. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php +15 -15
  121. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php +89 -89
  122. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php +80 -80
  123. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php +88 -88
  124. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php +85 -85
  125. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php +110 -110
  126. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php +48 -48
  127. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php +64 -64
  128. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php +91 -91
  129. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php +17 -17
  130. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php +104 -104
  131. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php +88 -88
  132. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php +27 -27
  133. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php +107 -107
  134. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php +272 -272
  135. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php +51 -51
  136. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Paths.php +44 -44
  137. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Reader.php +81 -81
  138. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/FileStore.php +72 -72
  139. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php +141 -141
  140. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php +17 -17
  141. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Store/StringStore.php +37 -37
  142. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Util/Regex.php +112 -112
  143. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Util/Str.php +108 -108
  144. package/src/backend/_core/vendor/vlucas/phpdotenv/src/Validator.php +207 -207
  145. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/DotenvTest.php +387 -0
  146. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Loader/LoaderTest.php +86 -0
  147. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/EntryParserTest.php +234 -0
  148. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LexerTest.php +40 -0
  149. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LinesTest.php +53 -0
  150. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/ParserTest.php +98 -0
  151. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php +57 -0
  152. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php +75 -0
  153. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php +52 -0
  154. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php +75 -0
  155. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/RepositoryTest.php +305 -0
  156. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Store/StoreTest.php +141 -0
  157. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/ValidatorTest.php +479 -0
  158. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/.env +5 -0
  159. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/assertions.env +18 -0
  160. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/booleans.env +33 -0
  161. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/commented.env +15 -0
  162. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/empty.env +1 -0
  163. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/example.env +1 -0
  164. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/exported.env +7 -0
  165. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/immutable.env +1 -0
  166. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/integers.env +17 -0
  167. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/large.env +2 -0
  168. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multibyte.env +3 -0
  169. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiline.env +14 -0
  170. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiple.env +4 -0
  171. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/mutable.env +1 -0
  172. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/nested.env +15 -0
  173. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/quoted.env +11 -0
  174. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/specialchars.env +8 -0
  175. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/unicodevarnames.env +2 -0
  176. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/utf8-with-bom-encoding.env +3 -0
  177. package/src/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/windows.env +1 -0
  178. package/src/backend/_core/vendor/vlucas/phpdotenv/vendor-bin/phpstan/composer.json +15 -0
  179. package/src/backend/api/protected/example-protected.php +1 -1
  180. package/src/backend/api/public/example-public.php +1 -1
  181. package/src/backend/config.php +19 -0
  182. package/src/backend/web.config +2 -5
  183. package/src/frontend/.htaccess +4 -1
  184. package/src/frontend/js/modules/exampleModule.js +7 -0
  185. package/src/frontend/js/pages/404.js +3 -11
  186. package/src/frontend/js/pages/homepage.js +3 -11
  187. package/src/frontend/ts/modules/exampleModule.ts +3 -0
  188. package/src/frontend/ts/pages/404.ts +13 -0
  189. package/src/frontend/ts/pages/homepage.ts +13 -0
  190. package/src/frontend/web.config +21 -30
  191. package/src/frontend/js/modules/forms/normalizePhoneNumber.js +0 -42
  192. package/src/frontend/js/modules/forms/textAreaAutoExpand.js +0 -38
  193. package/src/frontend/js/modules/notification.js +0 -39
package/bin/create.js CHANGED
@@ -5,58 +5,87 @@ const path = require('path');
5
5
  const readline = require('readline');
6
6
  const { writeSync } = require('fs');
7
7
 
8
- const targetDir = process.argv[2] ? path.resolve(process.argv[2]) : process.cwd();
8
+ // ── PATHS ────────────────────────────────────────────────────────────────────
9
+
10
+ const targetDir = process.argv[2] ? path.resolve(process.argv[2]) : process.cwd();
9
11
  const templateDir = path.join(__dirname, '..');
10
12
 
11
- const COPY_TARGETS = [
12
- 'src',
13
+ // ── ENUMS ────────────────────────────────────────────────────────────────────
14
+
15
+ const LANGUAGE = Object.freeze({
16
+ JAVASCRIPT: 'javascript',
17
+ TYPESCRIPT: 'typescript',
18
+ });
19
+
20
+ const FRAMEWORK = Object.freeze({
21
+ BOOTSTRAP: 'bootstrap',
22
+ BULMA: 'bulma',
23
+ FOUNDATION: 'foundation',
24
+ UIKIT: 'uikit',
25
+ NONE: 'none',
26
+ });
27
+
28
+ // ── CHOICES ──────────────────────────────────────────────────────────────────
29
+
30
+ const LANGUAGE_CHOICES = [
31
+ { label: 'JavaScript (default)', value: LANGUAGE.JAVASCRIPT },
32
+ { label: 'TypeScript', value: LANGUAGE.TYPESCRIPT },
33
+ ];
34
+
35
+ const FRAMEWORK_CHOICES = [
36
+ { label: 'Bootstrap (default)', value: FRAMEWORK.BOOTSTRAP },
37
+ { label: 'Bulma', value: FRAMEWORK.BULMA },
38
+ { label: 'Foundation', value: FRAMEWORK.FOUNDATION },
39
+ { label: 'UIkit', value: FRAMEWORK.UIKIT },
40
+ { label: 'None', value: FRAMEWORK.NONE },
41
+ ];
42
+
43
+ // ── COPY CONFIG ───────────────────────────────────────────────────────────────
44
+
45
+ const MANDATORY_COPY = [
13
46
  'docs',
14
47
  '_tools',
15
48
  '.eleventy.js',
16
49
  '.eleventyignore',
50
+ 'src/backend',
51
+ 'src/frontend',
17
52
  ];
18
53
 
19
- const GITIGNORE_CONTENT = `
20
- node_modules/
21
- src/backend/_core/vendor/
22
- out/
23
- src/backend/config.php
24
- `;
54
+ const FRONTEND_EXCLUDE = {
55
+ [LANGUAGE.JAVASCRIPT]: ['ts'],
56
+ [LANGUAGE.TYPESCRIPT]: ['js'],
57
+ };
25
58
 
26
59
  const CREATE_DIRS = [
27
60
  'src/frontend/_routes',
28
61
  ];
29
62
 
30
- const ALL_FRAMEWORKS = ['bootstrap', 'bulma', 'foundation', 'uikit'];
63
+ // ── FRAMEWORK CONFIG ──────────────────────────────────────────────────────────
64
+
65
+ const ALL_FRAMEWORKS = Object.values(FRAMEWORK).filter(f => f !== FRAMEWORK.NONE);
31
66
 
32
67
  const FRAMEWORKS = {
33
- bootstrap: {
34
- label: 'Bootstrap',
35
- scss: 'bootstrap',
36
- njk: [
37
- '<script src="/js/bootstrap.bundle.min.js" defer></script>',
38
- ],
68
+ [FRAMEWORK.BOOTSTRAP]: {
69
+ scss: 'bootstrap',
70
+ njk: ['<script src="/js/bootstrap.bundle.min.js" defer></script>'],
39
71
  eleventy: [
40
72
  '"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js": "js/bootstrap.bundle.min.js",',
41
73
  '"node_modules/bootstrap-icons/font/fonts": "css/fonts",',
42
74
  ],
43
75
  },
44
- bulma: {
45
- label: 'Bulma',
46
- scss: 'bulma',
47
- njk: [],
76
+ [FRAMEWORK.BULMA]: {
77
+ scss: 'bulma',
78
+ njk: [],
48
79
  eleventy: [],
49
80
  },
50
- foundation: {
51
- label: 'Foundation',
52
- scss: 'foundation',
53
- njk: ['<script src="/js/foundation.min.js" defer></script>'],
81
+ [FRAMEWORK.FOUNDATION]: {
82
+ scss: 'foundation',
83
+ njk: ['<script src="/js/foundation.min.js" defer></script>'],
54
84
  eleventy: ['"node_modules/foundation-sites/dist/js/foundation.min.js": "js/foundation.min.js",'],
55
85
  },
56
- uikit: {
57
- label: 'UIkit',
58
- scss: 'uikit',
59
- njk: [
86
+ [FRAMEWORK.UIKIT]: {
87
+ scss: 'uikit',
88
+ njk: [
60
89
  '<script src="/js/uikit.min.js" defer></script>',
61
90
  '<script src="/js/uikit-icons.min.js" defer></script>',
62
91
  ],
@@ -65,25 +94,42 @@ const FRAMEWORKS = {
65
94
  '"node_modules/uikit/dist/js/uikit-icons.min.js": "js/uikit-icons.min.js",',
66
95
  ],
67
96
  },
68
- none: {
69
- label: 'None',
70
- scss: null,
71
- njk: [],
97
+ [FRAMEWORK.NONE]: {
98
+ scss: null,
99
+ njk: [],
72
100
  eleventy: [],
73
101
  },
74
102
  };
75
103
 
104
+ // ── LANGUAGE CONFIG ───────────────────────────────────────────────────────────
105
+
106
+ const LANGUAGE_ELEVENTY = Object.freeze({
107
+ jsEntry: 'const entryPoints = glob.sync("src/frontend/js/pages/*.js");',
108
+ tsEntry: 'const entryPoints = glob.sync("src/frontend/ts/pages/*.ts");',
109
+ });
110
+
111
+ // ── GENERATED FILE CONTENTS ───────────────────────────────────────────────────
112
+
113
+ const GITIGNORE_CONTENT = `
114
+ node_modules/
115
+ src/backend/_core/vendor/
116
+ out/
117
+ src/backend/config.php
118
+ `;
119
+
120
+
76
121
  const PROJECT_PACKAGE = {
77
- name: path.basename(targetDir),
78
- version: '2.0.15',
79
- private: true,
80
- scripts: {
122
+ name: path.basename(targetDir),
123
+ version: '2.2.0',
124
+ private: true,
125
+ outputDir: 'out',
126
+ "scripts": {
81
127
  "build:css": "sass src/frontend/scss:out/css --no-source-map --style=compressed --quiet --load-path=node_modules",
82
- "build:js": "esbuild \"src/frontend/js/pages/*.js\" --bundle --outdir=out/js/pages --minify",
128
+ "build:js": "node _tools/buildJs.js",
83
129
  "build:11ty": "eleventy",
84
130
  "build": "npm run clean && npm run build:css && npm run build:js && npm run build:11ty",
85
131
  "serve:css": "sass --watch src/frontend/scss:out/css --no-source-map --quiet --load-path=node_modules",
86
- "serve:js": "esbuild \"src/frontend/js/pages/*.js\" --bundle --outdir=out/js/pages --watch",
132
+ "serve:js": "node _tools/buildJs.js --watch",
87
133
  "serve:11ty": "eleventy --serve --quiet",
88
134
  "clean": "node _tools/cleanOutput.js",
89
135
  "serve": "npm run clean && concurrently \"npm run serve:11ty\" \"npm run serve:css\" \"npm run serve:js\"",
@@ -91,23 +137,25 @@ const PROJECT_PACKAGE = {
91
137
  "postinstall": "cd src/backend/_core && composer install --quiet"
92
138
  },
93
139
  dependencies: {
94
- '@11ty/eleventy': '^3.1.2',
140
+ '@11ty/eleventy': '^3.1.2',
95
141
  '@11ty/eleventy-img': '^6.0.4',
96
- 'bootstrap': '^5.3.8',
97
- 'bootstrap-icons': '^1.13.1',
98
- 'bulma': '^1.0.4',
99
- 'foundation-sites': '^6.9.0',
142
+ 'bootstrap': '^5.3.8',
143
+ 'bootstrap-icons': '^1.13.1',
144
+ 'bulma': '^1.0.4',
145
+ 'foundation-sites': '^6.9.0',
100
146
  'github-markdown-css': '^5.9.0',
101
- 'glob': '^13.0.6',
102
- 'uikit': '^3.25.13',
147
+ 'glob': '^13.0.6',
148
+ 'uikit': '^3.25.13',
103
149
  },
104
150
  devDependencies: {
105
151
  'concurrently': '^9.2.1',
106
- 'esbuild': '^0.27.3',
107
- 'sass': '^1.77.0',
152
+ 'esbuild': '^0.27.3',
153
+ 'sass': '^1.77.0',
108
154
  },
109
155
  };
110
156
 
157
+ // ── HELPERS ───────────────────────────────────────────────────────────────────
158
+
111
159
  function log(msg) {
112
160
  writeSync(1, msg + '\n');
113
161
  }
@@ -116,13 +164,14 @@ function escapeRegex(str) {
116
164
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
117
165
  }
118
166
 
119
- function copyRecursive(src, dest) {
167
+ function copyRecursive(src, dest, exclude = []) {
120
168
  const stat = fs.statSync(src);
121
169
  if (stat.isDirectory()) {
122
170
  fs.mkdirSync(dest, { recursive: true });
123
171
  for (const child of fs.readdirSync(src)) {
124
172
  if (child === '.git') continue;
125
- copyRecursive(path.join(src, child), path.join(dest, child));
173
+ if (exclude.includes(child)) continue;
174
+ copyRecursive(path.join(src, child), path.join(dest, child), exclude);
126
175
  }
127
176
  } else {
128
177
  fs.mkdirSync(path.dirname(dest), { recursive: true });
@@ -159,6 +208,8 @@ function njkUncomment(content, line) {
159
208
  return content.split(`{# ${line} #}`).join(line);
160
209
  }
161
210
 
211
+ // ── APPLY ─────────────────────────────────────────────────────────────────────
212
+
162
213
  function applyFramework(framework) {
163
214
  const config = FRAMEWORKS[framework];
164
215
 
@@ -178,13 +229,9 @@ function applyFramework(framework) {
178
229
  if (fs.existsSync(baseNjkPath)) {
179
230
  let content = fs.readFileSync(baseNjkPath, 'utf8');
180
231
  ALL_FRAMEWORKS.forEach(fw => {
181
- FRAMEWORKS[fw].njk.forEach(line => {
182
- content = njkComment(content, line);
183
- });
184
- });
185
- config.njk.forEach(line => {
186
- content = njkUncomment(content, line);
232
+ FRAMEWORKS[fw].njk.forEach(line => { content = njkComment(content, line); });
187
233
  });
234
+ config.njk.forEach(line => { content = njkUncomment(content, line); });
188
235
  fs.writeFileSync(baseNjkPath, content);
189
236
  }
190
237
 
@@ -192,49 +239,50 @@ function applyFramework(framework) {
192
239
  if (fs.existsSync(eleventyPath)) {
193
240
  let content = fs.readFileSync(eleventyPath, 'utf8');
194
241
  ALL_FRAMEWORKS.forEach(fw => {
195
- FRAMEWORKS[fw].eleventy.forEach(line => {
196
- content = slashComment(content, line);
197
- });
198
- });
199
- config.eleventy.forEach(line => {
200
- content = slashUncomment(content, line);
242
+ FRAMEWORKS[fw].eleventy.forEach(line => { content = slashComment(content, line); });
201
243
  });
244
+ config.eleventy.forEach(line => { content = slashUncomment(content, line); });
202
245
  fs.writeFileSync(eleventyPath, content);
203
246
  }
204
247
  }
205
248
 
206
- function askFramework() {
249
+ function applyLanguage(language) {
250
+ const eleventyPath = path.join(targetDir, '.eleventy.js');
251
+ if (!fs.existsSync(eleventyPath)) return;
252
+
253
+ let content = fs.readFileSync(eleventyPath, 'utf8');
254
+
255
+ if (language === LANGUAGE.TYPESCRIPT) {
256
+ content = slashComment(content, LANGUAGE_ELEVENTY.jsEntry);
257
+ content = slashUncomment(content, LANGUAGE_ELEVENTY.tsEntry);
258
+ } else {
259
+ content = slashUncomment(content, LANGUAGE_ELEVENTY.jsEntry);
260
+ content = slashComment(content, LANGUAGE_ELEVENTY.tsEntry);
261
+ }
262
+
263
+ fs.writeFileSync(eleventyPath, content);
264
+ }
265
+
266
+ // ── UI ────────────────────────────────────────────────────────────────────────
267
+
268
+ function askChoice(question, choices) {
207
269
  return new Promise((resolve) => {
208
- const choices = [
209
- { label: 'Bootstrap (default)', value: 'bootstrap' },
210
- { label: 'Bulma', value: 'bulma' },
211
- { label: 'Foundation', value: 'foundation' },
212
- { label: 'UIkit', value: 'uikit' },
213
- { label: 'None', value: 'none' }
214
- ];
215
270
  let selectedIndex = 0;
216
271
 
217
- log('\n>> Select a CSS framework (Use arrow keys and press Enter):\n');
272
+ log(`\n>> ${question} (Use arrow keys and press Enter):\n`);
218
273
 
219
274
  const render = (firstTime = false) => {
220
- if (!firstTime) {
221
- process.stdout.write(`\x1B[${choices.length}A`);
222
- }
223
- let output = '';
224
- choices.forEach((choice, index) => {
225
- if (index === selectedIndex) {
226
- output += ` \x1b[36m◉ ${choice.label}\x1b[0m\x1B[K\n`;
227
- } else {
228
- output += ` * ${choice.label}\x1B[K\n`;
229
- }
230
- });
275
+ if (!firstTime) process.stdout.write(`\x1B[${choices.length}A`);
276
+ const output = choices.map((choice, index) =>
277
+ index === selectedIndex
278
+ ? ` \x1b[36m◉ ${choice.label}\x1b[0m\x1B[K\n`
279
+ : ` * ${choice.label}\x1B[K\n`
280
+ ).join('');
231
281
  process.stdout.write(output);
232
282
  };
233
283
 
234
284
  readline.emitKeypressEvents(process.stdin);
235
- if (process.stdin.isTTY) {
236
- process.stdin.setRawMode(true);
237
- }
285
+ if (process.stdin.isTTY) process.stdin.setRawMode(true);
238
286
  process.stdin.resume();
239
287
 
240
288
  const onKeyPress = (str, key) => {
@@ -248,9 +296,7 @@ function askFramework() {
248
296
  render();
249
297
  } else if (key.name === 'return' || key.name === 'enter') {
250
298
  process.stdin.removeListener('keypress', onKeyPress);
251
- if (process.stdin.isTTY) {
252
- process.stdin.setRawMode(false);
253
- }
299
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
254
300
  process.stdin.pause();
255
301
  resolve(choices[selectedIndex].value);
256
302
  }
@@ -261,23 +307,26 @@ function askFramework() {
261
307
  });
262
308
  }
263
309
 
310
+ // ── INIT ──────────────────────────────────────────────────────────────────────
311
+
264
312
  async function init() {
265
- if (!fs.existsSync(targetDir)) {
266
- fs.mkdirSync(targetDir, { recursive: true });
267
- }
313
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
268
314
 
269
315
  log(`\n>> Creating berna-stencil project in ${targetDir}\n`);
270
316
 
271
- for (const target of COPY_TARGETS) {
272
- const src = path.join(templateDir, target);
317
+ const language = await askChoice('Select a language', LANGUAGE_CHOICES);
318
+ const framework = await askChoice('Select a CSS framework', FRAMEWORK_CHOICES);
319
+
320
+ for (const target of MANDATORY_COPY) {
321
+ const src = path.join(templateDir, target);
273
322
  const dest = path.join(targetDir, target);
274
- if (fs.existsSync(src)) {
275
- copyRecursive(src, dest);
276
- log(`+ ${target}`);
277
- }
323
+ if (!fs.existsSync(src)) continue;
324
+ const exclude = target === 'src/frontend' ? FRONTEND_EXCLUDE[language] : [];
325
+ copyRecursive(src, dest, exclude);
326
+ log(`+ ${target}`);
278
327
  }
279
328
 
280
- const configDest = path.join(targetDir, 'src/backend/config.php');
329
+ const configDest = path.join(targetDir, 'src/backend/config.php');
281
330
  const configExample = path.join(targetDir, 'src/backend/config.example.php');
282
331
  if (!fs.existsSync(configDest) && fs.existsSync(configExample)) {
283
332
  fs.copyFileSync(configExample, configDest);
@@ -285,30 +334,31 @@ async function init() {
285
334
  }
286
335
  deleteFileRecursive(targetDir, 'config.example.php');
287
336
 
288
- fs.writeFileSync(
289
- path.join(targetDir, 'package.json'),
290
- JSON.stringify(PROJECT_PACKAGE, null, 2)
291
- );
337
+ const pkg = { ...PROJECT_PACKAGE };
338
+
339
+ if (language === LANGUAGE.TYPESCRIPT) {
340
+ const tsSrc = path.join(templateDir, 'tsconfig.json');
341
+ const tsDest = path.join(targetDir, 'tsconfig.json');
342
+ fs.copyFileSync(tsSrc, tsDest);
343
+ log('+ tsconfig.json');
344
+ pkg.devDependencies = { ...pkg.devDependencies, typescript: 'latest' };
345
+ }
346
+
347
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
292
348
  log('+ package.json');
293
349
 
294
- fs.writeFileSync(
295
- path.join(targetDir, '.gitignore'),
296
- GITIGNORE_CONTENT
297
- );
350
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), GITIGNORE_CONTENT);
298
351
  log('+ .gitignore');
299
352
 
300
353
  for (const dir of CREATE_DIRS) {
301
- const dest = path.join(targetDir, dir);
302
- fs.mkdirSync(dest, { recursive: true });
354
+ fs.mkdirSync(path.join(targetDir, dir), { recursive: true });
303
355
  }
304
356
 
305
- const framework = await askFramework();
306
357
  applyFramework(framework);
358
+ applyLanguage(language);
307
359
 
308
360
  log(`\n>> Done! Now run:\n`);
309
- if (process.argv[2]) {
310
- log(`cd ${process.argv[2]}`);
311
- }
361
+ if (process.argv[2]) log(`cd ${process.argv[2]}`);
312
362
  log('npm install');
313
363
  log('npm run serve\n');
314
364
  }
@@ -1,5 +1,7 @@
1
1
  # Assistant CLI
2
2
 
3
+ > Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
4
+
3
5
  An interactive CLI to manage pages without touching files manually.
4
6
 
5
7
  ```
@@ -1,5 +1,7 @@
1
1
  # Creating Pages
2
2
 
3
+ > Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
4
+
3
5
  The recommended way is via the **Assistant CLI**
4
6
 
5
7
  ## What gets created
@@ -1,70 +1,63 @@
1
1
  # JavaScript
2
2
 
3
- ## Page JS
3
+ > Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
4
4
 
5
- Each page has its own JS entry point in `src/frontend/js/pages/`
5
+ ## Page JS
6
6
 
7
- It is bundled and minified by esbuild and loaded automatically by `base.njk`
7
+ Each page has its own JS entry point in `src/frontend/js/pages/`, bundled and minified by esbuild and loaded automatically by `base.njk`.
8
8
 
9
9
  Import only what the page needs.
10
10
 
11
11
  ### examplePage.js <small>(`src/frontend/js/pages/`)</small>
12
+
12
13
  ```js
13
- import { showNotification } from '../modules/notification.js';
14
+ //===========================
15
+ // JAVASCRIPT MODULES IMPORTS
16
+ //===========================
17
+
18
+ // import { initExampleModule } from '../modules/exampleModule.js';
14
19
 
15
- import { initNormalizePhoneNumber } from '../modules/forms/normalizePhoneNumber.js';
20
+ //==========================
21
+ // PAGE CUSTOM JAVASCRIPT
22
+ //==========================
16
23
 
17
24
  document.addEventListener("DOMContentLoaded", () => {
18
- initNormalizePhoneNumber();
25
+ // initExampleModule();
19
26
  });
20
-
21
- showNotification("Page loaded", "success", 3000);
22
27
  ```
23
28
 
24
29
  ## Modules
25
30
 
26
- Modules live in `src/frontend/js/modules/`. Some must be called inside `DOMContentLoaded` as they interact with the DOM; others create elements dynamically and can be called anywhere.
27
-
28
- ### Call inside `DOMContentLoaded`
29
-
30
- | Module | Function |
31
- |---|---|
32
- | `modules/langSwitcher.js` | `initLangSwitcher()` |
33
- | `modules/forms/form.js` | `initFormListener()` |
34
- | `modules/forms/textAreaAutoExpand.js` | `initTextAreaAutoExpand()` |
35
- | `modules/forms/normalizePhoneNumber.js` | `initNormalizePhoneNumber()` |
36
-
37
- ### Call anywhere
38
-
39
- | Module | Function |
40
- |---|---|
41
- | `modules/notification.js` | `showNotification(text, type, duration)` |
42
-
43
- ### `showNotification` parameters
44
-
45
- | Parameter | Type | Default | Values |
46
- |---|---|---|---|
47
- | `text` | string | — | Any string |
48
- | `type` | string | `"info"` | `"success"`, `"info"`, `"error"` |
49
- | `duration` | number | `5000` | ms, or `-1` for persistent with spinner |
31
+ Modules live in `src/frontend/js/modules/`. Modules that interact with the DOM must be called inside `DOMContentLoaded`; others can be called anywhere.
50
32
 
51
33
  ## Adding a module
52
34
 
53
- Create a new `.js` file in `src/frontend/js/modules/`. You can organize them into subfolders freely.
35
+ Create a new `.js` file in `src/frontend/js/modules/`. Subfolders are allowed.
54
36
 
55
37
  Use ESM syntax — esbuild handles the bundling:
56
38
 
39
+ ### exampleModule.js <small>(`src/frontend/js/modules/`)</small>
40
+
57
41
  ```js
58
- // _yourModule.js
59
- export function yourFunction() {
60
- // ...
42
+ //==========================
43
+ // EXAMPLE MODULE
44
+ //==========================
45
+
46
+ export function exampleModule() {
47
+ // Example module logic
61
48
  }
62
49
  ```
63
50
 
64
51
  Then import it in the pages that need it:
65
52
 
66
53
  ```js
67
- import { yourFunction } from '../modules/yourModule.js';
54
+ import { exampleModule } from '../modules/exampleModule.js';
55
+ ```
56
+
57
+ In TypeScript, omit the extension:
58
+
59
+ ```ts
60
+ import { exampleModule } from '../modules/exampleModule';
68
61
  ```
69
62
 
70
63
  > ⚠️ Files inside `_tools/` run directly in Node.js without a bundler — use CommonJS (`require` / `module.exports`) there, not ESM.
package/package.json CHANGED
@@ -1,63 +1,64 @@
1
- {
2
- "name": "create-berna-stencil",
3
- "version": "2.0.15",
4
- "description": "Eleventy boilerplate with per-page SCSS/JS pipeline, esbuild bundling, multi-framework CSS support and a built-in page management CLI",
5
- "keywords": [],
6
- "author": "Michele Garofalo",
7
- "license": "Apache-2.0",
8
- "repository": {
9
- "type": "git",
10
- "url": "https://github.com/rhaastrake/berna-stencil"
11
- },
12
- "homepage": "https://github.com/rhaastrake/berna-stencil#readme",
13
- "bugs": {
14
- "url": "https://github.com/rhaastrake/berna-stencil/issues"
15
- },
16
- "bin": {
17
- "create-berna-stencil": "bin/create.js"
18
- },
19
- "files": [
20
- "bin/",
21
- "docs/",
22
- "src/",
23
- "_tools/",
24
- ".eleventy.js",
25
- ".eleventyignore",
26
- ".gitignore",
27
- "LICENSE",
28
- "NOTICE"
29
- ],
30
- "engines": {
31
- "node": ">=18.0.0"
32
- },
33
- "dependencies": {
34
- "@11ty/eleventy": "^3.1.2",
35
- "@11ty/eleventy-img": "^6.0.4",
36
- "bootstrap": "^5.3.8",
37
- "bootstrap-icons": "^1.13.1",
38
- "bulma": "^1.0.4",
39
- "foundation-sites": "^6.9.0",
40
- "github-markdown-css": "^5.9.0",
41
- "glob": "^13.0.6",
42
- "markdown-it": "^14.2.0",
43
- "uikit": "^3.25.13"
44
- },
45
- "devDependencies": {
46
- "concurrently": "^9.2.1",
47
- "esbuild": "^0.27.3",
48
- "sass": "^1.77.0"
49
- },
50
- "scripts": {
51
- "build:css": "sass src/frontend/scss:out/css --no-source-map --style=compressed --quiet",
52
- "build:js": "esbuild \"src/frontend/js/pages/*.js\" --bundle --outdir=out/js/pages --minify",
53
- "build:11ty": "eleventy",
54
- "build": "npm run clean && npm run build:css && npm run build:js && npm run build:11ty",
55
- "serve:css": "sass --watch src/frontend/scss:out/css --no-source-map --quiet",
56
- "serve:js": "esbuild \"src/frontend/js/pages/*.js\" --bundle --outdir=out/js/pages --watch",
57
- "serve:11ty": "eleventy --serve --quiet",
58
- "clean": "node _tools/cleanOutput.js",
59
- "serve": "npm run clean && concurrently \"npm run serve:11ty\" \"npm run serve:css\" \"npm run serve:js\"",
60
- "assistant": "node _tools/assistant.js",
61
- "postinstall": "cd src/backend/_core && composer install --quiet"
62
- }
63
- }
1
+ {
2
+ "name": "create-berna-stencil",
3
+ "version": "2.2.0",
4
+ "description": "Eleventy boilerplate with per-page SCSS/JS pipeline, esbuild bundling, multi-framework CSS support and a built-in page management CLI",
5
+ "keywords": [],
6
+ "author": "Michele Garofalo",
7
+ "license": "Apache-2.0",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/rhaastrake/berna-stencil"
11
+ },
12
+ "homepage": "https://github.com/rhaastrake/berna-stencil#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/rhaastrake/berna-stencil/issues"
15
+ },
16
+ "bin": {
17
+ "create-berna-stencil": "bin/create.js"
18
+ },
19
+ "files": [
20
+ "bin/",
21
+ "docs/",
22
+ "src/",
23
+ "_tools/",
24
+ ".eleventy.js",
25
+ ".eleventyignore",
26
+ ".gitignore",
27
+ "LICENSE",
28
+ "NOTICE"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@11ty/eleventy": "^3.1.2",
35
+ "@11ty/eleventy-img": "^6.0.4",
36
+ "bootstrap": "^5.3.8",
37
+ "bootstrap-icons": "^1.13.1",
38
+ "bulma": "^1.0.4",
39
+ "foundation-sites": "^6.9.0",
40
+ "github-markdown-css": "^5.9.0",
41
+ "glob": "^13.0.6",
42
+ "markdown-it": "^14.2.0",
43
+ "uikit": "^3.25.13"
44
+ },
45
+ "devDependencies": {
46
+ "concurrently": "^9.2.1",
47
+ "esbuild": "^0.27.3",
48
+ "sass": "^1.77.0"
49
+ },
50
+ "scripts": {
51
+ "build:css": "sass src/frontend/scss:out/css --no-source-map --style=compressed --quiet --load-path=node_modules",
52
+ "build:js": "node _tools/buildJs.js",
53
+ "build:11ty": "eleventy",
54
+ "build": "npm run clean && npm run build:css && npm run build:js && npm run build:11ty",
55
+ "serve:css": "sass --watch src/frontend/scss:out/css --no-source-map --quiet --load-path=node_modules",
56
+ "serve:js": "node _tools/buildJs.js --watch",
57
+ "serve:11ty": "eleventy --serve --quiet",
58
+ "clean": "node _tools/cleanOutput.js",
59
+ "serve": "npm run clean && concurrently \"npm run serve:11ty\" \"npm run serve:css\" \"npm run serve:js\"",
60
+ "assistant": "node _tools/assistant.js",
61
+ "postinstall": "cd src/backend/_core && composer install --quiet"
62
+ },
63
+ "outputDir": "out"
64
+ }