desy-html 16.0.4 → 17.0.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/.node-version +1 -1
- package/.nvmrc +1 -1
- package/AGENTS.md +207 -207
- package/README.md +203 -203
- package/TESTING_PLAN.md +379 -379
- package/branding/BRANDING.md +398 -398
- package/branding/branding.config.js +76 -76
- package/branding/branding.config.yourorganization.js +72 -72
- package/branding/logos/aragon-compact.svg +1 -1
- package/branding/logos/aragon-expanded.svg +1 -1
- package/branding/logos/aragon-mini.svg +1 -1
- package/branding/logos/favicon.svg +1 -1
- package/branding/logos/yourlogo-compact.svg +13 -13
- package/branding/logos/yourlogo-expanded.svg +17 -17
- package/branding/logos/yourlogo-mini.svg +17 -17
- package/branding/logos/yourorganization-favicon.svg +13 -13
- package/branding/vite-branding-plugin.js +221 -221
- package/docs/_global.content-placeholder.njk +7 -7
- package/docs/_global.foot.njk +11 -11
- package/docs/_global.head.njk +42 -42
- package/docs/_include.template-header.njk +391 -386
- package/docs/_macro.component-example.njk +6 -6
- package/docs/_macro.example-render.njk +350 -346
- package/docs/_macro.load-component-params.njk +2 -2
- package/docs/_macro.load-component-template.njk +3 -3
- package/docs/_macro.render-caller.njk +3 -3
- package/docs/_macro.show-code-from-file.njk +57 -57
- package/docs/_macro.show-html-from-file.njk +42 -42
- package/docs/_template.default.njk +8 -8
- package/docs/_template.examples.njk +13 -13
- package/docs/catalogo.html +27 -27
- package/docs/componentes.html +187 -184
- package/docs/ds/_ds.example.accordion.njk +109 -109
- package/docs/ds/_ds.example.border.njk +39 -39
- package/docs/ds/_ds.example.botones-alert-sm.njk +264 -264
- package/docs/ds/_ds.example.botones-alert.njk +264 -264
- package/docs/ds/_ds.example.botones-default-sm.njk +264 -264
- package/docs/ds/_ds.example.botones-default.njk +258 -258
- package/docs/ds/_ds.example.botones-primary-lg.njk +264 -264
- package/docs/ds/_ds.example.botones-primary-sm.njk +264 -264
- package/docs/ds/_ds.example.botones-primary.njk +264 -264
- package/docs/ds/_ds.example.botones-transparent-sm.njk +264 -264
- package/docs/ds/_ds.example.botones-transparent.njk +264 -264
- package/docs/ds/_ds.example.breadcrumbs.njk +65 -65
- package/docs/ds/_ds.example.card.njk +198 -198
- package/docs/ds/_ds.example.checkboxes.njk +377 -377
- package/docs/ds/_ds.example.collapsible.njk +18 -18
- package/docs/ds/_ds.example.color-de-interaccion.njk +16 -16
- package/docs/ds/_ds.example.colores-cabecera.njk +15 -15
- package/docs/ds/_ds.example.colores-de-soporte.njk +82 -82
- package/docs/ds/_ds.example.colores-neutros.njk +26 -26
- package/docs/ds/_ds.example.combobox.njk +91 -0
- package/docs/ds/_ds.example.date-input.njk +363 -363
- package/docs/ds/_ds.example.description-list.njk +289 -289
- package/docs/ds/_ds.example.details.njk +23 -23
- package/docs/ds/_ds.example.dropdowns-default.njk +186 -186
- package/docs/ds/_ds.example.dropdowns-en-uso.njk +105 -105
- package/docs/ds/_ds.example.dropdowns-variaciones.njk +224 -224
- package/docs/ds/_ds.example.error-summary.njk +34 -34
- package/docs/ds/_ds.example.file-upload.njk +54 -54
- package/docs/ds/_ds.example.footer.njk +28 -28
- package/docs/ds/_ds.example.header-advanced.njk +70 -70
- package/docs/ds/_ds.example.header.njk +211 -211
- package/docs/ds/_ds.example.input-group.njk +242 -242
- package/docs/ds/_ds.example.input.njk +164 -164
- package/docs/ds/_ds.example.item.njk +176 -176
- package/docs/ds/_ds.example.layout-escritorio.njk +38 -38
- package/docs/ds/_ds.example.layout-movil.njk +14 -14
- package/docs/ds/_ds.example.layout-sidebar.njk +11 -11
- package/docs/ds/_ds.example.links-list.njk +170 -170
- package/docs/ds/_ds.example.listbox-default.njk +677 -677
- package/docs/ds/_ds.example.listbox-variaciones.njk +824 -824
- package/docs/ds/_ds.example.menu-horizontal.njk +66 -66
- package/docs/ds/_ds.example.menu-navigation.njk +493 -493
- package/docs/ds/_ds.example.menu-vertical.njk +129 -129
- package/docs/ds/_ds.example.menubar-en-uso.njk +244 -244
- package/docs/ds/_ds.example.menubar-variaciones.njk +785 -785
- package/docs/ds/_ds.example.modal.njk +210 -210
- package/docs/ds/_ds.example.nav.njk +158 -158
- package/docs/ds/_ds.example.notification.njk +122 -122
- package/docs/ds/_ds.example.pagination.njk +190 -190
- package/docs/ds/_ds.example.pills.njk +215 -215
- package/docs/ds/_ds.example.radios.njk +419 -419
- package/docs/ds/_ds.example.searchbar.njk +87 -87
- package/docs/ds/_ds.example.select.njk +390 -390
- package/docs/ds/_ds.example.status-item.njk +239 -239
- package/docs/ds/_ds.example.status.njk +47 -47
- package/docs/ds/_ds.example.table.njk +740 -740
- package/docs/ds/_ds.example.tabs.njk +136 -136
- package/docs/ds/_ds.example.textarea.njk +237 -237
- package/docs/ds/_ds.example.textos.njk +32 -32
- package/docs/ds/_ds.example.toggle.njk +75 -75
- package/docs/ds/_ds.example.tree.njk +456 -456
- package/docs/ds/_ds.example.treegrid.njk +810 -810
- package/docs/ds/_ds.example.typography.njk +225 -225
- package/docs/ds/_ds.macro.code-snippet.njk +32 -32
- package/docs/ds/_ds.macro.section-title.njk +2 -2
- package/docs/ds/_ds.macro.subsection-title.njk +2 -2
- package/docs/ds/_ds.section.avisos.njk +22 -22
- package/docs/ds/_ds.section.botones.njk +53 -53
- package/docs/ds/_ds.section.campo-y-area-de-texto.njk +19 -19
- package/docs/ds/_ds.section.color.njk +147 -147
- package/docs/ds/_ds.section.datos.njk +42 -42
- package/docs/ds/_ds.section.dropdowns.njk +23 -23
- package/docs/ds/_ds.section.espaciado.njk +296 -296
- package/docs/ds/_ds.section.forms.njk +73 -68
- package/docs/ds/_ds.section.informacion.njk +12 -12
- package/docs/ds/_ds.section.layout.njk +63 -63
- package/docs/ds/_ds.section.menubar.njk +18 -18
- package/docs/ds/_ds.section.mostrar-ocultar.njk +33 -33
- package/docs/ds/_ds.section.navigation.njk +57 -57
- package/docs/ds/_ds.section.textos.njk +287 -287
- package/docs/ds/_ds.section.typography.njk +28 -28
- package/docs/estilos.html +37 -37
- package/docs/examples-accordion-history.html +8 -8
- package/docs/examples-accordion.html +8 -8
- package/docs/examples-alert.html +8 -8
- package/docs/examples-breadcrumbs.html +8 -8
- package/docs/examples-button-loader.html +8 -8
- package/docs/examples-button.html +8 -8
- package/docs/examples-card.html +8 -8
- package/docs/examples-character-count.html +8 -8
- package/docs/examples-checkboxes.html +8 -8
- package/docs/examples-collapsible.html +8 -8
- package/docs/examples-combobox.html +8 -0
- package/docs/examples-date-input.html +8 -8
- package/docs/examples-datepicker.html +8 -8
- package/docs/examples-description-list.html +8 -8
- package/docs/examples-details.html +8 -8
- package/docs/examples-dialog.html +8 -8
- package/docs/examples-dropdown.html +8 -8
- package/docs/examples-error-message.html +8 -8
- package/docs/examples-error-summary.html +8 -8
- package/docs/examples-fieldset.html +8 -8
- package/docs/examples-file-upload.html +8 -8
- package/docs/examples-footer.html +8 -8
- package/docs/examples-header-advanced.html +8 -8
- package/docs/examples-header-mini.html +8 -8
- package/docs/examples-header.html +8 -8
- package/docs/examples-hint.html +8 -8
- package/docs/examples-input-group.html +8 -8
- package/docs/examples-input.html +8 -8
- package/docs/examples-item.html +8 -8
- package/docs/examples-label.html +8 -8
- package/docs/examples-links-list.html +8 -8
- package/docs/examples-listbox.html +8 -8
- package/docs/examples-media-object.html +8 -8
- package/docs/examples-menu-horizontal.html +8 -8
- package/docs/examples-menu-navigation.html +8 -8
- package/docs/examples-menu-vertical.html +8 -8
- package/docs/examples-menubar.html +8 -8
- package/docs/examples-modal.html +8 -8
- package/docs/examples-nav.html +8 -8
- package/docs/examples-notification.html +8 -8
- package/docs/examples-pagination.html +8 -8
- package/docs/examples-pill.html +8 -8
- package/docs/examples-radios.html +8 -8
- package/docs/examples-searchbar.html +8 -8
- package/docs/examples-select.html +8 -8
- package/docs/examples-skip-link.html +8 -8
- package/docs/examples-spinner.html +8 -8
- package/docs/examples-status-item.html +8 -8
- package/docs/examples-status.html +8 -8
- package/docs/examples-table-advanced.html +8 -8
- package/docs/examples-table.html +8 -8
- package/docs/examples-tabs.html +8 -8
- package/docs/examples-textarea.html +8 -8
- package/docs/examples-toggle.html +8 -8
- package/docs/examples-tooltip.html +8 -8
- package/docs/examples-tree.html +8 -8
- package/docs/examples-treegrid.html +8 -8
- package/docs/index.html +914 -908
- package/docs/pagina-accesibilidad.html +110 -110
- package/docs/pagina-mapa-web.html +137 -137
- package/docs/pagina-prueba.html +95 -95
- package/docs/plantilla-con-header-advanced.html +14 -14
- package/docs/plantilla-editar-con-cabecera-fija-y-sidebar-sticky.html +154 -154
- package/docs/plantilla-editar-con-cabecera-fija.html +17 -17
- package/docs/plantilla-logueado-con-cabecera-fija-headroom.html +15 -15
- package/docs/plantilla-logueado-con-cabecera-fija.html +15 -15
- package/docs/plantilla-logueado-con-selector-de-app-y-sidebar.html +164 -164
- package/docs/plantilla-logueado-con-selector-de-app-y-subheader.html +16 -16
- package/docs/plantilla-logueado-con-selector-de-app.html +14 -14
- package/docs/plantilla-logueado-con-titulo-de-app.html +14 -14
- package/docs/plantilla-sin-loguear.html +14 -14
- package/docs/plantillas.html +89 -89
- package/docs/spinner-plantilla-con-header-advanced.html +2 -2
- package/docs/spinner-plantilla-editar-con-cabecera-fija.html +4 -4
- package/docs/spinner-plantilla-logueado-con-cabecera-fija.html +2 -2
- package/docs/spinner-plantilla-logueado-con-selector-de-app-y-subheader.html +2 -2
- package/docs/spinner-plantilla-logueado-con-titulo-de-app.html +2 -2
- package/docs/spinner-plantilla-sin-loguear.html +18 -18
- package/docs/sprints/01-combobox-template.md +433 -0
- package/docs/sprints/02-combobox-styles.md +291 -0
- package/docs/sprints/03-combobox-javascript.md +589 -0
- package/docs/sprints/04-combobox-integration.md +386 -0
- package/docs/sprints/05-combobox-refinamiento-w3c.md +55 -0
- package/package.json +61 -61
- package/public/images/demo-icon.svg +9 -9
- package/replit.md +74 -74
- package/src/EUPL-1.2.txt +287 -287
- package/src/css/base.css +44 -44
- package/src/css/component.form-group.css +24 -24
- package/src/css/component.headroom.css +32 -32
- package/src/css/component.text.css +169 -169
- package/src/css/styles.css +247 -246
- package/src/js/aria/MenuHorizontal.js +65 -65
- package/src/js/aria/MenuNavigation.js +34 -2
- package/src/js/aria/MenuVertical.js +60 -60
- package/src/js/aria/Nav.js +60 -60
- package/src/js/aria/PopupMenuAction.js +30 -12
- package/src/js/aria/accordion.js +264 -264
- package/src/js/aria/checkBoxes.js +52 -52
- package/src/js/aria/collapsible.js +44 -44
- package/src/js/aria/combobox.js +584 -0
- package/src/js/aria/dataGrid.js +950 -950
- package/src/js/aria/linksList.js +42 -42
- package/src/js/aria/notification.js +56 -56
- package/src/js/aria/radioButton.js +50 -50
- package/src/js/aria/toggle.js +61 -61
- package/src/js/aria/treegrid.js +507 -507
- package/src/js/aria/utils.js +219 -219
- package/src/js/desy-html.js +860 -813
- package/src/js/headroom.min.js +6 -6
- package/src/js/index.js +74 -72
- package/src/main.js +2 -2
- package/src/templates/components/accordion/_examples.accordion.njk +482 -482
- package/src/templates/components/accordion/_macro.accordion.njk +3 -3
- package/src/templates/components/accordion/_template.accordion.njk +131 -131
- package/src/templates/components/accordion/params.accordion.yaml +125 -125
- package/src/templates/components/accordion-history/_examples.accordion-history.njk +521 -521
- package/src/templates/components/accordion-history/_macro.accordion-history.njk +3 -3
- package/src/templates/components/accordion-history/_template.accordion-history.njk +193 -193
- package/src/templates/components/accordion-history/params.accordion-history.yaml +129 -129
- package/src/templates/components/alert/_examples.alert.njk +91 -91
- package/src/templates/components/alert/_macro.alert.njk +3 -3
- package/src/templates/components/alert/_styles.alert.css +9 -9
- package/src/templates/components/alert/_template.alert.njk +16 -16
- package/src/templates/components/alert/params.alert.yaml +25 -25
- package/src/templates/components/breadcrumbs/_examples.breadcrumbs.njk +464 -464
- package/src/templates/components/breadcrumbs/_macro.breadcrumbs.njk +3 -3
- package/src/templates/components/breadcrumbs/_styles.breadcrumbs.css +81 -81
- package/src/templates/components/breadcrumbs/_template.breadcrumbs.njk +77 -77
- package/src/templates/components/breadcrumbs/params.breadcrumbs.yaml +44 -44
- package/src/templates/components/button/_examples.button.njk +518 -511
- package/src/templates/components/button/_macro.button.njk +3 -3
- package/src/templates/components/button/_styles.button.css +180 -180
- package/src/templates/components/button/_template.button.njk +49 -49
- package/src/templates/components/button/params.button.yaml +48 -48
- package/src/templates/components/button-loader/_examples.button-loader.njk +499 -492
- package/src/templates/components/button-loader/_macro.button-loader.njk +3 -3
- package/src/templates/components/button-loader/_styles.button-loader.css +194 -194
- package/src/templates/components/button-loader/_template.button-loader.njk +84 -84
- package/src/templates/components/button-loader/params.button-loader.yaml +74 -74
- package/src/templates/components/card/_examples.card.njk +381 -381
- package/src/templates/components/card/_macro.card.njk +3 -3
- package/src/templates/components/card/_template.card.njk +34 -34
- package/src/templates/components/card/params.card.yaml +112 -112
- package/src/templates/components/character-count/_examples.character-count.njk +195 -195
- package/src/templates/components/character-count/_macro.character-count.njk +5 -5
- package/src/templates/components/character-count/_template.character-count.njk +38 -38
- package/src/templates/components/character-count/params.character-count.yaml +77 -77
- package/src/templates/components/checkboxes/_examples.checkboxes.njk +835 -835
- package/src/templates/components/checkboxes/_macro.checkboxes.njk +3 -3
- package/src/templates/components/checkboxes/_styles.checkboxes.css +30 -30
- package/src/templates/components/checkboxes/_template.checkboxes.njk +137 -137
- package/src/templates/components/checkboxes/params.checkboxes.yaml +116 -116
- package/src/templates/components/collapsible/_examples.collapsible.njk +107 -107
- package/src/templates/components/collapsible/_macro.collapsible.njk +3 -3
- package/src/templates/components/collapsible/_styles.collapsible.css +35 -35
- package/src/templates/components/collapsible/_template.collapsible.njk +17 -17
- package/src/templates/components/collapsible/params.collapsible.yaml +48 -48
- package/src/templates/components/combobox/_examples.combobox.njk +440 -0
- package/src/templates/components/combobox/_macro.combobox.njk +3 -0
- package/src/templates/components/combobox/_styles.combobox.css +92 -0
- package/src/templates/components/combobox/_template.combobox.njk +97 -0
- package/src/templates/components/combobox/params.combobox.yaml +102 -0
- package/src/templates/components/date-input/_examples.date-input.njk +578 -578
- package/src/templates/components/date-input/_macro.date-input.njk +3 -3
- package/src/templates/components/date-input/_template.date-input.njk +125 -125
- package/src/templates/components/date-input/params.date-input.yaml +97 -97
- package/src/templates/components/datepicker/_examples.datepicker.njk +415 -415
- package/src/templates/components/datepicker/_macro.datepicker.njk +3 -3
- package/src/templates/components/datepicker/_styles.datepicker.css +87 -87
- package/src/templates/components/datepicker/_template.datepicker.njk +129 -129
- package/src/templates/components/datepicker/params.datepicker.yaml +104 -104
- package/src/templates/components/description-list/_examples.description-list.njk +502 -502
- package/src/templates/components/description-list/_macro.description-list.njk +3 -3
- package/src/templates/components/description-list/_template.description-list.njk +17 -17
- package/src/templates/components/description-list/params.description-list.yaml +61 -61
- package/src/templates/components/details/_examples.details.njk +68 -68
- package/src/templates/components/details/_macro.details.njk +3 -3
- package/src/templates/components/details/_template.details.njk +17 -17
- package/src/templates/components/details/params.details.yaml +40 -40
- package/src/templates/components/dialog/_examples.dialog.njk +156 -156
- package/src/templates/components/dialog/_macro.dialog.njk +3 -3
- package/src/templates/components/dialog/_styles.dialog.css +27 -27
- package/src/templates/components/dialog/_template.dialog.njk +12 -12
- package/src/templates/components/dialog/params.dialog.yaml +25 -25
- package/src/templates/components/dropdown/_examples.dropdown.njk +226 -226
- package/src/templates/components/dropdown/_macro.dropdown.njk +3 -3
- package/src/templates/components/dropdown/_styles.dropdown.css +191 -191
- package/src/templates/components/dropdown/_template.dropdown.njk +34 -34
- package/src/templates/components/dropdown/params.dropdown.yaml +32 -32
- package/src/templates/components/error-message/_examples.error-message.njk +28 -28
- package/src/templates/components/error-message/_macro.error-message.njk +5 -5
- package/src/templates/components/error-message/_template.error-message.njk +8 -8
- package/src/templates/components/error-message/params.error-message.yaml +23 -23
- package/src/templates/components/error-summary/_examples.error-summary.njk +111 -111
- package/src/templates/components/error-summary/_macro.error-summary.njk +5 -5
- package/src/templates/components/error-summary/_template.error-summary.njk +48 -48
- package/src/templates/components/error-summary/params.error-summary.yaml +50 -50
- package/src/templates/components/fieldset/_examples.fieldset.njk +79 -79
- package/src/templates/components/fieldset/_macro.fieldset.njk +2 -2
- package/src/templates/components/fieldset/_template.fieldset.njk +46 -46
- package/src/templates/components/fieldset/params.fieldset.yaml +49 -49
- package/src/templates/components/file-upload/_examples.file-upload.njk +103 -103
- package/src/templates/components/file-upload/_macro.file-upload.njk +2 -2
- package/src/templates/components/file-upload/_template.file-upload.njk +45 -45
- package/src/templates/components/file-upload/params.file-upload.yaml +48 -48
- package/src/templates/components/footer/_examples.footer.njk +558 -558
- package/src/templates/components/footer/_macro.footer.njk +3 -3
- package/src/templates/components/footer/_styles.footer.css +43 -43
- package/src/templates/components/footer/_template.footer.njk +130 -130
- package/src/templates/components/footer/params.footer.yaml +140 -140
- package/src/templates/components/header/_examples.header.njk +627 -627
- package/src/templates/components/header/_macro.header.header__dropdown.njk +3 -3
- package/src/templates/components/header/_macro.header.header__navigation.njk +3 -3
- package/src/templates/components/header/_macro.header.header__offcanvas.njk +3 -3
- package/src/templates/components/header/_macro.header.header__offcanvasButton.njk +3 -3
- package/src/templates/components/header/_macro.header.header__subnav.njk +3 -3
- package/src/templates/components/header/_macro.header.njk +3 -3
- package/src/templates/components/header/_styles.header.css +12 -12
- package/src/templates/components/header/_template.header.header__dropdown.njk +27 -27
- package/src/templates/components/header/_template.header.header__navigation.njk +25 -25
- package/src/templates/components/header/_template.header.header__offcanvas.njk +20 -20
- package/src/templates/components/header/_template.header.header__offcanvasButton.njk +10 -10
- package/src/templates/components/header/_template.header.header__subnav.njk +33 -33
- package/src/templates/components/header/_template.header.njk +155 -155
- package/src/templates/components/header/params.header.yaml +295 -295
- package/src/templates/components/header-advanced/_examples.header-advanced.njk +1110 -1110
- package/src/templates/components/header-advanced/_macro.header-advanced.njk +3 -3
- package/src/templates/components/header-advanced/_template.header-advanced.njk +171 -171
- package/src/templates/components/header-advanced/params.header-advanced.yaml +352 -352
- package/src/templates/components/header-mini/_examples.header-mini.njk +319 -316
- package/src/templates/components/header-mini/_macro.header-mini.njk +3 -3
- package/src/templates/components/header-mini/_template.header-mini.njk +51 -51
- package/src/templates/components/header-mini/params.header-mini.yaml +90 -90
- package/src/templates/components/hint/_examples.hint.njk +26 -26
- package/src/templates/components/hint/_macro.hint.njk +3 -3
- package/src/templates/components/hint/_template.hint.njk +3 -3
- package/src/templates/components/hint/params.hint.yaml +21 -21
- package/src/templates/components/input/_examples.input.njk +393 -393
- package/src/templates/components/input/_macro.input.njk +3 -3
- package/src/templates/components/input/_styles.input.css +17 -17
- package/src/templates/components/input/_template.input.njk +53 -53
- package/src/templates/components/input/params.input.yaml +80 -80
- package/src/templates/components/input-group/_examples.input-group.njk +558 -558
- package/src/templates/components/input-group/_macro.input-group.njk +3 -3
- package/src/templates/components/input-group/_template.input-group.njk +107 -107
- package/src/templates/components/input-group/params.input-group.yaml +123 -123
- package/src/templates/components/item/_examples.item.njk +370 -370
- package/src/templates/components/item/_macro.item.njk +3 -3
- package/src/templates/components/item/_template.item.njk +73 -73
- package/src/templates/components/item/params.item.yaml +101 -101
- package/src/templates/components/label/_examples.label.njk +58 -58
- package/src/templates/components/label/_macro.label.njk +5 -5
- package/src/templates/components/label/_template.label.njk +37 -37
- package/src/templates/components/label/params.label.yaml +28 -28
- package/src/templates/components/links-list/_examples.links-list.njk +761 -761
- package/src/templates/components/links-list/_macro.links-list.njk +3 -3
- package/src/templates/components/links-list/_template.links-list.njk +119 -119
- package/src/templates/components/links-list/params.links-list.yaml +101 -101
- package/src/templates/components/listbox/_examples.listbox.njk +813 -812
- package/src/templates/components/listbox/_macro.listbox.njk +3 -3
- package/src/templates/components/listbox/_styles.listbox.css +218 -218
- package/src/templates/components/listbox/_template.listbox.njk +90 -90
- package/src/templates/components/listbox/params.listbox.yaml +85 -85
- package/src/templates/components/media-object/_examples.media-object.njk +79 -79
- package/src/templates/components/media-object/_macro.media-object.njk +3 -3
- package/src/templates/components/media-object/_template.media-object.njk +21 -21
- package/src/templates/components/media-object/params.media-object.yaml +16 -16
- package/src/templates/components/menu-horizontal/_examples.menu-horizontal.njk +613 -613
- package/src/templates/components/menu-horizontal/_macro.menu-horizontal.njk +3 -3
- package/src/templates/components/menu-horizontal/_styles.menu-horizontal.css +167 -167
- package/src/templates/components/menu-horizontal/_template.menu-horizontal.njk +36 -36
- package/src/templates/components/menu-horizontal/params.menu-horizontal.yaml +53 -53
- package/src/templates/components/menu-navigation/_examples.menu-navigation.njk +1292 -1292
- package/src/templates/components/menu-navigation/_macro.menu-navigation.njk +3 -3
- package/src/templates/components/menu-navigation/_styles.menu-navigation.css +238 -238
- package/src/templates/components/menu-navigation/_template.menu-navigation.njk +87 -87
- package/src/templates/components/menu-navigation/params.menu-navigation.yaml +86 -86
- package/src/templates/components/menu-vertical/_examples.menu-vertical.njk +835 -835
- package/src/templates/components/menu-vertical/_macro.menu-vertical.njk +3 -3
- package/src/templates/components/menu-vertical/_template.menu-vertical.njk +92 -92
- package/src/templates/components/menu-vertical/params.menu-vertical.yaml +77 -77
- package/src/templates/components/menubar/_examples.menubar.njk +1843 -1843
- package/src/templates/components/menubar/_macro.menubar.njk +3 -3
- package/src/templates/components/menubar/_styles.menubar.css +259 -259
- package/src/templates/components/menubar/_template.menubar.njk +106 -106
- package/src/templates/components/menubar/params.menubar.yaml +130 -130
- package/src/templates/components/modal/_examples.modal.njk +432 -432
- package/src/templates/components/modal/_macro.modal.njk +3 -3
- package/src/templates/components/modal/_template.modal.njk +129 -129
- package/src/templates/components/modal/params.modal.yaml +77 -77
- package/src/templates/components/nav/_examples.nav.njk +468 -468
- package/src/templates/components/nav/_macro.nav.njk +3 -3
- package/src/templates/components/nav/_template.nav.njk +53 -53
- package/src/templates/components/nav/params.nav.yaml +61 -61
- package/src/templates/components/notification/_examples.notification.njk +277 -277
- package/src/templates/components/notification/_macro.notification.njk +3 -3
- package/src/templates/components/notification/_styles.notification.css +30 -30
- package/src/templates/components/notification/_template.notification.njk +86 -86
- package/src/templates/components/notification/params.notification.yaml +101 -101
- package/src/templates/components/pagination/_examples.pagination.njk +444 -444
- package/src/templates/components/pagination/_macro.pagination.njk +3 -3
- package/src/templates/components/pagination/_template.pagination.njk +162 -162
- package/src/templates/components/pagination/params.pagination.yaml +150 -150
- package/src/templates/components/pill/_examples.pill.njk +180 -180
- package/src/templates/components/pill/_macro.pill.njk +3 -3
- package/src/templates/components/pill/_styles.pill.css +59 -59
- package/src/templates/components/pill/_template.pill.njk +38 -38
- package/src/templates/components/pill/params.pill.yaml +28 -28
- package/src/templates/components/radios/_examples.radios.njk +698 -698
- package/src/templates/components/radios/_macro.radios.njk +5 -5
- package/src/templates/components/radios/_styles.radios.css +30 -30
- package/src/templates/components/radios/_template.radios.njk +133 -133
- package/src/templates/components/radios/params.radios.yaml +104 -104
- package/src/templates/components/searchbar/_examples.searchbar.njk +147 -147
- package/src/templates/components/searchbar/_macro.searchbar.njk +3 -3
- package/src/templates/components/searchbar/_template.searchbar.njk +45 -45
- package/src/templates/components/searchbar/params.searchbar.yaml +46 -46
- package/src/templates/components/select/_examples.select.njk +364 -364
- package/src/templates/components/select/_macro.select.njk +5 -5
- package/src/templates/components/select/_styles.select.css +45 -45
- package/src/templates/components/select/_template.select.njk +67 -67
- package/src/templates/components/select/params.select.yaml +93 -93
- package/src/templates/components/skip-link/_examples.skip-link.njk +31 -31
- package/src/templates/components/skip-link/_macro.skip-link.njk +3 -3
- package/src/templates/components/skip-link/_styles.skip-link.css +12 -12
- package/src/templates/components/skip-link/_template.skip-link.njk +4 -4
- package/src/templates/components/skip-link/params.skip-link.yaml +20 -20
- package/src/templates/components/spinner/_examples.spinner.njk +91 -101
- package/src/templates/components/spinner/_macro.spinner.njk +3 -3
- package/src/templates/components/spinner/_styles.spinner.css +33 -33
- package/src/templates/components/spinner/_template.spinner.njk +12 -12
- package/src/templates/components/spinner/params.spinner.yaml +12 -12
- package/src/templates/components/status/_examples.status.njk +80 -80
- package/src/templates/components/status/_macro.status.njk +3 -3
- package/src/templates/components/status/_template.status.njk +23 -23
- package/src/templates/components/status/params.status.yaml +24 -24
- package/src/templates/components/status-item/_examples.status-item.njk +319 -319
- package/src/templates/components/status-item/_macro.status-item.njk +3 -3
- package/src/templates/components/status-item/_template.status-item.njk +99 -99
- package/src/templates/components/status-item/params.status-item.yaml +106 -106
- package/src/templates/components/table/_examples.table.njk +705 -705
- package/src/templates/components/table/_macro.table.njk +3 -3
- package/src/templates/components/table/_styles.table.css +19 -19
- package/src/templates/components/table/_template.table.njk +68 -68
- package/src/templates/components/table/params.table.yaml +130 -130
- package/src/templates/components/table-advanced/_examples.table-advanced.njk +839 -839
- package/src/templates/components/table-advanced/_macro.table-advanced.njk +3 -3
- package/src/templates/components/table-advanced/_styles.table-advanced.css +63 -63
- package/src/templates/components/table-advanced/_template.table-advanced.njk +210 -210
- package/src/templates/components/table-advanced/params.table-advanced.yaml +160 -160
- package/src/templates/components/tabs/_examples.tabs.njk +588 -588
- package/src/templates/components/tabs/_macro.tabs.njk +3 -3
- package/src/templates/components/tabs/_styles.tabs.css +132 -132
- package/src/templates/components/tabs/_template.tabs.njk +70 -70
- package/src/templates/components/tabs/params.tabs.yaml +83 -83
- package/src/templates/components/textarea/_examples.textarea.njk +174 -174
- package/src/templates/components/textarea/_macro.textarea.njk +5 -5
- package/src/templates/components/textarea/_template.textarea.njk +47 -47
- package/src/templates/components/textarea/params.textarea.yaml +64 -64
- package/src/templates/components/toggle/_examples.toggle.njk +248 -248
- package/src/templates/components/toggle/_macro.toggle.njk +3 -3
- package/src/templates/components/toggle/_styles.toggle.css +31 -31
- package/src/templates/components/toggle/_template.toggle.njk +27 -27
- package/src/templates/components/toggle/params.toggle.yaml +69 -69
- package/src/templates/components/tooltip/_examples.tooltip.njk +134 -134
- package/src/templates/components/tooltip/_macro.tooltip.njk +3 -3
- package/src/templates/components/tooltip/_styles.tooltip.css +9 -9
- package/src/templates/components/tooltip/_template.tooltip.njk +40 -40
- package/src/templates/components/tooltip/params.tooltip.yaml +40 -40
- package/src/templates/components/tree/_examples.tree.njk +2671 -2671
- package/src/templates/components/tree/_macro.tree.njk +3 -3
- package/src/templates/components/tree/_styles.tree.css +54 -54
- package/src/templates/components/tree/_template.tree.njk +232 -232
- package/src/templates/components/tree/params.tree.yaml +173 -173
- package/src/templates/components/treegrid/_examples.treegrid.njk +1269 -1269
- package/src/templates/components/treegrid/_macro.treegrid.njk +3 -3
- package/src/templates/components/treegrid/_styles.treegrid.css +31 -31
- package/src/templates/components/treegrid/_template.treegrid.njk +90 -90
- package/src/templates/components/treegrid/params.treegrid.yaml +131 -131
- package/src/templates/includes/_abrir-notificaciones-extra.njk +2 -2
- package/src/templates/includes/_abrir-notificaciones.njk +2 -2
- package/src/templates/includes/_acciones-de-cabecera.njk +28 -28
- package/src/templates/includes/_ejemplo-titulo-h2-parrafo.njk +4 -4
- package/src/templates/includes/_ejemplo-titulo-parrafo.njk +4 -4
- package/src/templates/includes/_test-include.njk +13 -13
- package/src/templates/pages/_page.foot-headroom.njk +30 -30
- package/src/templates/pages/_page.foot.njk +5 -5
- package/src/templates/pages/_page.footer.njk +19 -19
- package/src/templates/pages/_page.head.njk +27 -27
- package/src/templates/pages/_page.notification-edit-inner-content.njk +31 -31
- package/src/templates/pages/_page.notification-edit.njk +30 -30
- package/src/templates/pages/_page.notification-footer.njk +32 -32
- package/src/templates/pages/_page.notification-header-fixed.njk +30 -30
- package/src/templates/pages/_page.notification-header.njk +31 -31
- package/src/templates/pages/_page.sidebar-content.njk +20 -20
- package/src/templates/pages/_page.spinner-block.njk +14 -14
- package/src/templates/pages/_page.spinner-show.njk +15 -15
- package/src/templates/pages/_page.spinner.njk +15 -15
- package/src/templates/pages/_template.edit-fixed-with-sticky-sidebar.njk +36 -36
- package/src/templates/pages/_template.edit-fixed.njk +74 -74
- package/src/templates/pages/_template.home.njk +111 -111
- package/src/templates/pages/_template.logged-out.njk +56 -56
- package/src/templates/pages/_template.logged-selector-fixed-headroom.njk +198 -198
- package/src/templates/pages/_template.logged-selector-fixed.njk +195 -195
- package/src/templates/pages/_template.logged-selector-subheader.njk +80 -80
- package/src/templates/pages/_template.logged-selector-with-sidebar.njk +28 -28
- package/src/templates/pages/_template.logged-selector.njk +195 -195
- package/src/templates/pages/_template.logged.njk +141 -141
- package/src/templates/pages/_template.mfe-iframe-content.njk +31 -31
- package/src/templates/pages/_template.mfe.njk +83 -83
- package/src/templates/pages/_template.test.njk +48 -48
- package/src/templates/pages/_template.with-header-advanced.njk +311 -311
- package/vite.config.js +982 -982
package/vite.config.js
CHANGED
|
@@ -1,982 +1,982 @@
|
|
|
1
|
-
import tailwindcss from '@tailwindcss/vite';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { glob } from 'glob';
|
|
4
|
-
import hljs from 'highlight.js';
|
|
5
|
-
import beautify from 'js-beautify';
|
|
6
|
-
import yaml from 'js-yaml';
|
|
7
|
-
import nunjucks from 'nunjucks';
|
|
8
|
-
import { outdent } from 'outdent';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
import sharp from 'sharp';
|
|
11
|
-
import { defineConfig } from 'vite';
|
|
12
|
-
import { brandingPlugin, loadBrandingConfig } from './branding/vite-branding-plugin.js';
|
|
13
|
-
|
|
14
|
-
// Load branding configuration
|
|
15
|
-
const brandingConfig = await loadBrandingConfig(
|
|
16
|
-
process.env.BRANDING_CONFIG || 'default',
|
|
17
|
-
process.cwd(),
|
|
18
|
-
'branding'
|
|
19
|
-
);
|
|
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
|
-
|
|
43
|
-
// Helper functions
|
|
44
|
-
function kebabCaseToPascalCase(value) {
|
|
45
|
-
return value
|
|
46
|
-
.toLowerCase()
|
|
47
|
-
.split('-')
|
|
48
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
49
|
-
.join('');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function getBeautifyOptions() {
|
|
53
|
-
const options = beautify.html.defaultOptions();
|
|
54
|
-
return {
|
|
55
|
-
indent_size: 2,
|
|
56
|
-
inline: options.inline.filter((tag) => !['label'].includes(tag)),
|
|
57
|
-
max_preserve_newlines: 0,
|
|
58
|
-
wrap_attributes: 'preserve'
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Function to configure Nunjucks environment
|
|
63
|
-
function setupNunjucksEnvironment(paths, options = {}) {
|
|
64
|
-
const defaultOptions = {
|
|
65
|
-
autoescape: true,
|
|
66
|
-
trimBlocks: true,
|
|
67
|
-
lstripBlocks: true,
|
|
68
|
-
throwOnUndefined: false,
|
|
69
|
-
noCache: true
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const env = nunjucks.configure(paths, { ...defaultOptions, ...options });
|
|
73
|
-
|
|
74
|
-
// Global configuration
|
|
75
|
-
env.addGlobal('assetsUrl', '/src');
|
|
76
|
-
env.addGlobal('branding', brandingConfig);
|
|
77
|
-
|
|
78
|
-
env.addGlobal('loadComponentParams', function (componentName) {
|
|
79
|
-
try {
|
|
80
|
-
const yamlPath = path.join(process.cwd(), 'src/templates/components', componentName, `params.${componentName}.yaml`);
|
|
81
|
-
|
|
82
|
-
if (fs.existsSync(yamlPath)) {
|
|
83
|
-
const content = fs.readFileSync(yamlPath, 'utf8');
|
|
84
|
-
const parsedYaml = yaml.load(content);
|
|
85
|
-
const dumpedYaml = yaml.dump(parsedYaml);
|
|
86
|
-
return dumpedYaml;
|
|
87
|
-
}
|
|
88
|
-
console.error(`YAML file not found: ${yamlPath}`);
|
|
89
|
-
return '';
|
|
90
|
-
} catch (err) {
|
|
91
|
-
console.error(`Error loading YAML for ${componentName}:`, err);
|
|
92
|
-
return '';
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Four globals for code blocks
|
|
97
|
-
env.addGlobal('getHTMLCodeFromExample', function (componentName, params, pathName) {
|
|
98
|
-
const templatePath = `${pathName}s/${componentName}/_template.${componentName}.njk`;
|
|
99
|
-
const html = env.render(templatePath, { params }).trim();
|
|
100
|
-
return beautify.html(html, getBeautifyOptions());
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
env.addGlobal('getHTMLCodeFromFile', function (templatePath) {
|
|
104
|
-
const html = env.render(templatePath).trim();
|
|
105
|
-
return beautify.html(html, getBeautifyOptions());
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
env.addGlobal('getNunjucksCodeFromExample', function (componentName, params, pathName) {
|
|
109
|
-
const macroName = kebabCaseToPascalCase(componentName);
|
|
110
|
-
return outdent`
|
|
111
|
-
{% from "${pathName}s/${componentName}/_macro.${componentName}.njk" import ${pathName}${macroName} %}
|
|
112
|
-
{{ ${pathName}${macroName}(${JSON.stringify(params, undefined, 2)
|
|
113
|
-
}) }}
|
|
114
|
-
`;
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
env.addGlobal('getNunjucksCodeFromFile', function (templatePath) {
|
|
118
|
-
try {
|
|
119
|
-
const codeFromFile = fs.readFileSync(
|
|
120
|
-
path.join(process.cwd(), 'src/templates', templatePath),
|
|
121
|
-
'utf8'
|
|
122
|
-
);
|
|
123
|
-
return outdent`
|
|
124
|
-
${codeFromFile}
|
|
125
|
-
`;
|
|
126
|
-
} catch (err) {
|
|
127
|
-
console.error(`Error reading file ${templatePath}:`, err);
|
|
128
|
-
return '';
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// Filter configuration
|
|
133
|
-
env.addFilter('filtercaller', function (params) {
|
|
134
|
-
if (params && typeof params === 'object') {
|
|
135
|
-
const filteredParams = { ...params };
|
|
136
|
-
delete filteredParams.caller;
|
|
137
|
-
return filteredParams;
|
|
138
|
-
}
|
|
139
|
-
return params;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
env.addFilter('filterslugify', function (str) {
|
|
143
|
-
if (typeof str !== 'string') {
|
|
144
|
-
return '';
|
|
145
|
-
}
|
|
146
|
-
let slug = str.replace(/\s+/g, '-');
|
|
147
|
-
slug = slug.replace(/[^\w-]+/g, '');
|
|
148
|
-
slug = slug.toLowerCase();
|
|
149
|
-
return slug;
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
env.addFilter('highlight', function (code, language) {
|
|
153
|
-
try {
|
|
154
|
-
return hljs.highlight(code.trim(), {
|
|
155
|
-
language: language || 'plaintext'
|
|
156
|
-
}).value;
|
|
157
|
-
} catch (err) {
|
|
158
|
-
console.error('Error highlighting code:', err);
|
|
159
|
-
return code;
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
env.addFilter('filterescapeltgt', function (str) {
|
|
164
|
-
if (typeof str !== 'string' && typeof str !== 'object') return str;
|
|
165
|
-
if (typeof str !== 'string') str = JSON.stringify(str);
|
|
166
|
-
return str.replace(/</g, '<').replace(/>/g, '>');
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
env.addFilter('filterquotes', function (str) {
|
|
170
|
-
if (typeof str !== 'string') return str;
|
|
171
|
-
let inTag = false;
|
|
172
|
-
let result = '';
|
|
173
|
-
let currentQuote = null;
|
|
174
|
-
for (let i = 0; i < str.length; i++) {
|
|
175
|
-
const char = str[i];
|
|
176
|
-
if (char === '<') {
|
|
177
|
-
inTag = true;
|
|
178
|
-
result += char;
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (char === '>') {
|
|
182
|
-
inTag = false;
|
|
183
|
-
result += char;
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
if (inTag) {
|
|
187
|
-
if (char === "'" || char === '"') {
|
|
188
|
-
if (currentQuote === null) {
|
|
189
|
-
currentQuote = char;
|
|
190
|
-
result += '"';
|
|
191
|
-
} else if (currentQuote === char) {
|
|
192
|
-
currentQuote = null;
|
|
193
|
-
result += '"';
|
|
194
|
-
} else {
|
|
195
|
-
result += char;
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
result += char;
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
result += char;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return result;
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
env.addFilter('filterversion', function () {
|
|
208
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
|
|
209
|
-
return pkg.version;
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
return env;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Improved function to process Nunjucks files during build
|
|
216
|
-
async function processNunjucksFiles(env) {
|
|
217
|
-
try {
|
|
218
|
-
const docsPath = path.resolve(process.cwd(), 'docs');
|
|
219
|
-
const htmlFiles = await glob('*.html', { cwd: docsPath });
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
for (const htmlFile of htmlFiles) {
|
|
223
|
-
try {
|
|
224
|
-
const sourcePath = path.join(docsPath, htmlFile);
|
|
225
|
-
const outputPath = path.join(process.cwd(), 'dist', htmlFile);
|
|
226
|
-
|
|
227
|
-
const content = await fs.promises.readFile(sourcePath, 'utf8');
|
|
228
|
-
let processedHtml = env.renderString(content);
|
|
229
|
-
|
|
230
|
-
// Transformations for relative paths
|
|
231
|
-
processedHtml = processedHtml.replace(/href=["']\/src\/css\/([^"']*)["']/g, 'href="css/$1"');
|
|
232
|
-
processedHtml = processedHtml.replace(/href=["']src\/css\/([^"']*)["']/g, 'href="css/$1"');
|
|
233
|
-
|
|
234
|
-
// Transform JS paths (ensure they are relative without leading slash)
|
|
235
|
-
processedHtml = processedHtml.replace(/src=["']\/src\/js\/([^"']*)["']/g, 'src="js/$1"');
|
|
236
|
-
processedHtml = processedHtml.replace(/src=["']src\/js\/([^"']*)["']/g, 'src="js/$1"');
|
|
237
|
-
|
|
238
|
-
// Also process if any route with /js/ (absolute) remains
|
|
239
|
-
processedHtml = processedHtml.replace(/src=["']\/js\/([^"']*)["']/g, 'src="js/$1"');
|
|
240
|
-
|
|
241
|
-
// Transform image paths
|
|
242
|
-
processedHtml = processedHtml.replace(/src=["']\/src\/images\/([^"']*)["']/g, 'src="images/$1"');
|
|
243
|
-
processedHtml = processedHtml.replace(/src=["']src\/images\/([^"']*)["']/g, 'src="images/$1"');
|
|
244
|
-
|
|
245
|
-
await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });
|
|
246
|
-
await fs.promises.writeFile(outputPath, processedHtml);
|
|
247
|
-
} catch (fileError) {
|
|
248
|
-
console.error(`Error processing file ${htmlFile}:`, fileError);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
} catch (error) {
|
|
252
|
-
console.error('Error in processNunjucksFiles:', error);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// NEW FUNCTION: Process JavaScript files to remove CSS imports and fix modules
|
|
257
|
-
async function processJavaScriptFiles() {
|
|
258
|
-
try {
|
|
259
|
-
const srcJsDir = path.resolve(process.cwd(), 'src/js');
|
|
260
|
-
const distJsDir = path.resolve(process.cwd(), 'dist/js');
|
|
261
|
-
|
|
262
|
-
// Create dist/js directory if it doesn't exist
|
|
263
|
-
await fs.promises.mkdir(distJsDir, { recursive: true });
|
|
264
|
-
|
|
265
|
-
// 1. Copy npm packages to the vendor folder
|
|
266
|
-
const vendorDir = path.join(distJsDir, 'vendor');
|
|
267
|
-
await fs.promises.mkdir(vendorDir, { recursive: true });
|
|
268
|
-
|
|
269
|
-
// 2. Process all JS files
|
|
270
|
-
const jsFiles = await glob('**/*.js', { cwd: srcJsDir });
|
|
271
|
-
|
|
272
|
-
for (const jsFile of jsFiles) {
|
|
273
|
-
const sourcePath = path.join(srcJsDir, jsFile);
|
|
274
|
-
const destPath = path.join(distJsDir, jsFile);
|
|
275
|
-
|
|
276
|
-
// Create directory if necessary
|
|
277
|
-
await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
|
|
278
|
-
|
|
279
|
-
// Read file content
|
|
280
|
-
const content = await fs.promises.readFile(sourcePath, 'utf8');
|
|
281
|
-
|
|
282
|
-
// Transform the content
|
|
283
|
-
let processedContent = content;
|
|
284
|
-
|
|
285
|
-
// a) Remove CSS file imports
|
|
286
|
-
processedContent = processedContent.replace(
|
|
287
|
-
/import\s+['"](.+\.css)['"];?/g,
|
|
288
|
-
'// CSS import removed: $1'
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
// c) Fix other relative import paths between JS files
|
|
292
|
-
processedContent = processedContent.replace(
|
|
293
|
-
/from\s+["']\/src\/js\/([^"']*)["']/g,
|
|
294
|
-
(match, importPath) => {
|
|
295
|
-
const depth = jsFile.split('/').length - 1;
|
|
296
|
-
const prefix = depth > 0 ? '../'.repeat(depth) : './';
|
|
297
|
-
return `from "${prefix}${importPath}"`;
|
|
298
|
-
}
|
|
299
|
-
);
|
|
300
|
-
|
|
301
|
-
processedContent = processedContent.replace(
|
|
302
|
-
/from\s+["']src\/js\/([^"']*)["']/g,
|
|
303
|
-
(match, importPath) => {
|
|
304
|
-
const depth = jsFile.split('/').length - 1;
|
|
305
|
-
const prefix = depth > 0 ? '../'.repeat(depth) : './';
|
|
306
|
-
return `from "${prefix}${importPath}"`;
|
|
307
|
-
}
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
// Write the processed file
|
|
311
|
-
await fs.promises.writeFile(destPath, processedContent);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
} catch (error) {
|
|
315
|
-
console.error('Error processing JavaScript:', error);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Optimize image with Sharp
|
|
320
|
-
async function optimizeImage(sourcePath, destPath, fileExtension) {
|
|
321
|
-
try {
|
|
322
|
-
const image = sharp(sourcePath);
|
|
323
|
-
|
|
324
|
-
switch (fileExtension.toLowerCase()) {
|
|
325
|
-
case '.jpg':
|
|
326
|
-
case '.jpeg':
|
|
327
|
-
await image
|
|
328
|
-
.jpeg({
|
|
329
|
-
quality: 80,
|
|
330
|
-
mozjpeg: true,
|
|
331
|
-
progressive: true
|
|
332
|
-
})
|
|
333
|
-
.toFile(destPath);
|
|
334
|
-
break;
|
|
335
|
-
|
|
336
|
-
case '.png':
|
|
337
|
-
await image
|
|
338
|
-
.png({
|
|
339
|
-
quality: 80,
|
|
340
|
-
palette: true,
|
|
341
|
-
compressionLevel: 9
|
|
342
|
-
})
|
|
343
|
-
.toFile(destPath);
|
|
344
|
-
break;
|
|
345
|
-
|
|
346
|
-
case '.webp':
|
|
347
|
-
await image
|
|
348
|
-
.webp({
|
|
349
|
-
quality: 80,
|
|
350
|
-
effort: 6
|
|
351
|
-
})
|
|
352
|
-
.toFile(destPath);
|
|
353
|
-
break;
|
|
354
|
-
|
|
355
|
-
case '.avif':
|
|
356
|
-
await image
|
|
357
|
-
.avif({
|
|
358
|
-
quality: 80,
|
|
359
|
-
effort: 4
|
|
360
|
-
})
|
|
361
|
-
.toFile(destPath);
|
|
362
|
-
break;
|
|
363
|
-
|
|
364
|
-
case '.svg':
|
|
365
|
-
// SVGs are not processed with Sharp, they are copied directly
|
|
366
|
-
await fs.promises.copyFile(sourcePath, destPath);
|
|
367
|
-
break;
|
|
368
|
-
|
|
369
|
-
default:
|
|
370
|
-
// For other formats, copy without optimizing
|
|
371
|
-
await fs.promises.copyFile(sourcePath, destPath);
|
|
372
|
-
break;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return true;
|
|
376
|
-
} catch (error) {
|
|
377
|
-
console.error(`❌ Error optimizing ${sourcePath}:`, error.message);
|
|
378
|
-
// If optimization fails, try direct copy as fallback
|
|
379
|
-
try {
|
|
380
|
-
await fs.promises.copyFile(sourcePath, destPath);
|
|
381
|
-
return true;
|
|
382
|
-
} catch (copyError) {
|
|
383
|
-
console.error(`❌ Error copying ${sourcePath}:`, copyError.message);
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// UPDATED FUNCTION: Copy and optimize all images
|
|
390
|
-
async function copyAndOptimizeImages() {
|
|
391
|
-
try {
|
|
392
|
-
const srcImagesDir = path.resolve(process.cwd(), 'public/images');
|
|
393
|
-
const distImagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
394
|
-
|
|
395
|
-
// Check if public/images folder exists
|
|
396
|
-
if (!fs.existsSync(srcImagesDir)) {
|
|
397
|
-
console.log('📁 public/images does not exist, skipping image optimization');
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Create dist/images directory if it doesn't exist
|
|
402
|
-
await fs.promises.mkdir(distImagesDir, { recursive: true });
|
|
403
|
-
|
|
404
|
-
// Get all image files recursively
|
|
405
|
-
const imageFiles = await glob('**/*.{png,jpg,jpeg,gif,svg,webp,avif,ico,bmp,tiff}', {
|
|
406
|
-
cwd: srcImagesDir,
|
|
407
|
-
nodir: true
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
console.log(`🖼️ Optimizing ${imageFiles.length} images with Sharp...`);
|
|
411
|
-
|
|
412
|
-
let optimizedCount = 0;
|
|
413
|
-
let copiedCount = 0;
|
|
414
|
-
let totalSizeBefore = 0;
|
|
415
|
-
let totalSizeAfter = 0;
|
|
416
|
-
|
|
417
|
-
for (const imageFile of imageFiles) {
|
|
418
|
-
const sourcePath = path.join(srcImagesDir, imageFile);
|
|
419
|
-
const destPath = path.join(distImagesDir, imageFile);
|
|
420
|
-
const fileExtension = path.extname(imageFile);
|
|
421
|
-
|
|
422
|
-
// Create directory if necessary (for subdirectories)
|
|
423
|
-
await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
|
|
424
|
-
|
|
425
|
-
// Get original file size
|
|
426
|
-
const statsBefore = await fs.promises.stat(sourcePath);
|
|
427
|
-
totalSizeBefore += statsBefore.size;
|
|
428
|
-
|
|
429
|
-
// Optimize or copy image
|
|
430
|
-
const success = await optimizeImage(sourcePath, destPath, fileExtension);
|
|
431
|
-
|
|
432
|
-
if (success) {
|
|
433
|
-
// Get file size after optimization
|
|
434
|
-
if (fs.existsSync(destPath)) {
|
|
435
|
-
const statsAfter = await fs.promises.stat(destPath);
|
|
436
|
-
totalSizeAfter += statsAfter.size;
|
|
437
|
-
|
|
438
|
-
const reduction = ((statsBefore.size - statsAfter.size) / statsBefore.size * 100).toFixed(1);
|
|
439
|
-
|
|
440
|
-
if (['.jpg', '.jpeg', '.png', '.webp', '.avif'].includes(fileExtension.toLowerCase())) {
|
|
441
|
-
console.log(`✨ Optimized: ${imageFile} (${reduction}% reduced)`);
|
|
442
|
-
optimizedCount++;
|
|
443
|
-
} else {
|
|
444
|
-
console.log(`📄 Copied: ${imageFile}`);
|
|
445
|
-
copiedCount++;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const totalReduction = ((totalSizeBefore - totalSizeAfter) / totalSizeBefore * 100).toFixed(1);
|
|
452
|
-
const sizeBefore = (totalSizeBefore / 1024).toFixed(1);
|
|
453
|
-
const sizeAfter = (totalSizeAfter / 1024).toFixed(1);
|
|
454
|
-
|
|
455
|
-
console.log(`🎉 Processing completed:`);
|
|
456
|
-
console.log(` ✨ ${optimizedCount} images optimized`);
|
|
457
|
-
console.log(` 📄 ${copiedCount} files copied`);
|
|
458
|
-
console.log(` 📊 Size: ${sizeBefore}KB → ${sizeAfter}KB (${totalReduction}% reduced)`);
|
|
459
|
-
|
|
460
|
-
} catch (error) {
|
|
461
|
-
console.error('❌ Error in copyAndOptimizeImages:', error);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Copy branding assets to dist/images
|
|
466
|
-
async function copyBrandingAssets() {
|
|
467
|
-
try {
|
|
468
|
-
const brandingDir = path.resolve(process.cwd(), 'branding');
|
|
469
|
-
const distImagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
470
|
-
|
|
471
|
-
if (!fs.existsSync(brandingDir)) {
|
|
472
|
-
console.log('📁 branding/ does not exist, skipping branding assets');
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
await fs.promises.mkdir(distImagesDir, { recursive: true });
|
|
477
|
-
|
|
478
|
-
// Copy logos (flattened to images/)
|
|
479
|
-
const logosDir = path.join(brandingDir, 'logos');
|
|
480
|
-
if (fs.existsSync(logosDir)) {
|
|
481
|
-
const logoFiles = await glob('**/*.svg', { cwd: logosDir, nodir: true });
|
|
482
|
-
for (const file of logoFiles) {
|
|
483
|
-
const sourcePath = path.join(logosDir, file);
|
|
484
|
-
const fileName = path.basename(file); // Get just the filename to flatten structure
|
|
485
|
-
const destPath = path.join(distImagesDir, fileName);
|
|
486
|
-
await fs.promises.copyFile(sourcePath, destPath);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Copy header background images
|
|
491
|
-
const imagesDir = path.join(brandingDir, 'images');
|
|
492
|
-
if (fs.existsSync(imagesDir)) {
|
|
493
|
-
const imageFiles = await glob('*.svg', { cwd: imagesDir, nodir: true });
|
|
494
|
-
for (const file of imageFiles) {
|
|
495
|
-
const sourcePath = path.join(imagesDir, file);
|
|
496
|
-
const destPath = path.join(distImagesDir, file);
|
|
497
|
-
await fs.promises.copyFile(sourcePath, destPath);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
console.log('✅ Branding assets copied to dist/images/');
|
|
502
|
-
} catch (error) {
|
|
503
|
-
console.error('❌ Error in copyBrandingAssets:', error);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Function to copy assets (updated to use optimization)
|
|
508
|
-
async function copyAssets() {
|
|
509
|
-
try {
|
|
510
|
-
// Copy and optimize images
|
|
511
|
-
await copyAndOptimizeImages();
|
|
512
|
-
|
|
513
|
-
// Copy branding assets
|
|
514
|
-
await copyBrandingAssets();
|
|
515
|
-
|
|
516
|
-
// Copy license if it exists
|
|
517
|
-
const licenseFile = path.resolve(process.cwd(), 'EUPL-1.2.txt');
|
|
518
|
-
if (fs.existsSync(licenseFile)) {
|
|
519
|
-
await fs.promises.copyFile(licenseFile, path.join(process.cwd(), 'dist', 'EUPL-1.2.txt'));
|
|
520
|
-
}
|
|
521
|
-
} catch (error) {
|
|
522
|
-
console.error('Error copying assets:', error);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Final verification to ensure there are no common errors
|
|
527
|
-
async function validateBuild() {
|
|
528
|
-
try {
|
|
529
|
-
const distPath = path.resolve(process.cwd(), 'dist');
|
|
530
|
-
|
|
531
|
-
// 1. Check that there are no src/ references in HTML files
|
|
532
|
-
const htmlFiles = await glob('**/*.html', { cwd: distPath });
|
|
533
|
-
|
|
534
|
-
for (const htmlFile of htmlFiles) {
|
|
535
|
-
const filePath = path.join(distPath, htmlFile);
|
|
536
|
-
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
537
|
-
|
|
538
|
-
// Search for /src/ or src/ references
|
|
539
|
-
if (content.includes('/src/') || content.includes('src/')) {
|
|
540
|
-
|
|
541
|
-
// Auto-correct
|
|
542
|
-
let correctedContent = content
|
|
543
|
-
.replace(/href=["']\/src\/([^"']*)["']/g, 'href="$1"')
|
|
544
|
-
.replace(/href=["']src\/([^"']*)["']/g, 'href="$1"')
|
|
545
|
-
.replace(/src=["']\/src\/([^"']*)["']/g, 'src="$1"')
|
|
546
|
-
.replace(/src=["']src\/([^"']*)["']/g, 'src="$1"');
|
|
547
|
-
|
|
548
|
-
await fs.promises.writeFile(filePath, correctedContent);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// 2. Check that there are no absolute references in scripts
|
|
553
|
-
for (const htmlFile of htmlFiles) {
|
|
554
|
-
const filePath = path.join(distPath, htmlFile);
|
|
555
|
-
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
556
|
-
|
|
557
|
-
// Search for script tags with src that starts with "/"
|
|
558
|
-
const hasAbsoluteScripts = /<script[^>]*src=["']\/[^"']*["'][^>]*>/g.test(content);
|
|
559
|
-
|
|
560
|
-
if (hasAbsoluteScripts) {
|
|
561
|
-
|
|
562
|
-
// Auto-correct
|
|
563
|
-
let correctedContent = content.replace(
|
|
564
|
-
/<script([^>]*)src=["']\/([^"']*)["']([^>]*)>/g,
|
|
565
|
-
'<script$1src="$2"$3>'
|
|
566
|
-
);
|
|
567
|
-
|
|
568
|
-
await fs.promises.writeFile(filePath, correctedContent);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// 3. Check JS files
|
|
573
|
-
const jsFiles = await glob('js/**/*.js', { cwd: distPath });
|
|
574
|
-
|
|
575
|
-
for (const jsFile of jsFiles) {
|
|
576
|
-
const filePath = path.join(distPath, jsFile);
|
|
577
|
-
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
578
|
-
|
|
579
|
-
// Search for CSS imports
|
|
580
|
-
if (content.includes('.css"') || content.includes(".css'")) {
|
|
581
|
-
|
|
582
|
-
// Comment out these imports
|
|
583
|
-
let correctedContent = content.replace(
|
|
584
|
-
/import\s+['"](.+\.css)['"];?/g,
|
|
585
|
-
'// CSS import removed: $1'
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
await fs.promises.writeFile(filePath, correctedContent);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
} catch (error) {
|
|
593
|
-
console.error('Error validating build:', error);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// Correct CSS image paths for production
|
|
598
|
-
async function rewriteCssImagePaths() {
|
|
599
|
-
const stylePath = path.resolve(process.cwd(), 'dist/css/styles.css');
|
|
600
|
-
if (!fs.existsSync(stylePath)) return;
|
|
601
|
-
let content = await fs.promises.readFile(stylePath, 'utf8');
|
|
602
|
-
content = content.replace(/url\((['"]?)\/images\//g, 'url($1../images/');
|
|
603
|
-
await fs.promises.writeFile(stylePath, content);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
// Correct HTML image paths for production (from /images/, /branding/logos/, /branding/images/ to ../images/)
|
|
607
|
-
async function rewriteHtmlImagePaths() {
|
|
608
|
-
const distDir = path.resolve(process.cwd(), 'dist');
|
|
609
|
-
if (!fs.existsSync(distDir)) return;
|
|
610
|
-
|
|
611
|
-
const htmlFiles = await glob('**/*.html', { cwd: distDir, nodir: true });
|
|
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
|
-
|
|
626
|
-
for (const htmlFile of htmlFiles) {
|
|
627
|
-
const filePath = path.join(distDir, htmlFile);
|
|
628
|
-
let content = await fs.promises.readFile(filePath, 'utf8');
|
|
629
|
-
|
|
630
|
-
// Aplicar todas las reglas
|
|
631
|
-
let modified = content;
|
|
632
|
-
for (const rule of rewriteRules) {
|
|
633
|
-
modified = modified.replace(rule.pattern, rule.replacement);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
if (modified !== content) {
|
|
637
|
-
await fs.promises.writeFile(filePath, modified);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
|
|
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;
|
|
647
|
-
|
|
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
|
-
];
|
|
656
|
-
|
|
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
|
-
}
|
|
674
|
-
|
|
675
|
-
if (modified !== content) {
|
|
676
|
-
await fs.promises.writeFile(filePath, modified);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if (htmlFiles.length > 0) {
|
|
681
|
-
console.log(`✓ Rewrote favicon paths in ${htmlFiles.length} HTML file(s)`);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Custom Nunjucks plugin
|
|
686
|
-
function customNunjucksPlugin() {
|
|
687
|
-
let env;
|
|
688
|
-
|
|
689
|
-
return {
|
|
690
|
-
name: 'vite-plugin-custom-nunjucks',
|
|
691
|
-
|
|
692
|
-
configureServer({ middlewares, watcher, ws }) {
|
|
693
|
-
|
|
694
|
-
// Configure Nunjucks with development-specific options
|
|
695
|
-
env = setupNunjucksEnvironment(['docs', 'src/templates'], {
|
|
696
|
-
watch: true,
|
|
697
|
-
throwOnUndefined: false
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
// Watch for CSS and NJK files
|
|
701
|
-
watcher.add(['src/**/*.css', 'docs/**/*.njk', 'src/**/*.njk']);
|
|
702
|
-
watcher.on('change', (path) => {
|
|
703
|
-
ws.send({
|
|
704
|
-
type: 'full-reload'
|
|
705
|
-
});
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
// Middleware to serve branding assets in development
|
|
709
|
-
middlewares.use(async (req, res, next) => {
|
|
710
|
-
if (req.url.startsWith('/branding/')) {
|
|
711
|
-
const filePath = path.join(process.cwd(), req.url);
|
|
712
|
-
if (serveFileWithMimeType(filePath, res)) return;
|
|
713
|
-
}
|
|
714
|
-
next();
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
// Helper function to find file recursively in directories
|
|
718
|
-
function findFileRecursive(dir, fileName) {
|
|
719
|
-
if (!fs.existsSync(dir)) return null;
|
|
720
|
-
const files = fs.readdirSync(dir);
|
|
721
|
-
for (const file of files) {
|
|
722
|
-
const fullPath = path.join(dir, file);
|
|
723
|
-
if (fs.statSync(fullPath).isDirectory()) {
|
|
724
|
-
const found = findFileRecursive(fullPath, fileName);
|
|
725
|
-
if (found) return found;
|
|
726
|
-
} else if (file === fileName) {
|
|
727
|
-
return fullPath;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
return null;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// Middleware to serve branding assets from /images/ in development
|
|
734
|
-
middlewares.use(async (req, res, next) => {
|
|
735
|
-
if (req.url.startsWith('/images/')) {
|
|
736
|
-
let filePath = path.join(process.cwd(), 'public', req.url);
|
|
737
|
-
|
|
738
|
-
if (!fs.existsSync(filePath)) {
|
|
739
|
-
const fileName = path.basename(req.url);
|
|
740
|
-
|
|
741
|
-
// Search in branding/logos (including subdirectories like eu/)
|
|
742
|
-
const brandingLogosPath = path.join(process.cwd(), 'branding/logos');
|
|
743
|
-
const foundInLogos = findFileRecursive(brandingLogosPath, fileName);
|
|
744
|
-
|
|
745
|
-
if (foundInLogos) {
|
|
746
|
-
filePath = foundInLogos;
|
|
747
|
-
} else {
|
|
748
|
-
// Search in branding/images
|
|
749
|
-
const brandingImagesPath = path.join(process.cwd(), 'branding/images');
|
|
750
|
-
const foundInImages = findFileRecursive(brandingImagesPath, fileName);
|
|
751
|
-
if (foundInImages) {
|
|
752
|
-
filePath = foundInImages;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
if (fs.existsSync(filePath)) {
|
|
758
|
-
if (serveFileWithMimeType(filePath, res)) return;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
next();
|
|
762
|
-
});
|
|
763
|
-
|
|
764
|
-
// Middleware to serve branding assets from /branding/logos/ in development
|
|
765
|
-
middlewares.use(async (req, res, next) => {
|
|
766
|
-
if (req.url.startsWith('/branding/logos/')) {
|
|
767
|
-
const fileName = path.basename(req.url);
|
|
768
|
-
const brandingLogosPath = path.join(process.cwd(), 'branding/logos');
|
|
769
|
-
const filePath = findFileRecursive(brandingLogosPath, fileName);
|
|
770
|
-
|
|
771
|
-
if (serveFileWithMimeType(filePath, res)) return;
|
|
772
|
-
}
|
|
773
|
-
next();
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
// Middleware to serve branding assets from /branding/images/ in development
|
|
777
|
-
middlewares.use(async (req, res, next) => {
|
|
778
|
-
if (req.url.startsWith('/branding/images/')) {
|
|
779
|
-
const fileName = path.basename(req.url);
|
|
780
|
-
const brandingImagesPath = path.join(process.cwd(), 'branding/images');
|
|
781
|
-
const filePath = findFileRecursive(brandingImagesPath, fileName);
|
|
782
|
-
|
|
783
|
-
if (serveFileWithMimeType(filePath, res)) return;
|
|
784
|
-
}
|
|
785
|
-
next();
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
// Middleware to transform paths in development (keep as is)
|
|
789
|
-
middlewares.use(async (req, res, next) => {
|
|
790
|
-
// Intercept requests to /css/ and redirect to /src/css/
|
|
791
|
-
if (req.url.startsWith('/css/')) {
|
|
792
|
-
req.url = req.url.replace('/css/', '/src/css/');
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// Intercept requests to /js/ and redirect to /src/js/
|
|
796
|
-
if (req.url.startsWith('/js/')) {
|
|
797
|
-
req.url = req.url.replace('/js/', '/src/js/');
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
// Set correct MIME type for JS
|
|
801
|
-
if (req.url.endsWith('.js')) {
|
|
802
|
-
res.setHeader('Content-Type', 'application/javascript');
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
next();
|
|
806
|
-
});
|
|
807
|
-
|
|
808
|
-
// Middleware for CSS
|
|
809
|
-
middlewares.use(async (req, res, next) => {
|
|
810
|
-
if (req.url.startsWith('/src/css/')) {
|
|
811
|
-
return next();
|
|
812
|
-
}
|
|
813
|
-
next();
|
|
814
|
-
});
|
|
815
|
-
|
|
816
|
-
// Middleware for .html
|
|
817
|
-
middlewares.use(async (req, res, next) => {
|
|
818
|
-
if (req.url === '/') {
|
|
819
|
-
req.url = '/index.html';
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
if (req.url.endsWith('.html')) {
|
|
823
|
-
try {
|
|
824
|
-
const templatePath = req.url.slice(1);
|
|
825
|
-
const html = env.render(templatePath);
|
|
826
|
-
res.setHeader('Content-Type', 'text/html');
|
|
827
|
-
return res.end(html);
|
|
828
|
-
} catch (e) {
|
|
829
|
-
console.error('Error rendering template:', e.message);
|
|
830
|
-
return next(e);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
next();
|
|
834
|
-
});
|
|
835
|
-
},
|
|
836
|
-
|
|
837
|
-
configurePreviewServer(server) {
|
|
838
|
-
const { middlewares } = server;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
// Configure Nunjucks with preview-specific options
|
|
842
|
-
env = setupNunjucksEnvironment(['docs', 'src/templates'], {
|
|
843
|
-
watch: false,
|
|
844
|
-
throwOnUndefined: false
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
// Configure the middlewares
|
|
848
|
-
middlewares.use(async (req, res, next) => {
|
|
849
|
-
if (req.url === '/') {
|
|
850
|
-
req.url = '/index.html';
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
if (req.url.endsWith('.html')) {
|
|
854
|
-
try {
|
|
855
|
-
const templatePath = req.url.slice(1);
|
|
856
|
-
const html = env.render(templatePath);
|
|
857
|
-
res.setHeader('Content-Type', 'text/html');
|
|
858
|
-
return res.end(html);
|
|
859
|
-
} catch (e) {
|
|
860
|
-
console.error('Error in preview:', e.message);
|
|
861
|
-
return next(e);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
next();
|
|
865
|
-
});
|
|
866
|
-
},
|
|
867
|
-
|
|
868
|
-
transform(content, id) {
|
|
869
|
-
if (id.endsWith('.njk')) {
|
|
870
|
-
const html = env.renderString(content);
|
|
871
|
-
return {
|
|
872
|
-
code: `export default ${JSON.stringify(html)}`,
|
|
873
|
-
map: null
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
},
|
|
877
|
-
|
|
878
|
-
async buildStart() {
|
|
879
|
-
// Configure Nunjucks for the build
|
|
880
|
-
env = setupNunjucksEnvironment(['docs', 'docs/ds', 'src/templates'], {
|
|
881
|
-
watch: false,
|
|
882
|
-
throwOnUndefined: false
|
|
883
|
-
});
|
|
884
|
-
},
|
|
885
|
-
|
|
886
|
-
async closeBundle() {
|
|
887
|
-
console.log('🚀 Starting custom build process...');
|
|
888
|
-
|
|
889
|
-
// Step 1: Copy and optimize assets (including images) first
|
|
890
|
-
await copyAssets();
|
|
891
|
-
|
|
892
|
-
// Step 2: Correct CSS image paths for production
|
|
893
|
-
await rewriteCssImagePaths();
|
|
894
|
-
|
|
895
|
-
// Step 3: Skip processing JavaScript files - let Vite handle bundling
|
|
896
|
-
// (Vite will resolve npm dependencies like @floating-ui/dom correctly)
|
|
897
|
-
// await processJavaScriptFiles();
|
|
898
|
-
|
|
899
|
-
// Step 4: Process Nunjucks files (images are already in dist)
|
|
900
|
-
await processNunjucksFiles(env);
|
|
901
|
-
|
|
902
|
-
// Step 4b: Correct HTML image paths for production (after HTML files are generated)
|
|
903
|
-
await rewriteHtmlImagePaths();
|
|
904
|
-
|
|
905
|
-
// Step 4c: Rewrite favicon paths from /branding/logos/ to ./images/
|
|
906
|
-
await rewriteFaviconPaths();
|
|
907
|
-
|
|
908
|
-
// Step 5: Final validation to find and fix common errors
|
|
909
|
-
await validateBuild();
|
|
910
|
-
|
|
911
|
-
console.log('✅ Custom build completed');
|
|
912
|
-
}
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// Main Vite configuration
|
|
917
|
-
export default defineConfig({
|
|
918
|
-
plugins: [
|
|
919
|
-
brandingPlugin(brandingConfig),
|
|
920
|
-
customNunjucksPlugin(),
|
|
921
|
-
tailwindcss()
|
|
922
|
-
],
|
|
923
|
-
build: {
|
|
924
|
-
outDir: 'dist',
|
|
925
|
-
minify: true,
|
|
926
|
-
emptyOutDir: true,
|
|
927
|
-
cssCodeSplit: false,
|
|
928
|
-
manifest: true,
|
|
929
|
-
rollupOptions: {
|
|
930
|
-
input: {
|
|
931
|
-
index: path.resolve(__dirname, 'src/js/index.js')
|
|
932
|
-
},
|
|
933
|
-
output: {
|
|
934
|
-
entryFileNames: 'js/[name].js',
|
|
935
|
-
chunkFileNames: 'js/[name]-[hash].js',
|
|
936
|
-
assetFileNames: (assetInfo) => {
|
|
937
|
-
// Rename style.css to styles.css
|
|
938
|
-
if (assetInfo.name === 'style.css') {
|
|
939
|
-
return 'css/styles.css';
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// Handle other asset types
|
|
943
|
-
const info = assetInfo.name.split('.');
|
|
944
|
-
const extType = info[info.length - 1];
|
|
945
|
-
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) {
|
|
946
|
-
return 'images/[name][extname]';
|
|
947
|
-
}
|
|
948
|
-
if (/css/i.test(extType)) {
|
|
949
|
-
return 'css/[name][extname]';
|
|
950
|
-
}
|
|
951
|
-
return '[name][extname]';
|
|
952
|
-
},
|
|
953
|
-
format: 'es',
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
},
|
|
957
|
-
server: {
|
|
958
|
-
open: '/',
|
|
959
|
-
watch: {
|
|
960
|
-
ignored: ['!**/docs/**', '!**/src/templates/**']
|
|
961
|
-
},
|
|
962
|
-
host: '0.0.0.0',
|
|
963
|
-
allowedHosts: [
|
|
964
|
-
'82c7d68c-1131-4db5-bf36-165d8f8b1d98-00-2b55tnaqky432.worf.replit.dev',
|
|
965
|
-
'localhost'
|
|
966
|
-
]
|
|
967
|
-
},
|
|
968
|
-
resolve: {
|
|
969
|
-
alias: {
|
|
970
|
-
'@': path.resolve(__dirname, 'src'),
|
|
971
|
-
'/src': path.resolve(__dirname, 'src'),
|
|
972
|
-
'@css': path.resolve(__dirname, 'src/css'),
|
|
973
|
-
'@components': path.resolve(__dirname, 'src/templates/components'),
|
|
974
|
-
'@ds': path.resolve(__dirname, 'docs/ds'),
|
|
975
|
-
'@images': path.resolve(__dirname, 'public/images')
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
|
-
publicDir: 'public',
|
|
979
|
-
optimizeDeps: {
|
|
980
|
-
include: ['tailwindcss']
|
|
981
|
-
}
|
|
982
|
-
});
|
|
1
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import hljs from 'highlight.js';
|
|
5
|
+
import beautify from 'js-beautify';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import nunjucks from 'nunjucks';
|
|
8
|
+
import { outdent } from 'outdent';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import sharp from 'sharp';
|
|
11
|
+
import { defineConfig } from 'vite';
|
|
12
|
+
import { brandingPlugin, loadBrandingConfig } from './branding/vite-branding-plugin.js';
|
|
13
|
+
|
|
14
|
+
// Load branding configuration
|
|
15
|
+
const brandingConfig = await loadBrandingConfig(
|
|
16
|
+
process.env.BRANDING_CONFIG || 'default',
|
|
17
|
+
process.cwd(),
|
|
18
|
+
'branding'
|
|
19
|
+
);
|
|
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
|
+
|
|
43
|
+
// Helper functions
|
|
44
|
+
function kebabCaseToPascalCase(value) {
|
|
45
|
+
return value
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.split('-')
|
|
48
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
49
|
+
.join('');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getBeautifyOptions() {
|
|
53
|
+
const options = beautify.html.defaultOptions();
|
|
54
|
+
return {
|
|
55
|
+
indent_size: 2,
|
|
56
|
+
inline: options.inline.filter((tag) => !['label'].includes(tag)),
|
|
57
|
+
max_preserve_newlines: 0,
|
|
58
|
+
wrap_attributes: 'preserve'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Function to configure Nunjucks environment
|
|
63
|
+
function setupNunjucksEnvironment(paths, options = {}) {
|
|
64
|
+
const defaultOptions = {
|
|
65
|
+
autoescape: true,
|
|
66
|
+
trimBlocks: true,
|
|
67
|
+
lstripBlocks: true,
|
|
68
|
+
throwOnUndefined: false,
|
|
69
|
+
noCache: true
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const env = nunjucks.configure(paths, { ...defaultOptions, ...options });
|
|
73
|
+
|
|
74
|
+
// Global configuration
|
|
75
|
+
env.addGlobal('assetsUrl', '/src');
|
|
76
|
+
env.addGlobal('branding', brandingConfig);
|
|
77
|
+
|
|
78
|
+
env.addGlobal('loadComponentParams', function (componentName) {
|
|
79
|
+
try {
|
|
80
|
+
const yamlPath = path.join(process.cwd(), 'src/templates/components', componentName, `params.${componentName}.yaml`);
|
|
81
|
+
|
|
82
|
+
if (fs.existsSync(yamlPath)) {
|
|
83
|
+
const content = fs.readFileSync(yamlPath, 'utf8');
|
|
84
|
+
const parsedYaml = yaml.load(content);
|
|
85
|
+
const dumpedYaml = yaml.dump(parsedYaml);
|
|
86
|
+
return dumpedYaml;
|
|
87
|
+
}
|
|
88
|
+
console.error(`YAML file not found: ${yamlPath}`);
|
|
89
|
+
return '';
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(`Error loading YAML for ${componentName}:`, err);
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Four globals for code blocks
|
|
97
|
+
env.addGlobal('getHTMLCodeFromExample', function (componentName, params, pathName) {
|
|
98
|
+
const templatePath = `${pathName}s/${componentName}/_template.${componentName}.njk`;
|
|
99
|
+
const html = env.render(templatePath, { params }).trim();
|
|
100
|
+
return beautify.html(html, getBeautifyOptions());
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
env.addGlobal('getHTMLCodeFromFile', function (templatePath) {
|
|
104
|
+
const html = env.render(templatePath).trim();
|
|
105
|
+
return beautify.html(html, getBeautifyOptions());
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
env.addGlobal('getNunjucksCodeFromExample', function (componentName, params, pathName) {
|
|
109
|
+
const macroName = kebabCaseToPascalCase(componentName);
|
|
110
|
+
return outdent`
|
|
111
|
+
{% from "${pathName}s/${componentName}/_macro.${componentName}.njk" import ${pathName}${macroName} %}
|
|
112
|
+
{{ ${pathName}${macroName}(${JSON.stringify(params, undefined, 2)
|
|
113
|
+
}) }}
|
|
114
|
+
`;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
env.addGlobal('getNunjucksCodeFromFile', function (templatePath) {
|
|
118
|
+
try {
|
|
119
|
+
const codeFromFile = fs.readFileSync(
|
|
120
|
+
path.join(process.cwd(), 'src/templates', templatePath),
|
|
121
|
+
'utf8'
|
|
122
|
+
);
|
|
123
|
+
return outdent`
|
|
124
|
+
${codeFromFile}
|
|
125
|
+
`;
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error(`Error reading file ${templatePath}:`, err);
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Filter configuration
|
|
133
|
+
env.addFilter('filtercaller', function (params) {
|
|
134
|
+
if (params && typeof params === 'object') {
|
|
135
|
+
const filteredParams = { ...params };
|
|
136
|
+
delete filteredParams.caller;
|
|
137
|
+
return filteredParams;
|
|
138
|
+
}
|
|
139
|
+
return params;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
env.addFilter('filterslugify', function (str) {
|
|
143
|
+
if (typeof str !== 'string') {
|
|
144
|
+
return '';
|
|
145
|
+
}
|
|
146
|
+
let slug = str.replace(/\s+/g, '-');
|
|
147
|
+
slug = slug.replace(/[^\w-]+/g, '');
|
|
148
|
+
slug = slug.toLowerCase();
|
|
149
|
+
return slug;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
env.addFilter('highlight', function (code, language) {
|
|
153
|
+
try {
|
|
154
|
+
return hljs.highlight(code.trim(), {
|
|
155
|
+
language: language || 'plaintext'
|
|
156
|
+
}).value;
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error('Error highlighting code:', err);
|
|
159
|
+
return code;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
env.addFilter('filterescapeltgt', function (str) {
|
|
164
|
+
if (typeof str !== 'string' && typeof str !== 'object') return str;
|
|
165
|
+
if (typeof str !== 'string') str = JSON.stringify(str);
|
|
166
|
+
return str.replace(/</g, '<').replace(/>/g, '>');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
env.addFilter('filterquotes', function (str) {
|
|
170
|
+
if (typeof str !== 'string') return str;
|
|
171
|
+
let inTag = false;
|
|
172
|
+
let result = '';
|
|
173
|
+
let currentQuote = null;
|
|
174
|
+
for (let i = 0; i < str.length; i++) {
|
|
175
|
+
const char = str[i];
|
|
176
|
+
if (char === '<') {
|
|
177
|
+
inTag = true;
|
|
178
|
+
result += char;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (char === '>') {
|
|
182
|
+
inTag = false;
|
|
183
|
+
result += char;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (inTag) {
|
|
187
|
+
if (char === "'" || char === '"') {
|
|
188
|
+
if (currentQuote === null) {
|
|
189
|
+
currentQuote = char;
|
|
190
|
+
result += '"';
|
|
191
|
+
} else if (currentQuote === char) {
|
|
192
|
+
currentQuote = null;
|
|
193
|
+
result += '"';
|
|
194
|
+
} else {
|
|
195
|
+
result += char;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
result += char;
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
result += char;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return result;
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
env.addFilter('filterversion', function () {
|
|
208
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
|
|
209
|
+
return pkg.version;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return env;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Improved function to process Nunjucks files during build
|
|
216
|
+
async function processNunjucksFiles(env) {
|
|
217
|
+
try {
|
|
218
|
+
const docsPath = path.resolve(process.cwd(), 'docs');
|
|
219
|
+
const htmlFiles = await glob('*.html', { cwd: docsPath });
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
for (const htmlFile of htmlFiles) {
|
|
223
|
+
try {
|
|
224
|
+
const sourcePath = path.join(docsPath, htmlFile);
|
|
225
|
+
const outputPath = path.join(process.cwd(), 'dist', htmlFile);
|
|
226
|
+
|
|
227
|
+
const content = await fs.promises.readFile(sourcePath, 'utf8');
|
|
228
|
+
let processedHtml = env.renderString(content);
|
|
229
|
+
|
|
230
|
+
// Transformations for relative paths
|
|
231
|
+
processedHtml = processedHtml.replace(/href=["']\/src\/css\/([^"']*)["']/g, 'href="css/$1"');
|
|
232
|
+
processedHtml = processedHtml.replace(/href=["']src\/css\/([^"']*)["']/g, 'href="css/$1"');
|
|
233
|
+
|
|
234
|
+
// Transform JS paths (ensure they are relative without leading slash)
|
|
235
|
+
processedHtml = processedHtml.replace(/src=["']\/src\/js\/([^"']*)["']/g, 'src="js/$1"');
|
|
236
|
+
processedHtml = processedHtml.replace(/src=["']src\/js\/([^"']*)["']/g, 'src="js/$1"');
|
|
237
|
+
|
|
238
|
+
// Also process if any route with /js/ (absolute) remains
|
|
239
|
+
processedHtml = processedHtml.replace(/src=["']\/js\/([^"']*)["']/g, 'src="js/$1"');
|
|
240
|
+
|
|
241
|
+
// Transform image paths
|
|
242
|
+
processedHtml = processedHtml.replace(/src=["']\/src\/images\/([^"']*)["']/g, 'src="images/$1"');
|
|
243
|
+
processedHtml = processedHtml.replace(/src=["']src\/images\/([^"']*)["']/g, 'src="images/$1"');
|
|
244
|
+
|
|
245
|
+
await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });
|
|
246
|
+
await fs.promises.writeFile(outputPath, processedHtml);
|
|
247
|
+
} catch (fileError) {
|
|
248
|
+
console.error(`Error processing file ${htmlFile}:`, fileError);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error('Error in processNunjucksFiles:', error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// NEW FUNCTION: Process JavaScript files to remove CSS imports and fix modules
|
|
257
|
+
async function processJavaScriptFiles() {
|
|
258
|
+
try {
|
|
259
|
+
const srcJsDir = path.resolve(process.cwd(), 'src/js');
|
|
260
|
+
const distJsDir = path.resolve(process.cwd(), 'dist/js');
|
|
261
|
+
|
|
262
|
+
// Create dist/js directory if it doesn't exist
|
|
263
|
+
await fs.promises.mkdir(distJsDir, { recursive: true });
|
|
264
|
+
|
|
265
|
+
// 1. Copy npm packages to the vendor folder
|
|
266
|
+
const vendorDir = path.join(distJsDir, 'vendor');
|
|
267
|
+
await fs.promises.mkdir(vendorDir, { recursive: true });
|
|
268
|
+
|
|
269
|
+
// 2. Process all JS files
|
|
270
|
+
const jsFiles = await glob('**/*.js', { cwd: srcJsDir });
|
|
271
|
+
|
|
272
|
+
for (const jsFile of jsFiles) {
|
|
273
|
+
const sourcePath = path.join(srcJsDir, jsFile);
|
|
274
|
+
const destPath = path.join(distJsDir, jsFile);
|
|
275
|
+
|
|
276
|
+
// Create directory if necessary
|
|
277
|
+
await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
|
|
278
|
+
|
|
279
|
+
// Read file content
|
|
280
|
+
const content = await fs.promises.readFile(sourcePath, 'utf8');
|
|
281
|
+
|
|
282
|
+
// Transform the content
|
|
283
|
+
let processedContent = content;
|
|
284
|
+
|
|
285
|
+
// a) Remove CSS file imports
|
|
286
|
+
processedContent = processedContent.replace(
|
|
287
|
+
/import\s+['"](.+\.css)['"];?/g,
|
|
288
|
+
'// CSS import removed: $1'
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// c) Fix other relative import paths between JS files
|
|
292
|
+
processedContent = processedContent.replace(
|
|
293
|
+
/from\s+["']\/src\/js\/([^"']*)["']/g,
|
|
294
|
+
(match, importPath) => {
|
|
295
|
+
const depth = jsFile.split('/').length - 1;
|
|
296
|
+
const prefix = depth > 0 ? '../'.repeat(depth) : './';
|
|
297
|
+
return `from "${prefix}${importPath}"`;
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
processedContent = processedContent.replace(
|
|
302
|
+
/from\s+["']src\/js\/([^"']*)["']/g,
|
|
303
|
+
(match, importPath) => {
|
|
304
|
+
const depth = jsFile.split('/').length - 1;
|
|
305
|
+
const prefix = depth > 0 ? '../'.repeat(depth) : './';
|
|
306
|
+
return `from "${prefix}${importPath}"`;
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
// Write the processed file
|
|
311
|
+
await fs.promises.writeFile(destPath, processedContent);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error('Error processing JavaScript:', error);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Optimize image with Sharp
|
|
320
|
+
async function optimizeImage(sourcePath, destPath, fileExtension) {
|
|
321
|
+
try {
|
|
322
|
+
const image = sharp(sourcePath);
|
|
323
|
+
|
|
324
|
+
switch (fileExtension.toLowerCase()) {
|
|
325
|
+
case '.jpg':
|
|
326
|
+
case '.jpeg':
|
|
327
|
+
await image
|
|
328
|
+
.jpeg({
|
|
329
|
+
quality: 80,
|
|
330
|
+
mozjpeg: true,
|
|
331
|
+
progressive: true
|
|
332
|
+
})
|
|
333
|
+
.toFile(destPath);
|
|
334
|
+
break;
|
|
335
|
+
|
|
336
|
+
case '.png':
|
|
337
|
+
await image
|
|
338
|
+
.png({
|
|
339
|
+
quality: 80,
|
|
340
|
+
palette: true,
|
|
341
|
+
compressionLevel: 9
|
|
342
|
+
})
|
|
343
|
+
.toFile(destPath);
|
|
344
|
+
break;
|
|
345
|
+
|
|
346
|
+
case '.webp':
|
|
347
|
+
await image
|
|
348
|
+
.webp({
|
|
349
|
+
quality: 80,
|
|
350
|
+
effort: 6
|
|
351
|
+
})
|
|
352
|
+
.toFile(destPath);
|
|
353
|
+
break;
|
|
354
|
+
|
|
355
|
+
case '.avif':
|
|
356
|
+
await image
|
|
357
|
+
.avif({
|
|
358
|
+
quality: 80,
|
|
359
|
+
effort: 4
|
|
360
|
+
})
|
|
361
|
+
.toFile(destPath);
|
|
362
|
+
break;
|
|
363
|
+
|
|
364
|
+
case '.svg':
|
|
365
|
+
// SVGs are not processed with Sharp, they are copied directly
|
|
366
|
+
await fs.promises.copyFile(sourcePath, destPath);
|
|
367
|
+
break;
|
|
368
|
+
|
|
369
|
+
default:
|
|
370
|
+
// For other formats, copy without optimizing
|
|
371
|
+
await fs.promises.copyFile(sourcePath, destPath);
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return true;
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error(`❌ Error optimizing ${sourcePath}:`, error.message);
|
|
378
|
+
// If optimization fails, try direct copy as fallback
|
|
379
|
+
try {
|
|
380
|
+
await fs.promises.copyFile(sourcePath, destPath);
|
|
381
|
+
return true;
|
|
382
|
+
} catch (copyError) {
|
|
383
|
+
console.error(`❌ Error copying ${sourcePath}:`, copyError.message);
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// UPDATED FUNCTION: Copy and optimize all images
|
|
390
|
+
async function copyAndOptimizeImages() {
|
|
391
|
+
try {
|
|
392
|
+
const srcImagesDir = path.resolve(process.cwd(), 'public/images');
|
|
393
|
+
const distImagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
394
|
+
|
|
395
|
+
// Check if public/images folder exists
|
|
396
|
+
if (!fs.existsSync(srcImagesDir)) {
|
|
397
|
+
console.log('📁 public/images does not exist, skipping image optimization');
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Create dist/images directory if it doesn't exist
|
|
402
|
+
await fs.promises.mkdir(distImagesDir, { recursive: true });
|
|
403
|
+
|
|
404
|
+
// Get all image files recursively
|
|
405
|
+
const imageFiles = await glob('**/*.{png,jpg,jpeg,gif,svg,webp,avif,ico,bmp,tiff}', {
|
|
406
|
+
cwd: srcImagesDir,
|
|
407
|
+
nodir: true
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
console.log(`🖼️ Optimizing ${imageFiles.length} images with Sharp...`);
|
|
411
|
+
|
|
412
|
+
let optimizedCount = 0;
|
|
413
|
+
let copiedCount = 0;
|
|
414
|
+
let totalSizeBefore = 0;
|
|
415
|
+
let totalSizeAfter = 0;
|
|
416
|
+
|
|
417
|
+
for (const imageFile of imageFiles) {
|
|
418
|
+
const sourcePath = path.join(srcImagesDir, imageFile);
|
|
419
|
+
const destPath = path.join(distImagesDir, imageFile);
|
|
420
|
+
const fileExtension = path.extname(imageFile);
|
|
421
|
+
|
|
422
|
+
// Create directory if necessary (for subdirectories)
|
|
423
|
+
await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
|
|
424
|
+
|
|
425
|
+
// Get original file size
|
|
426
|
+
const statsBefore = await fs.promises.stat(sourcePath);
|
|
427
|
+
totalSizeBefore += statsBefore.size;
|
|
428
|
+
|
|
429
|
+
// Optimize or copy image
|
|
430
|
+
const success = await optimizeImage(sourcePath, destPath, fileExtension);
|
|
431
|
+
|
|
432
|
+
if (success) {
|
|
433
|
+
// Get file size after optimization
|
|
434
|
+
if (fs.existsSync(destPath)) {
|
|
435
|
+
const statsAfter = await fs.promises.stat(destPath);
|
|
436
|
+
totalSizeAfter += statsAfter.size;
|
|
437
|
+
|
|
438
|
+
const reduction = ((statsBefore.size - statsAfter.size) / statsBefore.size * 100).toFixed(1);
|
|
439
|
+
|
|
440
|
+
if (['.jpg', '.jpeg', '.png', '.webp', '.avif'].includes(fileExtension.toLowerCase())) {
|
|
441
|
+
console.log(`✨ Optimized: ${imageFile} (${reduction}% reduced)`);
|
|
442
|
+
optimizedCount++;
|
|
443
|
+
} else {
|
|
444
|
+
console.log(`📄 Copied: ${imageFile}`);
|
|
445
|
+
copiedCount++;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const totalReduction = ((totalSizeBefore - totalSizeAfter) / totalSizeBefore * 100).toFixed(1);
|
|
452
|
+
const sizeBefore = (totalSizeBefore / 1024).toFixed(1);
|
|
453
|
+
const sizeAfter = (totalSizeAfter / 1024).toFixed(1);
|
|
454
|
+
|
|
455
|
+
console.log(`🎉 Processing completed:`);
|
|
456
|
+
console.log(` ✨ ${optimizedCount} images optimized`);
|
|
457
|
+
console.log(` 📄 ${copiedCount} files copied`);
|
|
458
|
+
console.log(` 📊 Size: ${sizeBefore}KB → ${sizeAfter}KB (${totalReduction}% reduced)`);
|
|
459
|
+
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error('❌ Error in copyAndOptimizeImages:', error);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Copy branding assets to dist/images
|
|
466
|
+
async function copyBrandingAssets() {
|
|
467
|
+
try {
|
|
468
|
+
const brandingDir = path.resolve(process.cwd(), 'branding');
|
|
469
|
+
const distImagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
470
|
+
|
|
471
|
+
if (!fs.existsSync(brandingDir)) {
|
|
472
|
+
console.log('📁 branding/ does not exist, skipping branding assets');
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
await fs.promises.mkdir(distImagesDir, { recursive: true });
|
|
477
|
+
|
|
478
|
+
// Copy logos (flattened to images/)
|
|
479
|
+
const logosDir = path.join(brandingDir, 'logos');
|
|
480
|
+
if (fs.existsSync(logosDir)) {
|
|
481
|
+
const logoFiles = await glob('**/*.svg', { cwd: logosDir, nodir: true });
|
|
482
|
+
for (const file of logoFiles) {
|
|
483
|
+
const sourcePath = path.join(logosDir, file);
|
|
484
|
+
const fileName = path.basename(file); // Get just the filename to flatten structure
|
|
485
|
+
const destPath = path.join(distImagesDir, fileName);
|
|
486
|
+
await fs.promises.copyFile(sourcePath, destPath);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Copy header background images
|
|
491
|
+
const imagesDir = path.join(brandingDir, 'images');
|
|
492
|
+
if (fs.existsSync(imagesDir)) {
|
|
493
|
+
const imageFiles = await glob('*.svg', { cwd: imagesDir, nodir: true });
|
|
494
|
+
for (const file of imageFiles) {
|
|
495
|
+
const sourcePath = path.join(imagesDir, file);
|
|
496
|
+
const destPath = path.join(distImagesDir, file);
|
|
497
|
+
await fs.promises.copyFile(sourcePath, destPath);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
console.log('✅ Branding assets copied to dist/images/');
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.error('❌ Error in copyBrandingAssets:', error);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Function to copy assets (updated to use optimization)
|
|
508
|
+
async function copyAssets() {
|
|
509
|
+
try {
|
|
510
|
+
// Copy and optimize images
|
|
511
|
+
await copyAndOptimizeImages();
|
|
512
|
+
|
|
513
|
+
// Copy branding assets
|
|
514
|
+
await copyBrandingAssets();
|
|
515
|
+
|
|
516
|
+
// Copy license if it exists
|
|
517
|
+
const licenseFile = path.resolve(process.cwd(), 'EUPL-1.2.txt');
|
|
518
|
+
if (fs.existsSync(licenseFile)) {
|
|
519
|
+
await fs.promises.copyFile(licenseFile, path.join(process.cwd(), 'dist', 'EUPL-1.2.txt'));
|
|
520
|
+
}
|
|
521
|
+
} catch (error) {
|
|
522
|
+
console.error('Error copying assets:', error);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Final verification to ensure there are no common errors
|
|
527
|
+
async function validateBuild() {
|
|
528
|
+
try {
|
|
529
|
+
const distPath = path.resolve(process.cwd(), 'dist');
|
|
530
|
+
|
|
531
|
+
// 1. Check that there are no src/ references in HTML files
|
|
532
|
+
const htmlFiles = await glob('**/*.html', { cwd: distPath });
|
|
533
|
+
|
|
534
|
+
for (const htmlFile of htmlFiles) {
|
|
535
|
+
const filePath = path.join(distPath, htmlFile);
|
|
536
|
+
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
537
|
+
|
|
538
|
+
// Search for /src/ or src/ references
|
|
539
|
+
if (content.includes('/src/') || content.includes('src/')) {
|
|
540
|
+
|
|
541
|
+
// Auto-correct
|
|
542
|
+
let correctedContent = content
|
|
543
|
+
.replace(/href=["']\/src\/([^"']*)["']/g, 'href="$1"')
|
|
544
|
+
.replace(/href=["']src\/([^"']*)["']/g, 'href="$1"')
|
|
545
|
+
.replace(/src=["']\/src\/([^"']*)["']/g, 'src="$1"')
|
|
546
|
+
.replace(/src=["']src\/([^"']*)["']/g, 'src="$1"');
|
|
547
|
+
|
|
548
|
+
await fs.promises.writeFile(filePath, correctedContent);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// 2. Check that there are no absolute references in scripts
|
|
553
|
+
for (const htmlFile of htmlFiles) {
|
|
554
|
+
const filePath = path.join(distPath, htmlFile);
|
|
555
|
+
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
556
|
+
|
|
557
|
+
// Search for script tags with src that starts with "/"
|
|
558
|
+
const hasAbsoluteScripts = /<script[^>]*src=["']\/[^"']*["'][^>]*>/g.test(content);
|
|
559
|
+
|
|
560
|
+
if (hasAbsoluteScripts) {
|
|
561
|
+
|
|
562
|
+
// Auto-correct
|
|
563
|
+
let correctedContent = content.replace(
|
|
564
|
+
/<script([^>]*)src=["']\/([^"']*)["']([^>]*)>/g,
|
|
565
|
+
'<script$1src="$2"$3>'
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
await fs.promises.writeFile(filePath, correctedContent);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// 3. Check JS files
|
|
573
|
+
const jsFiles = await glob('js/**/*.js', { cwd: distPath });
|
|
574
|
+
|
|
575
|
+
for (const jsFile of jsFiles) {
|
|
576
|
+
const filePath = path.join(distPath, jsFile);
|
|
577
|
+
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
578
|
+
|
|
579
|
+
// Search for CSS imports
|
|
580
|
+
if (content.includes('.css"') || content.includes(".css'")) {
|
|
581
|
+
|
|
582
|
+
// Comment out these imports
|
|
583
|
+
let correctedContent = content.replace(
|
|
584
|
+
/import\s+['"](.+\.css)['"];?/g,
|
|
585
|
+
'// CSS import removed: $1'
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
await fs.promises.writeFile(filePath, correctedContent);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
} catch (error) {
|
|
593
|
+
console.error('Error validating build:', error);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Correct CSS image paths for production
|
|
598
|
+
async function rewriteCssImagePaths() {
|
|
599
|
+
const stylePath = path.resolve(process.cwd(), 'dist/css/styles.css');
|
|
600
|
+
if (!fs.existsSync(stylePath)) return;
|
|
601
|
+
let content = await fs.promises.readFile(stylePath, 'utf8');
|
|
602
|
+
content = content.replace(/url\((['"]?)\/images\//g, 'url($1../images/');
|
|
603
|
+
await fs.promises.writeFile(stylePath, content);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Correct HTML image paths for production (from /images/, /branding/logos/, /branding/images/ to ../images/)
|
|
607
|
+
async function rewriteHtmlImagePaths() {
|
|
608
|
+
const distDir = path.resolve(process.cwd(), 'dist');
|
|
609
|
+
if (!fs.existsSync(distDir)) return;
|
|
610
|
+
|
|
611
|
+
const htmlFiles = await glob('**/*.html', { cwd: distDir, nodir: true });
|
|
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
|
+
|
|
626
|
+
for (const htmlFile of htmlFiles) {
|
|
627
|
+
const filePath = path.join(distDir, htmlFile);
|
|
628
|
+
let content = await fs.promises.readFile(filePath, 'utf8');
|
|
629
|
+
|
|
630
|
+
// Aplicar todas las reglas
|
|
631
|
+
let modified = content;
|
|
632
|
+
for (const rule of rewriteRules) {
|
|
633
|
+
modified = modified.replace(rule.pattern, rule.replacement);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (modified !== content) {
|
|
637
|
+
await fs.promises.writeFile(filePath, modified);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
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;
|
|
647
|
+
|
|
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
|
+
];
|
|
656
|
+
|
|
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
|
+
}
|
|
674
|
+
|
|
675
|
+
if (modified !== content) {
|
|
676
|
+
await fs.promises.writeFile(filePath, modified);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (htmlFiles.length > 0) {
|
|
681
|
+
console.log(`✓ Rewrote favicon paths in ${htmlFiles.length} HTML file(s)`);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Custom Nunjucks plugin
|
|
686
|
+
function customNunjucksPlugin() {
|
|
687
|
+
let env;
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
name: 'vite-plugin-custom-nunjucks',
|
|
691
|
+
|
|
692
|
+
configureServer({ middlewares, watcher, ws }) {
|
|
693
|
+
|
|
694
|
+
// Configure Nunjucks with development-specific options
|
|
695
|
+
env = setupNunjucksEnvironment(['docs', 'src/templates'], {
|
|
696
|
+
watch: true,
|
|
697
|
+
throwOnUndefined: false
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// Watch for CSS and NJK files
|
|
701
|
+
watcher.add(['src/**/*.css', 'docs/**/*.njk', 'src/**/*.njk']);
|
|
702
|
+
watcher.on('change', (path) => {
|
|
703
|
+
ws.send({
|
|
704
|
+
type: 'full-reload'
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
// Middleware to serve branding assets in development
|
|
709
|
+
middlewares.use(async (req, res, next) => {
|
|
710
|
+
if (req.url.startsWith('/branding/')) {
|
|
711
|
+
const filePath = path.join(process.cwd(), req.url);
|
|
712
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
713
|
+
}
|
|
714
|
+
next();
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// Helper function to find file recursively in directories
|
|
718
|
+
function findFileRecursive(dir, fileName) {
|
|
719
|
+
if (!fs.existsSync(dir)) return null;
|
|
720
|
+
const files = fs.readdirSync(dir);
|
|
721
|
+
for (const file of files) {
|
|
722
|
+
const fullPath = path.join(dir, file);
|
|
723
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
724
|
+
const found = findFileRecursive(fullPath, fileName);
|
|
725
|
+
if (found) return found;
|
|
726
|
+
} else if (file === fileName) {
|
|
727
|
+
return fullPath;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Middleware to serve branding assets from /images/ in development
|
|
734
|
+
middlewares.use(async (req, res, next) => {
|
|
735
|
+
if (req.url.startsWith('/images/')) {
|
|
736
|
+
let filePath = path.join(process.cwd(), 'public', req.url);
|
|
737
|
+
|
|
738
|
+
if (!fs.existsSync(filePath)) {
|
|
739
|
+
const fileName = path.basename(req.url);
|
|
740
|
+
|
|
741
|
+
// Search in branding/logos (including subdirectories like eu/)
|
|
742
|
+
const brandingLogosPath = path.join(process.cwd(), 'branding/logos');
|
|
743
|
+
const foundInLogos = findFileRecursive(brandingLogosPath, fileName);
|
|
744
|
+
|
|
745
|
+
if (foundInLogos) {
|
|
746
|
+
filePath = foundInLogos;
|
|
747
|
+
} else {
|
|
748
|
+
// Search in branding/images
|
|
749
|
+
const brandingImagesPath = path.join(process.cwd(), 'branding/images');
|
|
750
|
+
const foundInImages = findFileRecursive(brandingImagesPath, fileName);
|
|
751
|
+
if (foundInImages) {
|
|
752
|
+
filePath = foundInImages;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (fs.existsSync(filePath)) {
|
|
758
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
next();
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
// Middleware to serve branding assets from /branding/logos/ in development
|
|
765
|
+
middlewares.use(async (req, res, next) => {
|
|
766
|
+
if (req.url.startsWith('/branding/logos/')) {
|
|
767
|
+
const fileName = path.basename(req.url);
|
|
768
|
+
const brandingLogosPath = path.join(process.cwd(), 'branding/logos');
|
|
769
|
+
const filePath = findFileRecursive(brandingLogosPath, fileName);
|
|
770
|
+
|
|
771
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
772
|
+
}
|
|
773
|
+
next();
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// Middleware to serve branding assets from /branding/images/ in development
|
|
777
|
+
middlewares.use(async (req, res, next) => {
|
|
778
|
+
if (req.url.startsWith('/branding/images/')) {
|
|
779
|
+
const fileName = path.basename(req.url);
|
|
780
|
+
const brandingImagesPath = path.join(process.cwd(), 'branding/images');
|
|
781
|
+
const filePath = findFileRecursive(brandingImagesPath, fileName);
|
|
782
|
+
|
|
783
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
784
|
+
}
|
|
785
|
+
next();
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// Middleware to transform paths in development (keep as is)
|
|
789
|
+
middlewares.use(async (req, res, next) => {
|
|
790
|
+
// Intercept requests to /css/ and redirect to /src/css/
|
|
791
|
+
if (req.url.startsWith('/css/')) {
|
|
792
|
+
req.url = req.url.replace('/css/', '/src/css/');
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Intercept requests to /js/ and redirect to /src/js/
|
|
796
|
+
if (req.url.startsWith('/js/')) {
|
|
797
|
+
req.url = req.url.replace('/js/', '/src/js/');
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Set correct MIME type for JS
|
|
801
|
+
if (req.url.endsWith('.js')) {
|
|
802
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
next();
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// Middleware for CSS
|
|
809
|
+
middlewares.use(async (req, res, next) => {
|
|
810
|
+
if (req.url.startsWith('/src/css/')) {
|
|
811
|
+
return next();
|
|
812
|
+
}
|
|
813
|
+
next();
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// Middleware for .html
|
|
817
|
+
middlewares.use(async (req, res, next) => {
|
|
818
|
+
if (req.url === '/') {
|
|
819
|
+
req.url = '/index.html';
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (req.url.endsWith('.html')) {
|
|
823
|
+
try {
|
|
824
|
+
const templatePath = req.url.slice(1);
|
|
825
|
+
const html = env.render(templatePath);
|
|
826
|
+
res.setHeader('Content-Type', 'text/html');
|
|
827
|
+
return res.end(html);
|
|
828
|
+
} catch (e) {
|
|
829
|
+
console.error('Error rendering template:', e.message);
|
|
830
|
+
return next(e);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
next();
|
|
834
|
+
});
|
|
835
|
+
},
|
|
836
|
+
|
|
837
|
+
configurePreviewServer(server) {
|
|
838
|
+
const { middlewares } = server;
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
// Configure Nunjucks with preview-specific options
|
|
842
|
+
env = setupNunjucksEnvironment(['docs', 'src/templates'], {
|
|
843
|
+
watch: false,
|
|
844
|
+
throwOnUndefined: false
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
// Configure the middlewares
|
|
848
|
+
middlewares.use(async (req, res, next) => {
|
|
849
|
+
if (req.url === '/') {
|
|
850
|
+
req.url = '/index.html';
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (req.url.endsWith('.html')) {
|
|
854
|
+
try {
|
|
855
|
+
const templatePath = req.url.slice(1);
|
|
856
|
+
const html = env.render(templatePath);
|
|
857
|
+
res.setHeader('Content-Type', 'text/html');
|
|
858
|
+
return res.end(html);
|
|
859
|
+
} catch (e) {
|
|
860
|
+
console.error('Error in preview:', e.message);
|
|
861
|
+
return next(e);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
next();
|
|
865
|
+
});
|
|
866
|
+
},
|
|
867
|
+
|
|
868
|
+
transform(content, id) {
|
|
869
|
+
if (id.endsWith('.njk')) {
|
|
870
|
+
const html = env.renderString(content);
|
|
871
|
+
return {
|
|
872
|
+
code: `export default ${JSON.stringify(html)}`,
|
|
873
|
+
map: null
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
|
|
878
|
+
async buildStart() {
|
|
879
|
+
// Configure Nunjucks for the build
|
|
880
|
+
env = setupNunjucksEnvironment(['docs', 'docs/ds', 'src/templates'], {
|
|
881
|
+
watch: false,
|
|
882
|
+
throwOnUndefined: false
|
|
883
|
+
});
|
|
884
|
+
},
|
|
885
|
+
|
|
886
|
+
async closeBundle() {
|
|
887
|
+
console.log('🚀 Starting custom build process...');
|
|
888
|
+
|
|
889
|
+
// Step 1: Copy and optimize assets (including images) first
|
|
890
|
+
await copyAssets();
|
|
891
|
+
|
|
892
|
+
// Step 2: Correct CSS image paths for production
|
|
893
|
+
await rewriteCssImagePaths();
|
|
894
|
+
|
|
895
|
+
// Step 3: Skip processing JavaScript files - let Vite handle bundling
|
|
896
|
+
// (Vite will resolve npm dependencies like @floating-ui/dom correctly)
|
|
897
|
+
// await processJavaScriptFiles();
|
|
898
|
+
|
|
899
|
+
// Step 4: Process Nunjucks files (images are already in dist)
|
|
900
|
+
await processNunjucksFiles(env);
|
|
901
|
+
|
|
902
|
+
// Step 4b: Correct HTML image paths for production (after HTML files are generated)
|
|
903
|
+
await rewriteHtmlImagePaths();
|
|
904
|
+
|
|
905
|
+
// Step 4c: Rewrite favicon paths from /branding/logos/ to ./images/
|
|
906
|
+
await rewriteFaviconPaths();
|
|
907
|
+
|
|
908
|
+
// Step 5: Final validation to find and fix common errors
|
|
909
|
+
await validateBuild();
|
|
910
|
+
|
|
911
|
+
console.log('✅ Custom build completed');
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// Main Vite configuration
|
|
917
|
+
export default defineConfig({
|
|
918
|
+
plugins: [
|
|
919
|
+
brandingPlugin(brandingConfig),
|
|
920
|
+
customNunjucksPlugin(),
|
|
921
|
+
tailwindcss()
|
|
922
|
+
],
|
|
923
|
+
build: {
|
|
924
|
+
outDir: 'dist',
|
|
925
|
+
minify: true,
|
|
926
|
+
emptyOutDir: true,
|
|
927
|
+
cssCodeSplit: false,
|
|
928
|
+
manifest: true,
|
|
929
|
+
rollupOptions: {
|
|
930
|
+
input: {
|
|
931
|
+
index: path.resolve(__dirname, 'src/js/index.js')
|
|
932
|
+
},
|
|
933
|
+
output: {
|
|
934
|
+
entryFileNames: 'js/[name].js',
|
|
935
|
+
chunkFileNames: 'js/[name]-[hash].js',
|
|
936
|
+
assetFileNames: (assetInfo) => {
|
|
937
|
+
// Rename style.css to styles.css
|
|
938
|
+
if (assetInfo.name === 'style.css') {
|
|
939
|
+
return 'css/styles.css';
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Handle other asset types
|
|
943
|
+
const info = assetInfo.name.split('.');
|
|
944
|
+
const extType = info[info.length - 1];
|
|
945
|
+
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) {
|
|
946
|
+
return 'images/[name][extname]';
|
|
947
|
+
}
|
|
948
|
+
if (/css/i.test(extType)) {
|
|
949
|
+
return 'css/[name][extname]';
|
|
950
|
+
}
|
|
951
|
+
return '[name][extname]';
|
|
952
|
+
},
|
|
953
|
+
format: 'es',
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
},
|
|
957
|
+
server: {
|
|
958
|
+
open: '/',
|
|
959
|
+
watch: {
|
|
960
|
+
ignored: ['!**/docs/**', '!**/src/templates/**']
|
|
961
|
+
},
|
|
962
|
+
host: '0.0.0.0',
|
|
963
|
+
allowedHosts: [
|
|
964
|
+
'82c7d68c-1131-4db5-bf36-165d8f8b1d98-00-2b55tnaqky432.worf.replit.dev',
|
|
965
|
+
'localhost'
|
|
966
|
+
]
|
|
967
|
+
},
|
|
968
|
+
resolve: {
|
|
969
|
+
alias: {
|
|
970
|
+
'@': path.resolve(__dirname, 'src'),
|
|
971
|
+
'/src': path.resolve(__dirname, 'src'),
|
|
972
|
+
'@css': path.resolve(__dirname, 'src/css'),
|
|
973
|
+
'@components': path.resolve(__dirname, 'src/templates/components'),
|
|
974
|
+
'@ds': path.resolve(__dirname, 'docs/ds'),
|
|
975
|
+
'@images': path.resolve(__dirname, 'public/images')
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
publicDir: 'public',
|
|
979
|
+
optimizeDeps: {
|
|
980
|
+
include: ['tailwindcss']
|
|
981
|
+
}
|
|
982
|
+
});
|