@ship-ui/core 0.19.2 → 0.19.3

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.
Binary file
package/bin/ship-fg.mjs CHANGED
@@ -3235,7 +3235,7 @@ import { parseArgs } from "util";
3235
3235
  // bin/src/ship-fg.ts
3236
3236
  import { spawnSync } from "child_process";
3237
3237
  import { watch } from "fs";
3238
- import { readFile as readFile2, writeFile } from "fs/promises";
3238
+ import { readFile as readFile2, writeFile, readdir } from "fs/promises";
3239
3239
  import { dirname, join, resolve } from "path";
3240
3240
  import { fileURLToPath } from "url";
3241
3241
  import { gzipSync } from "zlib";
@@ -3326,6 +3326,104 @@ function formatFileSize(bytes, dm = 2) {
3326
3326
  var _dirname = dirname(fileURLToPath(import.meta.url));
3327
3327
  var CWD_PATH = process.cwd();
3328
3328
  var PHOSPHOR_SRC_PATH = resolve(CWD_PATH, "node_modules", "@phosphor-icons", "web", "src");
3329
+ var jsScannerFallback = async (PROJECT_SRC, shipUiDir, CWD_PATH2) => {
3330
+ const uniqueIcons = new Set;
3331
+ const isValidIcon = (s) => /^[a-z0-9-]+$/.test(s);
3332
+ try {
3333
+ const pkgPath = resolve(shipUiDir, "package.json");
3334
+ const pkgData = JSON.parse(await readFile2(pkgPath, "utf8"));
3335
+ if (pkgData.libraryIcons) {
3336
+ pkgData.libraryIcons.forEach((i) => uniqueIcons.add(i));
3337
+ }
3338
+ } catch (e) {}
3339
+ async function scanDir(dir) {
3340
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
3341
+ for (const entry of entries) {
3342
+ if (entry.name === "node_modules" || entry.name.startsWith("."))
3343
+ continue;
3344
+ const fullPath = resolve(dir, entry.name);
3345
+ if (entry.isDirectory()) {
3346
+ await scanDir(fullPath);
3347
+ } else if (entry.name.endsWith(".html") || entry.name.endsWith(".ts")) {
3348
+ const contents = await readFile2(fullPath, "utf8");
3349
+ const htmlStartMatches = contents.matchAll(/<sh-icon[^>]*icon=['"]([^'"]+)['"][^>]*>/g);
3350
+ for (const m of htmlStartMatches) {
3351
+ const icon = m[1].trim();
3352
+ if (isValidIcon(icon))
3353
+ uniqueIcons.add(icon);
3354
+ }
3355
+ const htmlContentMatches = contents.matchAll(/<sh-icon[^>]*>([^<]+)<\/sh-icon>/g);
3356
+ for (const m of htmlContentMatches) {
3357
+ const icon = m[1].trim();
3358
+ if (isValidIcon(icon))
3359
+ uniqueIcons.add(icon);
3360
+ }
3361
+ const tsMatches = contents.matchAll(/shicon:([^'"]+)['"]/g);
3362
+ for (const m of tsMatches) {
3363
+ const icon = m[1].trim();
3364
+ if (isValidIcon(icon))
3365
+ uniqueIcons.add(icon);
3366
+ }
3367
+ }
3368
+ }
3369
+ }
3370
+ await scanDir(PROJECT_SRC);
3371
+ const variants = ["bold", "thin", "light", "fill", "regular", "duotone"];
3372
+ const glyphMaps = new Map;
3373
+ for (const variant of variants) {
3374
+ try {
3375
+ const selPath = resolve(CWD_PATH2, "node_modules", "@phosphor-icons", "web", "src", variant, "selection.json");
3376
+ const parsed = JSON.parse(await readFile2(selPath, "utf8"));
3377
+ const isDuotone = variant === "duotone";
3378
+ for (const icon of parsed.icons || []) {
3379
+ const hexCode = icon.properties.code.toString(16);
3380
+ const unicodeStr = `U+${hexCode}`;
3381
+ const glyph = String.fromCodePoint(icon.properties.code);
3382
+ let glyphName = isDuotone ? icon.properties.name : icon.properties.ligatures;
3383
+ if (glyphName)
3384
+ glyphMaps.set(glyphName, [glyph, unicodeStr]);
3385
+ }
3386
+ } catch (e) {}
3387
+ }
3388
+ const grouped = {
3389
+ bold: [],
3390
+ thin: [],
3391
+ light: [],
3392
+ fill: [],
3393
+ regular: [],
3394
+ duotone: [],
3395
+ text: [],
3396
+ missing: []
3397
+ };
3398
+ for (const icon of uniqueIcons) {
3399
+ const isBold = icon.endsWith("-bold");
3400
+ const isThin = icon.endsWith("-thin");
3401
+ const isLight = icon.endsWith("-light");
3402
+ const isFill = icon.endsWith("-fill");
3403
+ const isDuotone = icon.endsWith("-duotone");
3404
+ const isRegular = !isBold && !isThin && !isLight && !isFill && !isDuotone;
3405
+ const mapEntry = glyphMaps.get(icon);
3406
+ if (mapEntry) {
3407
+ const tuple1 = [icon, ""];
3408
+ const tuple2 = mapEntry;
3409
+ if (isBold)
3410
+ grouped.bold.push(tuple1, tuple2);
3411
+ else if (isThin)
3412
+ grouped.thin.push(tuple1, tuple2);
3413
+ else if (isLight)
3414
+ grouped.light.push(tuple1, tuple2);
3415
+ else if (isFill)
3416
+ grouped.fill.push(tuple1, tuple2);
3417
+ else if (isDuotone)
3418
+ grouped.duotone.push(tuple1, tuple2);
3419
+ else
3420
+ grouped.regular.push(tuple1, tuple2);
3421
+ } else {
3422
+ grouped.missing.push(icon);
3423
+ }
3424
+ }
3425
+ return grouped;
3426
+ };
3329
3427
  var writtenCssSize = 0;
3330
3428
  var compressedCssSize = 0;
3331
3429
  var watchers = [];
@@ -3339,15 +3437,20 @@ var run = async (PROJECT_SRC, PROJECT_PUBLIC, TARGET_FONT_TYPE, values) => {
3339
3437
  try {
3340
3438
  const proc = spawnSync(scannerPath, [PROJECT_SRC, shipUiDir, CWD_PATH]);
3341
3439
  if (proc.error || proc.status !== 0) {
3342
- console.error("Error running scanner:", proc.stderr?.toString() || proc.error);
3343
3440
  throw new Error("Native scanner failed");
3344
3441
  }
3345
- const parsed = JSON.parse(proc.stdout?.toString() || "{}");
3346
- groupedIcons = parsed;
3347
- missingIconsArray = parsed.missing || [];
3442
+ const { missing, ...rest } = JSON.parse(proc.stdout?.toString() || "{}");
3443
+ groupedIcons = rest;
3444
+ missingIconsArray = missing || [];
3348
3445
  } catch (err) {
3349
- console.error("Failed to run high-performance zig scanner:", err);
3350
- throw err;
3446
+ try {
3447
+ const { missing, ...rest } = await jsScannerFallback(PROJECT_SRC, shipUiDir, CWD_PATH);
3448
+ groupedIcons = rest;
3449
+ missingIconsArray = missing || [];
3450
+ } catch (fallbackErr) {
3451
+ console.error("Failed to run high-performance zig scanner and the javascript fallback failed:", fallbackErr);
3452
+ throw err;
3453
+ }
3351
3454
  }
3352
3455
  if (missingIconsArray.length) {
3353
3456
  console.log(`Following icons does not exist in font:
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { spawnSync } from 'child_process';
4
4
  import { FSWatcher, watch } from 'fs';
5
- import { readFile, writeFile } from 'fs/promises';
5
+ import { readFile, writeFile, readdir } from 'fs/promises';
6
6
  import { dirname, join, resolve } from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { gzipSync } from 'zlib';
@@ -16,6 +16,107 @@ import { formatFileSize, InputArguments, SupportedFontTypes } from './utilities'
16
16
  const CWD_PATH = process.cwd();
17
17
  const PHOSPHOR_SRC_PATH = resolve(CWD_PATH, 'node_modules', '@phosphor-icons', 'web', 'src');
18
18
 
19
+ const jsScannerFallback = async (PROJECT_SRC: string, shipUiDir: string, CWD_PATH: string) => {
20
+ const uniqueIcons = new Set<string>();
21
+
22
+ const isValidIcon = (s: string) => /^[a-z0-9-]+$/.test(s);
23
+
24
+ try {
25
+ const pkgPath = resolve(shipUiDir, 'package.json');
26
+ const pkgData = JSON.parse(await readFile(pkgPath, 'utf8'));
27
+ if (pkgData.libraryIcons) {
28
+ pkgData.libraryIcons.forEach((i: string) => uniqueIcons.add(i));
29
+ }
30
+ } catch (e) {}
31
+
32
+ async function scanDir(dir: string) {
33
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
34
+ for (const entry of entries) {
35
+ if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
36
+ const fullPath = resolve(dir, entry.name);
37
+ if (entry.isDirectory()) {
38
+ await scanDir(fullPath);
39
+ } else if (entry.name.endsWith('.html') || entry.name.endsWith('.ts')) {
40
+ const contents = await readFile(fullPath, 'utf8');
41
+
42
+ const htmlStartMatches = contents.matchAll(/<sh-icon[^>]*icon=['"]([^'"]+)['"][^>]*>/g);
43
+ for (const m of htmlStartMatches) {
44
+ const icon = m[1].trim();
45
+ if (isValidIcon(icon)) uniqueIcons.add(icon);
46
+ }
47
+ const htmlContentMatches = contents.matchAll(/<sh-icon[^>]*>([^<]+)<\/sh-icon>/g);
48
+ for (const m of htmlContentMatches) {
49
+ const icon = m[1].trim();
50
+ if (isValidIcon(icon)) uniqueIcons.add(icon);
51
+ }
52
+
53
+ const tsMatches = contents.matchAll(/shicon:([^'"]+)['"]/g);
54
+ for (const m of tsMatches) {
55
+ const icon = m[1].trim();
56
+ if (isValidIcon(icon)) uniqueIcons.add(icon);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ await scanDir(PROJECT_SRC);
62
+
63
+ const variants = ['bold', 'thin', 'light', 'fill', 'regular', 'duotone'];
64
+ const glyphMaps = new Map<string, [string, string]>();
65
+
66
+ for (const variant of variants) {
67
+ try {
68
+ const selPath = resolve(CWD_PATH, 'node_modules', '@phosphor-icons', 'web', 'src', variant, 'selection.json');
69
+ const parsed = JSON.parse(await readFile(selPath, 'utf8'));
70
+ const isDuotone = variant === 'duotone';
71
+
72
+ for (const icon of parsed.icons || []) {
73
+ const hexCode = icon.properties.code.toString(16);
74
+ const unicodeStr = `U+${hexCode}`;
75
+ const glyph = String.fromCodePoint(icon.properties.code);
76
+
77
+ let glyphName = isDuotone ? icon.properties.name : icon.properties.ligatures;
78
+ if (glyphName) glyphMaps.set(glyphName, [glyph, unicodeStr]);
79
+ }
80
+ } catch (e) {}
81
+ }
82
+
83
+ const grouped = {
84
+ bold: [] as [string, string][],
85
+ thin: [] as [string, string][],
86
+ light: [] as [string, string][],
87
+ fill: [] as [string, string][],
88
+ regular: [] as [string, string][],
89
+ duotone: [] as [string, string][],
90
+ text: [] as [string, string][],
91
+ missing: [] as string[],
92
+ };
93
+
94
+ for (const icon of uniqueIcons) {
95
+ const isBold = icon.endsWith('-bold');
96
+ const isThin = icon.endsWith('-thin');
97
+ const isLight = icon.endsWith('-light');
98
+ const isFill = icon.endsWith('-fill');
99
+ const isDuotone = icon.endsWith('-duotone');
100
+ const isRegular = !isBold && !isThin && !isLight && !isFill && !isDuotone;
101
+
102
+ const mapEntry = glyphMaps.get(icon);
103
+ if (mapEntry) {
104
+ const tuple1: [string, string] = [icon, ''];
105
+ const tuple2 = mapEntry; // [glyph, unicodeStr]
106
+ if (isBold) grouped.bold.push(tuple1, tuple2);
107
+ else if (isThin) grouped.thin.push(tuple1, tuple2);
108
+ else if (isLight) grouped.light.push(tuple1, tuple2);
109
+ else if (isFill) grouped.fill.push(tuple1, tuple2);
110
+ else if (isDuotone) grouped.duotone.push(tuple1, tuple2);
111
+ else grouped.regular.push(tuple1, tuple2);
112
+ } else {
113
+ grouped.missing.push(icon);
114
+ }
115
+ }
116
+
117
+ return grouped;
118
+ };
119
+
19
120
  let writtenCssSize = 0;
20
121
  let compressedCssSize = 0;
21
122
  let watchers: FSWatcher[] = [];
@@ -37,16 +138,21 @@ const run = async (
37
138
  try {
38
139
  const proc = spawnSync(scannerPath, [PROJECT_SRC, shipUiDir, CWD_PATH]);
39
140
  if (proc.error || proc.status !== 0) {
40
- console.error('Error running scanner:', proc.stderr?.toString() || proc.error);
41
141
  throw new Error('Native scanner failed');
42
142
  }
43
143
 
44
- const parsed = JSON.parse(proc.stdout?.toString() || '{}');
45
- groupedIcons = parsed;
46
- missingIconsArray = parsed.missing || [];
144
+ const { missing, ...rest } = JSON.parse(proc.stdout?.toString() || '{}');
145
+ groupedIcons = rest;
146
+ missingIconsArray = missing || [];
47
147
  } catch (err) {
48
- console.error('Failed to run high-performance zig scanner:', err);
49
- throw err;
148
+ try {
149
+ const { missing, ...rest } = await jsScannerFallback(PROJECT_SRC, shipUiDir, CWD_PATH);
150
+ groupedIcons = rest;
151
+ missingIconsArray = missing || [];
152
+ } catch (fallbackErr) {
153
+ console.error('Failed to run high-performance zig scanner and the javascript fallback failed:', fallbackErr);
154
+ throw err;
155
+ }
50
156
  }
51
157
 
52
158
  if (missingIconsArray.length) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ship-ui/core",
3
3
  "license": "MIT",
4
- "version": "0.19.2",
4
+ "version": "0.19.3",
5
5
  "peerDependencies": {
6
6
  "@angular/common": ">=20",
7
7
  "@angular/core": ">=20",
@@ -30,6 +30,7 @@
30
30
  },
31
31
  "bin": {
32
32
  "ship-fg": "./bin/ship-fg.mjs",
33
+ "ship-fg-ts": "./bin/ship-fg.ts",
33
34
  "ship-mcp": "./bin/mcp/index.js"
34
35
  },
35
36
  "libraryIcons": [