@massimo-cassandro/minimo 1.24.11 → 1.25.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.
@@ -81,6 +81,10 @@ const config = {
81
81
  name: 'LAYOUT',
82
82
  prefixes: ['body', 'layout'],
83
83
  },
84
+ {
85
+ name: 'UI',
86
+ prefixes: ['ui'],
87
+ },
84
88
  ],
85
89
 
86
90
 
@@ -3,8 +3,8 @@
3
3
  # IMPORTANTE: questo script è idempotente e deve restare tale.
4
4
  # Può essere rilanciato più volte sullo stesso progetto (anche via `npx starter-kit`
5
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.
6
+ # i file esistenti vengono saltati o copiati con prefisso NEW- (o _ per
7
+ # package.json e *.code-workspace).
8
8
  # Ogni modifica futura deve conservare questa proprietà.
9
9
 
10
10
  RED='\033[0;31m'
@@ -19,8 +19,8 @@ if [ -z "$ZSH_VERSION" ]; then
19
19
  fi
20
20
 
21
21
  # --no-npm: salta le installazioni npm (utile per testare rapidamente lo script)
22
- # --root / --frontend: posizione della configurazione frontend (salta il prompt,
23
- # utile per esecuzioni non interattive)
22
+ # --root / --frontend / --app: posizione della configurazione frontend (salta
23
+ # il prompt, utile per esecuzioni non interattive)
24
24
  SKIP_NPM=0
25
25
  FRONTEND_CHOICE=""
26
26
  for ARG in "$@"; do
@@ -28,6 +28,7 @@ for ARG in "$@"; do
28
28
  --no-npm) SKIP_NPM=1 ;;
29
29
  --root) FRONTEND_CHOICE=1 ;;
30
30
  --frontend) FRONTEND_CHOICE=2 ;;
31
+ --app) FRONTEND_CHOICE=3 ;;
31
32
  esac
32
33
  done
33
34
 
@@ -64,7 +65,11 @@ BLOCKED_FILES=()
64
65
 
65
66
  # Wrapper: copia solo se il file non esiste, altrimenti registra il blocco.
66
67
  # Con terzo argomento "new", se la destinazione esiste già la copia viene
67
- # comunque creata con prefisso NEW- (da integrare manualmente o rimuovere)
68
+ # comunque creata con prefisso NEW- (da integrare manualmente o rimuovere).
69
+ # Con terzo argomento "underscore", stesso comportamento ma con prefisso _
70
+ # (usato per package.json e *.code-workspace: il file esistente non viene
71
+ # toccato, il template arriva accanto con un _ davanti al nome, da integrare
72
+ # o rimuovere manualmente)
68
73
  safe_cat() {
69
74
  local src="$1"
70
75
  local dest="$2"
@@ -74,38 +79,28 @@ safe_cat() {
74
79
  return 1
75
80
  fi
76
81
  if [ -e "$dest" ]; then
77
- if [ "$mode" = "new" ]; then
78
- local new_dest="$(dirname "$dest")/NEW-$(basename "$dest")"
79
- cat "$src" >| "$new_dest"
80
- BLOCKED_FILES+=("$dest → copiato come $new_dest")
81
- else
82
- BLOCKED_FILES+=("$dest")
83
- fi
82
+ case "$mode" in
83
+ new)
84
+ local new_dest="$(dirname "$dest")/NEW-$(basename "$dest")"
85
+ cat "$src" >| "$new_dest"
86
+ BLOCKED_FILES+=("$dest → copiato come $new_dest")
87
+ ;;
88
+ underscore)
89
+ local new_dest="$(dirname "$dest")/_$(basename "$dest")"
90
+ cat "$src" >| "$new_dest"
91
+ BLOCKED_FILES+=("$dest → copiato come $new_dest")
92
+ ;;
93
+ *)
94
+ BLOCKED_FILES+=("$dest")
95
+ ;;
96
+ esac
84
97
  else
85
98
  cat "$src" > "$dest"
86
99
  fi
87
100
  }
88
101
 
89
102
  echo -e "${GREEN}...package.json${NC}"
90
- if [ ! -f package.json ]; then
91
- safe_cat "${TEMPLATES_DIR}/package-tpl.json" package.json
92
- elif [ ! -f "${TEMPLATES_DIR}/package-tpl.json" ]; then
93
- echo -e "${RED}Sorgente mancante: ${TEMPLATES_DIR}/package-tpl.json${NC}"
94
- else
95
- # package.json esistente: merge con il template, aggiungendo in fondo
96
- # le chiavi del template prefissate con '_' (ordine originale preservato)
97
- node -e '
98
- const fs = require("fs");
99
- const existing = JSON.parse(fs.readFileSync("package.json", "utf8"));
100
- const tpl = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
101
- for (const [key, value] of Object.entries(tpl)) {
102
- existing["_" + key] = value;
103
- }
104
- fs.writeFileSync("package.json", JSON.stringify(existing, null, 2) + "\n");
105
- ' "${TEMPLATES_DIR}/package-tpl.json"
106
- echo -e "${YELLOW}package.json esistente: chiavi del template aggiunte in fondo con prefisso '_' (da integrare o rimuovere)${NC}"
107
- BLOCKED_FILES+=("package.json → merge: chiavi template aggiunte con prefisso \`_\`")
108
- fi
103
+ safe_cat "${BASE_URL}/package-tpl.json" package.json underscore
109
104
 
110
105
  echo -e "${GREEN}...gitignore${NC}"
111
106
  safe_cat "${TEMPLATES_DIR}/_gitignore" .gitignore new
@@ -116,20 +111,24 @@ else
116
111
  echo -e "\n${GREEN}Where do you want to install the frontend configuration?${NC}"
117
112
  echo "1) Root directory"
118
113
  echo "2) 'frontend' Directory"
119
- read "?Choose (1 or 2) [default: 1]: " choice < /dev/tty
120
- choice=${choice:-1}
114
+ echo "3) 'app' Directory"
115
+ read "?Choose (1, 2 or 3) [default: 2]: " choice < /dev/tty
116
+ choice=${choice:-2}
121
117
  fi
122
118
 
123
- if [ "$choice" = "2" ]; then
124
- FRONTEND_INSTALL_PATH="./frontend"
125
- echo -e "${GREEN}Files will be installed in: ./frontend${NC}"
126
- else
119
+ if [ "$choice" = "1" ]; then
127
120
  FRONTEND_INSTALL_PATH="."
128
121
  echo -e "${GREEN}Files will be installed in: root directory${NC}"
122
+ elif [ "$choice" = "3" ]; then
123
+ FRONTEND_INSTALL_PATH="./app"
124
+ echo -e "${GREEN}Files will be installed in: ./app${NC}"
125
+ else
126
+ FRONTEND_INSTALL_PATH="./frontend"
127
+ echo -e "${GREEN}Files will be installed in: ./frontend${NC}"
129
128
  fi
130
129
 
131
- if [ "$FRONTEND_INSTALL_PATH" = "./frontend" ]; then
132
- mkdir -p frontend
130
+ if [ "$FRONTEND_INSTALL_PATH" != "." ]; then
131
+ mkdir -p "$FRONTEND_INSTALL_PATH"
133
132
  fi
134
133
 
135
134
 
@@ -138,8 +137,8 @@ echo -e "\n${GREEN}...config files & utilities${NC}"
138
137
  safe_cat "${TEMPLATES_DIR}/_browserslistrc" .browserslistrc new
139
138
  safe_cat "${TEMPLATES_DIR}/_editorconfig" .editorconfig new
140
139
  safe_cat "${TEMPLATES_DIR}/_prettierrc" .prettierrc new
141
- safe_cat "${TEMPLATES_DIR}/_root_htaccess" _root_htaccess
142
- safe_cat "${TEMPLATES_DIR}/_root_robots.txt" _root_robots.txt
140
+ safe_cat "${TEMPLATES_DIR}/_root_htaccess" "${FRONTEND_INSTALL_PATH}/_root_htaccess"
141
+ safe_cat "${TEMPLATES_DIR}/_root_robots.txt" "${FRONTEND_INSTALL_PATH}/_root_robots.txt"
143
142
 
144
143
  # jsconfig.json: sta SEMPRE nella root del progetto (è anche il file di
145
144
  # riferimento di vscode). I suoi path sono quindi relativi alla root: il
@@ -148,7 +147,7 @@ safe_cat "${TEMPLATES_DIR}/_root_robots.txt" _root_robots.txt
148
147
  if [ "$FRONTEND_INSTALL_PATH" = "." ]; then
149
148
  FRONTEND_REL="./"
150
149
  else
151
- FRONTEND_REL="./frontend/"
150
+ FRONTEND_REL="${FRONTEND_INSTALL_PATH}/"
152
151
  fi
153
152
  JSCONFIG_TPL="${TEMPLATES_DIR}/jsconfig.json"
154
153
  if [ ! -f "$JSCONFIG_TPL" ]; then
@@ -161,23 +160,15 @@ else
161
160
  fi
162
161
 
163
162
  # code-workspace: se un file .code-workspace esiste già (qualunque sia il nome),
164
- # le chiavi del template vengono aggiunte in fondo con prefisso '_'
163
+ # il template viene copiato accanto con un _ davanti al nome esistente
165
164
  WORKSPACE_TPL="${TEMPLATES_DIR}/__project__.code-workspace"
166
165
  WORKSPACE_FILES=( *.code-workspace(N) )
167
166
  if [ ${#WORKSPACE_FILES[@]} -gt 1 ]; then
168
- echo -e "${RED}Trovati più file .code-workspace (${WORKSPACE_FILES[*]}): merge non eseguito${NC}"
167
+ echo -e "${RED}Trovati più file .code-workspace (${WORKSPACE_FILES[*]}): copia saltata${NC}"
169
168
  elif [ ${#WORKSPACE_FILES[@]} -eq 0 ]; then
170
169
  safe_cat "$WORKSPACE_TPL" __project__.code-workspace
171
- elif [ ! -f "$WORKSPACE_TPL" ]; then
172
- echo -e "${RED}Sorgente mancante: ${WORKSPACE_TPL}${NC}"
173
170
  else
174
- WORKSPACE_FILE="${WORKSPACE_FILES[1]}"
175
- if node "${BASE_URL}/src/merge-jsonc.mjs" "$WORKSPACE_TPL" "$WORKSPACE_FILE"; then
176
- echo -e "${YELLOW}${WORKSPACE_FILE} esistente: chiavi del template aggiunte in fondo con prefisso '_' (da integrare o rimuovere)${NC}"
177
- BLOCKED_FILES+=("${WORKSPACE_FILE} → merge: chiavi template aggiunte con prefisso \`_\`")
178
- else
179
- echo -e "${RED}Merge di ${WORKSPACE_FILE} non riuscito${NC}"
180
- fi
171
+ safe_cat "$WORKSPACE_TPL" "${WORKSPACE_FILES[1]}" underscore
181
172
  fi
182
173
 
183
174
 
@@ -260,14 +251,17 @@ for CANDIDATE in "./node_modules/@massimo-cassandro/minimo" "${BASE_URL}/../..";
260
251
  done
261
252
 
262
253
  if [ -z "$MINIMO_PKG_DIR" ]; then
263
- echo -e "${RED}Sorgenti di minimo non trovati (né in ./node_modules/@massimo-cassandro/minimo né in ${BASE_URL}/../..): css di base e tokens-config non copiati${NC}"
264
- BLOCKED_FILES+=("css di base + tokens-config.mjs → sorgenti minimo non trovati")
254
+ if [ "$SKIP_NPM" = "1" ]; then
255
+ echo -e "${YELLOW}(--no-npm) css di base, index.css, tokens-config e config favicons non copiati: npm i è stato saltato, quindi @massimo-cassandro/minimo non è ancora installato in node_modules (rilancia lo script senza --no-npm per completare la copia)${NC}"
256
+ BLOCKED_FILES+=("css di base + index.css + tokens-config.mjs + create-favicons-cfg.mjs → saltati (--no-npm)")
257
+ else
258
+ echo -e "${RED}Sorgenti di minimo non trovati (né in ./node_modules/@massimo-cassandro/minimo né in ${BASE_URL}/../..): css di base, index.css, tokens-config e config favicons non copiati${NC}"
259
+ BLOCKED_FILES+=("css di base + index.css + tokens-config.mjs + create-favicons-cfg.mjs → sorgenti minimo non trovati")
260
+ fi
265
261
  else
266
262
  echo "sorgenti minimo: ${MINIMO_PKG_DIR}"
267
263
 
268
- # css di partenza del progetto: solo i file destinati a essere personalizzati.
269
- # minimo.css NON viene copiato: i moduli css del framework vanno importati dal
270
- # pacchetto (`@import '@minimo/css/...'`, vedi entry-tpl.css)
264
+ # css di partenza del progetto: solo i file destinati a essere personalizzati
271
265
  for FILE in custom-properties.css custom-media.css fonts.css; do
272
266
  safe_cat "${MINIMO_PKG_DIR}/src/${FILE}" "${FRONTEND_INSTALL_PATH}/src/css/${FILE}"
273
267
  done
@@ -275,18 +269,28 @@ else
275
269
  # config di build-tokens: collocato accanto al css generato (custom-properties.css).
276
270
  # NB: i percorsi relativi nel file vanno adattati alla sua posizione effettiva
277
271
  safe_cat "${MINIMO_PKG_DIR}/design-tokens/tokens-config-sample.mjs" "${FRONTEND_INSTALL_PATH}/src/css/tokens-config.mjs"
272
+
273
+ # entry css principale del progetto: copia diretta di minimo.css, rinominato
274
+ # index.css e collocato sulla root di installazione (non in /src)
275
+ safe_cat "${MINIMO_PKG_DIR}/src/minimo.css" "${FRONTEND_INSTALL_PATH}/index.css"
276
+
277
+ # favicons: config di default per `npx create-favicons`
278
+ safe_cat "${MINIMO_PKG_DIR}/dev-tools/create-favicons/src/default-params.mjs" "${FRONTEND_INSTALL_PATH}/favicons/create-favicons-cfg.mjs"
278
279
  fi
279
280
 
280
- # template delle entry css (globale/di pagina e critical): ogni entry deve
281
- # importare direttamente custom-properties.css (vedi commenti nei file stessi)
282
- safe_cat "${TEMPLATES_DIR}/entry-tpl.css" "${FRONTEND_INSTALL_PATH}/src/entry-tpl.css"
283
- safe_cat "${TEMPLATES_DIR}/entry-tpl.critical.css" "${FRONTEND_INSTALL_PATH}/src/entry-tpl.critical.css"
281
+ # entry css critical (inline nei template html) e entry js, sulla root di
282
+ # installazione accanto a index.css
283
+ safe_cat "${TEMPLATES_DIR}/layout-critical.css" "${FRONTEND_INSTALL_PATH}/src/css/layout-critical.css"
284
+ safe_cat "${TEMPLATES_DIR}/app_index.js" "${FRONTEND_INSTALL_PATH}/index.js"
285
+
286
+ # favicons: istruzioni d'uso di `npx create-favicons`
287
+ safe_cat "${TEMPLATES_DIR}/favicons-readme.md" "${FRONTEND_INSTALL_PATH}/favicons/readme.md"
284
288
 
285
289
  set +C
286
290
 
287
291
  # Riepilogo finale dei file già esistenti
288
292
  if [ ${#BLOCKED_FILES[@]} -gt 0 ]; then
289
- echo -e "\n${YELLOW}*** File già esistenti (non copiati o copiati con prefisso NEW-): ***${NC}"
293
+ echo -e "\n${YELLOW}*** File già esistenti (non copiati o copiati con prefisso NEW-/_): ***${NC}"
290
294
  for F in "${BLOCKED_FILES[@]}"; do
291
295
  echo -e "${YELLOW} ✗ $F${NC}"
292
296
  done
@@ -58,7 +58,7 @@
58
58
 
59
59
  ],
60
60
  // Do not ignore node_modules css files, which is ignored by default
61
- "cssvar.ignore": [],
61
+ // "cssvar.ignore": [], // decomment if you are using open props
62
62
 
63
63
  // add support for autocomplete in JS or JS like files
64
64
  "cssvar.extensions": [
@@ -0,0 +1 @@
1
+ import './index.css';
@@ -0,0 +1,5 @@
1
+ Eseguire (dalla root del progetto):
2
+
3
+ ```
4
+ npx create-favicons --dir=./favicons # o ./frontend/favicons, ./app/favicons
5
+ ```
@@ -9,14 +9,16 @@
9
9
  "target": "es2023",
10
10
  "module": "ES6",
11
11
  "paths": {
12
- "@css/*": ["./src/css/*"],
13
- "@src/*": ["./src/*"],
14
- "@icons/*": ["./icons/*"],
15
- "@imgs/*": ["./imgs/*"],
16
- "@frontend/*": ["./*"],
17
- "@minimo/*": ["__NODE_MODULES__/@massimo-cassandro/minimo/src/*"],
18
- "@minimoCharts/*": ["__NODE_MODULES__/@massimo-cassandro/minimo/charts/*"]
12
+ "@css/*": ["app/css/*"],
13
+ "@src/*": ["app/src/*"],
14
+ "@icons/*": ["app/icons/*"],
15
+ "@imgs/*": ["app/imgs/*"],
16
+
17
+ "@frontend/*": ["fromtend/*"],
18
+
19
+ "@minimo/*": ["./node_modules/@massimo-cassandro/minimo/src/*"],
20
+ "@minimoCharts/*": ["./node_modules/@massimo-cassandro/minimo/charts/*"]
19
21
  }
20
22
  },
21
- "exclude": ["__NODE_MODULES__", "dist", "docs", "build", "public"]
23
+ "exclude": ["node_modules", "dist", "docs", "build", "public"]
22
24
  }
@@ -1,5 +1,6 @@
1
1
  /*
2
- Template di entry css critical — rinominare e adattare.
2
+ Entry css critical di default (index-critical.css) — adattare al progetto.
3
+ Per ulteriori entry critical specifiche di pagina, copiare e rinominare.
3
4
 
4
5
  Le entry con suffisso `.critical` generano asset destinati a essere
5
6
  INLINATI nei template html: devono restare AUTOSUFFICIENTI e minimi.
@@ -150,12 +150,12 @@ const CopyWebpackPluginPatterns = [
150
150
  // =>> entries
151
151
  // NB: percorsi dalla root del progetto
152
152
  const entries = {
153
- 'xxxxxx': './src/index.js'
153
+ 'xxxxxx': './index.js'
154
154
 
155
155
  // css critici inlinati nei template html: il suffisso `.critical` nel nome
156
156
  // della entry attiva l'istanza PurgeCSS dedicata con purge stretto
157
157
  // (vedi purgecss-setup.mjs) e la regola dedicata in css-rules.mjs
158
- // ,'xxxxxx.critical': './src/xxxxxx.critical.css'
158
+ // ,'xxxxxx.critical': './index-critical.css'
159
159
  };
160
160
 
161
161
 
@@ -410,6 +410,7 @@ const config = {
410
410
  // webpack.config.mjs NON è in una sottodirectory)
411
411
  // path.resolve(__dirname, '../templates/**/*.twig'),
412
412
  // path.resolve(__dirname, '../src/**/*.php'),
413
+ path.resolve(__dirname, './index.js'), // entry js di default (vedi starter-install.sh)
413
414
  path.resolve(__dirname, './src/**/*.{js,mjs,jsx}'),
414
415
  path.resolve(__dirname, './src/**/*.ejs'), // template html di webpack
415
416
  path.resolve(__dirname, './error-pages/**/*.js'),
@@ -449,6 +450,8 @@ const config = {
449
450
  // delle dipendenze (vedi webpack-modules/purgecss-variables-safelist.mjs)
450
451
  variablesSafelist: {
451
452
  declarationGlobs: [
453
+ path.resolve(__dirname, './index.css'), // entry css di default (vedi starter-install.sh)
454
+ path.resolve(__dirname, './index-critical.css'),
452
455
  path.resolve(__dirname, './src/**/*.css'),
453
456
  path.resolve(__dirname, './error-pages/**/*.css'),
454
457
  `${minimo_path}/src/**/*.css`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massimo-cassandro/minimo",
3
- "version": "1.24.11",
3
+ "version": "1.25.0",
4
4
  "description": "Light css framework & utilities",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -14,10 +14,11 @@ import { parseDomString } from './parseDomString.js';
14
14
  * @property {string | string[]} [classname] - Alias for `className`.
15
15
  * @property {string | null} [id] - Unique element ID.
16
16
  * @property {[string, *] | [string, *][] | Object<string, *>} [attrs] - Attributes: a `[name, value]` pair, array of pairs, or `{name: value}` object.
17
- * @property {string | number | Function | Element | DomBuilderItem[] | null} [content] - Element content.
17
+ * @property {string | number | Function | Node | DomBuilderItem[] | null} [content] - Element content.
18
18
  * A string or number is set as plain text (`textContent`) unless it contains `<`, in which case it is
19
19
  * treated as markup: sanitized and inserted via the native Sanitizer API (`Element.setHTML`) where
20
- * supported, falling back to raw `innerHTML` on browsers without it.
20
+ * supported, falling back to raw `innerHTML` on browsers without it. A `Node` (an `Element`, a
21
+ * `DocumentFragment`, ...) is appended as-is, either directly or returned from a function.
21
22
  * @property {string | number} [text] - Literal text node shorthand: inserts a plain `Text` node (never
22
23
  * parsed as markup) in place of an element, so it can sit as a sibling of tags within `children`/`content`
23
24
  * arrays. Mutually exclusive with `tag`/`content`/`children`: when set (and `tag` is absent), every other
@@ -44,7 +45,7 @@ import { parseDomString } from './parseDomString.js';
44
45
  * className: 'xxx' | ['class1', 'class2'], // also `class`
45
46
  * id: 'element-id',
46
47
  * attrs: [attr_name, attr_value] | [[...], [...]] | {name: value},
47
- * content: 'xxx' | 'xxx <strong>yyy</strong>' | 123 | domBuilder Array | function | Element, // see DomBuilderItem.content above
48
+ * content: 'xxx' | 'xxx <strong>yyy</strong>' | 123 | domBuilder Array | function | Node, // see DomBuilderItem.content above
48
49
  * condition: true | false,
49
50
  * callback: el => ...,
50
51
  * children: [...]
@@ -89,7 +90,7 @@ import { parseDomString } from './parseDomString.js';
89
90
  * - `className` / `class` / `classname` {string | string[]} - CSS class(es): a single string or an array (falsy values filtered out).
90
91
  * - `id` {string | null} - Unique element ID.
91
92
  * - `attrs` {[string, *] | [string, *][] | Object<string, *>} - Attributes: a `[name, value]` pair, array of pairs, or `{name: value}` object.
92
- * - `content` {string | number | Function | Element | DomBuilderItem[] | null} - Element content. A string/number is set as `textContent` unless it contains `<`, in which case it's sanitized and inserted as markup (`Element.setHTML`, falling back to `innerHTML`).
93
+ * - `content` {string | number | Function | Node | DomBuilderItem[] | null} - Element content. A string/number is set as `textContent` unless it contains `<`, in which case it's sanitized and inserted as markup (`Element.setHTML`, falling back to `innerHTML`). A `Node` (`Element`, `DocumentFragment`, ...), passed directly or returned from a function, is appended as-is.
93
94
  * - `text` {string | number} - Literal text node shorthand: inserts a plain `Text` node (never parsed as markup) as a sibling of other items, in place of an element. Mutually exclusive with `tag`/`content`/`children`.
94
95
  * - `condition` {boolean} - When false, the element (or text node) is skipped. Default `true`.
95
96
  * - `callback` {(function(HTMLElement|Text): void) | null} - Invoked after the element (or text node) is created.
@@ -232,16 +233,21 @@ export function domBuilder(structureArray = [], parent, options = {}) {
232
233
 
233
234
  } else {
234
235
 
235
- /** @type {string | Element | null} */
236
+ /** @type {string | Node | null} */
236
237
  let content = null;
237
238
  if (typeof safeItem.content === 'function') {
238
239
  content = safeItem.content();
239
240
 
241
+ } else if (safeItem.content instanceof Node) {
242
+ content = safeItem.content;
243
+
240
244
  } else if (safeItem.content != null) {
241
245
  content = String(safeItem.content);
242
246
  }
243
247
 
244
- if (content instanceof Element) {
248
+ if (content instanceof Node) {
249
+ // Element, DocumentFragment, Text, ... appended as-is (a DocumentFragment's
250
+ // children are moved into `el`, emptying the fragment, per native DOM behavior)
245
251
  el.appendChild(content);
246
252
 
247
253
  } else if (content != null) {
@@ -1,2 +0,0 @@
1
- l'aggiunta dell'undescore davanti a proprietà di package.json o __project__.code-workspace se già esistenti è fonte di confusione
2
-
@@ -1,144 +0,0 @@
1
- #!/usr/bin/env node
2
- // Merge JSONC: aggiunge in fondo al file di destinazione le chiavi di primo
3
- // livello del template, prefissate con '_', preservando commenti e
4
- // formattazione di entrambi i file.
5
- // Uso: node merge-jsonc.mjs <template> <destinazione>
6
-
7
- import fs from 'fs';
8
-
9
- // Analizza un testo JSONC ignorando stringhe e commenti: individua
10
- // l'oggetto principale, le virgole a profondità 1 e la posizione
11
- // dell'ultimo carattere significativo (non spazio, non commento)
12
- function scan(text) {
13
- let depth = 0;
14
- let open = -1;
15
- let close = -1;
16
- let last = -1;
17
- const commas = [];
18
- let i = 0;
19
- while (i < text.length) {
20
- const c = text[i];
21
- const next = text[i + 1];
22
- if (c === '"') {
23
- i++;
24
- while (i < text.length && text[i] !== '"') {
25
- if (text[i] === '\\') i++;
26
- i++;
27
- }
28
- last = i;
29
- } else if (c === '/' && next === '/') {
30
- while (i < text.length && text[i] !== '\n') i++;
31
- continue;
32
- } else if (c === '/' && next === '*') {
33
- i += 2;
34
- while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i++;
35
- i++;
36
- } else {
37
- if (c === '{' || c === '[') {
38
- if (depth === 0 && open === -1 && c === '{') open = i;
39
- depth++;
40
- } else if (c === '}' || c === ']') {
41
- depth--;
42
- if (depth === 0 && close === -1) close = i;
43
- } else if (c === ',' && depth === 1) {
44
- commas.push(i);
45
- }
46
- if (!/\s/.test(c)) last = i;
47
- }
48
- i++;
49
- }
50
- return { open, close, commas, last };
51
- }
52
-
53
- // Posizione della prima doppia virgoletta fuori dai commenti
54
- function firstQuotePos(text) {
55
- let i = 0;
56
- while (i < text.length) {
57
- const c = text[i];
58
- const next = text[i + 1];
59
- if (c === '/' && next === '/') {
60
- while (i < text.length && text[i] !== '\n') i++;
61
- } else if (c === '/' && next === '*') {
62
- i += 2;
63
- while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i++;
64
- i += 2;
65
- } else if (c === '"') {
66
- return i;
67
- } else {
68
- i++;
69
- }
70
- }
71
- return -1;
72
- }
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
-
102
- const [tplPath, destPath] = process.argv.slice(2);
103
-
104
- try {
105
- const tpl = fs.readFileSync(tplPath, 'utf8');
106
- const dest = fs.readFileSync(destPath, 'utf8');
107
- const t = scan(tpl);
108
- const d = scan(dest);
109
- if (t.open < 0 || t.close < 0) throw new Error(tplPath + ': oggetto JSON non trovato');
110
- if (d.open < 0 || d.close < 0) throw new Error(destPath + ': oggetto JSON non trovato');
111
-
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
- );
116
-
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)
120
- const renamed = [];
121
- for (const entry of topLevelEntries(tpl, t)) {
122
- const cleaned = entry.replace(/^\s+/, '').replace(/\s+$/, '');
123
- const key = keyName(cleaned);
124
- if (key === null || destKeys.has('_' + key)) continue;
125
- const q = firstQuotePos(cleaned);
126
- renamed.push('\n ' + cleaned.slice(0, q + 1) + '_' + cleaned.slice(q + 1));
127
- }
128
- if (renamed.length === 0) process.exit(0);
129
-
130
- // aggiunge le voci prima della chiusura dell'oggetto principale della
131
- // destinazione, inserendo una virgola dopo l'ultima voce esistente se serve
132
- const dBody = dest.slice(d.open + 1, d.close);
133
- const b = scan(dBody);
134
- const needsComma = b.last >= 0 && dBody[b.last] !== ',';
135
- const insertAt = d.open + 1 + b.last + 1;
136
- const out = needsComma
137
- ? dest.slice(0, insertAt) + ',' + dest.slice(insertAt, d.close) + renamed.join(',') + '\n' + dest.slice(d.close)
138
- : dest.slice(0, d.close) + renamed.join(',') + '\n' + dest.slice(d.close);
139
-
140
- fs.writeFileSync(destPath, out);
141
- } catch (err) {
142
- console.error('merge-jsonc: ' + err.message);
143
- process.exit(1);
144
- }
@@ -1,24 +0,0 @@
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
- /* ... */