nibula 1.5.3 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +2 -0
- package/bin/create.js +1 -1
- package/bin/delegate.js +58 -0
- package/bin/nibula.js +4 -19
- package/docs/Assistant CLI.md +3 -3
- package/docs/Components.md +68 -12
- package/docs/Creating pages.md +18 -13
- package/docs/Head and SEO.md +7 -6
- package/docs/Javascript.md +11 -9
- package/docs/Styling with SCSS.md +9 -11
- package/package.json +17 -22
- package/src/frontend/components/welcome.njk +1 -0
- package/src/frontend/data/pages.json +27 -0
- package/src/frontend/data/site.json +1 -28
- package/src/frontend/js/global.js +11 -0
- package/src/frontend/js/pages/404.js +2 -1
- package/src/frontend/js/pages/homepage.js +2 -1
- package/src/frontend/layouts/base.njk +23 -19
- package/src/frontend/scss/{modules/_global.scss → _global.scss} +9 -10
- package/src/frontend/scss/_root.scss +14 -0
- package/src/frontend/scss/modules/_animations.scss +1 -1
- package/src/frontend/scss/modules/_footer.scss +2 -1
- package/src/frontend/scss/modules/_header.scss +1 -2
- package/src/frontend/scss/modules/_mobile.scss +1 -1
- package/src/frontend/scss/modules/_typography.scss +5 -1
- package/src/frontend/scss/pages/404.scss +2 -2
- package/src/frontend/scss/pages/homepage.scss +2 -2
- package/src/frontend/sitemap.njk +1 -1
- package/src/frontend/ts/global.ts +12 -0
- package/src/frontend/ts/pages/404.ts +2 -1
- package/src/frontend/ts/pages/homepage.ts +2 -1
- package/tools/cli/ui.js +17 -3
- package/tools/config/messages.json +10 -8
- package/tools/config/settings.json +2 -6
- package/tools/lib/pageActions.js +8 -5
- package/tools/lib/pagesData.js +88 -0
- package/tools/lib/paths.js +8 -5
- package/tools/lib/validation.js +1 -0
- package/tools/res/templates/template.js +1 -1
- package/tools/res/templates/template.scss +3 -3
- package/tools/res/templates/template.ts +2 -1
- package/src/frontend/js/modules/global.js +0 -10
- package/src/frontend/scss/modules/_root.scss +0 -35
- package/src/frontend/ts/modules/global.ts +0 -10
- package/tools/lib/siteData.js +0 -89
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// import { initExampleModule } from "./modules/exampleModule.js";
|
|
2
|
+
|
|
3
|
+
function initGlobal() {
|
|
4
|
+
// initExampleModule();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Global logic here
|
|
8
|
+
|
|
9
|
+
// Do not touch
|
|
10
|
+
// This instruction starts global.js itself
|
|
11
|
+
document.addEventListener('DOMContentLoaded', initGlobal);
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
<!-- This is the base of every page that you will create -->
|
|
2
2
|
|
|
3
3
|
<!DOCTYPE html>
|
|
4
|
-
<html lang="{{ site.lang or 'en' }}" data-bs-theme="{{ site.theme or 'light' }}">
|
|
4
|
+
<html lang="{{ site.lang or 'en' }}" data-theme="{{ site.theme or 'light' }}" data-bs-theme="{{ site.theme or 'light' }}">
|
|
5
5
|
|
|
6
6
|
<head>
|
|
7
7
|
<meta charset="UTF-8">
|
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
9
9
|
<meta name="color-scheme" content="{{ site.theme or 'light' }}">
|
|
10
10
|
|
|
11
|
-
{# Preconnect hints — establish early connections to external origins #}
|
|
12
|
-
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
|
13
|
-
|
|
14
11
|
{#╔══════════════════╗
|
|
15
12
|
║ PAGE DATA ║
|
|
16
13
|
╚══════════════════╝#}
|
|
17
14
|
|
|
18
15
|
{% set pageKey = title %}
|
|
19
|
-
{% set pageData =
|
|
16
|
+
{% set pageData = pages[pageKey] %}
|
|
20
17
|
|
|
21
18
|
{#╔══════════════════╗
|
|
22
19
|
║ SEO ║
|
|
@@ -27,15 +24,15 @@
|
|
|
27
24
|
<meta name="description" content="{{ pageData.seo.description or site.description }}">
|
|
28
25
|
<meta name="keywords" content="{{ pageData.seo.keywords or site.keywords }}">
|
|
29
26
|
<meta name="author" content="{{ site.author }}">
|
|
30
|
-
<meta name="theme-color" content="{{ site.
|
|
27
|
+
<meta name="theme-color" content="{{ site.theme }}">
|
|
31
28
|
|
|
32
29
|
{% if pageData.seo.noindex %}
|
|
33
30
|
<meta name="robots" content="noindex, follow">
|
|
34
31
|
{% endif %}
|
|
35
32
|
|
|
36
33
|
{# Open Graph #}
|
|
37
|
-
<meta property="og:title" content="{{ pageData.seo.title }}">
|
|
38
|
-
<meta property="og:description" content="{{ pageData.seo.description }}">
|
|
34
|
+
<meta property="og:title" content="{{ pageData.seo.title or title or site.title }}">
|
|
35
|
+
<meta property="og:description" content="{{ pageData.seo.description or site.description }}">
|
|
39
36
|
<meta property="og:type" content="website">
|
|
40
37
|
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
|
|
41
38
|
<meta property="og:image" content="{{ site.url }}{{ site.logo }}">
|
|
@@ -43,8 +40,8 @@
|
|
|
43
40
|
|
|
44
41
|
{# Twitter Card #}
|
|
45
42
|
<meta name="twitter:card" content="summary_large_image">
|
|
46
|
-
<meta name="twitter:title" content="{{ pageData.seo.title }}">
|
|
47
|
-
<meta name="twitter:description" content="{{ pageData.seo.description }}">
|
|
43
|
+
<meta name="twitter:title" content="{{ pageData.seo.title or title or site.title }}">
|
|
44
|
+
<meta name="twitter:description" content="{{ pageData.seo.description or site.description }}">
|
|
48
45
|
<meta name="twitter:image" content="{{ site.url }}{{ site.logo }}">
|
|
49
46
|
|
|
50
47
|
{#╔══════════════════════════════════════════╗
|
|
@@ -77,16 +74,16 @@
|
|
|
77
74
|
║ FAVICON ║
|
|
78
75
|
╚══════════════════╝#}
|
|
79
76
|
|
|
80
|
-
<link rel="icon" type="image/svg+xml" href="/assets/brand/favicon.svg">
|
|
81
|
-
<link rel="icon" type="image/png" sizes="32x32" href="/assets/brand/favicon-32.png">
|
|
82
|
-
<link rel="apple-touch-icon" sizes="180x180" href="/assets/brand/apple-touch-icon.png">
|
|
77
|
+
<link rel="icon" type="image/svg+xml" href="{{ '/assets/brand/favicon.svg' | url }}">
|
|
78
|
+
<link rel="icon" type="image/png" sizes="32x32" href="{{ '/assets/brand/favicon-32.png' | url }}">
|
|
79
|
+
<link rel="apple-touch-icon" sizes="180x180" href="{{ '/assets/brand/apple-touch-icon.png' | url }}">
|
|
83
80
|
|
|
84
81
|
{#╔══════════════════╗
|
|
85
82
|
║ CSS ║
|
|
86
83
|
╚══════════════════╝#}
|
|
87
84
|
|
|
88
85
|
{# Page CSS #}
|
|
89
|
-
<link rel="stylesheet" href="/css/pages/
|
|
86
|
+
<link rel="stylesheet" href="{{ ('/css/pages/' + title + '.css') | url }}"/>
|
|
90
87
|
|
|
91
88
|
{# Per-page CDN CSS #}
|
|
92
89
|
{% if pageData and pageData.cdn and pageData.cdn.css %}
|
|
@@ -112,10 +109,17 @@
|
|
|
112
109
|
╚══════════════════╝#}
|
|
113
110
|
|
|
114
111
|
{# Framework — uncomment the one you are using #}
|
|
115
|
-
|
|
116
|
-
{#
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
|
|
113
|
+
{# Bootstrap JS #}
|
|
114
|
+
<script src="{{ '/js/bootstrap.bundle.min.js' | url }}" defer></script>
|
|
115
|
+
|
|
116
|
+
{# Foundation JS #}
|
|
117
|
+
{# <script src="{{ '/js/foundation.min.js' | url }}" defer></script> #}
|
|
118
|
+
|
|
119
|
+
{# UIkit JS #}
|
|
120
|
+
{# <script src="{{ '/js/uikit.min.js' | url }}" defer></script> #}
|
|
121
|
+
{# <script src="{{ '/js/uikit-icons.min.js' | url }}" defer></script> #}
|
|
122
|
+
|
|
119
123
|
{# Bulma — no JS needed #}
|
|
120
124
|
|
|
121
125
|
{# Per-page CDN JS #}
|
|
@@ -126,7 +130,7 @@
|
|
|
126
130
|
{% endif %}
|
|
127
131
|
|
|
128
132
|
{# Page JS #}
|
|
129
|
-
<script src="/js/pages/
|
|
133
|
+
<script src="{{ ('/js/pages/' + title + '.js') | url }}"></script>
|
|
130
134
|
|
|
131
135
|
</body>
|
|
132
136
|
|
|
@@ -6,20 +6,19 @@
|
|
|
6
6
|
@use 'root' as root;
|
|
7
7
|
|
|
8
8
|
// Frameworks imports
|
|
9
|
-
@import "
|
|
10
|
-
// @import "
|
|
11
|
-
// @import "
|
|
12
|
-
// @import "
|
|
13
|
-
|
|
9
|
+
@import "modules/frameworks/bootstrap";
|
|
10
|
+
// @import "modules/frameworks/bulma";
|
|
11
|
+
// @import "modules/frameworks/foundation";
|
|
12
|
+
// @import "modules/frameworks/uikit";
|
|
14
13
|
|
|
15
14
|
// Modules imports
|
|
16
|
-
@import "typography";
|
|
15
|
+
@import "modules/typography";
|
|
17
16
|
|
|
18
|
-
@import "header";
|
|
19
|
-
@import "footer";
|
|
17
|
+
@import "modules/header";
|
|
18
|
+
@import "modules/footer";
|
|
20
19
|
|
|
21
|
-
@import "animations";
|
|
22
|
-
@import "mobile";
|
|
20
|
+
@import "modules/animations";
|
|
21
|
+
@import "modules/mobile";
|
|
23
22
|
|
|
24
23
|
//==========================
|
|
25
24
|
// Global css rules for the entire site
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// NOTE: This file contains only Sass variables (no CSS output).
|
|
2
|
+
// When imported with @import, all variables are global and may be
|
|
3
|
+
// overwritten by other imported files with the same variable names.
|
|
4
|
+
// Use @use 'root' as root; to avoid namespace collisions.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
//==========================
|
|
8
|
+
// STRUCTURE
|
|
9
|
+
//==========================
|
|
10
|
+
$header-height: 10vh;
|
|
11
|
+
$header-padding-y: 15px;
|
|
12
|
+
$header-padding-x: 15px;
|
|
13
|
+
|
|
14
|
+
$primary-color: rgba(0, 0, 0, 0.5);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//==========================
|
|
4
4
|
|
|
5
5
|
// Getting root rules from _root.scss
|
|
6
|
-
@use
|
|
6
|
+
@use "../root" as root;
|
|
7
7
|
|
|
8
8
|
header {
|
|
9
9
|
height: root.$header-height;
|
|
@@ -12,7 +12,6 @@ header {
|
|
|
12
12
|
justify-content: center;
|
|
13
13
|
align-items: center;
|
|
14
14
|
padding: root.$header-padding-y root.$header-padding-x;
|
|
15
|
-
background-color: root.$primary;
|
|
16
15
|
background-color: #2e7253;
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
// Use @use with a namespace to avoid framework variable conflicts:
|
|
6
6
|
// Example: root.$primary
|
|
7
|
-
@use "../
|
|
7
|
+
@use "../root" as root;
|
|
8
8
|
|
|
9
9
|
// This global module contains the import framework, necessary modules for each page and any other common styles
|
|
10
|
-
@import "../
|
|
10
|
+
@import "../global";
|
|
11
11
|
|
|
12
12
|
// Import any other module you need by import down here
|
|
13
13
|
// @import "../modules/moduleName";
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
// Use @use with a namespace to avoid framework variable conflicts:
|
|
6
6
|
// Example: root.$primary
|
|
7
|
-
@use "../
|
|
7
|
+
@use "../root" as root;
|
|
8
8
|
|
|
9
9
|
// This global module contains the import framework, necessary modules for each page and any other common styles
|
|
10
|
-
@import "../
|
|
10
|
+
@import "../global";
|
|
11
11
|
|
|
12
12
|
// Import any other module you need by import down here
|
|
13
13
|
// @import "../modules/moduleName";
|
package/src/frontend/sitemap.njk
CHANGED
|
@@ -5,7 +5,7 @@ eleventyExcludeFromCollections: true
|
|
|
5
5
|
<?xml version="1.0" encoding="utf-8"?>
|
|
6
6
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
7
7
|
{% for page in collections.all %}
|
|
8
|
-
{% set pageMeta =
|
|
8
|
+
{% set pageMeta = pages[page.data.title] %}
|
|
9
9
|
{% if not page.data.draft and '/404' not in page.url and not (pageMeta and pageMeta.seo.noindex) %}
|
|
10
10
|
<url>
|
|
11
11
|
<loc>{{ site.url }}{{ page.url | lower }}</loc>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// import { initExampleModule } from "/exampleModule";
|
|
2
|
+
import { initExampleModule } from "./modules/exampleModule"
|
|
3
|
+
|
|
4
|
+
function initGlobal() {
|
|
5
|
+
// initExampleModule();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Global logic here
|
|
9
|
+
|
|
10
|
+
// Do not touch
|
|
11
|
+
// This instruction starts global.ts itself
|
|
12
|
+
document.addEventListener('DOMContentLoaded', initGlobal);
|
package/tools/cli/ui.js
CHANGED
|
@@ -5,7 +5,6 @@ const { message } = require('../lib/logger');
|
|
|
5
5
|
const SYMBOLS = settings.cli.symbols;
|
|
6
6
|
const COLORS = settings.cli.colors;
|
|
7
7
|
const MENU = settings.cli.menu;
|
|
8
|
-
const BOX_WIDTH = settings.cli.boxWidth;
|
|
9
8
|
const BOX_TITLE = settings.cli.title;
|
|
10
9
|
const BOX_TOP_LEFT = '\u256d';
|
|
11
10
|
const BOX_TOP_RIGHT = '\u256e';
|
|
@@ -16,6 +15,17 @@ const BOX_VERTICAL = '\u2502';
|
|
|
16
15
|
const NEW_LINE = '\n';
|
|
17
16
|
const SPACE = ' ';
|
|
18
17
|
|
|
18
|
+
const LOCAL_FLAG = 'NIBULA_LOCAL';
|
|
19
|
+
const LOCAL_FLAG_VALUE = '1';
|
|
20
|
+
const TITLE_PADDING = 2;
|
|
21
|
+
|
|
22
|
+
const TITLE_SUFFIX = process.env[LOCAL_FLAG] === LOCAL_FLAG_VALUE
|
|
23
|
+
? ` (local v${require('../../package.json').version})`
|
|
24
|
+
: '';
|
|
25
|
+
|
|
26
|
+
const PLAIN_TITLE = `${BOX_TITLE}${TITLE_SUFFIX}`;
|
|
27
|
+
const BOX_WIDTH = Math.max(settings.cli.boxWidth, PLAIN_TITLE.length + TITLE_PADDING);
|
|
28
|
+
|
|
19
29
|
function line(colorName, text) {
|
|
20
30
|
return `${color[colorName]}${text}${color.reset}`;
|
|
21
31
|
}
|
|
@@ -41,10 +51,14 @@ function noticePrefix(text) {
|
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
function centeredTitle() {
|
|
44
|
-
const padding = BOX_WIDTH -
|
|
54
|
+
const padding = BOX_WIDTH - PLAIN_TITLE.length;
|
|
45
55
|
const left = SPACE.repeat(Math.ceil(padding / 2));
|
|
46
56
|
const right = SPACE.repeat(Math.floor(padding / 2));
|
|
47
|
-
|
|
57
|
+
|
|
58
|
+
if (!TITLE_SUFFIX) return `${left}${BOX_TITLE}${right}`;
|
|
59
|
+
|
|
60
|
+
const suffix = `${color.dim}${TITLE_SUFFIX}${color.reset}${color[COLORS.box]}${color.bold}`;
|
|
61
|
+
return `${left}${BOX_TITLE}${suffix}${right}`;
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
function renderMenu() {
|
|
@@ -34,14 +34,15 @@
|
|
|
34
34
|
"blockRemoved": "[cleaned] page block removed for \"{name}\"",
|
|
35
35
|
"blockRenamed": "[renamed] page block \"{source}\" \u2192 \"{destination}\""
|
|
36
36
|
},
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
"pagesData": {
|
|
38
|
+
"fileMissing": "[skip] not found: {path}",
|
|
39
|
+
"invalidStructure": "[error] {file} must contain an object of pages",
|
|
40
|
+
"parseFailed": "[error] cannot parse {file}: {error}",
|
|
41
|
+
"recordExists": "[skip] record \"{name}\" already exists",
|
|
42
|
+
"recordMissing": "[skip] record \"{name}\" not found",
|
|
43
|
+
"recordAdded": "[updated] record \"{name}\" added",
|
|
44
|
+
"recordRemoved": "[cleaned] record \"{name}\" removed",
|
|
45
|
+
"recordRenamed": "[updated] record \"{source}\" renamed to \"{destination}\""
|
|
45
46
|
},
|
|
46
47
|
"output": {
|
|
47
48
|
"emptyPath": "[skip] empty output path",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"nothingToClean": "(i) nothing to clean \u2192 {path}"
|
|
59
60
|
},
|
|
60
61
|
"cli": {
|
|
62
|
+
"usingLocalVersion": "Nibula cli local version ({local})",
|
|
61
63
|
"createPrompt": "Name of the new page: ",
|
|
62
64
|
"removePrompt": "Name of the page to remove: ",
|
|
63
65
|
"renameFromPrompt": "Page to rename: ",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"typescriptConfig": "tsconfig.json",
|
|
12
12
|
"routes": "src/frontend/routes",
|
|
13
13
|
"stylePages": "src/frontend/scss/pages",
|
|
14
|
+
"pagesData": "src/frontend/data/pages.json",
|
|
14
15
|
"siteData": "src/frontend/data/site.json",
|
|
15
16
|
"pageComponents": "src/frontend/layouts/page-components.njk",
|
|
16
17
|
"templates": "tools/res/templates"
|
|
@@ -79,11 +80,6 @@
|
|
|
79
80
|
"{pageName}",
|
|
80
81
|
"pages/{pageName}"
|
|
81
82
|
],
|
|
82
|
-
"dataKeys": {
|
|
83
|
-
"pages": "pages",
|
|
84
|
-
"seo": "seo",
|
|
85
|
-
"title": "title"
|
|
86
|
-
},
|
|
87
83
|
"defaultData": {
|
|
88
84
|
"seo": {
|
|
89
85
|
"title": "{title}",
|
|
@@ -101,7 +97,7 @@
|
|
|
101
97
|
"pageComponents": {
|
|
102
98
|
"anchor": "{% else %}",
|
|
103
99
|
"condition": "{% elif title == \"{camelName}\" %}",
|
|
104
|
-
"include": "{% include \"component.njk\" ignore missing %}"
|
|
100
|
+
"include": "{% include \"component-name.njk\" ignore missing %}"
|
|
105
101
|
},
|
|
106
102
|
"frontMatter": {
|
|
107
103
|
"title": "title: \"{camelName}\"",
|
package/tools/lib/pageActions.js
CHANGED
|
@@ -6,7 +6,7 @@ const { log } = require('./logger');
|
|
|
6
6
|
const { formatText } = require('./text');
|
|
7
7
|
const { languageSettings } = require('./project');
|
|
8
8
|
const { getPageArtifacts, pageExists } = require('./pageArtifacts');
|
|
9
|
-
const {
|
|
9
|
+
const { readPagesData, addPageData, removePageData, renamePageData } = require('./pagesData');
|
|
10
10
|
const { addPageBlock, removePageBlock, renamePageBlock } = require('./pageComponents');
|
|
11
11
|
|
|
12
12
|
const FRONT_MATTER_PATTERNS = Object.freeze({
|
|
@@ -63,7 +63,7 @@ function addPage(pageName) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
addPageBlock(pageName);
|
|
66
|
-
|
|
66
|
+
addPageData(pageName);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function renamePage(oldName, newName) {
|
|
@@ -75,6 +75,7 @@ function renamePage(oldName, newName) {
|
|
|
75
75
|
log('page.targetExists', { name: newName });
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
|
+
if (!readPagesData()) return;
|
|
78
79
|
|
|
79
80
|
const current = getPageArtifacts(oldName);
|
|
80
81
|
const next = getPageArtifacts(newName);
|
|
@@ -104,13 +105,15 @@ function renamePage(oldName, newName) {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
renamePageBlock(oldName, newName);
|
|
107
|
-
|
|
108
|
+
renamePageData(oldName, newName);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
function removePage(pageName) {
|
|
111
112
|
if (!pageExists(pageName)) {
|
|
112
113
|
log('page.doesNotExist', { name: pageName });
|
|
114
|
+
return;
|
|
113
115
|
}
|
|
116
|
+
if (!readPagesData()) return;
|
|
114
117
|
|
|
115
118
|
const { source, output } = getPageArtifacts(pageName);
|
|
116
119
|
const files = [source.style, source.script, source.route, ...output.files];
|
|
@@ -132,7 +135,7 @@ function removePage(pageName) {
|
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
removePageBlock(pageName);
|
|
135
|
-
|
|
138
|
+
removePageData(pageName);
|
|
136
139
|
|
|
137
140
|
const sourceDirectories = [source.style, source.script, source.route].map(file => path.dirname(file));
|
|
138
141
|
|
|
@@ -141,4 +144,4 @@ function removePage(pageName) {
|
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
146
|
|
|
144
|
-
module.exports = { addPage, removePage, renamePage };
|
|
147
|
+
module.exports = { addPage, removePage, renamePage };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const settings = require('../config/settings.json');
|
|
3
|
+
const { PATHS } = require('./paths');
|
|
4
|
+
const { exists, readText, writeJson } = require('./files');
|
|
5
|
+
const { log } = require('./logger');
|
|
6
|
+
const { toCamelCase, toTitleCase, formatValue } = require('./text');
|
|
7
|
+
|
|
8
|
+
function readPagesData() {
|
|
9
|
+
if (!exists(PATHS.pagesData)) {
|
|
10
|
+
log('pagesData.fileMissing', { path: PATHS.pagesData });
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const data = JSON.parse(readText(PATHS.pagesData));
|
|
15
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
16
|
+
log('pagesData.invalidStructure', { file: path.basename(PATHS.pagesData) });
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return data;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
log('pagesData.parseFailed', { file: path.basename(PATHS.pagesData), error: error.message });
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writePagesData(data) {
|
|
27
|
+
writeJson(PATHS.pagesData, data);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createRecord(pageName) {
|
|
31
|
+
return formatValue(settings.page.defaultData, { title: toTitleCase(pageName) });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function addPageData(pageName) {
|
|
35
|
+
const data = readPagesData();
|
|
36
|
+
if (!data) return;
|
|
37
|
+
|
|
38
|
+
const camelName = toCamelCase(pageName);
|
|
39
|
+
if (data[camelName]) {
|
|
40
|
+
log('pagesData.recordExists', { name: camelName });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
data[camelName] = createRecord(pageName);
|
|
45
|
+
|
|
46
|
+
writePagesData(data);
|
|
47
|
+
log('pagesData.recordAdded', { name: camelName });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function removePageData(pageName) {
|
|
51
|
+
const data = readPagesData();
|
|
52
|
+
if (!data) return;
|
|
53
|
+
|
|
54
|
+
const camelName = toCamelCase(pageName);
|
|
55
|
+
if (!data[camelName]) {
|
|
56
|
+
log('pagesData.recordMissing', { name: camelName });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
delete data[camelName];
|
|
61
|
+
writePagesData(data);
|
|
62
|
+
log('pagesData.recordRemoved', { name: camelName });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function renamePageData(oldName, newName) {
|
|
66
|
+
const data = readPagesData();
|
|
67
|
+
if (!data) return;
|
|
68
|
+
|
|
69
|
+
const oldCamelName = toCamelCase(oldName);
|
|
70
|
+
const newCamelName = toCamelCase(newName);
|
|
71
|
+
|
|
72
|
+
if (!data[oldCamelName]) {
|
|
73
|
+
log('pagesData.recordMissing', { name: oldCamelName });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (data[newCamelName]) {
|
|
77
|
+
log('pagesData.recordExists', { name: newCamelName });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
data[newCamelName] = data[oldCamelName];
|
|
82
|
+
delete data[oldCamelName];
|
|
83
|
+
|
|
84
|
+
writePagesData(data);
|
|
85
|
+
log('pagesData.recordRenamed', { source: oldCamelName, destination: newCamelName });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { readPagesData, addPageData, removePageData, renamePageData };
|
package/tools/lib/paths.js
CHANGED
|
@@ -2,11 +2,9 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const settings = require('../config/settings.json');
|
|
4
4
|
const { paint } = require('./colors');
|
|
5
|
-
const { message } = require('./logger');
|
|
6
5
|
|
|
7
6
|
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
|
|
8
7
|
const PATH_SEPARATOR = '/';
|
|
9
|
-
const NOT_INSIDE_PROJECT_MESSAGE = paint('red', message('project.notInside'));
|
|
10
8
|
|
|
11
9
|
let cachedProjectRoot = null;
|
|
12
10
|
|
|
@@ -24,7 +22,7 @@ function projectRoot() {
|
|
|
24
22
|
if (cachedProjectRoot) return cachedProjectRoot;
|
|
25
23
|
const root = findProjectRoot();
|
|
26
24
|
if (!root) {
|
|
27
|
-
console.error(
|
|
25
|
+
console.error(paint('red', require('./logger').message('project.notInside')));
|
|
28
26
|
process.exit(1);
|
|
29
27
|
}
|
|
30
28
|
cachedProjectRoot = root;
|
|
@@ -47,15 +45,20 @@ const PATHS = Object.freeze({
|
|
|
47
45
|
get routes() { return resolveFromProject(settings.paths.routes); },
|
|
48
46
|
get stylePages() { return resolveFromProject(settings.paths.stylePages); },
|
|
49
47
|
get siteData() { return resolveFromProject(settings.paths.siteData); },
|
|
48
|
+
get pagesData() { return resolveFromProject(settings.paths.pagesData); },
|
|
50
49
|
get pageComponents() { return resolveFromProject(settings.paths.pageComponents); },
|
|
51
50
|
get templates() { return resolveFromPackage(settings.paths.templates); },
|
|
52
51
|
});
|
|
53
52
|
|
|
53
|
+
function getNotInsideProjectMessage() {
|
|
54
|
+
return paint('red', require('./logger').message('project.notInside'));
|
|
55
|
+
}
|
|
56
|
+
|
|
54
57
|
module.exports = {
|
|
55
58
|
PATHS,
|
|
56
59
|
PACKAGE_ROOT,
|
|
57
|
-
|
|
60
|
+
getNotInsideProjectMessage,
|
|
58
61
|
findProjectRoot,
|
|
59
62
|
resolveFromProject,
|
|
60
63
|
resolveFromPackage,
|
|
61
|
-
};
|
|
64
|
+
};
|
package/tools/lib/validation.js
CHANGED
|
@@ -12,6 +12,7 @@ function requiredFiles() {
|
|
|
12
12
|
{ label: settings.paths.eleventyConfig, path: PATHS.eleventyConfig },
|
|
13
13
|
{ label: settings.paths.packageJson, path: PATHS.packageJson },
|
|
14
14
|
{ label: settings.paths.siteData, path: PATHS.siteData },
|
|
15
|
+
{ label: settings.paths.pagesData, path: PATHS.pagesData },
|
|
15
16
|
{ label: settings.paths.pageComponents, path: PATHS.pageComponents },
|
|
16
17
|
{ label: message('validation.templatesLabel'), path: PATHS.templates },
|
|
17
18
|
];
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
// Use @use with a namespace to avoid framework variable conflicts:
|
|
6
6
|
// Example: root.$primary
|
|
7
|
-
@use "../
|
|
7
|
+
@use "../root" as root;
|
|
8
8
|
|
|
9
9
|
// This global module contains the import framework, necessary modules for each page and any other common styles
|
|
10
|
-
@import "../
|
|
10
|
+
@import "../global";
|
|
11
11
|
|
|
12
12
|
// Import any other module you need by import down here
|
|
13
13
|
// @import "../modules/moduleName";
|
|
@@ -19,5 +19,5 @@
|
|
|
19
19
|
// Add any custom rule specific to this page below
|
|
20
20
|
// These rules override the framework and module styles
|
|
21
21
|
// body {
|
|
22
|
-
// background-color: root.$primary;
|
|
22
|
+
// background-color: root.$primary-color;
|
|
23
23
|
// }
|