react-native-nano-icons 0.0.0 → 0.1.0

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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/app.plugin.js +1 -0
  4. package/lib/commonjs/cli/build.d.ts +28 -0
  5. package/lib/commonjs/cli/build.js +83 -0
  6. package/lib/commonjs/cli/config.d.ts +9 -0
  7. package/lib/commonjs/cli/config.js +25 -0
  8. package/lib/commonjs/cli/index.d.ts +4 -0
  9. package/lib/commonjs/cli/index.js +13 -0
  10. package/lib/commonjs/cli/link.d.ts +11 -0
  11. package/lib/commonjs/cli/link.js +135 -0
  12. package/lib/commonjs/cli/logger.d.ts +27 -0
  13. package/lib/commonjs/cli/logger.js +83 -0
  14. package/lib/commonjs/index.node.js +8 -0
  15. package/lib/commonjs/plugin/src/buildFonts.d.ts +7 -0
  16. package/lib/commonjs/plugin/src/buildFonts.js +23 -0
  17. package/lib/commonjs/plugin/src/index.d.ts +5 -0
  18. package/lib/commonjs/plugin/src/index.js +43 -0
  19. package/lib/commonjs/plugin/src/types.d.ts +31 -0
  20. package/lib/commonjs/plugin/src/types.js +2 -0
  21. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.d.ts +14 -0
  22. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.js +92 -0
  23. package/lib/commonjs/scripts/cli.js +28 -0
  24. package/lib/commonjs/src/const/colors.d.ts +1 -0
  25. package/lib/commonjs/src/const/colors.js +153 -0
  26. package/lib/commonjs/src/core/font/compile.d.ts +9 -0
  27. package/lib/commonjs/src/core/font/compile.js +68 -0
  28. package/lib/commonjs/src/core/font/metrics.d.ts +1 -0
  29. package/lib/commonjs/src/core/font/metrics.js +33 -0
  30. package/lib/commonjs/src/core/pipeline/config.d.ts +14 -0
  31. package/lib/commonjs/src/core/pipeline/config.js +17 -0
  32. package/lib/commonjs/src/core/pipeline/index.d.ts +3 -0
  33. package/lib/commonjs/src/core/pipeline/index.js +7 -0
  34. package/lib/commonjs/src/core/pipeline/managers.d.ts +11 -0
  35. package/lib/commonjs/src/core/pipeline/managers.js +89 -0
  36. package/lib/commonjs/src/core/pipeline/run.d.ts +14 -0
  37. package/lib/commonjs/src/core/pipeline/run.js +99 -0
  38. package/lib/commonjs/src/core/svg/layers.d.ts +24 -0
  39. package/lib/commonjs/src/core/svg/layers.js +42 -0
  40. package/lib/commonjs/src/core/svg/svg_dom.d.ts +22 -0
  41. package/lib/commonjs/src/core/svg/svg_dom.js +84 -0
  42. package/lib/commonjs/src/core/svg/svg_pathops.d.ts +21 -0
  43. package/lib/commonjs/src/core/svg/svg_pathops.js +611 -0
  44. package/lib/commonjs/src/core/types.d.ts +120 -0
  45. package/lib/commonjs/src/core/types.js +2 -0
  46. package/lib/commonjs/src/utils/fingerPrint.d.ts +1 -0
  47. package/lib/commonjs/src/utils/fingerPrint.js +20 -0
  48. package/lib/commonjs/src/utils/parse.d.ts +2 -0
  49. package/lib/commonjs/src/utils/parse.js +64 -0
  50. package/lib/module/const/colors.js +153 -0
  51. package/lib/module/const/colors.js.map +1 -0
  52. package/lib/module/core/font/compile.js +70 -0
  53. package/lib/module/core/font/compile.js.map +1 -0
  54. package/lib/module/core/font/metrics.js +37 -0
  55. package/lib/module/core/font/metrics.js.map +1 -0
  56. package/lib/module/core/pipeline/config.js +20 -0
  57. package/lib/module/core/pipeline/config.js.map +1 -0
  58. package/lib/module/core/pipeline/index.js +5 -0
  59. package/lib/module/core/pipeline/index.js.map +1 -0
  60. package/lib/module/core/pipeline/managers.js +80 -0
  61. package/lib/module/core/pipeline/managers.js.map +1 -0
  62. package/lib/module/core/pipeline/run.js +114 -0
  63. package/lib/module/core/pipeline/run.js.map +1 -0
  64. package/lib/module/core/shims/pathops.py +181 -0
  65. package/lib/module/core/svg/layers.js +51 -0
  66. package/lib/module/core/svg/layers.js.map +1 -0
  67. package/lib/module/core/svg/svg_dom.js +83 -0
  68. package/lib/module/core/svg/svg_dom.js.map +1 -0
  69. package/lib/module/core/svg/svg_pathops.js +566 -0
  70. package/lib/module/core/svg/svg_pathops.js.map +1 -0
  71. package/lib/module/core/tsconfig.json +32 -0
  72. package/lib/module/core/types.js +2 -0
  73. package/lib/module/core/types.js.map +1 -0
  74. package/lib/module/createNanoIconsSet.js +84 -0
  75. package/lib/module/createNanoIconsSet.js.map +1 -0
  76. package/lib/module/index.js +5 -0
  77. package/lib/module/index.js.map +1 -0
  78. package/lib/module/index.node.js +13 -0
  79. package/lib/module/index.node.js.map +1 -0
  80. package/lib/module/package.json +1 -0
  81. package/lib/module/utils/fingerPrint.js +17 -0
  82. package/lib/module/utils/fingerPrint.js.map +1 -0
  83. package/lib/module/utils/parse.js +53 -0
  84. package/lib/module/utils/parse.js.map +1 -0
  85. package/lib/typescript/__tests__/build.unit.test.d.ts +3 -0
  86. package/lib/typescript/__tests__/build.unit.test.d.ts.map +1 -0
  87. package/lib/typescript/__tests__/clippath.e2e.test.d.ts +3 -0
  88. package/lib/typescript/__tests__/clippath.e2e.test.d.ts.map +1 -0
  89. package/lib/typescript/__tests__/fingerprint.unit.test.d.ts +3 -0
  90. package/lib/typescript/__tests__/fingerprint.unit.test.d.ts.map +1 -0
  91. package/lib/typescript/__tests__/pipeline.e2e.test.d.ts +3 -0
  92. package/lib/typescript/__tests__/pipeline.e2e.test.d.ts.map +1 -0
  93. package/lib/typescript/__tests__/placement.unit.test.d.ts +3 -0
  94. package/lib/typescript/__tests__/placement.unit.test.d.ts.map +1 -0
  95. package/lib/typescript/__tests__/svg_dom.unit.test.d.ts +3 -0
  96. package/lib/typescript/__tests__/svg_dom.unit.test.d.ts.map +1 -0
  97. package/lib/typescript/cli/build.d.ts +29 -0
  98. package/lib/typescript/cli/build.d.ts.map +1 -0
  99. package/lib/typescript/cli/logger.d.ts +28 -0
  100. package/lib/typescript/cli/logger.d.ts.map +1 -0
  101. package/lib/typescript/package.json +1 -0
  102. package/lib/typescript/src/const/colors.d.ts +2 -0
  103. package/lib/typescript/src/const/colors.d.ts.map +1 -0
  104. package/lib/typescript/src/core/font/compile.d.ts +10 -0
  105. package/lib/typescript/src/core/font/compile.d.ts.map +1 -0
  106. package/lib/typescript/src/core/font/metrics.d.ts +2 -0
  107. package/lib/typescript/src/core/font/metrics.d.ts.map +1 -0
  108. package/lib/typescript/src/core/pipeline/config.d.ts +15 -0
  109. package/lib/typescript/src/core/pipeline/config.d.ts.map +1 -0
  110. package/lib/typescript/src/core/pipeline/index.d.ts +4 -0
  111. package/lib/typescript/src/core/pipeline/index.d.ts.map +1 -0
  112. package/lib/typescript/src/core/pipeline/managers.d.ts +12 -0
  113. package/lib/typescript/src/core/pipeline/managers.d.ts.map +1 -0
  114. package/lib/typescript/src/core/pipeline/run.d.ts +15 -0
  115. package/lib/typescript/src/core/pipeline/run.d.ts.map +1 -0
  116. package/lib/typescript/src/core/svg/layers.d.ts +25 -0
  117. package/lib/typescript/src/core/svg/layers.d.ts.map +1 -0
  118. package/lib/typescript/src/core/svg/svg_dom.d.ts +23 -0
  119. package/lib/typescript/src/core/svg/svg_dom.d.ts.map +1 -0
  120. package/lib/typescript/src/core/svg/svg_pathops.d.ts +22 -0
  121. package/lib/typescript/src/core/svg/svg_pathops.d.ts.map +1 -0
  122. package/lib/typescript/src/core/types.d.ts +121 -0
  123. package/lib/typescript/src/core/types.d.ts.map +1 -0
  124. package/lib/typescript/src/createNanoIconsSet.d.ts +19 -0
  125. package/lib/typescript/src/createNanoIconsSet.d.ts.map +1 -0
  126. package/lib/typescript/src/index.d.ts +3 -0
  127. package/lib/typescript/src/index.d.ts.map +1 -0
  128. package/lib/typescript/src/index.node.d.ts +2 -0
  129. package/lib/typescript/src/index.node.d.ts.map +1 -0
  130. package/lib/typescript/src/utils/fingerPrint.d.ts +2 -0
  131. package/lib/typescript/src/utils/fingerPrint.d.ts.map +1 -0
  132. package/lib/typescript/src/utils/parse.d.ts +3 -0
  133. package/lib/typescript/src/utils/parse.d.ts.map +1 -0
  134. package/package.json +160 -1
  135. package/plugin/src/buildFonts.ts +29 -0
  136. package/plugin/src/index.ts +68 -0
  137. package/plugin/src/types.ts +33 -0
  138. package/plugin/src/withNanoIconsFontLinking.ts +119 -0
  139. package/plugin/tsconfig.json +9 -0
  140. package/scripts/cli.ts +34 -0
  141. package/scripts/tsconfig.json +25 -0
  142. package/src/const/colors.ts +150 -0
  143. package/src/core/font/compile.ts +96 -0
  144. package/src/core/font/metrics.ts +44 -0
  145. package/src/core/pipeline/config.ts +24 -0
  146. package/src/core/pipeline/index.ts +3 -0
  147. package/src/core/pipeline/managers.ts +114 -0
  148. package/src/core/pipeline/run.ts +151 -0
  149. package/src/core/shims/pathops.py +181 -0
  150. package/src/core/svg/layers.ts +66 -0
  151. package/src/core/svg/svg_dom.ts +99 -0
  152. package/src/core/svg/svg_pathops.ts +796 -0
  153. package/src/core/tsconfig.json +32 -0
  154. package/src/core/types.ts +138 -0
  155. package/src/createNanoIconsSet.tsx +131 -0
  156. package/src/index.node.ts +14 -0
  157. package/src/index.ts +7 -0
  158. package/src/utils/fingerPrint.ts +20 -0
  159. package/src/utils/parse.ts +67 -0
@@ -0,0 +1,5 @@
1
+ import type { ConfigPlugin } from '@expo/config-plugins';
2
+ import type { NanoIconsPluginOptions } from './types.js';
3
+ declare const withNanoIcons: ConfigPlugin<NanoIconsPluginOptions>;
4
+ export default withNanoIcons;
5
+ export type { NanoIconsPluginOptions, IconSetConfig, BuiltFont, } from './types.js';
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config_plugins_1 = require("@expo/config-plugins");
4
+ const buildFonts_js_1 = require("./buildFonts.js");
5
+ const withNanoIconsFontLinking_js_1 = require("./withNanoIconsFontLinking.js");
6
+ const BUILT_FONTS_KEY = '_nanoIconsBuilt';
7
+ // Single Pyodide/PathKit run for the whole prebuild; reused across ios/android mods.
8
+ let _builtFontsCache = null;
9
+ function getOrBuildFonts(projectRoot, iconSets) {
10
+ if (_builtFontsCache)
11
+ return Promise.resolve(_builtFontsCache);
12
+ return (0, buildFonts_js_1.buildAllFonts)(iconSets, projectRoot).then((built) => {
13
+ _builtFontsCache = built;
14
+ return built;
15
+ });
16
+ }
17
+ const withNanoIcons = (config, options) => {
18
+ if (!options?.iconSets?.length)
19
+ return config;
20
+ // Build fonts (once per process, cached) and attach to config for linking mods.
21
+ config = (0, config_plugins_1.withDangerousMod)(config, [
22
+ 'ios',
23
+ async (config) => {
24
+ const projectRoot = config.modRequest.projectRoot;
25
+ const built = await getOrBuildFonts(projectRoot, options.iconSets);
26
+ config[BUILT_FONTS_KEY] = built;
27
+ return config;
28
+ },
29
+ ]);
30
+ config = (0, config_plugins_1.withDangerousMod)(config, [
31
+ 'android',
32
+ async (config) => {
33
+ const projectRoot = config.modRequest.projectRoot;
34
+ const built = await getOrBuildFonts(projectRoot, options.iconSets);
35
+ config[BUILT_FONTS_KEY] = built;
36
+ return config;
37
+ },
38
+ ]);
39
+ // Link built TTFs into native projects (reads _nanoIconsBuilt from config).
40
+ config = (0, withNanoIconsFontLinking_js_1.withNanoIconsFontLinking)(config);
41
+ return config;
42
+ };
43
+ exports.default = withNanoIcons;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Config for one icon set: input SVGs → one TTF + glyphmap.
3
+ */
4
+ export interface IconSetConfig {
5
+ /** Path to folder of SVG files (relative to project root). */
6
+ inputDir: string;
7
+ /** Font family name (used for TTF and glyphmap filenames). */
8
+ fontFamily: string;
9
+ /** Path where .ttf and .glyphmap.json will be saved. Defaults to a sibling nanoicons folder relative to inputDir. */
10
+ outputDir?: string;
11
+ /** Units per em (default 1024). */
12
+ upm?: number;
13
+ /** Safe zone inside UPM for glyphs (default 1020). */
14
+ safeZone?: number;
15
+ /** First Unicode codepoint for glyphs (default 0xe900). Hex string or number. */
16
+ startUnicode?: number | string;
17
+ }
18
+ /**
19
+ * plugins: [ [ "react-native-nano-icons", { iconSets: [...] } ] ]
20
+ */
21
+ export interface NanoIconsPluginOptions {
22
+ iconSets: IconSetConfig[];
23
+ }
24
+ /**
25
+ * Result of building one icon set.
26
+ */
27
+ export interface BuiltFont {
28
+ fontFamily: string;
29
+ ttfPath: string;
30
+ glyphmapPath: string;
31
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,14 @@
1
+ import { withXcodeProject, withDangerousMod } from '@expo/config-plugins';
2
+ /**
3
+ * Add TTFs to the iOS project (Resources group + UIAppFonts in Info.plist).
4
+ * Reads cached built font paths from config._nanoIconsBuilt (set by the build mod) to prevent redundant builds for iOS and Android.
5
+ */
6
+ export declare function withNanoIconsIos(config: Parameters<typeof withXcodeProject>[0]): ReturnType<typeof withXcodeProject>;
7
+ /**
8
+ * Copy TTFs to Android assets/fonts. Reads paths from config._nanoIconsBuilt.
9
+ */
10
+ export declare function withNanoIconsAndroid(config: Parameters<typeof withDangerousMod>[0]): ReturnType<typeof withDangerousMod>;
11
+ /**
12
+ * Apply iOS and Android font linking. Built font list is read from config (set by build mod).
13
+ */
14
+ export declare function withNanoIconsFontLinking(config: Parameters<typeof withNanoIconsIos>[0]): ReturnType<typeof withNanoIconsAndroid>;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.withNanoIconsIos = withNanoIconsIos;
7
+ exports.withNanoIconsAndroid = withNanoIconsAndroid;
8
+ exports.withNanoIconsFontLinking = withNanoIconsFontLinking;
9
+ const config_plugins_1 = require("@expo/config-plugins");
10
+ const promises_1 = __importDefault(require("fs/promises"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const ANDROID_ASSETS_FONTS_DIR = 'app/src/main/assets/fonts';
13
+ const BUILT_FONTS_KEY = '_nanoIconsBuilt';
14
+ function getBuiltFonts(config) {
15
+ return config[BUILT_FONTS_KEY];
16
+ }
17
+ /**
18
+ * Add TTFs to the iOS project (Resources group + UIAppFonts in Info.plist).
19
+ * Reads cached built font paths from config._nanoIconsBuilt (set by the build mod) to prevent redundant builds for iOS and Android.
20
+ */
21
+ function withNanoIconsIos(config) {
22
+ config = (0, config_plugins_1.withXcodeProject)(config, async (config) => {
23
+ const built = getBuiltFonts(config);
24
+ if (!built?.length)
25
+ return config;
26
+ const ttfPaths = built.map((b) => b.ttfPath);
27
+ const project = config.modResults;
28
+ const platformProjectRoot = config.modRequest.platformProjectRoot;
29
+ config_plugins_1.IOSConfig.XcodeUtils.ensureGroupRecursively(project, 'Resources');
30
+ for (const fontPath of ttfPaths) {
31
+ const relativePath = path_1.default.relative(platformProjectRoot, fontPath);
32
+ config_plugins_1.IOSConfig.XcodeUtils.addResourceFileToGroup({
33
+ filepath: relativePath,
34
+ groupName: 'Resources',
35
+ project,
36
+ isBuildFile: true,
37
+ verbose: true,
38
+ });
39
+ }
40
+ return config;
41
+ });
42
+ config = (0, config_plugins_1.withInfoPlist)(config, async (config) => {
43
+ const built = getBuiltFonts(config);
44
+ if (!built?.length)
45
+ return config;
46
+ const ttfPaths = built.map((b) => b.ttfPath);
47
+ const existingFonts = getUIAppFonts(config.modResults);
48
+ const fontList = ttfPaths.map((f) => path_1.default.basename(f));
49
+ const allFonts = [...existingFonts, ...fontList];
50
+ config.modResults.UIAppFonts = Array.from(new Set(allFonts));
51
+ return config;
52
+ });
53
+ return config;
54
+ }
55
+ function getUIAppFonts(infoPlist) {
56
+ const fonts = infoPlist['UIAppFonts'];
57
+ if (fonts != null &&
58
+ Array.isArray(fonts) &&
59
+ fonts.every((font) => typeof font === 'string')) {
60
+ return fonts;
61
+ }
62
+ return [];
63
+ }
64
+ /**
65
+ * Copy TTFs to Android assets/fonts. Reads paths from config._nanoIconsBuilt.
66
+ */
67
+ function withNanoIconsAndroid(config) {
68
+ return (0, config_plugins_1.withDangerousMod)(config, [
69
+ 'android',
70
+ async (config) => {
71
+ const built = getBuiltFonts(config);
72
+ if (!built?.length)
73
+ return config;
74
+ const fontsDir = path_1.default.join(config.modRequest.platformProjectRoot, ANDROID_ASSETS_FONTS_DIR);
75
+ await promises_1.default.mkdir(fontsDir, { recursive: true });
76
+ for (const b of built) {
77
+ const filename = path_1.default.basename(b.ttfPath);
78
+ const dest = path_1.default.join(fontsDir, filename);
79
+ await promises_1.default.copyFile(b.ttfPath, dest);
80
+ }
81
+ return config;
82
+ },
83
+ ]);
84
+ }
85
+ /**
86
+ * Apply iOS and Android font linking. Built font list is read from config (set by build mod).
87
+ */
88
+ function withNanoIconsFontLinking(config) {
89
+ config = withNanoIconsIos(config);
90
+ config = withNanoIconsAndroid(config);
91
+ return config;
92
+ }
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /**
5
+ * Bare React Native workflow: build icon fonts and link them into the native project.
6
+ *
7
+ * Run from your app root: npx react-native-nano-icons [--verbose]
8
+ *
9
+ * Reads .nanoicons.json (same shape as Expo plugin options) so Expo and bare apps
10
+ * share one config format.
11
+ *
12
+ * Flags:
13
+ * --verbose Show per-SVG processing details and pipeline timing
14
+ */
15
+ const index_js_1 = require("../cli/index.js");
16
+ async function main() {
17
+ const verbose = process.argv.includes('--verbose');
18
+ const level = verbose ? 'verbose' : 'normal';
19
+ const logger = await (0, index_js_1.createOraLogger)(level);
20
+ const config = (0, index_js_1.loadNanoIconsConfig)(process.cwd());
21
+ const built = await (0, index_js_1.buildAllFonts)(config.iconSets, process.cwd(), { logger });
22
+ await (0, index_js_1.linkBare)(process.cwd(), built, logger);
23
+ }
24
+ main().catch((err) => {
25
+ const message = err instanceof Error ? err.message : String(err);
26
+ console.error(message);
27
+ process.exit(1);
28
+ });
@@ -0,0 +1 @@
1
+ export declare const CSS_NAMED_COLORS: Record<string, [number, number, number]>;
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CSS_NAMED_COLORS = void 0;
4
+ exports.CSS_NAMED_COLORS = {
5
+ aliceblue: [240, 248, 255],
6
+ antiquewhite: [250, 235, 215],
7
+ aqua: [0, 255, 255],
8
+ aquamarine: [127, 255, 212],
9
+ azure: [240, 255, 255],
10
+ beige: [245, 245, 220],
11
+ bisque: [255, 228, 196],
12
+ black: [0, 0, 0],
13
+ blanchedalmond: [255, 235, 205],
14
+ blue: [0, 0, 255],
15
+ blueviolet: [138, 43, 226],
16
+ brown: [165, 42, 42],
17
+ burlywood: [222, 184, 135],
18
+ cadetblue: [95, 158, 160],
19
+ chartreuse: [127, 255, 0],
20
+ chocolate: [210, 105, 30],
21
+ coral: [255, 127, 80],
22
+ cornflowerblue: [100, 149, 237],
23
+ cornsilk: [255, 248, 220],
24
+ crimson: [220, 20, 60],
25
+ cyan: [0, 255, 255],
26
+ darkblue: [0, 0, 139],
27
+ darkcyan: [0, 139, 139],
28
+ darkgoldenrod: [184, 134, 11],
29
+ darkgray: [169, 169, 169],
30
+ darkgreen: [0, 100, 0],
31
+ darkgrey: [169, 169, 169],
32
+ darkkhaki: [189, 183, 107],
33
+ darkmagenta: [139, 0, 139],
34
+ darkolivegreen: [85, 107, 47],
35
+ darkorange: [255, 140, 0],
36
+ darkorchid: [153, 50, 204],
37
+ darkred: [139, 0, 0],
38
+ darksalmon: [233, 150, 122],
39
+ darkseagreen: [143, 188, 143],
40
+ darkslateblue: [72, 61, 139],
41
+ darkslategray: [47, 79, 79],
42
+ darkslategrey: [47, 79, 79],
43
+ darkturquoise: [0, 206, 209],
44
+ darkviolet: [148, 0, 211],
45
+ deeppink: [255, 20, 147],
46
+ deepskyblue: [0, 191, 255],
47
+ dimgray: [105, 105, 105],
48
+ dimgrey: [105, 105, 105],
49
+ dodgerblue: [30, 144, 255],
50
+ firebrick: [178, 34, 34],
51
+ floralwhite: [255, 250, 240],
52
+ forestgreen: [34, 139, 34],
53
+ fuchsia: [255, 0, 255],
54
+ gainsboro: [220, 220, 220],
55
+ ghostwhite: [248, 248, 255],
56
+ gold: [255, 215, 0],
57
+ goldenrod: [218, 165, 32],
58
+ gray: [128, 128, 128],
59
+ green: [0, 128, 0],
60
+ greenyellow: [173, 255, 47],
61
+ grey: [128, 128, 128],
62
+ honeydew: [240, 255, 240],
63
+ hotpink: [255, 105, 180],
64
+ indianred: [205, 92, 92],
65
+ indigo: [75, 0, 130],
66
+ ivory: [255, 255, 240],
67
+ khaki: [240, 230, 140],
68
+ lavender: [230, 230, 250],
69
+ lavenderblush: [255, 240, 245],
70
+ lawngreen: [124, 252, 0],
71
+ lemonchiffon: [255, 250, 205],
72
+ lightblue: [173, 216, 230],
73
+ lightcoral: [240, 128, 128],
74
+ lightcyan: [224, 255, 255],
75
+ lightgoldenrodyellow: [250, 250, 210],
76
+ lightgray: [211, 211, 211],
77
+ lightgreen: [144, 238, 144],
78
+ lightgrey: [211, 211, 211],
79
+ lightpink: [255, 182, 193],
80
+ lightsalmon: [255, 160, 122],
81
+ lightseagreen: [32, 178, 170],
82
+ lightskyblue: [135, 206, 250],
83
+ lightslategray: [119, 136, 153],
84
+ lightslategrey: [119, 136, 153],
85
+ lightsteelblue: [176, 196, 222],
86
+ lightyellow: [255, 255, 224],
87
+ lime: [0, 255, 0],
88
+ limegreen: [50, 205, 50],
89
+ linen: [250, 240, 230],
90
+ magenta: [255, 0, 255],
91
+ maroon: [128, 0, 0],
92
+ mediumaquamarine: [102, 205, 170],
93
+ mediumblue: [0, 0, 205],
94
+ mediumorchid: [186, 85, 211],
95
+ mediumpurple: [147, 112, 219],
96
+ mediumseagreen: [60, 179, 113],
97
+ mediumslateblue: [123, 104, 238],
98
+ mediumspringgreen: [0, 250, 154],
99
+ mediumturquoise: [72, 209, 204],
100
+ mediumvioletred: [199, 21, 133],
101
+ midnightblue: [25, 25, 112],
102
+ mintcream: [245, 255, 250],
103
+ mistyrose: [255, 228, 225],
104
+ moccasin: [255, 228, 181],
105
+ navajowhite: [255, 222, 173],
106
+ navy: [0, 0, 128],
107
+ oldlace: [253, 245, 230],
108
+ olive: [128, 128, 0],
109
+ olivedrab: [107, 142, 35],
110
+ orange: [255, 165, 0],
111
+ orangered: [255, 69, 0],
112
+ orchid: [218, 112, 214],
113
+ palegoldenrod: [238, 232, 170],
114
+ palegreen: [152, 251, 152],
115
+ paleturquoise: [175, 238, 238],
116
+ palevioletred: [219, 112, 147],
117
+ papayawhip: [255, 239, 213],
118
+ peachpuff: [255, 218, 185],
119
+ peru: [205, 133, 63],
120
+ pink: [255, 192, 203],
121
+ plum: [221, 160, 221],
122
+ powderblue: [176, 224, 230],
123
+ purple: [128, 0, 128],
124
+ rebeccapurple: [102, 51, 153],
125
+ red: [255, 0, 0],
126
+ rosybrown: [188, 143, 143],
127
+ royalblue: [65, 105, 225],
128
+ saddlebrown: [139, 69, 19],
129
+ salmon: [250, 128, 114],
130
+ sandybrown: [244, 164, 96],
131
+ seagreen: [46, 139, 87],
132
+ seashell: [255, 245, 238],
133
+ sienna: [160, 82, 45],
134
+ silver: [192, 192, 192],
135
+ skyblue: [135, 206, 235],
136
+ slateblue: [106, 90, 205],
137
+ slategray: [112, 128, 144],
138
+ slategrey: [112, 128, 144],
139
+ snow: [255, 250, 250],
140
+ springgreen: [0, 255, 127],
141
+ steelblue: [70, 130, 180],
142
+ tan: [210, 180, 140],
143
+ teal: [0, 128, 128],
144
+ thistle: [216, 191, 216],
145
+ tomato: [255, 99, 71],
146
+ turquoise: [64, 224, 208],
147
+ violet: [238, 130, 238],
148
+ wheat: [245, 222, 179],
149
+ white: [255, 255, 255],
150
+ whitesmoke: [245, 245, 245],
151
+ yellow: [255, 255, 0],
152
+ yellowgreen: [154, 205, 50],
153
+ };
@@ -0,0 +1,9 @@
1
+ export declare function compileTtfFromGlyphSVGs(opts: {
2
+ glyphDir: string;
3
+ outTtfPath: string;
4
+ fontName: string;
5
+ upm: number;
6
+ ascent: number;
7
+ descent: number;
8
+ lineGap?: number;
9
+ }): Promise<void>;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.compileTtfFromGlyphSVGs = compileTtfFromGlyphSVGs;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const node_events_1 = require("node:events");
10
+ const metrics_js_1 = require("./metrics.js");
11
+ const svg2ttf_1 = __importDefault(require("svg2ttf"));
12
+ const parse_js_1 = require("../../utils/parse.js");
13
+ async function writeGlyphStreamToFont(fontStream, svgPath, filename) {
14
+ const codepoint = (0, parse_js_1.parseCodepointFromFilename)(filename);
15
+ const name = node_path_1.default.basename(filename, '.svg');
16
+ return new Promise((resolve, reject) => {
17
+ const glyphStream = node_fs_1.default.createReadStream(svgPath);
18
+ glyphStream.metadata = {
19
+ name,
20
+ unicode: [String.fromCodePoint(codepoint)],
21
+ };
22
+ glyphStream.on('error', reject);
23
+ // Do not add fontStream.on("error", reject) here — one per glyph would exceed
24
+ // Node's default MaxListeners (10). Font stream errors are handled once below.
25
+ fontStream.write(glyphStream);
26
+ glyphStream.on('end', resolve);
27
+ });
28
+ }
29
+ async function compileTtfFromGlyphSVGs(opts) {
30
+ const { SVGIcons2SVGFontStream } = await import('svgicons2svgfont');
31
+ const { glyphDir, outTtfPath, fontName, upm, ascent, descent } = opts;
32
+ const lineGap = opts.lineGap ?? 0;
33
+ const files = node_fs_1.default
34
+ .readdirSync(glyphDir)
35
+ .filter((f) => /^u[0-9a-fA-F]+\.svg$/.test(f))
36
+ .sort((a, b) => (0, parse_js_1.parseCodepointFromFilename)(a) - (0, parse_js_1.parseCodepointFromFilename)(b));
37
+ if (files.length === 0)
38
+ throw new Error(`No glyph SVGs found in: ${glyphDir}`);
39
+ const fontStream = new SVGIcons2SVGFontStream({
40
+ fontName,
41
+ fontHeight: upm,
42
+ normalize: false,
43
+ ascent,
44
+ descent,
45
+ });
46
+ const svgFontChunks = [];
47
+ fontStream.on('data', (c) => svgFontChunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c)));
48
+ // Single error listener for the font stream; per-glyph errors are handled in writeGlyphStreamToFont.
49
+ let fontStreamReject;
50
+ const fontStreamErrorPromise = new Promise((_, rej) => {
51
+ fontStreamReject = rej;
52
+ });
53
+ fontStream.on('error', (err) => fontStreamReject(err));
54
+ for (const f of files) {
55
+ await Promise.race([
56
+ writeGlyphStreamToFont(fontStream, node_path_1.default.join(glyphDir, f), f),
57
+ fontStreamErrorPromise,
58
+ ]);
59
+ }
60
+ fontStream.end();
61
+ await (0, node_events_1.once)(fontStream, 'end');
62
+ const svgFontString = Buffer.concat(svgFontChunks).toString('utf8');
63
+ const ttfRaw = (0, svg2ttf_1.default)(svgFontString);
64
+ const rawBuf = Buffer.from(ttfRaw.buffer);
65
+ const fixedBuf = (0, metrics_js_1.forceTtfMetrics)(rawBuf, upm, ascent, descent, lineGap);
66
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(outTtfPath), { recursive: true });
67
+ node_fs_1.default.writeFileSync(outTtfPath, fixedBuf);
68
+ }
@@ -0,0 +1 @@
1
+ export declare function forceTtfMetrics(ttfBuffer: Buffer, upm: number, ascent: number, descent: number, lineGap: number): Buffer;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.forceTtfMetrics = forceTtfMetrics;
4
+ const fonteditor_core_1 = require("fonteditor-core");
5
+ function forceTtfMetrics(ttfBuffer, upm, ascent, descent, lineGap) {
6
+ const font = fonteditor_core_1.Font.create(ttfBuffer, {
7
+ type: 'ttf',
8
+ hinting: false,
9
+ compound2simple: false,
10
+ combinePath: false,
11
+ });
12
+ const data = font.get();
13
+ data.head.unitsPerEm = upm;
14
+ data.hhea.ascent = ascent;
15
+ data.hhea.descent = -Math.abs(descent);
16
+ data.hhea.lineGap = lineGap;
17
+ data['OS/2'].usWinAscent = ascent;
18
+ data['OS/2'].usWinDescent = Math.abs(descent);
19
+ data['OS/2'].sTypoAscender = ascent;
20
+ data['OS/2'].sTypoDescender = -Math.abs(descent);
21
+ data['OS/2'].sTypoLineGap = lineGap;
22
+ // Set USE_TYPO_METRICS flag (bit 7) in fsSelection.
23
+ // This tells renderers to use sTypoAscender/sTypoDescender/sTypoLineGap for
24
+ // line height calculations instead of the Win metrics (usWinAscent/usWinDescent).
25
+ // Without this flag, Windows and some Android renderers ignore the Typo values
26
+ // set above and fall back to Win metrics, causing icons to appear clipped or
27
+ // misaligned vertically. The bitwise OR preserves all other existing style flags
28
+ // (italic, bold, etc.) while ensuring this bit is always on.
29
+ data['OS/2'].fsSelection = (data['OS/2'].fsSelection || 0) | (1 << 7);
30
+ font.set(data);
31
+ const out = font.write({ type: 'ttf', hinting: false });
32
+ return Buffer.from(out);
33
+ }
@@ -0,0 +1,14 @@
1
+ export type PipelineConfig = {
2
+ fontFamily: string;
3
+ upm: number;
4
+ safeZone: number;
5
+ startUnicode: number;
6
+ };
7
+ export type PipelinePaths = {
8
+ inputDir: string;
9
+ outputDir: string;
10
+ tempDir: string;
11
+ };
12
+ export declare function ensureEmptyDir(dir: string): void;
13
+ /** Create directory if it does not exist; do not remove existing contents (for shared output dirs). */
14
+ export declare function ensureDir(dir: string): void;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ensureEmptyDir = ensureEmptyDir;
7
+ exports.ensureDir = ensureDir;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ function ensureEmptyDir(dir) {
10
+ if (node_fs_1.default.existsSync(dir))
11
+ node_fs_1.default.rmSync(dir, { recursive: true, force: true });
12
+ node_fs_1.default.mkdirSync(dir, { recursive: true });
13
+ }
14
+ /** Create directory if it does not exist; do not remove existing contents (for shared output dirs). */
15
+ function ensureDir(dir) {
16
+ node_fs_1.default.mkdirSync(dir, { recursive: true });
17
+ }
@@ -0,0 +1,3 @@
1
+ export { runPipeline, type PipelineResult } from './run.js';
2
+ export type { PipelineConfig, PipelinePaths } from './config.js';
3
+ export { ensureEmptyDir } from './config.js';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureEmptyDir = exports.runPipeline = void 0;
4
+ var run_js_1 = require("./run.js");
5
+ Object.defineProperty(exports, "runPipeline", { enumerable: true, get: function () { return run_js_1.runPipeline; } });
6
+ var config_js_1 = require("./config.js");
7
+ Object.defineProperty(exports, "ensureEmptyDir", { enumerable: true, get: function () { return config_js_1.ensureEmptyDir; } });
@@ -0,0 +1,11 @@
1
+ import type { PathKitModule, PyodideModule } from '../types.js';
2
+ export declare class PathKitManager {
3
+ private static instance;
4
+ static getInstance(): Promise<PathKitModule>;
5
+ }
6
+ export declare class PyodideManager {
7
+ private static instance;
8
+ static getInstance(): Promise<PyodideModule>;
9
+ static picoFromFile(hostFilePath: string, content?: string): Promise<string>;
10
+ }
11
+ export declare function picoFromFile(hostFilePath: string, content?: string): Promise<string>;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PyodideManager = exports.PathKitManager = void 0;
7
+ exports.picoFromFile = picoFromFile;
8
+ const pyodide_1 = require("pyodide");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const promises_1 = __importDefault(require("node:fs/promises"));
11
+ const svg_pathops_js_1 = require("../svg/svg_pathops.js");
12
+ /** Package root (where package.json lives). */
13
+ function getPackageRoot() {
14
+ // Compiled to lib/commonjs/src/core/pipeline/ — 5 dirs up is package root.
15
+ // Running from source (Jest/ts-node) in src/core/pipeline/ — 3 dirs up is package root.
16
+ return __dirname.includes(`${node_path_1.default.sep}lib${node_path_1.default.sep}`)
17
+ ? node_path_1.default.resolve(__dirname, '../../../../..')
18
+ : node_path_1.default.resolve(__dirname, '../../..');
19
+ }
20
+ class PathKitManager {
21
+ static instance = null;
22
+ static async getInstance() {
23
+ if (this.instance)
24
+ return this.instance;
25
+ const PathKitInit = require('pathkit-wasm/bin/pathkit.js');
26
+ const pathkitJsPath = require.resolve('pathkit-wasm/bin/pathkit.js');
27
+ const pathkitBinDir = node_path_1.default.dirname(pathkitJsPath);
28
+ const pathkitWasmPath = node_path_1.default.join(pathkitBinDir, 'pathkit.wasm');
29
+ const wasmBinary = await promises_1.default.readFile(pathkitWasmPath);
30
+ const pkInit = PathKitInit({
31
+ wasmBinary,
32
+ locateFile: (file) => node_path_1.default.join(pathkitBinDir, file),
33
+ });
34
+ const PathKit = await (typeof pkInit?.ready === 'function'
35
+ ? pkInit.ready()
36
+ : pkInit);
37
+ this.instance = PathKit;
38
+ return PathKit;
39
+ }
40
+ }
41
+ exports.PathKitManager = PathKitManager;
42
+ class PyodideManager {
43
+ static instance = null;
44
+ static async getInstance() {
45
+ if (this.instance)
46
+ return this.instance;
47
+ const pyodideAsmPath = require.resolve('pyodide/pyodide.asm.js');
48
+ const pyodideDir = node_path_1.default.dirname(pyodideAsmPath) + node_path_1.default.sep;
49
+ const py = (await (0, pyodide_1.loadPyodide)({
50
+ indexURL: pyodideDir,
51
+ }));
52
+ py.mountNodeFS('/app', process.cwd());
53
+ const PathKit = await PathKitManager.getInstance();
54
+ py.registerJsModule('_pathops_js', (0, svg_pathops_js_1.buildPathopsBackend)(PathKit));
55
+ const pathopsPyPath = node_path_1.default.join(getPackageRoot(), 'src', 'core', 'shims', 'pathops.py');
56
+ const pathopsPy = await promises_1.default.readFile(pathopsPyPath, 'utf8');
57
+ py.FS.writeFile('/pathops.py', pathopsPy);
58
+ await py.loadPackage(['micropip', 'lxml'], { messageCallback: () => { } });
59
+ await py.runPythonAsync(`
60
+ import sys
61
+ if "/" not in sys.path:
62
+ sys.path.insert(0, "/")
63
+
64
+ import micropip
65
+ await micropip.install("picosvg", deps=False)
66
+
67
+ import pathops
68
+ import picosvg
69
+ `);
70
+ this.instance = py;
71
+ return py;
72
+ }
73
+ static async picoFromFile(hostFilePath, content) {
74
+ const py = await this.getInstance();
75
+ const svgContent = content ?? (await promises_1.default.readFile(hostFilePath, 'utf-8'));
76
+ py.globals.set('_svg_content', svgContent);
77
+ const out = py.runPython(`
78
+ from picosvg.svg import SVG
79
+ svg = SVG.fromstring(_svg_content)
80
+ pico = svg.topicosvg()
81
+ pico.tostring(pretty_print=True)
82
+ `);
83
+ return out;
84
+ }
85
+ }
86
+ exports.PyodideManager = PyodideManager;
87
+ async function picoFromFile(hostFilePath, content) {
88
+ return PyodideManager.picoFromFile(hostFilePath, content);
89
+ }
@@ -0,0 +1,14 @@
1
+ import { type PipelineConfig, type PipelinePaths } from './config.js';
2
+ import type { NanoLogger } from '../types.js';
3
+ export type PipelineResult = {
4
+ ttfPath: string;
5
+ glyphmapPath: string;
6
+ };
7
+ /**
8
+ * Run the font pipeline with given config and paths.
9
+ * Uses the singleton Pyodide/PathKit instance (initialized on first call).
10
+ */
11
+ export declare function runPipeline(config: PipelineConfig, paths: PipelinePaths, options?: {
12
+ logger?: NanoLogger;
13
+ inputHash?: string;
14
+ }): Promise<PipelineResult>;