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.
Files changed (51) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/CLAUDE.md +138 -1775
  3. package/README.md +49 -20
  4. package/dist/build.js +3 -0
  5. package/dist/cli.js +1 -0
  6. package/dist/commands/test.js +56 -0
  7. package/dist/defaults/CLAUDE.md +72 -6
  8. package/dist/gulp/tasks/defaults.js +20 -4
  9. package/dist/gulp/tasks/distribute.js +29 -26
  10. package/dist/gulp/tasks/jsonToHtml.js +86 -80
  11. package/dist/gulp/tasks/minifyHtml.js +55 -51
  12. package/dist/gulp/tasks/sass.js +7 -6
  13. package/dist/gulp/tasks/utils/template-transform.js +35 -35
  14. package/dist/index.js +4 -0
  15. package/dist/test/assert.js +120 -0
  16. package/dist/test/fixtures/consumer-site/_site/about.html +13 -0
  17. package/dist/test/fixtures/consumer-site/_site/assets/css/main.bundle.css +2 -0
  18. package/dist/test/fixtures/consumer-site/_site/assets/js/main.bundle.js +6 -0
  19. package/dist/test/fixtures/consumer-site/_site/build.json +11 -0
  20. package/dist/test/fixtures/consumer-site/_site/index.html +28 -0
  21. package/dist/test/fixtures/consumer-site/_site/service-worker.js +29 -0
  22. package/dist/test/fixtures/consumer-site/package.json +6 -0
  23. package/dist/test/harness/page/index.html +51 -0
  24. package/dist/test/index.js +63 -0
  25. package/dist/test/runner.js +402 -0
  26. package/dist/test/runners/boot.js +109 -0
  27. package/dist/test/runners/chromium.js +255 -0
  28. package/dist/test/server.js +127 -0
  29. package/dist/test/suites/boot/service-worker.test.js +84 -0
  30. package/dist/test/suites/boot/site-loads.test.js +65 -0
  31. package/dist/test/suites/build/cli.test.js +49 -0
  32. package/dist/test/suites/build/collect-text-nodes.test.js +37 -0
  33. package/dist/test/suites/build/dictionary.test.js +17 -0
  34. package/dist/test/suites/build/expect.test.js +59 -0
  35. package/dist/test/suites/build/exports.test.js +32 -0
  36. package/dist/test/suites/build/logger.test.js +62 -0
  37. package/dist/test/suites/build/manager.test.js +186 -0
  38. package/dist/test/suites/build/merge-jekyll-configs.test.js +95 -0
  39. package/dist/test/suites/build/mode-helpers.test.js +65 -0
  40. package/dist/test/suites/build/template-transform.test.js +94 -0
  41. package/dist/test/suites/build/templating-brackets.test.js +46 -0
  42. package/dist/test/suites/build/validate-yaml.test.js +60 -0
  43. package/dist/test/suites/page/dom-baseline.test.js +34 -0
  44. package/dist/test/suites/page/harness-globals.test.js +38 -0
  45. package/dist/test/suites/page/prerendered-icons.test.js +32 -0
  46. package/dist/utils/mode-helpers.js +84 -0
  47. package/docs/_legacy-claude-md.md +1832 -0
  48. package/docs/cross-context-helpers.md +75 -0
  49. package/docs/test-boot-layer.md +110 -0
  50. package/docs/test-framework.md +183 -0
  51. package/package.json +18 -16
@@ -0,0 +1,60 @@
1
+ // validateYAMLFrontMatter pure helper from src/gulp/tasks/utils/_validate-yaml.js.
2
+ // Used by audit to verify _posts / _layouts have valid frontmatter before Jekyll
3
+ // chokes on them with cryptic errors. We test the pure function via a temp file.
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const os = require('os');
8
+
9
+ module.exports = {
10
+ layer: 'build',
11
+ description: 'validateYAMLFrontMatter (utils/_validate-yaml.js)',
12
+ type: 'group',
13
+ tests: [
14
+ {
15
+ name: 'returns { valid: true } for a file with valid frontmatter',
16
+ run: async (ctx) => {
17
+ const { validateYAMLFrontMatter } = require('../../../gulp/tasks/utils/_validate-yaml.js');
18
+ const tmp = path.join(os.tmpdir(), `ujm-test-valid-${Date.now()}.md`);
19
+ fs.writeFileSync(tmp, '---\ntitle: Hello\nlayout: post\n---\nBody text.', 'utf8');
20
+ try {
21
+ const result = validateYAMLFrontMatter(tmp);
22
+ ctx.expect(result.valid).toBe(true);
23
+ } finally {
24
+ fs.unlinkSync(tmp);
25
+ }
26
+ },
27
+ },
28
+ {
29
+ name: 'returns { valid: true } when no frontmatter present',
30
+ run: async (ctx) => {
31
+ const { validateYAMLFrontMatter } = require('../../../gulp/tasks/utils/_validate-yaml.js');
32
+ const tmp = path.join(os.tmpdir(), `ujm-test-noyaml-${Date.now()}.md`);
33
+ fs.writeFileSync(tmp, 'Just markdown, no frontmatter.\n', 'utf8');
34
+ try {
35
+ const result = validateYAMLFrontMatter(tmp);
36
+ ctx.expect(result.valid).toBe(true);
37
+ } finally {
38
+ fs.unlinkSync(tmp);
39
+ }
40
+ },
41
+ },
42
+ {
43
+ name: 'flags malformed YAML frontmatter as invalid with error message',
44
+ run: async (ctx) => {
45
+ const { validateYAMLFrontMatter } = require('../../../gulp/tasks/utils/_validate-yaml.js');
46
+ const tmp = path.join(os.tmpdir(), `ujm-test-invalid-${Date.now()}.md`);
47
+ // Mismatched quotes — js-yaml will refuse to parse this.
48
+ fs.writeFileSync(tmp, '---\ntitle: "Unclosed\ndescription: foo\n---\nBody.', 'utf8');
49
+ try {
50
+ const result = validateYAMLFrontMatter(tmp);
51
+ ctx.expect(result.valid).toBe(false);
52
+ ctx.expect(typeof result.error).toBe('string');
53
+ ctx.expect(result.file).toBe(tmp);
54
+ } finally {
55
+ fs.unlinkSync(tmp);
56
+ }
57
+ },
58
+ },
59
+ ],
60
+ };
@@ -0,0 +1,34 @@
1
+ // Baseline page-layer sanity: the harness loads, DOM APIs work, fetch works
2
+ // against the local server. If THIS suite fails the harness is broken — no
3
+ // other page-layer test result is trustworthy.
4
+
5
+ module.exports = {
6
+ layer: 'page',
7
+ description: 'page-layer baseline (DOM + fetch + storage)',
8
+ type: 'group',
9
+ tests: [
10
+ {
11
+ name: 'document is interactive or complete',
12
+ run: async (ctx) => {
13
+ ctx.expect(['interactive', 'complete']).toContain(document.readyState);
14
+ },
15
+ },
16
+ {
17
+ name: 'fetch() works against the local harness server',
18
+ run: async (ctx) => {
19
+ const res = await fetch('/');
20
+ ctx.expect(res.ok).toBe(true);
21
+ const text = await res.text();
22
+ ctx.expect(text).toContain('UJM Test Harness');
23
+ },
24
+ },
25
+ {
26
+ name: 'localStorage is available',
27
+ run: async (ctx) => {
28
+ localStorage.setItem('ujm-test-key', 'value');
29
+ ctx.expect(localStorage.getItem('ujm-test-key')).toBe('value');
30
+ localStorage.removeItem('ujm-test-key');
31
+ },
32
+ },
33
+ ],
34
+ };
@@ -0,0 +1,38 @@
1
+ // Verify the page-layer harness provides the globals UJM's frontend Manager
2
+ // reads at runtime: window.Configuration with brand/theme/web_manager, plus
3
+ // document.documentElement.dataset.{pagePath,assetPath}.
4
+ //
5
+ // These are what Jekyll-rendered consumer pages provide. The harness stubs
6
+ // them so we can test frontend Manager logic without a full Jekyll build.
7
+
8
+ module.exports = {
9
+ layer: 'page',
10
+ description: 'harness globals (window.Configuration + dataset)',
11
+ type: 'group',
12
+ tests: [
13
+ {
14
+ name: 'window.Configuration has brand + theme + web_manager',
15
+ run: async (ctx) => {
16
+ ctx.expect(typeof window.Configuration).toBe('object');
17
+ ctx.expect(window.Configuration.brand.id).toBeTruthy();
18
+ ctx.expect(window.Configuration.theme.id).toBeTruthy();
19
+ ctx.expect(window.Configuration.web_manager).toBeTruthy();
20
+ },
21
+ },
22
+ {
23
+ name: 'document.documentElement.dataset.pagePath is set',
24
+ run: async (ctx) => {
25
+ ctx.expect(typeof document.documentElement.dataset.pagePath).toBe('string');
26
+ // No leading slash (Manager.initialize strips before using).
27
+ // Harness sets it to '/' so the assertion just checks shape.
28
+ ctx.expect(document.documentElement.dataset.pagePath).toBe('/');
29
+ },
30
+ },
31
+ {
32
+ name: 'UJ_TEST_MODE is signalled on globalThis',
33
+ run: async (ctx) => {
34
+ ctx.expect(globalThis.UJ_TEST_MODE).toBe(true);
35
+ },
36
+ },
37
+ ],
38
+ };
@@ -0,0 +1,32 @@
1
+ // The harness ships a single icon in <template id="prerendered-icons">.
2
+ // UJM's libs/prerendered-icons.js exposes getPrerenderedIcon(name, classes)
3
+ // that pulls SVG from this template. We can't easily import that lib from
4
+ // in-tab (it's webpack-bundled ESM), so we reimplement the lookup logic
5
+ // inline and assert the template + its data-icon contract.
6
+
7
+ module.exports = {
8
+ layer: 'page',
9
+ description: 'prerendered icons template lookup',
10
+ type: 'group',
11
+ tests: [
12
+ {
13
+ name: 'template#prerendered-icons exists and has the test icon',
14
+ run: async (ctx) => {
15
+ const tmpl = document.getElementById('prerendered-icons');
16
+ ctx.expect(tmpl).toBeTruthy();
17
+ ctx.expect(tmpl.tagName.toLowerCase()).toBe('template');
18
+ const svg = tmpl.content.querySelector('[data-icon="test"]');
19
+ ctx.expect(svg).toBeTruthy();
20
+ ctx.expect(svg.tagName.toLowerCase()).toBe('svg');
21
+ },
22
+ },
23
+ {
24
+ name: 'looking up a missing icon returns null',
25
+ run: async (ctx) => {
26
+ const tmpl = document.getElementById('prerendered-icons');
27
+ const missing = tmpl.content.querySelector('[data-icon="nope-not-real"]');
28
+ ctx.expect(missing).toBeNull();
29
+ },
30
+ },
31
+ ],
32
+ };
@@ -0,0 +1,84 @@
1
+ // Runtime mode helpers (BEM/EM/BXM-pattern), shared across UJM's Managers
2
+ // (build-time `src/build.js`, frontend ES module `src/index.js`, service worker
3
+ // `src/service-worker.js`).
4
+ //
5
+ // Three orthogonal concepts:
6
+ // isDevelopment() — true when running in dev mode (jekyll dev server, not a
7
+ // production build). Detected via NODE_ENV / UJ_BUILD_MODE /
8
+ // site config.
9
+ // isProduction() — inverse. Running a production-built `_site/`.
10
+ // isTesting() — true when UJM's test framework is running this process. Set
11
+ // by UJM's test runners (UJ_TEST_MODE=true) and consumer test
12
+ // setups that want the same signal.
13
+ //
14
+ // Use these whenever behavior should differ by *what kind of process* you're in —
15
+ // shorter timeouts in tests, prompts suppressed in tests, dev-only banners.
16
+ // Don't use them for "should we hit dev or prod backends" — that's a config
17
+ // concern; use `getEnvironment()` for that (in build.js).
18
+ //
19
+ // Context caveat: in build-time Node (gulp / CLI), `window` is undefined. We
20
+ // detect via `typeof window` so the same code works in every context. In test
21
+ // mode the browser-side check is short-circuited via UJ_TEST_MODE / global so
22
+ // `isTesting()` returns a stable value regardless of which test layer is running.
23
+
24
+ function isDevelopment() {
25
+ // Build-time Node fallback.
26
+ if (typeof process !== 'undefined' && process.env) {
27
+ if (process.env.UJ_BUILD_MODE === 'true') return false;
28
+ if (process.env.NODE_ENV === 'development') return true;
29
+ if (process.env.UJ_IS_SERVER === 'true') return false;
30
+ }
31
+ // Browser-side: look at window.Configuration if present.
32
+ if (typeof window !== 'undefined' && window.Configuration && window.Configuration.uj) {
33
+ if (window.Configuration.uj.environment === 'development') return true;
34
+ if (window.Configuration.uj.environment === 'production') return false;
35
+ }
36
+ return false;
37
+ }
38
+
39
+ function isProduction() {
40
+ return !this.isDevelopment();
41
+ }
42
+
43
+ function isTesting() {
44
+ // Canonical signal — set by UJM's test runners and consumer test setups alike.
45
+ // Works in Node (process.env) AND in browser contexts (the harness preload sets
46
+ // globalThis.UJ_TEST_MODE before any consumer code runs).
47
+ if (typeof process !== 'undefined' && process.env && process.env.UJ_TEST_MODE === 'true') return true;
48
+ if (typeof globalThis !== 'undefined' && globalThis.UJ_TEST_MODE === true) return true;
49
+ return false;
50
+ }
51
+
52
+ // `getVersion()` — returns UJM's own version string.
53
+ // 1. `<cwd>/package.json#version` for build-time scripts.
54
+ // 2. null when nothing resolves (e.g. shipped browser bundle with no package.json).
55
+ function getVersion() {
56
+ try {
57
+ const path = require('path');
58
+ const pkg = require(path.join(process.cwd(), 'package.json'));
59
+ return pkg.version || null;
60
+ } catch (_) {
61
+ return null;
62
+ }
63
+ }
64
+
65
+ // Mix the helpers into a Manager constructor's prototype + the constructor itself
66
+ // (so `Manager.isTesting()` works statically too, matching BEM/EM/BXM pattern).
67
+ function attachTo(Manager) {
68
+ Manager.prototype.isDevelopment = isDevelopment;
69
+ Manager.prototype.isProduction = isProduction;
70
+ Manager.prototype.isTesting = isTesting;
71
+ Manager.prototype.getVersion = getVersion;
72
+ Manager.isDevelopment = isDevelopment;
73
+ Manager.isProduction = isProduction;
74
+ Manager.isTesting = isTesting;
75
+ Manager.getVersion = getVersion;
76
+ }
77
+
78
+ module.exports = {
79
+ attachTo,
80
+ isDevelopment,
81
+ isProduction,
82
+ isTesting,
83
+ getVersion,
84
+ };