@massimo-cassandro/minimo 1.36.1 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dev-tools/design-tokens-builder/README.md +22 -0
  2. package/dev-tools/design-tokens-builder/build-tokens-src/config.mjs +22 -3
  3. package/dev-tools/design-tokens-builder/build-tokens-src/platforms.mjs +4 -2
  4. package/dev-tools/design-tokens-builder/build-tokens-src/source-prefixes.mjs +129 -0
  5. package/dev-tools/design-tokens-builder/build-tokens-src/transforms.mjs +20 -0
  6. package/dev-tools/design-tokens-builder/build-tokens.mjs +9 -0
  7. package/dev-tools/design-tokens-builder/check-unresolved-custom-props.mjs +13 -8
  8. package/dev-tools/design-tokens-builder/tokens-config-sample.mjs +10 -1
  9. package/dev-tools/starter-kit/source_files/app/email/_mail-properties.scss +18 -0
  10. package/dev-tools/starter-kit/source_files/app/email/mail-embed.scss +68 -0
  11. package/dev-tools/starter-kit/source_files/app/email/mail-inline.scss +203 -0
  12. package/dev-tools/starter-kit/source_files/app/email/readme.md +26 -0
  13. package/dev-tools/starter-kit/source_files/app/email/{_mail-properties.css → versione postcss/_mail-properties.css } +2 -5
  14. package/dev-tools/starter-kit/source_files/app/email/{mail-embed.css → versione postcss/mail-embed.css } +14 -15
  15. package/dev-tools/starter-kit/source_files/app/email/{mail-inline.css → versione postcss/mail-inline.css } +50 -61
  16. package/dev-tools/starter-kit/source_files/app/email/{postcss.config.mjs → versione postcss/postcss.config.mjs } +1 -1
  17. package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates}/_main-tpl.html.twig +3 -0
  18. package/dev-tools/starter-kit/source_files/root/{sf_templates → _sf/templates}/email/_email-tpl.html.twig +8 -9
  19. package/dev-tools/starter-kit/source_files/root/_sf/templates/email/css/.gitkeep +0 -0
  20. package/dev-tools/starter-kit/source_files/root/_sf/templates/email/imgs/.gitkeep +0 -0
  21. package/dev-tools/starter-kit/source_files/root/jsconfig.json +1 -1
  22. package/dev-tools/starter-kit/source_files/root/package-tpl.json +5 -2
  23. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/css-rules.mjs +1 -0
  24. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/custom-props-purgecss-plugin.mjs +328 -0
  25. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/get-jsConfig-aliases.mjs +1 -0
  26. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/inline-critical-css.mjs +1 -0
  27. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/mini-svg-data-uri-loader.cjs +1 -0
  28. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/postcss.config.mjs +9 -5
  29. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/purgecss-setup.mjs +23 -27
  30. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/svg-rules.mjs +1 -0
  31. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/svgo.config.mjs +1 -0
  32. package/dev-tools/starter-kit/source_files/root/webpack.config.mjs +137 -65
  33. package/dev-tools/starter-kit/starter-install.sh +2 -1
  34. package/dev-tools/starter-kit/webpack-config-upg.md +147 -0
  35. package/package.json +1 -1
  36. package/src/css/forms/form-group.css +0 -1
  37. package/src/minimo.css +42 -42
  38. package/dev-tools/starter-kit/source_files/root/sf_templates/email/css/mail-embed.css +0 -1
  39. package/dev-tools/starter-kit/source_files/root/sf_templates/email/css/mail-inline.css +0 -1
  40. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/purgecss-variables-safelist.mjs +0 -92
  41. /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root}/_env.local +0 -0
  42. /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf}/symfony-webpack-setup.md +0 -0
  43. /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates}/bundles/TwigBundle/Exception/error.html.twig +0 -0
  44. /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates/incl}/flash-messages.html.twig +0 -0
  45. /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates/incl}/minimo-form-layout.html.twig +0 -0
  46. /package/snippets/{twig-symfony → symfony}/ImgViewerController.php +0 -0
  47. /package/snippets/{twig-symfony → symfony}/UnsplashController.php +0 -0
  48. /package/snippets/{twig-symfony → symfony}/ViewerController.php +0 -0
@@ -0,0 +1,328 @@
1
+ // webpack-config-modules/custom-props-purgecss-plugin.mjs
2
+ // v.3
3
+ import fs from 'fs';
4
+ import postcss from 'postcss';
5
+ import { globSync } from 'glob';
6
+
7
+ /*
8
+ Plugin webpack per l'estrazione ottimizzata delle CSS custom properties.
9
+
10
+ Contesto: il file "master" (definitionsFile) contiene TUTTE le definizioni di
11
+ custom properties disponibili. I css compilati da webpack contengono invece
12
+ solo *usi* (`var(--nome)`), mai definizioni. Dopo la minificazione degli
13
+ asset, il plugin:
14
+ 1. scansiona i css di ogni "set" per trovare le custom properties usate;
15
+ 2. risolve le dipendenze transitive (`--a: calc(var(--b) * 2)` => anche
16
+ `--b`, fino al fixed point, fallback `var(--x, var(--y))` inclusi);
17
+ 3. estrae dal master le sole definizioni necessarie, PRESERVANDO l'ordine
18
+ originale di dichiarazione;
19
+ 4. le inietta all'inizio del file target del set (dopo l'eventuale banner
20
+ webpack, cioè un commento che inizia con `/*!`, o `@charset`), oppure le
21
+ emette come nuovo asset.
22
+
23
+ Rispetto a postcss-jit-props (che opera sul singolo file, prima del bundling)
24
+ lavora sugli asset finali e permette di decidere il raggruppamento
25
+ sources -> target.
26
+
27
+ Regole di scope del master:
28
+ - una prop è identificata dal nome: se `--x` è usata, TUTTE le sue
29
+ dichiarazioni nel master vengono portate con sé, anche quelle dentro
30
+ blocchi di override (`@media (prefers-color-scheme: dark)`,
31
+ `[data-theme="dark"]`, ecc.), altrimenti il tema alternativo si rompe
32
+ in silenzio; le var() presenti nei valori degli override entrano nella
33
+ risoluzione transitiva;
34
+ - le dichiarazioni non custom (es. `color-scheme`) sono mantenute solo nei
35
+ blocchi che conservano almeno una custom property;
36
+ - le props "locali" con prefisso `--_` non fanno parte del pool globale:
37
+ sono escluse dalla scansione degli usi e dalla risoluzione transitiva
38
+ (e ignorate nel master).
39
+
40
+ Stage: PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE, quindi dopo la minificazione
41
+ (CssMinimizerPlugin, OPTIMIZE_SIZE) e dopo PurgeCSSPlugin (ADDITIONAL), ma
42
+ prima del calcolo del contenthash reale (OPTIMIZE_HASH, `realContentHash`).
43
+ Il match degli usi avviene quindi sul css realmente servito e l'hash
44
+ riflette il contenuto finale.
45
+
46
+ Watch mode: il master è parsato una sola volta e ricaricato solo se cambia
47
+ (mtime); è comunque registrato come fileDependency. Il set di props usate
48
+ viene ricalcolato a ogni build.
49
+ */
50
+
51
+ const PLUGIN_NAME = 'CustomPropsPurgeCssPlugin';
52
+
53
+ const VAR_REFS_REGEX = /var\(\s*(--[A-Za-z0-9_-]+)/g;
54
+ // banner webpack (commento `/*!`) e/o `@charset` iniziali, che devono restare in testa
55
+ const LEADING_REGEX = /^(?:\s*(?:@charset\s+["'][^"']*["']\s*;|\/\*![\s\S]*?\*\/))*\s*/;
56
+
57
+ // le props con prefisso `--_` sono locali: fuori dal pool globale
58
+ const isGlobalCustomProp = (name) => name.startsWith('--') && !name.startsWith('--_');
59
+
60
+ const varRefs = (text) => [...text.matchAll(VAR_REFS_REGEX)]
61
+ .map(m => m[1])
62
+ .filter(isGlobalCustomProp);
63
+
64
+ // pattern asset: stringa con `*` come wildcard (es. 'index.*.css') o RegExp,
65
+ // confrontati con il nome completo dell'asset (path relativo a output.path)
66
+ const toMatcher = (pattern) => {
67
+ if (pattern instanceof RegExp) {
68
+ return (name) => pattern.test(name);
69
+ }
70
+ const regex = new RegExp(
71
+ '^' + pattern.split('*').map(s => s.replace(/[.+?^${}()|[\]\\]/g, '\\$&')).join('.*') + '$'
72
+ );
73
+ return (name) => regex.test(name);
74
+ };
75
+
76
+ const hasCustomDecl = (node) => {
77
+ let found = false;
78
+ node.walkDecls(decl => {
79
+ if (isGlobalCustomProp(decl.prop)) {
80
+ found = true;
81
+ }
82
+ });
83
+ return found;
84
+ };
85
+
86
+ // rimuove commenti, custom props non incluse e blocchi rimasti senza custom props
87
+ const prune = (container, keep) => {
88
+ container.each(node => {
89
+ if (node.type === 'comment') {
90
+ node.remove();
91
+ } else if (node.type === 'decl') {
92
+ if (isGlobalCustomProp(node.prop) ? !keep.has(node.prop) : node.prop.startsWith('--')) {
93
+ node.remove();
94
+ }
95
+ } else if (node.type === 'rule' || (node.type === 'atrule' && node.nodes)) {
96
+ prune(node, keep);
97
+ if (!hasCustomDecl(node)) {
98
+ node.remove();
99
+ }
100
+ } else {
101
+ // at-rule senza blocco (@import, @charset, @layer a, b;): non pertinenti
102
+ node.remove();
103
+ }
104
+ });
105
+ };
106
+
107
+ // serializzazione compatta (senza indentazioni) per gli asset minificati
108
+ const compact = (root) => {
109
+ root.walk(node => {
110
+ node.raws.before = '';
111
+ if (node.type === 'decl') {
112
+ node.raws.between = ':';
113
+ } else {
114
+ node.raws.between = '';
115
+ node.raws.after = '';
116
+ node.raws.semicolon = false;
117
+ if (node.type === 'atrule') {
118
+ node.raws.afterName = ' ';
119
+ }
120
+ }
121
+ });
122
+ root.raws.after = '';
123
+ };
124
+
125
+ export class CustomPropsPurgeCssPlugin {
126
+
127
+ /**
128
+ * @param {object} options
129
+ * @param {string} options.definitionsFile - Percorso assoluto del file master con tutte
130
+ * le definizioni di custom properties
131
+ * @param {{ sources: (string|RegExp)[], target?: (string|RegExp) }[]} options.sets - Set
132
+ * di asset css da elaborare in modo indipendente. `sources`: nomi degli asset css
133
+ * compilati (con l'hash nel nome), stringhe con `*` come wildcard (es. `'index.*.css'`)
134
+ * o RegExp. `target`: asset in cui iniettare le definizioni (stesso formato; se il
135
+ * pattern non corrisponde ad alcun asset e non contiene `*`, viene emesso come nuovo
136
+ * asset con quel nome). Se omesso, è il primo asset che corrisponde a `sources`
137
+ * @param {string} [options.selector=':root'] - Selettore con cui sostituire i blocchi
138
+ * `:root` del master (es. `':where(html)'`); gli altri selettori (temi, ecc.) restano invariati (default: ':root')
139
+ * @param {string[]} [options.extraUsageGlobs=[]] - Glob di file css i cui usi di var()
140
+ * contano come usati in TUTTI i set: css caricati fuori dagli asset (shadow DOM dei
141
+ * web components, import `?raw`) (default: [])
142
+ * @param {(string|RegExp)[]} [options.seeds=[]] - Props da includere comunque in tutti i
143
+ * set (nomi esatti o pattern risolti sui nomi dichiarati nel master), es. override
144
+ * consumati da un altro asset (default: [])
145
+ * @param {boolean} [options.minify=false] - Serializza le definizioni in forma compatta
146
+ * (default: false)
147
+ *
148
+ * @example
149
+ * new CustomPropsPurgeCssPlugin({
150
+ * definitionsFile: path.resolve(__dirname, './app/css/custom-properties.css'),
151
+ * sets: [
152
+ * // le definizioni vengono iniettate in layout.critical.*.css (inline nel template)
153
+ * // e valgono anche per index.*.css
154
+ * { sources: ['index.*.css', 'layout.critical.*.css'], target: 'layout.critical.*.css' },
155
+ * // target omesso: definizioni iniettate nel primo asset del set
156
+ * { sources: ['admin.*.css'] }
157
+ * ],
158
+ * selector: ':root', // default
159
+ * extraUsageGlobs: [], // default
160
+ * seeds: [], // default
161
+ * minify: !isDevelopment // default: false
162
+ * })
163
+ */
164
+ constructor({
165
+ definitionsFile,
166
+ sets,
167
+ selector = ':root',
168
+ extraUsageGlobs = [],
169
+ seeds = [],
170
+ minify = false
171
+ }) {
172
+ if (!definitionsFile || !Array.isArray(sets)) {
173
+ throw new Error(`${PLUGIN_NAME}: 'definitionsFile' e 'sets' sono obbligatori`);
174
+ }
175
+ this.definitionsFile = definitionsFile;
176
+ this.sets = sets.map(set => ({
177
+ sources: set.sources.map(toMatcher),
178
+ target: set.target == null ? null : {
179
+ matcher: toMatcher(set.target),
180
+ // nome letterale, usato per emettere un nuovo asset se nessuno corrisponde
181
+ literal: typeof set.target === 'string' && !set.target.includes('*') ? set.target : null
182
+ }
183
+ }));
184
+ this.selector = selector;
185
+ this.extraUsageGlobs = extraUsageGlobs;
186
+ this.seeds = seeds;
187
+ this.minify = minify;
188
+
189
+ this.cache = { mtimeMs: null, root: null, declaredRefs: null };
190
+ }
191
+
192
+ // parsing (cachato) del master: root PostCSS + grafo nome -> Set(var() nei valori)
193
+ loadDefinitions() {
194
+ const { mtimeMs } = fs.statSync(this.definitionsFile);
195
+
196
+ if (this.cache.mtimeMs !== mtimeMs) {
197
+ const root = postcss.parse(fs.readFileSync(this.definitionsFile, 'utf8'), { from: this.definitionsFile });
198
+ const declaredRefs = new Map();
199
+
200
+ root.walkDecls(decl => {
201
+ if (!isGlobalCustomProp(decl.prop)) {
202
+ return;
203
+ }
204
+ if (!declaredRefs.has(decl.prop)) {
205
+ declaredRefs.set(decl.prop, new Set());
206
+ }
207
+ varRefs(decl.value).forEach(ref => declaredRefs.get(decl.prop).add(ref));
208
+ });
209
+
210
+ this.cache = { mtimeMs, root, declaredRefs };
211
+ }
212
+ return this.cache;
213
+ }
214
+
215
+ apply(compiler) {
216
+ const { Compilation, sources } = compiler.webpack;
217
+ const { RawSource, ReplaceSource } = sources;
218
+
219
+ compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
220
+
221
+ compilation.hooks.processAssets.tap(
222
+ { name: PLUGIN_NAME, stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE },
223
+ () => {
224
+ compilation.fileDependencies.add(this.definitionsFile);
225
+
226
+ const { root: masterRoot, declaredRefs } = this.loadDefinitions();
227
+ const declaredNames = [...declaredRefs.keys()];
228
+
229
+ // usi comuni a tutti i set: file css fuori dagli asset + seeds
230
+ const commonUsages = new Set();
231
+ for (const file of globSync(this.extraUsageGlobs, { nodir: true, absolute: true })) {
232
+ compilation.fileDependencies.add(file);
233
+ varRefs(fs.readFileSync(file, 'utf8')).forEach(ref => commonUsages.add(ref));
234
+ }
235
+ for (const seed of this.seeds) {
236
+ (typeof seed === 'string' ? [seed] : declaredNames.filter(name => seed.test(name)))
237
+ .forEach(name => commonUsages.add(name));
238
+ }
239
+
240
+ const cssAssetNames = () => compilation.getAssets()
241
+ .map(asset => asset.name)
242
+ .filter(name => /\.css$/i.test(name));
243
+
244
+ for (const set of this.sets) {
245
+ const sourceNames = cssAssetNames().filter(name => set.sources.some(match => match(name)));
246
+
247
+ if (sourceNames.length === 0) {
248
+ continue;
249
+ }
250
+
251
+ // props usate nei css del set
252
+ const used = new Set(commonUsages);
253
+ for (const name of sourceNames) {
254
+ varRefs(compilation.getAsset(name).source.source().toString()).forEach(ref => used.add(ref));
255
+ }
256
+
257
+ // chiusura transitiva (solo props dichiarate nel master)
258
+ const keep = new Set();
259
+ const queue = [...used];
260
+ while (queue.length > 0) {
261
+ const name = queue.pop();
262
+ if (!keep.has(name) && declaredRefs.has(name)) {
263
+ keep.add(name);
264
+ queue.push(...declaredRefs.get(name));
265
+ }
266
+ }
267
+
268
+ if (keep.size === 0) {
269
+ continue;
270
+ }
271
+
272
+ // definizioni: si parte da una copia del master (l'ordine originale è preservato)
273
+ const definitionsRoot = masterRoot.clone();
274
+ prune(definitionsRoot, keep);
275
+ if (this.selector !== ':root') {
276
+ definitionsRoot.walkRules(rule => {
277
+ if (rule.selector.trim() === ':root') {
278
+ rule.selector = this.selector;
279
+ }
280
+ });
281
+ }
282
+ if (this.minify) {
283
+ compact(definitionsRoot);
284
+ } else {
285
+ // primo nodo senza riga vuota iniziale
286
+ if (definitionsRoot.first) {
287
+ definitionsRoot.first.raws.before = '';
288
+ }
289
+ }
290
+ const definitions = definitionsRoot.toString().trim() + '\n';
291
+
292
+ // target
293
+ let targetName;
294
+ if (set.target == null) {
295
+ targetName = sourceNames[0];
296
+ } else {
297
+ const matching = cssAssetNames().filter(name => set.target.matcher(name));
298
+ if (matching.length > 0) {
299
+ targetName = matching[0];
300
+ if (matching.length > 1) {
301
+ compilation.warnings.push(new compiler.webpack.WebpackError(
302
+ `${PLUGIN_NAME}: il target del set corrisponde a più asset (${matching.join(', ')}), usato: ${targetName}`
303
+ ));
304
+ }
305
+ } else if (set.target.literal) {
306
+ compilation.emitAsset(set.target.literal, new RawSource(definitions));
307
+ continue;
308
+ } else {
309
+ compilation.warnings.push(new compiler.webpack.WebpackError(
310
+ `${PLUGIN_NAME}: nessun asset corrisponde al target del set (sources: ${sourceNames.join(', ')})`
311
+ ));
312
+ continue;
313
+ }
314
+ }
315
+
316
+ const original = compilation.getAsset(targetName).source;
317
+ const originalText = original.source().toString();
318
+ const position = originalText.match(LEADING_REGEX)[0].length;
319
+ const replaced = new ReplaceSource(original, PLUGIN_NAME);
320
+ // insert(pos) inserisce PRIMA del carattere in posizione `pos`
321
+ replaced.insert(position, definitions);
322
+ compilation.updateAsset(targetName, replaced);
323
+ }
324
+ }
325
+ );
326
+ });
327
+ }
328
+ }
@@ -1,3 +1,4 @@
1
+ // v.3
1
2
  import path from 'path';
2
3
  import fs from 'fs';
3
4
 
@@ -1,4 +1,5 @@
1
1
  /* plugins/inline-critical-css.mjs */
2
+ // v.3
2
3
  import HtmlWebpackPlugin from 'html-webpack-plugin';
3
4
 
4
5
  export class InlineCriticalCssPlugin {
@@ -1,4 +1,5 @@
1
1
  // ./loaders/mini-svg-data-uri-loader.cjs
2
+ // v.3
2
3
  // Questo file USA la libreria 'mini-svg-data-uri', ma è un wrapper specifico per Webpack.
3
4
 
4
5
  /* eslint-env node */
@@ -1,18 +1,21 @@
1
1
  // webpack-modules/postcss.config.mjs
2
+ // v.3
2
3
  import path from 'path';
3
4
  import { fileURLToPath } from 'url';
4
5
  import globalData from '@csstools/postcss-global-data';
5
6
  import customMedia from 'postcss-custom-media';
6
7
  import autoprefixer from 'autoprefixer';
7
8
  // import jitProps from 'postcss-jit-props';
9
+ // import OpenProps from 'open-props';
8
10
 
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = path.dirname(__filename);
11
13
 
12
14
  // NB: le custom properties NON sono gestite qui (postcss-jit-props rimosso):
13
- // ogni entry css importa direttamente custom-properties.css e le props
14
- // non utilizzate vengono rimosse in build da PurgeCSS (`variables: true`,
15
- // vedi webpack.config.mjs)
15
+ // i css del progetto contengono solo usi `var(--nome)` e le definizioni
16
+ // (solo quelle usate) sono iniettate a fine build dal plugin
17
+ // custom-props-purgecss-plugin.mjs, che parte dal file master
18
+ // custom-properties.css (vedi `useCustomPropsPlugin` in webpack.config.mjs)
16
19
 
17
20
  // NB: la minificazione è gestita da CssMinimizerPlugin (webpack.config.mjs):
18
21
  // cssnano NON va aggiunto qui, oltre a duplicare la minificazione
@@ -25,8 +28,8 @@ export default (loaderContext) => {
25
28
  const plugins = [
26
29
  // rende disponibili le @custom-media a tutti i file css
27
30
  // (il contenuto iniettato viene rimosso a fine elaborazione;
28
- // NON aggiungere qui custom-properties.css: duplicherebbe le props
29
- // in ogni asset e interferirebbe con il purge, vedi sopra)
31
+ // NON aggiungere qui custom-properties.css: le definizioni sono
32
+ // gestite dal plugin delle custom properties, vedi sopra)
30
33
  globalData({
31
34
  files: [
32
35
  path.resolve(__dirname, '../app/css/custom-media.css'),
@@ -34,6 +37,7 @@ export default (loaderContext) => {
34
37
  }),
35
38
 
36
39
  // jitProps({
40
+ // ...OpenProps,
37
41
  // files: [
38
42
  // path.resolve(__dirname, '../app/css/custom-properties.css'),
39
43
  // ]
@@ -1,7 +1,7 @@
1
1
  // webpack-modules/purgecss-setup.mjs
2
+ // v.3
2
3
  import { PurgeCSSPlugin } from 'purgecss-webpack-plugin';
3
4
  import { globSync } from 'glob';
4
- import { purgecssVariablesSafelist } from './purgecss-variables-safelist.mjs';
5
5
 
6
6
  /*
7
7
  Istanzia i plugin PurgeCSS del progetto: un passaggio stretto sui css
@@ -9,37 +9,39 @@ import { purgecssVariablesSafelist } from './purgecss-variables-safelist.mjs';
9
9
  passaggio generale su tutti gli altri asset (il purge è idempotente: il
10
10
  secondo passaggio sui critical non rimuove altro).
11
11
 
12
- NB: il plugin elabora ogni asset css SINGOLARMENTE, quindi con `variables: true`
13
- una custom property viene mantenuta solo se definita e usata nello stesso asset.
14
- Per questo, lato progetto:
15
- - ogni entry css importa direttamente custom-properties.css (che sostituisce
16
- jit-props: le props non usate vengono rimosse qui)
17
- - il chunk `shared` deve contenere solo js (il css estratto resta nel chunk
18
- dell'entry che lo importa)
19
- - le props dichiarate in un asset ma consumate altrove (override di tema,
20
- shadow DOM dei web components) vanno indicate tramite `variablesSafelist`
21
- (vedi purgecss-variables-safelist.mjs)
12
+ NB: PurgeCSS gestisce SOLO classi/id/tag e @keyframes. Le custom properties
13
+ NON sono gestite qui (`variables` è sempre false): le definizioni vengono
14
+ iniettate, solo per quelle usate, da custom-props-purgecss-plugin.mjs, che
15
+ opera sugli asset css già finalizzati (dopo questo purge e dopo la
16
+ minificazione), quindi vede anche gli usi di var() nei blocchi
17
+ `purgecss start/end ignore`, che PurgeCSS mantiene.
18
+ Il purge lavora sul singolo asset css: i @keyframes definiti in un asset
19
+ (es. `shared.css`) ma usati solo in un altro vengono rimossi (vedi
20
+ `safelist.keyframes` / `keyframes: false`).
22
21
  */
23
22
 
24
23
  /**
25
24
  * @param {object} params
26
25
  * @param {string[]} params.contentGlobs file da scansionare per le classi/id usati
27
26
  * (twig, php, js...); i file di stile vengono comunque scartati dal plugin
28
- * @param {object} params.safelist { standard, deep, greedy } — selettori sempre
27
+ * @param {object} params.safelist { standard, deep, greedy, keyframes } — selettori sempre
29
28
  * mantenuti, indipendentemente dal contenuto scansionato
30
- * @param {object} params.variablesSafelist opzioni per purgecssVariablesSafelist
31
- * ({ declarationGlobs, shadowGlobs, seeds })
32
- * @param {boolean} [params.variables=true] rimuove le custom properties non usate
33
- * @param {boolean} [params.keyframes=true] rimuove i @keyframes non referenziati
34
- * @param {boolean} [params.debug=false] logga i selettori rimossi a fine build
29
+ * @param {boolean} [params.keyframes=true] rimuove i @keyframes non referenziati (default: true)
30
+ * @param {boolean} [params.debug=false] logga i selettori rimossi a fine build (default: false)
35
31
  * @returns {object[]} plugin webpack da inserire in `plugins`
36
32
  * (critical + generale, più il logger di debug se `debug: true`)
33
+ *
34
+ * @example
35
+ * createPurgeCSSPlugins({
36
+ * contentGlobs: [path.resolve(__dirname, './app/src/**\/*.{js,mjs}')],
37
+ * safelist: { standard: [/^alert-/], deep: [/^m_/], greedy: [/^&$/] },
38
+ * keyframes: true, // default
39
+ * debug: false // default
40
+ * })
37
41
  */
38
42
  export function createPurgeCSSPlugins({
39
43
  contentGlobs,
40
44
  safelist,
41
- variablesSafelist,
42
- variables = true,
43
45
  keyframes = true,
44
46
  debug = false
45
47
  }) {
@@ -49,7 +51,7 @@ export function createPurgeCSSPlugins({
49
51
  // extractor di default (/[A-Za-z0-9_-]+/g): adeguato alle classi/tag/id del progetto,
50
52
  // non servono extractors custom
51
53
 
52
- variables,
54
+ variables: false, // vedi sopra: custom properties gestite da custom-props-purgecss-plugin.mjs
53
55
  keyframes,
54
56
  // fontFace resta false (default): i font sono referenziati solo tramite
55
57
  // var(--font-family) e purgecss non risolve le custom properties nei valori
@@ -65,13 +67,7 @@ export function createPurgeCSSPlugins({
65
67
 
66
68
  const pluginMain = new PurgeCSSPlugin({
67
69
  ...commonOptions,
68
- safelist: {
69
- ...safelist,
70
- // elenco generato: usi di var() nei blocchi `purgecss ignore` e nei css
71
- // shadow-DOM/`?raw`, override cross-asset (seeds), con chiusura transitiva
72
- // delle dipendenze (vedi purgecss-variables-safelist.mjs)
73
- variables: purgecssVariablesSafelist(variablesSafelist)
74
- }
70
+ safelist
75
71
  });
76
72
 
77
73
  return [
@@ -1,3 +1,4 @@
1
+ // v.3
1
2
  /**
2
3
  * Funzione per definire le regole per SVG
3
4
  * @returns {import('webpack').RuleSetRule[]}
@@ -1,4 +1,5 @@
1
1
  // svgo.config.js
2
+ // v.3
2
3
  import addViewBox from 'svgo-add-viewbox';
3
4
 
4
5
  // https://svgo.dev/docs/plugins/