@loworbitstudio/visor 0.6.0 → 0.7.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/dist/CHANGELOG.json +1 -1
- package/dist/index.js +161 -44
- package/dist/registry.json +90 -43
- package/dist/visor-manifest.json +165 -7
- package/package.json +1 -1
package/dist/CHANGELOG.json
CHANGED
package/dist/index.js
CHANGED
|
@@ -2211,9 +2211,121 @@ function printIssue(issue) {
|
|
|
2211
2211
|
console.log(`${prefix} ${code} ${issue.message}${path2}`);
|
|
2212
2212
|
}
|
|
2213
2213
|
|
|
2214
|
+
// src/commands/theme-verify.ts
|
|
2215
|
+
import { spawnSync as _spawnSync } from "child_process";
|
|
2216
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2217
|
+
import { resolve as resolve5 } from "path";
|
|
2218
|
+
function themeVerifyCommand(dir, cwd, options, _spawnFn = _spawnSync) {
|
|
2219
|
+
const target = options.target ?? "flutter";
|
|
2220
|
+
if (target !== "flutter") {
|
|
2221
|
+
if (options.json) {
|
|
2222
|
+
console.log(
|
|
2223
|
+
JSON.stringify({
|
|
2224
|
+
valid: false,
|
|
2225
|
+
target,
|
|
2226
|
+
errors: [
|
|
2227
|
+
{
|
|
2228
|
+
code: "UNSUPPORTED_TARGET",
|
|
2229
|
+
message: `Unsupported target: "${target}". Only "flutter" is supported.`
|
|
2230
|
+
}
|
|
2231
|
+
]
|
|
2232
|
+
})
|
|
2233
|
+
);
|
|
2234
|
+
} else {
|
|
2235
|
+
logger.error(`Unsupported target: "${target}". Only "flutter" is supported.`);
|
|
2236
|
+
}
|
|
2237
|
+
process.exit(1);
|
|
2238
|
+
}
|
|
2239
|
+
const dirPath = resolve5(cwd, dir);
|
|
2240
|
+
if (!existsSync7(dirPath)) {
|
|
2241
|
+
if (options.json) {
|
|
2242
|
+
console.log(
|
|
2243
|
+
JSON.stringify({
|
|
2244
|
+
valid: false,
|
|
2245
|
+
target,
|
|
2246
|
+
dir: dirPath,
|
|
2247
|
+
errors: [
|
|
2248
|
+
{
|
|
2249
|
+
code: "DIR_NOT_FOUND",
|
|
2250
|
+
message: `Directory not found: ${dirPath}`
|
|
2251
|
+
}
|
|
2252
|
+
]
|
|
2253
|
+
})
|
|
2254
|
+
);
|
|
2255
|
+
} else {
|
|
2256
|
+
logger.error(`Directory not found: ${dirPath}`);
|
|
2257
|
+
logger.info("Make sure the path exists and is readable.");
|
|
2258
|
+
}
|
|
2259
|
+
process.exit(1);
|
|
2260
|
+
}
|
|
2261
|
+
const flutterBin = findFlutterBin();
|
|
2262
|
+
if (!flutterBin) {
|
|
2263
|
+
if (options.json) {
|
|
2264
|
+
console.log(
|
|
2265
|
+
JSON.stringify({
|
|
2266
|
+
valid: false,
|
|
2267
|
+
target,
|
|
2268
|
+
dir: dirPath,
|
|
2269
|
+
errors: [
|
|
2270
|
+
{
|
|
2271
|
+
code: "FLUTTER_NOT_FOUND",
|
|
2272
|
+
message: "Flutter binary not found. Set FLUTTER_ROOT, add flutter to PATH, or install via FVM."
|
|
2273
|
+
}
|
|
2274
|
+
]
|
|
2275
|
+
})
|
|
2276
|
+
);
|
|
2277
|
+
} else {
|
|
2278
|
+
logger.error("Flutter binary not found.");
|
|
2279
|
+
logger.info(
|
|
2280
|
+
"Set FLUTTER_ROOT, add flutter to PATH, or install via FVM."
|
|
2281
|
+
);
|
|
2282
|
+
}
|
|
2283
|
+
process.exit(1);
|
|
2284
|
+
}
|
|
2285
|
+
if (!options.json) {
|
|
2286
|
+
logger.info(`Verifying Flutter output at: ${dirPath}`);
|
|
2287
|
+
logger.item(`Using Flutter binary: ${flutterBin}`);
|
|
2288
|
+
}
|
|
2289
|
+
const result = _spawnFn(flutterBin, ["analyze", "--no-pub"], {
|
|
2290
|
+
cwd: dirPath,
|
|
2291
|
+
stdio: options.json ? "pipe" : "inherit",
|
|
2292
|
+
encoding: "utf-8"
|
|
2293
|
+
});
|
|
2294
|
+
if (options.json) {
|
|
2295
|
+
const stdout = (result.stdout ?? "").toString();
|
|
2296
|
+
const stderr = (result.stderr ?? "").toString();
|
|
2297
|
+
const success = result.status === 0;
|
|
2298
|
+
console.log(
|
|
2299
|
+
JSON.stringify({
|
|
2300
|
+
valid: success,
|
|
2301
|
+
target,
|
|
2302
|
+
dir: dirPath,
|
|
2303
|
+
exitCode: result.status,
|
|
2304
|
+
stdout: stdout.trim() || void 0,
|
|
2305
|
+
stderr: stderr.trim() || void 0,
|
|
2306
|
+
errors: success ? [] : [
|
|
2307
|
+
{
|
|
2308
|
+
code: "DART_ANALYZE_FAILED",
|
|
2309
|
+
message: stderr.trim() || stdout.trim() || "dart analyze reported errors"
|
|
2310
|
+
}
|
|
2311
|
+
]
|
|
2312
|
+
})
|
|
2313
|
+
);
|
|
2314
|
+
process.exit(success ? 0 : 1);
|
|
2315
|
+
}
|
|
2316
|
+
if (result.status === 0) {
|
|
2317
|
+
logger.success("Flutter output is clean \u2014 dart analyze passed.");
|
|
2318
|
+
process.exit(0);
|
|
2319
|
+
} else {
|
|
2320
|
+
logger.error("dart analyze reported errors in the generated output.");
|
|
2321
|
+
logger.info("Fix the errors above, then re-run: visor theme apply --target flutter");
|
|
2322
|
+
process.exit(1);
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2214
2326
|
// src/commands/theme-extract.ts
|
|
2215
|
-
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as
|
|
2216
|
-
import { resolve as
|
|
2327
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
2328
|
+
import { resolve as resolve6, join as join10, basename, extname as extname2, relative } from "path";
|
|
2217
2329
|
import { stringify as stringifyYaml } from "yaml";
|
|
2218
2330
|
import {
|
|
2219
2331
|
extractFromCSS,
|
|
@@ -2242,8 +2354,8 @@ var CSS_DIRS = [
|
|
|
2242
2354
|
"packages/design-tokens"
|
|
2243
2355
|
];
|
|
2244
2356
|
function themeExtractCommand(cwd, options) {
|
|
2245
|
-
const targetDir =
|
|
2246
|
-
if (!
|
|
2357
|
+
const targetDir = resolve6(cwd, options.from ?? ".");
|
|
2358
|
+
if (!existsSync8(targetDir)) {
|
|
2247
2359
|
if (options.json) {
|
|
2248
2360
|
console.log(JSON.stringify({ success: false, error: `Directory not found: ${targetDir}` }));
|
|
2249
2361
|
} else {
|
|
@@ -2306,7 +2418,7 @@ function collectCSSFiles(targetDir) {
|
|
|
2306
2418
|
}
|
|
2307
2419
|
for (const dir of CSS_DIRS) {
|
|
2308
2420
|
const dirPath = join10(targetDir, dir);
|
|
2309
|
-
if (
|
|
2421
|
+
if (existsSync8(dirPath) && statSync3(dirPath).isDirectory()) {
|
|
2310
2422
|
scanDirForCSS(dirPath, files, seen, 2);
|
|
2311
2423
|
}
|
|
2312
2424
|
}
|
|
@@ -2314,9 +2426,9 @@ function collectCSSFiles(targetDir) {
|
|
|
2314
2426
|
return files;
|
|
2315
2427
|
}
|
|
2316
2428
|
function addFileIfExists(filePath, files, seen) {
|
|
2317
|
-
const resolved =
|
|
2429
|
+
const resolved = resolve6(filePath);
|
|
2318
2430
|
if (seen.has(resolved)) return;
|
|
2319
|
-
if (!
|
|
2431
|
+
if (!existsSync8(resolved)) return;
|
|
2320
2432
|
try {
|
|
2321
2433
|
const content = readFileSync11(resolved, "utf-8");
|
|
2322
2434
|
if (content.includes("--")) {
|
|
@@ -2327,7 +2439,7 @@ function addFileIfExists(filePath, files, seen) {
|
|
|
2327
2439
|
}
|
|
2328
2440
|
}
|
|
2329
2441
|
function scanDirForCSS(dir, files, seen, maxDepth) {
|
|
2330
|
-
if (!
|
|
2442
|
+
if (!existsSync8(dir)) return;
|
|
2331
2443
|
const SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2332
2444
|
"node_modules",
|
|
2333
2445
|
".next",
|
|
@@ -2432,7 +2544,7 @@ function parseNextFontFromLayouts(targetDir) {
|
|
|
2432
2544
|
const fontMap = /* @__PURE__ */ new Map();
|
|
2433
2545
|
for (const relPath of LAYOUT_FILE_PATHS) {
|
|
2434
2546
|
const fullPath = join10(targetDir, relPath);
|
|
2435
|
-
if (!
|
|
2547
|
+
if (!existsSync8(fullPath)) continue;
|
|
2436
2548
|
try {
|
|
2437
2549
|
const content = readFileSync11(fullPath, "utf-8");
|
|
2438
2550
|
parseNextFontDeclarations(content, fontMap);
|
|
@@ -2520,7 +2632,7 @@ var MONO_FONT_NAMES = /* @__PURE__ */ new Set([
|
|
|
2520
2632
|
]);
|
|
2521
2633
|
function extractFontHints(targetDir) {
|
|
2522
2634
|
const pkgPath = join10(targetDir, "package.json");
|
|
2523
|
-
if (!
|
|
2635
|
+
if (!existsSync8(pkgPath)) return void 0;
|
|
2524
2636
|
try {
|
|
2525
2637
|
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
2526
2638
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
@@ -2559,7 +2671,7 @@ function extractFontHints(targetDir) {
|
|
|
2559
2671
|
}
|
|
2560
2672
|
function inferThemeName(targetDir) {
|
|
2561
2673
|
const pkgPath = join10(targetDir, "package.json");
|
|
2562
|
-
if (
|
|
2674
|
+
if (existsSync8(pkgPath)) {
|
|
2563
2675
|
try {
|
|
2564
2676
|
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
2565
2677
|
if (pkg.name) {
|
|
@@ -2598,7 +2710,7 @@ function outputJSON(result, validationResult) {
|
|
|
2598
2710
|
}
|
|
2599
2711
|
function outputYAML(result, outputPath, cwd, validationResult) {
|
|
2600
2712
|
const yamlStr = buildAnnotatedYAML(result);
|
|
2601
|
-
const outFile =
|
|
2713
|
+
const outFile = resolve6(cwd, outputPath ?? ".visor.yaml");
|
|
2602
2714
|
const high = result.tokens.filter((t) => t.confidence === "high").length;
|
|
2603
2715
|
const med = result.tokens.filter((t) => t.confidence === "medium").length;
|
|
2604
2716
|
const low = result.tokens.filter((t) => t.confidence === "low").length;
|
|
@@ -2684,14 +2796,14 @@ function buildAnnotatedYAML(result) {
|
|
|
2684
2796
|
}
|
|
2685
2797
|
|
|
2686
2798
|
// src/commands/theme-register.ts
|
|
2687
|
-
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync4, existsSync as
|
|
2688
|
-
import { resolve as
|
|
2799
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync4, existsSync as existsSync10 } from "fs";
|
|
2800
|
+
import { resolve as resolve8, join as join12 } from "path";
|
|
2689
2801
|
import { generateThemeData as generateThemeData3 } from "@loworbitstudio/visor-theme-engine";
|
|
2690
2802
|
import { docsAdapter as docsAdapter2 } from "@loworbitstudio/visor-theme-engine/adapters";
|
|
2691
2803
|
|
|
2692
2804
|
// src/utils/theme-helpers.ts
|
|
2693
|
-
import { existsSync as
|
|
2694
|
-
import { resolve as
|
|
2805
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2806
|
+
import { resolve as resolve7, dirname as dirname5, join as join11 } from "path";
|
|
2695
2807
|
function toSlug(name) {
|
|
2696
2808
|
return name.toLowerCase().replace(/\s+/g, "-");
|
|
2697
2809
|
}
|
|
@@ -2699,9 +2811,9 @@ function toLabel(name) {
|
|
|
2699
2811
|
return name.split(/[\s-]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
2700
2812
|
}
|
|
2701
2813
|
function findRepoRoot(startDir) {
|
|
2702
|
-
let current =
|
|
2814
|
+
let current = resolve7(startDir);
|
|
2703
2815
|
while (true) {
|
|
2704
|
-
if (
|
|
2816
|
+
if (existsSync9(join11(current, "packages", "docs"))) {
|
|
2705
2817
|
return current;
|
|
2706
2818
|
}
|
|
2707
2819
|
const parent = dirname5(current);
|
|
@@ -2796,7 +2908,7 @@ ${indent}${newEntry},
|
|
|
2796
2908
|
return { updated, changed: true };
|
|
2797
2909
|
}
|
|
2798
2910
|
function themeRegisterCommand(file, cwd, options) {
|
|
2799
|
-
const filePath =
|
|
2911
|
+
const filePath = resolve8(cwd, file);
|
|
2800
2912
|
let yamlContent;
|
|
2801
2913
|
try {
|
|
2802
2914
|
yamlContent = readFileSync12(filePath, "utf-8");
|
|
@@ -2846,7 +2958,7 @@ function themeRegisterCommand(file, cwd, options) {
|
|
|
2846
2958
|
const cssFilePath = join12(docsAppDir, `${slug2}-theme.css`);
|
|
2847
2959
|
const globalsPath = join12(docsAppDir, "globals.css");
|
|
2848
2960
|
const themeConfigPath = join12(repoRoot, "packages", "docs", "lib", "theme-config.ts");
|
|
2849
|
-
if (!
|
|
2961
|
+
if (!existsSync10(docsAppDir)) {
|
|
2850
2962
|
const msg = `Docs app directory not found: ${docsAppDir}`;
|
|
2851
2963
|
if (options.json) {
|
|
2852
2964
|
console.log(JSON.stringify({ success: false, error: msg }));
|
|
@@ -2871,7 +2983,7 @@ function themeRegisterCommand(file, cwd, options) {
|
|
|
2871
2983
|
process.exit(1);
|
|
2872
2984
|
return;
|
|
2873
2985
|
}
|
|
2874
|
-
const cssExists =
|
|
2986
|
+
const cssExists = existsSync10(cssFilePath);
|
|
2875
2987
|
const cssChanged = !cssExists || readFileSync12(cssFilePath, "utf-8") !== css;
|
|
2876
2988
|
const { updated: newGlobals, changed: globalsChanged } = insertGlobalsImport(globalsContent, slug2);
|
|
2877
2989
|
const { updated: newThemeConfig, changed: themeConfigChanged, error: configError } = insertThemeConfig(
|
|
@@ -2956,7 +3068,7 @@ function themeRegisterCommand(file, cwd, options) {
|
|
|
2956
3068
|
}
|
|
2957
3069
|
|
|
2958
3070
|
// src/commands/theme-unregister.ts
|
|
2959
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as
|
|
3071
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync11, unlinkSync } from "fs";
|
|
2960
3072
|
import { join as join13 } from "path";
|
|
2961
3073
|
function removeGlobalsImport(content, slug2) {
|
|
2962
3074
|
const importLine = `@import './${slug2}-theme.css';`;
|
|
@@ -2993,7 +3105,7 @@ function themeUnregisterCommand(slug2, cwd, options) {
|
|
|
2993
3105
|
const cssFilePath = join13(docsAppDir, `${slug2}-theme.css`);
|
|
2994
3106
|
const globalsPath = join13(docsAppDir, "globals.css");
|
|
2995
3107
|
const themeConfigPath = join13(repoRoot, "packages", "docs", "lib", "theme-config.ts");
|
|
2996
|
-
if (!
|
|
3108
|
+
if (!existsSync11(docsAppDir)) {
|
|
2997
3109
|
const msg = `Docs app directory not found: ${docsAppDir}`;
|
|
2998
3110
|
if (options.json) {
|
|
2999
3111
|
console.log(JSON.stringify({ success: false, error: msg }));
|
|
@@ -3018,7 +3130,7 @@ function themeUnregisterCommand(slug2, cwd, options) {
|
|
|
3018
3130
|
process.exit(1);
|
|
3019
3131
|
return;
|
|
3020
3132
|
}
|
|
3021
|
-
const cssExists =
|
|
3133
|
+
const cssExists = existsSync11(cssFilePath);
|
|
3022
3134
|
const { updated: newGlobals, changed: globalsChanged } = removeGlobalsImport(globalsContent, slug2);
|
|
3023
3135
|
const { updated: newThemeConfig, changed: themeConfigChanged } = removeThemeConfigEntry(themeConfigContent, slug2);
|
|
3024
3136
|
if (!cssExists && !globalsChanged && !themeConfigChanged) {
|
|
@@ -3062,7 +3174,7 @@ import {
|
|
|
3062
3174
|
readFileSync as readFileSync14,
|
|
3063
3175
|
writeFileSync as writeFileSync9,
|
|
3064
3176
|
mkdirSync as mkdirSync5,
|
|
3065
|
-
existsSync as
|
|
3177
|
+
existsSync as existsSync12,
|
|
3066
3178
|
readdirSync as readdirSync4,
|
|
3067
3179
|
unlinkSync as unlinkSync2,
|
|
3068
3180
|
copyFileSync
|
|
@@ -3081,7 +3193,7 @@ var CUSTOM_OVERLAY_CSS_PATH = "packages/docs/app/custom-themes.generated.css";
|
|
|
3081
3193
|
var CUSTOM_OVERLAY_TS_PATH = "packages/docs/lib/theme-config.custom.generated.ts";
|
|
3082
3194
|
var CUSTOM_OVERLAY_IMPORT_LINE = "@import './custom-themes.generated.css';";
|
|
3083
3195
|
function scanThemeDir(dir) {
|
|
3084
|
-
if (!
|
|
3196
|
+
if (!existsSync12(dir)) return [];
|
|
3085
3197
|
return readdirSync4(dir).filter((f) => f.endsWith(".visor.yaml")).map((f) => join14(dir, f));
|
|
3086
3198
|
}
|
|
3087
3199
|
function extractGroup(yamlContent) {
|
|
@@ -3333,7 +3445,7 @@ function themeSyncCommand(cwd, options) {
|
|
|
3333
3445
|
try {
|
|
3334
3446
|
globalsContent = readFileSync14(globalsPath, "utf-8");
|
|
3335
3447
|
themeConfigContent = readFileSync14(themeConfigPath, "utf-8");
|
|
3336
|
-
gitignoreContent =
|
|
3448
|
+
gitignoreContent = existsSync12(gitignorePath) ? readFileSync14(gitignorePath, "utf-8") : "";
|
|
3337
3449
|
} catch (err) {
|
|
3338
3450
|
const msg = err instanceof Error ? err.message : "Could not read docs files";
|
|
3339
3451
|
if (options.json) {
|
|
@@ -3349,12 +3461,12 @@ function themeSyncCommand(cwd, options) {
|
|
|
3349
3461
|
const newGitignore = customSlugs.length > 0 ? updateGitignoreBlock(gitignoreContent, customSlugs) : gitignoreContent;
|
|
3350
3462
|
const newCustomOverlayCss = generateCustomOverlayCss(customManifest);
|
|
3351
3463
|
const newCustomOverlayTs = generateCustomOverlayTs(customManifest);
|
|
3352
|
-
const existingCssFiles =
|
|
3464
|
+
const existingCssFiles = existsSync12(docsAppDir) ? readdirSync4(docsAppDir).filter(
|
|
3353
3465
|
(f) => f.endsWith("-theme.css") && f !== "custom-themes.generated.css"
|
|
3354
3466
|
) : [];
|
|
3355
3467
|
const newCssSet = new Set(allSlugs.map((s) => `${s}-theme.css`));
|
|
3356
3468
|
const staleCssFiles = existingCssFiles.filter((f) => !newCssSet.has(f));
|
|
3357
|
-
const existingPublicYamls =
|
|
3469
|
+
const existingPublicYamls = existsSync12(docsPublicThemesDir) ? readdirSync4(docsPublicThemesDir).filter((f) => f.endsWith(".visor.yaml")) : [];
|
|
3358
3470
|
const newPublicYamlSet = new Set(manifest.map((e) => `${e.yamlFilename}.visor.yaml`));
|
|
3359
3471
|
const stalePublicYamls = existingPublicYamls.filter((f) => !newPublicYamlSet.has(f));
|
|
3360
3472
|
if (options.dryRun) {
|
|
@@ -3395,7 +3507,7 @@ function themeSyncCommand(cwd, options) {
|
|
|
3395
3507
|
writeFileSync9(customOverlayTsPath, newCustomOverlayTs, "utf-8");
|
|
3396
3508
|
writeFileSync9(themeConfigPath, newThemeConfig, "utf-8");
|
|
3397
3509
|
writeFileSync9(globalsPath, newGlobals, "utf-8");
|
|
3398
|
-
if (
|
|
3510
|
+
if (existsSync12(gitignorePath)) {
|
|
3399
3511
|
writeFileSync9(gitignorePath, newGitignore, "utf-8");
|
|
3400
3512
|
}
|
|
3401
3513
|
const allSourceFiles = [...stockFiles, ...customFiles];
|
|
@@ -3443,7 +3555,7 @@ import {
|
|
|
3443
3555
|
readFileSync as readFileSync15,
|
|
3444
3556
|
writeFileSync as writeFileSync10,
|
|
3445
3557
|
mkdirSync as mkdirSync6,
|
|
3446
|
-
existsSync as
|
|
3558
|
+
existsSync as existsSync13,
|
|
3447
3559
|
readdirSync as readdirSync5,
|
|
3448
3560
|
rmSync
|
|
3449
3561
|
} from "fs";
|
|
@@ -3451,7 +3563,7 @@ import { join as join15, basename as basename3, dirname as dirname6 } from "path
|
|
|
3451
3563
|
import { generateThemeData as generateThemeData5 } from "@loworbitstudio/visor-theme-engine";
|
|
3452
3564
|
import { flutterAdapter as flutterAdapter2 } from "@loworbitstudio/visor-theme-engine/adapters";
|
|
3453
3565
|
function scanThemeDir2(dir) {
|
|
3454
|
-
if (!
|
|
3566
|
+
if (!existsSync13(dir)) return [];
|
|
3455
3567
|
return readdirSync5(dir).filter((f) => f.endsWith(".visor.yaml")).map((f) => join15(dir, f)).sort();
|
|
3456
3568
|
}
|
|
3457
3569
|
function slugToCamel(slug2) {
|
|
@@ -3688,7 +3800,7 @@ function themeBatchApplyFlutterCommand(cwd, options) {
|
|
|
3688
3800
|
}
|
|
3689
3801
|
const slugs = processed.map((p) => p.slug);
|
|
3690
3802
|
const libSrcDir = join15(outputDir, "lib", "src");
|
|
3691
|
-
if (
|
|
3803
|
+
if (existsSync13(libSrcDir)) {
|
|
3692
3804
|
rmSync(libSrcDir, { recursive: true, force: true });
|
|
3693
3805
|
}
|
|
3694
3806
|
const packageFiles = {
|
|
@@ -3731,8 +3843,8 @@ function themeBatchApplyFlutterCommand(cwd, options) {
|
|
|
3731
3843
|
}
|
|
3732
3844
|
|
|
3733
3845
|
// src/commands/fonts-add.ts
|
|
3734
|
-
import { existsSync as
|
|
3735
|
-
import { resolve as
|
|
3846
|
+
import { existsSync as existsSync14, statSync as statSync4, readdirSync as readdirSync6, readFileSync as readFileSync16 } from "fs";
|
|
3847
|
+
import { resolve as resolve9, basename as basename4, extname as extname3 } from "path";
|
|
3736
3848
|
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
|
|
3737
3849
|
function deriveFamilySlug(filename) {
|
|
3738
3850
|
const name = basename4(filename, extname3(filename));
|
|
@@ -3776,8 +3888,8 @@ function deriveFamilySlug(filename) {
|
|
|
3776
3888
|
return parts.join("-").toLowerCase();
|
|
3777
3889
|
}
|
|
3778
3890
|
function collectWoff2Files(inputPath) {
|
|
3779
|
-
const resolved =
|
|
3780
|
-
if (!
|
|
3891
|
+
const resolved = resolve9(inputPath);
|
|
3892
|
+
if (!existsSync14(resolved)) {
|
|
3781
3893
|
throw new Error(`Path not found: ${resolved}`);
|
|
3782
3894
|
}
|
|
3783
3895
|
const stat = statSync4(resolved);
|
|
@@ -3790,7 +3902,7 @@ function collectWoff2Files(inputPath) {
|
|
|
3790
3902
|
return [resolved];
|
|
3791
3903
|
}
|
|
3792
3904
|
if (stat.isDirectory()) {
|
|
3793
|
-
const files = readdirSync6(resolved).filter((f) => extname3(f).toLowerCase() === ".woff2").map((f) =>
|
|
3905
|
+
const files = readdirSync6(resolved).filter((f) => extname3(f).toLowerCase() === ".woff2").map((f) => resolve9(resolved, f));
|
|
3794
3906
|
if (files.length === 0) {
|
|
3795
3907
|
throw new Error(
|
|
3796
3908
|
`No .woff2 files found in directory: ${resolved}`
|
|
@@ -3805,8 +3917,8 @@ function collectWoff2Files(inputPath) {
|
|
|
3805
3917
|
throw new Error(`Path is neither a file nor a directory: ${resolved}`);
|
|
3806
3918
|
}
|
|
3807
3919
|
function getNonWoff2Fonts(inputPath) {
|
|
3808
|
-
const resolved =
|
|
3809
|
-
if (!
|
|
3920
|
+
const resolved = resolve9(inputPath);
|
|
3921
|
+
if (!existsSync14(resolved) || !statSync4(resolved).isDirectory()) {
|
|
3810
3922
|
return [];
|
|
3811
3923
|
}
|
|
3812
3924
|
return readdirSync6(resolved).filter((f) => {
|
|
@@ -3859,7 +3971,7 @@ async function fontsAddCommand(inputPath, options) {
|
|
|
3859
3971
|
const r2Config = getR2Config();
|
|
3860
3972
|
const files = collectWoff2Files(inputPath);
|
|
3861
3973
|
const familySlug = options.family ?? deriveFamilySlug(basename4(files[0]));
|
|
3862
|
-
const resolved =
|
|
3974
|
+
const resolved = resolve9(inputPath);
|
|
3863
3975
|
const nonWoff2 = statSync4(resolved).isDirectory() ? getNonWoff2Fonts(resolved) : [];
|
|
3864
3976
|
if (!json) {
|
|
3865
3977
|
logger.heading("Visor Font Upload");
|
|
@@ -4147,12 +4259,12 @@ function findCssFiles(dir, maxDepth = 3) {
|
|
|
4147
4259
|
}
|
|
4148
4260
|
|
|
4149
4261
|
// src/utils/patterns.ts
|
|
4150
|
-
import { existsSync as
|
|
4262
|
+
import { existsSync as existsSync16, readdirSync as readdirSync8, readFileSync as readFileSync18 } from "fs";
|
|
4151
4263
|
import { join as join17 } from "path";
|
|
4152
4264
|
import { parse as parseYAML } from "yaml";
|
|
4153
4265
|
function loadPatternsFromYaml(repoRoot) {
|
|
4154
4266
|
const patternsDir = join17(repoRoot, "patterns");
|
|
4155
|
-
if (!
|
|
4267
|
+
if (!existsSync16(patternsDir)) return [];
|
|
4156
4268
|
const files = readdirSync8(patternsDir).filter(
|
|
4157
4269
|
(f) => f.endsWith(".visor-pattern.yaml")
|
|
4158
4270
|
);
|
|
@@ -4164,7 +4276,7 @@ function loadPatternsFromYaml(repoRoot) {
|
|
|
4164
4276
|
function findRepoRoot2(startDir) {
|
|
4165
4277
|
let current = startDir;
|
|
4166
4278
|
while (true) {
|
|
4167
|
-
if (
|
|
4279
|
+
if (existsSync16(join17(current, "patterns"))) {
|
|
4168
4280
|
return current;
|
|
4169
4281
|
}
|
|
4170
4282
|
const parent = join17(current, "..");
|
|
@@ -4618,6 +4730,11 @@ theme.command("validate").description("Run full validation ruleset on a .visor.y
|
|
|
4618
4730
|
themeValidateCommand(file, process.cwd(), options);
|
|
4619
4731
|
}
|
|
4620
4732
|
);
|
|
4733
|
+
theme.command("verify").description("Verify generated theme output for a target platform").argument("<dir>", "path to generated output directory").option("--target <platform>", "target platform (flutter)", "flutter").option("--json", "output structured JSON (for AI agents)").action(
|
|
4734
|
+
(dir, options) => {
|
|
4735
|
+
themeVerifyCommand(dir, process.cwd(), options);
|
|
4736
|
+
}
|
|
4737
|
+
);
|
|
4621
4738
|
theme.command("extract").description(
|
|
4622
4739
|
"Scan an existing project's CSS and produce a best-effort .visor.yaml theme file"
|
|
4623
4740
|
).option("--from <path>", "path to project directory to scan").option("--json", "output structured JSON (for AI agents)").option("-o, --output <path>", "output file path (default: .visor.yaml)").option("--validate", "run validator on the extracted theme").action(
|