@featherk/icons 0.15.7 → 0.16.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.
- package/README.md +15 -3
- package/bin/featherk-fontawesome-setup.mjs +4 -0
- package/dist/docs-manifest.d.ts +1 -0
- package/dist/docs-manifest.js +7 -5
- package/dist/fontawesome/build.cjs +1 -2
- package/dist/fontawesome/build.d.ts +1 -1
- package/dist/fontawesome/build.mjs +2 -3
- package/dist/fontawesome/catalog.cjs +0 -13
- package/dist/fontawesome/catalog.d.ts +0 -8
- package/dist/fontawesome/catalog.mjs +0 -13
- package/dist/fontawesome/chunks/{generate-BBurrhLv.cjs → generate-B_FxftR7.cjs} +12 -1
- package/dist/fontawesome/chunks/{generate-Zq9q2YZD.mjs → generate-DF9R65sV.mjs} +13 -2
- package/dist/fontawesome/chunks/manifest-CSRIDvaR.mjs +56 -0
- package/dist/fontawesome/chunks/manifest-Yjw62jxG.cjs +55 -0
- package/dist/fontawesome/cli.cjs +25 -40
- package/dist/fontawesome/cli.mjs +25 -40
- package/dist/fontawesome/generate.d.ts +7 -0
- package/dist/fontawesome/manifest.d.ts +50 -0
- package/dist/fontawesome/setup.cjs +482 -0
- package/dist/fontawesome/setup.d.ts +39 -0
- package/dist/fontawesome/setup.mjs +482 -0
- package/dist/fontawesome/vite.cjs +64 -28
- package/dist/fontawesome/vite.d.ts +26 -5
- package/dist/fontawesome/vite.mjs +65 -29
- package/docs/ICONS.md +1 -1
- package/docs/fontawesome/CreateFontAwesomeManifest.md +1 -1
- package/docs/fontawesome/FontAwesome.md +48 -44
- package/docs/fontawesome/FontAwesomeCLI.md +14 -17
- package/docs/fontawesome/FontAwesomeDeveloperSetup.md +134 -0
- package/docs/fontawesome/LICENSING.md +185 -26
- package/package.json +5 -4
- package/dist/fontawesome/catalog.test.d.ts +0 -1
- package/dist/fontawesome/cli.test.d.ts +0 -1
- package/dist/fontawesome/fontawesome-service.test.d.ts +0 -1
- package/dist/fontawesome/generate.test.d.ts +0 -1
- package/dist/fontawesome/normalize.test.d.ts +0 -1
- package/dist/fontawesome/vite.test.d.ts +0 -1
- package/dist/icon-service.test.d.ts +0 -1
- /package/{scripts/fontawesome.mjs → bin/featherk-fontawesome.mjs} +0 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { emitKeypressEvents } from "node:readline";
|
|
5
|
+
import { parseArgs } from "node:util";
|
|
6
|
+
import { fontAwesomeCatalog, resolveFontAwesomeKeys } from "./catalog.mjs";
|
|
7
|
+
import { f as fontAwesomeProSourcesDir, i as isFontAwesomeIsolated, r as readFontAwesomeManifest, a as fontAwesomeManifestKeys } from "./chunks/manifest-CSRIDvaR.mjs";
|
|
8
|
+
const tokenVariable = "FONTAWESOME_PACKAGE_TOKEN";
|
|
9
|
+
const tokenPlaceholder = `\${${tokenVariable}}`;
|
|
10
|
+
const registry = "https://npm.fontawesome.com/";
|
|
11
|
+
const authKey = "//npm.fontawesome.com/:_authToken";
|
|
12
|
+
const pinRegistry = `--@fortawesome:registry=${registry}`;
|
|
13
|
+
const npmrcLines = [
|
|
14
|
+
"# Route @fortawesome packages to the Font Awesome Pro registry.",
|
|
15
|
+
`@fortawesome:registry=${registry}`,
|
|
16
|
+
"# Authenticate using the token from the environment; never commit a token.",
|
|
17
|
+
`${authKey}=${tokenPlaceholder}`
|
|
18
|
+
];
|
|
19
|
+
const workspaceVariables = [
|
|
20
|
+
"npm_config_workspace",
|
|
21
|
+
"npm_config_workspaces",
|
|
22
|
+
"NPM_CONFIG_WORKSPACE",
|
|
23
|
+
"NPM_CONFIG_WORKSPACES"
|
|
24
|
+
];
|
|
25
|
+
const usage = `Usage:
|
|
26
|
+
featherk-fontawesome-setup --manifest <icons.json> [--free-only | --pro-in-app]
|
|
27
|
+
featherk-fontawesome-setup
|
|
28
|
+
|
|
29
|
+
Run from the application root. The manifest contains
|
|
30
|
+
{ "free": [keys], "pro": [keys] }, the same file the Vite plugin reads.
|
|
31
|
+
|
|
32
|
+
--manifest (default): installs exact Free pins into the app's devDependencies
|
|
33
|
+
from public npm, and writes exact Pro pins into an isolated private source
|
|
34
|
+
project, ${fontAwesomeProSourcesDir}/, then installs it with the token. The
|
|
35
|
+
app's own install never needs the token. Rerun after changing the manifest.
|
|
36
|
+
Commit package.json, package-lock.json, and ${fontAwesomeProSourcesDir}/
|
|
37
|
+
(its node_modules is ignored).
|
|
38
|
+
|
|
39
|
+
No arguments: installs ${fontAwesomeProSourcesDir}/ from its committed
|
|
40
|
+
lockfile with the token, for CI and fresh clones. Run it after the app's own
|
|
41
|
+
"npm ci".
|
|
42
|
+
|
|
43
|
+
--free-only: Free keys only from public npm. No token, no source project.
|
|
44
|
+
|
|
45
|
+
--pro-in-app: installs Free and Pro pins into the app's own devDependencies
|
|
46
|
+
and routes @fortawesome to the Pro registry in the app's .npmrc (token
|
|
47
|
+
placeholder only). Every install of the app, including CI, then needs the
|
|
48
|
+
token for "npm ci --ignore-scripts". Use only when the isolated project is not
|
|
49
|
+
an option.
|
|
50
|
+
|
|
51
|
+
The token comes from ${tokenVariable} or a hidden prompt in an interactive
|
|
52
|
+
terminal. --manifest setup changes package.json and is refused in CI
|
|
53
|
+
(CI, TEAMCITY_VERSION, or GITHUB_ACTIONS is set). Do not export the token
|
|
54
|
+
persistently.`;
|
|
55
|
+
function promptForTerminalToken() {
|
|
56
|
+
const input = process.stdin;
|
|
57
|
+
if (!input.isTTY || typeof input.setRawMode !== "function") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`${tokenVariable} is not set and this is not an interactive terminal. Provide it through an approved CI secret environment.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
emitKeypressEvents(input);
|
|
63
|
+
process.stdout.write("Font Awesome Pro Package Token (input hidden): ");
|
|
64
|
+
const wasRaw = input.isRaw;
|
|
65
|
+
input.setRawMode(true);
|
|
66
|
+
input.resume();
|
|
67
|
+
return new Promise((resolveToken, rejectToken) => {
|
|
68
|
+
const characters = [];
|
|
69
|
+
const finish = (error) => {
|
|
70
|
+
input.off("keypress", onKeypress);
|
|
71
|
+
input.setRawMode(wasRaw);
|
|
72
|
+
input.pause();
|
|
73
|
+
process.stdout.write("\n");
|
|
74
|
+
if (error) rejectToken(error);
|
|
75
|
+
else resolveToken(characters.join(""));
|
|
76
|
+
};
|
|
77
|
+
const onKeypress = (character, key = {}) => {
|
|
78
|
+
if (key.ctrl && key.name === "c") {
|
|
79
|
+
finish(new Error("Token entry cancelled."));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (key.name === "return" || key.name === "enter") {
|
|
83
|
+
finish();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (key.name === "backspace") {
|
|
87
|
+
if (characters.length) {
|
|
88
|
+
characters.pop();
|
|
89
|
+
process.stdout.write("\b \b");
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!key.ctrl && !key.meta && character && !character.startsWith("\x1B")) {
|
|
94
|
+
for (const item of character) {
|
|
95
|
+
if (item >= " " && item <= "~") {
|
|
96
|
+
characters.push(item);
|
|
97
|
+
process.stdout.write("*");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
input.on("keypress", onKeypress);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const spawnNpm = ({ npmCli, args, cwd, env }) => {
|
|
106
|
+
const result = spawnSync(process.execPath, [npmCli, ...args], {
|
|
107
|
+
cwd,
|
|
108
|
+
env,
|
|
109
|
+
stdio: "inherit"
|
|
110
|
+
});
|
|
111
|
+
if (result.error) throw result.error;
|
|
112
|
+
return result.status ?? 1;
|
|
113
|
+
};
|
|
114
|
+
async function readToken(context) {
|
|
115
|
+
const token = context.env[tokenVariable] || await context.promptForToken();
|
|
116
|
+
if (!token) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"A Font Awesome Pro Package Token is required to install Pro packages."
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return token;
|
|
122
|
+
}
|
|
123
|
+
function requireNpm(context) {
|
|
124
|
+
const npmCli = context.env.npm_execpath;
|
|
125
|
+
if (!npmCli) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
"Run this command through an npm script or npx so npm is available."
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return npmCli;
|
|
131
|
+
}
|
|
132
|
+
async function readKeys(values, context) {
|
|
133
|
+
if (!values.manifest) {
|
|
134
|
+
throw new Error("Provide --manifest. Use --help for usage.");
|
|
135
|
+
}
|
|
136
|
+
const manifest = await readFontAwesomeManifest(
|
|
137
|
+
resolve(context.cwd, values.manifest)
|
|
138
|
+
);
|
|
139
|
+
return fontAwesomeManifestKeys(manifest);
|
|
140
|
+
}
|
|
141
|
+
function resolvePins(keys) {
|
|
142
|
+
const tiers = new Map(
|
|
143
|
+
fontAwesomeCatalog.map((entry) => [entry.key, entry.tier])
|
|
144
|
+
);
|
|
145
|
+
const pins = /* @__PURE__ */ new Map();
|
|
146
|
+
for (const selection of resolveFontAwesomeKeys(keys, { allowPro: true })) {
|
|
147
|
+
const spec = selection.package === selection.registryPackage ? selection.version : `npm:${selection.registryPackage}@${selection.version}`;
|
|
148
|
+
const existing = pins.get(selection.package);
|
|
149
|
+
if (existing && existing.spec !== spec) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Conflicting pins for ${selection.package}: ${existing.spec} and ${spec}.`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (!existing) {
|
|
155
|
+
pins.set(selection.package, {
|
|
156
|
+
tier: tiers.get(selection.key),
|
|
157
|
+
spec,
|
|
158
|
+
key: selection.key
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return pins;
|
|
163
|
+
}
|
|
164
|
+
function checkNpmrc(config, file) {
|
|
165
|
+
let fontAwesomeRegistry;
|
|
166
|
+
let hasAuth = false;
|
|
167
|
+
for (const raw of config.split(/\r?\n/)) {
|
|
168
|
+
const line = raw.trim();
|
|
169
|
+
if (!line || line.startsWith("#") || line.startsWith(";")) continue;
|
|
170
|
+
const separator = line.indexOf("=");
|
|
171
|
+
const key = (separator < 0 ? line : line.slice(0, separator)).trim();
|
|
172
|
+
const value = separator < 0 ? "" : line.slice(separator + 1).trim();
|
|
173
|
+
if (key === "@fortawesome:registry") {
|
|
174
|
+
fontAwesomeRegistry = value;
|
|
175
|
+
} else if (key === authKey && value === tokenPlaceholder) {
|
|
176
|
+
hasAuth = true;
|
|
177
|
+
} else if (key.includes("npm.fontawesome.com")) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`${file} may configure npm.fontawesome.com only as ${authKey}=${tokenPlaceholder}; remove "${key}".`
|
|
180
|
+
);
|
|
181
|
+
} else if (line.includes(tokenVariable)) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`${file} references ${tokenVariable} in "${key}"; the token may only authenticate npm.fontawesome.com.`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (fontAwesomeRegistry !== void 0 && fontAwesomeRegistry.replace(/\/?$/, "/") !== registry) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`${file} routes @fortawesome to ${fontAwesomeRegistry}; the Pro token may only be sent to ${registry}.`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return { fontAwesomeRegistry, hasAuth };
|
|
193
|
+
}
|
|
194
|
+
function installIntoApp(pins, pro, token, context) {
|
|
195
|
+
const npmCli = requireNpm(context);
|
|
196
|
+
const environment = { ...context.env };
|
|
197
|
+
delete environment[tokenVariable];
|
|
198
|
+
if (pro && token) environment[tokenVariable] = token;
|
|
199
|
+
const specs = [...pins].map(([name, pin]) => `${name}@${pin.spec}`);
|
|
200
|
+
const status = context.runNpm({
|
|
201
|
+
npmCli,
|
|
202
|
+
args: [
|
|
203
|
+
"install",
|
|
204
|
+
"--save-dev",
|
|
205
|
+
"--save-exact",
|
|
206
|
+
"--ignore-scripts",
|
|
207
|
+
"--no-audit",
|
|
208
|
+
"--no-fund",
|
|
209
|
+
...pro ? [pinRegistry] : [],
|
|
210
|
+
...specs
|
|
211
|
+
],
|
|
212
|
+
cwd: context.cwd,
|
|
213
|
+
env: environment
|
|
214
|
+
});
|
|
215
|
+
if (status === 0) {
|
|
216
|
+
context.log(
|
|
217
|
+
`Installed ${specs.length} Font Awesome package(s) into the app: ${specs.join(" ")}`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return status;
|
|
221
|
+
}
|
|
222
|
+
function readDependencies(packageJsonPath) {
|
|
223
|
+
try {
|
|
224
|
+
const { dependencies = {} } = JSON.parse(
|
|
225
|
+
readFileSync(packageJsonPath, "utf8")
|
|
226
|
+
);
|
|
227
|
+
return JSON.stringify(
|
|
228
|
+
Object.fromEntries(Object.entries(dependencies).sort())
|
|
229
|
+
);
|
|
230
|
+
} catch {
|
|
231
|
+
return void 0;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function writeSourceProject(sourceRoot, proPins, context) {
|
|
235
|
+
const dependencies = Object.fromEntries(
|
|
236
|
+
[...proPins].map(([name, pin]) => [name, pin.spec]).sort()
|
|
237
|
+
);
|
|
238
|
+
const created = !existsSync(resolve(sourceRoot, "package.json"));
|
|
239
|
+
const lockfilePath = resolve(sourceRoot, "package-lock.json");
|
|
240
|
+
const removeStaleLockfile = existsSync(lockfilePath) && readDependencies(resolve(sourceRoot, "package.json")) !== JSON.stringify(dependencies);
|
|
241
|
+
const files = {
|
|
242
|
+
".npmrc": `${npmrcLines.join("\n")}
|
|
243
|
+
`,
|
|
244
|
+
".gitignore": "node_modules/\n",
|
|
245
|
+
"package.json": `${JSON.stringify(
|
|
246
|
+
{
|
|
247
|
+
name: fontAwesomeProSourcesDir,
|
|
248
|
+
version: "0.0.0",
|
|
249
|
+
private: true,
|
|
250
|
+
dependencies
|
|
251
|
+
},
|
|
252
|
+
null,
|
|
253
|
+
2
|
|
254
|
+
)}
|
|
255
|
+
`
|
|
256
|
+
};
|
|
257
|
+
mkdirSync(sourceRoot, { recursive: true });
|
|
258
|
+
for (const [name, content] of Object.entries(files)) {
|
|
259
|
+
writeFileSync(resolve(sourceRoot, name), content);
|
|
260
|
+
}
|
|
261
|
+
if (removeStaleLockfile) rmSync(lockfilePath);
|
|
262
|
+
context.log(
|
|
263
|
+
`${created ? "Created" : "Updated"} ${fontAwesomeProSourcesDir}/ with ${proPins.size} Pro package(s).`
|
|
264
|
+
);
|
|
265
|
+
if (removeStaleLockfile) {
|
|
266
|
+
context.log(
|
|
267
|
+
`Removed ${fontAwesomeProSourcesDir}/package-lock.json because the Pro pins changed; this install regenerates it.`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async function installSourceProject(sourceRoot, token, context) {
|
|
272
|
+
const npmCli = requireNpm(context);
|
|
273
|
+
const npmrc = `${fontAwesomeProSourcesDir}/.npmrc`;
|
|
274
|
+
const { fontAwesomeRegistry, hasAuth } = checkNpmrc(
|
|
275
|
+
readFileSync(resolve(sourceRoot, ".npmrc"), "utf8"),
|
|
276
|
+
npmrc
|
|
277
|
+
);
|
|
278
|
+
if (!fontAwesomeRegistry || !hasAuth) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`${npmrc} must contain @fortawesome:registry=${registry} and ${authKey}=${tokenPlaceholder}. Rerun with --manifest to rewrite it.`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const locked = existsSync(resolve(sourceRoot, "package-lock.json"));
|
|
284
|
+
if (context.isCi && !locked) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`${fontAwesomeProSourcesDir}/package-lock.json is required in CI. Run setup with --manifest locally first and commit the lockfile.`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
const environment = {
|
|
290
|
+
...context.env,
|
|
291
|
+
[tokenVariable]: await readToken(context)
|
|
292
|
+
};
|
|
293
|
+
for (const name of workspaceVariables) delete environment[name];
|
|
294
|
+
const status = context.runNpm({
|
|
295
|
+
npmCli,
|
|
296
|
+
args: [
|
|
297
|
+
locked ? "ci" : "install",
|
|
298
|
+
"--workspaces=false",
|
|
299
|
+
"--ignore-scripts",
|
|
300
|
+
"--no-audit",
|
|
301
|
+
"--no-fund",
|
|
302
|
+
pinRegistry
|
|
303
|
+
],
|
|
304
|
+
cwd: sourceRoot,
|
|
305
|
+
env: environment
|
|
306
|
+
});
|
|
307
|
+
if (status !== 0) return status;
|
|
308
|
+
const lockfile = `${fontAwesomeProSourcesDir}/package-lock.json`;
|
|
309
|
+
context.log(
|
|
310
|
+
locked ? `Pro sources installed from ${lockfile}.` : `Pro sources installed. Review and commit ${lockfile}.`
|
|
311
|
+
);
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
async function setupFromManifest(values, context) {
|
|
315
|
+
const { log } = context;
|
|
316
|
+
const freeOnly = Boolean(values["free-only"]);
|
|
317
|
+
const proInApp = Boolean(values["pro-in-app"]);
|
|
318
|
+
if (freeOnly && proInApp) {
|
|
319
|
+
throw new Error("Use either --free-only or --pro-in-app, not both.");
|
|
320
|
+
}
|
|
321
|
+
if (context.isCi) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
`--manifest setup changes package.json and is a local step. In CI, run the app's own "npm ci", then featherk-fontawesome-setup without arguments to install ${fontAwesomeProSourcesDir}/.`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
const appRoot = context.cwd;
|
|
327
|
+
if (!existsSync(resolve(appRoot, "package.json"))) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
"Run this command from the application root (package.json not found)."
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
const pins = resolvePins(await readKeys(values, context));
|
|
333
|
+
if (!pins.size) {
|
|
334
|
+
throw new Error("No Font Awesome keys selected.");
|
|
335
|
+
}
|
|
336
|
+
const freePins = new Map([...pins].filter(([, pin]) => pin.tier !== "pro"));
|
|
337
|
+
const proPins = new Map([...pins].filter(([, pin]) => pin.tier === "pro"));
|
|
338
|
+
if (freeOnly && proPins.size) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`--free-only cannot install Pro keys: ${[...proPins.values()].map((pin) => pin.key).join(", ")}.`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
const npmrcPath = resolve(appRoot, ".npmrc");
|
|
344
|
+
const config = existsSync(npmrcPath) ? readFileSync(npmrcPath, "utf8") : "";
|
|
345
|
+
const { fontAwesomeRegistry, hasAuth } = checkNpmrc(config, ".npmrc");
|
|
346
|
+
const sourceRoot = resolve(appRoot, fontAwesomeProSourcesDir);
|
|
347
|
+
const isolated = isFontAwesomeIsolated(appRoot);
|
|
348
|
+
if (proInApp && isolated) {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`${fontAwesomeProSourcesDir}/ exists, so this app keeps Pro in the isolated source project. Run without --pro-in-app, or remove ${fontAwesomeProSourcesDir}/ first.`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (!proInApp && fontAwesomeRegistry) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
`.npmrc routes @fortawesome to ${fontAwesomeRegistry}, so this app installs Pro in the app itself. Rerun with --pro-in-app.`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
if (freeOnly && isolated) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`${fontAwesomeProSourcesDir}/ exists, so this app uses Pro. Run without --free-only, or remove ${fontAwesomeProSourcesDir}/ first.`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
if (proInApp) {
|
|
364
|
+
const token = proPins.size ? await readToken(context) : void 0;
|
|
365
|
+
if (proPins.size) {
|
|
366
|
+
const missing = [];
|
|
367
|
+
if (!fontAwesomeRegistry) missing.push(npmrcLines[0], npmrcLines[1]);
|
|
368
|
+
if (!hasAuth) missing.push(npmrcLines[2], npmrcLines[3]);
|
|
369
|
+
if (missing.length) {
|
|
370
|
+
const separator = config && !config.endsWith("\n") ? "\n" : "";
|
|
371
|
+
writeFileSync(
|
|
372
|
+
npmrcPath,
|
|
373
|
+
`${config}${separator}${missing.join("\n")}
|
|
374
|
+
`
|
|
375
|
+
);
|
|
376
|
+
log(
|
|
377
|
+
`${config ? "Updated" : "Created"} .npmrc with the Font Awesome registry and token placeholder.`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const status2 = installIntoApp(pins, proPins.size > 0, token, context);
|
|
382
|
+
if (status2 !== 0) return status2;
|
|
383
|
+
log(lifecycleNote);
|
|
384
|
+
log(
|
|
385
|
+
`Next: commit .npmrc, package.json, and package-lock.json. Configure the Vite plugin with { manifest }. Every later install, including CI, gives ${tokenVariable} only to "npm ci --ignore-scripts"; run needed install scripts and builds without it.`
|
|
386
|
+
);
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
389
|
+
if (freePins.size) {
|
|
390
|
+
const status2 = installIntoApp(freePins, false, void 0, context);
|
|
391
|
+
if (status2 !== 0) return status2;
|
|
392
|
+
log(lifecycleNote);
|
|
393
|
+
}
|
|
394
|
+
if (!proPins.size) {
|
|
395
|
+
if (isolated) {
|
|
396
|
+
log(
|
|
397
|
+
`The manifest has no Pro keys; ${fontAwesomeProSourcesDir}/ is no longer needed and can be removed.`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
log(
|
|
401
|
+
"Next: commit package.json and package-lock.json. Configure the Vite plugin with { manifest }; no token is needed."
|
|
402
|
+
);
|
|
403
|
+
return 0;
|
|
404
|
+
}
|
|
405
|
+
writeSourceProject(sourceRoot, proPins, context);
|
|
406
|
+
const status = await installSourceProject(sourceRoot, void 0, context);
|
|
407
|
+
if (status !== 0) return status;
|
|
408
|
+
log(
|
|
409
|
+
`Next: commit package.json, package-lock.json, and ${fontAwesomeProSourcesDir}/. Configure the Vite plugin with { manifest }; builds include Pro icons wherever ${fontAwesomeProSourcesDir}/ is installed. In CI, run the app's "npm ci" without the token, then featherk-fontawesome-setup with ${tokenVariable}, then the normal build.`
|
|
410
|
+
);
|
|
411
|
+
return 0;
|
|
412
|
+
}
|
|
413
|
+
const lifecycleNote = "Lifecycle scripts were skipped. If needed, run npm rebuild for dependency scripts and the app's own scripts (e.g. npm run prepare --if-present).";
|
|
414
|
+
const removedFlags = [
|
|
415
|
+
"--keys",
|
|
416
|
+
"--source-root",
|
|
417
|
+
"--init",
|
|
418
|
+
"--force",
|
|
419
|
+
"--refresh-lock"
|
|
420
|
+
];
|
|
421
|
+
async function runFontAwesomeSetup(args, io = {}) {
|
|
422
|
+
const cwd = io.cwd ?? process.cwd();
|
|
423
|
+
const env = io.env ?? process.env;
|
|
424
|
+
const stderr = io.stderr ?? console.error;
|
|
425
|
+
const context = {
|
|
426
|
+
cwd,
|
|
427
|
+
env,
|
|
428
|
+
// TeamCity and GitHub Actions are detected directly; TeamCity does not set CI.
|
|
429
|
+
isCi: Boolean(env.CI) && env.CI !== "false" || Boolean(env.TEAMCITY_VERSION || env.GITHUB_ACTIONS),
|
|
430
|
+
log: io.stdout ?? console.log,
|
|
431
|
+
promptForToken: io.promptForToken ?? promptForTerminalToken,
|
|
432
|
+
runNpm: io.runNpm ?? spawnNpm
|
|
433
|
+
};
|
|
434
|
+
try {
|
|
435
|
+
const removed = removedFlags.find(
|
|
436
|
+
(flag) => args.some((arg) => arg === flag || arg.startsWith(`${flag}=`))
|
|
437
|
+
);
|
|
438
|
+
if (removed) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
`${removed} was removed in 0.16.0. Run setup with --manifest; Pro sources always live in ${fontAwesomeProSourcesDir}/, and rerunning with --manifest updates them. Use --help for usage.`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
const { values } = parseArgs({
|
|
444
|
+
args: [...args],
|
|
445
|
+
options: {
|
|
446
|
+
"free-only": { type: "boolean" },
|
|
447
|
+
"pro-in-app": { type: "boolean" },
|
|
448
|
+
manifest: { type: "string" },
|
|
449
|
+
help: { type: "boolean", short: "h" }
|
|
450
|
+
},
|
|
451
|
+
strict: true,
|
|
452
|
+
allowPositionals: false
|
|
453
|
+
});
|
|
454
|
+
if (values.help) {
|
|
455
|
+
context.log(usage);
|
|
456
|
+
return 0;
|
|
457
|
+
}
|
|
458
|
+
if (values.manifest !== void 0) {
|
|
459
|
+
return await setupFromManifest(values, context);
|
|
460
|
+
}
|
|
461
|
+
if (values["free-only"] || values["pro-in-app"]) {
|
|
462
|
+
throw new Error("--free-only and --pro-in-app require --manifest.");
|
|
463
|
+
}
|
|
464
|
+
if (!isFontAwesomeIsolated(cwd)) {
|
|
465
|
+
throw new Error(
|
|
466
|
+
`${fontAwesomeProSourcesDir}/package.json was not found. Run from the application root; for first-time setup, run with --manifest <icons.json>.`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
return await installSourceProject(
|
|
470
|
+
resolve(cwd, fontAwesomeProSourcesDir),
|
|
471
|
+
void 0,
|
|
472
|
+
context
|
|
473
|
+
);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
stderr(error instanceof Error ? error.message : "Unknown error");
|
|
476
|
+
return 1;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
export {
|
|
480
|
+
checkNpmrc,
|
|
481
|
+
runFontAwesomeSetup
|
|
482
|
+
};
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const node_path = require("node:path");
|
|
4
4
|
const promises = require("node:fs/promises");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const generate = require("./chunks/generate-B_FxftR7.cjs");
|
|
6
|
+
const manifest = require("./chunks/manifest-Yjw62jxG.cjs");
|
|
7
7
|
const virtualId = "virtual:featherk/fontawesome";
|
|
8
8
|
const resolvedId = `\0${virtualId}`;
|
|
9
9
|
function dependencyKey(file) {
|
|
@@ -11,20 +11,56 @@ function dependencyKey(file) {
|
|
|
11
11
|
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
12
12
|
}
|
|
13
13
|
function featherkFontAwesome(options) {
|
|
14
|
+
const legacy = options;
|
|
15
|
+
if (legacy.keys !== void 0 || legacy.allowPro !== void 0) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'Font Awesome Vite options "keys" and "allowPro" were removed in 0.16.0. List the keys in a { free, pro } manifest and pass { manifest: "fontawesome.keys.json" }.'
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
if (legacy.sourceRoot !== void 0 || legacy.proMode !== void 0) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Font Awesome Vite options "sourceRoot" and "proMode" were removed in 0.16.0. Pro sources always live in ${manifest.fontAwesomeProSourcesDir}/ and are used wherever it is installed; pass only { manifest }.`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (typeof options.manifest !== "string" || !options.manifest.trim()) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
'Font Awesome Vite options require a manifest path, such as { manifest: "fontawesome.keys.json" }'
|
|
28
|
+
);
|
|
29
|
+
}
|
|
14
30
|
let root = node_path.resolve(options.root ?? process.cwd());
|
|
31
|
+
let logger;
|
|
15
32
|
let generation;
|
|
16
33
|
let dependencies = /* @__PURE__ */ new Set();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
let watchFiles = [];
|
|
35
|
+
async function resolveSelection() {
|
|
36
|
+
const manifestFile = node_path.resolve(root, options.manifest);
|
|
37
|
+
const manifest$1 = await manifest.readFontAwesomeManifest(manifestFile).catch(
|
|
38
|
+
(error) => {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Cannot read Font Awesome manifest ${node_path.relative(root, manifestFile) || manifestFile}: ${error instanceof Error ? error.message : String(error)}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
return {
|
|
45
|
+
...manifest.selectFontAwesomeManifestKeys(manifest$1, root),
|
|
46
|
+
// Match the real paths that handleHotUpdate compares against.
|
|
47
|
+
manifestFile: await promises.realpath(manifestFile)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function regenerate(selection) {
|
|
51
|
+
generation = (async () => {
|
|
52
|
+
const selected = selection ?? await resolveSelection();
|
|
53
|
+
const result = await generate.generateFaIcons({
|
|
54
|
+
keys: selected.keys,
|
|
55
|
+
root,
|
|
56
|
+
sourceRoot: selected.sourceRoot,
|
|
57
|
+
allowPro: selected.allowPro,
|
|
58
|
+
virtualTypesFile: options.virtualTypesFile === false ? void 0 : options.virtualTypesFile ?? "src/featherk-fontawesome.d.ts"
|
|
59
|
+
});
|
|
60
|
+
watchFiles = [...result.dependencies, selected.manifestFile];
|
|
61
|
+
dependencies = new Set(watchFiles.map(dependencyKey));
|
|
26
62
|
return result;
|
|
27
|
-
});
|
|
63
|
+
})();
|
|
28
64
|
return generation;
|
|
29
65
|
}
|
|
30
66
|
return {
|
|
@@ -32,20 +68,12 @@ function featherkFontAwesome(options) {
|
|
|
32
68
|
enforce: "pre",
|
|
33
69
|
async configResolved(config) {
|
|
34
70
|
root = node_path.resolve(config.root, options.root ?? ".");
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
if (installCommand) {
|
|
39
|
-
console.info(
|
|
40
|
-
`[featherk-fontawesome] Install selected source packages with:
|
|
41
|
-
${installCommand}`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
await regenerate();
|
|
71
|
+
if (config.command === "build") logger = config.logger;
|
|
72
|
+
await regenerate(await resolveSelection());
|
|
45
73
|
},
|
|
46
74
|
async buildStart() {
|
|
47
|
-
|
|
48
|
-
for (const file of
|
|
75
|
+
await regenerate();
|
|
76
|
+
for (const file of watchFiles) this.addWatchFile(file);
|
|
49
77
|
},
|
|
50
78
|
resolveId(id) {
|
|
51
79
|
return id === virtualId ? resolvedId : void 0;
|
|
@@ -55,14 +83,14 @@ ${installCommand}`
|
|
|
55
83
|
return (await (generation ?? regenerate())).moduleCode;
|
|
56
84
|
},
|
|
57
85
|
async configureServer(server) {
|
|
58
|
-
|
|
59
|
-
server.watcher.add(
|
|
86
|
+
await (generation ?? regenerate());
|
|
87
|
+
server.watcher.add(watchFiles);
|
|
60
88
|
},
|
|
61
89
|
async handleHotUpdate(context) {
|
|
62
90
|
const file = await promises.realpath(context.file).catch(() => context.file);
|
|
63
91
|
if (!dependencies.has(dependencyKey(file))) return void 0;
|
|
64
|
-
|
|
65
|
-
context.server.watcher.add(
|
|
92
|
+
await regenerate();
|
|
93
|
+
context.server.watcher.add(watchFiles);
|
|
66
94
|
const module2 = context.server.moduleGraph.getModuleById(resolvedId);
|
|
67
95
|
if (module2) context.server.moduleGraph.invalidateModule(module2);
|
|
68
96
|
context.server.ws.send({ type: "full-reload" });
|
|
@@ -77,6 +105,14 @@ ${installCommand}`
|
|
|
77
105
|
source: result.notices
|
|
78
106
|
});
|
|
79
107
|
}
|
|
108
|
+
},
|
|
109
|
+
async closeBundle() {
|
|
110
|
+
const result = await generation?.catch(() => void 0);
|
|
111
|
+
if (logger && result) {
|
|
112
|
+
logger.info(
|
|
113
|
+
`[featherk-fontawesome] Generated ${generate.summarizeFaIcons(result.provenance)}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
80
116
|
}
|
|
81
117
|
};
|
|
82
118
|
}
|
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for the Font Awesome virtual module plugin. Selections come
|
|
4
|
+
* from the same keys-only `{ free, pro }` manifest used by
|
|
5
|
+
* `featherk-fontawesome-setup`.
|
|
6
|
+
*
|
|
7
|
+
* When the project has a `fontawesome-pro-sources/` source project (the
|
|
8
|
+
* default setup), the manifest's `pro` keys are generated from it only in
|
|
9
|
+
* builds where that project is installed. Otherwise every key comes from the
|
|
10
|
+
* project's own installed packages.
|
|
11
|
+
*/
|
|
12
|
+
export interface FontAwesomeViteOptions {
|
|
13
|
+
/** Keys-only manifest path, relative to the project root. */
|
|
14
|
+
manifest: string;
|
|
15
|
+
/** Project root, relative to Vite's root. */
|
|
16
|
+
root?: string;
|
|
5
17
|
virtualTypesFile?: string | false;
|
|
18
|
+
/** Removed in 0.16.0; list keys in the manifest instead. */
|
|
19
|
+
keys?: never;
|
|
20
|
+
/** Removed in 0.16.0; Pro selection follows the manifest and layout. */
|
|
21
|
+
allowPro?: never;
|
|
22
|
+
/** Removed in 0.16.0; Pro sources always live in `fontawesome-pro-sources/`. */
|
|
23
|
+
sourceRoot?: never;
|
|
6
24
|
}
|
|
7
25
|
/**
|
|
8
26
|
* Creates the Vite plugin for `virtual:featherk/fontawesome`.
|
|
9
27
|
*
|
|
10
|
-
*
|
|
11
|
-
* notices are emitted into production builds.
|
|
28
|
+
* Keys are read from the manifest and resolved during Vite config setup, and
|
|
29
|
+
* their license notices are emitted into production builds. Mode gating
|
|
30
|
+
* selects which keys to generate; it is not license authorization.
|
|
31
|
+
*
|
|
32
|
+
* @throws If `manifest` is missing or a removed option is provided.
|
|
12
33
|
*/
|
|
13
34
|
export declare function featherkFontAwesome(options: FontAwesomeViteOptions): Plugin;
|