@agimon-ai/po-style-system-tooling 0.2.14-alpha.2 → 0.2.14-alpha.21
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/LICENSE +149 -21
- package/README.md +1 -2
- package/dist/cli.cjs +9 -41
- package/dist/cli.mjs +10 -42
- package/dist/index.cjs +229 -7
- package/dist/index.d.cts +361 -257
- package/dist/index.d.mts +360 -256
- package/dist/index.mjs +224 -2
- package/dist/{stdio-CT9XjOJQ.cjs → stdio-CSwuqV9E.cjs} +427 -660
- package/dist/{stdio-Dgaj2-_8.mjs → stdio-DBEflKj3.mjs} +415 -658
- package/package.json +22 -14
- package/skills/style-system/SKILL.md +43 -21
|
@@ -2,10 +2,11 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { Container, ContainerModule, inject, injectable, optional } from "inversify";
|
|
3
3
|
import { accessSync, existsSync, promises, readFileSync, readdirSync } from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
5
|
-
import path, {
|
|
5
|
+
import path, { dirname } from "node:path";
|
|
6
6
|
import os, * as os$1 from "node:os";
|
|
7
7
|
import pino from "pino";
|
|
8
8
|
import * as fs from "node:fs/promises";
|
|
9
|
+
import { writeFile } from "node:fs/promises";
|
|
9
10
|
import * as yaml from "js-yaml";
|
|
10
11
|
import postcss from "postcss";
|
|
11
12
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -13,18 +14,36 @@ import { loadCsf } from "storybook/internal/csf-tools";
|
|
|
13
14
|
import { glob } from "glob";
|
|
14
15
|
import { z } from "zod";
|
|
15
16
|
import { chromium, firefox, webkit } from "playwright";
|
|
16
|
-
import sharp from "sharp";
|
|
17
17
|
import tailwindcss from "@tailwindcss/vite";
|
|
18
18
|
import { viteSingleFile } from "vite-plugin-singlefile";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
import react from "@vitejs/plugin-react";
|
|
21
|
-
import { execSync, spawn } from "node:child_process";
|
|
22
|
-
import { registerManagedProcess } from "@agimon-ai/foundation-exec";
|
|
23
21
|
import { Server } from "@modelcontextprotocol/server";
|
|
24
22
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
25
23
|
//#region \0rolldown/runtime.js
|
|
26
24
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
27
25
|
//#endregion
|
|
26
|
+
//#region src/container/types.ts
|
|
27
|
+
const STYLE_SYSTEM_TYPES = {
|
|
28
|
+
ListThemesTool: Symbol.for("StyleSystem:ListThemesTool"),
|
|
29
|
+
GetCSSClassesTool: Symbol.for("StyleSystem:GetCSSClassesTool"),
|
|
30
|
+
GetComponentVisualTool: Symbol.for("StyleSystem:GetComponentVisualTool"),
|
|
31
|
+
ListSharedComponentsTool: Symbol.for("StyleSystem:ListSharedComponentsTool"),
|
|
32
|
+
ListAppComponentsTool: Symbol.for("StyleSystem:ListAppComponentsTool"),
|
|
33
|
+
ThemeServiceFactory: Symbol.for("StyleSystem:ThemeServiceFactory"),
|
|
34
|
+
CSSClassesServiceFactory: Symbol.for("StyleSystem:CSSClassesServiceFactory"),
|
|
35
|
+
StoriesIndexService: Symbol.for("StyleSystem:StoriesIndexService"),
|
|
36
|
+
GetUiComponentService: Symbol.for("StyleSystem:GetUiComponentService"),
|
|
37
|
+
AppComponentsService: Symbol.for("StyleSystem:AppComponentsService"),
|
|
38
|
+
StoriesIndexFactory: Symbol.for("StyleSystem:StoriesIndexFactory"),
|
|
39
|
+
RendererFactory: Symbol.for("StyleSystem:RendererFactory"),
|
|
40
|
+
StyleSystemConfigLoader: Symbol.for("StyleSystem:StyleSystemConfigLoader"),
|
|
41
|
+
WorkspaceRoot: Symbol.for("StyleSystem:WorkspaceRoot"),
|
|
42
|
+
DefaultThemePath: Symbol.for("StyleSystem:DefaultThemePath"),
|
|
43
|
+
GetUiComponentServiceConfig: Symbol.for("StyleSystem:GetUiComponentServiceConfig"),
|
|
44
|
+
AppComponentsServiceConfig: Symbol.for("StyleSystem:AppComponentsServiceConfig")
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
28
47
|
//#region src/services/ThemeService/BaseThemeService.ts
|
|
29
48
|
/**
|
|
30
49
|
* BaseThemeService
|
|
@@ -95,12 +114,23 @@ const logger = pino({
|
|
|
95
114
|
mkdir: true,
|
|
96
115
|
sync: true
|
|
97
116
|
}));
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
function createLog(directory) {
|
|
118
|
+
const target = directory === void 0 ? logger : pino({
|
|
119
|
+
level: "debug",
|
|
120
|
+
timestamp: pino.stdTimeFunctions.isoTime
|
|
121
|
+
}, pino.destination({
|
|
122
|
+
dest: path$1.join(directory, "style-system.log"),
|
|
123
|
+
mkdir: true,
|
|
124
|
+
sync: true
|
|
125
|
+
}));
|
|
126
|
+
return {
|
|
127
|
+
debug: (msg, ...args) => target.debug({ args }, msg),
|
|
128
|
+
info: (msg, ...args) => target.info({ args }, msg),
|
|
129
|
+
warn: (msg, ...args) => target.warn({ args }, msg),
|
|
130
|
+
error: (msg, ...args) => target.error({ args }, msg)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const log = createLog();
|
|
104
134
|
//#endregion
|
|
105
135
|
//#region src/utils/fsHelpers.ts
|
|
106
136
|
/** True when candidate is root itself or inside it (sibling prefixes like `<root>-evil` are outside). */
|
|
@@ -248,9 +278,9 @@ var CSSThemeService = class extends BaseThemeService {
|
|
|
248
278
|
* Creates a new CSSThemeService instance
|
|
249
279
|
* @param config - Theme service configuration with themePath or cssFiles
|
|
250
280
|
*/
|
|
251
|
-
constructor(config) {
|
|
281
|
+
constructor(config, workspaceRoot) {
|
|
252
282
|
super(config);
|
|
253
|
-
this.monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
283
|
+
this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
254
284
|
}
|
|
255
285
|
/**
|
|
256
286
|
* Get the source identifier for this service
|
|
@@ -366,6 +396,18 @@ const DEFAULT_THEME_SERVICE_CONFIG = {
|
|
|
366
396
|
customServicePath: void 0
|
|
367
397
|
};
|
|
368
398
|
//#endregion
|
|
399
|
+
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateMetadata.js
|
|
400
|
+
function __decorateMetadata(k, v) {
|
|
401
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
402
|
+
}
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
|
|
405
|
+
function __decorateParam(paramIndex, decorator) {
|
|
406
|
+
return function(target, key) {
|
|
407
|
+
decorator(target, key, paramIndex);
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
369
411
|
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorate.js
|
|
370
412
|
function __decorate(decorators, target, key, desc) {
|
|
371
413
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -392,6 +434,10 @@ function __decorate(decorators, target, key, desc) {
|
|
|
392
434
|
* - Hardcoded service paths
|
|
393
435
|
*/
|
|
394
436
|
let ThemeServiceFactory = class ThemeServiceFactory {
|
|
437
|
+
workspaceRoot;
|
|
438
|
+
constructor(workspaceRoot) {
|
|
439
|
+
this.workspaceRoot = workspaceRoot;
|
|
440
|
+
}
|
|
395
441
|
/**
|
|
396
442
|
* Create a theme service based on configuration.
|
|
397
443
|
*
|
|
@@ -409,7 +455,7 @@ let ThemeServiceFactory = class ThemeServiceFactory {
|
|
|
409
455
|
...config
|
|
410
456
|
};
|
|
411
457
|
if (resolvedConfig.customServicePath) return this.loadCustomService(resolvedConfig);
|
|
412
|
-
return new CSSThemeService(resolvedConfig);
|
|
458
|
+
return new CSSThemeService(resolvedConfig, this.workspaceRoot);
|
|
413
459
|
}
|
|
414
460
|
/**
|
|
415
461
|
* Load a custom theme service from user-specified path.
|
|
@@ -436,7 +482,12 @@ let ThemeServiceFactory = class ThemeServiceFactory {
|
|
|
436
482
|
}
|
|
437
483
|
}
|
|
438
484
|
};
|
|
439
|
-
ThemeServiceFactory = __decorate([
|
|
485
|
+
ThemeServiceFactory = __decorate([
|
|
486
|
+
injectable(),
|
|
487
|
+
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
488
|
+
__decorateParam(0, optional()),
|
|
489
|
+
__decorateMetadata("design:paramtypes", [String])
|
|
490
|
+
], ThemeServiceFactory);
|
|
440
491
|
//#endregion
|
|
441
492
|
//#region src/services/CssClasses/BaseCSSClassesService.ts
|
|
442
493
|
/**
|
|
@@ -764,6 +815,10 @@ const VALID_SERVICE_EXTENSIONS$1 = [
|
|
|
764
815
|
".cjs"
|
|
765
816
|
];
|
|
766
817
|
let CSSClassesServiceFactory = class CSSClassesServiceFactory {
|
|
818
|
+
workspaceRoot;
|
|
819
|
+
constructor(workspaceRoot) {
|
|
820
|
+
this.workspaceRoot = workspaceRoot;
|
|
821
|
+
}
|
|
767
822
|
/**
|
|
768
823
|
* Create a CSS classes service based on configuration.
|
|
769
824
|
*
|
|
@@ -804,7 +859,7 @@ let CSSClassesServiceFactory = class CSSClassesServiceFactory {
|
|
|
804
859
|
async loadCustomService(config) {
|
|
805
860
|
const servicePath = config.customServicePath;
|
|
806
861
|
if (!servicePath) throw new Error("customServicePath is required for custom service loading");
|
|
807
|
-
const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
862
|
+
const monorepoRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
808
863
|
const resolvedPath = path.resolve(monorepoRoot, servicePath);
|
|
809
864
|
const normalizedWorkspaceRoot = path.resolve(monorepoRoot);
|
|
810
865
|
if (!resolvedPath.startsWith(normalizedWorkspaceRoot + path.sep)) throw new Error(`Security error: customServicePath "${servicePath}" resolves outside workspace root`);
|
|
@@ -825,12 +880,12 @@ let CSSClassesServiceFactory = class CSSClassesServiceFactory {
|
|
|
825
880
|
}
|
|
826
881
|
}
|
|
827
882
|
};
|
|
828
|
-
CSSClassesServiceFactory = __decorate([
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
883
|
+
CSSClassesServiceFactory = __decorate([
|
|
884
|
+
injectable(),
|
|
885
|
+
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
886
|
+
__decorateParam(0, optional()),
|
|
887
|
+
__decorateMetadata("design:paramtypes", [String])
|
|
888
|
+
], CSSClassesServiceFactory);
|
|
834
889
|
//#endregion
|
|
835
890
|
//#region src/services/StoriesIndexService/StoriesIndexService.ts
|
|
836
891
|
/**
|
|
@@ -885,6 +940,7 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
885
940
|
*/
|
|
886
941
|
async initialize() {
|
|
887
942
|
if (this.initialized && this.lastInitResult) return this.lastInitResult;
|
|
943
|
+
if (this.config.workspaceRoot) this.monorepoRoot = await promises.realpath(this.monorepoRoot);
|
|
888
944
|
log.info("[StoriesIndexService] Initializing story index...");
|
|
889
945
|
const storyFiles = await this.findStoryFiles();
|
|
890
946
|
log.info(`[StoriesIndexService] Found ${storyFiles.length} story files`);
|
|
@@ -956,6 +1012,7 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
956
1012
|
* parse is shared by content hash across service instances and concurrent rules.
|
|
957
1013
|
*/
|
|
958
1014
|
async indexStoryFile(filePath) {
|
|
1015
|
+
await this.assertContainedStory(filePath);
|
|
959
1016
|
const content = await promises.readFile(filePath, "utf-8");
|
|
960
1017
|
const fileHash = this.hashContent(content);
|
|
961
1018
|
const cached = storyParseCache.get(filePath);
|
|
@@ -982,12 +1039,19 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
982
1039
|
});
|
|
983
1040
|
if (component) this.storeComponent(component);
|
|
984
1041
|
}
|
|
1042
|
+
async assertContainedStory(filePath) {
|
|
1043
|
+
if (!this.config.workspaceRoot) return;
|
|
1044
|
+
const [root, story] = await Promise.all([promises.realpath(this.monorepoRoot), promises.realpath(filePath)]);
|
|
1045
|
+
if (!isContained(root, story)) throw new Error("Story must remain inside the configured workspace root.");
|
|
1046
|
+
}
|
|
985
1047
|
storeComponent(component) {
|
|
986
1048
|
this.componentIndex.set(component.title, component);
|
|
987
1049
|
this.componentFileIndex.set(path.resolve(component.filePath), component);
|
|
988
1050
|
}
|
|
989
1051
|
parseStoryFile(filePath, content, fileHash) {
|
|
990
|
-
const
|
|
1052
|
+
const alias = /^export\s+default\s+([A-Za-z_$][\w$]*)\s*;?\s*$/m.exec(content)?.[1];
|
|
1053
|
+
const parsable = alias ? content.replace(new RegExp(`^export\\s+(const|let|var)\\s+(${alias.replaceAll("$", "\\$")})\\b`, "m"), "$1 $2") : content;
|
|
1054
|
+
const csf = loadCsf(parsable, {
|
|
991
1055
|
fileName: filePath,
|
|
992
1056
|
makeTitle: (title) => title
|
|
993
1057
|
});
|
|
@@ -1081,6 +1145,7 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
1081
1145
|
* @returns True if file was updated, false if unchanged
|
|
1082
1146
|
*/
|
|
1083
1147
|
async refreshFile(filePath) {
|
|
1148
|
+
await this.assertContainedStory(filePath);
|
|
1084
1149
|
const content = await promises.readFile(filePath, "utf-8");
|
|
1085
1150
|
const newHash = this.hashContent(content);
|
|
1086
1151
|
const existingComponent = Array.from(this.componentIndex.values()).find((c) => c.filePath === filePath);
|
|
@@ -1120,27 +1185,6 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
1120
1185
|
};
|
|
1121
1186
|
StoriesIndexService = __decorate([injectable(), __decorateMetadata("design:paramtypes", [Object])], StoriesIndexService);
|
|
1122
1187
|
//#endregion
|
|
1123
|
-
//#region src/container/types.ts
|
|
1124
|
-
const STYLE_SYSTEM_TYPES = {
|
|
1125
|
-
ListThemesTool: Symbol.for("StyleSystem:ListThemesTool"),
|
|
1126
|
-
GetCSSClassesTool: Symbol.for("StyleSystem:GetCSSClassesTool"),
|
|
1127
|
-
GetComponentVisualTool: Symbol.for("StyleSystem:GetComponentVisualTool"),
|
|
1128
|
-
ListSharedComponentsTool: Symbol.for("StyleSystem:ListSharedComponentsTool"),
|
|
1129
|
-
ListAppComponentsTool: Symbol.for("StyleSystem:ListAppComponentsTool"),
|
|
1130
|
-
PolishComponentTool: Symbol.for("StyleSystem:PolishComponentTool"),
|
|
1131
|
-
ThemeServiceFactory: Symbol.for("StyleSystem:ThemeServiceFactory"),
|
|
1132
|
-
CSSClassesServiceFactory: Symbol.for("StyleSystem:CSSClassesServiceFactory"),
|
|
1133
|
-
StoriesIndexService: Symbol.for("StyleSystem:StoriesIndexService"),
|
|
1134
|
-
GetUiComponentService: Symbol.for("StyleSystem:GetUiComponentService"),
|
|
1135
|
-
AppComponentsService: Symbol.for("StyleSystem:AppComponentsService"),
|
|
1136
|
-
StoriesIndexFactory: Symbol.for("StyleSystem:StoriesIndexFactory"),
|
|
1137
|
-
RendererFactory: Symbol.for("StyleSystem:RendererFactory"),
|
|
1138
|
-
StyleSystemConfigLoader: Symbol.for("StyleSystem:StyleSystemConfigLoader"),
|
|
1139
|
-
DefaultThemePath: Symbol.for("StyleSystem:DefaultThemePath"),
|
|
1140
|
-
GetUiComponentServiceConfig: Symbol.for("StyleSystem:GetUiComponentServiceConfig"),
|
|
1141
|
-
AppComponentsServiceConfig: Symbol.for("StyleSystem:AppComponentsServiceConfig")
|
|
1142
|
-
};
|
|
1143
|
-
//#endregion
|
|
1144
1188
|
//#region src/config/types.ts
|
|
1145
1189
|
/**
|
|
1146
1190
|
* Style system configuration types.
|
|
@@ -1306,9 +1350,9 @@ const EXPO_APP_CONFIG_FILE = "app.json";
|
|
|
1306
1350
|
* values, and warns when it is the one that resolves.
|
|
1307
1351
|
*/
|
|
1308
1352
|
async function resolveStyleSystemThemePath(input) {
|
|
1309
|
-
const hostRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
1353
|
+
const hostRoot = input.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
1310
1354
|
const resolvedAppPath = input.appPath ? path.isAbsolute(input.appPath) ? path.normalize(input.appPath) : path.join(hostRoot, input.appPath) : hostRoot;
|
|
1311
|
-
const monorepoRoot = input.appPath ? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath) : hostRoot;
|
|
1355
|
+
const monorepoRoot = input.workspaceRoot ?? (input.appPath ? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath) : hostRoot);
|
|
1312
1356
|
const workspaceCandidate = path.join(monorepoRoot, input.themePath);
|
|
1313
1357
|
const candidates = path.isAbsolute(input.themePath) ? [input.themePath] : [workspaceCandidate, path.resolve(resolvedAppPath, input.themePath)];
|
|
1314
1358
|
const uniqueCandidates = [...new Set(candidates)];
|
|
@@ -1358,19 +1402,23 @@ function isNotFound(error) {
|
|
|
1358
1402
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
1359
1403
|
}
|
|
1360
1404
|
let StyleSystemConfigLoader = class StyleSystemConfigLoader {
|
|
1405
|
+
workspaceRoot;
|
|
1361
1406
|
/**
|
|
1362
1407
|
* Promises rather than values, so concurrent resolves de-duplicate. Several
|
|
1363
1408
|
* callers hit config from inside Promise.all/allSettled.
|
|
1364
1409
|
*/
|
|
1365
1410
|
rootPromises = /* @__PURE__ */ new Map();
|
|
1366
1411
|
projectCache = /* @__PURE__ */ new Map();
|
|
1412
|
+
constructor(workspaceRoot) {
|
|
1413
|
+
this.workspaceRoot = workspaceRoot;
|
|
1414
|
+
}
|
|
1367
1415
|
/** Drop all cached config state. */
|
|
1368
1416
|
invalidate() {
|
|
1369
1417
|
this.rootPromises.clear();
|
|
1370
1418
|
this.projectCache.clear();
|
|
1371
1419
|
}
|
|
1372
1420
|
/** Load and cache the workspace root config. */
|
|
1373
|
-
async loadRoot(workspaceRoot = TemplatesManagerService.getWorkspaceRootSync()) {
|
|
1421
|
+
async loadRoot(workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync()) {
|
|
1374
1422
|
let pending = this.rootPromises.get(workspaceRoot);
|
|
1375
1423
|
if (!pending) {
|
|
1376
1424
|
pending = this.readRoot(workspaceRoot);
|
|
@@ -1386,8 +1434,8 @@ let StyleSystemConfigLoader = class StyleSystemConfigLoader {
|
|
|
1386
1434
|
/** Resolve the merged configuration for a project. */
|
|
1387
1435
|
async resolveProject(appPath) {
|
|
1388
1436
|
if (!appPath || typeof appPath !== "string") throw new Error("appPath is required and must be a non-empty string");
|
|
1389
|
-
const resolvedAppPath = path.isAbsolute(appPath) ? path.normalize(appPath) : path.join(TemplatesManagerService.getWorkspaceRootSync(), appPath);
|
|
1390
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath);
|
|
1437
|
+
const resolvedAppPath = path.isAbsolute(appPath) ? path.normalize(appPath) : path.join(this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(), appPath);
|
|
1438
|
+
const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath);
|
|
1391
1439
|
const cached = this.projectCache.get(resolvedAppPath);
|
|
1392
1440
|
if (cached) return cached;
|
|
1393
1441
|
const pending = this.readProject(resolvedAppPath, workspaceRoot);
|
|
@@ -1454,7 +1502,12 @@ let StyleSystemConfigLoader = class StyleSystemConfigLoader {
|
|
|
1454
1502
|
};
|
|
1455
1503
|
}
|
|
1456
1504
|
};
|
|
1457
|
-
StyleSystemConfigLoader = __decorate([
|
|
1505
|
+
StyleSystemConfigLoader = __decorate([
|
|
1506
|
+
injectable(),
|
|
1507
|
+
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
1508
|
+
__decorateParam(0, optional()),
|
|
1509
|
+
__decorateMetadata("design:paramtypes", [String])
|
|
1510
|
+
], StyleSystemConfigLoader);
|
|
1458
1511
|
//#endregion
|
|
1459
1512
|
//#region src/config/index.ts
|
|
1460
1513
|
/**
|
|
@@ -1552,6 +1605,17 @@ const browserLaunchConfigs = [
|
|
|
1552
1605
|
];
|
|
1553
1606
|
const STORY_ROOT_SELECTOR = "#root";
|
|
1554
1607
|
const VITE_ERROR_OVERLAY_SELECTOR = "vite-error-overlay";
|
|
1608
|
+
const STORY_MOUNT_TIMEOUT_MS = 1e4;
|
|
1609
|
+
/**
|
|
1610
|
+
* Runs in the page (this package has no DOM types): resolves once web fonts and every image the
|
|
1611
|
+
* story rendered have loaded (or failed), then two frames have painted.
|
|
1612
|
+
*/
|
|
1613
|
+
const FONTS_IMAGES_AND_TWO_FRAMES = `Promise.all([
|
|
1614
|
+
document.fonts.ready,
|
|
1615
|
+
...Array.from(document.images, (image) =>
|
|
1616
|
+
image.complete ? null : new Promise((resolve) => { image.addEventListener('load', resolve, { once: true }); image.addEventListener('error', resolve, { once: true }); }),
|
|
1617
|
+
),
|
|
1618
|
+
]).then(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))))`;
|
|
1555
1619
|
/**
|
|
1556
1620
|
* Attempts to launch a browser, trying multiple configurations in order of preference.
|
|
1557
1621
|
* @returns Launched browser instance
|
|
@@ -1573,26 +1637,39 @@ async function launchBrowserWithFallback() {
|
|
|
1573
1637
|
throw new Error(`No browser available. Tried:\n${errors.join("\n")}\n\nPlease install Chrome, or run 'npx playwright install chromium'`);
|
|
1574
1638
|
}
|
|
1575
1639
|
async function takeScreenshot(options) {
|
|
1576
|
-
const { url, output, width = 1280, height = 800, fullPage = false, browser: browserName = "chromium", waitTime = 1e3, darkMode = false, mobile = false, generateThumbnail = false, thumbnailWidth = 400, thumbnailQuality = 80, base64 = false } = options;
|
|
1640
|
+
const { url, output, width = 1280, height = 800, fullPage = false, browser: browserName = "chromium", waitTime = 1e3, darkMode = false, mobile = false, generateThumbnail = false, thumbnailWidth = 400, thumbnailQuality = 80, base64 = false, sharedBrowser } = options;
|
|
1577
1641
|
let browser = null;
|
|
1642
|
+
let context = null;
|
|
1643
|
+
let release;
|
|
1578
1644
|
try {
|
|
1579
|
-
if (
|
|
1645
|
+
if (sharedBrowser) {
|
|
1646
|
+
const borrowed = await sharedBrowser();
|
|
1647
|
+
if ("browser" in borrowed) {
|
|
1648
|
+
browser = borrowed.browser;
|
|
1649
|
+
release = () => borrowed.release();
|
|
1650
|
+
} else browser = borrowed;
|
|
1651
|
+
} else if (browserName === "chromium") browser = await launchBrowserWithFallback();
|
|
1580
1652
|
else {
|
|
1581
1653
|
const browserType = browsers[browserName];
|
|
1582
1654
|
if (!browserType) throw new Error(`Unsupported browser: ${browserName}`);
|
|
1583
1655
|
browser = await browserType.launch({ headless: true });
|
|
1584
1656
|
}
|
|
1585
|
-
|
|
1657
|
+
context = await browser.newContext({
|
|
1586
1658
|
viewport: {
|
|
1587
1659
|
width,
|
|
1588
1660
|
height
|
|
1589
1661
|
},
|
|
1590
1662
|
colorScheme: darkMode ? "dark" : "light",
|
|
1591
1663
|
isMobile: mobile
|
|
1592
|
-
})
|
|
1664
|
+
});
|
|
1665
|
+
const page = await context.newPage();
|
|
1593
1666
|
const runtimeErrors = [];
|
|
1594
|
-
page.on("pageerror", (error) => runtimeErrors.push(error.stack
|
|
1595
|
-
await page.goto(url, { waitUntil: "
|
|
1667
|
+
page.on("pageerror", (error) => runtimeErrors.push(error.stack || error.message || String(error)));
|
|
1668
|
+
await page.goto(url, { waitUntil: "load" });
|
|
1669
|
+
if (await page.waitForSelector(`${STORY_ROOT_SELECTOR} > *, ${VITE_ERROR_OVERLAY_SELECTOR}`, {
|
|
1670
|
+
state: "attached",
|
|
1671
|
+
timeout: STORY_MOUNT_TIMEOUT_MS
|
|
1672
|
+
}).then(() => true).catch(() => false)) await page.evaluate(FONTS_IMAGES_AND_TWO_FRAMES);
|
|
1596
1673
|
if (waitTime > 0) await page.waitForTimeout(waitTime);
|
|
1597
1674
|
const hasViteErrorOverlay = await page.locator(VITE_ERROR_OVERLAY_SELECTOR).count();
|
|
1598
1675
|
const renderedChildCount = await page.locator(`${STORY_ROOT_SELECTOR} > *`).count();
|
|
@@ -1604,7 +1681,27 @@ async function takeScreenshot(options) {
|
|
|
1604
1681
|
path: output,
|
|
1605
1682
|
fullPage
|
|
1606
1683
|
});
|
|
1607
|
-
const screenshotMetadata = await
|
|
1684
|
+
const screenshotMetadata = await page.evaluate(`(async () => {
|
|
1685
|
+
const bytes = Uint8Array.from(atob(${JSON.stringify(screenshotBuffer.toString("base64"))}), (char) => char.charCodeAt(0));
|
|
1686
|
+
const image = await createImageBitmap(new Blob([bytes]));
|
|
1687
|
+
try {
|
|
1688
|
+
const result = { width: image.width, height: image.height };
|
|
1689
|
+
if (${generateThumbnail}) {
|
|
1690
|
+
const width = ${JSON.stringify(thumbnailWidth)};
|
|
1691
|
+
const quality = ${JSON.stringify(thumbnailQuality)};
|
|
1692
|
+
if (!Number.isSafeInteger(width) || width <= 0 || !Number.isInteger(quality) || quality < 1 || quality > 100)
|
|
1693
|
+
throw new Error('Invalid thumbnail width or quality.');
|
|
1694
|
+
const canvas = document.createElement('canvas');
|
|
1695
|
+
canvas.width = width;
|
|
1696
|
+
canvas.height = Math.max(1, Math.round(image.height * width / image.width));
|
|
1697
|
+
canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
1698
|
+
result.thumbnail = canvas.toDataURL('image/jpeg', quality / 100).split(',')[1];
|
|
1699
|
+
}
|
|
1700
|
+
return result;
|
|
1701
|
+
} finally {
|
|
1702
|
+
image.close();
|
|
1703
|
+
}
|
|
1704
|
+
})()`);
|
|
1608
1705
|
const result = {
|
|
1609
1706
|
imagePath: output,
|
|
1610
1707
|
width: screenshotMetadata.width ?? width,
|
|
@@ -1613,13 +1710,18 @@ async function takeScreenshot(options) {
|
|
|
1613
1710
|
if (generateThumbnail) {
|
|
1614
1711
|
const ext = path.extname(output);
|
|
1615
1712
|
const thumbnailPath = output.replace(ext, `-thumb${ext}`);
|
|
1616
|
-
await
|
|
1713
|
+
await writeFile(thumbnailPath.replace(ext, ".jpg"), Buffer.from(screenshotMetadata.thumbnail, "base64"));
|
|
1617
1714
|
result.thumbnailPath = thumbnailPath.replace(ext, ".jpg");
|
|
1618
1715
|
}
|
|
1619
1716
|
if (base64) result.base64 = screenshotBuffer.toString("base64");
|
|
1620
1717
|
return result;
|
|
1621
1718
|
} finally {
|
|
1622
|
-
|
|
1719
|
+
try {
|
|
1720
|
+
if (sharedBrowser) await context?.close();
|
|
1721
|
+
else if (browser) await browser.close();
|
|
1722
|
+
} finally {
|
|
1723
|
+
release?.();
|
|
1724
|
+
}
|
|
1623
1725
|
}
|
|
1624
1726
|
}
|
|
1625
1727
|
//#endregion
|
|
@@ -1869,6 +1971,37 @@ function createStoryEntryPlugin(getStoryConfig, generateCode) {
|
|
|
1869
1971
|
}
|
|
1870
1972
|
};
|
|
1871
1973
|
}
|
|
1974
|
+
/** Where a package the app depends on is installed, resolved from the app rather than from this tool. */
|
|
1975
|
+
function appPackageDir(appPath, packageName) {
|
|
1976
|
+
return path.dirname(createRequire(path.join(appPath, "package.json")).resolve(`${packageName}/package.json`));
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* React and react-dom are peers of this tool: every story bundle takes the app's own pair, so the
|
|
1980
|
+
* story, the generated entry and any workspace package it imports share one React that matches its
|
|
1981
|
+
* react-dom. A pair resolved from the tool fails in the browser with React error #527.
|
|
1982
|
+
*/
|
|
1983
|
+
function appReactAliases(appPath) {
|
|
1984
|
+
const reactDir = appPackageDir(appPath, "react");
|
|
1985
|
+
const reactDomDir = appPackageDir(appPath, "react-dom");
|
|
1986
|
+
return [
|
|
1987
|
+
{
|
|
1988
|
+
find: /^react$/,
|
|
1989
|
+
replacement: reactDir
|
|
1990
|
+
},
|
|
1991
|
+
{
|
|
1992
|
+
find: /^react\/(.*)$/,
|
|
1993
|
+
replacement: path.join(reactDir, "$1")
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
find: /^react-dom$/,
|
|
1997
|
+
replacement: reactDomDir
|
|
1998
|
+
},
|
|
1999
|
+
{
|
|
2000
|
+
find: /^react-dom\/(.*)$/,
|
|
2001
|
+
replacement: path.join(reactDomDir, "$1")
|
|
2002
|
+
}
|
|
2003
|
+
];
|
|
2004
|
+
}
|
|
1872
2005
|
/**
|
|
1873
2006
|
* Helper to create a Vite plugin that resolves the module specifiers TanStack Start's
|
|
1874
2007
|
* `tanstackStart()` plugin normally provides to inert client-safe stubs, so stories in TanStack
|
|
@@ -1909,6 +2042,7 @@ function createTanStackStartStubPlugin() {
|
|
|
1909
2042
|
* ```
|
|
1910
2043
|
*/
|
|
1911
2044
|
var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerService {
|
|
2045
|
+
workspaceRoot;
|
|
1912
2046
|
static instance = null;
|
|
1913
2047
|
server = null;
|
|
1914
2048
|
monorepoRoot;
|
|
@@ -1925,9 +2059,10 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
1925
2059
|
* Use getInstance() for singleton access.
|
|
1926
2060
|
* @param config - Service configuration options
|
|
1927
2061
|
*/
|
|
1928
|
-
constructor(config = {}) {
|
|
2062
|
+
constructor(config = {}, workspaceRoot) {
|
|
1929
2063
|
super(config);
|
|
1930
|
-
this.
|
|
2064
|
+
this.workspaceRoot = workspaceRoot;
|
|
2065
|
+
this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
1931
2066
|
}
|
|
1932
2067
|
/**
|
|
1933
2068
|
* Get the singleton instance of ViteReactBundlerService.
|
|
@@ -2028,6 +2163,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2028
2163
|
*/
|
|
2029
2164
|
async doStartDevServer(resolvedAppPath) {
|
|
2030
2165
|
const tmpDir = path.join(resolvedAppPath, TMP_DIR_NAME);
|
|
2166
|
+
let createdServer;
|
|
2031
2167
|
try {
|
|
2032
2168
|
await promises.mkdir(tmpDir, { recursive: true });
|
|
2033
2169
|
const { createServer } = await import("vite");
|
|
@@ -2042,6 +2178,33 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2042
2178
|
tailwindcss(),
|
|
2043
2179
|
createTanStackStartStubPlugin(),
|
|
2044
2180
|
...this.getAdditionalVitePlugins(resolvedAppPath),
|
|
2181
|
+
{
|
|
2182
|
+
name: "style-system-preview-html",
|
|
2183
|
+
configureServer: (server) => {
|
|
2184
|
+
server.middlewares.use(async (req, res, next) => {
|
|
2185
|
+
const url = req.url || "/";
|
|
2186
|
+
const match = url.match(PREVIEW_ROUTE_PATTERN);
|
|
2187
|
+
if (match) {
|
|
2188
|
+
const storyId = match[1];
|
|
2189
|
+
const config = this.storyConfigs.get(storyId);
|
|
2190
|
+
if (config) try {
|
|
2191
|
+
const htmlTemplate = this.generateHtmlTemplate(`${VIRTUAL_STORY_ENTRY_SCRIPT_PREFIX}${storyId}`, config.darkMode);
|
|
2192
|
+
const transformedHtml = await server.transformIndexHtml(url, htmlTemplate);
|
|
2193
|
+
res.statusCode = HTTP_STATUS_OK;
|
|
2194
|
+
res.setHeader(CONTENT_TYPE_HEADER, TEXT_HTML_CONTENT_TYPE);
|
|
2195
|
+
res.end(transformedHtml);
|
|
2196
|
+
return;
|
|
2197
|
+
} catch (e) {
|
|
2198
|
+
const err = e;
|
|
2199
|
+
log.error(`[ViteMiddleware] Error serving preview: ${err.message}`);
|
|
2200
|
+
next(err);
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
next();
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
},
|
|
2045
2208
|
createStoryEntryPlugin((id) => this.storyConfigs.get(id), (opts) => this.generateEntryFile(opts))
|
|
2046
2209
|
],
|
|
2047
2210
|
resolve: this.getViteResolveConfig(resolvedAppPath),
|
|
@@ -2053,28 +2216,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2053
2216
|
open: false
|
|
2054
2217
|
}
|
|
2055
2218
|
});
|
|
2056
|
-
this.server
|
|
2057
|
-
const url = req.url || "/";
|
|
2058
|
-
const match = url.match(PREVIEW_ROUTE_PATTERN);
|
|
2059
|
-
if (match) {
|
|
2060
|
-
const storyId = match[1];
|
|
2061
|
-
const config = this.storyConfigs.get(storyId);
|
|
2062
|
-
if (config) try {
|
|
2063
|
-
const htmlTemplate = this.generateHtmlTemplate(`${VIRTUAL_STORY_ENTRY_SCRIPT_PREFIX}${storyId}`, config.darkMode);
|
|
2064
|
-
const transformedHtml = await this.server?.transformIndexHtml(url, htmlTemplate);
|
|
2065
|
-
res.statusCode = HTTP_STATUS_OK;
|
|
2066
|
-
res.setHeader(CONTENT_TYPE_HEADER, TEXT_HTML_CONTENT_TYPE);
|
|
2067
|
-
res.end(transformedHtml);
|
|
2068
|
-
return;
|
|
2069
|
-
} catch (e) {
|
|
2070
|
-
const err = e;
|
|
2071
|
-
log.error(`[ViteMiddleware] Error serving preview: ${err.message}`);
|
|
2072
|
-
next(err);
|
|
2073
|
-
return;
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
next();
|
|
2077
|
-
});
|
|
2219
|
+
createdServer = this.server;
|
|
2078
2220
|
await this.server.listen();
|
|
2079
2221
|
const address = this.server.httpServer?.address();
|
|
2080
2222
|
if (!address || typeof address === "string") throw new Error("Failed to start Vite dev server. Ensure no other process is using the port.");
|
|
@@ -2089,6 +2231,15 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2089
2231
|
port
|
|
2090
2232
|
};
|
|
2091
2233
|
} catch (error) {
|
|
2234
|
+
try {
|
|
2235
|
+
await createdServer?.close();
|
|
2236
|
+
} catch (closeError) {
|
|
2237
|
+
log.warn(`[ViteReactBundlerService] Failed to close server after startup failure: ${String(closeError)}`);
|
|
2238
|
+
}
|
|
2239
|
+
this.server = null;
|
|
2240
|
+
this.serverUrl = null;
|
|
2241
|
+
this.serverPort = null;
|
|
2242
|
+
this.currentAppPath = null;
|
|
2092
2243
|
const err = /* @__PURE__ */ new Error(`Failed to start dev server for ${resolvedAppPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2093
2244
|
err.cause = error;
|
|
2094
2245
|
throw err;
|
|
@@ -2107,7 +2258,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2107
2258
|
validateComponentPath(componentPath);
|
|
2108
2259
|
validateArgsSize(args);
|
|
2109
2260
|
const resolvedAppPath = path.isAbsolute(appPath) ? appPath : path.join(this.monorepoRoot, appPath);
|
|
2110
|
-
validateCssFiles(cssFiles, TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2261
|
+
validateCssFiles(cssFiles, this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2111
2262
|
if (this.currentAppPath !== resolvedAppPath) throw new Error(`Dev server is running for ${this.currentAppPath} but requested ${resolvedAppPath}.`);
|
|
2112
2263
|
const tmpDir = path.join(resolvedAppPath, TMP_DIR_NAME);
|
|
2113
2264
|
try {
|
|
@@ -2155,7 +2306,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2155
2306
|
validateComponentPath(componentPath);
|
|
2156
2307
|
validateArgsSize(args);
|
|
2157
2308
|
const resolvedAppPath = path.isAbsolute(appPath) ? appPath : path.join(this.monorepoRoot, appPath);
|
|
2158
|
-
validateCssFiles(cssFiles, TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2309
|
+
validateCssFiles(cssFiles, this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2159
2310
|
const tmpDir = path.join(resolvedAppPath, TMP_DIR_NAME);
|
|
2160
2311
|
try {
|
|
2161
2312
|
await promises.mkdir(tmpDir, { recursive: true });
|
|
@@ -2181,9 +2332,10 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2181
2332
|
* Subclasses can override to add platform-specific aliases (e.g., react-native-web).
|
|
2182
2333
|
*/
|
|
2183
2334
|
getViteResolveConfig(appPath) {
|
|
2184
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2335
|
+
const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2185
2336
|
const frontendPackagePath = (packageDir, sourcePath = "index.ts") => path.join(workspaceRoot, WORKSPACE_PACKAGE_DIR_PREFIX, "frontend", packageDir, APP_SOURCE_DIR$1, sourcePath);
|
|
2186
2337
|
return { alias: [
|
|
2338
|
+
...appReactAliases(appPath),
|
|
2187
2339
|
{
|
|
2188
2340
|
find: APP_SOURCE_ALIAS_PATTERN,
|
|
2189
2341
|
replacement: `${path.join(appPath, APP_SOURCE_DIR$1)}/`
|
|
@@ -2290,7 +2442,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2290
2442
|
*/
|
|
2291
2443
|
async generateWrapperCss(appPath, cssFiles) {
|
|
2292
2444
|
const resolvedCssFiles = cssFiles.length > 0 ? cssFiles : await this.getDefaultCssFiles(appPath);
|
|
2293
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2445
|
+
const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2294
2446
|
const cssImportStatements = resolvedCssFiles.map((cssFile) => {
|
|
2295
2447
|
if (path.isAbsolute(cssFile)) return `@import '${cssFile}';`;
|
|
2296
2448
|
if (cssFile.startsWith(PACKAGE_IMPORT_PREFIX) || cssFile.startsWith(TAILWIND_IMPORT_PREFIX)) return `@import '${cssFile}';`;
|
|
@@ -2512,20 +2664,16 @@ mount();
|
|
|
2512
2664
|
*
|
|
2513
2665
|
* Extends ViteReactBundlerService with React Native → web aliases
|
|
2514
2666
|
* so native-ui components can be rendered in a browser via Playwright.
|
|
2515
|
-
* Uses React Native Web and
|
|
2667
|
+
* Uses the app's own React Native Web and Unistyles web runtime.
|
|
2516
2668
|
*
|
|
2517
|
-
*
|
|
2669
|
+
* Shim paths resolve relative to this package; React, react-dom and
|
|
2670
|
+
* react-native-web are peers and always resolve from the app.
|
|
2518
2671
|
*/
|
|
2519
2672
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
2520
|
-
const require$1 = createRequire(import.meta.url);
|
|
2521
|
-
const REACT_PACKAGE = "react";
|
|
2522
|
-
const REACT_DOM_PACKAGE = "react-dom";
|
|
2523
2673
|
const REACT_NATIVE_PACKAGE = "react-native";
|
|
2524
2674
|
const REACT_NATIVE_WEB_PACKAGE = "react-native-web";
|
|
2525
|
-
const REACT_NATIVE_INDEX_SUBPATH = "react-native/index.js";
|
|
2526
2675
|
const REACT_NATIVE_UNISTYLES_PACKAGE = "react-native-unistyles";
|
|
2527
2676
|
const REACT_NATIVE_UNISTYLES_BABEL_PLUGIN = "react-native-unistyles/plugin";
|
|
2528
|
-
const REACT_NATIVE_REANIMATED_PACKAGE = "react-native-reanimated";
|
|
2529
2677
|
const REACT_NATIVE_REANIMATED_BABEL_PLUGIN = "react-native-reanimated/plugin";
|
|
2530
2678
|
const BABEL_CORE_PACKAGE = "@babel/core";
|
|
2531
2679
|
const BABEL_PLUGIN_SYNTAX_JSX = "@babel/plugin-syntax-jsx";
|
|
@@ -2539,8 +2687,7 @@ const APP_SOURCE_ALIAS = "@";
|
|
|
2539
2687
|
const APP_SOURCE_DIR = "src";
|
|
2540
2688
|
const VITE_PLUGIN_ENFORCE_PRE = "pre";
|
|
2541
2689
|
const VITE_REACT_NATIVE_BUNDLER_ID = "vite-react-native";
|
|
2542
|
-
const
|
|
2543
|
-
const REACT_NATIVE_REANIMATED_REDIRECT_PLUGIN_NAME = "vite-redirect-react-native-reanimated";
|
|
2690
|
+
const REACT_NATIVE_OPTIMIZE_DEPS_PLUGIN_NAME = "vite-react-native-optimize-deps";
|
|
2544
2691
|
const EXPO_ICONS_REDIRECT_PLUGIN_NAME = "vite-redirect-expo-vector-icons";
|
|
2545
2692
|
const NATIVE_BABEL_TRANSFORM_PLUGIN_NAME = "vite-react-native-babel";
|
|
2546
2693
|
const EXPO_VECTOR_ICONS_RESOLVED_MODULE_PREFIX = `\0virtual:style-system-expo-vector-icons`;
|
|
@@ -2551,10 +2698,8 @@ const EXPO_VECTOR_ICONS_SHIM_FILE = "expo-vector-icons.ts";
|
|
|
2551
2698
|
const EXPO_SYMBOLS_SHIM_FILE = "expo-symbols.ts";
|
|
2552
2699
|
const REQUIRED_REACT_NATIVE_SHIM_FILES = [EXPO_VECTOR_ICONS_SHIM_FILE, EXPO_SYMBOLS_SHIM_FILE];
|
|
2553
2700
|
const PACKAGE_JSON_SUBPATH = "package.json";
|
|
2554
|
-
const REACT_NATIVE_WEB_DIST_INDEX = "dist/index.js";
|
|
2555
2701
|
const REACT_NATIVE_UNISTYLES_WEB_INDEX = "lib/module/index.js";
|
|
2556
2702
|
const REACT_NATIVE_UNISTYLES_NATIVE_COMPONENTS = "lib/module/components/native";
|
|
2557
|
-
const REACT_NATIVE_REANIMATED_WEB_INDEX = "lib/module/index.js";
|
|
2558
2703
|
const EXPO_VECTOR_ICONS_GLYPHMAP_DIR = "build/vendor/react-native-vector-icons/glyphmaps";
|
|
2559
2704
|
const EXPO_VECTOR_ICONS_FONT_DIR = "build/vendor/react-native-vector-icons/Fonts";
|
|
2560
2705
|
const GLYPHMAP_EXTENSION = ".json";
|
|
@@ -2573,6 +2718,8 @@ const REACT_NATIVE_DEV_GLOBAL = "__DEV__";
|
|
|
2573
2718
|
const REACT_NATIVE_DEV_VALUE = "false";
|
|
2574
2719
|
const REACT_NATIVE_GLOBAL = "global";
|
|
2575
2720
|
const REACT_NATIVE_GLOBAL_VALUE = "globalThis";
|
|
2721
|
+
const EXPO_OS_DEFINE = "process.env.EXPO_OS";
|
|
2722
|
+
const EXPO_OS_WEB_VALUE = JSON.stringify("web");
|
|
2576
2723
|
const VITE_RESOLVE_EXTENSIONS = [
|
|
2577
2724
|
".web.tsx",
|
|
2578
2725
|
".web.ts",
|
|
@@ -2595,18 +2742,13 @@ const SHIMS_DIR_CANDIDATES = [
|
|
|
2595
2742
|
path.resolve(__dirname, "../src/shims")
|
|
2596
2743
|
];
|
|
2597
2744
|
const SHIMS_DIR = resolveShimsDir();
|
|
2598
|
-
const REACT_NATIVE_WEB_DIR = resolvePackageDir(REACT_NATIVE_WEB_PACKAGE);
|
|
2599
2745
|
function resolveExistingDir(candidates, label, isValidDir = () => true) {
|
|
2600
2746
|
const existing = candidates.find((candidate) => existsSync(candidate) && isValidDir(candidate));
|
|
2601
2747
|
if (existing) return existing;
|
|
2602
2748
|
throw new Error(`Could not resolve ${label}. Tried: ${candidates.join(", ")}`);
|
|
2603
2749
|
}
|
|
2604
|
-
function resolvePackageDir(packageName) {
|
|
2605
|
-
return dirname(require$1.resolve(`${packageName}/${PACKAGE_JSON_SUBPATH}`));
|
|
2606
|
-
}
|
|
2607
2750
|
function resolvePackageDirFrom(fromPath, packageName) {
|
|
2608
|
-
|
|
2609
|
-
return dirname(fromRequire.resolve(`${packageName}/${PACKAGE_JSON_SUBPATH}`));
|
|
2751
|
+
return appPackageDir(fromPath, packageName);
|
|
2610
2752
|
}
|
|
2611
2753
|
function resolvePackageSubpathFrom(fromPath, subpath) {
|
|
2612
2754
|
return createRequire(path.join(fromPath, PACKAGE_JSON_SUBPATH)).resolve(subpath);
|
|
@@ -2701,6 +2843,7 @@ function createNativeBabelTransformPlugin(appPath) {
|
|
|
2701
2843
|
name: NATIVE_BABEL_TRANSFORM_PLUGIN_NAME,
|
|
2702
2844
|
enforce: VITE_PLUGIN_ENFORCE_PRE,
|
|
2703
2845
|
async transform(code, id) {
|
|
2846
|
+
id = id.split(VITE_QUERY_SEPARATOR)[0] ?? id;
|
|
2704
2847
|
const isFlowModule = toViteImportPath(id).includes("/@react-native-segmented-control/segmented-control/");
|
|
2705
2848
|
const isSupportedNodeModule = isFlowModule || id.includes(REACT_NATIVE_MARKDOWN_DISPLAY_PATH_SEGMENT);
|
|
2706
2849
|
if (id.includes(NODE_MODULES_PATH_SEGMENT) && !isSupportedNodeModule || !BABEL_TRANSFORM_EXTENSION_PATTERN.test(id)) return null;
|
|
@@ -2842,24 +2985,20 @@ var ViteReactNativeBundlerService = class ViteReactNativeBundlerService extends
|
|
|
2842
2985
|
return VITE_REACT_NATIVE_BUNDLER_ID;
|
|
2843
2986
|
}
|
|
2844
2987
|
getAdditionalVitePlugins(appPath) {
|
|
2845
|
-
const reactNativeWebEntry = path.join(REACT_NATIVE_WEB_DIR, REACT_NATIVE_WEB_DIST_INDEX);
|
|
2846
|
-
const reactNativeReanimatedEntry = appPath ? path.join(resolvePackageDirFrom(appPath, REACT_NATIVE_REANIMATED_PACKAGE), REACT_NATIVE_REANIMATED_WEB_INDEX) : null;
|
|
2847
2988
|
return [
|
|
2848
2989
|
{
|
|
2849
|
-
name:
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
return null;
|
|
2862
|
-
}
|
|
2990
|
+
name: REACT_NATIVE_OPTIMIZE_DEPS_PLUGIN_NAME,
|
|
2991
|
+
config: (config) => ({ optimizeDeps: {
|
|
2992
|
+
rolldownOptions: { resolve: { extensions: config.resolve?.extensions } },
|
|
2993
|
+
exclude: [
|
|
2994
|
+
"@react-native-segmented-control/segmented-control",
|
|
2995
|
+
SAFE_AREA_CONTEXT_PACKAGE,
|
|
2996
|
+
"expo-haptics",
|
|
2997
|
+
"expo-modules-core",
|
|
2998
|
+
"react-native-gesture-handler"
|
|
2999
|
+
],
|
|
3000
|
+
include: ["react-native-gesture-handler > hoist-non-react-statics", "react-native-gesture-handler > invariant"]
|
|
3001
|
+
} })
|
|
2863
3002
|
},
|
|
2864
3003
|
createExpoTypeDeclarationStubPlugin(),
|
|
2865
3004
|
createSvgComponentPlugin(),
|
|
@@ -2895,7 +3034,8 @@ const styleSystemSafeAreaMetrics = {
|
|
|
2895
3034
|
getViteDefineConfig() {
|
|
2896
3035
|
return {
|
|
2897
3036
|
[REACT_NATIVE_DEV_GLOBAL]: REACT_NATIVE_DEV_VALUE,
|
|
2898
|
-
[REACT_NATIVE_GLOBAL]: REACT_NATIVE_GLOBAL_VALUE
|
|
3037
|
+
[REACT_NATIVE_GLOBAL]: REACT_NATIVE_GLOBAL_VALUE,
|
|
3038
|
+
[EXPO_OS_DEFINE]: EXPO_OS_WEB_VALUE
|
|
2899
3039
|
};
|
|
2900
3040
|
}
|
|
2901
3041
|
generatePreviewStyles() {
|
|
@@ -2920,38 +3060,18 @@ const styleSystemSafeAreaMetrics = {
|
|
|
2920
3060
|
getViteResolveConfig(appPath) {
|
|
2921
3061
|
const reactNativeUnistylesDir = resolvePackageDirFrom(appPath, REACT_NATIVE_UNISTYLES_PACKAGE);
|
|
2922
3062
|
const reactNativeUnistylesEntry = path.join(reactNativeUnistylesDir, REACT_NATIVE_UNISTYLES_WEB_INDEX);
|
|
2923
|
-
const
|
|
2924
|
-
const reactDomDir = resolvePackageDirFrom(appPath, REACT_DOM_PACKAGE);
|
|
3063
|
+
const reactNativeWebDir = resolvePackageDirFrom(appPath, REACT_NATIVE_WEB_PACKAGE);
|
|
2925
3064
|
return {
|
|
2926
3065
|
extensions: [...VITE_RESOLVE_EXTENSIONS],
|
|
2927
3066
|
alias: [
|
|
2928
|
-
|
|
2929
|
-
find: /^react$/,
|
|
2930
|
-
replacement: reactDir
|
|
2931
|
-
},
|
|
2932
|
-
{
|
|
2933
|
-
find: /^react\/(.*)$/,
|
|
2934
|
-
replacement: path.join(reactDir, "$1")
|
|
2935
|
-
},
|
|
2936
|
-
{
|
|
2937
|
-
find: /^react-dom$/,
|
|
2938
|
-
replacement: reactDomDir
|
|
2939
|
-
},
|
|
2940
|
-
{
|
|
2941
|
-
find: /^react-dom\/(.*)$/,
|
|
2942
|
-
replacement: path.join(reactDomDir, "$1")
|
|
2943
|
-
},
|
|
3067
|
+
...appReactAliases(appPath),
|
|
2944
3068
|
{
|
|
2945
3069
|
find: APP_SOURCE_ALIAS,
|
|
2946
3070
|
replacement: path.join(appPath, APP_SOURCE_DIR)
|
|
2947
3071
|
},
|
|
2948
3072
|
{
|
|
2949
|
-
find:
|
|
2950
|
-
replacement:
|
|
2951
|
-
},
|
|
2952
|
-
{
|
|
2953
|
-
find: REACT_NATIVE_INDEX_SUBPATH,
|
|
2954
|
-
replacement: path.join(REACT_NATIVE_WEB_DIR, REACT_NATIVE_WEB_DIST_INDEX)
|
|
3073
|
+
find: /^react-native$/,
|
|
3074
|
+
replacement: reactNativeWebDir
|
|
2955
3075
|
},
|
|
2956
3076
|
{
|
|
2957
3077
|
find: /^react-native-unistyles\/components\/native\/(.+)$/,
|
|
@@ -3014,8 +3134,8 @@ const VALID_SERVICE_EXTENSIONS = [
|
|
|
3014
3134
|
* await bundler.startDevServer('apps/my-app');
|
|
3015
3135
|
* ```
|
|
3016
3136
|
*/
|
|
3017
|
-
function createDefaultBundlerService() {
|
|
3018
|
-
return ViteReactBundlerService.getInstance();
|
|
3137
|
+
function createDefaultBundlerService(workspaceRoot) {
|
|
3138
|
+
return workspaceRoot ? new ViteReactBundlerService({}, workspaceRoot) : ViteReactBundlerService.getInstance();
|
|
3019
3139
|
}
|
|
3020
3140
|
/**
|
|
3021
3141
|
* Registry of available bundler service factories.
|
|
@@ -3050,13 +3170,17 @@ bundlerRegistry.set("vite-react-native", () => ViteReactNativeBundlerService.get
|
|
|
3050
3170
|
* const bundler = getBundlerService('unknown'); // Returns ViteReactBundlerService
|
|
3051
3171
|
* ```
|
|
3052
3172
|
*/
|
|
3053
|
-
function getBundlerService(key) {
|
|
3173
|
+
function getBundlerService(key, workspaceRoot) {
|
|
3174
|
+
if (workspaceRoot) {
|
|
3175
|
+
if (key === "vite-react-native") return new ViteReactNativeBundlerService({}, workspaceRoot);
|
|
3176
|
+
if (key === "vite-react") return new ViteReactBundlerService({}, workspaceRoot);
|
|
3177
|
+
}
|
|
3054
3178
|
const factory = bundlerRegistry.get(key);
|
|
3055
3179
|
if (factory) return factory();
|
|
3056
|
-
return createDefaultBundlerService();
|
|
3180
|
+
return createDefaultBundlerService(workspaceRoot);
|
|
3057
3181
|
}
|
|
3058
3182
|
/** Cached bundler service instance loaded from config */
|
|
3059
|
-
let
|
|
3183
|
+
let cachedDefaultBundlerService = null;
|
|
3060
3184
|
/**
|
|
3061
3185
|
* Get bundler service based on the workspace style-system.config.yaml.
|
|
3062
3186
|
*
|
|
@@ -3080,25 +3204,31 @@ let cachedBundlerService = null;
|
|
|
3080
3204
|
* await bundler.startDevServer('apps/my-app');
|
|
3081
3205
|
* ```
|
|
3082
3206
|
*/
|
|
3083
|
-
async function getBundlerServiceFromConfig() {
|
|
3084
|
-
if (
|
|
3085
|
-
|
|
3207
|
+
async function getBundlerServiceFromConfig(options) {
|
|
3208
|
+
if (options) return loadBundlerServiceFromConfig(options);
|
|
3209
|
+
cachedDefaultBundlerService ??= await loadBundlerServiceFromConfig();
|
|
3210
|
+
return cachedDefaultBundlerService;
|
|
3211
|
+
}
|
|
3212
|
+
async function loadBundlerServiceFromConfig(options) {
|
|
3213
|
+
let cachedBundlerService = null;
|
|
3214
|
+
const fallback = options?.defaultFactory ?? (() => createDefaultBundlerService(options?.workspaceRoot));
|
|
3215
|
+
const config = options ? (await (options.configLoader ?? new StyleSystemConfigLoader(options.workspaceRoot)).loadRoot()).bundler : await getBundlerConfig();
|
|
3086
3216
|
if (!config?.customService) {
|
|
3087
|
-
cachedBundlerService =
|
|
3217
|
+
cachedBundlerService = fallback();
|
|
3088
3218
|
return cachedBundlerService;
|
|
3089
3219
|
}
|
|
3090
|
-
const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
3220
|
+
const monorepoRoot = options?.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
3091
3221
|
const customServicePath = path.resolve(monorepoRoot, config.customService);
|
|
3092
3222
|
const normalizedWorkspaceRoot = path.resolve(monorepoRoot);
|
|
3093
3223
|
if (!customServicePath.startsWith(normalizedWorkspaceRoot + path.sep)) {
|
|
3094
3224
|
log.error(`[BundlerServiceFactory] Security error: customService path "${config.customService}" resolves outside workspace root`);
|
|
3095
|
-
cachedBundlerService =
|
|
3225
|
+
cachedBundlerService = fallback();
|
|
3096
3226
|
return cachedBundlerService;
|
|
3097
3227
|
}
|
|
3098
3228
|
const ext = path.extname(customServicePath).toLowerCase();
|
|
3099
3229
|
if (!VALID_SERVICE_EXTENSIONS.includes(ext)) {
|
|
3100
3230
|
log.error(`[BundlerServiceFactory] Invalid file extension "${ext}" for customService. Expected one of: ${VALID_SERVICE_EXTENSIONS.join(", ")}`);
|
|
3101
|
-
cachedBundlerService =
|
|
3231
|
+
cachedBundlerService = fallback();
|
|
3102
3232
|
return cachedBundlerService;
|
|
3103
3233
|
}
|
|
3104
3234
|
try {
|
|
@@ -3130,7 +3260,7 @@ async function getBundlerServiceFromConfig() {
|
|
|
3130
3260
|
const message = error instanceof Error ? error.message : String(error);
|
|
3131
3261
|
log.error(`[BundlerServiceFactory] Failed to load custom bundler service: ${message}`);
|
|
3132
3262
|
log.info("[BundlerServiceFactory] Falling back to default ViteReactBundlerService");
|
|
3133
|
-
cachedBundlerService =
|
|
3263
|
+
cachedBundlerService = fallback();
|
|
3134
3264
|
return cachedBundlerService;
|
|
3135
3265
|
}
|
|
3136
3266
|
}
|
|
@@ -3175,8 +3305,8 @@ var ThemeService = class {
|
|
|
3175
3305
|
* Creates a new ThemeService instance
|
|
3176
3306
|
* @param config - Design system configuration
|
|
3177
3307
|
*/
|
|
3178
|
-
constructor(config) {
|
|
3179
|
-
this.monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
3308
|
+
constructor(config, workspaceRoot) {
|
|
3309
|
+
this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
3180
3310
|
this.config = config;
|
|
3181
3311
|
if (this.config.themeProvider) log.info(`[ThemeService] Using theme provider: ${this.config.themeProvider}`);
|
|
3182
3312
|
log.info(`[ThemeService] Design system type: ${this.config.type}`);
|
|
@@ -3339,7 +3469,8 @@ export { WrappedComponent };
|
|
|
3339
3469
|
*/
|
|
3340
3470
|
const DEFAULT_RENDER_WIDTH = 1280;
|
|
3341
3471
|
const DEFAULT_RENDER_HEIGHT = 800;
|
|
3342
|
-
|
|
3472
|
+
/** Settle time after the readiness check in takeScreenshot (mount, fonts, two frames). */
|
|
3473
|
+
const SCREENSHOT_WAIT_TIME_MS = 100;
|
|
3343
3474
|
const DEFAULT_CLEANUP_AGE_MS = 36e5;
|
|
3344
3475
|
const COMPONENT_FILE_PREFIX = "component-";
|
|
3345
3476
|
const FILE_URL_PREFIX = "file://";
|
|
@@ -3379,6 +3510,9 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3379
3510
|
themeService;
|
|
3380
3511
|
appPath;
|
|
3381
3512
|
bundlerFactory;
|
|
3513
|
+
log;
|
|
3514
|
+
preserveOutput;
|
|
3515
|
+
sharedBrowser;
|
|
3382
3516
|
/**
|
|
3383
3517
|
* Creates a new ComponentRendererService instance
|
|
3384
3518
|
* @param designSystemConfig - Design system configuration
|
|
@@ -3388,8 +3522,12 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3388
3522
|
constructor(designSystemConfig, appPath, options = {}) {
|
|
3389
3523
|
if (!appPath) throw new Error("appPath is required for ComponentRendererService");
|
|
3390
3524
|
this.appPath = appPath;
|
|
3391
|
-
this.
|
|
3392
|
-
this.
|
|
3525
|
+
this.sharedBrowser = options.sharedBrowser;
|
|
3526
|
+
this.tmpDir = options.temporaryDirectory ?? path.join(os.tmpdir(), "style-system");
|
|
3527
|
+
this.preserveOutput = options.temporaryDirectory !== void 0;
|
|
3528
|
+
if (options.temporaryDirectory !== void 0 && !path.isAbsolute(options.temporaryDirectory)) throw new Error("Renderer temporary directory must be absolute.");
|
|
3529
|
+
this.log = log;
|
|
3530
|
+
this.themeService = new ThemeService(designSystemConfig, options.workspaceRoot);
|
|
3393
3531
|
if (options.bundlerFactory) this.bundlerFactory = options.bundlerFactory;
|
|
3394
3532
|
else if (designSystemConfig.bundler) this.bundlerFactory = () => getBundlerService(designSystemConfig.bundler);
|
|
3395
3533
|
else this.bundlerFactory = createDefaultBundlerService;
|
|
@@ -3412,17 +3550,25 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3412
3550
|
async renderComponent(componentInfo, options = {}) {
|
|
3413
3551
|
const { storyName = componentInfo.stories[0] || "Default", args = {}, darkMode = false, width = DEFAULT_RENDER_WIDTH, height = DEFAULT_RENDER_HEIGHT } = options;
|
|
3414
3552
|
try {
|
|
3415
|
-
|
|
3416
|
-
|
|
3553
|
+
await promises.mkdir(this.tmpDir, {
|
|
3554
|
+
recursive: true,
|
|
3555
|
+
mode: 448
|
|
3556
|
+
});
|
|
3557
|
+
if (this.preserveOutput) {
|
|
3558
|
+
const info = await promises.lstat(this.tmpDir);
|
|
3559
|
+
if (!info.isDirectory() || info.isSymbolicLink() || await promises.realpath(this.tmpDir) !== this.tmpDir) throw new Error("Unsafe renderer temporary directory.");
|
|
3560
|
+
if (this.log === log) this.log = createLog(this.tmpDir);
|
|
3561
|
+
}
|
|
3562
|
+
this.log.info(`[ComponentRendererService] Rendering ${componentInfo.title} - ${storyName}`);
|
|
3417
3563
|
const designSystemConfig = this.themeService.getConfig();
|
|
3418
|
-
log.info(`[ComponentRendererService] Using theme provider: ${designSystemConfig.themeProvider}`);
|
|
3419
|
-
log.info(`[ComponentRendererService] Design system type: ${designSystemConfig.type}`);
|
|
3420
|
-
if (!await this.themeService.validateThemeProvider()) log.warn(`[ComponentRendererService] Theme provider path may not exist: ${designSystemConfig.themeProvider}`);
|
|
3564
|
+
this.log.info(`[ComponentRendererService] Using theme provider: ${designSystemConfig.themeProvider}`);
|
|
3565
|
+
this.log.info(`[ComponentRendererService] Design system type: ${designSystemConfig.type}`);
|
|
3566
|
+
if (!await this.themeService.validateThemeProvider()) this.log.warn(`[ComponentRendererService] Theme provider path may not exist: ${designSystemConfig.themeProvider}`);
|
|
3421
3567
|
const bundlerService = this.getBundlerService();
|
|
3422
3568
|
let componentUrl;
|
|
3423
3569
|
let htmlFilePath;
|
|
3424
3570
|
if (bundlerService.isServerRunning()) {
|
|
3425
|
-
log.info("[ComponentRendererService] Using dev server for fast rendering");
|
|
3571
|
+
this.log.info("[ComponentRendererService] Using dev server for fast rendering");
|
|
3426
3572
|
try {
|
|
3427
3573
|
const result = await bundlerService.serveComponent({
|
|
3428
3574
|
componentPath: componentInfo.filePath,
|
|
@@ -3437,9 +3583,9 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3437
3583
|
});
|
|
3438
3584
|
componentUrl = result.url;
|
|
3439
3585
|
htmlFilePath = result.htmlFilePath;
|
|
3440
|
-
log.info(`[ComponentRendererService] Component served at: ${componentUrl}`);
|
|
3586
|
+
this.log.info(`[ComponentRendererService] Component served at: ${componentUrl}`);
|
|
3441
3587
|
} catch (devServerError) {
|
|
3442
|
-
log.warn(`[ComponentRendererService] Dev server failed, falling back to static build: ${devServerError instanceof Error ? devServerError.message : String(devServerError)}`);
|
|
3588
|
+
this.log.warn(`[ComponentRendererService] Dev server failed, falling back to static build: ${devServerError instanceof Error ? devServerError.message : String(devServerError)}`);
|
|
3443
3589
|
const result = await bundlerService.prerenderComponent({
|
|
3444
3590
|
componentPath: componentInfo.filePath,
|
|
3445
3591
|
storyName,
|
|
@@ -3453,10 +3599,10 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3453
3599
|
});
|
|
3454
3600
|
componentUrl = `${FILE_URL_PREFIX}${result.htmlFilePath}`;
|
|
3455
3601
|
htmlFilePath = result.htmlFilePath;
|
|
3456
|
-
log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3602
|
+
this.log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3457
3603
|
}
|
|
3458
3604
|
} else {
|
|
3459
|
-
log.info("[ComponentRendererService] No dev server running, using static build");
|
|
3605
|
+
this.log.info("[ComponentRendererService] No dev server running, using static build");
|
|
3460
3606
|
const result = await bundlerService.prerenderComponent({
|
|
3461
3607
|
componentPath: componentInfo.filePath,
|
|
3462
3608
|
storyName,
|
|
@@ -3470,7 +3616,7 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3470
3616
|
});
|
|
3471
3617
|
componentUrl = `${FILE_URL_PREFIX}${result.htmlFilePath}`;
|
|
3472
3618
|
htmlFilePath = result.htmlFilePath;
|
|
3473
|
-
log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3619
|
+
this.log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3474
3620
|
}
|
|
3475
3621
|
const timestamp = Date.now();
|
|
3476
3622
|
const imageName = `${COMPONENT_FILE_PREFIX}${componentInfo.title.split("/").pop()}-${timestamp}-${randomUUID()}.png`;
|
|
@@ -3480,20 +3626,21 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3480
3626
|
output: imagePath,
|
|
3481
3627
|
width,
|
|
3482
3628
|
height,
|
|
3483
|
-
fullPage:
|
|
3629
|
+
fullPage: false,
|
|
3484
3630
|
browser: "chromium",
|
|
3485
3631
|
waitTime: SCREENSHOT_WAIT_TIME_MS,
|
|
3486
3632
|
darkMode,
|
|
3487
3633
|
mobile: false,
|
|
3488
3634
|
generateThumbnail: false,
|
|
3489
|
-
base64: false
|
|
3635
|
+
base64: false,
|
|
3636
|
+
sharedBrowser: this.sharedBrowser
|
|
3490
3637
|
});
|
|
3491
|
-
log.info(`[ComponentRendererService] Screenshot saved to: ${imagePath}`);
|
|
3638
|
+
this.log.info(`[ComponentRendererService] Screenshot saved to: ${imagePath}`);
|
|
3492
3639
|
let html = "";
|
|
3493
3640
|
if (htmlFilePath) {
|
|
3494
3641
|
html = await promises.readFile(htmlFilePath, "utf-8");
|
|
3495
|
-
log.info(`[ComponentRendererService] HTML file kept at: ${htmlFilePath}`);
|
|
3496
|
-
} else log.warn("[ComponentRendererService] No HTML file path available, returning empty HTML content");
|
|
3642
|
+
this.log.info(`[ComponentRendererService] HTML file kept at: ${htmlFilePath}`);
|
|
3643
|
+
} else this.log.warn("[ComponentRendererService] No HTML file path available, returning empty HTML content");
|
|
3497
3644
|
return {
|
|
3498
3645
|
imagePath,
|
|
3499
3646
|
html,
|
|
@@ -3536,20 +3683,20 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3536
3683
|
};
|
|
3537
3684
|
}));
|
|
3538
3685
|
for (const result of statResults) if (result.status === "fulfilled") componentFiles.push(result.value);
|
|
3539
|
-
else log.warn("[ComponentRendererService] Failed to inspect component file during cleanup:", result.reason);
|
|
3686
|
+
else this.log.warn("[ComponentRendererService] Failed to inspect component file during cleanup:", result.reason);
|
|
3540
3687
|
const oldFiles = componentFiles.filter((f) => now - f.mtime > olderThanMs);
|
|
3541
|
-
await Promise.all(oldFiles.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3688
|
+
await Promise.all(oldFiles.map((file) => promises.unlink(file.path).catch((err) => this.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3542
3689
|
let deletedCount = oldFiles.length;
|
|
3543
3690
|
const remainingFiles = componentFiles.filter((f) => now - f.mtime <= olderThanMs);
|
|
3544
3691
|
if (remainingFiles.length > keepCount) {
|
|
3545
3692
|
remainingFiles.sort((a, b) => a.mtime - b.mtime);
|
|
3546
3693
|
const toDelete = remainingFiles.slice(0, remainingFiles.length - keepCount);
|
|
3547
|
-
await Promise.all(toDelete.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3694
|
+
await Promise.all(toDelete.map((file) => promises.unlink(file.path).catch((err) => this.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3548
3695
|
deletedCount += toDelete.length;
|
|
3549
3696
|
}
|
|
3550
|
-
if (deletedCount > 0) log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
|
|
3697
|
+
if (deletedCount > 0) this.log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
|
|
3551
3698
|
} catch (error) {
|
|
3552
|
-
log.error("[ComponentRendererService] Cleanup error:", error);
|
|
3699
|
+
this.log.error("[ComponentRendererService] Cleanup error:", error);
|
|
3553
3700
|
}
|
|
3554
3701
|
}
|
|
3555
3702
|
/**
|
|
@@ -3559,11 +3706,11 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3559
3706
|
*/
|
|
3560
3707
|
async dispose() {
|
|
3561
3708
|
try {
|
|
3562
|
-
await this.cleanup(void 0, ComponentRendererService.DISPOSE_KEEP_COUNT);
|
|
3709
|
+
if (!this.preserveOutput) await this.cleanup(void 0, ComponentRendererService.DISPOSE_KEEP_COUNT);
|
|
3563
3710
|
const bundlerService = this.getBundlerService();
|
|
3564
3711
|
if (!bundlerService.isServerRunning()) await bundlerService.cleanup();
|
|
3565
3712
|
} catch (error) {
|
|
3566
|
-
log.error("[ComponentRendererService] Dispose error:", error);
|
|
3713
|
+
this.log.error("[ComponentRendererService] Dispose error:", error);
|
|
3567
3714
|
}
|
|
3568
3715
|
}
|
|
3569
3716
|
};
|
|
@@ -3579,13 +3726,6 @@ const DEFAULT_GET_UI_COMPONENT_CONFIG = {
|
|
|
3579
3726
|
defaultHeight: 800
|
|
3580
3727
|
};
|
|
3581
3728
|
//#endregion
|
|
3582
|
-
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
|
|
3583
|
-
function __decorateParam(paramIndex, decorator) {
|
|
3584
|
-
return function(target, key) {
|
|
3585
|
-
decorator(target, key, paramIndex);
|
|
3586
|
-
};
|
|
3587
|
-
}
|
|
3588
|
-
//#endregion
|
|
3589
3729
|
//#region src/services/GetUiComponentService/GetUiComponentService.ts
|
|
3590
3730
|
/**
|
|
3591
3731
|
* GetUiComponentService
|
|
@@ -3610,6 +3750,7 @@ const NATIVE_DEFAULT_VIEWPORT = {
|
|
|
3610
3750
|
height: 852
|
|
3611
3751
|
};
|
|
3612
3752
|
let GetUiComponentService = class GetUiComponentService {
|
|
3753
|
+
configLoader;
|
|
3613
3754
|
config;
|
|
3614
3755
|
storiesIndexFactory;
|
|
3615
3756
|
rendererFactory;
|
|
@@ -3619,7 +3760,8 @@ let GetUiComponentService = class GetUiComponentService {
|
|
|
3619
3760
|
* @param storiesIndexFactory - Factory for creating StoriesIndexService (for DI/testing)
|
|
3620
3761
|
* @param rendererFactory - Factory for creating ComponentRendererService (for DI/testing)
|
|
3621
3762
|
*/
|
|
3622
|
-
constructor(config = {}, storiesIndexFactory = () => new StoriesIndexService(), rendererFactory = (c, p) => new ComponentRendererService(c, p)) {
|
|
3763
|
+
constructor(config = {}, storiesIndexFactory = () => new StoriesIndexService(), rendererFactory = (c, p) => new ComponentRendererService(c, p), configLoader) {
|
|
3764
|
+
this.configLoader = configLoader;
|
|
3623
3765
|
this.config = {
|
|
3624
3766
|
...DEFAULT_GET_UI_COMPONENT_CONFIG,
|
|
3625
3767
|
...config
|
|
@@ -3651,7 +3793,7 @@ let GetUiComponentService = class GetUiComponentService {
|
|
|
3651
3793
|
const storiesIndex = this.storiesIndexFactory();
|
|
3652
3794
|
let designSystemConfig;
|
|
3653
3795
|
try {
|
|
3654
|
-
[, designSystemConfig] = await Promise.all([storiesIndex.initialize(), getAppDesignSystemConfig(appPath)]);
|
|
3796
|
+
[, designSystemConfig] = await Promise.all([storiesIndex.initialize(), this.configLoader ? this.configLoader.resolveProject(appPath).then((resolved) => resolved.config) : getAppDesignSystemConfig(appPath)]);
|
|
3655
3797
|
} catch (error) {
|
|
3656
3798
|
throw new Error(`Failed to initialize stories index or design system config: ${error instanceof Error ? error.message : String(error)}`);
|
|
3657
3799
|
}
|
|
@@ -3751,10 +3893,13 @@ GetUiComponentService = __decorate([
|
|
|
3751
3893
|
__decorateParam(1, optional()),
|
|
3752
3894
|
__decorateParam(2, inject(STYLE_SYSTEM_TYPES.RendererFactory)),
|
|
3753
3895
|
__decorateParam(2, optional()),
|
|
3896
|
+
__decorateParam(3, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
3897
|
+
__decorateParam(3, optional()),
|
|
3754
3898
|
__decorateMetadata("design:paramtypes", [
|
|
3755
3899
|
Object,
|
|
3756
3900
|
Object,
|
|
3757
|
-
Object
|
|
3901
|
+
Object,
|
|
3902
|
+
typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader
|
|
3758
3903
|
])
|
|
3759
3904
|
], GetUiComponentService);
|
|
3760
3905
|
//#endregion
|
|
@@ -3783,12 +3928,14 @@ const DEFAULT_APP_COMPONENTS_CONFIG = { pageSize: 50 };
|
|
|
3783
3928
|
* - Direct tool implementation (services should be tool-agnostic)
|
|
3784
3929
|
*/
|
|
3785
3930
|
let AppComponentsService = class AppComponentsService {
|
|
3931
|
+
workspaceRoot;
|
|
3786
3932
|
config;
|
|
3787
3933
|
/**
|
|
3788
3934
|
* Creates a new AppComponentsService instance.
|
|
3789
3935
|
* @param config - Service configuration options
|
|
3790
3936
|
*/
|
|
3791
|
-
constructor(config = {}) {
|
|
3937
|
+
constructor(config = {}, workspaceRoot) {
|
|
3938
|
+
this.workspaceRoot = workspaceRoot;
|
|
3792
3939
|
this.config = {
|
|
3793
3940
|
...DEFAULT_APP_COMPONENTS_CONFIG,
|
|
3794
3941
|
...config
|
|
@@ -3805,14 +3952,14 @@ let AppComponentsService = class AppComponentsService {
|
|
|
3805
3952
|
if (input.cursor !== void 0 && typeof input.cursor !== "string") throw new Error("cursor must be a string");
|
|
3806
3953
|
const { appPath, cursor } = input;
|
|
3807
3954
|
const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
|
|
3808
|
-
const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
3955
|
+
const monorepoRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
3809
3956
|
const resolvedAppPath = path.isAbsolute(appPath) ? appPath : path.join(monorepoRoot, appPath);
|
|
3810
3957
|
try {
|
|
3811
3958
|
await promises.access(resolvedAppPath);
|
|
3812
3959
|
} catch {
|
|
3813
3960
|
throw new Error(`App path does not exist: ${resolvedAppPath}`);
|
|
3814
3961
|
}
|
|
3815
|
-
const storiesIndex = new StoriesIndexService();
|
|
3962
|
+
const storiesIndex = new StoriesIndexService({ workspaceRoot: monorepoRoot });
|
|
3816
3963
|
const [appName, workspaceDependencies, packageMap] = await Promise.all([
|
|
3817
3964
|
this.getAppName(resolvedAppPath),
|
|
3818
3965
|
this.getWorkspaceDependencies(resolvedAppPath),
|
|
@@ -4015,18 +4162,42 @@ AppComponentsService = __decorate([
|
|
|
4015
4162
|
injectable(),
|
|
4016
4163
|
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.AppComponentsServiceConfig)),
|
|
4017
4164
|
__decorateParam(0, optional()),
|
|
4018
|
-
|
|
4165
|
+
__decorateParam(1, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
4166
|
+
__decorateParam(1, optional()),
|
|
4167
|
+
__decorateMetadata("design:paramtypes", [Object, String])
|
|
4019
4168
|
], AppComponentsService);
|
|
4020
4169
|
//#endregion
|
|
4021
4170
|
//#region src/tools/ListThemesTool.ts
|
|
4171
|
+
/**
|
|
4172
|
+
* ListThemesTool
|
|
4173
|
+
*
|
|
4174
|
+
* DESIGN PATTERNS:
|
|
4175
|
+
* - Tool pattern with getDefinition() and execute() methods
|
|
4176
|
+
* - Factory pattern for service creation
|
|
4177
|
+
* - JSON Schema validation for inputs
|
|
4178
|
+
*
|
|
4179
|
+
* CODING STANDARDS:
|
|
4180
|
+
* - Implement Tool interface from ../types
|
|
4181
|
+
* - Use TOOL_NAME constant with snake_case (e.g., 'list_themes')
|
|
4182
|
+
* - Return CallToolResult with content array
|
|
4183
|
+
* - Handle errors with isError flag
|
|
4184
|
+
* - Delegate complex logic to services
|
|
4185
|
+
*
|
|
4186
|
+
* AVOID:
|
|
4187
|
+
* - Complex business logic in execute method
|
|
4188
|
+
* - Unhandled promise rejections
|
|
4189
|
+
* - Missing input validation
|
|
4190
|
+
*/
|
|
4022
4191
|
var _ListThemesTool;
|
|
4023
4192
|
let ListThemesTool = class ListThemesTool {
|
|
4024
4193
|
static {
|
|
4025
4194
|
_ListThemesTool = this;
|
|
4026
4195
|
}
|
|
4196
|
+
configLoader;
|
|
4027
4197
|
static TOOL_NAME = "list_themes";
|
|
4028
4198
|
serviceFactory;
|
|
4029
|
-
constructor(serviceFactory) {
|
|
4199
|
+
constructor(serviceFactory, configLoader) {
|
|
4200
|
+
this.configLoader = configLoader;
|
|
4030
4201
|
this.serviceFactory = serviceFactory;
|
|
4031
4202
|
}
|
|
4032
4203
|
getDefinition() {
|
|
@@ -4048,12 +4219,13 @@ let ListThemesTool = class ListThemesTool {
|
|
|
4048
4219
|
let themePath;
|
|
4049
4220
|
let cssFiles;
|
|
4050
4221
|
if (input.appPath) {
|
|
4051
|
-
const appConfig = await getAppDesignSystemConfig(input.appPath);
|
|
4222
|
+
const appConfig = this.configLoader ? (await this.configLoader.resolveProject(input.appPath)).config : await getAppDesignSystemConfig(input.appPath);
|
|
4052
4223
|
themePath = appConfig.themePath ? await resolveStyleSystemThemePath({
|
|
4053
4224
|
appPath: input.appPath,
|
|
4054
|
-
themePath: appConfig.themePath
|
|
4225
|
+
themePath: appConfig.themePath,
|
|
4226
|
+
workspaceRoot: this.configLoader?.workspaceRoot
|
|
4055
4227
|
}) : void 0;
|
|
4056
|
-
cssFiles = themePath ? void 0 : appConfig.cssFiles;
|
|
4228
|
+
cssFiles = themePath ? void 0 : appConfig.cssFiles?.map((file) => path.resolve(this.configLoader?.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(), input.appPath, file));
|
|
4057
4229
|
}
|
|
4058
4230
|
const result = await (await this.serviceFactory.createService({
|
|
4059
4231
|
themePath,
|
|
@@ -4077,7 +4249,9 @@ let ListThemesTool = class ListThemesTool {
|
|
|
4077
4249
|
ListThemesTool = _ListThemesTool = __decorate([
|
|
4078
4250
|
injectable(),
|
|
4079
4251
|
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.ThemeServiceFactory)),
|
|
4080
|
-
|
|
4252
|
+
__decorateParam(1, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
4253
|
+
__decorateParam(1, optional()),
|
|
4254
|
+
__decorateMetadata("design:paramtypes", [typeof ThemeServiceFactory === "undefined" ? Object : ThemeServiceFactory, typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader])
|
|
4081
4255
|
], ListThemesTool);
|
|
4082
4256
|
//#endregion
|
|
4083
4257
|
//#region src/tools/GetCSSClassesTool.ts
|
|
@@ -4104,6 +4278,7 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4104
4278
|
static {
|
|
4105
4279
|
_GetCSSClassesTool = this;
|
|
4106
4280
|
}
|
|
4281
|
+
configLoader;
|
|
4107
4282
|
static TOOL_NAME = "get_css_classes";
|
|
4108
4283
|
static CSS_REUSE_INSTRUCTION = "IMPORTANT: Always reuse these existing CSS classes from the theme as much as possible instead of creating custom styles. This ensures design consistency and reduces CSS bloat.";
|
|
4109
4284
|
serviceFactory;
|
|
@@ -4113,7 +4288,8 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4113
4288
|
* @param serviceFactory - Injected CSSClassesServiceFactory
|
|
4114
4289
|
* @param defaultThemePath - Default path to theme CSS file (relative to workspace root)
|
|
4115
4290
|
*/
|
|
4116
|
-
constructor(serviceFactory, defaultThemePath) {
|
|
4291
|
+
constructor(serviceFactory, defaultThemePath, configLoader) {
|
|
4292
|
+
this.configLoader = configLoader;
|
|
4117
4293
|
this.serviceFactory = serviceFactory;
|
|
4118
4294
|
this.defaultThemePath = defaultThemePath;
|
|
4119
4295
|
}
|
|
@@ -4158,7 +4334,7 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4158
4334
|
const category = input.category || "all";
|
|
4159
4335
|
if (!isValidCategory(category)) throw new Error(`Invalid category: '${category}'. Must be one of: ${VALID_CATEGORIES.join(", ")}`);
|
|
4160
4336
|
const themePath = await this.resolveThemePath(input.appPath);
|
|
4161
|
-
const workspaceConfig = await getGetCssClassesConfig();
|
|
4337
|
+
const workspaceConfig = this.configLoader ? (await this.configLoader.loadRoot()).getCssClasses : await getGetCssClassesConfig();
|
|
4162
4338
|
const result = await (await this.serviceFactory.createService({ customServicePath: workspaceConfig?.customService })).extractClasses(category, themePath);
|
|
4163
4339
|
return { content: [{
|
|
4164
4340
|
type: "text",
|
|
@@ -4187,13 +4363,17 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4187
4363
|
*/
|
|
4188
4364
|
async resolveThemePath(appPath) {
|
|
4189
4365
|
if (appPath) {
|
|
4190
|
-
const config = await getAppDesignSystemConfig(appPath);
|
|
4366
|
+
const config = this.configLoader ? (await this.configLoader.resolveProject(appPath)).config : await getAppDesignSystemConfig(appPath);
|
|
4191
4367
|
if (config.themePath) return await resolveStyleSystemThemePath({
|
|
4192
4368
|
appPath,
|
|
4193
|
-
themePath: config.themePath
|
|
4369
|
+
themePath: config.themePath,
|
|
4370
|
+
workspaceRoot: this.configLoader?.workspaceRoot
|
|
4194
4371
|
});
|
|
4195
4372
|
}
|
|
4196
|
-
if (this.defaultThemePath) return await resolveStyleSystemThemePath({
|
|
4373
|
+
if (this.defaultThemePath) return await resolveStyleSystemThemePath({
|
|
4374
|
+
themePath: this.defaultThemePath,
|
|
4375
|
+
workspaceRoot: this.configLoader?.workspaceRoot
|
|
4376
|
+
});
|
|
4197
4377
|
throw new Error("No theme CSS path configured. Pass --theme-path, or set themePath in the target app style-system.config.yaml.");
|
|
4198
4378
|
}
|
|
4199
4379
|
};
|
|
@@ -4202,156 +4382,15 @@ GetCSSClassesTool = _GetCSSClassesTool = __decorate([
|
|
|
4202
4382
|
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory)),
|
|
4203
4383
|
__decorateParam(1, inject(STYLE_SYSTEM_TYPES.DefaultThemePath)),
|
|
4204
4384
|
__decorateParam(1, optional()),
|
|
4205
|
-
|
|
4385
|
+
__decorateParam(2, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
4386
|
+
__decorateParam(2, optional()),
|
|
4387
|
+
__decorateMetadata("design:paramtypes", [
|
|
4388
|
+
typeof CSSClassesServiceFactory === "undefined" ? Object : CSSClassesServiceFactory,
|
|
4389
|
+
String,
|
|
4390
|
+
typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader
|
|
4391
|
+
])
|
|
4206
4392
|
], GetCSSClassesTool);
|
|
4207
4393
|
//#endregion
|
|
4208
|
-
//#region src/services/AntigravityCliService.ts
|
|
4209
|
-
const DEFAULT_MODEL = "gemini-3.6-flash-high";
|
|
4210
|
-
const DEFAULT_TIMEOUT = 18e5;
|
|
4211
|
-
const MAX_BUFFER = 104857600;
|
|
4212
|
-
const FAILURE_SNIPPET_LENGTH = 400;
|
|
4213
|
-
const ANTIGRAVITY_CLI_COMMAND = "agy";
|
|
4214
|
-
const ANTIGRAVITY_PROVIDER_NAME = "antigravity";
|
|
4215
|
-
const ANTIGRAVITY_UNAVAILABLE_MESSAGE = "Antigravity CLI not found. Run `agy install` to configure it.";
|
|
4216
|
-
function sanitizePrompt(raw) {
|
|
4217
|
-
return raw.replace(/\u0000/g, "").replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/g, "").replace(/(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "").replace(/[\u0001-\u0008\u000B\u000C\u000E-\u001F\u007F\u0080-\u009F\uFEFF]/g, "");
|
|
4218
|
-
}
|
|
4219
|
-
function tailSnippet(value) {
|
|
4220
|
-
const trimmed = value.trim();
|
|
4221
|
-
if (!trimmed) return "";
|
|
4222
|
-
return trimmed.length <= FAILURE_SNIPPET_LENGTH ? trimmed : `...${trimmed.slice(-400)}`;
|
|
4223
|
-
}
|
|
4224
|
-
var AntigravityCliService = class {
|
|
4225
|
-
isAvailable() {
|
|
4226
|
-
try {
|
|
4227
|
-
execSync(`${ANTIGRAVITY_CLI_COMMAND} --version`, {
|
|
4228
|
-
stdio: "ignore",
|
|
4229
|
-
timeout: 5e3,
|
|
4230
|
-
env: process.env
|
|
4231
|
-
});
|
|
4232
|
-
return true;
|
|
4233
|
-
} catch {
|
|
4234
|
-
return false;
|
|
4235
|
-
}
|
|
4236
|
-
}
|
|
4237
|
-
async edit(params) {
|
|
4238
|
-
if (!this.isAvailable()) throw new Error(ANTIGRAVITY_UNAVAILABLE_MESSAGE);
|
|
4239
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
4240
|
-
const model = params.model ?? DEFAULT_MODEL;
|
|
4241
|
-
const timeout = params.timeout ?? DEFAULT_TIMEOUT;
|
|
4242
|
-
let fullPrompt = sanitizePrompt(params.prompt);
|
|
4243
|
-
if (params.systemPrompt) fullPrompt = `${sanitizePrompt(params.systemPrompt)}\n\n${fullPrompt}`;
|
|
4244
|
-
if (params.screenshotPaths?.length) fullPrompt = `${params.screenshotPaths.map((path) => `@${basename(path)}`).join(" ")}\n\n${fullPrompt}`;
|
|
4245
|
-
const args = [
|
|
4246
|
-
"--new-project",
|
|
4247
|
-
"--dangerously-skip-permissions",
|
|
4248
|
-
"--print",
|
|
4249
|
-
"--print-timeout",
|
|
4250
|
-
`${Math.ceil(timeout / 1e3)}s`,
|
|
4251
|
-
"--model",
|
|
4252
|
-
model,
|
|
4253
|
-
fullPrompt
|
|
4254
|
-
];
|
|
4255
|
-
return {
|
|
4256
|
-
content: (await this.execAntigravity(args, workspaceRoot, model, timeout)).trim(),
|
|
4257
|
-
model
|
|
4258
|
-
};
|
|
4259
|
-
}
|
|
4260
|
-
execAntigravity(args, cwd, model, timeout) {
|
|
4261
|
-
return new Promise((resolve, reject) => {
|
|
4262
|
-
const chunks = [];
|
|
4263
|
-
let totalBytes = 0;
|
|
4264
|
-
let settled = false;
|
|
4265
|
-
let stderrContent = "";
|
|
4266
|
-
const registeredArgs = [...args];
|
|
4267
|
-
registeredArgs[registeredArgs.length - 1] = "<prompt>";
|
|
4268
|
-
const child = spawn(ANTIGRAVITY_CLI_COMMAND, args, {
|
|
4269
|
-
stdio: [
|
|
4270
|
-
"ignore",
|
|
4271
|
-
"pipe",
|
|
4272
|
-
"pipe"
|
|
4273
|
-
],
|
|
4274
|
-
env: {
|
|
4275
|
-
...process.env,
|
|
4276
|
-
OTEL_SERVICE_NAME: ANTIGRAVITY_PROVIDER_NAME
|
|
4277
|
-
},
|
|
4278
|
-
cwd
|
|
4279
|
-
});
|
|
4280
|
-
let releaseChild;
|
|
4281
|
-
let registrationPromise;
|
|
4282
|
-
if (child.pid) registrationPromise = registerManagedProcess({
|
|
4283
|
-
repositoryPath: cwd,
|
|
4284
|
-
serviceName: `style-system-antigravity-${child.pid}`,
|
|
4285
|
-
serviceType: "tool",
|
|
4286
|
-
environment: process.env["NODE_ENV"] ?? "development",
|
|
4287
|
-
pid: child.pid,
|
|
4288
|
-
command: ANTIGRAVITY_CLI_COMMAND,
|
|
4289
|
-
args: registeredArgs,
|
|
4290
|
-
metadata: {
|
|
4291
|
-
model,
|
|
4292
|
-
parentPid: process.pid
|
|
4293
|
-
},
|
|
4294
|
-
killOnRelease: true,
|
|
4295
|
-
releasePortOnRelease: false
|
|
4296
|
-
}).then((release) => {
|
|
4297
|
-
releaseChild = release;
|
|
4298
|
-
}).catch((error) => {
|
|
4299
|
-
log.debug(`[AntigravityCliService] Process registration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4300
|
-
});
|
|
4301
|
-
const finalize = async (callback) => {
|
|
4302
|
-
try {
|
|
4303
|
-
await registrationPromise;
|
|
4304
|
-
await releaseChild?.();
|
|
4305
|
-
} catch (error) {
|
|
4306
|
-
log.debug(`[AntigravityCliService] Process cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4307
|
-
} finally {
|
|
4308
|
-
callback();
|
|
4309
|
-
}
|
|
4310
|
-
};
|
|
4311
|
-
const timer = setTimeout(() => {
|
|
4312
|
-
if (settled) return;
|
|
4313
|
-
settled = true;
|
|
4314
|
-
child.kill("SIGTERM");
|
|
4315
|
-
finalize(() => reject(/* @__PURE__ */ new Error(`Antigravity CLI timed out after ${timeout}ms.`)));
|
|
4316
|
-
}, timeout);
|
|
4317
|
-
child.stdout.on("data", (chunk) => {
|
|
4318
|
-
totalBytes += chunk.length;
|
|
4319
|
-
if (totalBytes > MAX_BUFFER) {
|
|
4320
|
-
if (!settled) {
|
|
4321
|
-
settled = true;
|
|
4322
|
-
child.kill("SIGTERM");
|
|
4323
|
-
finalize(() => reject(/* @__PURE__ */ new Error("Antigravity CLI output exceeded maximum buffer size.")));
|
|
4324
|
-
}
|
|
4325
|
-
return;
|
|
4326
|
-
}
|
|
4327
|
-
chunks.push(chunk);
|
|
4328
|
-
});
|
|
4329
|
-
child.stderr.on("data", (chunk) => {
|
|
4330
|
-
stderrContent += chunk.toString();
|
|
4331
|
-
});
|
|
4332
|
-
child.on("error", (error) => {
|
|
4333
|
-
clearTimeout(timer);
|
|
4334
|
-
if (settled) return;
|
|
4335
|
-
settled = true;
|
|
4336
|
-
const message = error.code === "ENOENT" ? ANTIGRAVITY_UNAVAILABLE_MESSAGE : `Failed to invoke Antigravity CLI: ${error.message}`;
|
|
4337
|
-
finalize(() => reject(new Error(message)));
|
|
4338
|
-
});
|
|
4339
|
-
child.on("close", (code) => {
|
|
4340
|
-
clearTimeout(timer);
|
|
4341
|
-
if (settled) return;
|
|
4342
|
-
settled = true;
|
|
4343
|
-
const stdoutContent = Buffer.concat(chunks).toString("utf-8");
|
|
4344
|
-
if (code !== 0) {
|
|
4345
|
-
const detail = tailSnippet(stderrContent) || tailSnippet(stdoutContent) || `exit code ${code}`;
|
|
4346
|
-
finalize(() => reject(new Error(detail)));
|
|
4347
|
-
return;
|
|
4348
|
-
}
|
|
4349
|
-
finalize(() => resolve(stdoutContent));
|
|
4350
|
-
});
|
|
4351
|
-
});
|
|
4352
|
-
}
|
|
4353
|
-
};
|
|
4354
|
-
//#endregion
|
|
4355
4394
|
//#region src/tools/GetComponentVisualTool.ts
|
|
4356
4395
|
var _GetComponentVisualTool;
|
|
4357
4396
|
/**
|
|
@@ -4480,11 +4519,13 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
4480
4519
|
static {
|
|
4481
4520
|
_ListSharedComponentsTool = this;
|
|
4482
4521
|
}
|
|
4522
|
+
configLoader;
|
|
4483
4523
|
static TOOL_NAME = "list_shared_components";
|
|
4484
4524
|
static PAGE_SIZE = 50;
|
|
4485
4525
|
static COMPONENT_REUSE_INSTRUCTION = "IMPORTANT: Before creating new components, check if a similar component already exists in the list above. Always reuse existing shared components to maintain consistency and reduce code duplication.";
|
|
4486
4526
|
storiesIndexFactory;
|
|
4487
|
-
constructor(storiesIndexFactory) {
|
|
4527
|
+
constructor(storiesIndexFactory, configLoader) {
|
|
4528
|
+
this.configLoader = configLoader;
|
|
4488
4529
|
this.storiesIndexFactory = storiesIndexFactory;
|
|
4489
4530
|
}
|
|
4490
4531
|
/**
|
|
@@ -4543,7 +4584,7 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
4543
4584
|
const { cursor, tags: inputTags } = input;
|
|
4544
4585
|
const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
|
|
4545
4586
|
const storiesIndex = this.storiesIndexFactory();
|
|
4546
|
-
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
|
|
4587
|
+
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), this.configLoader ? this.configLoader.loadRoot().then((root) => root.sharedComponentTags ?? DEFAULT_SHARED_COMPONENT_TAGS) : getSharedComponentTags()]);
|
|
4547
4588
|
const availableTags = storiesIndex.getAllTags();
|
|
4548
4589
|
const filterTags = inputTags !== void 0 ? inputTags : defaultTags;
|
|
4549
4590
|
const allComponentNames = storiesIndex.getComponentsByTags(filterTags.length > 0 ? filterTags : void 0).map((component) => component.title.split("/").pop() || component.title).filter((name, index, self) => self.indexOf(name) === index).sort();
|
|
@@ -4581,7 +4622,9 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
4581
4622
|
ListSharedComponentsTool = _ListSharedComponentsTool = __decorate([
|
|
4582
4623
|
injectable(),
|
|
4583
4624
|
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.StoriesIndexFactory)),
|
|
4584
|
-
|
|
4625
|
+
__decorateParam(1, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
4626
|
+
__decorateParam(1, optional()),
|
|
4627
|
+
__decorateMetadata("design:paramtypes", [Object, typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader])
|
|
4585
4628
|
], ListSharedComponentsTool);
|
|
4586
4629
|
//#endregion
|
|
4587
4630
|
//#region src/tools/ListAppComponentsTool.ts
|
|
@@ -4654,309 +4697,31 @@ ListAppComponentsTool = _ListAppComponentsTool = __decorate([
|
|
|
4654
4697
|
__decorateMetadata("design:paramtypes", [typeof AppComponentsService === "undefined" ? Object : AppComponentsService])
|
|
4655
4698
|
], ListAppComponentsTool);
|
|
4656
4699
|
//#endregion
|
|
4657
|
-
//#region src/tools/PolishComponentTool.ts
|
|
4658
|
-
/**
|
|
4659
|
-
* PolishComponentTool
|
|
4660
|
-
*
|
|
4661
|
-
* DESIGN PATTERNS:
|
|
4662
|
-
* - Tool pattern with getDefinition() and execute() methods
|
|
4663
|
-
* - Service delegation for business logic
|
|
4664
|
-
* - JSON Schema validation for inputs
|
|
4665
|
-
*
|
|
4666
|
-
* CODING STANDARDS:
|
|
4667
|
-
* - Implement Tool interface from ../types
|
|
4668
|
-
* - Use TOOL_NAME constant with snake_case
|
|
4669
|
-
* - Return CallToolResult with content array
|
|
4670
|
-
* - Handle errors with isError flag
|
|
4671
|
-
* - Delegate complex logic to services
|
|
4672
|
-
*
|
|
4673
|
-
* AVOID:
|
|
4674
|
-
* - Complex business logic in execute method
|
|
4675
|
-
* - Unhandled promise rejections
|
|
4676
|
-
* - Missing input validation
|
|
4677
|
-
*/
|
|
4678
|
-
var _PolishComponentTool;
|
|
4679
|
-
let PolishComponentTool = class PolishComponentTool {
|
|
4680
|
-
static {
|
|
4681
|
-
_PolishComponentTool = this;
|
|
4682
|
-
}
|
|
4683
|
-
static TOOL_NAME = "polish_component";
|
|
4684
|
-
antigravityService;
|
|
4685
|
-
getUiComponentService;
|
|
4686
|
-
cssClassesServiceFactory;
|
|
4687
|
-
storiesIndexFactory;
|
|
4688
|
-
constructor(getUiComponentService, cssClassesServiceFactory, storiesIndexFactory) {
|
|
4689
|
-
this.antigravityService = new AntigravityCliService();
|
|
4690
|
-
this.getUiComponentService = getUiComponentService;
|
|
4691
|
-
this.cssClassesServiceFactory = cssClassesServiceFactory;
|
|
4692
|
-
this.storiesIndexFactory = storiesIndexFactory;
|
|
4693
|
-
}
|
|
4694
|
-
getDefinition() {
|
|
4695
|
-
return {
|
|
4696
|
-
name: _PolishComponentTool.TOOL_NAME,
|
|
4697
|
-
description: "Polish a UI component by running Antigravity to directly edit the component file. Takes a .tsx file path, finds the collocated stories file for visual context, gathers design principles, design tokens, and shared components, then runs Antigravity to edit in place.",
|
|
4698
|
-
inputSchema: {
|
|
4699
|
-
type: "object",
|
|
4700
|
-
properties: {
|
|
4701
|
-
filePath: {
|
|
4702
|
-
type: "string",
|
|
4703
|
-
minLength: 1,
|
|
4704
|
-
description: "Path to the component .tsx file to polish (e.g., \"packages/frontend/web-atoms/src/components/Button/index.tsx\")"
|
|
4705
|
-
},
|
|
4706
|
-
instructions: {
|
|
4707
|
-
type: "string",
|
|
4708
|
-
description: "Specific polishing instructions (e.g., \"improve spacing\", \"add hover states\", \"fix dark mode\")"
|
|
4709
|
-
}
|
|
4710
|
-
},
|
|
4711
|
-
required: ["filePath"],
|
|
4712
|
-
additionalProperties: false
|
|
4713
|
-
}
|
|
4714
|
-
};
|
|
4715
|
-
}
|
|
4716
|
-
async execute(input) {
|
|
4717
|
-
try {
|
|
4718
|
-
if (!this.antigravityService.isAvailable()) return {
|
|
4719
|
-
content: [{
|
|
4720
|
-
type: "text",
|
|
4721
|
-
text: "Error: Antigravity CLI is not installed. Run `agy install` to configure it."
|
|
4722
|
-
}],
|
|
4723
|
-
isError: true
|
|
4724
|
-
};
|
|
4725
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
4726
|
-
const { instructions } = input;
|
|
4727
|
-
const filePath = path.isAbsolute(input.filePath) ? input.filePath : path.join(workspaceRoot, input.filePath);
|
|
4728
|
-
const componentDir = path.dirname(filePath);
|
|
4729
|
-
const storyFile = await this.findStoriesFile(componentDir);
|
|
4730
|
-
if (!storyFile) return {
|
|
4731
|
-
content: [{
|
|
4732
|
-
type: "text",
|
|
4733
|
-
text: `Error: No .stories.tsx file found in ${componentDir}`
|
|
4734
|
-
}],
|
|
4735
|
-
isError: true
|
|
4736
|
-
};
|
|
4737
|
-
const componentName = path.basename(componentDir);
|
|
4738
|
-
const appPath = await this.findAppPath(filePath, workspaceRoot);
|
|
4739
|
-
const dsConfig = appPath ? await getAppDesignSystemConfig(appPath).catch((error) => {
|
|
4740
|
-
log.debug(`[PolishComponentTool] Could not load design system config for ${appPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4741
|
-
return null;
|
|
4742
|
-
}) : null;
|
|
4743
|
-
const platform = this.detectPlatform(dsConfig);
|
|
4744
|
-
log.info(`[PolishComponentTool] Polishing ${filePath}, stories: ${storyFile}, appPath: ${appPath}, platform: ${platform}`);
|
|
4745
|
-
const [componentVisual, designPrinciples, sharedComponents, componentSource, designTokens] = await Promise.allSettled([
|
|
4746
|
-
appPath ? this.getComponentVisual(componentName, appPath) : Promise.reject(/* @__PURE__ */ new Error("No appPath")),
|
|
4747
|
-
appPath ? this.getDesignPrinciples(appPath) : Promise.reject(/* @__PURE__ */ new Error("No appPath")),
|
|
4748
|
-
this.getSharedComponents(),
|
|
4749
|
-
promises.readFile(filePath, "utf-8"),
|
|
4750
|
-
this.getDesignTokens(platform, dsConfig, appPath)
|
|
4751
|
-
]);
|
|
4752
|
-
const contextSections = [];
|
|
4753
|
-
if (componentSource.status === "fulfilled") contextSections.push(`## Component Source\n\nFile: ${filePath}\n\n\`\`\`tsx\n${componentSource.value}\n\`\`\``);
|
|
4754
|
-
const storyContent = await promises.readFile(storyFile, "utf-8").catch((error) => {
|
|
4755
|
-
log.debug(`[PolishComponentTool] Could not read stories file ${storyFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4756
|
-
return null;
|
|
4757
|
-
});
|
|
4758
|
-
if (storyContent) contextSections.push(`## Stories File\n\nFile: ${storyFile}\n\n\`\`\`tsx\n${storyContent}\n\`\`\``);
|
|
4759
|
-
if (designPrinciples.status === "fulfilled" && designPrinciples.value.length > 0) {
|
|
4760
|
-
const principlesText = designPrinciples.value.map((p) => `### ${p.variant}\n${yaml.dump(p.content, {
|
|
4761
|
-
indent: 2,
|
|
4762
|
-
lineWidth: 120
|
|
4763
|
-
})}`).join("\n");
|
|
4764
|
-
contextSections.push(`## Design Principles\n\n${principlesText}`);
|
|
4765
|
-
}
|
|
4766
|
-
if (designTokens.status === "fulfilled") contextSections.push(designTokens.value);
|
|
4767
|
-
if (sharedComponents.status === "fulfilled") contextSections.push(`## Available Shared Components\n\nReuse these existing components where possible:\n${sharedComponents.value}`);
|
|
4768
|
-
const systemPrompt = this.buildSystemPrompt(platform);
|
|
4769
|
-
const userPrompt = [
|
|
4770
|
-
`# Polish Component`,
|
|
4771
|
-
"",
|
|
4772
|
-
instructions ? `## Instructions\n\n${instructions}\n` : "",
|
|
4773
|
-
...contextSections,
|
|
4774
|
-
"",
|
|
4775
|
-
`Edit the component file at: ${filePath}`,
|
|
4776
|
-
"Polish this component following the design system. Edit the files directly."
|
|
4777
|
-
].filter(Boolean).join("\n");
|
|
4778
|
-
const screenshotPaths = componentVisual.status === "fulfilled" ? [componentVisual.value.imagePath] : [];
|
|
4779
|
-
log.info(`[PolishComponentTool] Running Antigravity to edit component...`);
|
|
4780
|
-
const response = await this.antigravityService.edit({
|
|
4781
|
-
prompt: userPrompt,
|
|
4782
|
-
systemPrompt,
|
|
4783
|
-
screenshotPaths
|
|
4784
|
-
});
|
|
4785
|
-
log.info(`[PolishComponentTool] Antigravity finished editing (model: ${response.model})`);
|
|
4786
|
-
const reviewPrompt = [
|
|
4787
|
-
"\n## Review the changes",
|
|
4788
|
-
"",
|
|
4789
|
-
"Antigravity has edited the component files directly. Review the result with `pnpm exec style-system get-ui-component`:",
|
|
4790
|
-
` - componentName: "${componentName}"`,
|
|
4791
|
-
appPath ? ` - appPath: "${appPath}"` : "",
|
|
4792
|
-
"",
|
|
4793
|
-
`Component file: ${filePath}`,
|
|
4794
|
-
`Stories file: ${storyFile}`
|
|
4795
|
-
].filter(Boolean).join("\n");
|
|
4796
|
-
return { content: [{
|
|
4797
|
-
type: "text",
|
|
4798
|
-
text: response.content
|
|
4799
|
-
}, {
|
|
4800
|
-
type: "text",
|
|
4801
|
-
text: reviewPrompt
|
|
4802
|
-
}] };
|
|
4803
|
-
} catch (error) {
|
|
4804
|
-
return {
|
|
4805
|
-
content: [{
|
|
4806
|
-
type: "text",
|
|
4807
|
-
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
4808
|
-
}],
|
|
4809
|
-
isError: true
|
|
4810
|
-
};
|
|
4811
|
-
}
|
|
4812
|
-
}
|
|
4813
|
-
detectPlatform(config) {
|
|
4814
|
-
if (config?.bundler?.includes("native")) return "native";
|
|
4815
|
-
return "web";
|
|
4816
|
-
}
|
|
4817
|
-
buildSystemPrompt(platform) {
|
|
4818
|
-
const base = [
|
|
4819
|
-
"You are a senior frontend designer and developer.",
|
|
4820
|
-
"You polish UI components to improve their visual quality, consistency, and adherence to the design system.",
|
|
4821
|
-
"You receive the component source code, a screenshot of its current rendering, the design principles, available design tokens, and shared components.",
|
|
4822
|
-
"Your job is to directly edit the component files to improve them.",
|
|
4823
|
-
"",
|
|
4824
|
-
"CRITICAL RULES:"
|
|
4825
|
-
];
|
|
4826
|
-
if (platform === "native") base.push("- This is a React Native component using react-native-unistyles v3 for styling.", "- ONLY use theme tokens from the UnistylesTheme interface listed in the \"Unistyles Theme Tokens\" section.", "- Access tokens via StyleSheet.create((theme) => ({...})) — e.g., theme.colors.primary, theme.spacing.md, theme.borderRadius.lg, theme.fontSize.base.", "- NEVER use hardcoded color hex values, pixel numbers, or inline styles when a theme token exists.", "- If a semantic value is not available in the theme tokens, use the closest match or leave it unchanged.");
|
|
4827
|
-
else base.push("- ONLY use CSS classes listed in the \"Available CSS Classes\" section. NEVER use raw Tailwind color/spacing classes (e.g., green-500, red-600, p-4) that are not in the provided design tokens.", "- If a semantic color or spacing value is not available in the design tokens, use the closest match from the provided list or leave it unchanged. Do NOT invent new color values.");
|
|
4828
|
-
base.push("- Reuse shared components instead of creating custom markup", "- Follow the design principles strictly", "- Edit the files directly — do not just suggest changes", "- Explain what you changed after editing");
|
|
4829
|
-
if (platform === "native") base.push("", "VIOLATION EXAMPLES (DO NOT DO THIS):", "- { color: \"#22c55e\" } → hardcoded hex, use theme.colors.success instead", "- { padding: 16 } → hardcoded pixel, use theme.spacing.md instead", "- { borderRadius: 8 } → hardcoded pixel, use theme.borderRadius.lg instead");
|
|
4830
|
-
else base.push("", "VIOLATION EXAMPLES (DO NOT DO THIS):", "- bg-green-500/10 text-green-700 → raw colors, not from design tokens", "- border-red-600/30 → raw color, not from design tokens", "- Instead use semantic classes like bg-destructive, text-destructive-foreground, bg-accent, etc.");
|
|
4831
|
-
return base.join("\n");
|
|
4832
|
-
}
|
|
4833
|
-
/**
|
|
4834
|
-
* Read design tokens from config.themePath.
|
|
4835
|
-
* For web: extracts Tailwind CSS classes. For native: reads the TypeScript types file.
|
|
4836
|
-
*/
|
|
4837
|
-
async getDesignTokens(platform, config, appPath) {
|
|
4838
|
-
if (!config?.themePath) return `No themePath configured in style-system.config.yaml.`;
|
|
4839
|
-
const themePath = await resolveStyleSystemThemePath({
|
|
4840
|
-
appPath,
|
|
4841
|
-
themePath: config.themePath
|
|
4842
|
-
});
|
|
4843
|
-
if (platform === "native") return `## Unistyles Theme Tokens\n\nUse these tokens via \`StyleSheet.create((theme) => ({...}))\`:\n\n\`\`\`typescript\n${await promises.readFile(themePath, "utf-8")}\n\`\`\``;
|
|
4844
|
-
const toolkitConfig = await getGetCssClassesConfig();
|
|
4845
|
-
const result = await (await this.cssClassesServiceFactory.createService({ customServicePath: toolkitConfig?.customService })).extractClasses("all", themePath);
|
|
4846
|
-
return `## Available CSS Classes (Design Tokens)\n\n${JSON.stringify(result, null, 2)}`;
|
|
4847
|
-
}
|
|
4848
|
-
async findStoriesFile(dir) {
|
|
4849
|
-
for (const pattern of [
|
|
4850
|
-
"*.stories.tsx",
|
|
4851
|
-
"*.stories.ts",
|
|
4852
|
-
"*.stories.jsx",
|
|
4853
|
-
"*.stories.js"
|
|
4854
|
-
]) {
|
|
4855
|
-
const matches = await glob(pattern, {
|
|
4856
|
-
cwd: dir,
|
|
4857
|
-
absolute: true
|
|
4858
|
-
});
|
|
4859
|
-
if (matches.length > 0) return matches[0];
|
|
4860
|
-
}
|
|
4861
|
-
return null;
|
|
4862
|
-
}
|
|
4863
|
-
/**
|
|
4864
|
-
* Find the app that owns a file, for style-system context.
|
|
4865
|
-
* Only apps declaring their own style-system.config.yaml are eligible.
|
|
4866
|
-
*/
|
|
4867
|
-
async findAppPath(filePath, workspaceRoot) {
|
|
4868
|
-
const parts = path.relative(workspaceRoot, filePath).split(path.sep);
|
|
4869
|
-
if (parts[0] === "apps" && parts.length > 1) {
|
|
4870
|
-
const appPath = path.join(parts[0], parts[1]);
|
|
4871
|
-
if (await hasStyleSystemProjectConfig(appPath)) return appPath;
|
|
4872
|
-
log.debug(`[PolishComponentTool] ${appPath} has no style-system config`);
|
|
4873
|
-
}
|
|
4874
|
-
const appsDir = path.join(workspaceRoot, "apps");
|
|
4875
|
-
let candidates;
|
|
4876
|
-
try {
|
|
4877
|
-
candidates = (await promises.readdir(appsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory() && entry.name.endsWith("-app")).map((entry) => entry.name).sort();
|
|
4878
|
-
} catch (error) {
|
|
4879
|
-
log.debug(`[PolishComponentTool] Could not scan apps dir: ${error instanceof Error ? error.message : String(error)}`);
|
|
4880
|
-
return null;
|
|
4881
|
-
}
|
|
4882
|
-
for (const name of candidates) {
|
|
4883
|
-
const appPath = `apps/${name}`;
|
|
4884
|
-
if (await hasStyleSystemProjectConfig(appPath)) return appPath;
|
|
4885
|
-
log.debug(`[PolishComponentTool] ${appPath} has no style-system config`);
|
|
4886
|
-
}
|
|
4887
|
-
return null;
|
|
4888
|
-
}
|
|
4889
|
-
async getComponentVisual(componentName, appPath) {
|
|
4890
|
-
return { imagePath: (await this.getUiComponentService.getComponent({
|
|
4891
|
-
componentName,
|
|
4892
|
-
appPath,
|
|
4893
|
-
darkMode: true
|
|
4894
|
-
})).imagePath };
|
|
4895
|
-
}
|
|
4896
|
-
async getDesignPrinciples(appPath) {
|
|
4897
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
4898
|
-
const product = path.basename(appPath).replace(/-app$|-native-app$|-mcp-app$|-e2e$/, "");
|
|
4899
|
-
const principlesDir = path.join(workspaceRoot, "docs", "architecture", product, "design-principles");
|
|
4900
|
-
const results = [];
|
|
4901
|
-
try {
|
|
4902
|
-
const files = await promises.readdir(principlesDir);
|
|
4903
|
-
for (const file of files) {
|
|
4904
|
-
if (!file.endsWith(".yaml")) continue;
|
|
4905
|
-
try {
|
|
4906
|
-
const content = await promises.readFile(path.join(principlesDir, file), "utf-8");
|
|
4907
|
-
const parsed = yaml.load(content);
|
|
4908
|
-
const variant = file.replace(".yaml", "");
|
|
4909
|
-
results.push({
|
|
4910
|
-
variant,
|
|
4911
|
-
content: parsed
|
|
4912
|
-
});
|
|
4913
|
-
} catch (error) {
|
|
4914
|
-
log.debug(`[PolishComponentTool] Skipping unreadable design principle ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4915
|
-
}
|
|
4916
|
-
}
|
|
4917
|
-
} catch (error) {
|
|
4918
|
-
log.debug(`[PolishComponentTool] No design principles at ${principlesDir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4919
|
-
}
|
|
4920
|
-
return results;
|
|
4921
|
-
}
|
|
4922
|
-
async getSharedComponents() {
|
|
4923
|
-
const storiesIndex = this.storiesIndexFactory();
|
|
4924
|
-
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
|
|
4925
|
-
return storiesIndex.getComponentsByTags(defaultTags.length > 0 ? defaultTags : void 0).map((c) => c.title.split("/").pop() || c.title).filter((name, index, self) => self.indexOf(name) === index).sort().map((n) => `- ${n}`).join("\n");
|
|
4926
|
-
}
|
|
4927
|
-
};
|
|
4928
|
-
PolishComponentTool = _PolishComponentTool = __decorate([
|
|
4929
|
-
injectable(),
|
|
4930
|
-
__decorateParam(0, inject(STYLE_SYSTEM_TYPES.GetUiComponentService)),
|
|
4931
|
-
__decorateParam(1, inject(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory)),
|
|
4932
|
-
__decorateParam(2, inject(STYLE_SYSTEM_TYPES.StoriesIndexFactory)),
|
|
4933
|
-
__decorateMetadata("design:paramtypes", [
|
|
4934
|
-
typeof GetUiComponentService === "undefined" ? Object : GetUiComponentService,
|
|
4935
|
-
typeof CSSClassesServiceFactory === "undefined" ? Object : CSSClassesServiceFactory,
|
|
4936
|
-
Object
|
|
4937
|
-
])
|
|
4938
|
-
], PolishComponentTool);
|
|
4939
|
-
//#endregion
|
|
4940
4700
|
//#region src/container/module.ts
|
|
4941
4701
|
function createStyleSystemModule(optionsInput = {}) {
|
|
4942
4702
|
return new ContainerModule((options) => {
|
|
4703
|
+
options.bind(STYLE_SYSTEM_TYPES.WorkspaceRoot).toConstantValue(optionsInput.workspaceRoot);
|
|
4943
4704
|
options.bind(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader).to(StyleSystemConfigLoader).inSingletonScope();
|
|
4944
4705
|
options.bind(STYLE_SYSTEM_TYPES.DefaultThemePath).toConstantValue(optionsInput.defaultThemePath);
|
|
4945
4706
|
options.bind(STYLE_SYSTEM_TYPES.GetUiComponentServiceConfig).toConstantValue(DEFAULT_GET_UI_COMPONENT_CONFIG);
|
|
4946
4707
|
options.bind(STYLE_SYSTEM_TYPES.AppComponentsServiceConfig).toConstantValue(DEFAULT_APP_COMPONENTS_CONFIG);
|
|
4947
4708
|
options.bind(STYLE_SYSTEM_TYPES.ThemeServiceFactory).to(ThemeServiceFactory).inSingletonScope();
|
|
4948
4709
|
options.bind(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory).to(CSSClassesServiceFactory).inSingletonScope();
|
|
4949
|
-
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexService).
|
|
4710
|
+
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexService).toDynamicValue(() => new StoriesIndexService({ workspaceRoot: optionsInput.workspaceRoot }));
|
|
4950
4711
|
options.bind(STYLE_SYSTEM_TYPES.AppComponentsService).to(AppComponentsService).inTransientScope();
|
|
4951
4712
|
options.bind(STYLE_SYSTEM_TYPES.GetUiComponentService).to(GetUiComponentService).inSingletonScope();
|
|
4952
|
-
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexFactory).toConstantValue(() => new StoriesIndexService());
|
|
4953
|
-
options.bind(STYLE_SYSTEM_TYPES.RendererFactory).toConstantValue((config, appPath) => new ComponentRendererService(config, appPath
|
|
4713
|
+
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexFactory).toConstantValue(() => new StoriesIndexService({ workspaceRoot: optionsInput.workspaceRoot }));
|
|
4714
|
+
options.bind(STYLE_SYSTEM_TYPES.RendererFactory).toConstantValue((config, appPath) => new ComponentRendererService(config, appPath, {
|
|
4715
|
+
workspaceRoot: optionsInput.workspaceRoot,
|
|
4716
|
+
temporaryDirectory: optionsInput.temporaryDirectory,
|
|
4717
|
+
sharedBrowser: optionsInput.sharedBrowser,
|
|
4718
|
+
...optionsInput.bundlerFactory ? { bundlerFactory: () => optionsInput.bundlerFactory(config) } : {}
|
|
4719
|
+
}));
|
|
4954
4720
|
options.bind(STYLE_SYSTEM_TYPES.ListThemesTool).to(ListThemesTool).inSingletonScope();
|
|
4955
4721
|
options.bind(STYLE_SYSTEM_TYPES.GetCSSClassesTool).to(GetCSSClassesTool).inSingletonScope();
|
|
4956
4722
|
options.bind(STYLE_SYSTEM_TYPES.GetComponentVisualTool).to(GetComponentVisualTool).inSingletonScope();
|
|
4957
4723
|
options.bind(STYLE_SYSTEM_TYPES.ListSharedComponentsTool).to(ListSharedComponentsTool).inSingletonScope();
|
|
4958
4724
|
options.bind(STYLE_SYSTEM_TYPES.ListAppComponentsTool).to(ListAppComponentsTool).inSingletonScope();
|
|
4959
|
-
options.bind(STYLE_SYSTEM_TYPES.PolishComponentTool).to(PolishComponentTool).inSingletonScope();
|
|
4960
4725
|
});
|
|
4961
4726
|
}
|
|
4962
4727
|
//#endregion
|
|
@@ -4968,7 +4733,7 @@ function createContainer(options) {
|
|
|
4968
4733
|
}
|
|
4969
4734
|
//#endregion
|
|
4970
4735
|
//#region package.json
|
|
4971
|
-
var version = "0.2.14-alpha.
|
|
4736
|
+
var version = "0.2.14-alpha.21";
|
|
4972
4737
|
//#endregion
|
|
4973
4738
|
//#region src/metadata.ts
|
|
4974
4739
|
const STYLE_SYSTEM_CLI_NAME = "style-system";
|
|
@@ -5002,7 +4767,6 @@ function createServer(containerOrThemePath) {
|
|
|
5002
4767
|
const getComponentVisualTool = container.get(STYLE_SYSTEM_TYPES.GetComponentVisualTool);
|
|
5003
4768
|
const listSharedComponentsTool = container.get(STYLE_SYSTEM_TYPES.ListSharedComponentsTool);
|
|
5004
4769
|
const listAppComponentsTool = container.get(STYLE_SYSTEM_TYPES.ListAppComponentsTool);
|
|
5005
|
-
const polishComponentTool = container.get(STYLE_SYSTEM_TYPES.PolishComponentTool);
|
|
5006
4770
|
server.setRequestHandler("tools/list", async () => {
|
|
5007
4771
|
return { tools: [
|
|
5008
4772
|
withCapabilities(listThemesTool.getDefinition(), [
|
|
@@ -5030,12 +4794,6 @@ function createServer(containerOrThemePath) {
|
|
|
5030
4794
|
"components",
|
|
5031
4795
|
"app-components",
|
|
5032
4796
|
"component-discovery"
|
|
5033
|
-
]),
|
|
5034
|
-
withCapabilities(polishComponentTool.getDefinition(), [
|
|
5035
|
-
"components",
|
|
5036
|
-
"design-review",
|
|
5037
|
-
"polish",
|
|
5038
|
-
"antigravity"
|
|
5039
4797
|
])
|
|
5040
4798
|
].map((tool) => {
|
|
5041
4799
|
if (tool.inputSchema.type !== "object") throw new Error(`Invalid schema for ${tool.name}`);
|
|
@@ -5052,8 +4810,7 @@ function createServer(containerOrThemePath) {
|
|
|
5052
4810
|
getCSSClassesTool,
|
|
5053
4811
|
getComponentVisualTool,
|
|
5054
4812
|
listSharedComponentsTool,
|
|
5055
|
-
listAppComponentsTool
|
|
5056
|
-
polishComponentTool
|
|
4813
|
+
listAppComponentsTool
|
|
5057
4814
|
].find((candidate) => candidate.getDefinition().name === name);
|
|
5058
4815
|
if (!tool) return {
|
|
5059
4816
|
content: [{
|
|
@@ -5240,4 +4997,4 @@ var StdioTransportHandler = class {
|
|
|
5240
4997
|
}
|
|
5241
4998
|
};
|
|
5242
4999
|
//#endregion
|
|
5243
|
-
export {
|
|
5000
|
+
export { resolveStyleSystemThemePath as A, STYLE_SYSTEM_TYPES as B, launchBrowserWithFallback as C, resetStyleSystemConfigCache as D, hasStyleSystemProjectConfig as E, TailwindCSSClassesService as F, BaseCSSClassesService as I, TemplatesManagerService as L, StoriesIndexService as M, CSSClassesServiceFactory as N, resolveAppDesignSystemConfig as O, DEFAULT_STYLE_SYSTEM_CONFIG as P, isContained as R, BaseBundlerService as S, getSharedComponentTags as T, createDefaultBundlerService as _, STYLE_SYSTEM_CLI_NAME as a, ViteReactNativeBundlerService as b, createStyleSystemModule as c, GetComponentVisualTool as d, GetCSSClassesTool as f, ThemeService as g, ComponentRendererService as h, createServer as i, MAX_STYLE_SYSTEM_VIEWPORT_DIMENSION as j, StyleSystemConfigLoader as k, ListAppComponentsTool as l, GetUiComponentService as m, extractCustomTokenNames as n, STYLE_SYSTEM_VERSION as o, ListThemesTool as p, generateRestrictedClassesConfig as r, createContainer as s, StdioTransportHandler as t, ListSharedComponentsTool as u, getBundlerService as v, getAppDesignSystemConfig as w, ViteReactBundlerService as x, getBundlerServiceFromConfig as y, log as z };
|