@featherk/icons 0.15.6 → 0.15.8
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/bin/featherk-fontawesome-setup.mjs +4 -0
- package/dist/docs-manifest.js +5 -5
- package/dist/fontawesome/chunks/manifest-BB3q10t1.mjs +20 -0
- package/dist/fontawesome/chunks/manifest-C15Xa22L.cjs +19 -0
- package/dist/fontawesome/cli.cjs +5 -17
- package/dist/fontawesome/cli.mjs +5 -17
- package/dist/fontawesome/manifest.d.ts +14 -0
- package/dist/fontawesome/setup.cjs +477 -0
- package/dist/fontawesome/setup.d.ts +39 -0
- package/dist/fontawesome/setup.mjs +477 -0
- package/docs/ICONS.md +1 -1
- package/docs/fontawesome/FontAwesomeCLI.md +2 -0
- package/docs/fontawesome/LICENSING.md +140 -16
- 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
package/dist/docs-manifest.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by scripts/generate-docs-manifest.mjs. Do not edit.
|
|
2
2
|
export const packageName = "@featherk/icons";
|
|
3
3
|
|
|
4
|
-
export const version = "0.15.
|
|
4
|
+
export const version = "0.15.8";
|
|
5
5
|
|
|
6
6
|
export const paths = {
|
|
7
7
|
CreateFontAwesomeManifest: "fontawesome/CreateFontAwesomeManifest.md",
|
|
@@ -11,10 +11,10 @@ export const paths = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export const docs = {
|
|
14
|
-
CreateFontAwesomeManifest: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.
|
|
15
|
-
FontAwesome: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.
|
|
16
|
-
FontAwesomeCLI: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.
|
|
17
|
-
LICENSING: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.
|
|
14
|
+
CreateFontAwesomeManifest: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.8/docs/fontawesome/CreateFontAwesomeManifest.md",
|
|
15
|
+
FontAwesome: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.8/docs/fontawesome/FontAwesome.md",
|
|
16
|
+
FontAwesomeCLI: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.8/docs/fontawesome/FontAwesomeCLI.md",
|
|
17
|
+
LICENSING: "https://cdn.jsdelivr.net/npm/@featherk/icons@0.15.8/docs/fontawesome/LICENSING.md",
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export const docUrl = (name, host = "https://cdn.jsdelivr.net/npm") =>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
const isKeyList = (value) => Array.isArray(value) && value.every((key) => typeof key === "string");
|
|
3
|
+
function parseFontAwesomeManifest(manifest) {
|
|
4
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
5
|
+
throw new Error("Font Awesome manifest must be a JSON object");
|
|
6
|
+
}
|
|
7
|
+
const { free, pro } = manifest;
|
|
8
|
+
if (!isKeyList(free) || pro !== void 0 && !isKeyList(pro)) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
'Font Awesome manifest must contain "free" and optional "pro" key arrays'
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
return { free, pro: pro ?? [] };
|
|
14
|
+
}
|
|
15
|
+
async function readFontAwesomeManifest(path) {
|
|
16
|
+
return parseFontAwesomeManifest(JSON.parse(await readFile(path, "utf8")));
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
readFontAwesomeManifest as r
|
|
20
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const promises = require("node:fs/promises");
|
|
3
|
+
const isKeyList = (value) => Array.isArray(value) && value.every((key) => typeof key === "string");
|
|
4
|
+
function parseFontAwesomeManifest(manifest) {
|
|
5
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
6
|
+
throw new Error("Font Awesome manifest must be a JSON object");
|
|
7
|
+
}
|
|
8
|
+
const { free, pro } = manifest;
|
|
9
|
+
if (!isKeyList(free) || pro !== void 0 && !isKeyList(pro)) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
'Font Awesome manifest must contain "free" and optional "pro" key arrays'
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return { free, pro: pro ?? [] };
|
|
15
|
+
}
|
|
16
|
+
async function readFontAwesomeManifest(path) {
|
|
17
|
+
return parseFontAwesomeManifest(JSON.parse(await promises.readFile(path, "utf8")));
|
|
18
|
+
}
|
|
19
|
+
exports.readFontAwesomeManifest = readFontAwesomeManifest;
|
package/dist/fontawesome/cli.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const promises = require("node:fs/promises");
|
|
4
3
|
const node_path = require("node:path");
|
|
5
4
|
const node_util = require("node:util");
|
|
6
5
|
const fontawesome_catalog = require("./catalog.cjs");
|
|
7
6
|
const generate = require("./chunks/generate-BBurrhLv.cjs");
|
|
7
|
+
const manifest = require("./chunks/manifest-C15Xa22L.cjs");
|
|
8
8
|
const usage = `Usage: featherk-fontawesome --manifest <icons.json> [--root <project>] [--source-root <installed-sources>] [--allow-pro] [--out <icons.mjs>] [--virtual-types <types.d.ts>]
|
|
9
9
|
|
|
10
10
|
The JSON manifest contains { "free": [keys], "pro": [keys] }.
|
|
@@ -40,13 +40,9 @@ async function runFontAwesomeCli(args, io = {}) {
|
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
const root = node_path.resolve(io.cwd ?? process.cwd(), values.root ?? ".");
|
|
43
|
-
const manifest =
|
|
44
|
-
|
|
43
|
+
const manifest$1 = await manifest.readFontAwesomeManifest(
|
|
44
|
+
node_path.resolve(root, values.manifest)
|
|
45
45
|
);
|
|
46
|
-
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
47
|
-
throw new Error("Font Awesome manifest must be a JSON object");
|
|
48
|
-
}
|
|
49
|
-
const record = manifest;
|
|
50
46
|
const common = {
|
|
51
47
|
root,
|
|
52
48
|
sourceRoot: values["source-root"],
|
|
@@ -54,17 +50,9 @@ async function runFontAwesomeCli(args, io = {}) {
|
|
|
54
50
|
outFile: values.out,
|
|
55
51
|
virtualTypesFile: values["virtual-types"]
|
|
56
52
|
};
|
|
57
|
-
const isKeyList = (value) => Array.isArray(value) && value.every((key) => typeof key === "string");
|
|
58
|
-
const freeKeys = record.free;
|
|
59
|
-
const proKeys = record.pro;
|
|
60
|
-
if (!isKeyList(freeKeys) || proKeys !== void 0 && !isKeyList(proKeys)) {
|
|
61
|
-
throw new Error(
|
|
62
|
-
'Font Awesome manifest must contain "free" and optional "pro" key arrays'
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
53
|
const keys = [
|
|
66
|
-
...
|
|
67
|
-
...values["allow-pro"] ?
|
|
54
|
+
...manifest$1.free,
|
|
55
|
+
...values["allow-pro"] ? manifest$1.pro : []
|
|
68
56
|
];
|
|
69
57
|
const installCommand = fontawesome_catalog.getFontAwesomeInstallCommand(keys, {
|
|
70
58
|
allowPro: values["allow-pro"]
|
package/dist/fontawesome/cli.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
1
|
import { resolve } from "node:path";
|
|
3
2
|
import { parseArgs } from "node:util";
|
|
4
3
|
import { getFontAwesomeInstallCommand } from "./catalog.mjs";
|
|
5
4
|
import { g as generateFaIcons } from "./chunks/generate-Zq9q2YZD.mjs";
|
|
5
|
+
import { r as readFontAwesomeManifest } from "./chunks/manifest-BB3q10t1.mjs";
|
|
6
6
|
const usage = `Usage: featherk-fontawesome --manifest <icons.json> [--root <project>] [--source-root <installed-sources>] [--allow-pro] [--out <icons.mjs>] [--virtual-types <types.d.ts>]
|
|
7
7
|
|
|
8
8
|
The JSON manifest contains { "free": [keys], "pro": [keys] }.
|
|
@@ -38,13 +38,9 @@ async function runFontAwesomeCli(args, io = {}) {
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
const root = resolve(io.cwd ?? process.cwd(), values.root ?? ".");
|
|
41
|
-
const manifest =
|
|
42
|
-
|
|
41
|
+
const manifest = await readFontAwesomeManifest(
|
|
42
|
+
resolve(root, values.manifest)
|
|
43
43
|
);
|
|
44
|
-
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
45
|
-
throw new Error("Font Awesome manifest must be a JSON object");
|
|
46
|
-
}
|
|
47
|
-
const record = manifest;
|
|
48
44
|
const common = {
|
|
49
45
|
root,
|
|
50
46
|
sourceRoot: values["source-root"],
|
|
@@ -52,17 +48,9 @@ async function runFontAwesomeCli(args, io = {}) {
|
|
|
52
48
|
outFile: values.out,
|
|
53
49
|
virtualTypesFile: values["virtual-types"]
|
|
54
50
|
};
|
|
55
|
-
const isKeyList = (value) => Array.isArray(value) && value.every((key) => typeof key === "string");
|
|
56
|
-
const freeKeys = record.free;
|
|
57
|
-
const proKeys = record.pro;
|
|
58
|
-
if (!isKeyList(freeKeys) || proKeys !== void 0 && !isKeyList(proKeys)) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
'Font Awesome manifest must contain "free" and optional "pro" key arrays'
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
51
|
const keys = [
|
|
64
|
-
...
|
|
65
|
-
...values["allow-pro"] ?
|
|
52
|
+
...manifest.free,
|
|
53
|
+
...values["allow-pro"] ? manifest.pro : []
|
|
66
54
|
];
|
|
67
55
|
const installCommand = getFontAwesomeInstallCommand(keys, {
|
|
68
56
|
allowPro: values["allow-pro"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Parsed manifest key groups; a missing `pro` group is empty. */
|
|
2
|
+
export interface FontAwesomeKeysManifest {
|
|
3
|
+
free: string[];
|
|
4
|
+
pro: string[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Validates parsed manifest JSON.
|
|
8
|
+
*
|
|
9
|
+
* @throws If the value is not an object with a `free` key array and an
|
|
10
|
+
* optional `pro` key array.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseFontAwesomeManifest(manifest: unknown): FontAwesomeKeysManifest;
|
|
13
|
+
/** Reads and validates a manifest file at an absolute or cwd-relative path. */
|
|
14
|
+
export declare function readFontAwesomeManifest(path: string): Promise<FontAwesomeKeysManifest>;
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const node_child_process = require("node:child_process");
|
|
4
|
+
const node_fs = require("node:fs");
|
|
5
|
+
const node_path = require("node:path");
|
|
6
|
+
const node_readline = require("node:readline");
|
|
7
|
+
const node_util = require("node:util");
|
|
8
|
+
const fontawesome_catalog = require("./catalog.cjs");
|
|
9
|
+
const manifest = require("./chunks/manifest-C15Xa22L.cjs");
|
|
10
|
+
const tokenVariable = "FONTAWESOME_PACKAGE_TOKEN";
|
|
11
|
+
const tokenPlaceholder = `\${${tokenVariable}}`;
|
|
12
|
+
const registry = "https://npm.fontawesome.com/";
|
|
13
|
+
const authKey = "//npm.fontawesome.com/:_authToken";
|
|
14
|
+
const pinRegistry = `--@fortawesome:registry=${registry}`;
|
|
15
|
+
const npmrcLines = [
|
|
16
|
+
"# Route @fortawesome packages to the Font Awesome Pro registry.",
|
|
17
|
+
`@fortawesome:registry=${registry}`,
|
|
18
|
+
"# Authenticate using the token from the environment; never commit a token.",
|
|
19
|
+
`${authKey}=${tokenPlaceholder}`
|
|
20
|
+
];
|
|
21
|
+
const workspaceVariables = [
|
|
22
|
+
"npm_config_workspace",
|
|
23
|
+
"npm_config_workspaces",
|
|
24
|
+
"NPM_CONFIG_WORKSPACE",
|
|
25
|
+
"NPM_CONFIG_WORKSPACES"
|
|
26
|
+
];
|
|
27
|
+
const usage = `Usage:
|
|
28
|
+
featherk-fontawesome-setup (--manifest <icons.json> | --keys <a,b>) [--free-only]
|
|
29
|
+
featherk-fontawesome-setup --source-root <dir> [--refresh-lock]
|
|
30
|
+
featherk-fontawesome-setup --source-root <dir> --init (--manifest <icons.json> | --keys <a,b>) [--force]
|
|
31
|
+
|
|
32
|
+
Default (Free + Pro): run once from the application root on a developer
|
|
33
|
+
machine. Adds the @fortawesome registry lines to .npmrc (token placeholder
|
|
34
|
+
only) and installs exact pins for all selected keys as devDependencies.
|
|
35
|
+
Commit .npmrc, package.json, and package-lock.json. Later installs, including
|
|
36
|
+
CI, give ${tokenVariable} only to "npm ci --ignore-scripts", then run any
|
|
37
|
+
needed install scripts (npm rebuild for dependencies, plus the app's own) and
|
|
38
|
+
builds without it. Do not export the token persistently.
|
|
39
|
+
|
|
40
|
+
--free-only: Free keys only from public npm. No .npmrc changes, no token.
|
|
41
|
+
|
|
42
|
+
--source-root <dir>: install licensed packages into an isolated private
|
|
43
|
+
source project instead (npm ci when its lockfile exists). --init scaffolds
|
|
44
|
+
that project; with --force, a lockfile for different dependencies is removed
|
|
45
|
+
so the next install regenerates it. --refresh-lock runs npm install to update
|
|
46
|
+
the lockfile after editing package.json by hand. In CI, --init and
|
|
47
|
+
--refresh-lock are refused and a lockfile is required.
|
|
48
|
+
|
|
49
|
+
The token comes from ${tokenVariable} or a hidden prompt in an interactive
|
|
50
|
+
terminal. App-root setup changes package.json and is refused in CI
|
|
51
|
+
(CI, TEAMCITY_VERSION, or GITHUB_ACTIONS is set).`;
|
|
52
|
+
function promptForTerminalToken() {
|
|
53
|
+
const input = process.stdin;
|
|
54
|
+
if (!input.isTTY || typeof input.setRawMode !== "function") {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`${tokenVariable} is not set and this is not an interactive terminal. Provide it through an approved CI secret environment.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
node_readline.emitKeypressEvents(input);
|
|
60
|
+
process.stdout.write("Font Awesome Pro Package Token (input hidden): ");
|
|
61
|
+
const wasRaw = input.isRaw;
|
|
62
|
+
input.setRawMode(true);
|
|
63
|
+
input.resume();
|
|
64
|
+
return new Promise((resolveToken, rejectToken) => {
|
|
65
|
+
const characters = [];
|
|
66
|
+
const finish = (error) => {
|
|
67
|
+
input.off("keypress", onKeypress);
|
|
68
|
+
input.setRawMode(wasRaw);
|
|
69
|
+
input.pause();
|
|
70
|
+
process.stdout.write("\n");
|
|
71
|
+
if (error) rejectToken(error);
|
|
72
|
+
else resolveToken(characters.join(""));
|
|
73
|
+
};
|
|
74
|
+
const onKeypress = (character, key = {}) => {
|
|
75
|
+
if (key.ctrl && key.name === "c") {
|
|
76
|
+
finish(new Error("Token entry cancelled."));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (key.name === "return" || key.name === "enter") {
|
|
80
|
+
finish();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (key.name === "backspace") {
|
|
84
|
+
if (characters.length) {
|
|
85
|
+
characters.pop();
|
|
86
|
+
process.stdout.write("\b \b");
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (!key.ctrl && !key.meta && character && !character.startsWith("\x1B")) {
|
|
91
|
+
for (const item of character) {
|
|
92
|
+
if (item >= " " && item <= "~") {
|
|
93
|
+
characters.push(item);
|
|
94
|
+
process.stdout.write("*");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
input.on("keypress", onKeypress);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const spawnNpm = ({ npmCli, args, cwd, env }) => {
|
|
103
|
+
const result = node_child_process.spawnSync(process.execPath, [npmCli, ...args], {
|
|
104
|
+
cwd,
|
|
105
|
+
env,
|
|
106
|
+
stdio: "inherit"
|
|
107
|
+
});
|
|
108
|
+
if (result.error) throw result.error;
|
|
109
|
+
return result.status ?? 1;
|
|
110
|
+
};
|
|
111
|
+
async function readToken(context) {
|
|
112
|
+
const token = context.env[tokenVariable] || await context.promptForToken();
|
|
113
|
+
if (!token) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
"A Font Awesome Pro Package Token is required to install Pro packages."
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return token;
|
|
119
|
+
}
|
|
120
|
+
function requireNpm(context) {
|
|
121
|
+
const npmCli = context.env.npm_execpath;
|
|
122
|
+
if (!npmCli) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"Run this command through an npm script or npx so npm is available."
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return npmCli;
|
|
128
|
+
}
|
|
129
|
+
async function readKeys(values, context) {
|
|
130
|
+
if (values.keys && values.manifest) {
|
|
131
|
+
throw new Error("Provide either --keys or --manifest, not both.");
|
|
132
|
+
}
|
|
133
|
+
if (values.keys) {
|
|
134
|
+
return values.keys.split(",").map((key) => key.trim()).filter(Boolean);
|
|
135
|
+
}
|
|
136
|
+
if (!values.manifest) {
|
|
137
|
+
throw new Error("Provide --manifest or --keys. Use --help for usage.");
|
|
138
|
+
}
|
|
139
|
+
const { free, pro } = await manifest.readFontAwesomeManifest(
|
|
140
|
+
node_path.resolve(context.cwd, values.manifest)
|
|
141
|
+
);
|
|
142
|
+
return [...free, ...pro];
|
|
143
|
+
}
|
|
144
|
+
function resolvePins(keys) {
|
|
145
|
+
const tiers = new Map(
|
|
146
|
+
fontawesome_catalog.fontAwesomeCatalog.map((entry) => [entry.key, entry.tier])
|
|
147
|
+
);
|
|
148
|
+
const pins = /* @__PURE__ */ new Map();
|
|
149
|
+
for (const selection of fontawesome_catalog.resolveFontAwesomeKeys(keys, { allowPro: true })) {
|
|
150
|
+
const spec = selection.package === selection.registryPackage ? selection.version : `npm:${selection.registryPackage}@${selection.version}`;
|
|
151
|
+
const existing = pins.get(selection.package);
|
|
152
|
+
if (existing && existing.spec !== spec) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Conflicting pins for ${selection.package}: ${existing.spec} and ${spec}.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
if (!existing) {
|
|
158
|
+
pins.set(selection.package, {
|
|
159
|
+
tier: tiers.get(selection.key),
|
|
160
|
+
spec,
|
|
161
|
+
key: selection.key
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return pins;
|
|
166
|
+
}
|
|
167
|
+
function checkNpmrc(config, file) {
|
|
168
|
+
let fontAwesomeRegistry;
|
|
169
|
+
let hasAuth = false;
|
|
170
|
+
for (const raw of config.split(/\r?\n/)) {
|
|
171
|
+
const line = raw.trim();
|
|
172
|
+
if (!line || line.startsWith("#") || line.startsWith(";")) continue;
|
|
173
|
+
const separator = line.indexOf("=");
|
|
174
|
+
const key = (separator < 0 ? line : line.slice(0, separator)).trim();
|
|
175
|
+
const value = separator < 0 ? "" : line.slice(separator + 1).trim();
|
|
176
|
+
if (key === "@fortawesome:registry") {
|
|
177
|
+
fontAwesomeRegistry = value;
|
|
178
|
+
} else if (key === authKey && value === tokenPlaceholder) {
|
|
179
|
+
hasAuth = true;
|
|
180
|
+
} else if (key.includes("npm.fontawesome.com")) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`${file} may configure npm.fontawesome.com only as ${authKey}=${tokenPlaceholder}; remove "${key}".`
|
|
183
|
+
);
|
|
184
|
+
} else if (line.includes(tokenVariable)) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`${file} references ${tokenVariable} in "${key}"; the token may only authenticate npm.fontawesome.com.`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (fontAwesomeRegistry !== void 0 && fontAwesomeRegistry.replace(/\/?$/, "/") !== registry) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`${file} routes @fortawesome to ${fontAwesomeRegistry}; the Pro token may only be sent to ${registry}.`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return { fontAwesomeRegistry, hasAuth };
|
|
196
|
+
}
|
|
197
|
+
async function setupAppRoot(values, context) {
|
|
198
|
+
const { log } = context;
|
|
199
|
+
if (context.isCi) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`App-root setup changes package.json and is a local, one-time step. In CI, give ${tokenVariable} only to "npm ci --ignore-scripts", then run needed install scripts and the build without it.`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const appRoot = context.cwd;
|
|
205
|
+
if (!node_fs.existsSync(node_path.resolve(appRoot, "package.json"))) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
"Run this command from the application root (package.json not found)."
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
const freeOnly = Boolean(values["free-only"]);
|
|
211
|
+
const pins = resolvePins(await readKeys(values, context));
|
|
212
|
+
const proPins = [...pins.values()].filter((pin) => pin.tier === "pro");
|
|
213
|
+
if (freeOnly && proPins.length) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`--free-only cannot install Pro keys: ${proPins.map((pin) => pin.key).join(", ")}.`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (!pins.size) {
|
|
219
|
+
throw new Error("No Font Awesome keys selected.");
|
|
220
|
+
}
|
|
221
|
+
const npmrcPath = node_path.resolve(appRoot, ".npmrc");
|
|
222
|
+
const config = node_fs.existsSync(npmrcPath) ? node_fs.readFileSync(npmrcPath, "utf8") : "";
|
|
223
|
+
const { fontAwesomeRegistry, hasAuth } = checkNpmrc(config, ".npmrc");
|
|
224
|
+
if (freeOnly && fontAwesomeRegistry) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`.npmrc routes @fortawesome to ${fontAwesomeRegistry}, so this app is set up for Pro. Run without --free-only.`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const npmCli = requireNpm(context);
|
|
230
|
+
const environment = { ...context.env };
|
|
231
|
+
if (!freeOnly) {
|
|
232
|
+
environment[tokenVariable] = await readToken(context);
|
|
233
|
+
const missing = [];
|
|
234
|
+
if (!fontAwesomeRegistry) missing.push(npmrcLines[0], npmrcLines[1]);
|
|
235
|
+
if (!hasAuth) missing.push(npmrcLines[2], npmrcLines[3]);
|
|
236
|
+
if (missing.length) {
|
|
237
|
+
const separator = config && !config.endsWith("\n") ? "\n" : "";
|
|
238
|
+
node_fs.writeFileSync(npmrcPath, `${config}${separator}${missing.join("\n")}
|
|
239
|
+
`);
|
|
240
|
+
log(
|
|
241
|
+
`${config ? "Updated" : "Created"} .npmrc with the Font Awesome registry and token placeholder.`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const specs = [...pins].map(([name, pin]) => `${name}@${pin.spec}`);
|
|
246
|
+
const status = context.runNpm({
|
|
247
|
+
npmCli,
|
|
248
|
+
args: [
|
|
249
|
+
"install",
|
|
250
|
+
"--save-dev",
|
|
251
|
+
"--save-exact",
|
|
252
|
+
"--ignore-scripts",
|
|
253
|
+
"--no-audit",
|
|
254
|
+
"--no-fund",
|
|
255
|
+
...freeOnly ? [] : [pinRegistry],
|
|
256
|
+
...specs
|
|
257
|
+
],
|
|
258
|
+
cwd: appRoot,
|
|
259
|
+
env: environment
|
|
260
|
+
});
|
|
261
|
+
if (status !== 0) return status;
|
|
262
|
+
log(`Installed ${specs.length} Font Awesome package(s): ${specs.join(" ")}`);
|
|
263
|
+
log(
|
|
264
|
+
"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)."
|
|
265
|
+
);
|
|
266
|
+
if (freeOnly) {
|
|
267
|
+
log(
|
|
268
|
+
"Next: commit package.json and package-lock.json. Keep the Vite plugin's allowPro unset (Free only); no CI token is needed."
|
|
269
|
+
);
|
|
270
|
+
} else {
|
|
271
|
+
log(
|
|
272
|
+
`Next: commit .npmrc, package.json, and package-lock.json. Set allowPro: true in the Vite plugin (no sourceRoot). Later installs, including CI, give ${tokenVariable} only to "npm ci --ignore-scripts"; run needed install scripts and builds without it. See LICENSING.md for the TeamCity setup.`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
function readDependencies(packageJsonPath) {
|
|
278
|
+
try {
|
|
279
|
+
const { dependencies = {} } = JSON.parse(
|
|
280
|
+
node_fs.readFileSync(packageJsonPath, "utf8")
|
|
281
|
+
);
|
|
282
|
+
return JSON.stringify(
|
|
283
|
+
Object.fromEntries(Object.entries(dependencies).sort())
|
|
284
|
+
);
|
|
285
|
+
} catch {
|
|
286
|
+
return void 0;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async function initSourceProject(sourceRoot, values, context) {
|
|
290
|
+
const { log, display } = context;
|
|
291
|
+
if (context.isCi) {
|
|
292
|
+
throw new Error(
|
|
293
|
+
"--init is a local, one-time step. Commit the scaffolded source project instead of generating it in CI."
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const pins = resolvePins(await readKeys(values, context));
|
|
297
|
+
const dependencies = {};
|
|
298
|
+
const skipped = [];
|
|
299
|
+
for (const [name, pin] of pins) {
|
|
300
|
+
if (pin.tier === "pro") dependencies[name] = pin.spec;
|
|
301
|
+
else skipped.push(pin.key);
|
|
302
|
+
}
|
|
303
|
+
if (!Object.keys(dependencies).length) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
"No Pro keys selected. Free packages belong in the application's own devDependencies."
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
const sortedDependencies = Object.fromEntries(
|
|
309
|
+
Object.entries(dependencies).sort()
|
|
310
|
+
);
|
|
311
|
+
const files = {
|
|
312
|
+
".npmrc": `${npmrcLines.join("\n")}
|
|
313
|
+
`,
|
|
314
|
+
".gitignore": "node_modules/\n",
|
|
315
|
+
"package.json": `${JSON.stringify(
|
|
316
|
+
{
|
|
317
|
+
name: "fontawesome-pro-sources",
|
|
318
|
+
version: "0.0.0",
|
|
319
|
+
private: true,
|
|
320
|
+
dependencies: sortedDependencies
|
|
321
|
+
},
|
|
322
|
+
null,
|
|
323
|
+
2
|
|
324
|
+
)}
|
|
325
|
+
`
|
|
326
|
+
};
|
|
327
|
+
const existing = Object.keys(files).filter(
|
|
328
|
+
(name) => node_fs.existsSync(node_path.resolve(sourceRoot, name))
|
|
329
|
+
);
|
|
330
|
+
if (existing.length && !values.force) {
|
|
331
|
+
throw new Error(
|
|
332
|
+
`${display(sourceRoot)} already contains ${existing.join(", ")}. Use --force to overwrite.`
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
const lockfilePath = node_path.resolve(sourceRoot, "package-lock.json");
|
|
336
|
+
const removeStaleLockfile = node_fs.existsSync(lockfilePath) && readDependencies(node_path.resolve(sourceRoot, "package.json")) !== JSON.stringify(sortedDependencies);
|
|
337
|
+
node_fs.mkdirSync(sourceRoot, { recursive: true });
|
|
338
|
+
for (const [name, content] of Object.entries(files)) {
|
|
339
|
+
node_fs.writeFileSync(node_path.resolve(sourceRoot, name), content);
|
|
340
|
+
}
|
|
341
|
+
if (removeStaleLockfile) node_fs.rmSync(lockfilePath);
|
|
342
|
+
if (skipped.length) {
|
|
343
|
+
log(
|
|
344
|
+
`Skipped Free keys (install them in the application instead): ${skipped.join(", ")}`
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
log(
|
|
348
|
+
`Scaffolded ${display(sourceRoot)} with ${Object.keys(dependencies).length} Pro package(s).`
|
|
349
|
+
);
|
|
350
|
+
if (removeStaleLockfile) {
|
|
351
|
+
log(
|
|
352
|
+
`Removed ${display(sourceRoot)}/package-lock.json because the dependencies changed; the next install regenerates it.`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
log(
|
|
356
|
+
"Next: run this command with --source-root and without --init, then commit the source project and its package-lock.json."
|
|
357
|
+
);
|
|
358
|
+
return 0;
|
|
359
|
+
}
|
|
360
|
+
async function installSourceProject(sourceRoot, values, context) {
|
|
361
|
+
const { log, display } = context;
|
|
362
|
+
const npmCli = requireNpm(context);
|
|
363
|
+
for (const name of ["package.json", ".npmrc"]) {
|
|
364
|
+
if (!node_fs.existsSync(node_path.resolve(sourceRoot, name))) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`${display(sourceRoot)}/${name} was not found. Check --source-root, or scaffold the project with --init.`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const npmrc = `${display(sourceRoot)}/.npmrc`;
|
|
371
|
+
const { fontAwesomeRegistry, hasAuth } = checkNpmrc(
|
|
372
|
+
node_fs.readFileSync(node_path.resolve(sourceRoot, ".npmrc"), "utf8"),
|
|
373
|
+
npmrc
|
|
374
|
+
);
|
|
375
|
+
if (!fontAwesomeRegistry || !hasAuth) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
`${npmrc} must contain @fortawesome:registry=${registry} and ${authKey}=${tokenPlaceholder}. Scaffold it with --init.`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
const hasLockfile = node_fs.existsSync(node_path.resolve(sourceRoot, "package-lock.json"));
|
|
381
|
+
if (context.isCi && !hasLockfile) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`${display(sourceRoot)}/package-lock.json is required in CI. Install locally first and commit the lockfile.`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
if (context.isCi && values["refresh-lock"]) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
"--refresh-lock updates the lockfile and is a local step. In CI, install from the committed lockfile."
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
const locked = hasLockfile && !values["refresh-lock"];
|
|
392
|
+
const environment = {
|
|
393
|
+
...context.env,
|
|
394
|
+
[tokenVariable]: await readToken(context)
|
|
395
|
+
};
|
|
396
|
+
for (const name of workspaceVariables) delete environment[name];
|
|
397
|
+
const status = context.runNpm({
|
|
398
|
+
npmCli,
|
|
399
|
+
args: [
|
|
400
|
+
locked ? "ci" : "install",
|
|
401
|
+
"--workspaces=false",
|
|
402
|
+
"--ignore-scripts",
|
|
403
|
+
"--no-audit",
|
|
404
|
+
"--no-fund",
|
|
405
|
+
pinRegistry
|
|
406
|
+
],
|
|
407
|
+
cwd: sourceRoot,
|
|
408
|
+
env: environment
|
|
409
|
+
});
|
|
410
|
+
if (status !== 0) return status;
|
|
411
|
+
const lockfile = `${display(sourceRoot)}/package-lock.json`;
|
|
412
|
+
log(
|
|
413
|
+
locked ? `Pro sources installed from ${lockfile}.` : `Pro sources installed. Review and commit ${lockfile}.`
|
|
414
|
+
);
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
417
|
+
async function runFontAwesomeSetup(args, io = {}) {
|
|
418
|
+
const cwd = io.cwd ?? process.cwd();
|
|
419
|
+
const env = io.env ?? process.env;
|
|
420
|
+
const stderr = io.stderr ?? console.error;
|
|
421
|
+
const context = {
|
|
422
|
+
cwd,
|
|
423
|
+
env,
|
|
424
|
+
// TeamCity and GitHub Actions are detected directly; TeamCity does not set CI.
|
|
425
|
+
isCi: Boolean(env.CI) && env.CI !== "false" || Boolean(env.TEAMCITY_VERSION || env.GITHUB_ACTIONS),
|
|
426
|
+
log: io.stdout ?? console.log,
|
|
427
|
+
promptForToken: io.promptForToken ?? promptForTerminalToken,
|
|
428
|
+
runNpm: io.runNpm ?? spawnNpm,
|
|
429
|
+
display: (path) => node_path.relative(cwd, path) || "."
|
|
430
|
+
};
|
|
431
|
+
try {
|
|
432
|
+
const { values } = node_util.parseArgs({
|
|
433
|
+
args: [...args],
|
|
434
|
+
options: {
|
|
435
|
+
"source-root": { type: "string" },
|
|
436
|
+
"free-only": { type: "boolean" },
|
|
437
|
+
init: { type: "boolean" },
|
|
438
|
+
keys: { type: "string" },
|
|
439
|
+
manifest: { type: "string" },
|
|
440
|
+
force: { type: "boolean" },
|
|
441
|
+
"refresh-lock": { type: "boolean" },
|
|
442
|
+
help: { type: "boolean", short: "h" }
|
|
443
|
+
},
|
|
444
|
+
strict: true,
|
|
445
|
+
allowPositionals: false
|
|
446
|
+
});
|
|
447
|
+
if (values.help) {
|
|
448
|
+
context.log(usage);
|
|
449
|
+
return 0;
|
|
450
|
+
}
|
|
451
|
+
if (values["source-root"] === void 0) {
|
|
452
|
+
if (values.init || values.force || values["refresh-lock"]) {
|
|
453
|
+
throw new Error(
|
|
454
|
+
"--init, --force, and --refresh-lock apply only with --source-root."
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
return await setupAppRoot(values, context);
|
|
458
|
+
}
|
|
459
|
+
if (values["free-only"]) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
"--free-only applies only to app-root setup; an isolated source project holds Pro packages."
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
if (values.init && values["refresh-lock"]) {
|
|
465
|
+
throw new Error(
|
|
466
|
+
"--refresh-lock applies to the install step, not --init."
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
const sourceRoot = node_path.resolve(cwd, values["source-root"]);
|
|
470
|
+
return values.init ? await initSourceProject(sourceRoot, values, context) : await installSourceProject(sourceRoot, values, context);
|
|
471
|
+
} catch (error) {
|
|
472
|
+
stderr(error instanceof Error ? error.message : "Unknown error");
|
|
473
|
+
return 1;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
exports.checkNpmrc = checkNpmrc;
|
|
477
|
+
exports.runFontAwesomeSetup = runFontAwesomeSetup;
|