desy-html 16.0.3 → 16.0.4
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/AGENTS.md +27 -1
- package/TESTING_PLAN.md +5 -2
- package/branding/BRANDING.md +4 -1
- package/branding/branding.config.yourorganization.js +7 -0
- package/branding/logos/yourorganization-apple-touch-icon.png +0 -0
- package/branding/logos/yourorganization-favicon.ico +0 -0
- package/branding/logos/yourorganization-favicon.svg +13 -0
- package/branding/vite-branding-plugin.js +74 -8
- package/docs/_global.head.njk +0 -1
- package/docs/index.html +6 -0
- package/package.json +10 -7
- package/public/images/demo-icon.svg +9 -0
- package/src/templates/components/header/params.header.yaml +4 -4
- package/src/templates/components/header-advanced/params.header-advanced.yaml +7 -5
- package/vite.config.js +86 -59
- package/public/images/logo-educa-aragon.svg +0 -1
package/AGENTS.md
CHANGED
|
@@ -155,7 +155,7 @@ desy-html/
|
|
|
155
155
|
|
|
156
156
|
To customize for different organizations:
|
|
157
157
|
1. Edit `branding/branding.config.js` or create `branding/branding.config.{name}.js`
|
|
158
|
-
2. Add favicon files to `branding/logos/` (favicon.svg, favicon.ico, apple-touch-icon.png)
|
|
158
|
+
2. Add favicon files to `branding/logos/` (favicon.svg, favicon.ico, apple-touch-icon.png). The plugin automatically copies them to `/dist/images/` during build.
|
|
159
159
|
3. Replace logos in `branding/logos/`
|
|
160
160
|
4. Run `BRANDING_CONFIG={name} npm run build`
|
|
161
161
|
|
|
@@ -176,6 +176,32 @@ To customize for different organizations:
|
|
|
176
176
|
2. Export function that accepts `aria` namespace
|
|
177
177
|
3. Register in `src/js/desy-html.js`
|
|
178
178
|
|
|
179
|
+
## Post-Implementation Documentation Review
|
|
180
|
+
|
|
181
|
+
After making code changes, review if documentation needs updates:
|
|
182
|
+
|
|
183
|
+
### Checklist
|
|
184
|
+
|
|
185
|
+
- [ ] **AGENTS.md** - If adding new commands, patterns, or workflows
|
|
186
|
+
- [ ] **README.md** - If changing project setup or usage
|
|
187
|
+
- [ ] **TESTING_PLAN.md** - If adding new test scenarios
|
|
188
|
+
- [ ] **branding/BRANDING.md** - If changing branding configuration
|
|
189
|
+
- [ ] **Other .md files** - Check for relevant changes
|
|
190
|
+
|
|
191
|
+
### When to Update
|
|
192
|
+
|
|
193
|
+
| Change Type | Files to Review |
|
|
194
|
+
| ----------- | --------------- |
|
|
195
|
+
| New feature | AGENTS.md, README.md, possibly TESTING_PLAN.md |
|
|
196
|
+
| Config change | BRANDING.md, AGENTS.md |
|
|
197
|
+
| Bug fix | TESTING_PLAN.md if adding test scenarios |
|
|
198
|
+
| Documentation only | Only the .md files being changed |
|
|
199
|
+
|
|
200
|
+
**Tip**: After any technical change, run a quick grep to find relevant .md files:
|
|
201
|
+
```bash
|
|
202
|
+
grep -r "keyword" --include="*.md"
|
|
203
|
+
```
|
|
204
|
+
|
|
179
205
|
## License
|
|
180
206
|
|
|
181
207
|
This project uses EUPL-1.2 license. See EUPL-1.2.txt for details.
|
package/TESTING_PLAN.md
CHANGED
|
@@ -57,7 +57,10 @@ This document defines the complete testing plan to execute before publishing the
|
|
|
57
57
|
- Organization logos (aragon-*.svg)
|
|
58
58
|
- EU logos (*.svg in subfolder or root)
|
|
59
59
|
- Background images (header-background*.svg)
|
|
60
|
-
- [ ] Verify favicon files are copied to dist
|
|
60
|
+
- [ ] Verify favicon files are copied to dist/images/:
|
|
61
|
+
- `dist/images/favicon.svg`
|
|
62
|
+
- `dist/images/favicon.ico`
|
|
63
|
+
- `dist/images/apple-touch-icon.png`
|
|
61
64
|
|
|
62
65
|
### 2.3. Production Paths Verification
|
|
63
66
|
|
|
@@ -80,7 +83,7 @@ Using the browser agent, test the following pages:
|
|
|
80
83
|
- [ ] Verify organization logo displays (not broken, no 404)
|
|
81
84
|
- [ ] Verify primary colors are Aragón's (#00607a)
|
|
82
85
|
- [ ] Verify typography is correct (Open Sans)
|
|
83
|
-
- [ ] Verify favicon displays in browser tab (
|
|
86
|
+
- [ ] Verify favicon displays in browser tab (all 3: svg, ico, apple-touch-icon in /images/)
|
|
84
87
|
|
|
85
88
|
#### Header
|
|
86
89
|
|
package/branding/BRANDING.md
CHANGED
|
@@ -148,7 +148,10 @@ Favicon configuration for browser tabs and mobile devices.
|
|
|
148
148
|
| `favicon.ico` | string | Yes | 32×32 | Legacy favicon in ICO format (fallback for older browsers) |
|
|
149
149
|
| `favicon.appleTouch` | string | Yes | 180×180 | Apple Touch Icon for iOS home screen |
|
|
150
150
|
|
|
151
|
-
> **Note:** For optimal compatibility, use the following sizes: 32×32 for browser tabs (both SVG and ICO), and 180×180 for Apple Touch Icon. The plugin automatically copies
|
|
151
|
+
> **Note:** For optimal compatibility, use the following sizes: 32×32 for browser tabs (both SVG and ICO), and 180×180 for Apple Touch Icon. The vite-branding-plugin automatically copies favicon files to `/dist/images/` during build and rewrites the HTML paths:
|
|
152
|
+
> - `favicon.svg` → `/dist/images/favicon.svg`
|
|
153
|
+
> - `favicon.ico` → `/dist/images/favicon.ico`
|
|
154
|
+
> - `apple-touch-icon.png` → `/dist/images/apple-touch-icon.png`
|
|
152
155
|
|
|
153
156
|
## CSS Variables
|
|
154
157
|
|
|
@@ -62,4 +62,11 @@ export default {
|
|
|
62
62
|
headerBackground: '/branding/images/header-background.svg',
|
|
63
63
|
headerBackgroundLg: '/branding/images/header-background-lg.svg',
|
|
64
64
|
},
|
|
65
|
+
|
|
66
|
+
// Favicon Configuration
|
|
67
|
+
favicon: {
|
|
68
|
+
svg: '/branding/logos/yourorganization-favicon.svg',
|
|
69
|
+
ico: '/branding/logos/yourorganization-favicon.ico',
|
|
70
|
+
appleTouch: '/branding/logos/yourorganization-apple-touch-icon.png'
|
|
71
|
+
},
|
|
65
72
|
};
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" aria-label="YourLogo" role="img">
|
|
2
|
+
<defs>
|
|
3
|
+
<clipPath id="clip-hex">
|
|
4
|
+
<path d="M16 1.5L29 9V23L16 30.5L3 23V9L16 1.5Z"/>
|
|
5
|
+
</clipPath>
|
|
6
|
+
</defs>
|
|
7
|
+
<!-- Hexagono exterior -->
|
|
8
|
+
<path d="M16 1.5L29 9V23L16 30.5L3 23V9L16 1.5Z" fill="#00607a" stroke="#00475c" stroke-width="1"/>
|
|
9
|
+
<!-- Cuadrado interior -->
|
|
10
|
+
<rect x="10" y="10" width="12" height="12" rx="1" fill="#ffffff"/>
|
|
11
|
+
<!-- Círculo centro -->
|
|
12
|
+
<circle cx="16" cy="16" r="3" fill="#00607a"/>
|
|
13
|
+
</svg>
|
|
@@ -125,28 +125,94 @@ export function brandingPlugin(config) {
|
|
|
125
125
|
|
|
126
126
|
closeBundle() {
|
|
127
127
|
const distDir = path.resolve(process.cwd(), 'dist');
|
|
128
|
+
const imagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
128
129
|
const favicon = config?.favicon;
|
|
129
130
|
|
|
131
|
+
// Early exit if no favicon configuration
|
|
130
132
|
if (!favicon) return;
|
|
131
133
|
|
|
134
|
+
// Helper function to recursively find HTML files
|
|
135
|
+
const findHtmlFiles = (dir, files = []) => {
|
|
136
|
+
if (!fs.existsSync(dir)) return files;
|
|
137
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const fullPath = path.join(dir, item.name);
|
|
140
|
+
if (item.isDirectory()) {
|
|
141
|
+
findHtmlFiles(fullPath, files);
|
|
142
|
+
} else if (item.name.endsWith('.html')) {
|
|
143
|
+
files.push(fullPath);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return files;
|
|
147
|
+
};
|
|
148
|
+
|
|
132
149
|
const faviconFiles = [
|
|
133
150
|
{ key: 'svg', fileName: 'favicon.svg' },
|
|
134
151
|
{ key: 'ico', fileName: 'favicon.ico' },
|
|
135
152
|
{ key: 'appleTouch', fileName: 'apple-touch-icon.png' }
|
|
136
153
|
];
|
|
137
154
|
|
|
155
|
+
// All go to imagesDir
|
|
156
|
+
const destDir = imagesDir;
|
|
157
|
+
|
|
138
158
|
for (const { key, fileName } of faviconFiles) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
159
|
+
const faviconPath = favicon[key];
|
|
160
|
+
if (!faviconPath) continue;
|
|
161
|
+
|
|
162
|
+
// Extraer el nombre del archivo de la ruta configurada
|
|
163
|
+
const fileNameFromPath = path.basename(faviconPath);
|
|
164
|
+
const src = path.resolve(process.cwd(), faviconPath.slice(1)); // Quitar el / inicial
|
|
165
|
+
|
|
166
|
+
if (fs.existsSync(src)) {
|
|
167
|
+
try {
|
|
168
|
+
// Ensure destination directory exists
|
|
169
|
+
if (!fs.existsSync(destDir)) {
|
|
170
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
fs.copyFileSync(src, path.join(destDir, fileNameFromPath));
|
|
173
|
+
console.log(`✓ Favicon ${fileNameFromPath}: branding/logos/${fileNameFromPath} → dist/images/`);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.warn(`Warning: Could not copy ${fileNameFromPath}:`, err.message);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Rewrite HTML files to update favicon href paths
|
|
181
|
+
// Note: This is now handled in vite.config.js after HTML generation (rewriteFaviconPaths)
|
|
182
|
+
// Kept here for reference but not executed (HTML files don't exist yet at this point)
|
|
183
|
+
try {
|
|
184
|
+
const htmlFiles = findHtmlFiles(distDir);
|
|
185
|
+
|
|
186
|
+
for (const htmlFile of htmlFiles) {
|
|
187
|
+
let content = fs.readFileSync(htmlFile, 'utf8');
|
|
188
|
+
let modified = content;
|
|
189
|
+
|
|
190
|
+
// Rewrite favicon hrefs from /branding/logos/ to ./images/
|
|
191
|
+
// Only rewrite if the file was actually copied (exists in imagesDir)
|
|
192
|
+
for (const { key } of faviconFiles) {
|
|
193
|
+
const faviconPath = favicon[key];
|
|
194
|
+
if (!faviconPath) continue;
|
|
195
|
+
|
|
196
|
+
const fileNameFromPath = path.basename(faviconPath);
|
|
197
|
+
const sourcePath = faviconPath; // e.g., "/branding/logos/favicon.svg"
|
|
198
|
+
const destPath = `./images/${fileNameFromPath}`;
|
|
199
|
+
|
|
200
|
+
// Only rewrite if the destination file exists
|
|
201
|
+
const destFilePath = path.join(destDir, fileNameFromPath);
|
|
202
|
+
if (fs.existsSync(destFilePath)) {
|
|
203
|
+
// Simple string replacement - sourcePath is like "/branding/logos/favicon.svg"
|
|
204
|
+
const oldHref = `href="${sourcePath}"`;
|
|
205
|
+
const newHref = `href="${destPath}"`;
|
|
206
|
+
modified = modified.replaceAll(oldHref, newHref);
|
|
147
207
|
}
|
|
148
208
|
}
|
|
209
|
+
|
|
210
|
+
if (modified !== content) {
|
|
211
|
+
fs.writeFileSync(htmlFile, modified);
|
|
212
|
+
}
|
|
149
213
|
}
|
|
214
|
+
} catch (err) {
|
|
215
|
+
// Silently ignore - favicon rewriting is handled in vite.config.js
|
|
150
216
|
}
|
|
151
217
|
}
|
|
152
218
|
};
|
package/docs/_global.head.njk
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
<meta name="viewport" content="width=1=device-width, initial-scale=1.0">
|
|
6
6
|
<title>{{ title if title else "desy-html docs" }}</title>
|
|
7
7
|
{% if description %}<meta name="description" content="{{ description }}">{% endif %}
|
|
8
|
-
<link rel="icon" type="image/x-icon" href="https://aplicaciones.aragon.es/favicon.ico">
|
|
9
8
|
{% if branding and branding.typography and branding.typography.fontUrl %}
|
|
10
9
|
{% if 'fonts.googleapis.com' in branding.typography.fontUrl %}
|
|
11
10
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
package/docs/index.html
CHANGED
|
@@ -147,8 +147,14 @@ cd desy-html</code></pre>
|
|
|
147
147
|
|
|
148
148
|
<h2>Changelog (English)</h2>
|
|
149
149
|
<p>What's new in the latest version of desy-html</p>
|
|
150
|
+
<h3>v.16.0.4</h3>
|
|
151
|
+
<ul class="text-sm">
|
|
152
|
+
<li>Fixed env vars in Windows to allow branding configuration.</li>
|
|
153
|
+
<li>Minor fixes.</li>
|
|
154
|
+
</ul>
|
|
150
155
|
<h3>v.16.0.3</h3>
|
|
151
156
|
<ul class="text-sm">
|
|
157
|
+
<li>Added favicon to branding configuration and vite plugin.</li>
|
|
152
158
|
<li>Fixed breakpoints variants in text classes.</li>
|
|
153
159
|
<li>Added missing params in header components.</li>
|
|
154
160
|
</ul>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "desy-html",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.4",
|
|
4
4
|
"description": "desy-html contains the code you need to start building a user interface for Gobierno de Aragón government webapps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -30,27 +30,30 @@
|
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"dev": "vite",
|
|
33
|
-
"dev:yourorganization": "BRANDING_CONFIG=yourorganization
|
|
33
|
+
"dev:yourorganization": "cross-env BRANDING_CONFIG=yourorganization npm run dev",
|
|
34
34
|
"build": "vite build",
|
|
35
|
-
"
|
|
35
|
+
"build:yourorganization": "cross-env BRANDING_CONFIG=yourorganization npm run build",
|
|
36
|
+
"preview": "vite preview",
|
|
37
|
+
"preview:yourorganization": "cross-env BRANDING_CONFIG=yourorganization npm run preview"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
40
|
"@floating-ui/dom": "^1.6.13",
|
|
41
|
+
"@tailwindcss/forms": "^0.5.10",
|
|
42
|
+
"@tailwindcss/typography": "^0.5.18",
|
|
39
43
|
"@tailwindcss/vite": "^4.1.17",
|
|
40
44
|
"autoprefixer": "^10.4.21",
|
|
41
45
|
"cally": "^0.8.0",
|
|
42
46
|
"chokidar": "^3.6.0",
|
|
43
47
|
"hex-rgb": "^5.0.0",
|
|
44
48
|
"js-yaml": "^4.1.0",
|
|
45
|
-
"tailwindcss": "^4.1.17"
|
|
46
|
-
"@tailwindcss/forms": "^0.5.10",
|
|
47
|
-
"@tailwindcss/typography": "^0.5.18"
|
|
49
|
+
"tailwindcss": "^4.1.17"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
52
|
+
"cross-env": "^10.1.0",
|
|
50
53
|
"glob": "^11.0.1",
|
|
51
54
|
"highlight.js": "^11.11.1",
|
|
52
|
-
"nunjucks": "^3.2.4",
|
|
53
55
|
"js-beautify": "^1.14.11",
|
|
56
|
+
"nunjucks": "^3.2.4",
|
|
54
57
|
"outdent": "^0.8.0",
|
|
55
58
|
"sharp": "^0.34.3",
|
|
56
59
|
"vite": "^7.1.6"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" aria-label="Demo Icon" role="img">
|
|
2
|
+
<!-- Face circle -->
|
|
3
|
+
<circle cx="16" cy="16" r="14" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
4
|
+
<!-- Eyes -->
|
|
5
|
+
<circle cx="10" cy="12" r="2" fill="currentColor"/>
|
|
6
|
+
<circle cx="22" cy="12" r="2" fill="currentColor"/>
|
|
7
|
+
<!-- Smile -->
|
|
8
|
+
<path d="M10 20 Q16 26 22 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
9
|
+
</svg>
|
|
@@ -58,7 +58,7 @@ params:
|
|
|
58
58
|
type: object
|
|
59
59
|
required: false
|
|
60
60
|
description: Options for the subnav at right.
|
|
61
|
-
|
|
61
|
+
params:
|
|
62
62
|
- name: text
|
|
63
63
|
type: string
|
|
64
64
|
required: true
|
|
@@ -140,7 +140,7 @@ params:
|
|
|
140
140
|
type: object
|
|
141
141
|
required: false
|
|
142
142
|
description: Options for the dropdown at right.
|
|
143
|
-
|
|
143
|
+
params:
|
|
144
144
|
- name: text
|
|
145
145
|
type: string
|
|
146
146
|
required: true
|
|
@@ -222,7 +222,7 @@ params:
|
|
|
222
222
|
type: object
|
|
223
223
|
required: false
|
|
224
224
|
description: Options for the navigation main menu.
|
|
225
|
-
|
|
225
|
+
params:
|
|
226
226
|
- name: classes
|
|
227
227
|
type: string
|
|
228
228
|
required: false
|
|
@@ -264,7 +264,7 @@ params:
|
|
|
264
264
|
type: object
|
|
265
265
|
required: false
|
|
266
266
|
description: Options for the offcanvas menu.
|
|
267
|
-
|
|
267
|
+
params:
|
|
268
268
|
- name: text
|
|
269
269
|
type: string
|
|
270
270
|
required: true
|
|
@@ -11,10 +11,12 @@ params:
|
|
|
11
11
|
type: object
|
|
12
12
|
required: false
|
|
13
13
|
description: This is an area over the title
|
|
14
|
+
params:
|
|
14
15
|
- name: logo
|
|
15
16
|
type: object
|
|
16
17
|
required: false
|
|
17
18
|
description: options for the logo element
|
|
19
|
+
params:
|
|
18
20
|
- name: url
|
|
19
21
|
type: string
|
|
20
22
|
required: true
|
|
@@ -143,7 +145,7 @@ params:
|
|
|
143
145
|
type: object
|
|
144
146
|
required: false
|
|
145
147
|
description: Options for the dropdown at right.
|
|
146
|
-
|
|
148
|
+
params:
|
|
147
149
|
- name: text
|
|
148
150
|
type: string
|
|
149
151
|
required: true
|
|
@@ -225,7 +227,7 @@ params:
|
|
|
225
227
|
type: object
|
|
226
228
|
required: false
|
|
227
229
|
description: Options for the navigation main menu.
|
|
228
|
-
|
|
230
|
+
params:
|
|
229
231
|
- name: classes
|
|
230
232
|
type: string
|
|
231
233
|
required: false
|
|
@@ -266,7 +268,7 @@ params:
|
|
|
266
268
|
- name: sub
|
|
267
269
|
type: object
|
|
268
270
|
required: false
|
|
269
|
-
description: This is an area
|
|
271
|
+
description: This is an area under the title
|
|
270
272
|
- name: logo
|
|
271
273
|
type: object
|
|
272
274
|
required: false
|
|
@@ -294,7 +296,7 @@ params:
|
|
|
294
296
|
- name: backgroundFullColor
|
|
295
297
|
type: string
|
|
296
298
|
required: true
|
|
297
|
-
description: The css color used in the background image that fills all the
|
|
299
|
+
description: The css color used in the background image that fills all the sub area.
|
|
298
300
|
- name: backgroundFullUrl
|
|
299
301
|
type: string
|
|
300
302
|
required: false
|
|
@@ -315,7 +317,7 @@ params:
|
|
|
315
317
|
type: object
|
|
316
318
|
required: false
|
|
317
319
|
description: Options for the offcanvas menu.
|
|
318
|
-
|
|
320
|
+
params:
|
|
319
321
|
- name: labelledId
|
|
320
322
|
type: string
|
|
321
323
|
required: false
|
package/vite.config.js
CHANGED
|
@@ -18,6 +18,28 @@ const brandingConfig = await loadBrandingConfig(
|
|
|
18
18
|
'branding'
|
|
19
19
|
);
|
|
20
20
|
|
|
21
|
+
// Helper para servir archivos con el MIME type correcto
|
|
22
|
+
function serveFileWithMimeType(filePath, res) {
|
|
23
|
+
if (!fs.existsSync(filePath)) return false;
|
|
24
|
+
|
|
25
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
26
|
+
const mimeTypes = {
|
|
27
|
+
'.svg': 'image/svg+xml',
|
|
28
|
+
'.png': 'image/png',
|
|
29
|
+
'.jpg': 'image/jpeg',
|
|
30
|
+
'.jpeg': 'image/jpeg',
|
|
31
|
+
'.webp': 'image/webp',
|
|
32
|
+
'.ico': 'image/x-icon',
|
|
33
|
+
'.js': 'application/javascript',
|
|
34
|
+
'.css': 'text/css',
|
|
35
|
+
'.html': 'text/html'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
39
|
+
res.end(fs.readFileSync(filePath));
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
21
43
|
// Helper functions
|
|
22
44
|
function kebabCaseToPascalCase(value) {
|
|
23
45
|
return value
|
|
@@ -588,32 +610,76 @@ async function rewriteHtmlImagePaths() {
|
|
|
588
610
|
|
|
589
611
|
const htmlFiles = await glob('**/*.html', { cwd: distDir, nodir: true });
|
|
590
612
|
|
|
613
|
+
// Configuración centralizada de rewrites
|
|
614
|
+
const rewriteRules = [
|
|
615
|
+
// Imágenes en /images/
|
|
616
|
+
{ pattern: /src="(\/images\/)/g, replacement: 'src="./images/' },
|
|
617
|
+
{ pattern: /srcset="(\/images\/)/g, replacement: 'srcset="./images/' },
|
|
618
|
+
// Imágenes en /branding/logos/
|
|
619
|
+
{ pattern: /src="(\/branding\/logos\/)/g, replacement: 'src="./images/' },
|
|
620
|
+
{ pattern: /srcset="(\/branding\/logos\/)/g, replacement: 'srcset="./images/' },
|
|
621
|
+
// Imágenes en /branding/images/
|
|
622
|
+
{ pattern: /src="(\/branding\/images\/)/g, replacement: 'src="./images/' },
|
|
623
|
+
{ pattern: /srcset="(\/branding\/images\/)/g, replacement: 'srcset="./images/' },
|
|
624
|
+
];
|
|
625
|
+
|
|
591
626
|
for (const htmlFile of htmlFiles) {
|
|
592
627
|
const filePath = path.join(distDir, htmlFile);
|
|
593
628
|
let content = await fs.promises.readFile(filePath, 'utf8');
|
|
594
629
|
|
|
595
|
-
//
|
|
596
|
-
let modified = content
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
630
|
+
// Aplicar todas las reglas
|
|
631
|
+
let modified = content;
|
|
632
|
+
for (const rule of rewriteRules) {
|
|
633
|
+
modified = modified.replace(rule.pattern, rule.replacement);
|
|
634
|
+
}
|
|
600
635
|
|
|
601
|
-
|
|
602
|
-
|
|
636
|
+
if (modified !== content) {
|
|
637
|
+
await fs.promises.writeFile(filePath, modified);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
603
641
|
|
|
604
|
-
|
|
605
|
-
|
|
642
|
+
// Rewrite favicon hrefs from /branding/logos/ to ./images/ (after HTML files are generated)
|
|
643
|
+
async function rewriteFaviconPaths() {
|
|
644
|
+
const distDir = path.resolve(process.cwd(), 'dist');
|
|
645
|
+
const imagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
646
|
+
if (!fs.existsSync(distDir)) return;
|
|
606
647
|
|
|
607
|
-
|
|
608
|
-
|
|
648
|
+
const htmlFiles = await glob('**/*.html', { cwd: distDir, nodir: true });
|
|
649
|
+
|
|
650
|
+
// Favicon files to rewrite
|
|
651
|
+
const faviconFiles = [
|
|
652
|
+
'favicon.svg',
|
|
653
|
+
'favicon.ico',
|
|
654
|
+
'apple-touch-icon.png'
|
|
655
|
+
];
|
|
609
656
|
|
|
610
|
-
|
|
611
|
-
|
|
657
|
+
for (const htmlFile of htmlFiles) {
|
|
658
|
+
const filePath = path.join(distDir, htmlFile);
|
|
659
|
+
let content = await fs.promises.readFile(filePath, 'utf8');
|
|
660
|
+
let modified = content;
|
|
661
|
+
|
|
662
|
+
for (const faviconFile of faviconFiles) {
|
|
663
|
+
const sourcePath = `/branding/logos/${faviconFile}`;
|
|
664
|
+
const destPath = `./images/${faviconFile}`;
|
|
665
|
+
|
|
666
|
+
// Only rewrite if the destination file exists in imagesDir
|
|
667
|
+
const destFilePath = path.join(imagesDir, faviconFile);
|
|
668
|
+
if (fs.existsSync(destFilePath)) {
|
|
669
|
+
const oldHref = `href="${sourcePath}"`;
|
|
670
|
+
const newHref = `href="${destPath}"`;
|
|
671
|
+
modified = modified.replaceAll(oldHref, newHref);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
612
674
|
|
|
613
675
|
if (modified !== content) {
|
|
614
676
|
await fs.promises.writeFile(filePath, modified);
|
|
615
677
|
}
|
|
616
678
|
}
|
|
679
|
+
|
|
680
|
+
if (htmlFiles.length > 0) {
|
|
681
|
+
console.log(`✓ Rewrote favicon paths in ${htmlFiles.length} HTML file(s)`);
|
|
682
|
+
}
|
|
617
683
|
}
|
|
618
684
|
|
|
619
685
|
// Custom Nunjucks plugin
|
|
@@ -643,18 +709,7 @@ function customNunjucksPlugin() {
|
|
|
643
709
|
middlewares.use(async (req, res, next) => {
|
|
644
710
|
if (req.url.startsWith('/branding/')) {
|
|
645
711
|
const filePath = path.join(process.cwd(), req.url);
|
|
646
|
-
if (
|
|
647
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
648
|
-
const mimeTypes = {
|
|
649
|
-
'.svg': 'image/svg+xml',
|
|
650
|
-
'.png': 'image/png',
|
|
651
|
-
'.jpg': 'image/jpeg',
|
|
652
|
-
'.jpeg': 'image/jpeg',
|
|
653
|
-
'.webp': 'image/webp'
|
|
654
|
-
};
|
|
655
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
656
|
-
return res.end(fs.readFileSync(filePath));
|
|
657
|
-
}
|
|
712
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
658
713
|
}
|
|
659
714
|
next();
|
|
660
715
|
});
|
|
@@ -700,16 +755,7 @@ function customNunjucksPlugin() {
|
|
|
700
755
|
}
|
|
701
756
|
|
|
702
757
|
if (fs.existsSync(filePath)) {
|
|
703
|
-
|
|
704
|
-
const mimeTypes = {
|
|
705
|
-
'.svg': 'image/svg+xml',
|
|
706
|
-
'.png': 'image/png',
|
|
707
|
-
'.jpg': 'image/jpeg',
|
|
708
|
-
'.jpeg': 'image/jpeg',
|
|
709
|
-
'.webp': 'image/webp'
|
|
710
|
-
};
|
|
711
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
712
|
-
return res.end(fs.readFileSync(filePath));
|
|
758
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
713
759
|
}
|
|
714
760
|
}
|
|
715
761
|
next();
|
|
@@ -722,18 +768,7 @@ function customNunjucksPlugin() {
|
|
|
722
768
|
const brandingLogosPath = path.join(process.cwd(), 'branding/logos');
|
|
723
769
|
const filePath = findFileRecursive(brandingLogosPath, fileName);
|
|
724
770
|
|
|
725
|
-
if (filePath
|
|
726
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
727
|
-
const mimeTypes = {
|
|
728
|
-
'.svg': 'image/svg+xml',
|
|
729
|
-
'.png': 'image/png',
|
|
730
|
-
'.jpg': 'image/jpeg',
|
|
731
|
-
'.jpeg': 'image/jpeg',
|
|
732
|
-
'.webp': 'image/webp'
|
|
733
|
-
};
|
|
734
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
735
|
-
return res.end(fs.readFileSync(filePath));
|
|
736
|
-
}
|
|
771
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
737
772
|
}
|
|
738
773
|
next();
|
|
739
774
|
});
|
|
@@ -745,18 +780,7 @@ function customNunjucksPlugin() {
|
|
|
745
780
|
const brandingImagesPath = path.join(process.cwd(), 'branding/images');
|
|
746
781
|
const filePath = findFileRecursive(brandingImagesPath, fileName);
|
|
747
782
|
|
|
748
|
-
if (filePath
|
|
749
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
750
|
-
const mimeTypes = {
|
|
751
|
-
'.svg': 'image/svg+xml',
|
|
752
|
-
'.png': 'image/png',
|
|
753
|
-
'.jpg': 'image/jpeg',
|
|
754
|
-
'.jpeg': 'image/jpeg',
|
|
755
|
-
'.webp': 'image/webp'
|
|
756
|
-
};
|
|
757
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
758
|
-
return res.end(fs.readFileSync(filePath));
|
|
759
|
-
}
|
|
783
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
760
784
|
}
|
|
761
785
|
next();
|
|
762
786
|
});
|
|
@@ -878,6 +902,9 @@ function customNunjucksPlugin() {
|
|
|
878
902
|
// Step 4b: Correct HTML image paths for production (after HTML files are generated)
|
|
879
903
|
await rewriteHtmlImagePaths();
|
|
880
904
|
|
|
905
|
+
// Step 4c: Rewrite favicon paths from /branding/logos/ to ./images/
|
|
906
|
+
await rewriteFaviconPaths();
|
|
907
|
+
|
|
881
908
|
// Step 5: Final validation to find and fix common errors
|
|
882
909
|
await validateBuild();
|
|
883
910
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="169" height="30.7" viewBox="0 0 169 30.7"><g fill="#ffffff" fill-rule="evenodd"><path d="M13.3 23.8a18.279 18.279 0 0 1-5.1.9 7.762 7.762 0 0 1-5.9-2.4A8.869 8.869 0 0 1 0 16a9.324 9.324 0 0 1 2-6.1 6.45 6.45 0 0 1 5.2-2.4 5.477 5.477 0 0 1 4.6 2.1c1.1 1.4 1.6 3.4 1.6 6v.9H3c.4 3.9 2.3 5.9 5.7 5.9a13.2 13.2 0 0 0 4.6-1ZM3.2 14.3h7.3c0-3.1-1.1-4.6-3.4-4.6-2.4 0-3.7 1.6-3.9 4.6Z"/><path data-name="Trazado 1" d="M28 24.3v-3.1a5.863 5.863 0 0 1-5.5 3.5 5.605 5.605 0 0 1-4.7-2.2 9.008 9.008 0 0 1-1.7-5.9A10.548 10.548 0 0 1 18 10a6.076 6.076 0 0 1 5.1-2.5 5.925 5.925 0 0 1 4.8 2.2V.4h3v23.9Zm0-12.5a6.482 6.482 0 0 0-4.4-2c-2.9 0-4.3 2.2-4.3 6.6 0 3.8 1.3 5.8 3.8 5.8 1.7 0 3.3-.9 4.9-2.7v-7.7Z"/><path d="M46.1 24.3v-3.1c-1.6 2.3-3.5 3.5-5.8 3.5a4.441 4.441 0 0 1-3.4-1.4 5.353 5.353 0 0 1-1.3-3.7V7.9h3v10.8a5.385 5.385 0 0 0 .5 2.6 2.067 2.067 0 0 0 1.8.8q2.7 0 5.1-3.6V7.9h3v16.4Z"/><path data-name="Trazado 2" d="M60.4 24.7a6.828 6.828 0 0 1-5.5-2.5 8.723 8.723 0 0 1-2.2-6.2 8.494 8.494 0 0 1 2.2-6.3 7.781 7.781 0 0 1 6-2.2 23.692 23.692 0 0 1 4.3.5v2.5a15.149 15.149 0 0 0-4.1-.7 4.8 4.8 0 0 0-3.7 1.7 7.209 7.209 0 0 0-1.4 4.6 7.314 7.314 0 0 0 1.4 4.5 4.655 4.655 0 0 0 3.7 1.7 9.245 9.245 0 0 0 4.2-1v2.6a19.135 19.135 0 0 1-4.9.8Z"/><path data-name="Trazado 3" d="M76.7 22.5a7.087 7.087 0 0 1-4.8 2.2 4.6 4.6 0 0 1-3.5-1.3 4.665 4.665 0 0 1-1.4-3.5 4.617 4.617 0 0 1 2.2-4.2 12.192 12.192 0 0 1 6.3-1.5h1.2v-1.5c0-1.7-1-2.6-3-2.6a10.966 10.966 0 0 0-5.3 1.5V8.5a16.05 16.05 0 0 1 6-1.2c4.3 0 6.5 1.7 6.5 5.2v7.4c0 1.3.4 2 1.3 2a1.486 1.486 0 0 0 .6-.1l.1 2.5a8.082 8.082 0 0 1-2.5.4c-1.8 0-3-.7-3.5-2.2h-.2Zm0-2.4v-3.4h-1.1c-2.9 0-4.3.9-4.3 2.7a2.452 2.452 0 0 0 .6 1.6 1.8 1.8 0 0 0 1.6.6 4.932 4.932 0 0 0 3.2-1.5Z"/><path data-name="Trazado 4" d="M85.7 24.3V7.7h4.5v3.1c1.2-2.3 2.9-3.5 5.3-3.5a1.949 1.949 0 0 1 .8.1v4a5.663 5.663 0 0 0-1.8-.3 5.137 5.137 0 0 0-4.4 2.7v10.5Z"/><path data-name="Trazado 5" d="M106.9 22.5a7.087 7.087 0 0 1-4.8 2.2 4.6 4.6 0 0 1-3.5-1.3 4.665 4.665 0 0 1-1.4-3.5 4.617 4.617 0 0 1 2.2-4.2 12.192 12.192 0 0 1 6.3-1.5h1.2v-1.5c0-1.7-1-2.6-3-2.6a10.966 10.966 0 0 0-5.3 1.5V8.5a16.05 16.05 0 0 1 6-1.2c4.3 0 6.5 1.7 6.5 5.2v7.4c0 1.3.4 2 1.3 2a1.486 1.486 0 0 0 .6-.1l.1 2.5a8.082 8.082 0 0 1-2.5.4c-1.8 0-3-.7-3.5-2.2h-.2Zm0-2.4v-3.4h-1.1c-2.9 0-4.3.9-4.3 2.7a2.452 2.452 0 0 0 .6 1.6 1.8 1.8 0 0 0 1.6.6 4.932 4.932 0 0 0 3.2-1.5Z"/><path data-name="Trazado 6" d="m116 29.5.4-3.3a13.3 13.3 0 0 0 5.3 1.3 4.99 4.99 0 0 0 3.6-1.1 4.78 4.78 0 0 0 1.1-3.4v-2.3a5.962 5.962 0 0 1-10.1 1.4 9.3 9.3 0 0 1-1.7-6 9.8 9.8 0 0 1 2-6.4 6.316 6.316 0 0 1 5.2-2.4 6.559 6.559 0 0 1 4.7 2.1l.5-1.7h4v12.7a23.154 23.154 0 0 1-.5 5.5 5.938 5.938 0 0 1-1.8 2.9 9.3 9.3 0 0 1-6.3 1.9 26.488 26.488 0 0 1-6.4-1.2Zm10.4-11.4V12a4.628 4.628 0 0 0-3.4-1.8 3.174 3.174 0 0 0-2.7 1.5 7.121 7.121 0 0 0-1 4.1c0 3.2 1 4.9 3.1 4.9 1.4 0 2.8-.9 4-2.6Z"/><path data-name="Trazado 7" d="M142.3 24.7a8.135 8.135 0 0 1-6.2-2.4 8.869 8.869 0 0 1-2.3-6.3 8.951 8.951 0 0 1 2.3-6.4c1.5-1.6 3.6-2.3 6.3-2.3a8.541 8.541 0 0 1 6.3 2.3 8.7 8.7 0 0 1 2.3 6.3 8.787 8.787 0 0 1-2.3 6.4 8.6 8.6 0 0 1-6.4 2.4Zm0-2.8c2.6 0 3.9-2 3.9-5.9a7.83 7.83 0 0 0-1-4.3 3.25 3.25 0 0 0-5.6 0 7.83 7.83 0 0 0-1 4.3 7.83 7.83 0 0 0 1 4.3 3.152 3.152 0 0 0 2.7 1.6Z"/><path data-name="Trazado 8" d="m139.5 4.9 3.6-4.9h4.1l-4.8 4.9Z"/><path data-name="Trazado 9" d="M154 24.3V7.7h4.5v3.1c1.5-2.3 3.4-3.5 5.7-3.5a4.513 4.513 0 0 1 3.5 1.4 5.167 5.167 0 0 1 1.3 3.8v11.8h-4.5V13.7c0-1.9-.6-2.8-1.9-2.8-1.4 0-2.8 1-4.1 3v10.4Z"/></g></svg>
|