ultimate-jekyll-manager 1.9.27 → 1.9.29

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.
@@ -40,8 +40,10 @@ const bundleFiles = [
40
40
  // Main bundles
41
41
  `${rootPathPackage}/dist/assets/css/bundles/*.scss`,
42
42
 
43
- // Project bundles
44
- 'src/assets/css/bundles/*.scss',
43
+ // Project bundles — only if the dir exists (gulp's src() throws ENOENT when it
44
+ // scandirs a missing directory, same as themePageGlobs below); most consumers
45
+ // don't define project bundles
46
+ ...(jetpack.exists('src/assets/css/bundles') ? ['src/assets/css/bundles/*.scss'] : []),
45
47
  ];
46
48
 
47
49
  // Build the active theme's page-CSS globs, but ONLY for `pages` dirs that exist —
@@ -129,30 +129,32 @@ async function getHttpsConfig() {
129
129
  // Check if mkcert certificates exist
130
130
  const certPath = path.join(rootPathProject, '.temp');
131
131
 
132
- // Look for any mkcert generated files (handles localhost+2.pem format)
133
- const certFiles = jetpack.find(certPath, { matching: 'localhost*.pem' }) || [];
132
+ // Look for any mkcert generated files. NOTE: mkcert names files after the FIRST
133
+ // SAN host (development.<brand>+N.pem, not localhost*.pem) match *.pem, the
134
+ // same pattern generateMkcertCertificates() finds after generating.
135
+ const certFiles = jetpack.find(certPath, { matching: '*.pem' }) || [];
134
136
  const keyFile = certFiles.find(f => f.includes('-key.pem'));
135
137
  const certFile = certFiles.find(f => !f.includes('-key.pem'));
136
138
 
137
139
  if (keyFile && certFile) {
138
- logger.log('Using mkcert certificates from .temp/');
139
- logger.log(`Certificate: ${certFile}`);
140
- logger.log(`Key: ${keyFile}`);
140
+ const problem = checkCertProblem(certFile);
141
141
 
142
- // Read certificate to check validity
143
- try {
144
- const certContent = jetpack.read(certFile);
145
- if (certContent && certContent.includes('BEGIN CERTIFICATE')) {
146
- logger.log('✅ Certificate appears valid');
147
- }
148
- } catch (e) {
149
- logger.log('⚠️ Could not read certificate');
142
+ if (!problem) {
143
+ logger.log('Using mkcert certificates from .temp/');
144
+ logger.log(`Certificate: ${certFile}`);
145
+ logger.log(`Key: ${keyFile}`);
146
+
147
+ return {
148
+ key: keyFile,
149
+ cert: certFile
150
+ };
150
151
  }
151
152
 
152
- return {
153
- key: keyFile,
154
- cert: certFile
155
- };
153
+ // Stale certs (expired, or issued by a DIFFERENT machine's mkcert CA — e.g.
154
+ // .temp copied over from another Mac) make browsers reject https://localhost:4000
155
+ // outright. Wipe and regenerate against THIS machine's trusted CA.
156
+ logger.log(`Existing certificates are not usable (${problem}) — regenerating...`);
157
+ certFiles.forEach((file) => jetpack.remove(file));
156
158
  }
157
159
 
158
160
  // Try to generate mkcert certificates
@@ -170,6 +172,41 @@ async function getHttpsConfig() {
170
172
  return true;
171
173
  }
172
174
 
175
+ // Returns a reason string when the existing cert must be regenerated, or null
176
+ // when it's usable: unexpired AND signed by this machine's trusted mkcert root CA.
177
+ function checkCertProblem(certFile) {
178
+ const { X509Certificate } = require('crypto');
179
+ const { execSync } = require('child_process');
180
+
181
+ let cert;
182
+ try {
183
+ cert = new X509Certificate(jetpack.read(certFile));
184
+ } catch (e) {
185
+ return 'unreadable certificate';
186
+ }
187
+
188
+ if (new Date(cert.validTo) <= new Date()) {
189
+ return `expired ${cert.validTo}`;
190
+ }
191
+
192
+ // Verify the signature chains to the CURRENT mkcert root CA. If mkcert (or its
193
+ // root) isn't available we can't verify — keep the existing certs rather than
194
+ // breaking the self-signed fallback path.
195
+ try {
196
+ const caRoot = execSync('mkcert -CAROOT', { encoding: 'utf8' }).trim();
197
+ const ca = new X509Certificate(jetpack.read(path.join(caRoot, 'rootCA.pem')));
198
+
199
+ if (!cert.verify(ca.publicKey)) {
200
+ const issuerCN = cert.issuer.split('\n').find((line) => line.startsWith('CN=')) || cert.issuer;
201
+ return `issued by a different CA (${issuerCN})`;
202
+ }
203
+ } catch (e) {
204
+ return null;
205
+ }
206
+
207
+ return null;
208
+ }
209
+
173
210
  // Generate mkcert certificates
174
211
  async function generateMkcertCertificates(certPath) {
175
212
  try {
@@ -19,7 +19,10 @@ const INCLUDES = path.join(ROOT, 'src/defaults/dist/_includes/themes');
19
19
 
20
20
  // _template is held to the asset contract too — it's what theme authors copy.
21
21
  // bootstrap is the shared Bootstrap source, not a theme.
22
- const themes = jetpack.list(ASSETS).filter((t) => t !== 'bootstrap');
22
+ // The published package ships no src/ (package.json files: assets/bin/dist/docs),
23
+ // so in consumer projects this framework-source suite has nothing to assert
24
+ // against — it degrades to a single skip (see module.exports) instead of crashing.
25
+ const themes = (jetpack.list(ASSETS) || []).filter((t) => t !== 'bootstrap');
23
26
 
24
27
  // Class tokens that would couple markup to one theme (markup must stay
25
28
  // swappable; theme prefixes live only in SCSS internals)
@@ -77,7 +80,12 @@ module.exports = {
77
80
  layer: 'build',
78
81
  description: 'theme contract (structure, swappability, cross-theme JS contracts)',
79
82
  type: 'group',
80
- tests: [
83
+ tests: themes.length === 0 ? [
84
+ {
85
+ name: 'theme sources present (framework repo only)',
86
+ run: (ctx) => ctx.skip('src/assets/themes is not shipped in the published package — this suite runs in the UJM repo'),
87
+ },
88
+ ] : [
81
89
  ...themes.map((theme) => ({
82
90
  name: `${theme}: entry files + config contract`,
83
91
  run: (ctx) => {
package/docs/themes.md CHANGED
@@ -178,7 +178,11 @@ The hooks (and the rest of the theme conventions: entry files, `$avatar-sizes`,
178
178
  scripts, page-asset shapes) are enforced by the build-layer **theme-contract
179
179
  test** — `npx mgr test mgr:build/theme-contract` — which globs every theme, so
180
180
  a new theme is covered the moment it lands. It caught neobrutalism's missing
181
- promo banner + `.card-title` the day it was written.
181
+ promo banner + `.card-title` the day it was written. The suite asserts on the
182
+ framework's `src/assets/themes` sources, which the published package doesn't
183
+ ship — in consumer projects it reports a single skip instead of running (or
184
+ crashing); it only executes inside the UJM repo (or a consumer linked to the
185
+ local repo via `npx mgr install dev`).
182
186
 
183
187
  #### Theme chrome: inherit classy's nav + footer, restyle via CSS
184
188
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "1.9.27",
3
+ "version": "1.9.29",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -116,7 +116,7 @@
116
116
  "prettier": "^3.9.4",
117
117
  "sass": "^1.101.0",
118
118
  "spellchecker": "^3.7.1",
119
- "web-manager": "^4.3.2",
119
+ "web-manager": "^4.3.5",
120
120
  "webpack": "^5.108.3",
121
121
  "wonderful-fetch": "^2.0.5",
122
122
  "wonderful-version": "^1.3.2",