@pantoken/cli 0.1.8 → 0.1.10
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/dist/index.mjs +62 -27
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
3
|
import { generateAndroid } from "@pantoken/android";
|
|
4
4
|
import { generateCompose } from "@pantoken/compose";
|
|
5
5
|
import { generateFlutter } from "@pantoken/flutter";
|
|
@@ -44,6 +44,51 @@ const SUPPORTED = /* @__PURE__ */ new Set([
|
|
|
44
44
|
"mintlify"
|
|
45
45
|
]);
|
|
46
46
|
const PLANNED = /* @__PURE__ */ new Set();
|
|
47
|
+
const VALID_THEMES = /* @__PURE__ */ new Set([
|
|
48
|
+
"rebrand",
|
|
49
|
+
"canvas",
|
|
50
|
+
"canvasHighContrast"
|
|
51
|
+
]);
|
|
52
|
+
const KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
53
|
+
"out",
|
|
54
|
+
"theme",
|
|
55
|
+
"class",
|
|
56
|
+
"icons",
|
|
57
|
+
"format",
|
|
58
|
+
"no-scope",
|
|
59
|
+
"no-important",
|
|
60
|
+
"no-prune"
|
|
61
|
+
]);
|
|
62
|
+
const VALID_CLASS_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
63
|
+
const RUST_FORMATS = /* @__PURE__ */ new Set(["egui", "iced"]);
|
|
64
|
+
/** Write an array of path+content assets into `outDir`, creating intermediate directories. */
|
|
65
|
+
function writeAssets(outDir, assets) {
|
|
66
|
+
for (const asset of assets) {
|
|
67
|
+
const file = join(outDir, asset.path);
|
|
68
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
69
|
+
writeFileSync(file, asset.content);
|
|
70
|
+
console.log(`✓ pantoken: wrote ${file}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Parse argv into positionals and a validated flags record; throw on unknown flags. */
|
|
74
|
+
function parseFlags(argv) {
|
|
75
|
+
const positionals = [];
|
|
76
|
+
const flags = {};
|
|
77
|
+
for (let i = 0; i < argv.length; i++) {
|
|
78
|
+
const arg = argv[i];
|
|
79
|
+
if (arg.startsWith("--")) {
|
|
80
|
+
const key = arg.slice(2);
|
|
81
|
+
const next = argv[i + 1];
|
|
82
|
+
if (!KNOWN_FLAGS.has(key)) throw new Error(`Unknown flag "--${key}". Run pantoken generate --help for usage.`);
|
|
83
|
+
if (next === void 0 || next.startsWith("--")) flags[key] = "true";
|
|
84
|
+
else flags[key] = argv[++i];
|
|
85
|
+
} else positionals.push(arg);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
positionals,
|
|
89
|
+
flags
|
|
90
|
+
};
|
|
91
|
+
}
|
|
47
92
|
/**
|
|
48
93
|
* Parse `generate <target> [--out dir] [--theme t] [--class Name]`.
|
|
49
94
|
*
|
|
@@ -66,22 +111,17 @@ const PLANNED = /* @__PURE__ */ new Set();
|
|
|
66
111
|
* ```
|
|
67
112
|
*/
|
|
68
113
|
function parseArgs(argv) {
|
|
69
|
-
const positionals =
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const next = argv[i + 1];
|
|
75
|
-
if (next === void 0 || next.startsWith("--")) flags[arg.slice(2)] = "true";
|
|
76
|
-
else flags[arg.slice(2)] = argv[++i];
|
|
77
|
-
} else positionals.push(arg);
|
|
78
|
-
}
|
|
114
|
+
const { positionals, flags } = parseFlags(argv);
|
|
115
|
+
const theme = flags.theme ?? "rebrand";
|
|
116
|
+
if (!VALID_THEMES.has(theme)) throw new Error(`Unknown theme "${theme}". Valid themes: ${[...VALID_THEMES].join(", ")}.`);
|
|
117
|
+
const className = flags.class ?? "PanTokens";
|
|
118
|
+
if (!VALID_CLASS_RE.test(className)) throw new Error(`Invalid class name "${className}". Must be a valid identifier.`);
|
|
79
119
|
return {
|
|
80
120
|
command: positionals[0] ?? "",
|
|
81
121
|
target: positionals[1] ?? "",
|
|
82
122
|
out: flags.out ?? "./pantoken-out",
|
|
83
|
-
theme
|
|
84
|
-
className
|
|
123
|
+
theme,
|
|
124
|
+
className,
|
|
85
125
|
icons: flags.icons ? flags.icons.split(",").filter(Boolean) : void 0,
|
|
86
126
|
format: flags.format,
|
|
87
127
|
noScope: "no-scope" in flags,
|
|
@@ -171,16 +211,12 @@ function runMintlify(args) {
|
|
|
171
211
|
}
|
|
172
212
|
/** Write the Drupal theme assets. */
|
|
173
213
|
function runDrupal(args) {
|
|
174
|
-
|
|
175
|
-
const file = join(args.out, asset.path);
|
|
176
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
177
|
-
writeFileSync(file, asset.content);
|
|
178
|
-
console.log(`✓ pantoken: wrote ${file}`);
|
|
179
|
-
}
|
|
214
|
+
writeAssets(args.out, toDrupalTheme());
|
|
180
215
|
}
|
|
181
216
|
/** Write the Rust token source for the egui or iced format. */
|
|
182
217
|
function runRust(args) {
|
|
183
|
-
const format = args.format
|
|
218
|
+
const format = args.format ?? "egui";
|
|
219
|
+
if (!RUST_FORMATS.has(format)) throw new Error(`Unknown Rust format "${format}". Use egui or iced.`);
|
|
184
220
|
const file = args.out.includes(".") ? args.out : join(args.out, "tokens.rs");
|
|
185
221
|
mkdirSync(dirname(file), { recursive: true });
|
|
186
222
|
writeFileSync(file, generateRust({
|
|
@@ -228,13 +264,7 @@ async function runIconFont(args) {
|
|
|
228
264
|
}
|
|
229
265
|
/** Write the Jekyll or Hugo static-site assets (same asset shape, different source). */
|
|
230
266
|
function runStaticSite(args) {
|
|
231
|
-
|
|
232
|
-
for (const asset of assets) {
|
|
233
|
-
const file = join(args.out, asset.path);
|
|
234
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
235
|
-
writeFileSync(file, asset.content);
|
|
236
|
-
console.log(`✓ pantoken: wrote ${file}`);
|
|
237
|
-
}
|
|
267
|
+
writeAssets(args.out, args.target === "jekyll" ? toJekyllAssets() : toHugoAssets());
|
|
238
268
|
}
|
|
239
269
|
/** Write the Pendo `global.css`, honouring the `--no-scope`/`--no-important`/`--no-prune` flags. */
|
|
240
270
|
function runPendo(args) {
|
|
@@ -248,6 +278,10 @@ function runPendo(args) {
|
|
|
248
278
|
}));
|
|
249
279
|
console.log(`✓ pantoken: wrote ${file}`);
|
|
250
280
|
}
|
|
281
|
+
/** Warn when the resolved output path escapes cwd — guards against accidental ../traversal. */
|
|
282
|
+
function warnIfUnsafePath(out) {
|
|
283
|
+
if (relative(process.cwd(), resolve(out)).startsWith("..")) console.warn(`⚠️ pantoken: output path "${out}" escapes the current directory.`);
|
|
284
|
+
}
|
|
251
285
|
/**
|
|
252
286
|
* Run the CLI.
|
|
253
287
|
*
|
|
@@ -269,6 +303,7 @@ function runPendo(args) {
|
|
|
269
303
|
async function run(argv) {
|
|
270
304
|
const args = parseArgs(argv);
|
|
271
305
|
assertGenerateTarget(args);
|
|
306
|
+
warnIfUnsafePath(args.out);
|
|
272
307
|
if (args.target === "swift") return runSwift(args);
|
|
273
308
|
if (args.target === "android") return runAndroid(args);
|
|
274
309
|
if (args.target === "compose") return runCompose(args);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pantoken/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "pantoken generate <target> — emit native design-token source (Swift now; Kotlin/Dart/Drupal to follow).",
|
|
5
5
|
"homepage": "https://pantoken.iywahl.com",
|
|
6
6
|
"bugs": "https://github.com/thedannywahl/pantoken/issues",
|