@uxf/icons-generator 11.128.0 → 12.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.
@@ -1,3 +1,14 @@
1
1
  import type { FaProIconName } from "../fa-pro-types";
2
2
  import { type IconProvider } from "../types";
3
+ /**
4
+ * Download whatever the deprecated npm packages cannot answer, in a single
5
+ * streamed pass. Called by the CLI between loading the config and generating.
6
+ *
7
+ * A failure here is deliberately **not** fatal. Rebuilding without Font Awesome
8
+ * access is a supported mode — the committed `faPro.json` snapshot exists for
9
+ * exactly that — so an unreachable registry must not turn a project that never
10
+ * needed the network into a failing build. {@link getIcon} raises it only if an
11
+ * icon really cannot be resolved from anywhere else.
12
+ */
13
+ export declare function prefetchFaProIcons(): Promise<void>;
3
14
  export declare const faPro: IconProvider<FaProIconName>;
@@ -1,16 +1,32 @@
1
1
  "use strict";
2
- var _a;
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.faPro = void 0;
4
+ exports.prefetchFaProIcons = prefetchFaProIcons;
5
+ /* eslint-disable no-console */
5
6
  const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const _faProTarballCache_1 = require("../utils/_faProTarballCache");
6
9
  const _generateAdapterFallback_1 = require("../utils/_generateAdapterFallback");
7
10
  const _getIconNameForProvider_1 = require("../utils/_getIconNameForProvider");
11
+ const _parseFaSvg_1 = require("../utils/_parseFaSvg");
12
+ const _prefetchRegistry_1 = require("../utils/_prefetchRegistry");
8
13
  const KEY = "faPro";
9
14
  const LEGACY_PACKAGE = "@fortawesome/fontawesome-pro";
10
- // FA's npm naming is inconsistent: the legacy duotone solid keeps the `pro-`
11
- // prefix (`@fortawesome/pro-duotone-svg-icons`) while the v7 duotone-light /
12
- // duotone-regular / duotone-thin variants are published without it.
13
- const NAMESPACE_TO_PACKAGE = {
15
+ /**
16
+ * Per-style npm packages, kept only so projects that still have them installed
17
+ * keep building. The styles Font Awesome 7 added on top of classic/sharp/duotone
18
+ * are not published this way, and installing them costs ~43 MB of `node_modules`
19
+ * per style.
20
+ *
21
+ * FA's npm naming is inconsistent: the legacy duotone solid keeps the `pro-`
22
+ * prefix (`@fortawesome/pro-duotone-svg-icons`) while the v7 duotone-light /
23
+ * duotone-regular / duotone-thin variants are published without it.
24
+ *
25
+ * @deprecated Icons stream from the kit package instead; uninstall the
26
+ * `@fortawesome/*-svg-icons` packages. Removed in the next major — see
27
+ * `docs/migration/fa-pro-kit-stream.md`.
28
+ */
29
+ const DEPRECATED_NAMESPACE_TO_PACKAGE = {
14
30
  brands: "@fortawesome/free-brands-svg-icons",
15
31
  duotone: "@fortawesome/pro-duotone-svg-icons",
16
32
  "duotone-light": "@fortawesome/duotone-light-svg-icons",
@@ -29,18 +45,25 @@ const NAMESPACE_TO_PACKAGE = {
29
45
  solid: "@fortawesome/pro-solid-svg-icons",
30
46
  thin: "@fortawesome/pro-thin-svg-icons",
31
47
  };
32
- const NODE_MODULES_PATH = (_a = process.env.NODE_MODULES_PATH) !== null && _a !== void 0 ? _a : "./node_modules";
48
+ // Where the deprecated per-style lookup resolves from. Without it a bare
49
+ // `require` walks up from this file, which in a monorepo checkout reaches the
50
+ // root `node_modules`; pointing it at an empty directory is how a test drives
51
+ // the streamed path regardless of what happens to be installed.
52
+ const NODE_MODULES_PATH_OVERRIDE = process.env.NODE_MODULES_PATH;
53
+ const NODE_MODULES_PATH = NODE_MODULES_PATH_OVERRIDE !== null && NODE_MODULES_PATH_OVERRIDE !== void 0 ? NODE_MODULES_PATH_OVERRIDE : "./node_modules";
33
54
  if ((0, fs_1.existsSync)(`${NODE_MODULES_PATH}/${LEGACY_PACKAGE}`)) {
34
- throw new Error(`Package "${LEGACY_PACKAGE}" is installed but no longer used. The faPro adapter reads from per-style packages instead. Run \`yarn remove ${LEGACY_PACKAGE}\`.`);
55
+ throw new Error(`Package "${LEGACY_PACKAGE}" is installed but no longer used — the faPro adapter streams icons straight from ${(0, _faProTarballCache_1._getFaProPackage)()} instead. Run \`yarn remove ${LEGACY_PACKAGE}\`.`);
35
56
  }
36
57
  const kebabToPascal = (name) => name
37
58
  .split("-")
38
59
  .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
39
60
  .join("");
40
61
  const tryRequire = (pkg) => {
62
+ // `resolve` already anchors a relative override on the working directory.
63
+ const specifier = NODE_MODULES_PATH_OVERRIDE ? (0, path_1.resolve)(NODE_MODULES_PATH_OVERRIDE, pkg) : pkg;
41
64
  try {
42
65
  // eslint-disable-next-line @typescript-eslint/no-require-imports
43
- return require(pkg);
66
+ return require(specifier);
44
67
  }
45
68
  catch {
46
69
  return null;
@@ -49,31 +72,62 @@ const tryRequire = (pkg) => {
49
72
  const wrapPath = (pathData) => Array.isArray(pathData)
50
73
  ? pathData.map((d) => `<path fill="currentColor" d="${d}"/>`).join("")
51
74
  : `<path fill="currentColor" d="${pathData}"/>`;
52
- const resolvePackage = (namespace, iconName) => {
53
- const pkg = NAMESPACE_TO_PACKAGE[namespace];
75
+ /* ------------------------------------------------- deprecated npm packages */
76
+ const warnedPackages = new Set();
77
+ function warnDeprecatedPackage(pkg) {
78
+ if (warnedPackages.has(pkg)) {
79
+ return;
80
+ }
81
+ warnedPackages.add(pkg);
82
+ console.warn(`[@uxf/icons-generator] Reading icons from "${pkg}" in node_modules. This is deprecated and will be removed in a future major — icons now stream from the Font Awesome kit at generation time. Uninstall the "@fortawesome/*-svg-icons" packages; see docs/migration/fa-pro-kit-stream.md.`);
83
+ }
84
+ /**
85
+ * Resolve from a per-style package, if the project still has one installed.
86
+ *
87
+ * @deprecated Superseded by {@link loadIconFromTarball}. Kept so that upgrading
88
+ * cannot break a project that still installs the `@fortawesome/*-svg-icons`
89
+ * packages; delete together with {@link DEPRECATED_NAMESPACE_TO_PACKAGE}.
90
+ */
91
+ function loadIconFromPackage(iconName) {
92
+ const [namespace, ...rest] = iconName.split(".");
93
+ const name = rest.join(".");
94
+ const pkg = DEPRECATED_NAMESPACE_TO_PACKAGE[namespace];
54
95
  if (!pkg) {
55
- const supported = Object.keys(NAMESPACE_TO_PACKAGE).join(", ");
56
- throw new Error(`Unsupported FA namespace "${namespace}" for icon "${iconName}". Supported namespaces: ${supported}.`);
96
+ return null;
57
97
  }
58
- return pkg;
59
- };
60
- const loadIconFromPackage = (iconName) => {
61
- const [namespace, name] = iconName.split(".");
62
- const pkg = resolvePackage(namespace, iconName);
63
98
  const pkgExports = tryRequire(pkg);
64
99
  if (!pkgExports) {
65
100
  return null;
66
101
  }
67
- const exportKey = `fa${kebabToPascal(name)}`;
68
- const def = pkgExports[exportKey];
69
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
102
+ const def = pkgExports[`fa${kebabToPascal(name)}`];
70
103
  if (!def || !Array.isArray(def.icon)) {
71
- throw new Error(`Icon "${iconName}" not found in "${pkg}" (looked up export "${exportKey}").`);
104
+ // The package is installed but does not carry this icon — let the
105
+ // tarball answer instead of failing the whole run.
106
+ return null;
72
107
  }
108
+ warnDeprecatedPackage(pkg);
73
109
  const [width, height, , , pathData] = def.icon;
74
110
  return { width: Number(width), height: Number(height), path: wrapPath(pathData) };
75
- };
76
- const readFromFallback = (iconName, config) => {
111
+ }
112
+ /* --------------------------------------------------------- streamed source */
113
+ function loadIconFromTarball(iconName) {
114
+ const svgContent = (0, _faProTarballCache_1._readFaProSvg)(iconName);
115
+ if (!svgContent) {
116
+ return null;
117
+ }
118
+ try {
119
+ const { height, pathData, width } = (0, _parseFaSvg_1._parseFaSvg)(svgContent);
120
+ return { width, height, path: wrapPath(pathData) };
121
+ }
122
+ catch (error) {
123
+ // One unreadable SVG must not take the whole run down: name it, and let
124
+ // the committed snapshot answer for it the way a missing icon is handled.
125
+ console.warn(`[@uxf/icons-generator] Could not read "${iconName}" from ${(0, _faProTarballCache_1._getFaProPackage)()}: ${error instanceof Error ? error.message : String(error)}`);
126
+ return null;
127
+ }
128
+ }
129
+ /* --------------------------------------------------------- committed cache */
130
+ function readFromFallback(iconName, config) {
77
131
  const fallbackPath = config.fallbackFilesDirectory + KEY + ".json";
78
132
  let fileContent;
79
133
  try {
@@ -91,27 +145,67 @@ const readFromFallback = (iconName, config) => {
91
145
  height: Number(icon.height),
92
146
  path: decodeURIComponent(icon.path),
93
147
  };
148
+ }
149
+ /* ------------------------------------------------------------------ prefetch */
150
+ /**
151
+ * Icon names collected while `icons.config.js` is being evaluated. The provider
152
+ * API is synchronous, so everything the tarball has to answer is resolved up
153
+ * front by {@link prefetchFaProIcons} before the generator runs.
154
+ */
155
+ const requestedIcons = new Set();
156
+ /**
157
+ * Reason the streamed source could not be read, if it could not. Reported only
158
+ * once an icon turns out to need it, so it does not drown out a plain typo.
159
+ */
160
+ let prefetchError = null;
161
+ /**
162
+ * Download whatever the deprecated npm packages cannot answer, in a single
163
+ * streamed pass. Called by the CLI between loading the config and generating.
164
+ *
165
+ * A failure here is deliberately **not** fatal. Rebuilding without Font Awesome
166
+ * access is a supported mode — the committed `faPro.json` snapshot exists for
167
+ * exactly that — so an unreachable registry must not turn a project that never
168
+ * needed the network into a failing build. {@link getIcon} raises it only if an
169
+ * icon really cannot be resolved from anywhere else.
170
+ */
171
+ async function prefetchFaProIcons() {
172
+ const needed = [...requestedIcons].filter((iconName) => loadIconFromPackage(iconName) === null);
173
+ if (needed.length === 0) {
174
+ return;
175
+ }
176
+ try {
177
+ await (0, _faProTarballCache_1._prefetchFaProIcons)(needed);
178
+ }
179
+ catch (error) {
180
+ prefetchError = error instanceof Error ? error : new Error(String(error));
181
+ console.warn(`[@uxf/icons-generator] Could not read ${(0, _faProTarballCache_1._getFaProPackage)()}, falling back to the committed ${KEY}.json snapshot. Icons already generated will keep working; adding a new one will not.\n${prefetchError.message}`);
182
+ }
183
+ }
184
+ /* ---------------------------------------------------------------- provider */
185
+ function getIcon(iconName, config) {
186
+ var _a;
187
+ const resolved = (_a = loadIconFromPackage(iconName)) !== null && _a !== void 0 ? _a : loadIconFromTarball(iconName);
188
+ if (resolved) {
189
+ (0, _generateAdapterFallback_1._generateAdapterFallback)(KEY, iconName, resolved, config);
190
+ return resolved;
191
+ }
192
+ const fromSnapshot = readFromFallback(iconName, config);
193
+ if (fromSnapshot) {
194
+ return fromSnapshot;
195
+ }
196
+ throw new Error(`Can't resolve icon "${iconName}". It was not found in ${(0, _faProTarballCache_1._getFaProPackage)()}, in any installed "@fortawesome/*-svg-icons" package, or in the committed ${KEY}.json fallback. Check the namespace and the icon name.` +
197
+ (prefetchError ? `\n\nThe package could not be read:\n${prefetchError.message}` : ""));
198
+ }
199
+ const icon = (iconName) => {
200
+ requestedIcons.add(iconName);
201
+ return (config) => getIcon(iconName, config);
94
202
  };
95
- const getIcon = (iconName, config) => {
96
- const iconDefinition = loadIconFromPackage(iconName);
97
- if (iconDefinition) {
98
- (0, _generateAdapterFallback_1._generateAdapterFallback)(KEY, iconName, iconDefinition, config);
99
- return iconDefinition;
100
- }
101
- const fallbackIcon = readFromFallback(iconName, config);
102
- if (fallbackIcon) {
103
- return fallbackIcon;
104
- }
105
- const [namespace] = iconName.split(".");
106
- const pkg = resolvePackage(namespace, iconName);
107
- throw new Error(`Can't resolve icon "${iconName}": package "${pkg}" is not installed. Run \`yarn add -D ${pkg}\` to enable "${namespace}" icons.`);
108
- };
109
- const icon = (iconName) => (config) => getIcon(iconName, config);
110
203
  exports.faPro = {
111
- icon(iconName) {
112
- return (config) => getIcon(iconName, config);
113
- },
114
204
  adapter(icons) {
115
205
  return Object.fromEntries(icons.map((iconName) => [(0, _getIconNameForProvider_1._getIconNameForProvider)(KEY, iconName), icon(iconName)]));
116
206
  },
207
+ icon,
117
208
  };
209
+ // Loading this module is itself the signal that the project uses the adapter,
210
+ // so the CLI never has to know which providers exist.
211
+ (0, _prefetchRegistry_1._registerPrefetch)(prefetchFaProIcons);
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const process_1 = require("process");
7
7
  const yargs_1 = __importDefault(require("yargs"));
8
8
  const _getConfig_1 = require("../utils/_getConfig");
9
+ const _prefetchRegistry_1 = require("../utils/_prefetchRegistry");
9
10
  const index_1 = __importDefault(require("./index"));
10
11
  exports.default = async () => {
11
12
  const cli = (0, yargs_1.default)()
@@ -18,5 +19,10 @@ exports.default = async () => {
18
19
  if (help) {
19
20
  return 0;
20
21
  }
21
- (0, index_1.default)((0, _getConfig_1._getConfig)(configFile));
22
+ // Loading the config is what pulls in the providers it uses and registers
23
+ // every icon they are asked for; the prefetch then resolves them all in one
24
+ // pass, because the provider API itself is synchronous.
25
+ const config = (0, _getConfig_1._getConfig)(configFile);
26
+ await (0, _prefetchRegistry_1._runPrefetch)();
27
+ (0, index_1.default)(config);
22
28
  };
@@ -0,0 +1,48 @@
1
+ export interface FaProSource {
2
+ package: string;
3
+ version: string;
4
+ }
5
+ export interface FaProIndex extends FaProSource {
6
+ /** namespace → sorted icon names. */
7
+ icons: Partial<Record<string, string[]>>;
8
+ schema: number;
9
+ }
10
+ export declare function _getFaProPackage(): string;
11
+ /** The version `src/fa-pro-types.ts` is expected to have been generated from. */
12
+ export declare function _getFaProDefaultVersion(): string;
13
+ /**
14
+ * Resolve the package version to read icons from: {@link DEFAULT_VERSION}
15
+ * unless `UXF_FA_PRO_VERSION` overrides it, and only the literal `latest`
16
+ * consults the registry's mutable dist-tag.
17
+ *
18
+ * The promise is memoised so concurrent callers share one round trip, but a
19
+ * rejection is not: a transient network failure must not be sticky for the rest
20
+ * of the process, because the prefetch swallows it and later callers retry.
21
+ */
22
+ export declare function _resolveFaProSource(): Promise<FaProSource>;
23
+ interface EntryLocation {
24
+ iconName: string;
25
+ namespace: string;
26
+ }
27
+ /**
28
+ * Derive the namespace an archive entry belongs to, or `null` for entries that
29
+ * are not per-icon SVGs (webfonts, sprites, the `svgs-full` duplicates, …).
30
+ *
31
+ * The namespace is the style directory verbatim, so `svgs/sharp-regular/x.svg`
32
+ * is `sharp-regular.x` — the same string the per-style npm packages produced.
33
+ */
34
+ export declare function _locateEntry(name: string): EntryLocation | null;
35
+ /**
36
+ * Make sure every requested `"<namespace>.<icon-name>"` is available to
37
+ * {@link _readFaProSvg}, downloading the package tarball at most once.
38
+ *
39
+ * Icons already in the on-disk cache cost nothing but a file read, and since
40
+ * the version is pinned rather than resolved from a dist-tag, such a run makes
41
+ * no request at all.
42
+ */
43
+ export declare function _prefetchFaProIcons(names: Iterable<string>): Promise<void>;
44
+ /** Read a prefetched icon. Returns `null` when the icon is not in the package. */
45
+ export declare function _readFaProSvg(qualifiedName: string): string | null;
46
+ /** Namespaces the resolved package provides, with their icon names. */
47
+ export declare function _getFaProIndex(): Promise<FaProIndex>;
48
+ export {};