ultimate-jekyll-manager 1.1.9 โ 1.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.
- package/CHANGELOG.md +40 -0
- package/CLAUDE.md +138 -1775
- package/README.md +49 -20
- package/dist/build.js +3 -0
- package/dist/cli.js +1 -0
- package/dist/commands/test.js +56 -0
- package/dist/defaults/CLAUDE.md +72 -6
- package/dist/gulp/tasks/defaults.js +20 -4
- package/dist/gulp/tasks/distribute.js +29 -26
- package/dist/gulp/tasks/jsonToHtml.js +86 -80
- package/dist/gulp/tasks/minifyHtml.js +55 -51
- package/dist/gulp/tasks/sass.js +7 -6
- package/dist/gulp/tasks/utils/template-transform.js +35 -35
- package/dist/index.js +4 -0
- package/dist/test/assert.js +120 -0
- package/dist/test/fixtures/consumer-site/_site/about.html +13 -0
- package/dist/test/fixtures/consumer-site/_site/assets/css/main.bundle.css +2 -0
- package/dist/test/fixtures/consumer-site/_site/assets/js/main.bundle.js +6 -0
- package/dist/test/fixtures/consumer-site/_site/build.json +11 -0
- package/dist/test/fixtures/consumer-site/_site/index.html +28 -0
- package/dist/test/fixtures/consumer-site/_site/service-worker.js +29 -0
- package/dist/test/fixtures/consumer-site/package.json +6 -0
- package/dist/test/harness/page/index.html +51 -0
- package/dist/test/index.js +63 -0
- package/dist/test/runner.js +402 -0
- package/dist/test/runners/boot.js +109 -0
- package/dist/test/runners/chromium.js +255 -0
- package/dist/test/server.js +127 -0
- package/dist/test/suites/boot/service-worker.test.js +84 -0
- package/dist/test/suites/boot/site-loads.test.js +65 -0
- package/dist/test/suites/build/cli.test.js +49 -0
- package/dist/test/suites/build/collect-text-nodes.test.js +37 -0
- package/dist/test/suites/build/dictionary.test.js +17 -0
- package/dist/test/suites/build/expect.test.js +59 -0
- package/dist/test/suites/build/exports.test.js +32 -0
- package/dist/test/suites/build/logger.test.js +62 -0
- package/dist/test/suites/build/manager.test.js +186 -0
- package/dist/test/suites/build/merge-jekyll-configs.test.js +95 -0
- package/dist/test/suites/build/mode-helpers.test.js +65 -0
- package/dist/test/suites/build/template-transform.test.js +94 -0
- package/dist/test/suites/build/templating-brackets.test.js +46 -0
- package/dist/test/suites/build/validate-yaml.test.js +60 -0
- package/dist/test/suites/page/dom-baseline.test.js +34 -0
- package/dist/test/suites/page/harness-globals.test.js +38 -0
- package/dist/test/suites/page/prerendered-icons.test.js +32 -0
- package/dist/utils/mode-helpers.js +84 -0
- package/docs/_legacy-claude-md.md +1832 -0
- package/docs/cross-context-helpers.md +75 -0
- package/docs/test-boot-layer.md +110 -0
- package/docs/test-framework.md +183 -0
- package/package.json +18 -16
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* **SEO Optimized**: Ultimate Jekyll is fully SEO optimized.
|
|
29
29
|
* **Blazingly Fast**: Ultimate Jekyll is blazingly fast.
|
|
30
30
|
* **NPM & Gulp**: Ultimate Jekyll is fueled by an intuitive incorporation of npm and gulp.
|
|
31
|
+
* **Built-in test framework**: three layers (`build` / `page` / `boot`) โ plain Node, headless Chromium tab, headless Chromium against real `_site/` with SW registration verification.
|
|
31
32
|
|
|
32
33
|
## ๐ Getting started
|
|
33
34
|
1. [Create a repo](https://github.com/itw-creative-works/ultimate-jekyll/generate) from the **Ultimate Jekyll** template.
|
|
@@ -37,6 +38,49 @@
|
|
|
37
38
|
npm start
|
|
38
39
|
```
|
|
39
40
|
|
|
41
|
+
## ๐งช Testing
|
|
42
|
+
|
|
43
|
+
UJM ships a built-in three-layer test harness. Write tests under `test/<layer>/*.test.js` and run with:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx mgr test # all layers
|
|
47
|
+
npx mgr test --layer build # plain Node, fast
|
|
48
|
+
npx mgr test --layer page # headless Chromium tab against harness HTML
|
|
49
|
+
npx mgr test --layer boot # headless Chromium against built _site/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Test files use Jest-compatible matchers:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
// test/build/config.test.js
|
|
56
|
+
const Manager = require('ultimate-jekyll-manager/build');
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
layer: 'build',
|
|
60
|
+
description: 'config has brand.id',
|
|
61
|
+
run: async (ctx) => {
|
|
62
|
+
const cfg = Manager.getConfig('project');
|
|
63
|
+
ctx.expect(cfg.brand.id).toBeTruthy();
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Boot tests run against your actually-built `_site/` after `npm run build`:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
// test/boot/site.test.js
|
|
72
|
+
module.exports = {
|
|
73
|
+
layer: 'boot',
|
|
74
|
+
description: 'home renders + SW registers',
|
|
75
|
+
inspect: async ({ site, page, expect }) => {
|
|
76
|
+
await page.goto(site.baseUrl + '/');
|
|
77
|
+
expect((await page.title()).length).toBeGreaterThan(0);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Full guide: [docs/test-framework.md](docs/test-framework.md). Boot layer deep-dive: [docs/test-boot-layer.md](docs/test-boot-layer.md).
|
|
83
|
+
|
|
40
84
|
## ๐ฆ How to sync with the template
|
|
41
85
|
1. Simply run `npm start` in Terminal to get all the latest updates from the **Ultimate Jekyll template** and launch your website in the browser.
|
|
42
86
|
|
|
@@ -761,23 +805,8 @@ Add this to any js file to ONLY run in development mode (it will be excluded in
|
|
|
761
805
|
/* @dev-only:end */
|
|
762
806
|
```
|
|
763
807
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
โ seconds, while allowing further customization down the line. โ
|
|
770
|
-
โ Right now i am struggling on the theme portion of this โ
|
|
771
|
-
โ project. I want the user to be able to define the theme in โ
|
|
772
|
-
โ their _config.yml (which currently they do by setting โ
|
|
773
|
-
โ theme.id). I have some themes from the official bootstrap โ
|
|
774
|
-
โ team. usually a theme comes with a frontend, a backend/admin โ
|
|
775
|
-
โ dashboard, and docs. these 3 subparts of the theme have โ
|
|
776
|
-
โ different html structure and css and js requirements. so i โ
|
|
777
|
-
โ need a super easy system that allows me to make a file in โ
|
|
778
|
-
โ the consuming project, say its the index.html for example, โ
|
|
779
|
-
โ and i should easily be able to put which subseciton (or โ
|
|
780
|
-
โ target as i call it) of the theme to use. so for an agency โ
|
|
781
|
-
โ website i will probably use the frontend target, while for a โ
|
|
782
|
-
โ chat app i will probably use the backend target. however, i โ
|
|
783
|
-
โ need to be able to use
|
|
808
|
+
## ๐งฐ Sister projects
|
|
809
|
+
|
|
810
|
+
- [Electron Manager (EM)](https://github.com/itw-creative-works/electron-manager) โ same patterns, but for Electron desktop apps
|
|
811
|
+
- [Browser Extension Manager (BXM)](https://github.com/itw-creative-works/browser-extension-manager) โ same patterns, but for cross-browser MV3 extensions
|
|
812
|
+
- [Backend Manager (BEM)](https://github.com/itw-creative-works/backend-manager) โ Firebase Functions backend framework
|
package/dist/build.js
CHANGED
|
@@ -351,5 +351,8 @@ Manager.prototype.processBatches = Manager.processBatches;
|
|
|
351
351
|
// };
|
|
352
352
|
// };
|
|
353
353
|
|
|
354
|
+
// Mix in cross-context mode helpers (isDevelopment/isProduction/isTesting/getVersion).
|
|
355
|
+
require('./utils/mode-helpers.js').attachTo(Manager);
|
|
356
|
+
|
|
354
357
|
// Export
|
|
355
358
|
module.exports = Manager;
|
package/dist/cli.js
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Libraries
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const Manager = require('../build.js');
|
|
5
|
+
const mgr = new Manager();
|
|
6
|
+
const logger = mgr.logger('test');
|
|
7
|
+
const { run } = require('../test/runner.js');
|
|
8
|
+
|
|
9
|
+
module.exports = async function (options) {
|
|
10
|
+
const layer = options.layer || 'all';
|
|
11
|
+
const filter = options.filter || null;
|
|
12
|
+
const reporter = options.reporter || 'pretty';
|
|
13
|
+
const integration = options.integration === true || options.integration === 'true';
|
|
14
|
+
|
|
15
|
+
if (integration) {
|
|
16
|
+
process.env.UJ_TEST_INTEGRATION = '1';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Canonical signal โ every Manager picks this up via isTesting().
|
|
20
|
+
process.env.UJ_TEST_MODE = 'true';
|
|
21
|
+
|
|
22
|
+
// When UJM itself runs its own boot-layer tests (the cwd's package.json is
|
|
23
|
+
// UJM's package.json), there's no real consumer site to target. Point the
|
|
24
|
+
// boot runner at the fixture under dist/test/fixtures/consumer-site unless
|
|
25
|
+
// the caller has already set UJ_TEST_BOOT_PROJECT explicitly.
|
|
26
|
+
if (!process.env.UJ_TEST_BOOT_PROJECT) {
|
|
27
|
+
try {
|
|
28
|
+
const cwdPkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
|
|
29
|
+
if (cwdPkg.name === 'ultimate-jekyll-manager') {
|
|
30
|
+
process.env.UJ_TEST_BOOT_PROJECT = path.join(__dirname, '..', 'test', 'fixtures', 'consumer-site');
|
|
31
|
+
}
|
|
32
|
+
} catch (_) { /* no package.json โ leave unset */ }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (reporter !== 'json') {
|
|
36
|
+
logger.log(`Running tests (layer=${layer}${filter ? ` filter="${filter}"` : ''}${integration ? ' +integration' : ''})`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const result = await run({ layer, filter, reporter });
|
|
40
|
+
|
|
41
|
+
if (reporter === 'json') {
|
|
42
|
+
// Final machine-readable summary.
|
|
43
|
+
process.stdout.write(JSON.stringify({
|
|
44
|
+
event: 'summary',
|
|
45
|
+
passed: result.passed,
|
|
46
|
+
failed: result.failed,
|
|
47
|
+
skipped: result.skipped,
|
|
48
|
+
total: result.passed + result.failed + result.skipped,
|
|
49
|
+
}) + '\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (result.failed > 0) {
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
throw new Error(`${result.failed} test(s) failed`);
|
|
55
|
+
}
|
|
56
|
+
};
|
package/dist/defaults/CLAUDE.md
CHANGED
|
@@ -1,8 +1,74 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# ========== Default Values ==========
|
|
2
|
+
# Ultimate Jekyll Manager (UJM) โ consumer project
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
You should have a full understanding of Ultimate Jekyll before editing this project, which can be found at: node_modules/ultimate-jekyll-manager/CLAUDE.md
|
|
4
|
+
> **Auto-managed file.** Everything between `# ========== Default Values ==========` and `# ========== Custom Values ==========` is owned by `ultimate-jekyll-manager` and rewritten on every `npx mgr setup`. Put your own project-specific notes BELOW the `Custom Values` marker โ that section is preserved verbatim across setups.
|
|
6
5
|
|
|
7
|
-
##
|
|
8
|
-
|
|
6
|
+
## Framework
|
|
7
|
+
|
|
8
|
+
This project consumes **Ultimate Jekyll Manager** (UJM) โ a comprehensive framework for building modern Jekyll-powered static sites. UJM provides one-line bootstrap per context (build / frontend / service-worker), a multi-stage gulp pipeline (defaults / distribute / webpack / sass / imagemin / jekyll / audit / translation / minifyHtml / serve), default Jekyll layouts + themes, a frontend ES-module Manager with dynamic per-page module loading, a service worker with Firebase Messaging + cache management, and a built-in three-layer test framework.
|
|
9
|
+
|
|
10
|
+
**Framework's own docs** (read these for deep-dives; both paths point to the same files, the absolute path works regardless of working directory):
|
|
11
|
+
- Top-level overview: `/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-manager/CLAUDE.md` (or `node_modules/ultimate-jekyll-manager/CLAUDE.md`)
|
|
12
|
+
- Subsystem references: `/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-manager/docs/` (or `node_modules/ultimate-jekyll-manager/docs/`)
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm start # dev: clean โ setup โ bundle exec gulp serve (Jekyll + BrowserSync + livereload)
|
|
18
|
+
npm run build # production build (UJ_BUILD_MODE=true): clean โ setup โ full gulp pipeline โ _site/
|
|
19
|
+
npm run deploy # build โ `npu sync --message='Deploy'` (publishes _site/)
|
|
20
|
+
npx mgr test # run framework + project test suites (build / page / boot layers)
|
|
21
|
+
npx mgr audit # HTML validation + spellcheck + optional Lighthouse
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Where things live
|
|
25
|
+
|
|
26
|
+
- `src/_config.yml` โ Jekyll config: brand, theme, meta, web_manager (Firebase). `Manager.getConfig('project')` reads this. **`brand.id` + `theme.id` are required.**
|
|
27
|
+
- `config/ultimate-jekyll-manager.json` โ UJM-specific config (JSON5): purgecss safelist, webpack target, imagemin options, distribute glob patterns. `Manager.getUJMConfig()` reads this.
|
|
28
|
+
- `src/pages/<name>.html` โ your custom pages. May contain frontmatter only (and use a UJM `blueprint/*` layout) to customize a default page without writing HTML.
|
|
29
|
+
- `src/_layouts/`, `src/_includes/` โ custom layouts / includes that override UJM's defaults.
|
|
30
|
+
- `src/assets/css/main.scss` โ shared SCSS. Theme load paths resolve via `__theme__` webpack alias.
|
|
31
|
+
- `src/assets/css/pages/<page>/index.scss` โ page-specific styles (compile to `dist/assets/css/pages/<page>/...bundle.css`).
|
|
32
|
+
- `src/assets/js/main.js` โ main JS entry.
|
|
33
|
+
- `src/assets/js/pages/<page>/index.js` โ page-specific JS. UJM's frontend Manager loads these dynamically based on `data-page-path`.
|
|
34
|
+
- `src/_includes/frontend/sections/{nav,footer}.json` โ JSON-driven nav/footer config. UJM renders these into HTML at build time.
|
|
35
|
+
- `hooks/build/{pre,post}.js`, `hooks/middleware/request.js` โ optional lifecycle hooks.
|
|
36
|
+
- `_site/` โ Jekyll output (the deployable site). Not committed.
|
|
37
|
+
- `dist/` โ intermediate compile output (webpack bundles, sass, processed images) before Jekyll merges them into `_site/`.
|
|
38
|
+
- `test/**/*.js` โ your project test suites (framework auto-runs them alongside its own).
|
|
39
|
+
|
|
40
|
+
## Per-context imports
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
// Frontend (browser ES module) โ every consumer page gets one
|
|
44
|
+
import Manager from 'ultimate-jekyll-manager';
|
|
45
|
+
new Manager().initialize();
|
|
46
|
+
|
|
47
|
+
// Service worker โ at the top of src/service-worker.js
|
|
48
|
+
importScripts('/build.js'); // exposes UJ_BUILD_JSON
|
|
49
|
+
// ...then construct your service-worker Manager
|
|
50
|
+
|
|
51
|
+
// Build-time / gulp / commands
|
|
52
|
+
const Manager = require('ultimate-jekyll-manager/build');
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Available APIs at runtime
|
|
56
|
+
|
|
57
|
+
After `new Manager().initialize()`, the frontend Manager exposes:
|
|
58
|
+
- `manager.webManager` โ Web Manager singleton (Firebase, auth, analytics, reactive `data-wm-bind` directives)
|
|
59
|
+
- `manager.isDevelopment()` / `isProduction()` / `isTesting()` / `getVersion()` โ cross-context helpers
|
|
60
|
+
|
|
61
|
+
At build time, `require('ultimate-jekyll-manager/build')` exposes:
|
|
62
|
+
- `Manager.getConfig(type)` โ read `_config.yml` (`'project'` or `'main'`)
|
|
63
|
+
- `Manager.getPackage(type)` โ read `package.json` (`'project'` or `'main'`)
|
|
64
|
+
- `Manager.getUJMConfig()` โ read `config/ultimate-jekyll-manager.json`
|
|
65
|
+
- `Manager.getEnvironment()` โ `'development'` or `'production'`
|
|
66
|
+
- `Manager.isBuildMode()` / `isQuickMode()` / `isServer()` / `actLikeProduction()` โ env-gated flags
|
|
67
|
+
- `Manager.logger(name)` โ timestamped logger instance
|
|
68
|
+
- `Manager.require(path)` โ escape hatch for UJM transitive deps (use sparingly)
|
|
69
|
+
|
|
70
|
+
# ========== Custom Values ==========
|
|
71
|
+
|
|
72
|
+
## Project-specific notes
|
|
73
|
+
|
|
74
|
+
Add anything specific to THIS project here. Edits below this line are preserved across `npx mgr setup` runs.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Manager = new (require('../../build.js'));
|
|
3
3
|
const logger = Manager.logger('defaults');
|
|
4
4
|
const { src, dest, watch, series } = require('gulp');
|
|
5
|
-
const
|
|
5
|
+
const { Transform } = require('node:stream');
|
|
6
6
|
const jetpack = require('fs-jetpack');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { minimatch } = require('minimatch');
|
|
@@ -98,6 +98,15 @@ const FILE_MAP = {
|
|
|
98
98
|
mergeLines: true, // Merge line-by-line instead of overwriting
|
|
99
99
|
},
|
|
100
100
|
|
|
101
|
+
// Consumer CLAUDE.md uses the same marker-based merge as .env/.gitignore.
|
|
102
|
+
// Framework owns the Default section; consumer owns everything below the Custom marker.
|
|
103
|
+
// Must come AFTER `**/*.md` (which sets overwrite: false) โ `getFileOptions` does
|
|
104
|
+
// last-match-wins, so this rule's `mergeLines: true` activates the merge path even though
|
|
105
|
+
// the catch-all would otherwise skip.
|
|
106
|
+
'CLAUDE.md': {
|
|
107
|
+
mergeLines: true,
|
|
108
|
+
},
|
|
109
|
+
|
|
101
110
|
// Config file with smart merging
|
|
102
111
|
'config/ultimate-jekyll-manager.json': {
|
|
103
112
|
overwrite: true,
|
|
@@ -319,8 +328,12 @@ function mergeLineBasedFiles(existingContent, newContent, fileName) {
|
|
|
319
328
|
result.push(DEFAULT_SECTION_MARKER);
|
|
320
329
|
result.push(...mergedDefaultSection);
|
|
321
330
|
|
|
322
|
-
// Add custom section
|
|
323
|
-
|
|
331
|
+
// Add custom section. Insert a single blank line before the marker, but only if the
|
|
332
|
+
// merged default doesn't already end with one โ otherwise we'd accumulate an extra blank
|
|
333
|
+
// line on every merge, breaking idempotency after the first re-run.
|
|
334
|
+
if (mergedDefaultSection.length === 0 || mergedDefaultSection[mergedDefaultSection.length - 1].trim() !== '') {
|
|
335
|
+
result.push('');
|
|
336
|
+
}
|
|
324
337
|
result.push(CUSTOM_SECTION_MARKER);
|
|
325
338
|
|
|
326
339
|
// First add any user lines that were in default section (moved to custom)
|
|
@@ -406,7 +419,9 @@ function defaults(complete, changedFile) {
|
|
|
406
419
|
}
|
|
407
420
|
|
|
408
421
|
function customTransform() {
|
|
409
|
-
return
|
|
422
|
+
return new Transform({
|
|
423
|
+
objectMode: true,
|
|
424
|
+
transform(file, _, callback) {
|
|
410
425
|
// Skip if it's a directory
|
|
411
426
|
if (file.isDirectory()) {
|
|
412
427
|
return callback(null, file);
|
|
@@ -539,6 +554,7 @@ function customTransform() {
|
|
|
539
554
|
|
|
540
555
|
// Complete
|
|
541
556
|
return callback();
|
|
557
|
+
},
|
|
542
558
|
});
|
|
543
559
|
}
|
|
544
560
|
function defaultsWatcher(complete) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Manager = new (require('../../build.js'));
|
|
3
3
|
const logger = Manager.logger('distribute');
|
|
4
4
|
const { src, dest, watch, series } = require('gulp');
|
|
5
|
-
const
|
|
5
|
+
const { Transform } = require('node:stream');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const jetpack = require('fs-jetpack');
|
|
8
8
|
const { template } = require('node-powertools');
|
|
@@ -180,38 +180,41 @@ function getFilesRecursive(dir) {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
function customTransform() {
|
|
183
|
-
return
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
return new Transform({
|
|
184
|
+
objectMode: true,
|
|
185
|
+
transform(file, _, callback) {
|
|
186
|
+
// Skip if it's a directory
|
|
187
|
+
if (file.isDirectory()) {
|
|
188
|
+
return callback(null, file);
|
|
189
|
+
}
|
|
188
190
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
// Get relative path from src base
|
|
192
|
+
const relativePath = path.relative(file.base, file.path).replace(/\\/g, '/');
|
|
191
193
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
// Log
|
|
195
|
+
if (LOUD) {
|
|
196
|
+
logger.log(`Processing file: ${relativePath}`);
|
|
197
|
+
}
|
|
196
198
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
// Change path if it starts with 'pages/'
|
|
200
|
+
// if (relativePath.startsWith('pages/')) {
|
|
201
|
+
// // Remove 'pages/' prefix
|
|
202
|
+
// const newRelativePath = relativePath.replace(/^pages\//, '');
|
|
201
203
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
// // Update file path to remove pages directory
|
|
205
|
+
// // This will make src/pages/index.html -> dist/index.html
|
|
206
|
+
// file.path = path.join(file.base, newRelativePath);
|
|
205
207
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
// // Log
|
|
209
|
+
// logger.log(` -> Moving from pages/ to root: ${newRelativePath}`);
|
|
210
|
+
// }
|
|
209
211
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
// Push the file
|
|
213
|
+
this.push(file);
|
|
212
214
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
// Continue
|
|
216
|
+
callback();
|
|
217
|
+
},
|
|
215
218
|
});
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Manager = new (require('../../build.js'));
|
|
3
3
|
const logger = Manager.logger('json-to-html');
|
|
4
4
|
const { src, dest, watch, series } = require('gulp');
|
|
5
|
-
const
|
|
5
|
+
const { Transform } = require('node:stream');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const jetpack = require('fs-jetpack');
|
|
8
8
|
const { template } = require('node-powertools');
|
|
@@ -42,90 +42,96 @@ function jsonToHtml(complete) {
|
|
|
42
42
|
|
|
43
43
|
// First, copy JSON files to _data directory
|
|
44
44
|
const jsonCopy = src(input)
|
|
45
|
-
.pipe(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
45
|
+
.pipe(new Transform({
|
|
46
|
+
objectMode: true,
|
|
47
|
+
transform(file, _, callback) {
|
|
48
|
+
if (file.isDirectory()) {
|
|
49
|
+
return callback(null, file);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Parse JSON5 content
|
|
54
|
+
const json5Content = file.contents.toString();
|
|
55
|
+
const parsedData = JSON5.parse(json5Content);
|
|
56
|
+
|
|
57
|
+
// Convert to regular JSON (pretty printed for readability)
|
|
58
|
+
const regularJson = JSON.stringify(parsedData, null, 2);
|
|
59
|
+
|
|
60
|
+
// Also write to _data directory for Jekyll to pick up
|
|
61
|
+
const relativePath = path.relative(file.base, file.path);
|
|
62
|
+
const dataPath = path.join(dataOutput, relativePath);
|
|
63
|
+
const dataDir = path.dirname(dataPath);
|
|
64
|
+
|
|
65
|
+
// Ensure directory exists
|
|
66
|
+
jetpack.dir(dataDir);
|
|
67
|
+
|
|
68
|
+
// Write regular JSON file to _data
|
|
69
|
+
jetpack.write(dataPath, regularJson);
|
|
70
|
+
|
|
71
|
+
// Track compiled files
|
|
72
|
+
compiled[dataPath] = true;
|
|
73
|
+
|
|
74
|
+
// Update file contents with regular JSON
|
|
75
|
+
file.contents = Buffer.from(regularJson);
|
|
76
|
+
|
|
77
|
+
callback(null, file);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
logger.error(`Error parsing JSON5 file ${file.path}: ${err.message}`);
|
|
80
|
+
callback(err);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
80
83
|
}))
|
|
81
84
|
.pipe(dest(dataOutput));
|
|
82
85
|
|
|
83
86
|
// Then create HTML wrapper files
|
|
84
87
|
const htmlGeneration = src(input)
|
|
85
|
-
.pipe(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
88
|
+
.pipe(new Transform({
|
|
89
|
+
objectMode: true,
|
|
90
|
+
transform(file, _, callback) {
|
|
91
|
+
// Skip if it's a directory
|
|
92
|
+
if (file.isDirectory()) {
|
|
93
|
+
return callback(null, file);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Get relative path from src/_includes
|
|
97
|
+
const relativePath = path.relative(file.base, file.path);
|
|
98
|
+
const relativeDir = path.dirname(relativePath);
|
|
99
|
+
const basename = path.basename(file.path, '.json');
|
|
100
|
+
|
|
101
|
+
// Convert path for Jekyll data reference
|
|
102
|
+
// _includes/frontend/sections/nav.json -> _includes.frontend.sections.nav
|
|
103
|
+
const dataPath = '_includes.' + relativePath
|
|
104
|
+
.replace(/\\/g, '/')
|
|
105
|
+
.replace('.json', '')
|
|
106
|
+
.split('/')
|
|
107
|
+
.join('.');
|
|
108
|
+
|
|
109
|
+
// Determine the template path
|
|
110
|
+
// Look for corresponding template in themes/{theme}/{target}/...
|
|
111
|
+
const templatePath = `themes/${config.theme.id}/${relativeDir}/${basename}.html`;
|
|
112
|
+
|
|
113
|
+
// Create the HTML content
|
|
114
|
+
const htmlContent = template(INCLUDE_TEMPLATE, {
|
|
115
|
+
dataPath: dataPath,
|
|
116
|
+
templatePath: templatePath
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Update file contents and extension
|
|
120
|
+
file.contents = Buffer.from(htmlContent);
|
|
121
|
+
file.path = file.path.replace('.json', '.html');
|
|
122
|
+
|
|
123
|
+
// Log transformation
|
|
124
|
+
logger.log(`Converting: ${relativePath} -> ${path.basename(file.path)}`);
|
|
125
|
+
logger.log(` Data path: site.data.${dataPath}`);
|
|
126
|
+
logger.log(` Template: ${templatePath}`);
|
|
127
|
+
|
|
128
|
+
// Track the full output path
|
|
129
|
+
const fullPath = path.resolve(output, path.relative(file.base, file.path));
|
|
130
|
+
compiled[fullPath] = true;
|
|
131
|
+
|
|
132
|
+
// Continue
|
|
133
|
+
callback(null, file);
|
|
134
|
+
},
|
|
129
135
|
}))
|
|
130
136
|
.pipe(dest(output));
|
|
131
137
|
|