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.
- package/README.md +1 -1
- package/_tools/assistant.js +92 -88
- package/_tools/modules/constants.js +23 -0
- package/_tools/modules/pageComponents.js +77 -0
- package/_tools/modules/updateData.js +25 -44
- package/_tools/modules/updateOutputPath.js +59 -63
- package/_tools/modules/updatePage.js +131 -104
- package/_tools/modules/utils.js +16 -19
- package/_tools/modules/validation.js +20 -0
- package/bin/create.js +74 -12
- package/out/.htaccess +16 -0
- package/out/404.html +163 -0
- package/out/assets/brand/favicon.svg +37 -0
- package/out/assets/brand/logo.svg +37 -0
- package/out/backend/.htaccess +7 -0
- package/out/backend/_core/composer.json +5 -0
- package/out/backend/_core/composer.lock +492 -0
- package/out/backend/_core/index.php +148 -0
- package/out/backend/_core/init.php +34 -0
- package/out/backend/_core/modules/RateLimiter.php +31 -0
- package/out/backend/_core/modules/Response.php +49 -0
- package/{src → out}/backend/_core/vendor/composer/autoload_static.php +10 -10
- package/{src → out}/backend/_core/vendor/composer/installed.json +6 -6
- package/{src → out}/backend/_core/vendor/composer/installed.php +2 -2
- package/out/backend/_core/vendor/graham-campbell/result-type/CHANGELOG.md +53 -0
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/LICENSE +21 -21
- package/out/backend/_core/vendor/graham-campbell/result-type/README.md +42 -0
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/composer.json +33 -33
- package/out/backend/_core/vendor/graham-campbell/result-type/phpunit.xml.dist +13 -0
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Error.php +121 -121
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Result.php +69 -69
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Success.php +120 -120
- package/out/backend/_core/vendor/graham-campbell/result-type/tests/ResultTest.php +95 -0
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/LICENSE +200 -200
- package/out/backend/_core/vendor/phpoption/phpoption/Makefile +17 -0
- package/out/backend/_core/vendor/phpoption/phpoption/README.md +201 -0
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/composer.json +50 -50
- package/out/backend/_core/vendor/phpoption/phpoption/phpstan-baseline.neon +44 -0
- package/out/backend/_core/vendor/phpoption/phpoption/phpstan.neon.dist +7 -0
- package/out/backend/_core/vendor/phpoption/phpoption/phpunit.xml.dist +13 -0
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php +175 -175
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/None.php +136 -136
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Option.php +434 -434
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Some.php +169 -169
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/EnsureTest.php +72 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/LazyOptionTest.php +357 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/NoneTest.php +153 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/OptionTest.php +166 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/SomeTest.php +194 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/bootstrap.php +8 -0
- package/out/backend/_core/vendor/phpoption/phpoption/vendor-bin/phpstan/composer.json +8 -0
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/Ctype.php +232 -232
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/LICENSE +19 -19
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/README.md +12 -12
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/bootstrap.php +50 -50
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/bootstrap80.php +46 -46
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/composer.json +38 -38
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/LICENSE +19 -19
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Mbstring.php +1077 -1077
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/README.md +13 -13
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php +119 -119
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php +1397 -1397
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php +5 -5
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php +1489 -1489
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap.php +171 -171
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap80.php +167 -167
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/composer.json +39 -39
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/LICENSE +19 -19
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Php80.php +115 -115
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/PhpToken.php +106 -106
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/README.md +25 -25
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php +31 -31
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php +16 -16
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php +20 -20
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php +16 -16
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php +16 -16
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/bootstrap.php +42 -42
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/composer.json +37 -37
- package/out/backend/_core/vendor/vlucas/phpdotenv/.editorconfig +15 -0
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/LICENSE +30 -30
- package/out/backend/_core/vendor/vlucas/phpdotenv/Makefile +17 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/README.md +370 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/UPGRADING.md +196 -0
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/composer.json +60 -60
- package/out/backend/_core/vendor/vlucas/phpdotenv/phpstan-baseline.neon +157 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/phpstan.neon.dist +7 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/phpunit.xml.dist +13 -0
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Dotenv.php +267 -267
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ValidationException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Loader.php +48 -48
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php +20 -20
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Resolver.php +65 -65
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Entry.php +59 -59
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php +299 -299
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lexer.php +58 -58
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lines.php +127 -127
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Parser.php +53 -53
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php +19 -19
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Value.php +88 -88
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php +15 -15
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php +89 -89
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php +80 -80
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php +88 -88
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php +85 -85
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php +110 -110
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php +48 -48
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php +64 -64
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php +91 -91
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php +17 -17
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php +104 -104
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php +88 -88
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php +27 -27
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php +107 -107
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php +272 -272
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php +51 -51
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Paths.php +44 -44
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Reader.php +81 -81
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/FileStore.php +72 -72
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php +141 -141
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php +17 -17
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StringStore.php +37 -37
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Util/Regex.php +112 -112
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Util/Str.php +108 -108
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Validator.php +207 -207
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/DotenvTest.php +387 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Loader/LoaderTest.php +86 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/EntryParserTest.php +234 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LexerTest.php +40 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LinesTest.php +53 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/ParserTest.php +98 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php +57 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php +75 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php +52 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php +75 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/RepositoryTest.php +305 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Store/StoreTest.php +141 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/ValidatorTest.php +479 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/.env +5 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/assertions.env +18 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/booleans.env +33 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/commented.env +15 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/empty.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/example.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/exported.env +7 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/immutable.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/integers.env +17 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/large.env +2 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multibyte.env +3 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiline.env +14 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiple.env +4 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/mutable.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/nested.env +15 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/quoted.env +11 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/specialchars.env +8 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/unicodevarnames.env +2 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/utf8-with-bom-encoding.env +3 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/windows.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/vendor-bin/phpstan/composer.json +15 -0
- package/out/backend/api/protected/example-protected.php +17 -0
- package/out/backend/api/public/example-public.php +17 -0
- package/out/backend/database/Database.php +24 -0
- package/out/backend/database/migrations/create_example_db.sql +1 -0
- package/out/backend/web.config +17 -0
- package/out/css/fonts/bootstrap-icons.woff +0 -0
- package/out/css/fonts/bootstrap-icons.woff2 +0 -0
- package/out/css/github-markdown-dark.css +1124 -0
- package/out/css/github-markdown-light.css +1124 -0
- package/out/css/pages/404.css +21517 -0
- package/out/css/pages/homepage.css +21508 -0
- package/out/data/site.json +44 -0
- package/out/index.html +1147 -0
- package/out/js/bootstrap.bundle.min.js +7 -0
- package/out/js/pages/js/pages/404.js +6 -0
- package/out/js/pages/js/pages/homepage.js +6 -0
- package/out/js/pages/ts/pages/404.js +6 -0
- package/out/js/pages/ts/pages/homepage.js +6 -0
- package/out/llms.txt +14 -0
- package/out/robots.txt +4 -0
- package/out/sitemap.xml +15 -0
- package/out/web.config +27 -0
- package/package.json +14 -47
- package/src/frontend/data/site.json +1 -1
- package/.gitignore +0 -4
- package/_tools/modules/updateIncludes.js +0 -81
- package/src/backend/config.example.php +0 -28
- /package/{src → out}/backend/_core/vendor/autoload.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/ClassLoader.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/InstalledVersions.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/LICENSE +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_classmap.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_files.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_namespaces.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_psr4.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_real.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/platform_check.php +0 -0
|
@@ -1,135 +1,162 @@
|
|
|
1
|
-
const
|
|
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 {
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
35
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
158
|
+
removePageBlock(pageName);
|
|
132
159
|
removeSiteData(pageName);
|
|
133
160
|
}
|
|
134
161
|
|
|
135
|
-
module.exports = { addPage, removePage, renamePage };
|
|
162
|
+
module.exports = { addPage, removePage, renamePage, pageExists };
|
package/_tools/modules/utils.js
CHANGED
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
const fs
|
|
2
|
-
const
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { PATHS } = require('./constants');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
function toCamelCase(value) {
|
|
5
|
+
return value.toLowerCase().replace(/[-_][a-z0-9]/g, (group) => group.slice(1).toUpperCase());
|
|
6
|
+
}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
.
|
|
22
|
-
.map(
|
|
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(
|
|
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 };
|
package/bin/create.js
CHANGED
|
@@ -4,6 +4,19 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const { writeSync } = require('fs');
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const color = {
|
|
10
|
+
reset: '\x1b[0m',
|
|
11
|
+
bold: '\x1b[1m',
|
|
12
|
+
dim: '\x1b[2m',
|
|
13
|
+
red: '\x1b[31m',
|
|
14
|
+
green: '\x1b[32m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
blue: '\x1b[34m',
|
|
17
|
+
magenta: '\x1b[35m',
|
|
18
|
+
cyan: '\x1b[36m',
|
|
19
|
+
};
|
|
7
20
|
|
|
8
21
|
// ── PATHS ────────────────────────────────────────────────────────────────────
|
|
9
22
|
|
|
@@ -134,8 +147,7 @@ const PROJECT_PACKAGE = {
|
|
|
134
147
|
"serve:11ty": "eleventy --serve --quiet",
|
|
135
148
|
"clean": "node _tools/cleanOutput.js",
|
|
136
149
|
"serve": "npm run clean && concurrently \"npm run serve:11ty\" \"npm run serve:css\" \"npm run serve:js\"",
|
|
137
|
-
"assistant": "node _tools/assistant.js"
|
|
138
|
-
"postinstall": "cd src/backend/_core && composer install --quiet"
|
|
150
|
+
"assistant": "node _tools/assistant.js"
|
|
139
151
|
},
|
|
140
152
|
dependencies: {
|
|
141
153
|
'@11ty/eleventy': '^3.1.2',
|
|
@@ -161,6 +173,10 @@ function log(msg) {
|
|
|
161
173
|
writeSync(1, msg + '\n');
|
|
162
174
|
}
|
|
163
175
|
|
|
176
|
+
function logAdd(name) {
|
|
177
|
+
log(`${color.green}+${color.reset} ${name}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
164
180
|
function escapeRegex(str) {
|
|
165
181
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
166
182
|
}
|
|
@@ -209,6 +225,48 @@ function njkUncomment(content, line) {
|
|
|
209
225
|
return content.split(`{# ${line} #}`).join(line);
|
|
210
226
|
}
|
|
211
227
|
|
|
228
|
+
function installDependencies() {
|
|
229
|
+
const backendCore = path.join(targetDir, 'src', 'backend', '_core');
|
|
230
|
+
|
|
231
|
+
log(`${color.blue}\n>> Installing Node modules...${color.reset}`);
|
|
232
|
+
const npm = spawnSync('npm', ['install'], {
|
|
233
|
+
cwd: targetDir,
|
|
234
|
+
stdio: 'inherit',
|
|
235
|
+
shell: process.platform === 'win32',
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (npm.status !== 0) {
|
|
239
|
+
log('\n(!) npm install failed. Finish manually:');
|
|
240
|
+
if (process.argv[2]) log(` cd ${process.argv[2]}`);
|
|
241
|
+
log(' npm install\n');
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!fs.existsSync(path.join(backendCore, 'composer.json'))) {
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const probe = spawnSync('composer', ['--version'], {
|
|
250
|
+
stdio: 'ignore',
|
|
251
|
+
shell: process.platform === 'win32',
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (probe.status !== 0) {
|
|
255
|
+
log('\n(!) Composer not found — skipping backend dependencies.');
|
|
256
|
+
log(' Install Composer, then run: cd src/backend/_core && composer install\n');
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
log(`\n${color.blue}>> Installing Composer modules...${color.reset}\n`);
|
|
261
|
+
spawnSync('composer', ['install'], {
|
|
262
|
+
cwd: backendCore,
|
|
263
|
+
stdio: 'inherit',
|
|
264
|
+
shell: process.platform === 'win32',
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
212
270
|
// ── APPLY ─────────────────────────────────────────────────────────────────────
|
|
213
271
|
|
|
214
272
|
function applyFramework(framework) {
|
|
@@ -313,25 +371,27 @@ function askChoice(question, choices) {
|
|
|
313
371
|
async function init() {
|
|
314
372
|
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
315
373
|
|
|
316
|
-
log(`\n>> Creating berna-stencil project in ${targetDir}\n`);
|
|
374
|
+
log(`\n>> ${color.magenta}Creating berna-stencil project in ${targetDir}\n${color.reset}`);
|
|
317
375
|
|
|
318
376
|
const language = await askChoice('Select a language', LANGUAGE_CHOICES);
|
|
319
377
|
const framework = await askChoice('Select a CSS framework', FRAMEWORK_CHOICES);
|
|
320
378
|
|
|
379
|
+
log('');
|
|
380
|
+
|
|
321
381
|
for (const target of MANDATORY_COPY) {
|
|
322
382
|
const src = path.join(templateDir, target);
|
|
323
383
|
const dest = path.join(targetDir, target);
|
|
324
384
|
if (!fs.existsSync(src)) continue;
|
|
325
385
|
const exclude = target === 'src/frontend' ? FRONTEND_EXCLUDE[language] : [];
|
|
326
386
|
copyRecursive(src, dest, exclude);
|
|
327
|
-
|
|
387
|
+
logAdd(target);
|
|
328
388
|
}
|
|
329
389
|
|
|
330
390
|
const configDest = path.join(targetDir, 'src/backend/config.php');
|
|
331
391
|
const configExample = path.join(targetDir, 'src/backend/config.example.php');
|
|
332
392
|
if (!fs.existsSync(configDest) && fs.existsSync(configExample)) {
|
|
333
393
|
fs.copyFileSync(configExample, configDest);
|
|
334
|
-
|
|
394
|
+
logAdd('src/backend/config.php');
|
|
335
395
|
}
|
|
336
396
|
deleteFileRecursive(targetDir, 'config.example.php');
|
|
337
397
|
|
|
@@ -341,15 +401,15 @@ async function init() {
|
|
|
341
401
|
const tsSrc = path.join(templateDir, 'tsconfig.json');
|
|
342
402
|
const tsDest = path.join(targetDir, 'tsconfig.json');
|
|
343
403
|
fs.copyFileSync(tsSrc, tsDest);
|
|
344
|
-
|
|
404
|
+
logAdd('tsconfig.json');
|
|
345
405
|
pkg.devDependencies = { ...pkg.devDependencies, typescript: 'latest' };
|
|
346
406
|
}
|
|
347
407
|
|
|
348
408
|
fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
349
|
-
|
|
409
|
+
logAdd('package.json');
|
|
350
410
|
|
|
351
411
|
fs.writeFileSync(path.join(targetDir, '.gitignore'), GITIGNORE_CONTENT);
|
|
352
|
-
|
|
412
|
+
logAdd('.gitignore');
|
|
353
413
|
|
|
354
414
|
for (const dir of CREATE_DIRS) {
|
|
355
415
|
fs.mkdirSync(path.join(targetDir, dir), { recursive: true });
|
|
@@ -358,10 +418,12 @@ async function init() {
|
|
|
358
418
|
applyFramework(framework);
|
|
359
419
|
applyLanguage(language);
|
|
360
420
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
log(
|
|
364
|
-
log(
|
|
421
|
+
installDependencies();
|
|
422
|
+
|
|
423
|
+
log(`\n${color.green}>> Done!${color.reset}`);
|
|
424
|
+
log(`${color.yellow}\nNow run:\n${color.reset}`);
|
|
425
|
+
if (process.argv[2]) log(` ${color.yellow}cd ${process.argv[2]}${color.reset}`);
|
|
426
|
+
log(` ${color.yellow}npm run serve${color.reset}\n`);
|
|
365
427
|
}
|
|
366
428
|
|
|
367
429
|
init();
|
package/out/.htaccess
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Options -Indexes
|
|
2
|
+
ErrorDocument 404 /404.html
|
|
3
|
+
ErrorDocument 403 /404.html
|
|
4
|
+
|
|
5
|
+
<IfModule mod_rewrite.c>
|
|
6
|
+
RewriteEngine On
|
|
7
|
+
RewriteBase /
|
|
8
|
+
|
|
9
|
+
RewriteRule ^(web\.config|llms\.txt|robots\.txt|sitemap\.xml|\..*)$ /404.html [L,NC]
|
|
10
|
+
|
|
11
|
+
RewriteRule ^api/(.*)$ backend/_core/index.php [QSA,L]
|
|
12
|
+
|
|
13
|
+
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
|
14
|
+
RewriteCond %{REQUEST_FILENAME} -d
|
|
15
|
+
RewriteRule ^ - [L]
|
|
16
|
+
</IfModule>
|
package/out/404.html
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<!-- This is the base of every page that you will create -->
|
|
2
|
+
|
|
3
|
+
<!DOCTYPE html>
|
|
4
|
+
<html lang="en" data-bs-theme="dark">
|
|
5
|
+
|
|
6
|
+
<head>
|
|
7
|
+
<meta charset="UTF-8">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
<link rel="canonical" href="https://yoursite.com/404.html"/>
|
|
21
|
+
<title>404 - Not found</title>
|
|
22
|
+
<meta name="description" content="Site description">
|
|
23
|
+
<meta name="keywords" content="keyword1, keyword2, keyword3">
|
|
24
|
+
<meta name="author" content="Name and surname">
|
|
25
|
+
<meta name="theme-color" content="">
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
<meta property="og:title" content="404 - Not found">
|
|
29
|
+
<meta property="og:description" content="">
|
|
30
|
+
<meta property="og:type" content="website">
|
|
31
|
+
<meta property="og:url" content="https://yoursite.com/404.html">
|
|
32
|
+
<meta property="og:image" content="https://yoursite.com/assets/brand/logo.svg">
|
|
33
|
+
<meta property="og:site-name" content="NaN">
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
37
|
+
<meta name="twitter:title" content="404 - Not found">
|
|
38
|
+
<meta name="twitter:description" content="">
|
|
39
|
+
<meta name="twitter:image" content="https://yoursite.com/assets/brand/logo.svg">
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
<script type="application/ld+json">
|
|
44
|
+
{
|
|
45
|
+
"@context": "https://schema.org",
|
|
46
|
+
"@type": "WebPage",
|
|
47
|
+
"name": "404 - Not found",
|
|
48
|
+
"description": "Site description",
|
|
49
|
+
"url": "https://yoursite.com/404.html",
|
|
50
|
+
"author": {
|
|
51
|
+
"@type": "Person",
|
|
52
|
+
"name": "Name and surname"
|
|
53
|
+
},
|
|
54
|
+
"publisher": {
|
|
55
|
+
"@type": "Organization",
|
|
56
|
+
"name": "Site name",
|
|
57
|
+
"logo": {
|
|
58
|
+
"@type": "ImageObject",
|
|
59
|
+
"url": "https://yoursite.com/assets/brand/logo.svg"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
<link rel="icon" type="image/svg+xml" href="/assets/brand/favicon.svg">
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
<link rel="stylesheet" href="/css/github-markdown-dark.css">
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
<link rel="stylesheet" href="/css/pages/404.css"/>
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
</head>
|
|
86
|
+
|
|
87
|
+
<body>
|
|
88
|
+
|
|
89
|
+
<header>
|
|
90
|
+
<nav>
|
|
91
|
+
<a class="logo" href="/">
|
|
92
|
+
<img src="/assets/brand/logo.svg" alt="Site title" height="40">
|
|
93
|
+
</a>
|
|
94
|
+
<div class="nav-links">
|
|
95
|
+
<a class="btn" href="/">Homepage</a>
|
|
96
|
+
<a class="btn" href="/example-page">Example page</a>
|
|
97
|
+
</div>
|
|
98
|
+
</nav>
|
|
99
|
+
</header>
|
|
100
|
+
|
|
101
|
+
<main>
|
|
102
|
+
|
|
103
|
+
<!-- !IMPORTANT -->
|
|
104
|
+
<!-- This is the only page that you need to modify statically -->
|
|
105
|
+
|
|
106
|
+
<div class="fade-in not-found">
|
|
107
|
+
<h1>Oops! Page not found</h1>
|
|
108
|
+
<a href="/">Return to homepage</a>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
</main>
|
|
115
|
+
|
|
116
|
+
<footer>
|
|
117
|
+
<div>
|
|
118
|
+
<h6>Site title</h6>
|
|
119
|
+
<p>Site description</p>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div>
|
|
123
|
+
<h6>Sitemap</h6>
|
|
124
|
+
<ul>
|
|
125
|
+
<li><a href="/">Homepage</a></li>
|
|
126
|
+
</ul>
|
|
127
|
+
</div>
|
|
128
|
+
<div>
|
|
129
|
+
© 2026 Copyright text
|
|
130
|
+
</div>
|
|
131
|
+
<div>
|
|
132
|
+
<h6>Legal</h6>
|
|
133
|
+
<small>
|
|
134
|
+
<ul>
|
|
135
|
+
<li><a href="">Privacy Policy</a></li>
|
|
136
|
+
<li><a href="">Cookie Policy</a></li>
|
|
137
|
+
<li><a href="">Terms and conditions</a></li>
|
|
138
|
+
</ul>
|
|
139
|
+
</small>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</footer>
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
<script src="/js/bootstrap.bundle.min.js" defer></script>
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
<script src="/js/pages/404.js"></script>
|
|
160
|
+
|
|
161
|
+
</body>
|
|
162
|
+
|
|
163
|
+
</html>
|