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.
Files changed (46) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +2 -0
  3. package/bin/create.js +1 -1
  4. package/bin/delegate.js +58 -0
  5. package/bin/nibula.js +4 -19
  6. package/docs/Assistant CLI.md +3 -3
  7. package/docs/Components.md +68 -12
  8. package/docs/Creating pages.md +18 -13
  9. package/docs/Head and SEO.md +7 -6
  10. package/docs/Javascript.md +11 -9
  11. package/docs/Styling with SCSS.md +9 -11
  12. package/package.json +17 -22
  13. package/src/frontend/components/welcome.njk +1 -0
  14. package/src/frontend/data/pages.json +27 -0
  15. package/src/frontend/data/site.json +1 -28
  16. package/src/frontend/js/global.js +11 -0
  17. package/src/frontend/js/pages/404.js +2 -1
  18. package/src/frontend/js/pages/homepage.js +2 -1
  19. package/src/frontend/layouts/base.njk +23 -19
  20. package/src/frontend/scss/{modules/_global.scss → _global.scss} +9 -10
  21. package/src/frontend/scss/_root.scss +14 -0
  22. package/src/frontend/scss/modules/_animations.scss +1 -1
  23. package/src/frontend/scss/modules/_footer.scss +2 -1
  24. package/src/frontend/scss/modules/_header.scss +1 -2
  25. package/src/frontend/scss/modules/_mobile.scss +1 -1
  26. package/src/frontend/scss/modules/_typography.scss +5 -1
  27. package/src/frontend/scss/pages/404.scss +2 -2
  28. package/src/frontend/scss/pages/homepage.scss +2 -2
  29. package/src/frontend/sitemap.njk +1 -1
  30. package/src/frontend/ts/global.ts +12 -0
  31. package/src/frontend/ts/pages/404.ts +2 -1
  32. package/src/frontend/ts/pages/homepage.ts +2 -1
  33. package/tools/cli/ui.js +17 -3
  34. package/tools/config/messages.json +10 -8
  35. package/tools/config/settings.json +2 -6
  36. package/tools/lib/pageActions.js +8 -5
  37. package/tools/lib/pagesData.js +88 -0
  38. package/tools/lib/paths.js +8 -5
  39. package/tools/lib/validation.js +1 -0
  40. package/tools/res/templates/template.js +1 -1
  41. package/tools/res/templates/template.scss +3 -3
  42. package/tools/res/templates/template.ts +2 -1
  43. package/src/frontend/js/modules/global.js +0 -10
  44. package/src/frontend/scss/modules/_root.scss +0 -35
  45. package/src/frontend/ts/modules/global.ts +0 -10
  46. 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,4 +1,5 @@
1
- import '../modules/global.js';
1
+ import '../global.js';
2
+
2
3
  // import { initExampleModule } from '../modules/exampleModule';
3
4
 
4
5
  document.addEventListener("DOMContentLoaded", () => {
@@ -1,4 +1,5 @@
1
- import '../modules/global.js';
1
+ import '../global.js';
2
+
2
3
  // import { initExampleModule } from '../modules/exampleModule';
3
4
 
4
5
  document.addEventListener("DOMContentLoaded", () => {
@@ -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 = site.pages[pageKey] %}
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.theme_color }}">
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/{{ title }}.css"/>
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
- <script src="/js/bootstrap.bundle.min.js" defer></script>
116
- {# <script src="/js/foundation.min.js" defer></script> #}
117
- {# <script src="/js/uikit.min.js" defer></script> #}
118
- {# <script src="/js/uikit-icons.min.js" defer></script> #}
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/{{ title }}.js"></script>
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 "../modules/frameworks/bootstrap";
10
- // @import "../modules/frameworks/bulma";
11
- // @import "../modules/frameworks/foundation";
12
- // @import "../modules/frameworks/uikit";
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 'root' as root;
6
+ @use "../root" as root;
7
7
 
8
8
  .fade-in {
9
9
  animation: fadeIn 1s;
@@ -2,7 +2,8 @@
2
2
  // CSS RULES FOR FOOTER
3
3
  //==========================
4
4
 
5
- @use 'root' as root;
5
+ // Getting root rules from _root.scss
6
+ @use "../root" as root;
6
7
 
7
8
  footer {
8
9
  display: flex;
@@ -3,7 +3,7 @@
3
3
  //==========================
4
4
 
5
5
  // Getting root rules from _root.scss
6
- @use 'root' as root;
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
 
@@ -3,7 +3,7 @@
3
3
  //==========================
4
4
 
5
5
  // Getting root rules from _root.scss
6
- @use 'root' as root;
6
+ @use "../root" as root;
7
7
 
8
8
  //==========================
9
9
  // MEDIA QUERIES
@@ -1,5 +1,9 @@
1
+ //==========================
2
+ // CSS RULES FOR TYPOGRAPHY
3
+ //==========================
4
+
1
5
  // Getting root rules from _root.scss
2
- @use "root" as root;
6
+ @use "../root" as root;
3
7
 
4
8
  html {
5
9
  font-size: 16px;
@@ -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 "../modules/root" as root;
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 "../modules/global";
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 "../modules/root" as root;
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 "../modules/global";
10
+ @import "../global";
11
11
 
12
12
  // Import any other module you need by import down here
13
13
  // @import "../modules/moduleName";
@@ -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 = site.pages[page.data.title] %}
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);
@@ -1,4 +1,5 @@
1
- import '../modules/global.js';
1
+ import '../global';
2
+
2
3
  // import { initExampleModule } from '../modules/exampleModule';
3
4
 
4
5
  document.addEventListener("DOMContentLoaded", (): void => {
@@ -1,4 +1,5 @@
1
- import '../modules/global.js';
1
+ import '../global';
2
+
2
3
  // import { initExampleModule } from '../modules/exampleModule';
3
4
 
4
5
  document.addEventListener("DOMContentLoaded", (): void => {
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 - BOX_TITLE.length;
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
- return `${left}${BOX_TITLE}${right}`;
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
- "siteData": {
38
- "fileMissing": "[skip] not found: {path}",
39
- "parseFailed": "[error] cannot parse {file}: {error}",
40
- "recordExists": "[skip] record \"{name}\" already exists",
41
- "recordMissing": "[skip] record \"{name}\" not found",
42
- "recordAdded": "[updated] record \"{name}\" added",
43
- "recordRemoved": "[cleaned] record \"{name}\" removed",
44
- "recordRenamed": "[updated] record \"{source}\" renamed to \"{destination}\""
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}\"",
@@ -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 { addSiteData, removeSiteData, renameSiteData } = require('./siteData');
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
- addSiteData(pageName);
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
- renameSiteData(oldName, newName);
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
- removeSiteData(pageName);
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 };
@@ -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(NOT_INSIDE_PROJECT_MESSAGE);
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
- NOT_INSIDE_PROJECT_MESSAGE,
60
+ getNotInsideProjectMessage,
58
61
  findProjectRoot,
59
62
  resolveFromProject,
60
63
  resolveFromPackage,
61
- };
64
+ };
@@ -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
  ];
@@ -1,4 +1,4 @@
1
- import '../modules/global.js';
1
+ import '../global.js';
2
2
  // import { initExampleModule } from '../modules/exampleModule';
3
3
 
4
4
  document.addEventListener("DOMContentLoaded", () => {
@@ -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 "../modules/root" as root;
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 "../modules/global";
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
  // }
@@ -1,4 +1,5 @@
1
- import '../modules/global.js';
1
+ import '../global';
2
+
2
3
  // import { initExampleModule } from '../modules/exampleModule';
3
4
 
4
5
  document.addEventListener("DOMContentLoaded", (): void => {