@massimo-cassandro/minimo 1.8.4 → 1.9.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 +9 -2
- package/dev-tools/README.md +31 -0
- package/dev-tools/create-favicons/README.md +101 -0
- package/dev-tools/create-favicons/index.mjs +70 -0
- package/dev-tools/create-favicons/src/create-favicons.mjs +37 -0
- package/dev-tools/create-favicons/src/create-ico.OLD.mjs +32 -0
- package/dev-tools/create-favicons/src/create-ico.mjs +18 -0
- package/dev-tools/create-favicons/src/create-png.mjs +21 -0
- package/dev-tools/create-favicons/src/create-snippet.mjs +64 -0
- package/dev-tools/create-favicons/src/create-svg.mjs +15 -0
- package/dev-tools/create-favicons/src/create-webmanifest.mjs +64 -0
- package/dev-tools/create-favicons/src/default-params.mjs +91 -0
- package/dev-tools/create-favicons/src/init.mjs +52 -0
- package/dev-tools/create-favicons/src/parse-params.mjs +61 -0
- package/dev-tools/create-favicons/src/print-frame.mjs +56 -0
- package/dev-tools/create-favicons/src/print-result.mjs +60 -0
- package/dev-tools/create-favicons/src/remove-homedir-string.mjs +9 -0
- package/dev-tools/svg-icons-tools/README.md +75 -0
- package/dev-tools/svg-icons-tools/index.mjs +51 -0
- package/dev-tools/svg-icons-tools/src/config-manager.mjs +23 -0
- package/dev-tools/svg-icons-tools/src/create-dest-folder.mjs +20 -0
- package/dev-tools/svg-icons-tools/src/default-config.mjs +343 -0
- package/dev-tools/svg-icons-tools/src/homedir-path-to-tilde.mjs +9 -0
- package/dev-tools/svg-icons-tools/src/init.mjs +38 -0
- package/dev-tools/svg-icons-tools/src/jsx-icon-file-builder.mjs +62 -0
- package/dev-tools/svg-icons-tools/src/parse-svg-filename.mjs +19 -0
- package/dev-tools/svg-icons-tools/src/parse-svg-files.mjs +82 -0
- package/dev-tools/svg-icons-tools/src/parse-svg-markup.mjs +75 -0
- package/dev-tools/svg-icons-tools/src/print-result.mjs +26 -0
- package/dev-tools/svg-icons-tools/src/start.mjs +23 -0
- package/dev-tools/svg-icons-tools/src/svg-to-jsx.mjs +82 -0
- package/dev-tools/svg-icons-tools/src/svg-to-optimized.mjs +44 -0
- package/dev-tools/svg-icons-tools/src/svg-to-scss.mjs +72 -0
- package/dev-tools/svg-icons-tools/src/svg-to-symbols.mjs +70 -0
- package/dev-tools/svg-icons-tools/src/symbols-demo-tpl.html +110 -0
- package/dev-tools/upd@m/README.md +9 -0
- package/dev-tools/upd@m/index.mjs +72 -0
- package/dev-tools/update-version/README.md +19 -0
- package/dev-tools/update-version/convert-changelog.mjs +67 -0
- package/dev-tools/update-version/index.mjs +110 -0
- package/dev-tools/update-version/src/chooser-inquirer.mjs +43 -0
- package/dev-tools/update-version/src/create-log-row.mjs +25 -0
- package/dev-tools/update-version/src/get-cli-params.mjs +28 -0
- package/dev-tools/update-version/src/params.mjs +26 -0
- package/dev-tools/update-version/src/update-log-file.mjs +17 -0
- package/dev-tools/update-version/src/update-packageJson.mjs +10 -0
- package/dev-tools/update-version/src/update-semver.mjs +29 -0
- package/package.json +25 -7
- package/src/components/unsplash-page/fetch-unsplash-data.js +2 -2
package/README.md
CHANGED
|
@@ -20,8 +20,15 @@ For a complete install of all required webpack modules, use the install script a
|
|
|
20
20
|
```bash
|
|
21
21
|
zsh ./node_modules/@massimo-cassandro/minimo/webpack-setup/starter-install.sh
|
|
22
22
|
```
|
|
23
|
+
---
|
|
23
24
|
|
|
25
|
+
## Dev Tools
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
The [`dev-tools`](./dev-tools/README.md) directory contains a set of standalone Node.js CLI utilities for managing a web project — updating dependencies, bumping the version and changelog, and generating/optimizing SVG icons and favicons. They are published as part of the `minimo` package and exposed via the `bin` entries in `package.json` (`update-version`, `upd@m`, `svgIconsTools`, `create-favicons`); see the [Dev Tools README](./dev-tools/README.md) for the full list and usage.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Docs & Demo
|
|
31
|
+
|
|
32
|
+
[Docs](docs)
|
|
26
33
|
|
|
27
|
-
[
|
|
34
|
+
[Demo](demo-build)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Dev tools
|
|
2
|
+
|
|
3
|
+
A collection of standalone Node.js CLI utilities to help manage a web project's day-to-day chores (dependency updates, versioning/changelog, icon and favicon generation). Each tool lives in its own subfolder, works independently of the others and of the `minimo` package itself, and is documented in its own README — this page only gives a quick overview.
|
|
4
|
+
|
|
5
|
+
* [update-version](./update-version/README.md) — bumps the version in `package.json` and appends an entry to `changelog.md`
|
|
6
|
+
* [Svg Icons Tools](./svg-icons-tools/README.md) — optimizes SVG icons and combines/converts them into symbols, JSX, or SCSS variables
|
|
7
|
+
* [Create favicons](./create-favicons/README.md) — generates the full favicon set (SVG, PNG, ICO, webmanifest, HTML snippet) from a source image
|
|
8
|
+
* [upd@m](./upd@m/README.md) — updates all `@massimo-cassandro/*` packages installed in `node_modules`
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Usage of SVG adn favicons tools
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
## SVG Icons Tools
|
|
15
|
+
npx svgIconsTools --config ./path/to/svg-icons-tools.config.mjs
|
|
16
|
+
|
|
17
|
+
# create favicons
|
|
18
|
+
npx create-favicons init
|
|
19
|
+
npx create-favicons [--dir=./path/to/dir]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Adding frequently used tools in the script section of the `package.json` file:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"scripts": {
|
|
27
|
+
"update-version": "npx update-version # optional: --config=./dev-utilities.config.mjs",
|
|
28
|
+
"upd@m": "npx upd@m"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Create favicons
|
|
2
|
+
|
|
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
|
+
|
|
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.
|
|
6
|
+
|
|
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
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -D @massimo-cassandro/create-favicons
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Creating template config files (see below)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx create-favicons init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx create-favicons [--dir=./path/to/dir]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The script normally runs in the current directory, but using the optional `--dir` parameter,
|
|
28
|
+
you can specify an alternative directory (path relative to the execution directory).
|
|
29
|
+
|
|
30
|
+
The script first searches the working directory for the `create-favicons-cfg.mjs` file, which contains an object
|
|
31
|
+
with all the necessary parameters (see below).
|
|
32
|
+
|
|
33
|
+
If it's missing, it searches for the `favicon-src.svg` file to use as the source for all images, assuming the default values (see below) for all other parameters.
|
|
34
|
+
|
|
35
|
+
Among others, you can specify the `small_src_img` parameter if you need to specify an image optimized for small dimensions (32px).
|
|
36
|
+
|
|
37
|
+
The best format for source files is SVG, or alternatively PNG.
|
|
38
|
+
|
|
39
|
+
If both files are missing, an error is returned.
|
|
40
|
+
|
|
41
|
+
The default parameters are listed in detail in the file `src/create-favicons/src/default-params.mjs`,
|
|
42
|
+
and can be customized in the configuration file, which must have this form:
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// file create-favicons-cfg.mjs
|
|
46
|
+
const params = [{ /* ... */ }];
|
|
47
|
+
|
|
48
|
+
export default params;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`params` can be an object or an array. In the latter case, each element of the array corresponds to a different set of favicons.
|
|
52
|
+
|
|
53
|
+
To create a sample cfg file **in the current directory** (with all default values and their descriptions),
|
|
54
|
+
use the command:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx create-favicons init
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Remote Use
|
|
61
|
+
|
|
62
|
+
The commands can also be run without first installing the package:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
npx --package=@massimo-cassandro/dev-utilities create-favicons init
|
|
66
|
+
npx --package=@massimo-cassandro/dev-utilities create-favicons [--dir=...]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Execution
|
|
70
|
+
|
|
71
|
+
The script produces the various png and svg images, the `manifest.webmanifest` file, and an HTML snippet (or in the language specified in the `snippet_language` parameter).
|
|
72
|
+
All images are optimized with [SVGO](https://github.com/svg/svgo) and [imagemin](https://github.com/imagemin/imagemin).
|
|
73
|
+
|
|
74
|
+
All files are saved in the directory specified in `output_dir` (default: current directory).
|
|
75
|
+
|
|
76
|
+
Optionally, the snippet file can be saved in a different directory (`snippet_path`) or you can choose not to create it,
|
|
77
|
+
by setting the `snippet_name` value to `null`.
|
|
78
|
+
|
|
79
|
+
In the configuration file, you can also set the `webmanifest_extra` parameter, which allows you to add additional entries to the *manifest* file.
|
|
80
|
+
For more info: <https://developer.mozilla.org/en-US/docs/Web/Manifest>
|
|
81
|
+
|
|
82
|
+
Files generated:
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<link rel="icon" href="/favicon.ico" sizes="any"> <!-- 32×32 + 16x16 -->
|
|
86
|
+
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
|
87
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png"> <!-- 180×180 -->
|
|
88
|
+
<link rel="manifest" href="/manifest.webmanifest">
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
// manifest.webmanifest
|
|
93
|
+
{
|
|
94
|
+
"icons": [
|
|
95
|
+
{ "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
|
|
96
|
+
{ "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
See also demo files
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/* eslint-disable no-console */
|
|
4
|
+
/* eslint-env node */
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
|
|
10
|
+
import { log } from './src/print-frame.mjs';
|
|
11
|
+
|
|
12
|
+
import { parseParams } from './src/parse-params.mjs';
|
|
13
|
+
import { default_params } from './src/default-params.mjs';
|
|
14
|
+
import { createFavicons } from './src/create-favicons.mjs';
|
|
15
|
+
import { init } from './src/init.mjs';
|
|
16
|
+
|
|
17
|
+
// nome del file di configurazione, se utilizzato
|
|
18
|
+
const config_filename = 'create-favicons-cfg.mjs';
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
|
|
22
|
+
// init: creazione file cfg base
|
|
23
|
+
if(process.argv.indexOf('init') !== -1) {
|
|
24
|
+
|
|
25
|
+
init();
|
|
26
|
+
|
|
27
|
+
} else {
|
|
28
|
+
|
|
29
|
+
let work_dir;
|
|
30
|
+
const dir_param_idx = process.argv.findIndex(el => /^--dir/.test(el) );
|
|
31
|
+
|
|
32
|
+
if(dir_param_idx !== -1) {
|
|
33
|
+
[, work_dir] = process.argv[dir_param_idx].split('=');
|
|
34
|
+
|
|
35
|
+
} else {
|
|
36
|
+
work_dir = './';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
work_dir = path.resolve(process.cwd(), work_dir) ;
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if(fs.existsSync(path.resolve(work_dir, config_filename))) {
|
|
43
|
+
|
|
44
|
+
import(path.resolve(work_dir, config_filename))
|
|
45
|
+
.then((config_params) => {
|
|
46
|
+
|
|
47
|
+
const imported_params = [...(Array.isArray(config_params.default)? config_params.default : [config_params.default])];
|
|
48
|
+
|
|
49
|
+
imported_params.forEach(params_item => {
|
|
50
|
+
createFavicons(parseParams(params_item, work_dir));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
} else if(default_params.src_img && fs.existsSync(path.resolve(work_dir, default_params.src_img))) {
|
|
56
|
+
|
|
57
|
+
createFavicons(parseParams(null, work_dir));
|
|
58
|
+
|
|
59
|
+
} else {
|
|
60
|
+
|
|
61
|
+
throw new Error( `'${config_filename}' e '${default_params.src_img}' non presenti`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
} catch(err) {
|
|
69
|
+
log('bgRed', `\n ${err} \n`, 'error');
|
|
70
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
// import * as fs from 'fs';
|
|
4
|
+
// import * as path from 'path';
|
|
5
|
+
import { log } from './print-frame.mjs';
|
|
6
|
+
|
|
7
|
+
import { createIco } from './create-ico.mjs';
|
|
8
|
+
import { createPng } from './create-png.mjs';
|
|
9
|
+
import { createSvg } from './create-svg.mjs';
|
|
10
|
+
import { createWebmanifest } from './create-webmanifest.mjs';
|
|
11
|
+
import { createSnippet } from './create-snippet.mjs';
|
|
12
|
+
import { printResult } from './print-result.mjs';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export async function createFavicons(params) {
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
|
|
19
|
+
log('green', 'Creating favicons...');
|
|
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
|
+
])
|
|
28
|
+
.then(result => {
|
|
29
|
+
printResult(params);
|
|
30
|
+
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
} catch(err) {
|
|
34
|
+
log('bgRed', ` ${err} `, 'error');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import sharp from 'sharp';
|
|
3
|
+
import toIco from 'to-ico';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// https://github.com/kevva/to-ico
|
|
7
|
+
// alternativa: https://github.com/steambap/png-to-ico
|
|
8
|
+
|
|
9
|
+
// const ico_source = fs.readFileSync(`${output_dir}/apple-touch-icon.png`);
|
|
10
|
+
|
|
11
|
+
export async function createIco(params) {
|
|
12
|
+
|
|
13
|
+
await sharp(params.small_src_img?? params.src_img)
|
|
14
|
+
// .png()
|
|
15
|
+
// .then(info => console.log(info))
|
|
16
|
+
.toBuffer()
|
|
17
|
+
.then(bufferData => {
|
|
18
|
+
|
|
19
|
+
toIco([bufferData], {
|
|
20
|
+
sizes: [16, 32],
|
|
21
|
+
resize: true
|
|
22
|
+
}).then( async result => {
|
|
23
|
+
await fs.promises.writeFile(`${params.output_dir}/favicon.ico`, result, {
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
flag: "w",
|
|
26
|
+
mode: 0o666
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
})
|
|
31
|
+
.catch(err => { throw new Error( err + ' (createIco)'); });
|
|
32
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
import ico from 'sharp-ico';
|
|
3
|
+
|
|
4
|
+
// https://github.com/ssnangua/sharp-ico
|
|
5
|
+
|
|
6
|
+
export async function createIco(params) {
|
|
7
|
+
|
|
8
|
+
await ico.sharpsToIco(
|
|
9
|
+
[sharp(params.small_src_img?? params.src_img)],
|
|
10
|
+
`${params.output_dir}/favicon.ico`,
|
|
11
|
+
{
|
|
12
|
+
sizes: [16, 32],
|
|
13
|
+
// sizes: "default", // equal to [256, 128, 64, 48, 32, 24, 16]
|
|
14
|
+
resizeOptions: {}, // sharp resize optinos
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
|
|
3
|
+
export async function createPng(params) {
|
|
4
|
+
|
|
5
|
+
// https://sharp.pixelplumbing.com/api-constructor
|
|
6
|
+
// https://sharp.pixelplumbing.com/api-output#png
|
|
7
|
+
await Promise.all([
|
|
8
|
+
['apple-touch-icon.png', 180],
|
|
9
|
+
['icon-192.png', 192],
|
|
10
|
+
['icon-512.png', 512]
|
|
11
|
+
].map(file => {
|
|
12
|
+
sharp(params.src_img)
|
|
13
|
+
.resize({ width: file[1], fit: 'inside' })
|
|
14
|
+
.png(params.png_parameters)
|
|
15
|
+
.toFile(`${params.output_dir}/${file[0]}`)
|
|
16
|
+
|
|
17
|
+
}))
|
|
18
|
+
.catch(err => { throw new Error( err ); });
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
|
|
3
|
+
export async function createSnippet(params) {
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
if(params.create_snippet) {
|
|
7
|
+
|
|
8
|
+
let snippet_content;
|
|
9
|
+
|
|
10
|
+
if (params.snippet_language === 'ejs') {
|
|
11
|
+
|
|
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' +
|
|
14
|
+
'<link rel="apple-touch-icon" href="<%= require(\'./apple-touch-icon.png\') %>"/>\n' +
|
|
15
|
+
'<link rel="manifest" href="./manifest.webmanifest"/>';
|
|
16
|
+
|
|
17
|
+
//'\n<!-- <link rel="manifest" href="<%= require(\'./manifest.webmanifest.ejs\')() %>"/> -->';
|
|
18
|
+
|
|
19
|
+
} else {
|
|
20
|
+
|
|
21
|
+
const cache_buster = params.add_cache_buster? `?_=${Date.now()}` : '',
|
|
22
|
+
create_href = nome_file => params.href_template.replace('%_file_name_%', nome_file)
|
|
23
|
+
.replace('%_cache_buster_%', cache_buster);
|
|
24
|
+
|
|
25
|
+
|
|
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` +
|
|
28
|
+
`<link rel="apple-touch-icon" href="${create_href('apple-touch-icon.png')}"/>\n` +
|
|
29
|
+
`<link rel="manifest" href="${create_href('manifest.webmanifest')}"/>`;
|
|
30
|
+
|
|
31
|
+
if (params.snippet_language === 'pug') {
|
|
32
|
+
snippet_content = snippet_content.replace(/<link (.*?)\/?>/g, 'link($1)');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
snippet_content = params.snippet_template.replace('%_link_tags_%', snippet_content);
|
|
38
|
+
|
|
39
|
+
if (params.snippet_target_file) {
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(params.snippet_target_file)) {
|
|
42
|
+
const targetFileContent = fs.readFileSync(params.snippet_target_file, 'utf8'),
|
|
43
|
+
regexp = /<!-- ?favicon-snippet-start ?-->(.*?)<!-- ?favicon-snippet-end ?-->/mis;
|
|
44
|
+
|
|
45
|
+
fs.promises.writeFile(params.snippet_target_file,
|
|
46
|
+
targetFileContent.replace(regexp,
|
|
47
|
+
`<!-- favicon-snippet-start -->\n${snippet_content}\n<!-- favicon-snippet-end -->`
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error( `Il file '${params.snippet_target_file}' non esiste` );
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
} else {
|
|
56
|
+
|
|
57
|
+
fs.promises.writeFile(
|
|
58
|
+
`${params.snippet_path}/${params.snippet_name}`,
|
|
59
|
+
snippet_content
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { optimize } from 'svgo';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
|
|
4
|
+
export async function createSvg(params) {
|
|
5
|
+
|
|
6
|
+
// TODO add extra config ???
|
|
7
|
+
await fs.readFile(params.small_src_img?? params.src_img, { encoding: 'utf8' })
|
|
8
|
+
.then(svgString => {
|
|
9
|
+
|
|
10
|
+
const svg_result = optimize(svgString, { multipass: true});
|
|
11
|
+
|
|
12
|
+
fs.writeFile(`${params.output_dir}/favicon.svg`, svg_result.data);
|
|
13
|
+
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// web manifest
|
|
2
|
+
|
|
3
|
+
// import * as fs from 'fs/promises';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import { createHash } from 'node:crypto'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async function getHash(path) {
|
|
9
|
+
|
|
10
|
+
const fileHash = await new Promise((resolve, reject) => {
|
|
11
|
+
const hash = createHash('sha256');
|
|
12
|
+
const rs = fs.createReadStream(path);
|
|
13
|
+
rs.on('error', reject);
|
|
14
|
+
rs.on('data', chunk => hash.update(chunk));
|
|
15
|
+
rs.on('end', () => resolve(hash.digest('hex')));
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return fileHash;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function createWebmanifest(params) {
|
|
22
|
+
|
|
23
|
+
const sizes = [192, 512];
|
|
24
|
+
|
|
25
|
+
let files = {};
|
|
26
|
+
for await (const size of sizes) {
|
|
27
|
+
|
|
28
|
+
if(params.snippet_language === 'ejs') {
|
|
29
|
+
files[size] = `<%= require('./icon-${size}.png') %>`;
|
|
30
|
+
|
|
31
|
+
} else if(params.webmanifest_add_hash_to_files) {
|
|
32
|
+
const hashValue = await getHash(`${params.output_dir}/icon-${size}.png`);
|
|
33
|
+
files[size] = `./icon-${size}.png?_=${hashValue}`;
|
|
34
|
+
|
|
35
|
+
} else {
|
|
36
|
+
files[size] = `./icon-${size}.png`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const manifest = {
|
|
42
|
+
icons: sizes.map( size => {
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
src: files[size],
|
|
46
|
+
type: 'image/png',
|
|
47
|
+
sizes: `${size}x${size}`
|
|
48
|
+
};
|
|
49
|
+
}),
|
|
50
|
+
...(params.webmanifest_extra?? {})
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// await fs.writeFile(`${params.output_dir}/manifest.webmanifest`, JSON.stringify(manifest, null, ' '));
|
|
54
|
+
|
|
55
|
+
await fs.writeFile(
|
|
56
|
+
`${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
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export const default_params = {
|
|
2
|
+
|
|
3
|
+
// delimitazione porzione da copiare nel file init
|
|
4
|
+
/*** INIT START ***/
|
|
5
|
+
|
|
6
|
+
// tutti i percorsi sono relativi alla dir di lavoro
|
|
7
|
+
|
|
8
|
+
// immagine sorgente SVG o PNG 512x512
|
|
9
|
+
src_img: 'favicon-src.svg',
|
|
10
|
+
|
|
11
|
+
// sorgente opzionale per immagini piccole (32px)
|
|
12
|
+
small_src_img: null,
|
|
13
|
+
|
|
14
|
+
// directory output (percorso relativo alla dir di lavoro)
|
|
15
|
+
// se la dir non esiste, viene creata
|
|
16
|
+
output_dir: './output',
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// WEBMANIFEST
|
|
20
|
+
//****************************************************************************
|
|
21
|
+
// chiavi aggiuntive per webmanifest, normalmente non necessarie per il browser
|
|
22
|
+
// vedi https://developer.mozilla.org/en-US/docs/Web/Manifest
|
|
23
|
+
// esempio:
|
|
24
|
+
// webmanifest_extra: {
|
|
25
|
+
// name: "my Application",
|
|
26
|
+
// short_name: "my app",
|
|
27
|
+
// background_color: "#ffffff",
|
|
28
|
+
// theme_color: "#ffffff",
|
|
29
|
+
// display: "fullscreen"
|
|
30
|
+
// }
|
|
31
|
+
webmanifest_extra: null,
|
|
32
|
+
|
|
33
|
+
// if true, all files linked in manifest will end with
|
|
34
|
+
// '?_=[hash]'
|
|
35
|
+
webmanifest_add_hash_to_files: true,
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// SNIPPET
|
|
39
|
+
//****************************************************************************
|
|
40
|
+
|
|
41
|
+
// nome del file snippet, compresa l'estensione
|
|
42
|
+
// se questo parametro è null e anche `snippet_target_file` lo è, lo snippet non viene generato
|
|
43
|
+
// se il linguaggio è ejs il nome è sempre 'favicons.incl.ejs' e snippet_name viene ignorato
|
|
44
|
+
snippet_name: 'favicon.html',
|
|
45
|
+
|
|
46
|
+
// path (relativo a questo file) in cui salvare lo snippet
|
|
47
|
+
// se null, viene utilizzato lo stesso percorso indicato in `output_dir`
|
|
48
|
+
// se la dir non esiste, viene creata
|
|
49
|
+
snippet_path: null,
|
|
50
|
+
|
|
51
|
+
// path del file in cui scrivere le righe dello snippet
|
|
52
|
+
// questa impostazione è alternativa alle due precedenti e, se presente, queste
|
|
53
|
+
// ultime vengono ignorate.
|
|
54
|
+
// Per utilizzarla, le va assegnato il percorso del file (già esistente) nel quale
|
|
55
|
+
// scrivere le righe delle snippet.
|
|
56
|
+
// Il file deve contenere dei segnaposti per contrassegnare lo spazio entro il quale
|
|
57
|
+
// scrivere lo snippets
|
|
58
|
+
// <!-- favicon-snippet-start --><!-- favicon-snippet-end -->
|
|
59
|
+
snippet_target_file: null,
|
|
60
|
+
|
|
61
|
+
// linguaggio da utilizzare per la sintassi dello snippet html
|
|
62
|
+
// html, pug, ejs
|
|
63
|
+
snippet_language: 'ejs',
|
|
64
|
+
|
|
65
|
+
// template per la costruzione dello snippet.
|
|
66
|
+
// si tratta di una stringa (anche su più righe) in cui deve essere presente
|
|
67
|
+
// '%_link_tags_%', che verrà sostituito con i tag links dei vari file
|
|
68
|
+
snippet_template: '%_link_tags_%',
|
|
69
|
+
|
|
70
|
+
// template per la costruzione di ogni singolo attributo `href` dei tag link.
|
|
71
|
+
// deve contenere la stringa '%_file_name_%', che conterrà il nome del file,
|
|
72
|
+
// e la stringa '%_cache_buster_%' (se attivata)', che conterrà `?_=xxxx`, dove
|
|
73
|
+
// 'xxxx' rappresenta il timestamp generato per annullare la cache.
|
|
74
|
+
// Va scritto tenendo conto del linguaggio utilizzato.
|
|
75
|
+
href_template: '/%_file_name_%%_cache_buster_%',
|
|
76
|
+
|
|
77
|
+
// se true aggiunge allo snippet una query string per ovviare ad eventuali
|
|
78
|
+
// problemi di caching del browser
|
|
79
|
+
add_cache_buster: false,
|
|
80
|
+
|
|
81
|
+
//****************************************************************************
|
|
82
|
+
|
|
83
|
+
// SHARP PNG PARAMETERS
|
|
84
|
+
// parametri per i file PNG
|
|
85
|
+
// vedi https://sharp.pixelplumbing.com/api-output#png
|
|
86
|
+
png_parameters: {compressionLevel: 5, quality: 60, palette: true}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/*** INIT END ***/
|
|
90
|
+
|
|
91
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* eslint-env node */
|
|
2
|
+
|
|
3
|
+
import { URL } from 'url';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import { printFrame } from './print-frame.mjs';
|
|
8
|
+
import { remove_homedir_string } from './remove-homedir-string.mjs';
|
|
9
|
+
|
|
10
|
+
export function init() {
|
|
11
|
+
// const __filename = new URL('', import.meta.url).pathname;
|
|
12
|
+
// Will contain trailing slash
|
|
13
|
+
const src_dir = new URL('.', import.meta.url).pathname,
|
|
14
|
+
cfg_sample_file = process.cwd() + '/create-favicons-cfg.mjs';
|
|
15
|
+
|
|
16
|
+
if(fs.existsSync(cfg_sample_file)) {
|
|
17
|
+
throw `${cfg_sample_file} già presente`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
let default_params = fs.readFileSync(src_dir + './default-params.mjs', 'UTF8');
|
|
22
|
+
|
|
23
|
+
const start_string = '/*** INIT START ***/',
|
|
24
|
+
end_string = '/*** INIT END ***/',
|
|
25
|
+
init_start_text = fs.readFileSync(src_dir +'./init-start-text.txt', 'utf8');
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
default_params = default_params.substring(
|
|
30
|
+
default_params.indexOf(start_string) + start_string.length, //from
|
|
31
|
+
default_params.indexOf(end_string)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
fs.writeFileSync(
|
|
35
|
+
cfg_sample_file,
|
|
36
|
+
init_start_text.trim() + '\n\n' +
|
|
37
|
+
'const params = [{\n' +
|
|
38
|
+
default_params +
|
|
39
|
+
'\n}];\n\n' +
|
|
40
|
+
'export default params;'
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
printFrame({
|
|
44
|
+
strings: [
|
|
45
|
+
{string: '** CREATE-FAVICONS **', color: 'yellow', bold: true,},
|
|
46
|
+
{string: 'File di configurazione di generato:', color: 'green'},
|
|
47
|
+
{string: remove_homedir_string(cfg_sample_file), color: 'bgGreen'},
|
|
48
|
+
],
|
|
49
|
+
frameColor: 'green',
|
|
50
|
+
frameType: 'double'
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { default_params } from './default-params.mjs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
|
|
5
|
+
export function parseParams(config_params = null, work_dir = process.cwd()) {
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
|
|
9
|
+
const params = {...default_params, ...(config_params??{})}
|
|
10
|
+
params.work_dir = work_dir;
|
|
11
|
+
params.manifest_file_name = 'manifest.webmanifest';
|
|
12
|
+
|
|
13
|
+
// ejs overrides
|
|
14
|
+
if(params.snippet_language === 'ejs') {
|
|
15
|
+
params.manifest_file_name = 'manifest.webmanifest.ejs';
|
|
16
|
+
params.webmanifest_add_hash_to_files = false;
|
|
17
|
+
params.create_snippet = true;
|
|
18
|
+
params.snippet_name = 'favicons.incl.ejs';
|
|
19
|
+
params.snippet_target_file = null;
|
|
20
|
+
params.snippet_path = params.output_dir;
|
|
21
|
+
params.add_cache_buster = false
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
[
|
|
25
|
+
'src_img',
|
|
26
|
+
'small_src_img',
|
|
27
|
+
'snippet_path',
|
|
28
|
+
'snippet_target_file',
|
|
29
|
+
'output_dir'
|
|
30
|
+
].forEach(item =>
|
|
31
|
+
params[item] = params[item]? path.resolve(params.work_dir, params[item]) : null
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// creazione dir output se non esiste
|
|
35
|
+
if (!fs.existsSync(params.output_dir)){
|
|
36
|
+
fs.mkdirSync(params.output_dir);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// definizione snippet_path e creazione dir se necessario
|
|
40
|
+
params.create_snippet = params.snippet_name || params.snippet_target_file;
|
|
41
|
+
|
|
42
|
+
if(params.create_snippet) {
|
|
43
|
+
|
|
44
|
+
params.snippet_path = params.snippet_path?? params.output_dir;
|
|
45
|
+
|
|
46
|
+
if (!params.snippet_target_file && !fs.existsSync(params.snippet_path )){
|
|
47
|
+
fs.mkdirSync(params.snippet_path );
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
params.snippet_language = params.snippet_language.toLowerCase();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
return params;
|
|
56
|
+
|
|
57
|
+
} catch(e) {
|
|
58
|
+
console.error( e ); // eslint-disable-line
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
}
|