@massimo-cassandro/minimo 1.16.0 → 1.16.1
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/README.md +3 -3
- package/design-tokens/_src/typography.minimo.tokens.mjs +1 -1
- package/dev-tools/README.md +11 -7
- package/dev-tools/starter-kit/TODO.md +135 -0
- package/dev-tools/{webpack-setup/merge-jsonc.cjs → starter-kit/src/merge-jsonc.mjs} +40 -15
- package/dev-tools/{webpack-setup → starter-kit}/starter-install.sh +22 -3
- package/dev-tools/starter-kit/templates/_root_htaccess +68 -0
- package/dev-tools/starter-kit/templates/_root_robots.txt +5 -0
- package/dev-tools/starter-kit/templates/entry-tpl.critical.css +18 -0
- package/dev-tools/starter-kit/templates/entry-tpl.css +24 -0
- package/dev-tools/starter-kit/templates/tokens-config.mjs +33 -0
- package/dev-tools/{webpack-setup → starter-kit}/webpack-modules/css-rules.mjs +6 -1
- package/dev-tools/{webpack-setup → starter-kit}/webpack-modules/postcss.config.mjs +14 -34
- package/dev-tools/starter-kit/webpack-modules/purgecss-variables-safelist.mjs +92 -0
- package/dev-tools/{webpack-setup → starter-kit}/webpack.config.mjs +169 -37
- package/package.json +3 -2
- /package/dev-tools/{webpack-setup → starter-kit}/templates/__project__.code-workspace +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/_browserslistrc +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/_editorconfig +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/_gitignore +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/_prettierrc +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/eslint.config.mjs +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/jsconfig.json +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/package-tpl.json +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/templates/stylelint.config.mjs +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/webpack-modules/get-jsConfig-aliases.mjs +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/webpack-modules/mini-svg-data-uri-loader.cjs +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/webpack-modules/svg-rules.mjs +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/webpack-modules/svgo.config.mjs +0 -0
- /package/dev-tools/{webpack-setup → starter-kit}/webpack-template.ejs +0 -0
package/README.md
CHANGED
|
@@ -12,13 +12,13 @@ npm i -D @massimo-cassandro/eslint-config
|
|
|
12
12
|
npm i -D @massimo-cassandro/stylelint-config
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
> Note: **minimo** is designed to be used with WebPack using the configuration you can find in the [
|
|
15
|
+
> Note: **minimo** is designed to be used with WebPack using the configuration you can find in the [starter-kit folder](./dev-tools/starter-kit/) of this repository. If changes are made (for example, to how SVG files are imported), things may not work as expected.
|
|
16
16
|
>
|
|
17
17
|
|
|
18
|
-
For a complete install of all required webpack modules, use the install script at `./
|
|
18
|
+
For a complete install of all required webpack modules, use the install script at `./dev-tools/starter-kit/starter-install.sh` launching:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
zsh ./node_modules/@massimo-cassandro/minimo/
|
|
21
|
+
zsh ./node_modules/@massimo-cassandro/minimo/dev-tools/starter-kit/starter-install.sh
|
|
22
22
|
```
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
$type: 'fontFamily'
|
|
6
6
|
},
|
|
7
7
|
'family-mono': {
|
|
8
|
-
$value:
|
|
8
|
+
$value: '\'Monaspace Neon Var\', \'Monaspace Neon\', \'Aptos Mono\', \'Roboto Mono\', \'SFMono-Regular\', \'Menlo\', \'Monaco\', \'Consolas\', \'Liberation Mono\', \'Andale Mono\', \'Courier New\', monospace',
|
|
9
9
|
$type: 'fontFamily'
|
|
10
10
|
},
|
|
11
11
|
|
package/dev-tools/README.md
CHANGED
|
@@ -6,27 +6,31 @@ A collection of standalone Node.js CLI utilities to help manage a web project's
|
|
|
6
6
|
* [Svg Icons Tools](./svg-icons-tools/README.md) — optimizes SVG icons and combines/converts them into symbols, JSX, or SCSS variables
|
|
7
7
|
* [Create favicons](./create-favicons/README.md) — generates the full favicon set (SVG, PNG, ICO, webmanifest, HTML snippet) from a source image
|
|
8
8
|
* [upd@m](./upd@m/README.md) — updates all `@massimo-cassandro/*` packages installed in `node_modules`
|
|
9
|
-
* [
|
|
9
|
+
* [starter-kit](#starter-kit) — script for installing minimo environment
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## starter-kit
|
|
12
12
|
|
|
13
|
-
`
|
|
13
|
+
`starter-kit/starter-install.sh` bootstraps a new frontend project: it copies the starter config files (`package.json`, `.gitignore`, `jsconfig.json`, eslint/stylelint configs, webpack config and modules, ...) and installs all the required dev dependencies.
|
|
14
14
|
|
|
15
15
|
The script **requires zsh** (it exits with an error if launched with `sh` or `bash`). Existing files are never overwritten: root config files are copied with a `NEW-` prefix, while `package.json` and the `*.code-workspace` file are merged (template keys are appended at the end, prefixed with `_`).
|
|
16
16
|
|
|
17
17
|
### If minimo is already installed
|
|
18
18
|
|
|
19
|
+
The script is exposed as a `bin` of the package:
|
|
20
|
+
|
|
19
21
|
```bash
|
|
20
|
-
|
|
22
|
+
npx starter-kit
|
|
21
23
|
```
|
|
22
24
|
|
|
25
|
+
Since the installation is fully overwrite-safe, the script can also be **re-run at any time to update** an existing setup (e.g. to refresh the webpack modules or reinstall the dev dependencies): existing files are skipped or copied with the `NEW-` prefix, and the `package.json` / `*.code-workspace` merges are idempotent (running the script again neither duplicates nor overwrites anything).
|
|
26
|
+
|
|
23
27
|
### Without installing minimo
|
|
24
28
|
|
|
25
|
-
To bootstrap a project **before** (or without) installing the whole package, download only the `
|
|
29
|
+
To bootstrap a project **before** (or without) installing the whole package, download only the `starter-kit` folder from the npm tarball and run the script from there:
|
|
26
30
|
|
|
27
31
|
```bash
|
|
28
|
-
curl -sL "$(npm view @massimo-cassandro/minimo dist.tarball)" | tar xz package/dev-tools/
|
|
29
|
-
zsh package/dev-tools/
|
|
32
|
+
curl -sL "$(npm view @massimo-cassandro/minimo dist.tarball)" | tar xz package/dev-tools/starter-kit
|
|
33
|
+
zsh package/dev-tools/starter-kit/starter-install.sh
|
|
30
34
|
rm -rf package
|
|
31
35
|
```
|
|
32
36
|
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# TODO — Starter kit: config come punto di regia, moduli autonomi
|
|
2
|
+
|
|
3
|
+
## Obiettivo
|
|
4
|
+
|
|
5
|
+
`webpack.config.mjs` deve diventare **solo** un punto di impostazione di scelte e
|
|
6
|
+
parametri (in piccolo già lo è: `useSass`, `useSvgo`, `purgeCSSDebug`, ecc.).
|
|
7
|
+
Tutta la logica deve migrare nei moduli di `webpack-modules/`, che devono diventare
|
|
8
|
+
**completamente autonomi e indipendenti dalle impostazioni e dal layout del progetto**:
|
|
9
|
+
in questo modo potranno essere aggiornati/sovrascritti in qualsiasi progetto senza
|
|
10
|
+
danni, perché il timone resta sempre nel config del progetto.
|
|
11
|
+
|
|
12
|
+
## Contratto dei moduli (da rispettare in ogni intervento)
|
|
13
|
+
|
|
14
|
+
1. **Nessun percorso di progetto hardcodato** nei moduli: tutti i path (sorgenti,
|
|
15
|
+
node_modules, template, ecc.) arrivano come parametri dal config.
|
|
16
|
+
2. Ogni modulo esporta **factory pure** (funzioni che ricevono opzioni e
|
|
17
|
+
restituiscono rules/plugin/config): nessun side effect a import time.
|
|
18
|
+
3. Ogni parametro ha un **default sensato** e JSDoc: il modulo deve funzionare
|
|
19
|
+
anche senza opzioni, e un'opzione nuova non deve mai rompere un config vecchio.
|
|
20
|
+
4. **Niente dipendenze implicite tra moduli**: se un modulo ha bisogno di un altro
|
|
21
|
+
(es. css-rules → postcss config), il collegamento avviene via parametro passato
|
|
22
|
+
dal config, non con import/path interni.
|
|
23
|
+
5. Aggiornare un modulo non deve mai richiedere modifiche al config esistente,
|
|
24
|
+
se non per adottare volontariamente opzioni nuove.
|
|
25
|
+
6. **Import opzionali = import dinamici**: i pacchetti usati solo quando una
|
|
26
|
+
scelta li attiva (es. sass-loader con `useSass`, responsive-loader, svgo)
|
|
27
|
+
non vanno importati staticamente: con la nuova struttura ogni import
|
|
28
|
+
condizionato da un flag va fatto con `await import(...)` dentro la factory
|
|
29
|
+
(come già avviene per `svgo.config.mjs` nel config), così un pacchetto non
|
|
30
|
+
installato non rompe il caricamento del config/modulo se la relativa
|
|
31
|
+
funzionalità è disattivata.
|
|
32
|
+
|
|
33
|
+
## Stato attuale (inventario del coupling)
|
|
34
|
+
|
|
35
|
+
| Modulo | Stato | Problemi |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `get-jsConfig-aliases.mjs` | ✅ autonomo | riceve il path del jsconfig dal config |
|
|
38
|
+
| `purgecss-variables-safelist.mjs` | ✅ autonomo | tutti i glob/seeds arrivano dal config |
|
|
39
|
+
| `svgo.config.mjs` | ✅ autonomo | nessun riferimento al progetto |
|
|
40
|
+
| `mini-svg-data-uri-loader.cjs` | ✅ autonomo | loader puro |
|
|
41
|
+
| `svg-rules.mjs` | ⚠️ quasi | parametrico, ma da riverificare rispetto al contratto (default, JSDoc) |
|
|
42
|
+
| `css-rules.mjs` | ⚠️ quasi | parametrico, ma riceve `postcssConfig_path` (file di config esterno invece di opzioni); scelte come `localIdentName` non sovrascrivibili dal config |
|
|
43
|
+
| `postcss.config.mjs` | ❌ accoppiato | **hardcoda** `../src/css/custom-media.css` (layout del progetto): non aggiornabile senza danni |
|
|
44
|
+
| `webpack.config.mjs` | ❌ contiene logica | blocchi che sono logica, non scelte: sezione PurgeCSS (opzioni comuni + 2 istanze + log debug), test del cacheGroup `shared`, rules asset (js/ejs/html/img/font/video/favicon), BannerPlugin |
|
|
45
|
+
|
|
46
|
+
## Piano d'azione
|
|
47
|
+
|
|
48
|
+
### 1. Oggetto `settings` unico nel config
|
|
49
|
+
Raggruppare in testa al config un unico oggetto di scelte e percorsi
|
|
50
|
+
(flag attuali + tutti i path oggi sparsi: sorgenti js/css, template twig/php,
|
|
51
|
+
node_modules di minimo, glob shadow-DOM, seeds, favicons, output...).
|
|
52
|
+
Le factory dei moduli ricevono ciò che serve da qui. Il config diventa:
|
|
53
|
+
`settings` → chiamate alle factory → oggetto `config`.
|
|
54
|
+
|
|
55
|
+
### 2. `postcss.config.mjs` → factory senza path hardcodati
|
|
56
|
+
- Trasformarlo in una factory (es. `postcssPlugins({ globalDataFiles, preserveCustomMedia })`)
|
|
57
|
+
che riceve i file per `postcss-global-data` dal config.
|
|
58
|
+
- `css-rules.mjs` non riceve più `postcssConfig_path` (file caricato da
|
|
59
|
+
postcss-loader) ma direttamente l'oggetto `postcssOptions` (o la factory + le sue
|
|
60
|
+
opzioni) costruito nel config: sparisce l'ultimo path relativo al layout progetto.
|
|
61
|
+
- Attenzione a preservare il comportamento attuale di `loaderContext.mode`.
|
|
62
|
+
- **DA CONTROLLARE E VALUTARE — eliminazione del file postcss config**: verificare
|
|
63
|
+
la possibilità di eliminare del tutto il file di config postcss e sostituirlo
|
|
64
|
+
con regole definite direttamente nel webpack config (plugin postcss dichiarati
|
|
65
|
+
inline in `postcssOptions`). Considerare però che in certi casi postcss
|
|
66
|
+
necessita di impostazioni ad hoc per progetto (è il motivo per cui oggi esiste
|
|
67
|
+
il parametro `postcssConfig_path`): in quel caso la strada è mantenere la
|
|
68
|
+
possibilità di override — es. default inline nel config + parametro opzionale
|
|
69
|
+
per un file/oggetto di impostazioni custom. Tenere conto anche degli altri
|
|
70
|
+
consumatori del file: un postcss config a livello di progetto può servire ad
|
|
71
|
+
altri tool (stylelint, editor/IDE, CLI postcss) oltre che a postcss-loader.
|
|
72
|
+
|
|
73
|
+
### 3. Sezione PurgeCSS → `webpack-modules/purgecss-plugins.mjs`
|
|
74
|
+
Factory (es. `purgecssPlugins({ contentGlobs, safelist, variablesSafelistOptions, debug })`)
|
|
75
|
+
che incapsula: opzioni comuni, safelist base (deep `m_`, greedy `&`), le due istanze
|
|
76
|
+
(critical + generale) e il mini-plugin di log `purgedStats`.
|
|
77
|
+
Restituisce l'array di plugin pronto per lo spread nel config.
|
|
78
|
+
Nel config restano SOLO: i glob dei contenuti, le safelist specifiche del progetto
|
|
79
|
+
(standard/seeds), il flag `purgeCSSDebug`. I commenti "strutturali" (problemi a/b/c)
|
|
80
|
+
migrano nel modulo; nel config restano i commenti sulle scelte.
|
|
81
|
+
|
|
82
|
+
### 4. Rules asset generiche → `webpack-modules/asset-rules.mjs`
|
|
83
|
+
Estrarre js/babel, ejs, html, immagini/pdf, font, video, favicons in una factory
|
|
84
|
+
parametrica con on/off per gruppo (es. `assetRules({ favicons_path_regexp, useVideo, ... })`).
|
|
85
|
+
Le parti oggi commentate nel config (responsive-loader, video) diventano opzioni
|
|
86
|
+
documentate del modulo.
|
|
87
|
+
|
|
88
|
+
### 5. splitChunks
|
|
89
|
+
Valutare una piccola factory (es. `sharedCacheGroup({ pathsRegexp })`) che incapsuli
|
|
90
|
+
l'esclusione dei moduli css (`module.type !== 'css/mini-extract'`, requisito PurgeCSS)
|
|
91
|
+
lasciando nel config solo l'elenco dei path. In alternativa lasciarla nel config ma
|
|
92
|
+
con l'esclusione css delegata a una helper del modulo purgecss (è un suo requisito).
|
|
93
|
+
|
|
94
|
+
### 6. `starter-install.sh`
|
|
95
|
+
- Aggiornare l'array `FILES` con i nuovi moduli.
|
|
96
|
+
- Distinguere due categorie nel flusso di copia:
|
|
97
|
+
- `webpack-modules/` = aggiornabili sempre (una volta autonomi, la copia
|
|
98
|
+
NEW- può diventare sovrascrittura diretta o diff);
|
|
99
|
+
- `webpack.config.mjs` e template = mai sovrascritti (proprietà del progetto).
|
|
100
|
+
- **Audit delle dipendenze installate**: verificare la corrispondenza tra i
|
|
101
|
+
pacchetti degli `npm i -D` dello script e quelli effettivamente usati da
|
|
102
|
+
config, moduli e template, per evitare installazioni inutili. Da controllare
|
|
103
|
+
in entrambe le direzioni:
|
|
104
|
+
- installati ma (forse) mai usati: es. `postcss-preset-env` (assente dal
|
|
105
|
+
postcss config), `responsive-loader` (solo in blocchi commentati),
|
|
106
|
+
`style-dictionary` (riguarda i design token, non la build webpack);
|
|
107
|
+
- usati ma non installati: es. `file-loader` (referenziato dalla rule video
|
|
108
|
+
del config) e `sass-loader`/`sass` (necessari con `useSass: true`).
|
|
109
|
+
- i pacchetti legati a funzionalità opzionali (sass, responsive-loader,
|
|
110
|
+
video, svgr...) escono dall'installazione di default: o vengono installati
|
|
111
|
+
su richiesta dallo script (prompt/flag) o documentati come "da installare
|
|
112
|
+
quando si attiva l'opzione X" — coerentemente con la regola degli import
|
|
113
|
+
dinamici del contratto (punto 6).
|
|
114
|
+
|
|
115
|
+
### 7. Versionamento dei moduli
|
|
116
|
+
Aggiungere in testa a ogni modulo un commento con versione (o data) per capire a
|
|
117
|
+
colpo d'occhio, nei progetti, quale revisione è installata.
|
|
118
|
+
|
|
119
|
+
### 8. Documentazione
|
|
120
|
+
README dello starter kit con la mappa: cosa si imposta nel config (e solo lì),
|
|
121
|
+
cosa fa ogni modulo, come aggiornare i moduli in un progetto esistente.
|
|
122
|
+
|
|
123
|
+
## Ordine consigliato
|
|
124
|
+
|
|
125
|
+
2 → 3 → 4 → 5 (refactor moduli, uno per volta, build demo di verifica dopo ognuno)
|
|
126
|
+
→ 1 (consolidamento `settings`) → 6 → 7 → 8.
|
|
127
|
+
|
|
128
|
+
## Note
|
|
129
|
+
|
|
130
|
+
- `merge-jsonc` è già stato spostato in `src/merge-jsonc.mjs` (convertito in ESM):
|
|
131
|
+
la dir `src/` ospita le utility interne dell'installer, NON copiate nei progetti;
|
|
132
|
+
`webpack-modules/` resta la dir dei moduli copiati nei progetti.
|
|
133
|
+
- Criterio di "fatto": si deve poter copiare l'intera `webpack-modules/` aggiornata
|
|
134
|
+
in un progetto esistente senza toccare il suo `webpack.config.mjs` e ottenere
|
|
135
|
+
una build identica (o migliorata) senza errori.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Merge JSONC: aggiunge in fondo al file di destinazione le chiavi di primo
|
|
3
3
|
// livello del template, prefissate con '_', preservando commenti e
|
|
4
4
|
// formattazione di entrambi i file.
|
|
5
|
-
// Uso: node merge-jsonc.
|
|
5
|
+
// Uso: node merge-jsonc.mjs <template> <destinazione>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import fs from 'fs';
|
|
8
8
|
|
|
9
9
|
// Analizza un testo JSONC ignorando stringhe e commenti: individua
|
|
10
10
|
// l'oggetto principale, le virgole a profondità 1 e la posizione
|
|
@@ -71,6 +71,34 @@ function firstQuotePos(text) {
|
|
|
71
71
|
return -1;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
// Voci di primo livello di un oggetto JSONC (spezzate sulle virgole a profondità 1)
|
|
75
|
+
function topLevelEntries(text, s) {
|
|
76
|
+
const body = text.slice(s.open + 1, s.close);
|
|
77
|
+
const offset = s.open + 1;
|
|
78
|
+
const bounds = s.commas.map((p) => p - offset).concat(body.length);
|
|
79
|
+
const entries = [];
|
|
80
|
+
let prev = 0;
|
|
81
|
+
for (const b of bounds) {
|
|
82
|
+
entries.push(body.slice(prev, b));
|
|
83
|
+
prev = b + 1;
|
|
84
|
+
}
|
|
85
|
+
return entries;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Nome della chiave di una voce (contenuto della prima stringa fuori dai commenti)
|
|
89
|
+
function keyName(entry) {
|
|
90
|
+
const q = firstQuotePos(entry);
|
|
91
|
+
if (q < 0) return null;
|
|
92
|
+
let key = '';
|
|
93
|
+
let i = q + 1;
|
|
94
|
+
while (i < entry.length && entry[i] !== '"') {
|
|
95
|
+
if (entry[i] === '\\') i++;
|
|
96
|
+
key += entry[i];
|
|
97
|
+
i++;
|
|
98
|
+
}
|
|
99
|
+
return key;
|
|
100
|
+
}
|
|
101
|
+
|
|
74
102
|
const [tplPath, destPath] = process.argv.slice(2);
|
|
75
103
|
|
|
76
104
|
try {
|
|
@@ -81,23 +109,20 @@ try {
|
|
|
81
109
|
if (t.open < 0 || t.close < 0) throw new Error(tplPath + ': oggetto JSON non trovato');
|
|
82
110
|
if (d.open < 0 || d.close < 0) throw new Error(destPath + ': oggetto JSON non trovato');
|
|
83
111
|
|
|
84
|
-
//
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const entries = [];
|
|
89
|
-
let prev = 0;
|
|
90
|
-
for (const b of bounds) {
|
|
91
|
-
entries.push(body.slice(prev, b));
|
|
92
|
-
prev = b + 1;
|
|
93
|
-
}
|
|
112
|
+
// chiavi di primo livello già presenti nella destinazione
|
|
113
|
+
const destKeys = new Set(
|
|
114
|
+
topLevelEntries(dest, d).map((entry) => keyName(entry)).filter((k) => k !== null)
|
|
115
|
+
);
|
|
94
116
|
|
|
95
|
-
// prefissa con '_' la chiave di ogni voce
|
|
117
|
+
// prefissa con '_' la chiave di ogni voce del template, saltando le voci di
|
|
118
|
+
// soli commenti/spazi e le chiavi già presenti nella destinazione
|
|
119
|
+
// (così rilanciare lo script non produce duplicati)
|
|
96
120
|
const renamed = [];
|
|
97
|
-
for (const entry of
|
|
121
|
+
for (const entry of topLevelEntries(tpl, t)) {
|
|
98
122
|
const cleaned = entry.replace(/^\s+/, '').replace(/\s+$/, '');
|
|
123
|
+
const key = keyName(cleaned);
|
|
124
|
+
if (key === null || destKeys.has('_' + key)) continue;
|
|
99
125
|
const q = firstQuotePos(cleaned);
|
|
100
|
-
if (q < 0) continue;
|
|
101
126
|
renamed.push('\n ' + cleaned.slice(0, q + 1) + '_' + cleaned.slice(q + 1));
|
|
102
127
|
}
|
|
103
128
|
if (renamed.length === 0) process.exit(0);
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#!/bin/zsh
|
|
2
2
|
|
|
3
|
+
# IMPORTANTE: questo script è idempotente e deve restare tale.
|
|
4
|
+
# Può essere rilanciato più volte sullo stesso progetto (anche via `npx starter-kit`
|
|
5
|
+
# per aggiornare un setup esistente) senza sovrascrivere nulla né creare duplicati:
|
|
6
|
+
# i file esistenti vengono saltati o copiati con prefisso NEW-, i merge di
|
|
7
|
+
# package.json e *.code-workspace non duplicano le chiavi già presenti.
|
|
8
|
+
# Ogni modifica futura deve conservare questa proprietà.
|
|
9
|
+
|
|
3
10
|
RED='\033[0;31m'
|
|
4
11
|
YELLOW='\033[0;33m'
|
|
5
12
|
GREEN='\033[0;32m'
|
|
@@ -24,7 +31,7 @@ BASE_URL=${0:A:h}
|
|
|
24
31
|
echo "BASE_URL: ${BASE_URL}"
|
|
25
32
|
|
|
26
33
|
TEMPLATES_DIR="${BASE_URL}/templates"
|
|
27
|
-
# WEBPACK_SOURCE_DIR="$BASE_URL/
|
|
34
|
+
# WEBPACK_SOURCE_DIR="$BASE_URL/starter-kit"
|
|
28
35
|
WEBPACK_SOURCE_DIR="$BASE_URL"
|
|
29
36
|
|
|
30
37
|
# protezione contro la sovrascrittura
|
|
@@ -106,6 +113,9 @@ safe_cat "${TEMPLATES_DIR}/_browserslistrc" .browserslistrc new
|
|
|
106
113
|
safe_cat "${TEMPLATES_DIR}/_editorconfig" .editorconfig new
|
|
107
114
|
safe_cat "${TEMPLATES_DIR}/_prettierrc" .prettierrc new
|
|
108
115
|
safe_cat "${TEMPLATES_DIR}/jsconfig.json" jsconfig.json new
|
|
116
|
+
safe_cat "${TEMPLATES_DIR}/tokens-config.mjs" tokens-config.mjs new
|
|
117
|
+
safe_cat "${TEMPLATES_DIR}/_root_htaccess" _root_htaccess
|
|
118
|
+
safe_cat "${TEMPLATES_DIR}/_root_robots.txt" _root_robots.txt
|
|
109
119
|
|
|
110
120
|
# code-workspace: se un file .code-workspace esiste già (qualunque sia il nome),
|
|
111
121
|
# le chiavi del template vengono aggiunte in fondo con prefisso '_'
|
|
@@ -119,7 +129,7 @@ elif [ ! -f "$WORKSPACE_TPL" ]; then
|
|
|
119
129
|
echo -e "${RED}Sorgente mancante: ${WORKSPACE_TPL}${NC}"
|
|
120
130
|
else
|
|
121
131
|
WORKSPACE_FILE="${WORKSPACE_FILES[1]}"
|
|
122
|
-
if node "${BASE_URL}/merge-jsonc.
|
|
132
|
+
if node "${BASE_URL}/src/merge-jsonc.mjs" "$WORKSPACE_TPL" "$WORKSPACE_FILE"; then
|
|
123
133
|
echo -e "${YELLOW}${WORKSPACE_FILE} esistente: chiavi del template aggiunte in fondo con prefisso '_' (da integrare o rimuovere)${NC}"
|
|
124
134
|
BLOCKED_FILES+=("${WORKSPACE_FILE} → merge: chiavi template aggiunte con prefisso \`_\`")
|
|
125
135
|
else
|
|
@@ -141,7 +151,9 @@ npm i -D webpack-cli webpack-dev-server webpack-manifest-plugin webpack
|
|
|
141
151
|
npm i -D @babel/core @babel/preset-env babel-loader terser-webpack-plugin
|
|
142
152
|
npm i -D webpack-remove-empty-scripts copy-webpack-plugin html-loader html-webpack-plugin
|
|
143
153
|
npm i -D postcss autoprefixer postcss-custom-media @csstools/postcss-global-data postcss-loader postcss-preset-env
|
|
144
|
-
|
|
154
|
+
# NB: niente cssnano: la minificazione è di css-minimizer-webpack-plugin e cssnano
|
|
155
|
+
# nel loader postcss eliminerebbe i commenti `purgecss ignore` prima di PurgeCSS
|
|
156
|
+
npm i -D mini-css-extract-plugin style-loader css-loader css-minimizer-webpack-plugin
|
|
145
157
|
npm i -D responsive-loader
|
|
146
158
|
npm i -D process dotenv-webpack
|
|
147
159
|
npm i -D svgo svg-url-loader svgo-loader svgo-add-viewbox mini-svg-data-uri
|
|
@@ -163,6 +175,7 @@ FILES=(
|
|
|
163
175
|
'svg-rules.mjs'
|
|
164
176
|
'svgo.config.mjs'
|
|
165
177
|
'postcss.config.mjs'
|
|
178
|
+
'purgecss-variables-safelist.mjs'
|
|
166
179
|
)
|
|
167
180
|
|
|
168
181
|
# se la cartella esiste già, i moduli vengono copiati in NEW-webpack-modules
|
|
@@ -186,6 +199,12 @@ mkdir -p "$TEMPLATE_DIR_NAME"
|
|
|
186
199
|
cd "$TEMPLATE_DIR_NAME"
|
|
187
200
|
mkdir -p error-pages imgs icons src src/css favicons design-tokens
|
|
188
201
|
cd ..
|
|
202
|
+
|
|
203
|
+
# template delle entry css (globale/di pagina e critical): ogni entry deve
|
|
204
|
+
# importare direttamente custom-properties.css (vedi commenti nei file stessi)
|
|
205
|
+
safe_cat "${TEMPLATES_DIR}/entry-tpl.css" "${TEMPLATE_DIR_NAME}/src/entry-tpl.css"
|
|
206
|
+
safe_cat "${TEMPLATES_DIR}/entry-tpl.critical.css" "${TEMPLATE_DIR_NAME}/src/entry-tpl.critical.css"
|
|
207
|
+
|
|
189
208
|
set +C
|
|
190
209
|
|
|
191
210
|
# Riepilogo finale dei file già esistenti
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
# no cache
|
|
3
|
+
# <IfModule mod_headers.c>
|
|
4
|
+
# Header set Cache-Control "no-cache, no-store, must-revalidate"
|
|
5
|
+
# Header set Pragma "no-cache"
|
|
6
|
+
# Header set Expires 0
|
|
7
|
+
# </IfModule>
|
|
8
|
+
|
|
9
|
+
# blocco iframe
|
|
10
|
+
# "deny" = tutti siti
|
|
11
|
+
# "sameorigin" tutti tranne quelli che vengono dalla stessa origine
|
|
12
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
|
13
|
+
Header set X-Frame-Options "sameorigin"
|
|
14
|
+
|
|
15
|
+
<IfModule mod_expires.c>
|
|
16
|
+
ExpiresActive On
|
|
17
|
+
AddType application/vnd.ms-fontobject .eot
|
|
18
|
+
AddType application/x-font-ttf .ttf
|
|
19
|
+
AddType application/x-font-opentype .otf
|
|
20
|
+
AddType application/x-font-woff .woff
|
|
21
|
+
AddType application/x-font-woff2 .woff2
|
|
22
|
+
AddType application/font-sfnt otf ttf
|
|
23
|
+
AddType application/font-woff woff
|
|
24
|
+
AddType application/font-woff2 woff2
|
|
25
|
+
AddType image/svg+xml .svg
|
|
26
|
+
|
|
27
|
+
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
|
|
28
|
+
ExpiresByType application/x-font-ttf "access plus 1 year"
|
|
29
|
+
ExpiresByType application/x-font-opentype "access plus 1 year"
|
|
30
|
+
ExpiresByType application/x-font-woff "access plus 1 year"
|
|
31
|
+
ExpiresByType application/x-font-woff2 "access plus 1 year"
|
|
32
|
+
ExpiresByType application/font-sfnt "access plus 1 year"
|
|
33
|
+
ExpiresByType application/font-woff "access plus 1 year"
|
|
34
|
+
ExpiresByType application/font-woff2 "access plus 1 year"
|
|
35
|
+
|
|
36
|
+
ExpiresByType text/html "access plus 1 hour"
|
|
37
|
+
ExpiresByType text/css "access plus 1 year"
|
|
38
|
+
ExpiresByType text/x-javascript "access plus 1 year"
|
|
39
|
+
ExpiresByType application/javascript "access plus 1 year"
|
|
40
|
+
ExpiresByType application/x-javascript "access plus 1 year"
|
|
41
|
+
|
|
42
|
+
ExpiresByType image/svg+xml "access plus 1 year"
|
|
43
|
+
ExpiresByType image/gif "access plus 1 year"
|
|
44
|
+
ExpiresByType image/png "access plus 1 year"
|
|
45
|
+
ExpiresByType image/jpeg "access plus 1 year"
|
|
46
|
+
ExpiresByType image/pjpeg "access plus 1 year"
|
|
47
|
+
ExpiresByType image/webp "access plus 1 year"
|
|
48
|
+
ExpiresByType image/avif "access plus 1 year"
|
|
49
|
+
|
|
50
|
+
ExpiresByType application/pdf "access plus 1 year"
|
|
51
|
+
|
|
52
|
+
ExpiresByType video/mp4 "access plus 1 year"
|
|
53
|
+
ExpiresByType video/webm "access plus 1 year"
|
|
54
|
+
ExpiresByType video/x-m4v "access plus 1 year"
|
|
55
|
+
ExpiresByType video/quicktime "access plus 1 year"
|
|
56
|
+
ExpiresByType audio/mpeg "access plus 1 year"
|
|
57
|
+
ExpiresByType audio/x-wav "access plus 1 year"
|
|
58
|
+
|
|
59
|
+
ExpiresByType image/x-icon "access plus 1 year"
|
|
60
|
+
ExpiresDefault "access plus 1 year"
|
|
61
|
+
</IfModule>
|
|
62
|
+
|
|
63
|
+
<IfModule !mod_expires.c>
|
|
64
|
+
<IfModule mod_headers.c>
|
|
65
|
+
# 1 year
|
|
66
|
+
Header set Cache-Control 'public, max-age 31536000'
|
|
67
|
+
</IfModule>
|
|
68
|
+
</IfModule>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Template di entry css critical — rinominare e adattare.
|
|
3
|
+
|
|
4
|
+
Le entry con suffisso `.critical` generano asset destinati a essere
|
|
5
|
+
INLINATI nei template html: devono restare AUTOSUFFICIENTI e minimi.
|
|
6
|
+
Il suffisso attiva l'istanza PurgeCSS dedicata con purge stretto, in dev
|
|
7
|
+
e in prod (vedi purgeCSSPluginCritical in webpack.config.mjs), e la regola
|
|
8
|
+
dedicata in css-rules.mjs.
|
|
9
|
+
|
|
10
|
+
Anche qui va importato direttamente custom-properties.css: PurgeCSS
|
|
11
|
+
elabora gli asset singolarmente e mantiene in questo asset solo le
|
|
12
|
+
custom properties usate in questo file, quindi l'asset resta minimo
|
|
13
|
+
senza dipendere dal css globale della pagina.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
@import '@css/custom-properties.css';
|
|
17
|
+
|
|
18
|
+
/* stili above-the-fold della pagina (layout iniziale, spinner, ecc.) */
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Template di entry css (globale o di pagina) — rinominare e adattare.
|
|
3
|
+
|
|
4
|
+
NB: OGNI entry css (globale, di pagina, `.critical`) deve importare
|
|
5
|
+
direttamente custom-properties.css: PurgeCSS elabora gli asset css
|
|
6
|
+
SINGOLARMENTE e mantiene in ciascuno solo le custom properties
|
|
7
|
+
effettivamente usate nell'asset stesso (`variables: true`, che
|
|
8
|
+
sostituisce postcss-jit-props). L'import diretto non produce quindi
|
|
9
|
+
duplicazioni nel css finale.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
@import '@css/custom-properties.css';
|
|
13
|
+
/* @import '@css/fonts.css'; */
|
|
14
|
+
|
|
15
|
+
/* @import '@css/custom-media.css'; */ /* NON necessario: iniettato da postcss-global-data */
|
|
16
|
+
|
|
17
|
+
/* moduli minimo: importare solo quelli effettivamente usati */
|
|
18
|
+
/* @import '@minimo/css/reset.css'; */
|
|
19
|
+
/* @import '@minimo/css/layout-base.css'; */
|
|
20
|
+
/* @import '@minimo/css/container.css'; */
|
|
21
|
+
/* @import '@minimo/css/forms/forms.css'; */
|
|
22
|
+
/* @import '@minimo/css/buttons/buttons.css'; */
|
|
23
|
+
/* @import '@minimo/css/alerts.css'; */
|
|
24
|
+
/* ... */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
// import path from 'path';
|
|
3
|
+
|
|
4
|
+
// path relativi a questo file
|
|
5
|
+
|
|
6
|
+
const config = {
|
|
7
|
+
// build tokens
|
|
8
|
+
stylelintConfigPath: '../../stylelint.config.mjs',
|
|
9
|
+
buildPath: '../src/css',
|
|
10
|
+
destFile: 'custom-properties.css',
|
|
11
|
+
source: [
|
|
12
|
+
'../../node_modules/@massimo-cassandro/minimo/design-tokens/_src/**/*.tokens.{mjs,jsonc}',
|
|
13
|
+
'./figma-tokens-OK/*.{jsonc,mjs}',
|
|
14
|
+
],
|
|
15
|
+
|
|
16
|
+
colorScalePrefixes: ['primary', 'secondary', 'neutral'],
|
|
17
|
+
|
|
18
|
+
penpotFormat: 'jsonc',
|
|
19
|
+
penpotBuildPath: null,
|
|
20
|
+
penpotDestFile: null,
|
|
21
|
+
penpotExpressions: 'resolve',
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// check-unresolved-custom-props.mjs settings
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
extra_custom_props_files: [],
|
|
27
|
+
dir_to_check: '../src', // percorso della dir che include i css da analizzare
|
|
28
|
+
exclude_pattern: [ // patterns per l'esclusione dal controllo unresolved-props
|
|
29
|
+
/^--_/,
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default config;
|
|
@@ -146,7 +146,12 @@ function css_loaders({
|
|
|
146
146
|
modules: css_modules
|
|
147
147
|
? {
|
|
148
148
|
auto: true,
|
|
149
|
-
|
|
149
|
+
// il prefisso fisso `m_`, presente in ENTRAMBE le modalità, consente
|
|
150
|
+
// il safelist delle classi dei css modules in PurgeCSS (safelist.deep
|
|
151
|
+
// [/^m_/], vedi webpack.config.mjs): le classi hashate non possono
|
|
152
|
+
// comparire nei file di contenuto scansionati, e il purge è attivo
|
|
153
|
+
// anche in dev
|
|
154
|
+
localIdentName: isDevelopment ? 'm_[local]_[hash:base64:6]' : 'm_[hash:base64:8]',
|
|
150
155
|
exportLocalsConvention: 'as-is',
|
|
151
156
|
|
|
152
157
|
// namedExport: false,
|
|
@@ -4,40 +4,34 @@ import { fileURLToPath } from 'url';
|
|
|
4
4
|
import globalData from '@csstools/postcss-global-data';
|
|
5
5
|
import customMedia from 'postcss-custom-media';
|
|
6
6
|
import autoprefixer from 'autoprefixer';
|
|
7
|
-
import cssnano from 'cssnano';
|
|
8
|
-
import jitProps from 'postcss-jit-props';
|
|
9
|
-
// import openProps from './modulo_cjs.cjs';
|
|
10
7
|
|
|
11
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
9
|
const __dirname = path.dirname(__filename);
|
|
13
10
|
|
|
11
|
+
// NB: le custom properties NON sono gestite qui (postcss-jit-props rimosso):
|
|
12
|
+
// ogni entry css importa direttamente custom-properties.css e le props
|
|
13
|
+
// non utilizzate vengono rimosse in build da PurgeCSS (`variables: true`,
|
|
14
|
+
// vedi webpack.config.mjs)
|
|
15
|
+
|
|
16
|
+
// NB: la minificazione è gestita da CssMinimizerPlugin (webpack.config.mjs):
|
|
17
|
+
// cssnano NON va aggiunto qui, oltre a duplicare la minificazione
|
|
18
|
+
// rimuoverebbe i commenti speciali `/* purgecss start/end ignore */`
|
|
19
|
+
// prima dell'esecuzione di PurgeCSS
|
|
20
|
+
|
|
14
21
|
export default (loaderContext) => {
|
|
15
22
|
const isDevelopment = loaderContext.mode === 'development';
|
|
16
23
|
|
|
17
24
|
const plugins = [
|
|
25
|
+
// rende disponibili le @custom-media a tutti i file css
|
|
26
|
+
// (il contenuto iniettato viene rimosso a fine elaborazione;
|
|
27
|
+
// NON aggiungere qui custom-properties.css: duplicherebbe le props
|
|
28
|
+
// in ogni asset e interferirebbe con il purge, vedi sopra)
|
|
18
29
|
globalData({
|
|
19
30
|
files: [
|
|
20
|
-
// openProps.
|
|
21
|
-
path.resolve(__dirname, '../src/css/custom-properties.css'),
|
|
22
31
|
path.resolve(__dirname, '../src/css/custom-media.css'),
|
|
23
32
|
]
|
|
24
33
|
}),
|
|
25
34
|
|
|
26
|
-
// https://github.com/GoogleChromeLabs/postcss-jit-props
|
|
27
|
-
// https://github.com/argyleink/open-props
|
|
28
|
-
// require('postcss-jit-props')(require('open-props')),
|
|
29
|
-
// require('postcss-jit-props')({
|
|
30
|
-
// ...require('open-props'),
|
|
31
|
-
// custom_selector: ':where(html)'
|
|
32
|
-
// }),
|
|
33
|
-
jitProps({
|
|
34
|
-
files: [
|
|
35
|
-
// ...openProps,
|
|
36
|
-
path.resolve(__dirname, '../src/css/custom-properties.css')
|
|
37
|
-
],
|
|
38
|
-
custom_selector: ':where(html)'
|
|
39
|
-
}),
|
|
40
|
-
|
|
41
35
|
// per IOS < 17 (test)
|
|
42
36
|
// https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting
|
|
43
37
|
// https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-light-dark-function#readme
|
|
@@ -47,7 +41,6 @@ export default (loaderContext) => {
|
|
|
47
41
|
//}),
|
|
48
42
|
// require('@csstools/postcss-light-dark-function')({preserve: false}), /* NB non funziona benissimo... */
|
|
49
43
|
|
|
50
|
-
|
|
51
44
|
// https://github.com/csstools/postcss-plugins/
|
|
52
45
|
|
|
53
46
|
customMedia({
|
|
@@ -58,19 +51,6 @@ export default (loaderContext) => {
|
|
|
58
51
|
autoprefixer(),
|
|
59
52
|
];
|
|
60
53
|
|
|
61
|
-
if (!isDevelopment) {
|
|
62
|
-
// per IOS < 17
|
|
63
|
-
// require('postcss-nesting')({
|
|
64
|
-
// edition: '2021',
|
|
65
|
-
// noIsPseudoSelector: true
|
|
66
|
-
//}),
|
|
67
|
-
// require('@csstools/postcss-light-dark-function'), /* NB non funziona benissimo... */
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
plugins.push(cssnano({ preset: 'default' }));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
54
|
return {
|
|
75
55
|
plugins: plugins
|
|
76
56
|
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// webpack-modules/purgecss-variables-safelist.mjs
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { globSync } from 'glob';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Genera l'elenco di custom properties da passare a `safelist.variables` di PurgeCSS.
|
|
7
|
+
L'elenco viene ricalcolato in memoria a ogni avvio di webpack: nessun file su disco.
|
|
8
|
+
|
|
9
|
+
Perché serve: PurgeCSS (`variables: true`) registra l'uso delle variabili solo
|
|
10
|
+
nelle regole che effettivamente elabora, all'interno del singolo asset css.
|
|
11
|
+
Restano quindi "invisibili" gli usi di var() che avvengono:
|
|
12
|
+
|
|
13
|
+
1. nei blocchi racchiusi tra i commenti `purgecss start/end ignore`
|
|
14
|
+
(presenti ad es. nei sorgenti di minimo): le regole vengono mantenute,
|
|
15
|
+
ma le custom properties che consumano verrebbero rimosse dal :root;
|
|
16
|
+
2. nei css caricati fuori dagli asset purgati (shadow DOM dei web components,
|
|
17
|
+
import con `?raw`): consumano le props del :root della pagina;
|
|
18
|
+
3. nelle dichiarazioni di un asset consumate in un altro asset
|
|
19
|
+
(es. override di tema definiti nel css di una pagina e consumati
|
|
20
|
+
dal css globale): vanno indicate tramite `seeds`.
|
|
21
|
+
|
|
22
|
+
Inoltre `safelist.variables` NON segue le catene di dipendenze (una prop
|
|
23
|
+
safelisted resta nel css, ma i suoi riferimenti var() non vengono marcati
|
|
24
|
+
come usati: `--a: var(--b)` in safelist non salva `--b`): l'elenco viene
|
|
25
|
+
quindi espanso con la chiusura transitiva calcolata sul grafo di tutte le
|
|
26
|
+
dichiarazioni `--x: ...` presenti nei sorgenti.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const VAR_REFS_REGEX = /var\(\s*(--[A-Za-z0-9_-]+)/g;
|
|
30
|
+
const DECLARATION_REGEX = /(--[A-Za-z0-9_-]+)\s*:\s*([^;}]+)/g;
|
|
31
|
+
const IGNORE_START_REGEX = /\/\*\s*purgecss start ignore\s*\*\//;
|
|
32
|
+
const IGNORE_END_REGEX = /\/\*\s*purgecss end ignore\s*\*\//;
|
|
33
|
+
|
|
34
|
+
const varRefs = (cssText) => [...cssText.matchAll(VAR_REFS_REGEX)].map(m => m[1]);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {object} params
|
|
38
|
+
* @param {string[]} params.declarationGlobs glob dei css che contribuiscono al grafo
|
|
39
|
+
* delle dipendenze; i loro blocchi `purgecss ignore` contano come usi
|
|
40
|
+
* @param {string[]} [params.shadowGlobs] glob dei css usati fuori dagli asset purgati
|
|
41
|
+
* (shadow DOM / `?raw`): TUTTI i loro usi di var() contano
|
|
42
|
+
* @param {(string|RegExp)[]} [params.seeds] props da mantenere comunque (nomi o pattern
|
|
43
|
+
* risolti sui nomi dichiarati), es. override consumati in un altro asset
|
|
44
|
+
* @returns {string[]} elenco ordinato di nomi di custom properties
|
|
45
|
+
*/
|
|
46
|
+
export function purgecssVariablesSafelist({ declarationGlobs, shadowGlobs = [], seeds = [] }) {
|
|
47
|
+
|
|
48
|
+
const declaredRefs = new Map(); // nome → Set(var() referenziate nelle sue dichiarazioni)
|
|
49
|
+
const usages = new Set(); // usi da mantenere (blocchi ignore + shadow css)
|
|
50
|
+
|
|
51
|
+
for (const file of globSync(declarationGlobs, { nodir: true })) {
|
|
52
|
+
const src = fs.readFileSync(file, 'utf8');
|
|
53
|
+
|
|
54
|
+
// grafo delle dipendenze: ogni dichiarazione `--x: ...` con le var() nel valore
|
|
55
|
+
for (const declaration of src.matchAll(DECLARATION_REGEX)) {
|
|
56
|
+
const [, name, value] = declaration;
|
|
57
|
+
if (!declaredRefs.has(name)) {
|
|
58
|
+
declaredRefs.set(name, new Set());
|
|
59
|
+
}
|
|
60
|
+
varRefs(value).forEach(ref => declaredRefs.get(name).add(ref));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// usi di var() nei blocchi `purgecss start/end ignore`
|
|
64
|
+
src.split(IGNORE_START_REGEX).slice(1).forEach(chunk => {
|
|
65
|
+
varRefs(chunk.split(IGNORE_END_REGEX)[0]).forEach(ref => usages.add(ref));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// shadow css: ogni uso di var() conta
|
|
70
|
+
for (const file of globSync(shadowGlobs, { nodir: true })) {
|
|
71
|
+
varRefs(fs.readFileSync(file, 'utf8')).forEach(ref => usages.add(ref));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// risoluzione dei seeds sui nomi dichiarati
|
|
75
|
+
const declaredNames = [...declaredRefs.keys()];
|
|
76
|
+
const seedNames = seeds.flatMap(seed =>
|
|
77
|
+
typeof seed === 'string' ? [seed] : declaredNames.filter(name => seed.test(name))
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// chiusura transitiva delle dipendenze
|
|
81
|
+
const result = new Set();
|
|
82
|
+
const queue = [...seedNames, ...usages];
|
|
83
|
+
while (queue.length > 0) {
|
|
84
|
+
const name = queue.pop();
|
|
85
|
+
if (!result.has(name)) {
|
|
86
|
+
result.add(name);
|
|
87
|
+
queue.push(...(declaredRefs.get(name) ?? []));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return [...result].sort();
|
|
92
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// webpack.config.mjs
|
|
1
|
+
// webpack.config.mjs __project_name__
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import webpack from 'webpack';
|
|
@@ -22,6 +22,7 @@ import { PurgeCSSPlugin } from 'purgecss-webpack-plugin';
|
|
|
22
22
|
import { globSync } from 'glob';
|
|
23
23
|
|
|
24
24
|
import { cssRules } from './webpack-modules/css-rules.mjs';
|
|
25
|
+
import { purgecssVariablesSafelist } from './webpack-modules/purgecss-variables-safelist.mjs';
|
|
25
26
|
import { getJsConfigAliases } from './webpack-modules/get-jsConfig-aliases.mjs';
|
|
26
27
|
import { svgRules } from './webpack-modules/svg-rules.mjs';
|
|
27
28
|
|
|
@@ -41,6 +42,7 @@ const isDevelopment = process.env.NODE_ENV === 'development'
|
|
|
41
42
|
,favicons_path_regexp = new RegExp(favicons_path) // source pattern per le favicons (regexp o null)
|
|
42
43
|
,jsConfigAliases = getJsConfigAliases(path.resolve(__dirname, './jsconfig.json'))
|
|
43
44
|
,packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, './package.json'), 'utf-8'))
|
|
45
|
+
,purgeCSSDebug = false // true per loggare i selettori rimossi da PurgeCSS a fine build
|
|
44
46
|
// ,manifest_shared_seed = {}
|
|
45
47
|
;
|
|
46
48
|
|
|
@@ -87,6 +89,111 @@ const CopyWebpackPluginPatterns = [
|
|
|
87
89
|
// }
|
|
88
90
|
];
|
|
89
91
|
|
|
92
|
+
// =>> PurgeCSS: opzioni e istanze (usate nei plugins e dal log di debug)
|
|
93
|
+
//
|
|
94
|
+
// Vengono create DUE istanze, attive in ENTRAMBE le modalità (dev e prod),
|
|
95
|
+
// così i problemi di purge emergono subito durante lo sviluppo:
|
|
96
|
+
// - `purgeCSSPluginCritical`: solo le entry `*.critical` (purge stretto, gli asset
|
|
97
|
+
// devono essere autosufficienti e minimi perché inlinati nei template html)
|
|
98
|
+
// - `purgeCSSPlugin`: tutti gli asset (il doppio passaggio sui critical è idempotente)
|
|
99
|
+
//
|
|
100
|
+
// NB (watch mode): i template twig NON sono osservati da webpack: dopo aver
|
|
101
|
+
// aggiunto una classe solo in un twig occorre rilanciare la build (o toccare
|
|
102
|
+
// un file js/css) perché il purge venga ricalcolato.
|
|
103
|
+
// NB (dev): il plugin agisce solo sugli asset css emessi; con
|
|
104
|
+
// `inlineCssInDevMode: true` i css non-critical in dev sono iniettati da
|
|
105
|
+
// style-loader e non vengono purgati (i critical passano comunque da
|
|
106
|
+
// MiniCssExtract e sono sempre purgati)
|
|
107
|
+
|
|
108
|
+
// `paths`: SOLO file che generano markup o classi (html/ejs, twig, php, js);
|
|
109
|
+
// NIENTE file css tra i paths: verrebbero letti come "contenuto" e ogni selettore
|
|
110
|
+
// risulterebbe usato, vanificando il purge
|
|
111
|
+
const purgeCSSCommonOptions = {
|
|
112
|
+
paths: () => globSync(
|
|
113
|
+
[
|
|
114
|
+
path.resolve(__dirname, './src/**/*.{js,mjs,jsx}'),
|
|
115
|
+
path.resolve(__dirname, './*.ejs'), // template html di webpack
|
|
116
|
+
path.resolve(__dirname, './src/**/*.ejs'),
|
|
117
|
+
// progetti symfony: template twig e classi generate lato php
|
|
118
|
+
// path.resolve(__dirname, '../templates/**/*.twig'),
|
|
119
|
+
// path.resolve(__dirname, '../src/**/*.php'),
|
|
120
|
+
// il js di minimo genera markup con classi proprie (snackbar, unsplash-page, ecc.)
|
|
121
|
+
// NB: adattare il percorso di node_modules se questo config è in una
|
|
122
|
+
// sottodirectory del progetto (es. frontend/ → '../node_modules/...')
|
|
123
|
+
path.resolve(__dirname, './node_modules/@massimo-cassandro/minimo/src/**/*.{js,mjs}'),
|
|
124
|
+
],
|
|
125
|
+
{ nodir: true }
|
|
126
|
+
),
|
|
127
|
+
|
|
128
|
+
// extractor di default (/[A-Za-z0-9_-]+/g): adeguato a classi/tag/id,
|
|
129
|
+
// NON definire extractors custom (il vecchio /[A-z0-9-:/]+/ era buggato:
|
|
130
|
+
// il range [A-z] include caratteri non voluti come `[`, `\`, `^`, ...)
|
|
131
|
+
|
|
132
|
+
variables: true, // rimuove le custom properties non usate (sostituisce postcss-jit-props)
|
|
133
|
+
keyframes: true, // rimuove i @keyframes non referenziati
|
|
134
|
+
// fontFace resta false (default): i font sono referenziati solo tramite
|
|
135
|
+
// var(--font-family) e purgecss non risolve le custom properties nei valori,
|
|
136
|
+
// quindi con `true` i @font-face verrebbero rimossi anche se usati
|
|
137
|
+
|
|
138
|
+
rejected: purgeCSSDebug // popola purgedStats per il log di debug
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// safelist base minima, comune alle due istanze
|
|
142
|
+
const purgeCSSSafelistBase = {
|
|
143
|
+
standard: [
|
|
144
|
+
// classi costruite dinamicamente nei template o lato server, invisibili
|
|
145
|
+
// all'analisi statica dei paths. Esempio (da adattare al progetto):
|
|
146
|
+
// flash messages twig con class="alert alert-{{ label }}"
|
|
147
|
+
/^alert-(success|notice|error|warning)$/
|
|
148
|
+
],
|
|
149
|
+
deep: [
|
|
150
|
+
// css modules: prefisso `m_` presente in dev e prod (vedi css-rules.mjs),
|
|
151
|
+
// le classi hashate non possono comparire nei file scansionati
|
|
152
|
+
/^m_/
|
|
153
|
+
],
|
|
154
|
+
greedy: [
|
|
155
|
+
// INDISPENSABILE: purgecss non riconosce il nesting selector nativo e senza
|
|
156
|
+
// questo pattern i selettori annidati composti solo da `&` + pseudo classi
|
|
157
|
+
// (&:hover, &:focus-visible, `&` semplice, ecc.) verrebbero SEMPRE rimossi
|
|
158
|
+
/^&$/
|
|
159
|
+
]
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// css della pagina critici (inlinati nei template): purge stretto,
|
|
163
|
+
// devono essere autosufficienti e minimi
|
|
164
|
+
const purgeCSSPluginCritical = new PurgeCSSPlugin({
|
|
165
|
+
...purgeCSSCommonOptions,
|
|
166
|
+
only: ['.critical'],
|
|
167
|
+
safelist: purgeCSSSafelistBase
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// tutti gli altri asset (il purge è idempotente: il secondo passaggio
|
|
171
|
+
// sui critical non rimuove altro)
|
|
172
|
+
const purgeCSSPlugin = new PurgeCSSPlugin({
|
|
173
|
+
...purgeCSSCommonOptions,
|
|
174
|
+
safelist: {
|
|
175
|
+
...purgeCSSSafelistBase,
|
|
176
|
+
// elenco generato: usi di var() nei blocchi `purgecss ignore` e nei css
|
|
177
|
+
// shadow-DOM/`?raw`, override cross-asset (seeds), con chiusura transitiva
|
|
178
|
+
// delle dipendenze (vedi webpack-modules/purgecss-variables-safelist.mjs)
|
|
179
|
+
variables: purgecssVariablesSafelist({
|
|
180
|
+
declarationGlobs: [
|
|
181
|
+
path.resolve(__dirname, './src/**/*.css'),
|
|
182
|
+
path.resolve(__dirname, './node_modules/@massimo-cassandro/minimo/src/**/*.css'),
|
|
183
|
+
],
|
|
184
|
+
shadowGlobs: [
|
|
185
|
+
path.resolve(__dirname, './src/web-components/**/*.css'),
|
|
186
|
+
path.resolve(__dirname, './node_modules/@massimo-cassandro/minimo/src/web-components/**/*.css'),
|
|
187
|
+
],
|
|
188
|
+
seeds: [
|
|
189
|
+
// props dichiarate in un asset ma consumate in un altro (es. override
|
|
190
|
+
// di tema definiti nel css di una pagina e consumati dal css globale):
|
|
191
|
+
// indicarle qui per nome ('--btn-primary-bg') o pattern (/^--btn-/)
|
|
192
|
+
]
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
90
197
|
// recupero metadata immagini
|
|
91
198
|
// const require = createRequire(import.meta.url);
|
|
92
199
|
// responsive-loader adapter (CJS) — use createRequire
|
|
@@ -96,8 +203,14 @@ const CopyWebpackPluginPatterns = [
|
|
|
96
203
|
/** CONFIG **/
|
|
97
204
|
|
|
98
205
|
// =>> entries
|
|
206
|
+
// NB: percorsi dalla root del progetto
|
|
99
207
|
const entries = {
|
|
100
|
-
'xxxxxx': './src/index.js'
|
|
208
|
+
'xxxxxx': './src/index.js'
|
|
209
|
+
|
|
210
|
+
// css critici inlinati nei template html: il suffisso `.critical` nel nome
|
|
211
|
+
// della entry attiva l'istanza PurgeCSS dedicata con purge stretto
|
|
212
|
+
// (vedi purgeCSSPluginCritical) e la regola dedicata in css-rules.mjs
|
|
213
|
+
// ,'xxxxxx.critical': './src/xxxxxx.critical.css'
|
|
101
214
|
};
|
|
102
215
|
|
|
103
216
|
|
|
@@ -106,7 +219,14 @@ const config = {
|
|
|
106
219
|
|
|
107
220
|
// watch: isDevelopment, // necessario se non si usa devServer
|
|
108
221
|
watchOptions: {
|
|
109
|
-
ignored: [
|
|
222
|
+
ignored: [
|
|
223
|
+
'**/build/**',
|
|
224
|
+
'**/_dev/**',
|
|
225
|
+
'**/.git/**',
|
|
226
|
+
'**/_private/**',
|
|
227
|
+
'**/node_modules/**',
|
|
228
|
+
'!**/node_modules/@massimo-cassandro/**'
|
|
229
|
+
],
|
|
110
230
|
// poll: 1000, // se necessario, risolve il problema `Error: EMFILE: too many open files, watch`
|
|
111
231
|
},
|
|
112
232
|
|
|
@@ -153,7 +273,7 @@ const config = {
|
|
|
153
273
|
extractComments: false
|
|
154
274
|
})
|
|
155
275
|
],
|
|
156
|
-
runtimeChunk: true
|
|
276
|
+
runtimeChunk: 'single', // true
|
|
157
277
|
splitChunks: {
|
|
158
278
|
cacheGroups: {
|
|
159
279
|
|
|
@@ -172,7 +292,18 @@ const config = {
|
|
|
172
292
|
},
|
|
173
293
|
|
|
174
294
|
shared: {
|
|
175
|
-
|
|
295
|
+
// solo moduli js (module.type !== 'css/mini-extract'): il css estratto
|
|
296
|
+
// resta nel chunk della entry che lo importa, così ogni asset css è
|
|
297
|
+
// autosufficiente. Necessario per PurgeCSS (`variables: true` funziona
|
|
298
|
+
// per singolo asset) e per i critical css inlinati nei template
|
|
299
|
+
//
|
|
300
|
+
// TODO: se in futuro la duplicazione di css tra le pagine dovesse crescere
|
|
301
|
+
// (componenti importati dal js di più entry), valutare un cacheGroup `styles`
|
|
302
|
+
// dedicato ai css condivisi: andranno escluse le entry `.critical` (devono
|
|
303
|
+
// restare autosufficienti) e gestite in safelist.variables le custom properties
|
|
304
|
+
// definite/consumate tra asset diversi (vedi purgecss-variables-safelist.mjs)
|
|
305
|
+
test: (module) => module.type !== 'css/mini-extract'
|
|
306
|
+
&& shared_chunk_paths.test(module.nameForCondition?.() ?? ''),
|
|
176
307
|
// test: /[\\/]node_modules[\\/]/,
|
|
177
308
|
|
|
178
309
|
// alternativa: non include nel chunk shared i path che contengono la stringa `blurhash`
|
|
@@ -281,7 +412,7 @@ const config = {
|
|
|
281
412
|
// removeKeyHash: true, // /([a-f0-9]{32}\.?)/gi, // /(\?as_asset)$/,
|
|
282
413
|
// // rimuove i font dal manifest. Non necessari, rendono il file inutilmente grande
|
|
283
414
|
// filter: isDevelopment? undefined : (FileDescriptor) => {
|
|
284
|
-
// return /
|
|
415
|
+
// return /fonts/.test(FileDescriptor.path)? false : true;
|
|
285
416
|
// },
|
|
286
417
|
// sort: isDevelopment? undefined : (a, b) => a.name.localeCompare(b.name)
|
|
287
418
|
// }),
|
|
@@ -368,41 +499,42 @@ const config = {
|
|
|
368
499
|
);
|
|
369
500
|
},
|
|
370
501
|
raw: true,
|
|
371
|
-
//
|
|
502
|
+
// niente banner nei critical css: vengono inlinati nei template html
|
|
503
|
+
exclude: /\.critical/ // chunk name
|
|
372
504
|
}),
|
|
373
505
|
|
|
374
|
-
// =>> plugins: PurgeCSSPlugin (per ultimo
|
|
506
|
+
// =>> plugins: PurgeCSSPlugin (per ultimo, attivo in dev e prod: i problemi
|
|
507
|
+
// di purge devono emergere subito durante lo sviluppo, non solo in build)
|
|
375
508
|
// https://github.com/FullHuman/purgecss/tree/main/packages/purgecss-webpack-plugin
|
|
376
509
|
// https://purgecss.com/configuration.html
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
})
|
|
510
|
+
//
|
|
511
|
+
// NB: il plugin elabora ogni asset css SINGOLARMENTE, quindi con `variables: true`
|
|
512
|
+
// una custom property viene mantenuta solo se definita e usata nello stesso asset.
|
|
513
|
+
// Per questo:
|
|
514
|
+
// - ogni entry css importa direttamente custom-properties.css (che sostituisce
|
|
515
|
+
// postcss-jit-props: le props non usate vengono rimosse qui)
|
|
516
|
+
// - il chunk `shared` contiene solo js (vedi optimization.splitChunks)
|
|
517
|
+
// - le props dichiarate in un asset ma consumate altrove (override di tema,
|
|
518
|
+
// shadow DOM dei web components) sono gestite dalla safelist generata
|
|
519
|
+
// (vedi purgecss-variables-safelist.mjs)
|
|
520
|
+
purgeCSSPluginCritical,
|
|
521
|
+
purgeCSSPlugin,
|
|
522
|
+
|
|
523
|
+
// log dei selettori rimossi (richiede `rejected: true` nei plugin, vedi purgeCSSDebug).
|
|
524
|
+
// NB: legge `purgedStats` dalle ISTANZE del plugin, non da compilation.purgedStats
|
|
525
|
+
// (proprietà inesistente: il vecchio log basato su di essa era codice morto)
|
|
526
|
+
...(purgeCSSDebug ? [{
|
|
527
|
+
apply: (compiler) => {
|
|
528
|
+
compiler.hooks.afterEmit.tap('PurgeCSSLog', () => {
|
|
529
|
+
/* eslint-disable no-console */
|
|
530
|
+
console.log('\n--- [PurgeCSS] Selettori rimossi (critical) ---');
|
|
531
|
+
console.dir(purgeCSSPluginCritical.purgedStats, { depth: null, maxArrayLength: null });
|
|
532
|
+
console.log('\n--- [PurgeCSS] Selettori rimossi ---');
|
|
533
|
+
console.dir(purgeCSSPlugin.purgedStats, { depth: null, maxArrayLength: null });
|
|
534
|
+
/* eslint-enable no-console */
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}] : [])
|
|
406
538
|
|
|
407
539
|
], // end plugins
|
|
408
540
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massimo-cassandro/minimo",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.1",
|
|
4
4
|
"description": "Light css framework & utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"update-version": "./dev-tools/update-version/index.mjs",
|
|
25
25
|
"upd@m": "./dev-tools/upd@m/index.mjs",
|
|
26
26
|
"svgIconsTools": "./dev-tools/svg-icons-tools/index.mjs",
|
|
27
|
-
"create-favicons": "./dev-tools/create-favicons/index.mjs"
|
|
27
|
+
"create-favicons": "./dev-tools/create-favicons/index.mjs",
|
|
28
|
+
"starter-kit": "./dev-tools/starter-kit/starter-install.sh"
|
|
28
29
|
},
|
|
29
30
|
"repository": {
|
|
30
31
|
"type": "git",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/dev-tools/{webpack-setup → starter-kit}/webpack-modules/mini-svg-data-uri-loader.cjs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|