generated from shgysk8zer0/npm-template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerator.js
149 lines (129 loc) · 4.71 KB
/
generator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env node
import { readJSONFile } from '@shgysk8zer0/npm-utils/json';
import { readYAMLFile } from '@shgysk8zer0/npm-utils/yaml';
import { readFile, writeFile, ENCODING, listDirByExt } from '@shgysk8zer0/npm-utils/fs';
import { SVG as SVG_MIME } from '@shgysk8zer0/consts/mimes';
import { JSON as JSON_EXTS, YAML as YAML_EXTS } from '@shgysk8zer0/consts/exts';
import { isURL, isObject } from '@shgysk8zer0/npm-utils/utils';
import { CSV as CSV_EXTS, readCSVFile } from './csv.js';
import { basename, extname, isAbsolute } from 'node:path';
import { getFileURL } from '@shgysk8zer0/npm-utils/path';
import { load } from 'cheerio';
const ROOT = process.cwd();
const EXT_TYPES = {
json: JSON_EXTS,
yaml: YAML_EXTS,
csv: CSV_EXTS,
};
function getFileType(ext) {
const match = Object.entries(EXT_TYPES).find(([, exts]) => exts.includes(ext));
if (Array.isArray(match)) {
return match[0];
}
}
async function readConfig(path, { encoding = ENCODING, signal } = {}) {
const ext = extname(path);
switch(getFileType(ext)) {
case 'json': return readJSONFile(`${ROOT}/${path}`, { encoding, signal });
case 'yaml': return readYAMLFile(`${ROOT}/${path}`, { encoding, signal });
case 'csv': return readCSVFile(`${ROOT}/${path}`, { encoding, signal })
.then(Object.fromEntries);
}
}
async function fetchIcon(url, { signal } = {}) {
const resp = await fetch(url, {
headers: new Headers({ Accept: SVG_MIME }),
signal,
});
if (! resp.ok) {
throw new Error(`Error fetching ${url}`);
} else if (! resp.headers.get('Content-Type').startsWith(SVG_MIME)) {
throw new TypeError(`<${url}> is not an SVG. Content-Type: ${resp.headers.get('Content-Type')}`);
} else {
return resp.text();
}
}
async function loadIcon(loc, { encoding = ENCODING, signal } = {}) {
if (isURL(loc)) {
return await fetchIcon(loc, { signal });
} else {
try {
if (isAbsolute(loc)) {
return await readFile(loc, { encoding, signal });
} else {
return await readFile(`${ROOT}/${loc}`, { encoding, signal });
}
} catch {
throw new Error(`Unable to find or read file at ${loc}.`);
}
}
}
export async function generateSymbol(id, loc, { encoding, signal } = {}) {
const icon = await loadIcon(loc, { encoding, signal });
const svg = load(`<div>${icon}</div>`, { xmlMode: true })('svg');
if (svg.length === 0) {
throw new Error(`Missing or empty svg for ${id}`);
} else {
return `<symbol id="${id}" viewBox="${svg.attr('viewBox')}">${svg.html()}</symbol>`;
}
}
export async function writeSVG(path, symbols, { encoding } = {}) {
if (! Array.isArray(symbols)) {
throw new TypeError('symbols must be an array of <symbol>s.');
} else if (typeof path === 'string') {
await writeSVG(getFileURL(path, ROOT), symbols, { encoding });
} else if (! (path instanceof URL)) {
throw new TypeError('Path must be a URL or string.');
} else {
await writeFile(
path,
`<svg xmlns="http://www.w3.org/2000/svg">${symbols.join('')}</svg>`,
{ encoding },
);
console.info(`Wrote ${symbols.length} <symbol>s to "${path}".`);
}
}
export async function generateSymbols(configFile, { encoding = ENCODING, output, signal } = {}) {
const config = await readConfig(configFile, { encoding, signal });
if (isObject(config)) {
if (typeof output !== 'string') {
throw new Error('Missing required output option (-o or --output)');
} else {
const symbols = await Promise.all(
Object.entries(config).map(
async ([id, loc]) => generateSymbol(id, loc, { encoding, signal })
)
);
await writeSVG(output, symbols, { encoding, signal });
}
} else if (Array.isArray(config)) {
if (typeof output === 'string') {
throw new Error('Output is ignored when the config file contains an array for multiple output files.');
}
await Promise.all(config.map(async ({ output, icons }) => {
if (typeof output !== 'string') {
throw new TypeError('`output` expected to be a string');
} else if (! isObject(icons)) {
throw new TypeError('`icons` expected to be an object of { id: path_or_url }');
} else {
const symbols = await Promise.all(
Object.entries(icons).map(
async ([id, loc]) => generateSymbol(id, loc, { encoding, signal })
)
);
await writeSVG(output, symbols, { encoding, signal });
}
}));
}
}
export async function generateSymbolsFromDirectory(directory, { encoding, output, signal } = {}) {
if (typeof output !== 'string') {
throw new Error('Output is a required option for directory usage.');
}
const svgs = await listDirByExt(`${ROOT}/${directory}`, '.svg');
const symbols = await Promise.all(svgs.map(async path => {
const id = basename(path).replace(extname(path), '');
return await generateSymbol(id, path, { encoding, signal });
}));
await writeSVG(output, symbols, { encoding, signal });
}