@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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jsx_icon_file_builder
|
|
3
|
+
*
|
|
4
|
+
* Function to create JSX icon files
|
|
5
|
+
*
|
|
6
|
+
* The `parsed_svg` argument is the object returned by the parse function
|
|
7
|
+
* and contains:
|
|
8
|
+
* - svg: the whole optimized svg markup (including the svg tag),
|
|
9
|
+
* - viewbox: the viewbox attribute content,
|
|
10
|
+
* - svg_content: the optimized svg markup without the svg tag)
|
|
11
|
+
* - classes: the aspect ratio class as per the `non_square_icons_classes` parameter and or
|
|
12
|
+
* the 'fill` or `stroke` class as per the `icon_type_class` parameter
|
|
13
|
+
* - icon_type: icon type string: `fill` or `stroke`
|
|
14
|
+
* - filename: the name of the svg file without extension and with the `remove_prefix` strings removed
|
|
15
|
+
* - filename_camel_case: the filename in camel case (nb: dashes follewed ny numbers are converted to underscores)
|
|
16
|
+
* - filename_pascal_case: similar to `filename_camel_case` but with the first letter capitalized
|
|
17
|
+
*
|
|
18
|
+
* The function must return an object with the following properties:
|
|
19
|
+
* - component_name: the name of the component
|
|
20
|
+
* - jsx_content: the jsx content of the component
|
|
21
|
+
* - filename: the name of the jsx file (including extension) to be saved (default: `component_name` + '.jsx')
|
|
22
|
+
*
|
|
23
|
+
*
|
|
24
|
+
* NB: Since it is assumed that most attributes in the svg content have been removed
|
|
25
|
+
* during optimization, no further cleanup is performed IN THIS VERSION and therefore it is not guaranteed
|
|
26
|
+
* that `parsed_svg.content` is valid JSX markup.
|
|
27
|
+
* If you decide to keep some attributes in the SVGO options, you should check the svg content and, if possible,
|
|
28
|
+
* modify the function accordingly. In the function below, the `replace` method,
|
|
29
|
+
* applied to `parsed_svg.svg_content`, is an example of how to temporarily solve this problem.
|
|
30
|
+
*
|
|
31
|
+
* NB: this release doesn't include any prettify feature.
|
|
32
|
+
*
|
|
33
|
+
* @param {*} parsed_svg
|
|
34
|
+
* @returns
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
// TODO add prettify feature
|
|
38
|
+
// TODO svg to jsx
|
|
39
|
+
|
|
40
|
+
export function jsx_icon_file_builder(parsed_svg) {
|
|
41
|
+
|
|
42
|
+
// adding `Icon` suffix to the component name
|
|
43
|
+
const component_name = `${parsed_svg.filename_pascal_case}Icon`;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
component_name: component_name,
|
|
47
|
+
filename: `${component_name}.jsx`,
|
|
48
|
+
jsx_content:
|
|
49
|
+
`export function ${component_name}({className, role, title, ...rest}) {
|
|
50
|
+
return <svg viewBox="${parsed_svg.viewbox}"
|
|
51
|
+
role={role? role : 'image'}
|
|
52
|
+
aria-hidden={title? null : 'true'}
|
|
53
|
+
className={['icona', ${parsed_svg.classes.map(cls => `'${cls}'`)?? []}, ...(className? [className] : [])].join(' ') || null}
|
|
54
|
+
{...rest}
|
|
55
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
+
>
|
|
57
|
+
{title && <title>{title}</title>}
|
|
58
|
+
${parsed_svg.svg_content.replace(/ class=/g, ' className=').replace(/ fill-rule=/g, ' fillRule=')}
|
|
59
|
+
</svg>;
|
|
60
|
+
}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { configManager } from './config-manager.mjs';
|
|
2
|
+
|
|
3
|
+
// remove extension and normalize filename
|
|
4
|
+
export function parseSvgFilename(filename) {
|
|
5
|
+
|
|
6
|
+
const cfg = configManager.getCfg();
|
|
7
|
+
|
|
8
|
+
let parsed_filename = filename.split('.').slice(0, -1).join('.') // remove extension
|
|
9
|
+
.replaceAll(' ', '-'); // replace spaces with hyphens
|
|
10
|
+
|
|
11
|
+
// removing filename prefixes
|
|
12
|
+
cfg.remove_prefix.forEach(prefix => {
|
|
13
|
+
if(parsed_filename.startsWith(prefix)) {
|
|
14
|
+
parsed_filename = parsed_filename.slice(prefix.length);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return parsed_filename;
|
|
19
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { parseSvgMarkup } from './parse-svg-markup.mjs';
|
|
4
|
+
import { parseSvgFilename } from './parse-svg-filename.mjs';
|
|
5
|
+
import { configManager } from './config-manager.mjs';
|
|
6
|
+
import { printResult } from './print-result.mjs';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export function parseSvgFiles(parseModeKey, callback) {
|
|
10
|
+
/*
|
|
11
|
+
parseModeKey: one of 'symbols', 'jsx' or 'optimize' (same keys of config obj)
|
|
12
|
+
*/
|
|
13
|
+
const cfg = configManager.getCfg();
|
|
14
|
+
let fileCount = 0;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const parseFolder = (folder, svgType) => {
|
|
18
|
+
folder = path.resolve(cfg.work_dir, folder);
|
|
19
|
+
|
|
20
|
+
if(fs.existsSync(folder)) {
|
|
21
|
+
|
|
22
|
+
fs.readdirSync(folder).forEach(file => {
|
|
23
|
+
if(file.endsWith('.svg')) {
|
|
24
|
+
|
|
25
|
+
fileCount++;
|
|
26
|
+
const filepath = path.resolve(folder, file),
|
|
27
|
+
parsedSvg = parseSvgMarkup(filepath, svgType, cfg[parseModeKey].svgo_config);
|
|
28
|
+
|
|
29
|
+
parsedSvg.filename = parseSvgFilename(file);
|
|
30
|
+
parsedSvg.filename_camel_case = parsedSvg.filename
|
|
31
|
+
.replace(/-([a-z])/g, (part) => part[1].toUpperCase())
|
|
32
|
+
.replace(/-([0-9])/g, (part) => `_${part[1]}`);
|
|
33
|
+
|
|
34
|
+
parsedSvg.filename_pascal_case = parsedSvg.filename_camel_case.at(0).toUpperCase() +
|
|
35
|
+
parsedSvg.filename_camel_case.substring(1);
|
|
36
|
+
|
|
37
|
+
callback(parsedSvg);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
} else {
|
|
42
|
+
throw new Error( `Folder “${folder}” doesn't exist` );
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
|
|
50
|
+
if(['symbols', 'jsx', 'optimize'].indexOf(parseModeKey) === -1) {
|
|
51
|
+
throw new Error( `“${parseModeKey}” type is not mapped. It must be one of 'symbols', 'jsx' or 'optimize'` );
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// source_folders is an array and not an object? (not used in the current version)
|
|
55
|
+
if(Array.isArray(cfg[parseModeKey].source_folders) ) {
|
|
56
|
+
|
|
57
|
+
cfg[parseModeKey].source_folders.forEach(folder => {
|
|
58
|
+
parseFolder(folder, null);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
} else {
|
|
62
|
+
|
|
63
|
+
for(const svgType in cfg[parseModeKey].source_folders) {
|
|
64
|
+
|
|
65
|
+
const foldersArray = cfg[parseModeKey].source_folders[svgType];
|
|
66
|
+
|
|
67
|
+
if(!Array.isArray(foldersArray) ) {
|
|
68
|
+
throw new Error( `“${foldersArray}” must be an array` );
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
foldersArray.forEach(folder => {
|
|
72
|
+
parseFolder(folder, svgType);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return fileCount;
|
|
78
|
+
|
|
79
|
+
} catch(e) {
|
|
80
|
+
printResult( e, 'error' );
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { optimize } from 'svgo';
|
|
2
|
+
import { configManager } from './config-manager.mjs';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
|
|
5
|
+
// https://svgo.dev/docs/introduction/
|
|
6
|
+
export function parseSvgMarkup(filepath, svg_type, custom_svgo_config = null) {
|
|
7
|
+
const cfg = configManager.getCfg();
|
|
8
|
+
|
|
9
|
+
let svgString = fs.readFileSync(filepath, 'utf8');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// remove pallets
|
|
13
|
+
cfg.pallets.forEach(pallet => {
|
|
14
|
+
svgString = svgString.replace(pallet, '');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// if present, custom_svgo_config ovverrides cfg.svgo_config
|
|
18
|
+
const optimized = optimize(svgString, custom_svgo_config? custom_svgo_config : cfg.svgo_config),
|
|
19
|
+
icon_type_class = svg_type? cfg.icon_type_class?.[svg_type]?? null : null,
|
|
20
|
+
classes = icon_type_class? [icon_type_class] : [];
|
|
21
|
+
|
|
22
|
+
// extracting viewbox
|
|
23
|
+
const viewbox = optimized.data.match(/viewBox=(?:"|')(\d+ \d+ \d+ \d+)(?:"|')/mi)[1];
|
|
24
|
+
|
|
25
|
+
// duotone classes
|
|
26
|
+
if(Object.keys(cfg.opacity_classes?? {}).length) {
|
|
27
|
+
optimized.data = optimized.data.replace(/(?:fill-)?opacity=(?:'|")0?\.(\d+)(?:'|")/g, (match, p1) => {
|
|
28
|
+
const opacity_value = Math.round(+((p1 + '000').slice(0, 3)) / 100);
|
|
29
|
+
|
|
30
|
+
let opacity_key = Object.keys(cfg.opacity_classes).reduce((prev, curr) => {
|
|
31
|
+
curr = +curr;
|
|
32
|
+
return (Math.abs(curr - opacity_value) < Math.abs(prev - opacity_value) ? curr : prev);
|
|
33
|
+
});
|
|
34
|
+
return `class='${cfg.opacity_classes[opacity_key]}'`;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// non-square icons
|
|
39
|
+
if(cfg.non_square_icons_classes.length ) {
|
|
40
|
+
|
|
41
|
+
const getAspectRatio = (w, h) => Math.round(w / h * 100) / 100
|
|
42
|
+
,[,,width, height] = viewbox.split(' ')
|
|
43
|
+
;
|
|
44
|
+
|
|
45
|
+
if(width !== height) {
|
|
46
|
+
const aspect_ratio = getAspectRatio(+width, +height);
|
|
47
|
+
|
|
48
|
+
const aspect_ratio_class_idx = cfg.non_square_icons_classes.map(i => getAspectRatio(i[0])).reduce((prev, curr, idx) => {
|
|
49
|
+
curr = idx;
|
|
50
|
+
const prev_value = prev !== null? cfg.non_square_icons_classes[prev][0] : 0;
|
|
51
|
+
return Math.abs(cfg.non_square_icons_classes[curr][0] - aspect_ratio) < Math.abs(prev_value - aspect_ratio) ? curr : prev;
|
|
52
|
+
}, null);
|
|
53
|
+
|
|
54
|
+
classes.push(cfg.non_square_icons_classes[aspect_ratio_class_idx][1]);
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
// extra classes are immediately assigned to the optimized svg markup
|
|
61
|
+
if(classes.length) {
|
|
62
|
+
optimized.data = optimized.data.replace(/<svg/, `<svg class="${classes.join(' ')}"`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// get svg content
|
|
66
|
+
const svg_content = optimized.data.match(/<svg(?:.*?)>(.*?)<\/svg>/mi)[1];
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
svg: optimized.data,
|
|
70
|
+
viewbox: viewbox,
|
|
71
|
+
svg_content: svg_content,
|
|
72
|
+
classes: classes,
|
|
73
|
+
icon_type: svg_type // fill or stroke
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { styleText } from 'node:util';
|
|
3
|
+
import { configManager } from './config-manager.mjs';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/console#styling_console_output
|
|
7
|
+
// https://nodejs.org/api/util.html#customizing-utilinspect-colors
|
|
8
|
+
|
|
9
|
+
export function printResult(text, mode = 'info') {
|
|
10
|
+
const cfg = configManager.getCfg()
|
|
11
|
+
,style = Array.isArray(cfg.console_colors[mode])? cfg.console_colors[mode] : [cfg.console_colors[mode]]
|
|
12
|
+
,isBg = style.reduce((acc, val) => acc || /^(bg)/.test(val), false)
|
|
13
|
+
;
|
|
14
|
+
|
|
15
|
+
if(isBg) {
|
|
16
|
+
text = ` ${text} `;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let message = styleText(cfg.console_colors[mode], text);
|
|
20
|
+
|
|
21
|
+
if(isBg) {
|
|
22
|
+
message = `\n${message}\n`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log( message);
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { svg_to_symbols } from './svg-to-symbols.mjs';
|
|
2
|
+
import { svg_to_jsx } from './svg-to-jsx.mjs';
|
|
3
|
+
import { svg_to_optimized } from './svg-to-optimized.mjs';
|
|
4
|
+
import { svg_to_scss } from './svg-to-scss.mjs';
|
|
5
|
+
// import * as fs from 'fs';
|
|
6
|
+
// import { configManager } from './config-manager.mjs';
|
|
7
|
+
|
|
8
|
+
import { printResult } from './print-result.mjs';
|
|
9
|
+
|
|
10
|
+
export async function start() {
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
printResult('SVG-ICONS-TOOLS start...');
|
|
14
|
+
|
|
15
|
+
await svg_to_jsx();
|
|
16
|
+
await svg_to_optimized();
|
|
17
|
+
svg_to_symbols();
|
|
18
|
+
svg_to_scss();
|
|
19
|
+
|
|
20
|
+
printResult('SVG-ICONS-TOOLS end', 'success');
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { configManager } from './config-manager.mjs';
|
|
4
|
+
import { parseSvgFiles } from './parse-svg-files.mjs';
|
|
5
|
+
import { printResult } from './print-result.mjs';
|
|
6
|
+
import { homedir_path_to_tilde } from './homedir-path-to-tilde.mjs';
|
|
7
|
+
import { jsx_icon_file_builder } from './jsx-icon-file-builder.mjs';
|
|
8
|
+
import { create_dest_folder } from './create-dest-folder.mjs';
|
|
9
|
+
|
|
10
|
+
export async function svg_to_jsx() {
|
|
11
|
+
|
|
12
|
+
const cfg = configManager.getCfg(),
|
|
13
|
+
cfg_obj = cfg.jsx,
|
|
14
|
+
icon_builder = cfg_obj.custom_icon_builder && typeof cfg_obj.custom_icon_builder === 'function'?
|
|
15
|
+
cfg_obj.custom_icon_builder : jsx_icon_file_builder;
|
|
16
|
+
|
|
17
|
+
let dest_folder = '', fileCount = 0, component_list = [];
|
|
18
|
+
|
|
19
|
+
const source_folders_length = Object.keys(cfg_obj.source_folders)
|
|
20
|
+
.reduce((acc, key) => acc + cfg_obj.source_folders[key].length, 0);
|
|
21
|
+
|
|
22
|
+
if (source_folders_length && cfg_obj.dest_folder) {
|
|
23
|
+
|
|
24
|
+
dest_folder = path.resolve(cfg.work_dir, cfg_obj.dest_folder);
|
|
25
|
+
|
|
26
|
+
// checking output dir for optimized svg files
|
|
27
|
+
await create_dest_folder(dest_folder, cfg_obj.clearDestFolder);
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
// parsing and saving JSX files
|
|
31
|
+
fileCount = parseSvgFiles('jsx',
|
|
32
|
+
(parsedSvg) => {
|
|
33
|
+
const result = icon_builder(parsedSvg),
|
|
34
|
+
dest_filename = result.filename?? `${result.component_name}.jsx`,
|
|
35
|
+
dest_file_path = path.resolve(dest_folder, dest_filename);
|
|
36
|
+
fs.writeFileSync(dest_file_path, result.jsx_content);
|
|
37
|
+
component_list.push({name: result.component_name, path: dest_file_path});
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// creating index file
|
|
42
|
+
let index_file_path = '';
|
|
43
|
+
if (component_list.length && cfg_obj.index_file) {
|
|
44
|
+
|
|
45
|
+
index_file_path = path.resolve(cfg.work_dir, cfg_obj.index_file);
|
|
46
|
+
const index_file_folder = path.dirname(index_file_path);
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(index_file_folder)) {
|
|
49
|
+
fs.mkdirSync(index_file_folder, {recursive: true});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const index_file_content = component_list.reduce((acc, component_obj) => {
|
|
53
|
+
const relative_path = path.relative(path.dirname(index_file_path), component_obj.path);
|
|
54
|
+
acc.push(`export { ${component_obj.name} } from './${relative_path}';`);
|
|
55
|
+
return acc;
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
fs.writeFileSync(index_file_path,
|
|
59
|
+
'// Generated file\n\n' +
|
|
60
|
+
index_file_content.toSorted((a, b) => a.localeCompare(b)).join('\n')
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if(fileCount) {
|
|
65
|
+
printResult(`\nSVG to JSX: ${fileCount} SVG files processed`);
|
|
66
|
+
printResult(`JSX files saved to ${homedir_path_to_tilde(dest_folder)}`, 'infoDim');
|
|
67
|
+
|
|
68
|
+
if(index_file_path) {
|
|
69
|
+
printResult(`Index file saved to ${homedir_path_to_tilde(index_file_path)}`, 'infoDim');
|
|
70
|
+
|
|
71
|
+
} else {
|
|
72
|
+
printResult('Index file has not been created', 'infoDim');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
} else {
|
|
76
|
+
printResult('\nSVG to JSX: no files processed', 'infoDim');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
} else {
|
|
80
|
+
printResult('\nSVG to JSX: no files processed', 'infoDim');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { configManager } from './config-manager.mjs';
|
|
4
|
+
import { parseSvgFiles } from './parse-svg-files.mjs';
|
|
5
|
+
import { printResult } from './print-result.mjs';
|
|
6
|
+
import { homedir_path_to_tilde } from './homedir-path-to-tilde.mjs';
|
|
7
|
+
import { create_dest_folder } from './create-dest-folder.mjs';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export async function svg_to_optimized() {
|
|
11
|
+
const cfg = configManager.getCfg(),
|
|
12
|
+
cfg_obj = cfg.optimize;
|
|
13
|
+
let dest_folder = '', fileCount = 0;
|
|
14
|
+
|
|
15
|
+
const source_folders_length = Object.keys(cfg_obj.source_folders)
|
|
16
|
+
.reduce((acc, key) => acc + cfg_obj.source_folders[key].length, 0);
|
|
17
|
+
|
|
18
|
+
if (source_folders_length && cfg_obj.dest_folder) {
|
|
19
|
+
|
|
20
|
+
dest_folder = path.resolve(cfg.work_dir, cfg_obj.dest_folder);
|
|
21
|
+
|
|
22
|
+
// checking output dir for optimized svg files
|
|
23
|
+
await create_dest_folder(dest_folder, cfg_obj.clearDestFolder);
|
|
24
|
+
|
|
25
|
+
// parsing and saving optimized svg files
|
|
26
|
+
fileCount = parseSvgFiles('optimize',
|
|
27
|
+
(parsedSvg) => {
|
|
28
|
+
fs.writeFileSync(path.resolve(dest_folder, `${parsedSvg.filename}.svg`), parsedSvg.svg);
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if(fileCount) {
|
|
33
|
+
printResult(`\nSVG to optimized: ${fileCount} SVG files processed`);
|
|
34
|
+
printResult(`Optimized files copied to ${homedir_path_to_tilde(dest_folder)}`, 'infoDim');
|
|
35
|
+
|
|
36
|
+
} else {
|
|
37
|
+
printResult('\nSVG to optimized: no files processed', 'infoDim');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
} else {
|
|
41
|
+
printResult('\nSVG to optimized: no files processed', 'infoDim');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { configManager } from './config-manager.mjs';
|
|
4
|
+
import { parseSvgMarkup } from './parse-svg-markup.mjs';
|
|
5
|
+
import { parseSvgFilename } from './parse-svg-filename.mjs';
|
|
6
|
+
import { printResult } from './print-result.mjs';
|
|
7
|
+
import { homedir_path_to_tilde } from './homedir-path-to-tilde.mjs';
|
|
8
|
+
|
|
9
|
+
export function svg_to_scss() {
|
|
10
|
+
|
|
11
|
+
const cfg = configManager.getCfg();
|
|
12
|
+
let svg_to_scss_array = [], fileCount = 0, skipped = 0;
|
|
13
|
+
|
|
14
|
+
if(cfg.svg_to_scss.files.length && cfg.svg_to_scss.scss_icons_file) {
|
|
15
|
+
|
|
16
|
+
cfg.svg_to_scss.files.forEach(file => {
|
|
17
|
+
|
|
18
|
+
const file_path = path.resolve(cfg.work_dir, file);
|
|
19
|
+
|
|
20
|
+
if(fs.existsSync(file_path)) {
|
|
21
|
+
const parsedSvg = parseSvgMarkup(file_path)
|
|
22
|
+
,filename = parseSvgFilename(path.basename(file_path))
|
|
23
|
+
;
|
|
24
|
+
|
|
25
|
+
let svg_string = parsedSvg.svg;
|
|
26
|
+
|
|
27
|
+
if(cfg.svg_to_scss.convert_to_css_url) {
|
|
28
|
+
svg_string = 'url("data:image/svg+xml;charset=utf8,' +
|
|
29
|
+
svg_string.replaceAll('"', '\'')
|
|
30
|
+
.replaceAll('<', '%3C')
|
|
31
|
+
.replaceAll('>', '%3E')
|
|
32
|
+
.replaceAll('&', '%26')
|
|
33
|
+
.replaceAll('#', '%23') +
|
|
34
|
+
'")';
|
|
35
|
+
|
|
36
|
+
} else {
|
|
37
|
+
svg_string = `'${svg_string.replace(/'/g, '\\\'')}'`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
svg_to_scss_array.push(`$${cfg.svg_to_scss?.varname_prefix?? ''}${filename}: ${svg_string};`);
|
|
42
|
+
|
|
43
|
+
fileCount++;
|
|
44
|
+
|
|
45
|
+
} else {
|
|
46
|
+
skipped++;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
const dest = path.resolve(cfg.work_dir, cfg.svg_to_scss.scss_icons_file);
|
|
54
|
+
|
|
55
|
+
fs.writeFileSync(
|
|
56
|
+
dest,
|
|
57
|
+
'// Generated file, don\'t edit\n\n' +
|
|
58
|
+
svg_to_scss_array.join('\n')
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
printResult(`\nSVG to scss variables: ${fileCount} SVG files processed`);
|
|
62
|
+
printResult(`\nSCSS icons variables file saved to ${homedir_path_to_tilde(dest)}`, 'infoDim');
|
|
63
|
+
if(skipped) {
|
|
64
|
+
printResult( `SVG to scss variables: ${skipped} SVG file${skipped > 1? 's were' : ' was'} not found`, 'warning' );
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
} else {
|
|
68
|
+
|
|
69
|
+
printResult('SVG to scss variables: no files processed', 'infoDim');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { configManager } from './config-manager.mjs';
|
|
4
|
+
import { parseSvgFiles } from './parse-svg-files.mjs';
|
|
5
|
+
import { printResult } from './print-result.mjs';
|
|
6
|
+
import { homedir_path_to_tilde } from './homedir-path-to-tilde.mjs';
|
|
7
|
+
|
|
8
|
+
export function svg_to_symbols() {
|
|
9
|
+
|
|
10
|
+
const cfg = configManager.getCfg();
|
|
11
|
+
let symbols_str = '', fileCount = 0, icons_list = [];
|
|
12
|
+
|
|
13
|
+
fileCount = parseSvgFiles('symbols',
|
|
14
|
+
(parsedSvg) => {
|
|
15
|
+
symbols_str += `<symbol id="${parsedSvg.filename}" viewBox="${parsedSvg.viewbox}"`+
|
|
16
|
+
(parsedSvg.classes.length? ` class="${parsedSvg.classes.join(' ')}"` : '') +
|
|
17
|
+
`>${parsedSvg.svg_content}</symbol>`;
|
|
18
|
+
icons_list.push(parsedSvg.filename);
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// symbols store
|
|
23
|
+
if (symbols_str) {
|
|
24
|
+
symbols_str = (cfg.symbols.add_xml_declaration? '<?xml version="1.0" encoding="UTF-8"?>' : '') +
|
|
25
|
+
(cfg.symbols.add_svg_doctype? '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' : '') +
|
|
26
|
+
'<svg xmlns="http://www.w3.org/2000/svg"' + (cfg.symbols.add_hidden_attribute? ' hidden="hidden"' : '') + '>' +
|
|
27
|
+
symbols_str +
|
|
28
|
+
'</svg>';
|
|
29
|
+
const dest = path.resolve(cfg.work_dir, cfg.symbols.dest_file),
|
|
30
|
+
folderName = path.dirname(dest);
|
|
31
|
+
if (!fs.existsSync(folderName)) {
|
|
32
|
+
fs.mkdirSync(folderName, {recursive: true});
|
|
33
|
+
}
|
|
34
|
+
fs.writeFileSync(path.resolve(cfg.work_dir, cfg.symbols.dest_file), symbols_str);
|
|
35
|
+
|
|
36
|
+
printResult(`\nSVG to symbols: ${fileCount} SVG files processed`);
|
|
37
|
+
|
|
38
|
+
} else {
|
|
39
|
+
printResult('\nSVG to symbols: no files processed', 'infoDim');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
// icons list
|
|
44
|
+
const icon_list_var_string = 'const icons_list = ' + JSON.stringify(icons_list.sort(), null, ' ').replace(/"/g, '\'') + ';\n';
|
|
45
|
+
if(fileCount && cfg.symbols.icons_list_file) {
|
|
46
|
+
fs.writeFileSync(path.resolve(cfg.work_dir, cfg.symbols.icons_list_file),
|
|
47
|
+
'// List of icon symbols\n' +
|
|
48
|
+
'// NB: this is a generated file, any changes will be overwritten\n\n' +
|
|
49
|
+
'export ' + icon_list_var_string
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
printResult(`Created icon list file: ${homedir_path_to_tilde(cfg.symbols.icons_list_file)}`, 'infoDim');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// demo file
|
|
56
|
+
if(fileCount && cfg.symbols.demo_tpl_path && cfg.symbols.demo_file_path) {
|
|
57
|
+
|
|
58
|
+
const tpl = fs.readFileSync(path.resolve(cfg.work_dir, cfg.symbols.demo_tpl_path), 'utf8');
|
|
59
|
+
|
|
60
|
+
fs.writeFileSync(
|
|
61
|
+
path.resolve(cfg.work_dir, cfg.symbols.demo_file_path),
|
|
62
|
+
tpl.replace(/<!-- ?demo-data ?-->/,
|
|
63
|
+
`<script>${icon_list_var_string}</script>${symbols_str}`
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
printResult(`Created demo file: ${homedir_path_to_tilde(cfg.symbols.demo_tpl_path)}`, 'infoDim');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Icons demo</title>
|
|
7
|
+
<link rel="icon" href="data:;base64,=">
|
|
8
|
+
<style type="text/css" media="all">
|
|
9
|
+
:root {
|
|
10
|
+
--icon-size: 6rem;
|
|
11
|
+
--icon-color: #4c4c4c;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: sans-serif;
|
|
16
|
+
font-size: 14px;
|
|
17
|
+
color: #333;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
h1, h2 {
|
|
21
|
+
margin-left: 1rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.icon-wrapper {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-wrap: wrap;
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.icon {
|
|
31
|
+
flex: 0 0 var(--icon-size);
|
|
32
|
+
max-width: var(--icon-size);
|
|
33
|
+
margin: 1rem;
|
|
34
|
+
text-align: center;
|
|
35
|
+
|
|
36
|
+
svg {
|
|
37
|
+
width: var(--icon-size);
|
|
38
|
+
aspect-ratio: 1;
|
|
39
|
+
margin: 0;
|
|
40
|
+
background: #ddd;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.fill-icon {
|
|
45
|
+
fill: var(--icon-color);
|
|
46
|
+
stroke: none;
|
|
47
|
+
}
|
|
48
|
+
.stroke-icon {
|
|
49
|
+
fill: none;
|
|
50
|
+
stroke: var(--icon-color);
|
|
51
|
+
stroke-linecap: round;
|
|
52
|
+
stroke-linejoin: round;
|
|
53
|
+
stroke-width: 3px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.duotone-light {
|
|
57
|
+
fill-opacity: .2;
|
|
58
|
+
}
|
|
59
|
+
.duotone-medium {
|
|
60
|
+
fill-opacity: .4;
|
|
61
|
+
}
|
|
62
|
+
.duotone-dark {
|
|
63
|
+
fill-opacity: .7;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
.icon-caption {
|
|
68
|
+
/* overflow: hidden;
|
|
69
|
+
white-space: nowrap;
|
|
70
|
+
text-overflow: ellipsis; */
|
|
71
|
+
max-width: 100%;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
74
|
+
|
|
75
|
+
</head>
|
|
76
|
+
<body>
|
|
77
|
+
<h1>Icons demo</h1>
|
|
78
|
+
|
|
79
|
+
<div class="icon-wrapper"></div>
|
|
80
|
+
|
|
81
|
+
<!-- demo-data -->
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
var wrapper = document.querySelector('.icon-wrapper');
|
|
85
|
+
|
|
86
|
+
icons_list.forEach(function (item) {
|
|
87
|
+
let div_class = ['icon'];
|
|
88
|
+
|
|
89
|
+
/* if (/-line$/.test(item)) {
|
|
90
|
+
div_class.push('line-icon');
|
|
91
|
+
|
|
92
|
+
} else if (/-fill$/.test(item)) {
|
|
93
|
+
div_class.push('fill-icon');
|
|
94
|
+
} */
|
|
95
|
+
|
|
96
|
+
wrapper.insertAdjacentHTML('beforeend',
|
|
97
|
+
`<div class="${div_class.join(' ')}">
|
|
98
|
+
<svg>
|
|
99
|
+
<title>${item}</title>
|
|
100
|
+
<use href="#${item}"></use>
|
|
101
|
+
</svg>
|
|
102
|
+
<p class="icon-caption" title="${item}"">
|
|
103
|
+
${item.replace(/(_|\-)/g, '$1<wbr>')}
|
|
104
|
+
</p>
|
|
105
|
+
</div>`
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
</script>
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|