ultimate-jekyll-manager 1.1.10 → 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 (46) hide show
  1. package/CHANGELOG.md +22 -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 +15 -2
  9. package/dist/index.js +4 -0
  10. package/dist/test/assert.js +120 -0
  11. package/dist/test/fixtures/consumer-site/_site/about.html +13 -0
  12. package/dist/test/fixtures/consumer-site/_site/assets/css/main.bundle.css +2 -0
  13. package/dist/test/fixtures/consumer-site/_site/assets/js/main.bundle.js +6 -0
  14. package/dist/test/fixtures/consumer-site/_site/build.json +11 -0
  15. package/dist/test/fixtures/consumer-site/_site/index.html +28 -0
  16. package/dist/test/fixtures/consumer-site/_site/service-worker.js +29 -0
  17. package/dist/test/fixtures/consumer-site/package.json +6 -0
  18. package/dist/test/harness/page/index.html +51 -0
  19. package/dist/test/index.js +63 -0
  20. package/dist/test/runner.js +402 -0
  21. package/dist/test/runners/boot.js +109 -0
  22. package/dist/test/runners/chromium.js +255 -0
  23. package/dist/test/server.js +127 -0
  24. package/dist/test/suites/boot/service-worker.test.js +84 -0
  25. package/dist/test/suites/boot/site-loads.test.js +65 -0
  26. package/dist/test/suites/build/cli.test.js +49 -0
  27. package/dist/test/suites/build/collect-text-nodes.test.js +37 -0
  28. package/dist/test/suites/build/dictionary.test.js +17 -0
  29. package/dist/test/suites/build/expect.test.js +59 -0
  30. package/dist/test/suites/build/exports.test.js +32 -0
  31. package/dist/test/suites/build/logger.test.js +62 -0
  32. package/dist/test/suites/build/manager.test.js +186 -0
  33. package/dist/test/suites/build/merge-jekyll-configs.test.js +95 -0
  34. package/dist/test/suites/build/mode-helpers.test.js +65 -0
  35. package/dist/test/suites/build/template-transform.test.js +94 -0
  36. package/dist/test/suites/build/templating-brackets.test.js +46 -0
  37. package/dist/test/suites/build/validate-yaml.test.js +60 -0
  38. package/dist/test/suites/page/dom-baseline.test.js +34 -0
  39. package/dist/test/suites/page/harness-globals.test.js +38 -0
  40. package/dist/test/suites/page/prerendered-icons.test.js +32 -0
  41. package/dist/utils/mode-helpers.js +84 -0
  42. package/docs/_legacy-claude-md.md +1832 -0
  43. package/docs/cross-context-helpers.md +75 -0
  44. package/docs/test-boot-layer.md +110 -0
  45. package/docs/test-framework.md +183 -0
  46. package/package.json +8 -5
@@ -0,0 +1,75 @@
1
+ # Cross-context Helpers
2
+
3
+ `src/utils/mode-helpers.js` exposes four shared helpers mixed into every UJM Manager via `attachTo(Manager)`. Mirrors the same pattern in EM and BXM. Used when behavior should differ by *what kind of process* you're in — short-circuit network probes in tests, suppress dev-only banners in production, etc.
4
+
5
+ ## API
6
+
7
+ | Method | Returns | Purpose |
8
+ |---|---|---|
9
+ | `Manager.isTesting()` | `boolean` | True when UJM's test framework is running this process. Set by `npx mgr test` (`UJ_TEST_MODE=true`) and consumer test setups that want the same signal. |
10
+ | `Manager.isDevelopment()` | `boolean` | True when running in dev mode (not a production build). Reads `UJ_BUILD_MODE` / `NODE_ENV` / `UJ_IS_SERVER` / `window.Configuration.uj.environment` depending on context. |
11
+ | `Manager.isProduction()` | `boolean` | Inverse of `isDevelopment()`. |
12
+ | `Manager.getVersion()` | `string \| null` | UJM's version from `<cwd>/package.json#version`. Null if no `package.json` (e.g. shipped browser bundle). |
13
+
14
+ All four are available both **statically** on the Manager constructor and on **`Manager.prototype`**, so these all work:
15
+
16
+ ```js
17
+ const Manager = require('ultimate-jekyll-manager/build');
18
+ Manager.isTesting(); // static
19
+ new Manager().isTesting(); // instance
20
+ ```
21
+
22
+ ## When to use
23
+
24
+ ```js
25
+ // In a build helper that fetches Firebase auth files:
26
+ async function fetchFirebaseAuthFiles() {
27
+ if (Manager.isTesting()) {
28
+ return; // short-circuit — tests provide their own stubs
29
+ }
30
+ // ...real fetch
31
+ }
32
+
33
+ // In a gulp task that opens the dev browser:
34
+ function maybeOpenBrowser() {
35
+ if (Manager.isBuildMode() || Manager.isTesting()) return;
36
+ // ...exec `open` etc.
37
+ }
38
+
39
+ // In a frontend module that logs verbose debug info:
40
+ if (manager.isDevelopment()) {
41
+ console.log('[dev] webManager loaded with', cfg);
42
+ }
43
+ ```
44
+
45
+ **Don't use these for "should I hit dev or prod backends"** — that's a config concern. Use `Manager.getEnvironment()` (returns `'development'` or `'production'` strings) for that distinction.
46
+
47
+ ## How `isDevelopment` is detected
48
+
49
+ Order of checks:
50
+
51
+ 1. **Build-time Node**: `process.env.UJ_BUILD_MODE === 'true'` → production. `process.env.NODE_ENV === 'development'` → development. `process.env.UJ_IS_SERVER === 'true'` → production.
52
+ 2. **Browser**: `window.Configuration.uj.environment` if present (`'development'` or `'production'`).
53
+ 3. Default → `false`.
54
+
55
+ ## How `isTesting` is detected
56
+
57
+ Two checks (either is sufficient):
58
+
59
+ 1. **Node**: `process.env.UJ_TEST_MODE === 'true'`. Set automatically by `npx mgr test`.
60
+ 2. **Browser**: `globalThis.UJ_TEST_MODE === true`. Set automatically by UJM's `page`-layer harness HTML before any consumer code runs.
61
+
62
+ This means consumer code that calls `Manager.isTesting()` from a tab context gets the right answer — Node-only check would always return false in a browser.
63
+
64
+ ## Adding new helpers
65
+
66
+ If you find yourself writing the same `if (process.env.UJ_FOO === 'true') ...` check more than twice, factor it into `mode-helpers.js`. Things to consider:
67
+
68
+ - Does the helper need to work in **all** contexts (Node build-time + browser page + service worker)? If yes, gate every check with `typeof process !== 'undefined'` / `typeof window !== 'undefined'` so the same code runs everywhere.
69
+ - Should it be mixed into static AND prototype? Almost always yes — the static form is for build-time CLI usage, the instance form for runtime use.
70
+ - Add a test in `src/test/suites/build/mode-helpers.test.js` that toggles the underlying env var and asserts both states.
71
+
72
+ ## See also
73
+
74
+ - [test-framework.md](test-framework.md) — the test harness that sets `UJ_TEST_MODE`
75
+ - [cli.md](cli.md) — full env-var matrix
@@ -0,0 +1,110 @@
1
+ # Boot Layer
2
+
3
+ The boot layer runs Puppeteer against a real built `_site/` served by a tiny embedded HTTP server. It's the integration smoke that catches "did my actually-shipped site boot?" regressions — things plain-Node build tests can't see.
4
+
5
+ ## Why a real HTTP server (not `file://`)
6
+
7
+ Service workers refuse to register from `file://` URLs. Since SW registration / activation / cache-name composition is a key boot signal, the harness spins up a zero-dep HTTP server (`src/test/server.js`, ~100 lines of pure Node `http`) on an ephemeral loopback port and points Puppeteer at it.
8
+
9
+ The server also serves files with correct MIME types (text/html / text/javascript / text/css / application/json / image/png / etc.) and sets `Service-Worker-Allowed: /` so consumers can register a SW from any subdirectory.
10
+
11
+ ## `_site/` discovery order
12
+
13
+ The runner picks the first of these that has an `index.html`:
14
+
15
+ 1. `UJ_TEST_BOOT_DIR` — absolute path. Full override. Useful when your build pipeline writes elsewhere (`build/`, `public/`, custom dirs).
16
+ 2. `UJ_TEST_BOOT_PROJECT/_site` — auto-set when UJM tests itself; points at the fixture site under `src/test/fixtures/consumer-site/`. Consumers can set this manually if they want to run boot tests against a sibling project.
17
+ 3. `<cwd>/_site` — the default for UJM consumers. After `npm run build`, Jekyll writes here.
18
+
19
+ If none match, boot tests are reported as skipped with a clear "run `npm run build` first" message.
20
+
21
+ ## Fixture site vs consumer site
22
+
23
+ UJM's own framework boot suites assert against a fixture `_site/` at `src/test/fixtures/consumer-site/_site/`. The fixture is hand-built (no real Jekyll run needed during framework self-tests) and contains the minimum surface needed to verify boot:
24
+
25
+ - `index.html` — renders title + h1
26
+ - `about.html` — proves Jekyll-style `/about` → `/about.html` suffix fallback works
27
+ - `service-worker.js` — registers + activates + responds to `get-cache-name` postMessage
28
+ - `build.json` — proves UJM-generated metadata is served as application/json
29
+ - `assets/css/main.bundle.css`, `assets/js/main.bundle.js` — prove MIME routing
30
+
31
+ When a real consumer runs `npx mgr test`, framework boot suites are **excluded** (their assertions reference fixture-only content like "UJM Fixture Consumer"). Consumers write their own boot tests under `test/boot/` against their real `_site/`.
32
+
33
+ ## Single-Chromium-instance amortization
34
+
35
+ All boot tests run inside one Puppeteer browser instance. Per-test cost: ~150ms for the page navigation + assertions. Browser startup (~1s) is paid once per `npx mgr test --layer boot` invocation, not once per test.
36
+
37
+ ## What to assert
38
+
39
+ Good boot-test targets (high-signal, low-flake):
40
+
41
+ | Assertion | Why it matters |
42
+ |---|---|
43
+ | Home page has expected title / h1 | Catches "template variable didn't resolve" + "Liquid build failed" silently |
44
+ | `/some-route` resolves with 200 | Catches Jekyll permalink regressions + suffix-fallback edge cases |
45
+ | `build.json` is present + parses + has expected `config.brand.id` | Catches `gulp/build-config` regressions where the runtime config injection broke |
46
+ | `service-worker.js` MIME is `text/javascript` | A `text/html` MIME means the SW route is wrong; SW registration silently fails |
47
+ | SW reaches `activated` state | Catches "skipWaiting / clients.claim never wired" |
48
+ | SW responds to a postMessage with expected cache name | Catches "UJ_BUILD_JSON injection broken" or "cache-name composition regressed" |
49
+ | No `console.error` events fire during page load | Catches broken bundles, malformed `window.Configuration`, webManager init crashes |
50
+
51
+ Avoid (high-flake):
52
+
53
+ - "Wait for a specific element to be visible" without `waitForSelector` — pages render asynchronously
54
+ - Animation-dependent assertions (CSS transition completes, etc.) — timing-sensitive
55
+ - Anything that hits production Firebase / external APIs — boot tests should be hermetic
56
+
57
+ ## Example consumer boot test
58
+
59
+ From Chatsy's `test/boot/site.test.js`:
60
+
61
+ ```js
62
+ module.exports = {
63
+ layer: 'boot',
64
+ description: 'Chatsy _site/ loads + SW registers',
65
+ type: 'group',
66
+ tests: [
67
+ {
68
+ description: 'home renders with title',
69
+ inspect: async ({ site, page, expect }) => {
70
+ const res = await page.goto(site.baseUrl + '/', { waitUntil: 'domcontentloaded' });
71
+ expect(res.status()).toBe(200);
72
+ expect((await page.title()).length).toBeGreaterThan(0);
73
+ },
74
+ },
75
+ {
76
+ description: 'service-worker.js has javascript content type',
77
+ inspect: async ({ site, page, expect }) => {
78
+ const res = await page.goto(site.baseUrl + '/service-worker.js');
79
+ expect(/(text|application)\/javascript/.test(res.headers()['content-type'])).toBe(true);
80
+ },
81
+ },
82
+ ],
83
+ };
84
+ ```
85
+
86
+ ## Build-then-test pattern
87
+
88
+ For local development:
89
+
90
+ ```bash
91
+ npm run build # gulp pipeline produces _site/
92
+ npx mgr test --layer boot
93
+ ```
94
+
95
+ For CI: do the same. Don't try to share `_site/` between machines — Jekyll's incremental cache makes that brittle. Always build, then test.
96
+
97
+ ## Debugging
98
+
99
+ Set `UJ_TEST_DEBUG=1` to see Puppeteer's console output piped to your terminal:
100
+
101
+ ```bash
102
+ UJ_TEST_DEBUG=1 npx mgr test --layer boot
103
+ ```
104
+
105
+ Useful when a boot test fails with an opaque error — you'll see the actual `console.error` that triggered the failure plus any other tab-side logging.
106
+
107
+ ## See also
108
+
109
+ - [test-framework.md](test-framework.md) — overall harness reference
110
+ - [build-system.md](build-system.md) — the gulp pipeline that produces `_site/`
@@ -0,0 +1,183 @@
1
+ # Test Framework
2
+
3
+ UJM ships a built-in three-layer test harness. `npx mgr test` discovers framework suites from `<ujm>/dist/test/suites/**/*.js` and consumer suites from `<cwd>/test/**/*.js`, partitions by `layer`, and runs each layer in the right environment. Same shape as the sister harnesses in EM (electron-manager) and BXM (browser-extension-manager).
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ npx mgr test # all layers
9
+ npx mgr test --layer build # plain Node, fast
10
+ npx mgr test --layer page # headless Chromium tab against harness HTML
11
+ npx mgr test --layer boot # headless Chromium against built _site/
12
+ npx mgr test --filter foo # filter tests by name substring
13
+ npx mgr test --reporter json # machine-readable __UJM_TEST__ events
14
+ ```
15
+
16
+ `npm test` works too — added to consumer `package.json#scripts.test` on `npx mgr setup`.
17
+
18
+ ## Layers
19
+
20
+ | Layer | Runs in | Use for |
21
+ |---|---|---|
22
+ | `build` | Plain Node, ~ms | `Manager.getConfig/getPackage/getUJMConfig`, CLI alias resolution, gulp pure helpers (`mergeJekyllConfigs`, `validateYAMLFrontMatter`, `createTemplateTransform`, `collectTextNodes`), mode-helpers env gating, templating brackets |
23
+ | `page` | Headless Chromium tab via Puppeteer | Frontend Manager lifecycle, `window.Configuration` plumbing, DOM assertions, prerendered icons template, anything that needs real `window`/`document` |
24
+ | `boot` | Headless Chromium loading the consumer's built `_site/` via a tiny local HTTP server | End-to-end smoke: site builds + serves, pages render, service worker registers + activates + responds, no console errors |
25
+
26
+ There is no `main`/`background` layer (EM/BXM have those because they own long-running runtime processes; UJM does not).
27
+
28
+ ## Writing a test
29
+
30
+ Every test file is a CommonJS module exporting one of three forms:
31
+
32
+ ```js
33
+ // Standalone
34
+ module.exports = {
35
+ layer: 'build',
36
+ description: 'config has brand.id',
37
+ timeout: 5000,
38
+ run: async (ctx) => {
39
+ const Manager = require('ultimate-jekyll-manager/build');
40
+ const cfg = Manager.getConfig('project');
41
+ ctx.expect(cfg.brand.id).toBeTruthy();
42
+ },
43
+ cleanup: async (ctx) => { /* optional */ },
44
+ };
45
+
46
+ // Suite — sequential, stops on first failure, shares `ctx.state`
47
+ module.exports = {
48
+ type: 'suite',
49
+ layer: 'build',
50
+ description: 'config flow',
51
+ tests: [
52
+ { name: 'load', run: async (ctx) => { ctx.state.cfg = Manager.getConfig('project'); } },
53
+ { name: 'check', run: async (ctx) => { ctx.expect(ctx.state.cfg.brand.id).toBe('foo'); } },
54
+ ],
55
+ };
56
+
57
+ // Group — runs ALL tests even if some fail
58
+ module.exports = {
59
+ type: 'group',
60
+ layer: 'build',
61
+ tests: [ /* ... */ ],
62
+ };
63
+
64
+ // Array form → implicit group
65
+ module.exports = [
66
+ { name: 'a', run: async (ctx) => { /* ... */ } },
67
+ { name: 'b', run: async (ctx) => { /* ... */ } },
68
+ ];
69
+ ```
70
+
71
+ ### The `ctx` (test context)
72
+
73
+ Every `run(ctx)` and `cleanup(ctx)` callback receives:
74
+
75
+ | Property | Description |
76
+ |---|---|
77
+ | `ctx.expect` | Jest-compatible assertion (`toBe`, `toEqual`, `toBeTruthy`, `toContain`, `toMatch`, `toThrow`, `toBeGreaterThan`, etc. + `.not.` negation) |
78
+ | `ctx.state` | Plain object shared across tests in a suite/group |
79
+ | `ctx.layer` | Current layer name |
80
+ | `ctx.skip(reason)` | Throws SkipError — the runner records as skipped |
81
+
82
+ ### Boot-layer test shape
83
+
84
+ Boot tests use `inspect` instead of `run`. The callback receives `{ site, page, expect, projectRoot }`:
85
+
86
+ ```js
87
+ module.exports = {
88
+ layer: 'boot',
89
+ description: 'home renders + SW registers',
90
+ timeout: 15000,
91
+ inspect: async ({ site, page, expect, projectRoot }) => {
92
+ const res = await page.goto(site.baseUrl + '/');
93
+ expect(res.status()).toBe(200);
94
+ expect(await page.title()).toBeTruthy();
95
+ },
96
+ };
97
+ ```
98
+
99
+ | Property | Description |
100
+ |---|---|
101
+ | `site.baseUrl` | `http://127.0.0.1:<port>` — the harness HTTP server root |
102
+ | `site.port` | Ephemeral port the local server bound to |
103
+ | `site.root` | Absolute path to the served `_site/` |
104
+ | `page` | Puppeteer Page (fresh per test) |
105
+ | `expect` | Same Jest-compatible matchers |
106
+ | `projectRoot` | Absolute path to the consumer project |
107
+
108
+ ## Consumer pattern — use the public Manager API
109
+
110
+ When writing consumer tests, **use the public Manager API** — don't reach into UJM's transitive deps:
111
+
112
+ ```js
113
+ // Good — uses the public API
114
+ const Manager = require('ultimate-jekyll-manager/build');
115
+ const cfg = Manager.getUJMConfig();
116
+ const pkg = Manager.getPackage('project');
117
+
118
+ // Bad — reaches into UJM's transitive deps. Brittle: if UJM swaps json5 for
119
+ // jsonc-parser, your test breaks even though UJM's public API hasn't changed.
120
+ const json5 = require('json5');
121
+ const fs = require('fs');
122
+ const cfg = json5.parse(fs.readFileSync('config/ultimate-jekyll-manager.json', 'utf8'));
123
+ ```
124
+
125
+ The public surface exposed by `require('ultimate-jekyll-manager/build')` includes:
126
+
127
+ - `Manager.getConfig(type)` — reads `_config.yml` (type: `'project'` or `'main'`)
128
+ - `Manager.getPackage(type)` — reads `package.json` (type: `'project'` or `'main'`)
129
+ - `Manager.getUJMConfig()` — reads `config/ultimate-jekyll-manager.json` (JSON5)
130
+ - `Manager.getRootPath(type)` — project cwd or UJM package root
131
+ - `Manager.getEnvironment()` — `'development'` or `'production'`
132
+ - `Manager.isBuildMode()` / `isQuickMode()` / `isServer()` / `actLikeProduction()`
133
+ - `Manager.isTesting()` / `isDevelopment()` / `isProduction()` / `getVersion()` (from `mode-helpers.js`)
134
+ - `Manager.getMemoryUsage()` / `Manager.logMemory(logger, label)` / `Manager.processBatches(items, size, fn, logger)`
135
+ - `Manager.logger(name)` — returns a `Logger` instance
136
+ - `Manager.require(path)` — escape hatch when you really need a UJM transitive dep
137
+
138
+ See [docs/cross-context-helpers.md](cross-context-helpers.md) for `isTesting`/`isDevelopment` semantics.
139
+
140
+ ## Reporter contract — `__UJM_TEST__` JSON-line events
141
+
142
+ `--reporter json` emits one JSON line per event for external tools (CI dashboards, IDE plugins):
143
+
144
+ ```
145
+ __UJM_TEST__{"event":"result","name":"my-test","passed":true,"duration":12}
146
+ __UJM_TEST__{"event":"result","name":"other-test","passed":false,"duration":34,"error":"expected 1 to be 2"}
147
+ __UJM_TEST__{"event":"skip","name":"x","reason":"manual"}
148
+ ```
149
+
150
+ Then a final summary line:
151
+
152
+ ```json
153
+ {"event":"summary","passed":42,"failed":1,"skipped":0,"total":43}
154
+ ```
155
+
156
+ Same protocol as EM (`__EM_TEST__`) and BXM (`__BXM_TEST__`). One marker per framework; same JSON shape.
157
+
158
+ ## Discovery
159
+
160
+ - **Framework suites**: glob `<ujm>/dist/test/suites/**/*.js` (resolved from `__dirname/suites` in `runner.js`).
161
+ - **Consumer suites**: glob `<cwd>/test/**/*.js`.
162
+ - **Excluded**: any directory starting with `_` (handy for shared helpers).
163
+ - **Framework boot suites** are excluded when the cwd's `package.json#name` is not `ultimate-jekyll-manager` — they target UJM's fixture site, not the consumer's. Consumers write their own boot tests in `<cwd>/test/boot/`.
164
+
165
+ ## Env vars
166
+
167
+ | Env | Set by | Purpose |
168
+ |---|---|---|
169
+ | `UJ_TEST_MODE=true` | `npx mgr test` always | Canonical test signal. `Manager.isTesting()` reads this. Use it to short-circuit network calls / prompts / long timers in code that runs during tests. |
170
+ | `UJ_TEST_INTEGRATION=1` | `--integration` flag | Opt-in flag for slower integration tests if your suite has them |
171
+ | `UJ_TEST_BOOT_PROJECT` | Auto-set when UJM tests itself; else manual | Project root the boot runner uses (its `_site/` is the boot target) |
172
+ | `UJ_TEST_BOOT_DIR` | Manual | Absolute override for the `_site/` directory. Wins over `UJ_TEST_BOOT_PROJECT/_site` and `<cwd>/_site` |
173
+ | `UJ_TEST_DEBUG=1` | Manual | Verbose Puppeteer console output piped to the parent stdout |
174
+
175
+ ## Puppeteer as a peer-optional dep
176
+
177
+ Puppeteer is a `devDependency` of UJM itself. Consumers don't get it unless they write `page` or `boot` tests. If a consumer tries to run `page`/`boot` tests without puppeteer installed, those layers report as skipped with a clear message — build-layer tests still run.
178
+
179
+ ## See also
180
+
181
+ - [test-boot-layer.md](test-boot-layer.md) — deep dive on boot layer (`_site/` discovery, HTTP server, fixture vs consumer)
182
+ - [cross-context-helpers.md](cross-context-helpers.md) — `Manager.isTesting()` / `isDevelopment()` semantics
183
+ - [cli.md](cli.md) — CLI surface, env-var conventions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "1.1.10",
3
+ "version": "1.2.0",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -13,7 +13,8 @@
13
13
  "prepare": "node -e \"require('prepare-package')()\"",
14
14
  "prepare:watch": "node -e \"require('prepare-package/watch')()\"",
15
15
  "wm:prod": "npm uninstall web-manager && npm i web-manager@latest",
16
- "wm:local": "npm uninstall web-manager && npm i ../web-manager"
16
+ "wm:local": "npm uninstall web-manager && npm i ../web-manager",
17
+ "test": "node ./bin/ultimate-jekyll test"
17
18
  },
18
19
  "bin": {
19
20
  "uj": "bin/ultimate-jekyll",
@@ -32,7 +33,8 @@
32
33
  "start": "npx mgr clean && npx mgr setup && bundle exec npm run gulp --",
33
34
  "gulp": "gulp --cwd ./ --gulpfile ./node_modules/ultimate-jekyll-manager/dist/gulp/main.js",
34
35
  "build": "npx mgr clean && npx mgr setup && UJ_BUILD_MODE=true bundle exec npm run gulp -- build",
35
- "deploy": "npx mgr deploy"
36
+ "deploy": "npx mgr deploy",
37
+ "test": "npx mgr test"
36
38
  },
37
39
  "engines": {
38
40
  "node": "22",
@@ -105,7 +107,7 @@
105
107
  "prettier": "^3.8.3",
106
108
  "sass": "^1.99.0",
107
109
  "spellchecker": "^3.7.1",
108
- "web-manager": "^4.1.41",
110
+ "web-manager": "^4.1.42",
109
111
  "webpack": "^5.106.2",
110
112
  "wonderful-fetch": "^2.0.5",
111
113
  "wonderful-version": "^1.3.2",
@@ -115,6 +117,7 @@
115
117
  "gulp": "^5.0.1"
116
118
  },
117
119
  "devDependencies": {
118
- "prepare-package": "^2.1.0"
120
+ "prepare-package": "^2.1.0",
121
+ "puppeteer": "^24.43.1"
119
122
  }
120
123
  }