@massimo-cassandro/minimo 1.12.1 → 1.13.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Create favicon files as described in [How to Favicon in 2024](https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs).
4
4
 
5
- The files to be processed must be in SVG format, while the generated files are in SVG (favicon), PNG (apple-touch-icon and other Android files), and ICO (another favicon for compatibility with older browsers). The `manifest.webmanifest` files are also generated, along with an optional HTML snippet with `link` tags for inserting the generated elements.
5
+ The source file can be SVG or a raster format (PNG, JPEG, WEBP, GIF, TIFF, AVIF). The generated files are PNG (apple-touch-icon and other Android files) and ICO (another favicon for compatibility with older browsers); an SVG favicon is also generated, but only when the source file itself is SVG (a vector favicon can't be derived from a raster source). The `manifest.webmanifest` files are also generated, along with an optional HTML snippet with `link` tags for inserting the generated elements (the snippet omits the SVG `link` tag when no SVG favicon was generated).
6
6
 
7
7
  Images are generated with [Sharp](https://sharp.pixelplumbing.com/), [SVGO](https://github.com/svg/svgo), and [sharp-ico](https://github.com/ssnangua/sharp-ico).
8
8
 
@@ -34,7 +34,7 @@ If it's missing, it searches for the `favicon-src.svg` file to use as the source
34
34
 
35
35
  Among others, you can specify the `small_src_img` parameter if you need to specify an image optimized for small dimensions (32px).
36
36
 
37
- The best format for source files is SVG, or alternatively PNG.
37
+ The best format for source files is SVG, or alternatively PNG/JPEG/WEBP/GIF/TIFF/AVIF.
38
38
 
39
39
  If both files are missing, an error is returned.
40
40
 
@@ -18,13 +18,21 @@ export async function createFavicons(params) {
18
18
 
19
19
  log('green', 'Creating favicons...');
20
20
 
21
- await Promise.all([
22
- await createSvg(params),
23
- await createPng(params),
24
- await createIco(params),
25
- await createWebmanifest(params), // after create png
26
- await createSnippet(params),
27
- ])
21
+ // createWebmanifest legge i png generati da createPng per calcolarne l'hash:
22
+ // deve quindi essere eseguito solo a scrittura completata
23
+ await createPng(params);
24
+
25
+ const tasks = [
26
+ createIco(params),
27
+ createWebmanifest(params),
28
+ createSnippet(params),
29
+ ];
30
+
31
+ if (params.generate_svg_favicon) {
32
+ tasks.push(createSvg(params));
33
+ }
34
+
35
+ await Promise.all(tasks)
28
36
  .then(result => {
29
37
  printResult(params);
30
38
 
@@ -9,7 +9,7 @@ export async function createPng(params) {
9
9
  ['icon-192.png', 192],
10
10
  ['icon-512.png', 512]
11
11
  ].map(file => {
12
- sharp(params.src_img)
12
+ return sharp(params.src_img)
13
13
  .resize({ width: file[1], fit: 'inside' })
14
14
  .png(params.png_parameters)
15
15
  .toFile(`${params.output_dir}/${file[0]}`)
@@ -10,7 +10,7 @@ export async function createSnippet(params) {
10
10
  if (params.snippet_language === 'ejs') {
11
11
 
12
12
  snippet_content = '<link rel="icon" href="<%= require(\'./favicon.ico\') %>" sizes="32x32"/>\n' +
13
- '<link rel="icon" href="<%= require(\'./favicon.svg\') %>" type="image/svg+xml"/>\n' +
13
+ (params.generate_svg_favicon? '<link rel="icon" href="<%= require(\'./favicon.svg\') %>" type="image/svg+xml"/>\n' : '') +
14
14
  '<link rel="apple-touch-icon" href="<%= require(\'./apple-touch-icon.png\') %>"/>\n' +
15
15
  '<link rel="manifest" href="./manifest.webmanifest"/>';
16
16
 
@@ -24,7 +24,7 @@ export async function createSnippet(params) {
24
24
 
25
25
 
26
26
  snippet_content = `<link rel="icon" href="${create_href('favicon.ico')}" sizes="32x32"/>\n` +
27
- `<link rel="icon" href="${create_href('favicon.svg')}" type="image/svg+xml"/>\n` +
27
+ (params.generate_svg_favicon? `<link rel="icon" href="${create_href('favicon.svg')}" type="image/svg+xml"/>\n` : '') +
28
28
  `<link rel="apple-touch-icon" href="${create_href('apple-touch-icon.png')}"/>\n` +
29
29
  `<link rel="manifest" href="${create_href('manifest.webmanifest')}"/>`;
30
30
 
@@ -42,7 +42,7 @@ export async function createSnippet(params) {
42
42
  const targetFileContent = fs.readFileSync(params.snippet_target_file, 'utf8'),
43
43
  regexp = /<!-- ?favicon-snippet-start ?-->(.*?)<!-- ?favicon-snippet-end ?-->/mis;
44
44
 
45
- fs.promises.writeFile(params.snippet_target_file,
45
+ await fs.promises.writeFile(params.snippet_target_file,
46
46
  targetFileContent.replace(regexp,
47
47
  `<!-- favicon-snippet-start -->\n${snippet_content}\n<!-- favicon-snippet-end -->`
48
48
  )
@@ -54,7 +54,7 @@ export async function createSnippet(params) {
54
54
 
55
55
  } else {
56
56
 
57
- fs.promises.writeFile(
57
+ await fs.promises.writeFile(
58
58
  `${params.snippet_path}/${params.snippet_name}`,
59
59
  snippet_content
60
60
  );
@@ -4,12 +4,8 @@ import * as fs from 'fs/promises';
4
4
  export async function createSvg(params) {
5
5
 
6
6
  // TODO add extra config ???
7
- await fs.readFile(params.small_src_img?? params.src_img, { encoding: 'utf8' })
8
- .then(svgString => {
7
+ const svgString = await fs.readFile(params.small_src_img?? params.src_img, { encoding: 'utf8' }),
8
+ svg_result = optimize(svgString, { multipass: true});
9
9
 
10
- const svg_result = optimize(svgString, { multipass: true});
11
-
12
- fs.writeFile(`${params.output_dir}/favicon.svg`, svg_result.data);
13
-
14
- });
10
+ await fs.writeFile(`${params.output_dir}/favicon.svg`, svg_result.data);
15
11
  }
@@ -1,7 +1,7 @@
1
1
  // web manifest
2
2
 
3
- // import * as fs from 'fs/promises';
4
3
  import * as fs from 'fs';
4
+ import * as fsPromises from 'fs/promises';
5
5
  import { createHash } from 'node:crypto'
6
6
 
7
7
 
@@ -50,15 +50,9 @@ export async function createWebmanifest(params) {
50
50
  ...(params.webmanifest_extra?? {})
51
51
  };
52
52
 
53
- // await fs.writeFile(`${params.output_dir}/manifest.webmanifest`, JSON.stringify(manifest, null, ' '));
54
-
55
- await fs.writeFile(
53
+ await fsPromises.writeFile(
56
54
  `${params.output_dir}/${params.manifest_file_name}`,
57
- JSON.stringify(manifest, null, ' '),
58
- (err) => {
59
- if (err) {throw err}
60
- // console.log('The file has been saved!');
61
- }
55
+ JSON.stringify(manifest, null, ' ')
62
56
  );
63
57
 
64
58
  }
@@ -5,10 +5,12 @@ export const default_params = {
5
5
 
6
6
  // tutti i percorsi sono relativi alla dir di lavoro
7
7
 
8
- // immagine sorgente SVG o PNG 512x512
8
+ // immagine sorgente, min. 512x512
9
+ // formati accettati: svg, png, jpg/jpeg, webp, gif, tiff/tif, avif
10
+ // il file favicon.svg viene generato solo se la sorgente utilizzata è in formato svg
9
11
  src_img: 'favicon-src.svg',
10
12
 
11
- // sorgente opzionale per immagini piccole (32px)
13
+ // sorgente opzionale per immagini piccole (32px), stessi formati di src_img
12
14
  small_src_img: null,
13
15
 
14
16
  // directory output (percorso relativo alla dir di lavoro)
@@ -2,6 +2,15 @@ import { default_params } from './default-params.mjs';
2
2
  import * as path from 'path';
3
3
  import * as fs from 'fs';
4
4
 
5
+ // formati accettati per le immagini sorgente (src_img / small_src_img)
6
+ // vettoriale: svg
7
+ // raster (letti tramite Sharp): png, jpg/jpeg, webp, gif, tiff/tif, avif
8
+ const supported_img_ext = ['svg', 'png', 'jpg', 'jpeg', 'webp', 'gif', 'tiff', 'tif', 'avif'];
9
+
10
+ function getImgExt(img_path) {
11
+ return path.extname(img_path).toLowerCase().replace(/^\./, '');
12
+ }
13
+
5
14
  export function parseParams(config_params = null, work_dir = process.cwd()) {
6
15
 
7
16
  try {
@@ -31,6 +40,22 @@ export function parseParams(config_params = null, work_dir = process.cwd()) {
31
40
  params[item] = params[item]? path.resolve(params.work_dir, params[item]) : null
32
41
  );
33
42
 
43
+ // validazione formato immagini sorgente
44
+ [params.src_img, params.small_src_img].filter(Boolean).forEach(img_path => {
45
+ const ext = getImgExt(img_path);
46
+ if (!supported_img_ext.includes(ext)) {
47
+ throw new Error(
48
+ `Formato immagine non supportato: '${img_path}'. ` +
49
+ `Formati accettati: ${supported_img_ext.join(', ')}`
50
+ );
51
+ }
52
+ });
53
+
54
+ // il file favicon.svg viene generato solo se l'immagine sorgente utilizzata
55
+ // per le versioni piccole (small_src_img, o src_img in sua assenza) è già SVG:
56
+ // da un raster non è possibile ricavare una favicon vettoriale
57
+ params.generate_svg_favicon = getImgExt(params.small_src_img?? params.src_img) === 'svg';
58
+
34
59
  // creazione dir output se non esiste
35
60
  if (!fs.existsSync(params.output_dir)){
36
61
  fs.mkdirSync(params.output_dir);
@@ -56,6 +81,7 @@ export function parseParams(config_params = null, work_dir = process.cwd()) {
56
81
 
57
82
  } catch(e) {
58
83
  console.error( e ); // eslint-disable-line
84
+ process.exit(1);
59
85
  }
60
86
 
61
87
  }
@@ -21,7 +21,7 @@ export function printResult(params) {
21
21
  [
22
22
  'apple-touch-icon.png',
23
23
  'favicon.ico',
24
- 'favicon.svg',
24
+ ...(params.generate_svg_favicon? ['favicon.svg'] : []),
25
25
  'icon-192.png',
26
26
  'icon-512.png',
27
27
  params.manifest_file_name
@@ -1,22 +1,21 @@
1
1
  {
2
2
  "__help": "https://code.visualstudio.com/docs/languages/jsconfig",
3
3
  "compilerOptions": {
4
- "checkJs": true,
5
- "target": "ES2023",
6
- "module": "ESNext",
4
+ "ignoreDeprecations": "6.0",
7
5
  "baseUrl": ".",
8
6
  "allowSyntheticDefaultImports": false,
7
+
8
+ "target": "es2023",
9
+ "module": "ES6",
9
10
  "paths": {
10
- "@frontend/*": ["./frontend/*"],
11
- "@src/*": ["frontend/src/*"],
11
+ "@css/*": ["./frontend/src/css/*"],
12
+ "@src/*": ["./frontend/src/*"],
13
+ "@icons/*": ["./frontend/icons/*"],
12
14
  "@imgs/*": ["./frontend/imgs/*"],
13
- "@icone/*": ["./frontend/icone/*"],
14
- "@backoffice/*": ["./frontend/backoffice/*"],
15
- "@minimo/*": ["./node_modules/@massimo-cassandro/minimo/src/*"]
15
+ "@frontend/*": ["./frontend/*"],
16
+ "@minimo/*": ["./node_modules/@massimo-cassandro/minimo/src/*"],
17
+ "@minimoCharts/*": ["./node_modules/@massimo-cassandro/minimo/charts/*"]
16
18
  }
17
-
18
-
19
-
20
19
  },
21
20
  "exclude": ["node_modules", "dist", "docs", "build", "public"]
22
21
  }
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "scripts": {
20
20
  "progetto JS webpack DEV": "npm 'launchChrome' && NODE_ENV=development webpack serve --config ./webpack.config.mjs #--open-app-name 'Google Chrome'",
21
- "progettop sf webpack DEV": "rm -rf ./public/_dev && NODE_ENV=development webpack --config ./frontend/webpack.config.mjs",
21
+ "progetto sf webpack DEV": "rm -rf ./public/_dev && NODE_ENV=development webpack --config ./frontend/webpack.config.mjs",
22
22
  "webpack PROD": "NODE_ENV=production webpack --config ./webpack.config.mjs --stats minimal",
23
23
  "create-favicons INIT": "npx create-favicons init",
24
24
  "create-favicons": "npx create-favicons --dir=./src/favicons",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massimo-cassandro/minimo",
3
- "version": "1.12.1",
3
+ "version": "1.13.0",
4
4
  "description": "Light css framework & utilities",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -78,6 +78,7 @@
78
78
  "@inquirer/select": "^5.2.1",
79
79
  "clipboardy": "^5.3.1",
80
80
  "sharp": "^0.35.3",
81
- "sharp-ico": "^0.1.5"
81
+ "sharp-ico": "^0.1.5",
82
+ "svgo": "^4.0.2"
82
83
  }
83
- }
84
+ }