@ship-ui/core 0.18.12 → 0.19.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.
@@ -1,373 +0,0 @@
1
- import { promises as fs, watch } from 'fs';
2
- import { glob } from 'glob';
3
- import path, { join, resolve } from 'path';
4
- import { performance } from 'perf_hooks';
5
- import zlib from 'zlib';
6
-
7
- import subsetFont from 'subset-font';
8
-
9
- import { fileURLToPath } from 'url';
10
- import { formatFileSize, getUnicodeObject } from './utilities.js';
11
-
12
- let writtenCssSize = 0;
13
- let compressedCssSize = 0;
14
-
15
- const run = async (PROJECT_SRC, LIB_ICONS, PROJECT_PUBLIC, GLYPH_MAP, TARGET_FONT_TYPE, values) => {
16
- const startTime = performance.now();
17
-
18
- const regex = /<sh-icon[^>]*>\s*((?!{{.*?}})[^<]*?)\s*<\/sh-icon>/g;
19
- const regex2 = /shicon:([^']+)/g;
20
- const iconsFound = new Set(LIB_ICONS);
21
- const missingIcons = new Set();
22
-
23
- const htmlFiles = await glob('**/*.html', { cwd: PROJECT_SRC });
24
- for (const file of htmlFiles) {
25
- const fileText = await fs.readFile(join(PROJECT_SRC, file), 'utf8');
26
- const matches = Array.from(fileText.matchAll(regex), (m) => m[1]);
27
-
28
- if (matches?.length) {
29
- for (const match of matches) {
30
- if (match) iconsFound.add(match);
31
- }
32
- }
33
- }
34
-
35
- const tsFiles = await glob('**/*.ts', { cwd: PROJECT_SRC });
36
-
37
- for (const file of tsFiles) {
38
- const fileText = await fs.readFile(join(PROJECT_SRC, file), 'utf8');
39
- const matches = Array.from(fileText.matchAll(regex2), (m) => m[1]);
40
-
41
- if (matches?.length) {
42
- for (const match of matches) {
43
- if (match) iconsFound.add(match);
44
- }
45
- }
46
- }
47
-
48
- const groupedIcons = Array.from(iconsFound).reduce(
49
- (acc, icon) => {
50
- const bold = icon.endsWith('-bold');
51
- const thin = icon.endsWith('-thin');
52
- const light = icon.endsWith('-light');
53
- const fill = icon.endsWith('-fill');
54
- const duotone = icon.endsWith('-duotone');
55
- const regular = !bold && !thin && !light && !fill && !duotone;
56
- const glyph = GLYPH_MAP[icon];
57
-
58
- if (!glyph) {
59
- missingIcons.add(icon);
60
- return acc;
61
- }
62
-
63
- if (bold) {
64
- acc['bold'].push([icon, '']);
65
- acc['bold'].push(glyph);
66
- }
67
- if (thin) {
68
- acc['thin'].push([icon, '']);
69
- acc['thin'].push(glyph);
70
- }
71
- if (light) {
72
- acc['light'].push([icon, '']);
73
- acc['light'].push(glyph);
74
- }
75
- if (fill) {
76
- acc['fill'].push([icon, '']);
77
- acc['fill'].push(glyph);
78
- }
79
- if (regular) {
80
- acc['regular'].push([icon, '']);
81
- acc['regular'].push(glyph);
82
- }
83
- if (duotone) {
84
- acc['duotone'].push([icon, '']);
85
- acc['duotone'].push(glyph);
86
- }
87
-
88
- return acc;
89
- },
90
- {
91
- bold: [],
92
- thin: [],
93
- light: [],
94
- fill: [],
95
- regular: [],
96
- duotone: [],
97
- text: [],
98
- }
99
- );
100
-
101
- const missingIconsArray = Array.from(missingIcons);
102
-
103
- if (missingIconsArray.length) {
104
- console.log('Following icons does not exist in font: \n ', missingIconsArray);
105
- }
106
-
107
- await writeCssFile(PROJECT_PUBLIC, values, groupedIcons, TARGET_FONT_TYPE);
108
-
109
- const fontTypes = ['bold', 'thin', 'light', 'fill', 'regular'].filter((x) => groupedIcons[x].length > 0);
110
- const targetFormat = TARGET_FONT_TYPE === 'ttf' ? 'truetype' : TARGET_FONT_TYPE;
111
- const fonts = fontTypes.map(async (fontType) => {
112
- const glyphs = uniqueString(groupedIcons[fontType].map((icon) => icon[0]).join(''));
113
- const fontFileName = `Phosphor${fontType === 'regular' ? '' : '-' + capitalize(fontType)}.${TARGET_FONT_TYPE}`;
114
- const fullPath = path.resolve(
115
- process.cwd(),
116
- 'node_modules',
117
- '@phosphor-icons',
118
- 'web',
119
- 'src',
120
- fontType,
121
- fontFileName
122
- );
123
-
124
- const arrayBuffer = await fs.readFile(fullPath);
125
- const subsetBuffer = await subsetFont(arrayBuffer, glyphs, {
126
- targetFormat,
127
- noLayoutClosure: true,
128
- });
129
-
130
- return subsetBuffer;
131
- });
132
-
133
- const _fonts = await Promise.all(fonts);
134
- let totalFontSize = 0;
135
- let totalCompressedFontSize = 0;
136
-
137
- for (let i = 0; i < _fonts.length; i++) {
138
- const subsetBuffer = _fonts[i];
139
- const fontType = fontTypes[i];
140
- const outputPath = `${PROJECT_PUBLIC}/sh${fontType === 'regular' ? '' : '-' + fontType}.${TARGET_FONT_TYPE}`;
141
-
142
- await fs.writeFile(outputPath, subsetBuffer);
143
- const fontWrites = subsetBuffer.length;
144
-
145
- const compressedFont = zlib.gzipSync(subsetBuffer);
146
- const iconsOnGroup = groupedIcons[fontType].filter((icon) => icon[1] === '').map((icon) => icon[0]);
147
-
148
- totalFontSize += fontWrites;
149
- totalCompressedFontSize += compressedFont.length;
150
-
151
- if (values.verbose) {
152
- console.log('Group: ', fontType, '\n', iconsOnGroup);
153
- console.log(
154
- `Font & CSS (Generated/Compressed size): ${formatFileSize(fontWrites)}/${formatFileSize(compressedFont.length)}`
155
- );
156
- console.log(' ');
157
- }
158
- }
159
-
160
- const endTime = performance.now();
161
- const runtime = endTime - startTime;
162
- console.log(
163
- `Font & CSS (Generated/Compressed size): ${formatFileSize(totalFontSize + writtenCssSize)}/${formatFileSize(totalCompressedFontSize + compressedCssSize)}`
164
- );
165
- console.log('Time taken: ', runtime.toFixed(2) + 'ms');
166
- console.log(' ');
167
- };
168
-
169
- function uniqueString(s) {
170
- const seen = new Set();
171
- let result = '';
172
-
173
- for (const char of s) {
174
- if (!seen.has(char)) {
175
- seen.add(char);
176
- result += char;
177
- }
178
- }
179
- return result;
180
- }
181
-
182
- const writeCssFile = async (PROJECT_PUBLIC, values, groupedIcons, TARGET_FONT_TYPE = 'woff2') => {
183
- const groupedIconsEntries = Object.entries(groupedIcons).map(([key, value], i) => {
184
- if (key === 'text') return '';
185
-
186
- const suffix = key === 'regular' ? '' : '-' + key;
187
- const fontUrl = `url('${values.rootPath}sh${suffix}.${TARGET_FONT_TYPE}') format('${TARGET_FONT_TYPE === 'ttf' ? 'truetype' : TARGET_FONT_TYPE}')`;
188
-
189
- return `
190
- @font-face {
191
- font-family: 'sh${suffix}';
192
- src: ${fontUrl};
193
- font-weight: normal;
194
- font-style: normal;
195
- font-display: block;
196
- }`;
197
- });
198
-
199
- const keys = Object.keys(groupedIcons).map((key) => {
200
- if (['regular', 'text'].includes(key)) return '';
201
-
202
- const suffix = key === 'regular' ? '' : '-' + key;
203
-
204
- return `
205
- sh-icon.${key} {
206
- font-family: 'sh${suffix}' !important;
207
- }`;
208
- });
209
-
210
- const cssFileContent = `
211
- ${groupedIconsEntries.join('\n')}
212
- ${keys.join('\n')}
213
- sh-icon {
214
- font-family: "sh" !important;
215
- speak: never;
216
- font-style: normal;
217
- font-weight: normal;
218
- font-variant: normal;
219
- text-transform: none;
220
- line-height: 1;
221
-
222
- letter-spacing: 0;
223
- -webkit-font-feature-settings: "liga";
224
- -moz-font-feature-settings: "liga=1";
225
- -moz-font-feature-settings: "liga";
226
- -ms-font-feature-settings: "liga" 1;
227
- font-feature-settings: "liga";
228
- -webkit-font-variant-ligatures: discretionary-ligatures;
229
- font-variant-ligatures: discretionary-ligatures;
230
-
231
- -webkit-font-smoothing: antialiased;
232
- -moz-osx-font-smoothing: grayscale;
233
- }`;
234
-
235
- const cssOutputPath = `${PROJECT_PUBLIC}/ship.css`;
236
- await fs.writeFile(cssOutputPath, cssFileContent);
237
- const cssWrites = Buffer.byteLength(cssFileContent, 'utf8');
238
-
239
- const compressedCss = zlib.gzipSync(Buffer.from(cssFileContent, 'utf8'));
240
-
241
- writtenCssSize = cssWrites;
242
- compressedCssSize = compressedCss.length;
243
- };
244
-
245
- const textMateSnippet = async (GLYPH_MAP) => {
246
- const iconsSnippetContent = `
247
- {
248
- "Phosphor icons": {
249
- "prefix": ["shicon:"],
250
- "scope": "javascript,typescript,html",
251
- "body": "\${1|${Object.keys(GLYPH_MAP).join(',')}|}",
252
- "description": "Add a phosphor icon"
253
- },
254
- "Ship sh-icon": {
255
- "prefix": ["sh-icon"],
256
- "scope": "html",
257
- "body": "<sh-icon>\${1|${Object.keys(GLYPH_MAP).join(',')}|}</sh-icon>",
258
- "description": "Add a ship phosphor icon"
259
- }
260
- }
261
- `;
262
-
263
- const vscodeDir = './.vscode';
264
- await fs.mkdir(vscodeDir, { recursive: true });
265
- await fs.writeFile('./.vscode/html.code-snippets', iconsSnippetContent);
266
- };
267
-
268
- function capitalize(str) {
269
- return str.charAt(0).toUpperCase() + str.slice(1);
270
- }
271
-
272
- export const main = async (values) => {
273
- const __filename = fileURLToPath(import.meta.url);
274
- const __dirname = path.dirname(__filename);
275
- const TARGET_FONT_TYPE = 'woff2';
276
- const packageJsonPath = resolve(__dirname, '../../package.json');
277
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
278
- const PROJECT_SRC = values.src;
279
- const PROJECT_PUBLIC = values.out;
280
- const fontVariants = ['bold', 'thin', 'light', 'fill', 'regular'];
281
-
282
- await fs.mkdir(PROJECT_PUBLIC, { recursive: true });
283
-
284
- const GLYPH_MAPS = await Promise.all(
285
- fontVariants.map(async (fontVariant) => {
286
- const selectionJsonFullPath = path.resolve(
287
- process.cwd(),
288
- 'node_modules',
289
- '@phosphor-icons',
290
- 'web',
291
- 'src',
292
- fontVariant,
293
- 'selection.json'
294
- );
295
-
296
- const selectionJson = JSON.parse(await fs.readFile(selectionJsonFullPath, 'utf8'));
297
- const unicodeObj = getUnicodeObject(selectionJson.icons, fontVariant === 'duotone');
298
-
299
- return unicodeObj;
300
- })
301
- );
302
-
303
- const GLYPH_MAP = GLYPH_MAPS.reduce((acc, curr) => {
304
- return {
305
- ...acc,
306
- ...curr,
307
- };
308
- }, {});
309
-
310
- let LIB_ICONS = packageJson.libraryIcons;
311
-
312
- try {
313
- await textMateSnippet(GLYPH_MAP);
314
- await run(PROJECT_SRC, LIB_ICONS, PROJECT_PUBLIC, GLYPH_MAP, TARGET_FONT_TYPE, values);
315
- } catch (error) {
316
- console.error('An error occurred during the initial run:', error);
317
- if (!values.watch && !values.watchLib) {
318
- process.exit(1);
319
- }
320
- }
321
-
322
- if (!values.watch && !values.watchLib) {
323
- return;
324
- }
325
-
326
- console.log('\nWatching for file changes. Press Cmd+C to stop.');
327
- let watchers = [];
328
-
329
- function killWatchers() {
330
- console.log(`\n✅ The icon font generation watch process has been stopped.`);
331
-
332
- for (const watcher of watchers) {
333
- watcher.close();
334
- }
335
-
336
- process.exit(0);
337
- }
338
-
339
- process.on('SIGINT', killWatchers);
340
- process.on('SIGTERM', killWatchers);
341
- process.on('SIGBREAK', killWatchers);
342
-
343
- if (values.watch) {
344
- const excludeFolders = ['node_modules', '.git', '.vscode', 'bin', 'assets'].concat([PROJECT_PUBLIC]);
345
- const watcher = watch(PROJECT_SRC, { recursive: true }, async (_, filename) => {
346
- if (filename && !excludeFolders.some((folder) => resolve(join(PROJECT_SRC, filename)).includes(folder))) {
347
- console.log(`Change detected in ${filename}, regenerating...`);
348
- try {
349
- await run(PROJECT_SRC, LIB_ICONS, PROJECT_PUBLIC, GLYPH_MAP, TARGET_FONT_TYPE, values);
350
- } catch (error) {
351
- console.error('Error during watched run:', error);
352
- }
353
- }
354
- });
355
-
356
- watchers.push(watcher);
357
- }
358
-
359
- if (values.watchLib) {
360
- const watcher = watch(packageJsonPath, {}, async (_, filename) => {
361
- console.log(`Change detected in package.json, regenerating...`);
362
- try {
363
- const updatedPackageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
364
- LIB_ICONS = updatedPackageJson.libraryIcons;
365
- await run(PROJECT_SRC, LIB_ICONS, PROJECT_PUBLIC, GLYPH_MAP, TARGET_FONT_TYPE, values);
366
- } catch (error) {
367
- console.error('Error during package.json watched run:', error);
368
- }
369
- });
370
-
371
- watchers.push(watcher);
372
- }
373
- };