@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
|
@@ -39,19 +39,36 @@ let storybook_internal_csf_tools = require("storybook/internal/csf-tools");
|
|
|
39
39
|
let glob = require("glob");
|
|
40
40
|
let zod = require("zod");
|
|
41
41
|
let playwright = require("playwright");
|
|
42
|
-
let
|
|
43
|
-
sharp = __toESM(sharp, 1);
|
|
42
|
+
let node_module = require("node:module");
|
|
44
43
|
let _tailwindcss_vite = require("@tailwindcss/vite");
|
|
45
44
|
_tailwindcss_vite = __toESM(_tailwindcss_vite, 1);
|
|
46
45
|
let vite_plugin_singlefile = require("vite-plugin-singlefile");
|
|
47
|
-
let node_module = require("node:module");
|
|
48
46
|
let node_url = require("node:url");
|
|
49
47
|
let _vitejs_plugin_react = require("@vitejs/plugin-react");
|
|
50
48
|
_vitejs_plugin_react = __toESM(_vitejs_plugin_react, 1);
|
|
51
|
-
let node_child_process = require("node:child_process");
|
|
52
|
-
let _agimon_ai_foundation_exec = require("@agimon-ai/foundation-exec");
|
|
53
49
|
let _modelcontextprotocol_server = require("@modelcontextprotocol/server");
|
|
54
50
|
let _modelcontextprotocol_server_stdio = require("@modelcontextprotocol/server/stdio");
|
|
51
|
+
//#region src/container/types.ts
|
|
52
|
+
const STYLE_SYSTEM_TYPES = {
|
|
53
|
+
ListThemesTool: Symbol.for("StyleSystem:ListThemesTool"),
|
|
54
|
+
GetCSSClassesTool: Symbol.for("StyleSystem:GetCSSClassesTool"),
|
|
55
|
+
GetComponentVisualTool: Symbol.for("StyleSystem:GetComponentVisualTool"),
|
|
56
|
+
ListSharedComponentsTool: Symbol.for("StyleSystem:ListSharedComponentsTool"),
|
|
57
|
+
ListAppComponentsTool: Symbol.for("StyleSystem:ListAppComponentsTool"),
|
|
58
|
+
ThemeServiceFactory: Symbol.for("StyleSystem:ThemeServiceFactory"),
|
|
59
|
+
CSSClassesServiceFactory: Symbol.for("StyleSystem:CSSClassesServiceFactory"),
|
|
60
|
+
StoriesIndexService: Symbol.for("StyleSystem:StoriesIndexService"),
|
|
61
|
+
GetUiComponentService: Symbol.for("StyleSystem:GetUiComponentService"),
|
|
62
|
+
AppComponentsService: Symbol.for("StyleSystem:AppComponentsService"),
|
|
63
|
+
StoriesIndexFactory: Symbol.for("StyleSystem:StoriesIndexFactory"),
|
|
64
|
+
RendererFactory: Symbol.for("StyleSystem:RendererFactory"),
|
|
65
|
+
StyleSystemConfigLoader: Symbol.for("StyleSystem:StyleSystemConfigLoader"),
|
|
66
|
+
WorkspaceRoot: Symbol.for("StyleSystem:WorkspaceRoot"),
|
|
67
|
+
DefaultThemePath: Symbol.for("StyleSystem:DefaultThemePath"),
|
|
68
|
+
GetUiComponentServiceConfig: Symbol.for("StyleSystem:GetUiComponentServiceConfig"),
|
|
69
|
+
AppComponentsServiceConfig: Symbol.for("StyleSystem:AppComponentsServiceConfig")
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
55
72
|
//#region src/services/ThemeService/BaseThemeService.ts
|
|
56
73
|
/**
|
|
57
74
|
* BaseThemeService
|
|
@@ -122,12 +139,23 @@ const logger = (0, pino.default)({
|
|
|
122
139
|
mkdir: true,
|
|
123
140
|
sync: true
|
|
124
141
|
}));
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
function createLog(directory) {
|
|
143
|
+
const target = directory === void 0 ? logger : (0, pino.default)({
|
|
144
|
+
level: "debug",
|
|
145
|
+
timestamp: pino.default.stdTimeFunctions.isoTime
|
|
146
|
+
}, pino.default.destination({
|
|
147
|
+
dest: node_path.join(directory, "style-system.log"),
|
|
148
|
+
mkdir: true,
|
|
149
|
+
sync: true
|
|
150
|
+
}));
|
|
151
|
+
return {
|
|
152
|
+
debug: (msg, ...args) => target.debug({ args }, msg),
|
|
153
|
+
info: (msg, ...args) => target.info({ args }, msg),
|
|
154
|
+
warn: (msg, ...args) => target.warn({ args }, msg),
|
|
155
|
+
error: (msg, ...args) => target.error({ args }, msg)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const log = createLog();
|
|
131
159
|
//#endregion
|
|
132
160
|
//#region src/utils/fsHelpers.ts
|
|
133
161
|
/** True when candidate is root itself or inside it (sibling prefixes like `<root>-evil` are outside). */
|
|
@@ -275,9 +303,9 @@ var CSSThemeService = class extends BaseThemeService {
|
|
|
275
303
|
* Creates a new CSSThemeService instance
|
|
276
304
|
* @param config - Theme service configuration with themePath or cssFiles
|
|
277
305
|
*/
|
|
278
|
-
constructor(config) {
|
|
306
|
+
constructor(config, workspaceRoot) {
|
|
279
307
|
super(config);
|
|
280
|
-
this.monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
308
|
+
this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
281
309
|
}
|
|
282
310
|
/**
|
|
283
311
|
* Get the source identifier for this service
|
|
@@ -393,6 +421,18 @@ const DEFAULT_THEME_SERVICE_CONFIG = {
|
|
|
393
421
|
customServicePath: void 0
|
|
394
422
|
};
|
|
395
423
|
//#endregion
|
|
424
|
+
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateMetadata.js
|
|
425
|
+
function __decorateMetadata(k, v) {
|
|
426
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
|
|
430
|
+
function __decorateParam(paramIndex, decorator) {
|
|
431
|
+
return function(target, key) {
|
|
432
|
+
decorator(target, key, paramIndex);
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
//#endregion
|
|
396
436
|
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorate.js
|
|
397
437
|
function __decorate(decorators, target, key, desc) {
|
|
398
438
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -419,6 +459,10 @@ function __decorate(decorators, target, key, desc) {
|
|
|
419
459
|
* - Hardcoded service paths
|
|
420
460
|
*/
|
|
421
461
|
let ThemeServiceFactory = class ThemeServiceFactory {
|
|
462
|
+
workspaceRoot;
|
|
463
|
+
constructor(workspaceRoot) {
|
|
464
|
+
this.workspaceRoot = workspaceRoot;
|
|
465
|
+
}
|
|
422
466
|
/**
|
|
423
467
|
* Create a theme service based on configuration.
|
|
424
468
|
*
|
|
@@ -436,7 +480,7 @@ let ThemeServiceFactory = class ThemeServiceFactory {
|
|
|
436
480
|
...config
|
|
437
481
|
};
|
|
438
482
|
if (resolvedConfig.customServicePath) return this.loadCustomService(resolvedConfig);
|
|
439
|
-
return new CSSThemeService(resolvedConfig);
|
|
483
|
+
return new CSSThemeService(resolvedConfig, this.workspaceRoot);
|
|
440
484
|
}
|
|
441
485
|
/**
|
|
442
486
|
* Load a custom theme service from user-specified path.
|
|
@@ -463,7 +507,12 @@ let ThemeServiceFactory = class ThemeServiceFactory {
|
|
|
463
507
|
}
|
|
464
508
|
}
|
|
465
509
|
};
|
|
466
|
-
ThemeServiceFactory = __decorate([
|
|
510
|
+
ThemeServiceFactory = __decorate([
|
|
511
|
+
(0, inversify.injectable)(),
|
|
512
|
+
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
513
|
+
__decorateParam(0, (0, inversify.optional)()),
|
|
514
|
+
__decorateMetadata("design:paramtypes", [String])
|
|
515
|
+
], ThemeServiceFactory);
|
|
467
516
|
//#endregion
|
|
468
517
|
//#region src/services/CssClasses/BaseCSSClassesService.ts
|
|
469
518
|
/**
|
|
@@ -791,6 +840,10 @@ const VALID_SERVICE_EXTENSIONS$1 = [
|
|
|
791
840
|
".cjs"
|
|
792
841
|
];
|
|
793
842
|
let CSSClassesServiceFactory = class CSSClassesServiceFactory {
|
|
843
|
+
workspaceRoot;
|
|
844
|
+
constructor(workspaceRoot) {
|
|
845
|
+
this.workspaceRoot = workspaceRoot;
|
|
846
|
+
}
|
|
794
847
|
/**
|
|
795
848
|
* Create a CSS classes service based on configuration.
|
|
796
849
|
*
|
|
@@ -831,7 +884,7 @@ let CSSClassesServiceFactory = class CSSClassesServiceFactory {
|
|
|
831
884
|
async loadCustomService(config) {
|
|
832
885
|
const servicePath = config.customServicePath;
|
|
833
886
|
if (!servicePath) throw new Error("customServicePath is required for custom service loading");
|
|
834
|
-
const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
887
|
+
const monorepoRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
835
888
|
const resolvedPath = node_path.default.resolve(monorepoRoot, servicePath);
|
|
836
889
|
const normalizedWorkspaceRoot = node_path.default.resolve(monorepoRoot);
|
|
837
890
|
if (!resolvedPath.startsWith(normalizedWorkspaceRoot + node_path.default.sep)) throw new Error(`Security error: customServicePath "${servicePath}" resolves outside workspace root`);
|
|
@@ -852,12 +905,12 @@ let CSSClassesServiceFactory = class CSSClassesServiceFactory {
|
|
|
852
905
|
}
|
|
853
906
|
}
|
|
854
907
|
};
|
|
855
|
-
CSSClassesServiceFactory = __decorate([
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
908
|
+
CSSClassesServiceFactory = __decorate([
|
|
909
|
+
(0, inversify.injectable)(),
|
|
910
|
+
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
911
|
+
__decorateParam(0, (0, inversify.optional)()),
|
|
912
|
+
__decorateMetadata("design:paramtypes", [String])
|
|
913
|
+
], CSSClassesServiceFactory);
|
|
861
914
|
//#endregion
|
|
862
915
|
//#region src/services/StoriesIndexService/StoriesIndexService.ts
|
|
863
916
|
/**
|
|
@@ -912,6 +965,7 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
912
965
|
*/
|
|
913
966
|
async initialize() {
|
|
914
967
|
if (this.initialized && this.lastInitResult) return this.lastInitResult;
|
|
968
|
+
if (this.config.workspaceRoot) this.monorepoRoot = await node_fs.promises.realpath(this.monorepoRoot);
|
|
915
969
|
log.info("[StoriesIndexService] Initializing story index...");
|
|
916
970
|
const storyFiles = await this.findStoryFiles();
|
|
917
971
|
log.info(`[StoriesIndexService] Found ${storyFiles.length} story files`);
|
|
@@ -983,6 +1037,7 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
983
1037
|
* parse is shared by content hash across service instances and concurrent rules.
|
|
984
1038
|
*/
|
|
985
1039
|
async indexStoryFile(filePath) {
|
|
1040
|
+
await this.assertContainedStory(filePath);
|
|
986
1041
|
const content = await node_fs.promises.readFile(filePath, "utf-8");
|
|
987
1042
|
const fileHash = this.hashContent(content);
|
|
988
1043
|
const cached = storyParseCache.get(filePath);
|
|
@@ -1009,12 +1064,19 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
1009
1064
|
});
|
|
1010
1065
|
if (component) this.storeComponent(component);
|
|
1011
1066
|
}
|
|
1067
|
+
async assertContainedStory(filePath) {
|
|
1068
|
+
if (!this.config.workspaceRoot) return;
|
|
1069
|
+
const [root, story] = await Promise.all([node_fs.promises.realpath(this.monorepoRoot), node_fs.promises.realpath(filePath)]);
|
|
1070
|
+
if (!isContained(root, story)) throw new Error("Story must remain inside the configured workspace root.");
|
|
1071
|
+
}
|
|
1012
1072
|
storeComponent(component) {
|
|
1013
1073
|
this.componentIndex.set(component.title, component);
|
|
1014
1074
|
this.componentFileIndex.set(node_path.default.resolve(component.filePath), component);
|
|
1015
1075
|
}
|
|
1016
1076
|
parseStoryFile(filePath, content, fileHash) {
|
|
1017
|
-
const
|
|
1077
|
+
const alias = /^export\s+default\s+([A-Za-z_$][\w$]*)\s*;?\s*$/m.exec(content)?.[1];
|
|
1078
|
+
const parsable = alias ? content.replace(new RegExp(`^export\\s+(const|let|var)\\s+(${alias.replaceAll("$", "\\$")})\\b`, "m"), "$1 $2") : content;
|
|
1079
|
+
const csf = (0, storybook_internal_csf_tools.loadCsf)(parsable, {
|
|
1018
1080
|
fileName: filePath,
|
|
1019
1081
|
makeTitle: (title) => title
|
|
1020
1082
|
});
|
|
@@ -1108,6 +1170,7 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
1108
1170
|
* @returns True if file was updated, false if unchanged
|
|
1109
1171
|
*/
|
|
1110
1172
|
async refreshFile(filePath) {
|
|
1173
|
+
await this.assertContainedStory(filePath);
|
|
1111
1174
|
const content = await node_fs.promises.readFile(filePath, "utf-8");
|
|
1112
1175
|
const newHash = this.hashContent(content);
|
|
1113
1176
|
const existingComponent = Array.from(this.componentIndex.values()).find((c) => c.filePath === filePath);
|
|
@@ -1147,27 +1210,6 @@ let StoriesIndexService = class StoriesIndexService {
|
|
|
1147
1210
|
};
|
|
1148
1211
|
StoriesIndexService = __decorate([(0, inversify.injectable)(), __decorateMetadata("design:paramtypes", [Object])], StoriesIndexService);
|
|
1149
1212
|
//#endregion
|
|
1150
|
-
//#region src/container/types.ts
|
|
1151
|
-
const STYLE_SYSTEM_TYPES = {
|
|
1152
|
-
ListThemesTool: Symbol.for("StyleSystem:ListThemesTool"),
|
|
1153
|
-
GetCSSClassesTool: Symbol.for("StyleSystem:GetCSSClassesTool"),
|
|
1154
|
-
GetComponentVisualTool: Symbol.for("StyleSystem:GetComponentVisualTool"),
|
|
1155
|
-
ListSharedComponentsTool: Symbol.for("StyleSystem:ListSharedComponentsTool"),
|
|
1156
|
-
ListAppComponentsTool: Symbol.for("StyleSystem:ListAppComponentsTool"),
|
|
1157
|
-
PolishComponentTool: Symbol.for("StyleSystem:PolishComponentTool"),
|
|
1158
|
-
ThemeServiceFactory: Symbol.for("StyleSystem:ThemeServiceFactory"),
|
|
1159
|
-
CSSClassesServiceFactory: Symbol.for("StyleSystem:CSSClassesServiceFactory"),
|
|
1160
|
-
StoriesIndexService: Symbol.for("StyleSystem:StoriesIndexService"),
|
|
1161
|
-
GetUiComponentService: Symbol.for("StyleSystem:GetUiComponentService"),
|
|
1162
|
-
AppComponentsService: Symbol.for("StyleSystem:AppComponentsService"),
|
|
1163
|
-
StoriesIndexFactory: Symbol.for("StyleSystem:StoriesIndexFactory"),
|
|
1164
|
-
RendererFactory: Symbol.for("StyleSystem:RendererFactory"),
|
|
1165
|
-
StyleSystemConfigLoader: Symbol.for("StyleSystem:StyleSystemConfigLoader"),
|
|
1166
|
-
DefaultThemePath: Symbol.for("StyleSystem:DefaultThemePath"),
|
|
1167
|
-
GetUiComponentServiceConfig: Symbol.for("StyleSystem:GetUiComponentServiceConfig"),
|
|
1168
|
-
AppComponentsServiceConfig: Symbol.for("StyleSystem:AppComponentsServiceConfig")
|
|
1169
|
-
};
|
|
1170
|
-
//#endregion
|
|
1171
1213
|
//#region src/config/types.ts
|
|
1172
1214
|
/**
|
|
1173
1215
|
* Style system configuration types.
|
|
@@ -1333,9 +1375,9 @@ const EXPO_APP_CONFIG_FILE = "app.json";
|
|
|
1333
1375
|
* values, and warns when it is the one that resolves.
|
|
1334
1376
|
*/
|
|
1335
1377
|
async function resolveStyleSystemThemePath(input) {
|
|
1336
|
-
const hostRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
1378
|
+
const hostRoot = input.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
1337
1379
|
const resolvedAppPath = input.appPath ? node_path.default.isAbsolute(input.appPath) ? node_path.default.normalize(input.appPath) : node_path.default.join(hostRoot, input.appPath) : hostRoot;
|
|
1338
|
-
const monorepoRoot = input.appPath ? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath) : hostRoot;
|
|
1380
|
+
const monorepoRoot = input.workspaceRoot ?? (input.appPath ? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath) : hostRoot);
|
|
1339
1381
|
const workspaceCandidate = node_path.default.join(monorepoRoot, input.themePath);
|
|
1340
1382
|
const candidates = node_path.default.isAbsolute(input.themePath) ? [input.themePath] : [workspaceCandidate, node_path.default.resolve(resolvedAppPath, input.themePath)];
|
|
1341
1383
|
const uniqueCandidates = [...new Set(candidates)];
|
|
@@ -1385,19 +1427,23 @@ function isNotFound(error) {
|
|
|
1385
1427
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
1386
1428
|
}
|
|
1387
1429
|
let StyleSystemConfigLoader = class StyleSystemConfigLoader {
|
|
1430
|
+
workspaceRoot;
|
|
1388
1431
|
/**
|
|
1389
1432
|
* Promises rather than values, so concurrent resolves de-duplicate. Several
|
|
1390
1433
|
* callers hit config from inside Promise.all/allSettled.
|
|
1391
1434
|
*/
|
|
1392
1435
|
rootPromises = /* @__PURE__ */ new Map();
|
|
1393
1436
|
projectCache = /* @__PURE__ */ new Map();
|
|
1437
|
+
constructor(workspaceRoot) {
|
|
1438
|
+
this.workspaceRoot = workspaceRoot;
|
|
1439
|
+
}
|
|
1394
1440
|
/** Drop all cached config state. */
|
|
1395
1441
|
invalidate() {
|
|
1396
1442
|
this.rootPromises.clear();
|
|
1397
1443
|
this.projectCache.clear();
|
|
1398
1444
|
}
|
|
1399
1445
|
/** Load and cache the workspace root config. */
|
|
1400
|
-
async loadRoot(workspaceRoot = TemplatesManagerService.getWorkspaceRootSync()) {
|
|
1446
|
+
async loadRoot(workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync()) {
|
|
1401
1447
|
let pending = this.rootPromises.get(workspaceRoot);
|
|
1402
1448
|
if (!pending) {
|
|
1403
1449
|
pending = this.readRoot(workspaceRoot);
|
|
@@ -1413,8 +1459,8 @@ let StyleSystemConfigLoader = class StyleSystemConfigLoader {
|
|
|
1413
1459
|
/** Resolve the merged configuration for a project. */
|
|
1414
1460
|
async resolveProject(appPath) {
|
|
1415
1461
|
if (!appPath || typeof appPath !== "string") throw new Error("appPath is required and must be a non-empty string");
|
|
1416
|
-
const resolvedAppPath = node_path.default.isAbsolute(appPath) ? node_path.default.normalize(appPath) : node_path.default.join(TemplatesManagerService.getWorkspaceRootSync(), appPath);
|
|
1417
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath);
|
|
1462
|
+
const resolvedAppPath = node_path.default.isAbsolute(appPath) ? node_path.default.normalize(appPath) : node_path.default.join(this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(), appPath);
|
|
1463
|
+
const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath);
|
|
1418
1464
|
const cached = this.projectCache.get(resolvedAppPath);
|
|
1419
1465
|
if (cached) return cached;
|
|
1420
1466
|
const pending = this.readProject(resolvedAppPath, workspaceRoot);
|
|
@@ -1481,7 +1527,12 @@ let StyleSystemConfigLoader = class StyleSystemConfigLoader {
|
|
|
1481
1527
|
};
|
|
1482
1528
|
}
|
|
1483
1529
|
};
|
|
1484
|
-
StyleSystemConfigLoader = __decorate([
|
|
1530
|
+
StyleSystemConfigLoader = __decorate([
|
|
1531
|
+
(0, inversify.injectable)(),
|
|
1532
|
+
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
1533
|
+
__decorateParam(0, (0, inversify.optional)()),
|
|
1534
|
+
__decorateMetadata("design:paramtypes", [String])
|
|
1535
|
+
], StyleSystemConfigLoader);
|
|
1485
1536
|
//#endregion
|
|
1486
1537
|
//#region src/config/index.ts
|
|
1487
1538
|
/**
|
|
@@ -1579,6 +1630,17 @@ const browserLaunchConfigs = [
|
|
|
1579
1630
|
];
|
|
1580
1631
|
const STORY_ROOT_SELECTOR = "#root";
|
|
1581
1632
|
const VITE_ERROR_OVERLAY_SELECTOR = "vite-error-overlay";
|
|
1633
|
+
const STORY_MOUNT_TIMEOUT_MS = 1e4;
|
|
1634
|
+
/**
|
|
1635
|
+
* Runs in the page (this package has no DOM types): resolves once web fonts and every image the
|
|
1636
|
+
* story rendered have loaded (or failed), then two frames have painted.
|
|
1637
|
+
*/
|
|
1638
|
+
const FONTS_IMAGES_AND_TWO_FRAMES = `Promise.all([
|
|
1639
|
+
document.fonts.ready,
|
|
1640
|
+
...Array.from(document.images, (image) =>
|
|
1641
|
+
image.complete ? null : new Promise((resolve) => { image.addEventListener('load', resolve, { once: true }); image.addEventListener('error', resolve, { once: true }); }),
|
|
1642
|
+
),
|
|
1643
|
+
]).then(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))))`;
|
|
1582
1644
|
/**
|
|
1583
1645
|
* Attempts to launch a browser, trying multiple configurations in order of preference.
|
|
1584
1646
|
* @returns Launched browser instance
|
|
@@ -1600,26 +1662,39 @@ async function launchBrowserWithFallback() {
|
|
|
1600
1662
|
throw new Error(`No browser available. Tried:\n${errors.join("\n")}\n\nPlease install Chrome, or run 'npx playwright install chromium'`);
|
|
1601
1663
|
}
|
|
1602
1664
|
async function takeScreenshot(options) {
|
|
1603
|
-
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;
|
|
1665
|
+
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;
|
|
1604
1666
|
let browser = null;
|
|
1667
|
+
let context = null;
|
|
1668
|
+
let release;
|
|
1605
1669
|
try {
|
|
1606
|
-
if (
|
|
1670
|
+
if (sharedBrowser) {
|
|
1671
|
+
const borrowed = await sharedBrowser();
|
|
1672
|
+
if ("browser" in borrowed) {
|
|
1673
|
+
browser = borrowed.browser;
|
|
1674
|
+
release = () => borrowed.release();
|
|
1675
|
+
} else browser = borrowed;
|
|
1676
|
+
} else if (browserName === "chromium") browser = await launchBrowserWithFallback();
|
|
1607
1677
|
else {
|
|
1608
1678
|
const browserType = browsers[browserName];
|
|
1609
1679
|
if (!browserType) throw new Error(`Unsupported browser: ${browserName}`);
|
|
1610
1680
|
browser = await browserType.launch({ headless: true });
|
|
1611
1681
|
}
|
|
1612
|
-
|
|
1682
|
+
context = await browser.newContext({
|
|
1613
1683
|
viewport: {
|
|
1614
1684
|
width,
|
|
1615
1685
|
height
|
|
1616
1686
|
},
|
|
1617
1687
|
colorScheme: darkMode ? "dark" : "light",
|
|
1618
1688
|
isMobile: mobile
|
|
1619
|
-
})
|
|
1689
|
+
});
|
|
1690
|
+
const page = await context.newPage();
|
|
1620
1691
|
const runtimeErrors = [];
|
|
1621
|
-
page.on("pageerror", (error) => runtimeErrors.push(error.stack
|
|
1622
|
-
await page.goto(url, { waitUntil: "
|
|
1692
|
+
page.on("pageerror", (error) => runtimeErrors.push(error.stack || error.message || String(error)));
|
|
1693
|
+
await page.goto(url, { waitUntil: "load" });
|
|
1694
|
+
if (await page.waitForSelector(`${STORY_ROOT_SELECTOR} > *, ${VITE_ERROR_OVERLAY_SELECTOR}`, {
|
|
1695
|
+
state: "attached",
|
|
1696
|
+
timeout: STORY_MOUNT_TIMEOUT_MS
|
|
1697
|
+
}).then(() => true).catch(() => false)) await page.evaluate(FONTS_IMAGES_AND_TWO_FRAMES);
|
|
1623
1698
|
if (waitTime > 0) await page.waitForTimeout(waitTime);
|
|
1624
1699
|
const hasViteErrorOverlay = await page.locator(VITE_ERROR_OVERLAY_SELECTOR).count();
|
|
1625
1700
|
const renderedChildCount = await page.locator(`${STORY_ROOT_SELECTOR} > *`).count();
|
|
@@ -1631,7 +1706,27 @@ async function takeScreenshot(options) {
|
|
|
1631
1706
|
path: output,
|
|
1632
1707
|
fullPage
|
|
1633
1708
|
});
|
|
1634
|
-
const screenshotMetadata = await (
|
|
1709
|
+
const screenshotMetadata = await page.evaluate(`(async () => {
|
|
1710
|
+
const bytes = Uint8Array.from(atob(${JSON.stringify(screenshotBuffer.toString("base64"))}), (char) => char.charCodeAt(0));
|
|
1711
|
+
const image = await createImageBitmap(new Blob([bytes]));
|
|
1712
|
+
try {
|
|
1713
|
+
const result = { width: image.width, height: image.height };
|
|
1714
|
+
if (${generateThumbnail}) {
|
|
1715
|
+
const width = ${JSON.stringify(thumbnailWidth)};
|
|
1716
|
+
const quality = ${JSON.stringify(thumbnailQuality)};
|
|
1717
|
+
if (!Number.isSafeInteger(width) || width <= 0 || !Number.isInteger(quality) || quality < 1 || quality > 100)
|
|
1718
|
+
throw new Error('Invalid thumbnail width or quality.');
|
|
1719
|
+
const canvas = document.createElement('canvas');
|
|
1720
|
+
canvas.width = width;
|
|
1721
|
+
canvas.height = Math.max(1, Math.round(image.height * width / image.width));
|
|
1722
|
+
canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
1723
|
+
result.thumbnail = canvas.toDataURL('image/jpeg', quality / 100).split(',')[1];
|
|
1724
|
+
}
|
|
1725
|
+
return result;
|
|
1726
|
+
} finally {
|
|
1727
|
+
image.close();
|
|
1728
|
+
}
|
|
1729
|
+
})()`);
|
|
1635
1730
|
const result = {
|
|
1636
1731
|
imagePath: output,
|
|
1637
1732
|
width: screenshotMetadata.width ?? width,
|
|
@@ -1640,13 +1735,18 @@ async function takeScreenshot(options) {
|
|
|
1640
1735
|
if (generateThumbnail) {
|
|
1641
1736
|
const ext = node_path.default.extname(output);
|
|
1642
1737
|
const thumbnailPath = output.replace(ext, `-thumb${ext}`);
|
|
1643
|
-
await (0,
|
|
1738
|
+
await (0, node_fs_promises.writeFile)(thumbnailPath.replace(ext, ".jpg"), Buffer.from(screenshotMetadata.thumbnail, "base64"));
|
|
1644
1739
|
result.thumbnailPath = thumbnailPath.replace(ext, ".jpg");
|
|
1645
1740
|
}
|
|
1646
1741
|
if (base64) result.base64 = screenshotBuffer.toString("base64");
|
|
1647
1742
|
return result;
|
|
1648
1743
|
} finally {
|
|
1649
|
-
|
|
1744
|
+
try {
|
|
1745
|
+
if (sharedBrowser) await context?.close();
|
|
1746
|
+
else if (browser) await browser.close();
|
|
1747
|
+
} finally {
|
|
1748
|
+
release?.();
|
|
1749
|
+
}
|
|
1650
1750
|
}
|
|
1651
1751
|
}
|
|
1652
1752
|
//#endregion
|
|
@@ -1896,6 +1996,37 @@ function createStoryEntryPlugin(getStoryConfig, generateCode) {
|
|
|
1896
1996
|
}
|
|
1897
1997
|
};
|
|
1898
1998
|
}
|
|
1999
|
+
/** Where a package the app depends on is installed, resolved from the app rather than from this tool. */
|
|
2000
|
+
function appPackageDir(appPath, packageName) {
|
|
2001
|
+
return node_path.default.dirname((0, node_module.createRequire)(node_path.default.join(appPath, "package.json")).resolve(`${packageName}/package.json`));
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* React and react-dom are peers of this tool: every story bundle takes the app's own pair, so the
|
|
2005
|
+
* story, the generated entry and any workspace package it imports share one React that matches its
|
|
2006
|
+
* react-dom. A pair resolved from the tool fails in the browser with React error #527.
|
|
2007
|
+
*/
|
|
2008
|
+
function appReactAliases(appPath) {
|
|
2009
|
+
const reactDir = appPackageDir(appPath, "react");
|
|
2010
|
+
const reactDomDir = appPackageDir(appPath, "react-dom");
|
|
2011
|
+
return [
|
|
2012
|
+
{
|
|
2013
|
+
find: /^react$/,
|
|
2014
|
+
replacement: reactDir
|
|
2015
|
+
},
|
|
2016
|
+
{
|
|
2017
|
+
find: /^react\/(.*)$/,
|
|
2018
|
+
replacement: node_path.default.join(reactDir, "$1")
|
|
2019
|
+
},
|
|
2020
|
+
{
|
|
2021
|
+
find: /^react-dom$/,
|
|
2022
|
+
replacement: reactDomDir
|
|
2023
|
+
},
|
|
2024
|
+
{
|
|
2025
|
+
find: /^react-dom\/(.*)$/,
|
|
2026
|
+
replacement: node_path.default.join(reactDomDir, "$1")
|
|
2027
|
+
}
|
|
2028
|
+
];
|
|
2029
|
+
}
|
|
1899
2030
|
/**
|
|
1900
2031
|
* Helper to create a Vite plugin that resolves the module specifiers TanStack Start's
|
|
1901
2032
|
* `tanstackStart()` plugin normally provides to inert client-safe stubs, so stories in TanStack
|
|
@@ -1936,6 +2067,7 @@ function createTanStackStartStubPlugin() {
|
|
|
1936
2067
|
* ```
|
|
1937
2068
|
*/
|
|
1938
2069
|
var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerService {
|
|
2070
|
+
workspaceRoot;
|
|
1939
2071
|
static instance = null;
|
|
1940
2072
|
server = null;
|
|
1941
2073
|
monorepoRoot;
|
|
@@ -1952,9 +2084,10 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
1952
2084
|
* Use getInstance() for singleton access.
|
|
1953
2085
|
* @param config - Service configuration options
|
|
1954
2086
|
*/
|
|
1955
|
-
constructor(config = {}) {
|
|
2087
|
+
constructor(config = {}, workspaceRoot) {
|
|
1956
2088
|
super(config);
|
|
1957
|
-
this.
|
|
2089
|
+
this.workspaceRoot = workspaceRoot;
|
|
2090
|
+
this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
1958
2091
|
}
|
|
1959
2092
|
/**
|
|
1960
2093
|
* Get the singleton instance of ViteReactBundlerService.
|
|
@@ -2055,6 +2188,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2055
2188
|
*/
|
|
2056
2189
|
async doStartDevServer(resolvedAppPath) {
|
|
2057
2190
|
const tmpDir = node_path.default.join(resolvedAppPath, TMP_DIR_NAME);
|
|
2191
|
+
let createdServer;
|
|
2058
2192
|
try {
|
|
2059
2193
|
await node_fs.promises.mkdir(tmpDir, { recursive: true });
|
|
2060
2194
|
const { createServer } = await import("vite");
|
|
@@ -2069,6 +2203,33 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2069
2203
|
(0, _tailwindcss_vite.default)(),
|
|
2070
2204
|
createTanStackStartStubPlugin(),
|
|
2071
2205
|
...this.getAdditionalVitePlugins(resolvedAppPath),
|
|
2206
|
+
{
|
|
2207
|
+
name: "style-system-preview-html",
|
|
2208
|
+
configureServer: (server) => {
|
|
2209
|
+
server.middlewares.use(async (req, res, next) => {
|
|
2210
|
+
const url = req.url || "/";
|
|
2211
|
+
const match = url.match(PREVIEW_ROUTE_PATTERN);
|
|
2212
|
+
if (match) {
|
|
2213
|
+
const storyId = match[1];
|
|
2214
|
+
const config = this.storyConfigs.get(storyId);
|
|
2215
|
+
if (config) try {
|
|
2216
|
+
const htmlTemplate = this.generateHtmlTemplate(`${VIRTUAL_STORY_ENTRY_SCRIPT_PREFIX}${storyId}`, config.darkMode);
|
|
2217
|
+
const transformedHtml = await server.transformIndexHtml(url, htmlTemplate);
|
|
2218
|
+
res.statusCode = HTTP_STATUS_OK;
|
|
2219
|
+
res.setHeader(CONTENT_TYPE_HEADER, TEXT_HTML_CONTENT_TYPE);
|
|
2220
|
+
res.end(transformedHtml);
|
|
2221
|
+
return;
|
|
2222
|
+
} catch (e) {
|
|
2223
|
+
const err = e;
|
|
2224
|
+
log.error(`[ViteMiddleware] Error serving preview: ${err.message}`);
|
|
2225
|
+
next(err);
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
next();
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
},
|
|
2072
2233
|
createStoryEntryPlugin((id) => this.storyConfigs.get(id), (opts) => this.generateEntryFile(opts))
|
|
2073
2234
|
],
|
|
2074
2235
|
resolve: this.getViteResolveConfig(resolvedAppPath),
|
|
@@ -2080,28 +2241,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2080
2241
|
open: false
|
|
2081
2242
|
}
|
|
2082
2243
|
});
|
|
2083
|
-
this.server
|
|
2084
|
-
const url = req.url || "/";
|
|
2085
|
-
const match = url.match(PREVIEW_ROUTE_PATTERN);
|
|
2086
|
-
if (match) {
|
|
2087
|
-
const storyId = match[1];
|
|
2088
|
-
const config = this.storyConfigs.get(storyId);
|
|
2089
|
-
if (config) try {
|
|
2090
|
-
const htmlTemplate = this.generateHtmlTemplate(`${VIRTUAL_STORY_ENTRY_SCRIPT_PREFIX}${storyId}`, config.darkMode);
|
|
2091
|
-
const transformedHtml = await this.server?.transformIndexHtml(url, htmlTemplate);
|
|
2092
|
-
res.statusCode = HTTP_STATUS_OK;
|
|
2093
|
-
res.setHeader(CONTENT_TYPE_HEADER, TEXT_HTML_CONTENT_TYPE);
|
|
2094
|
-
res.end(transformedHtml);
|
|
2095
|
-
return;
|
|
2096
|
-
} catch (e) {
|
|
2097
|
-
const err = e;
|
|
2098
|
-
log.error(`[ViteMiddleware] Error serving preview: ${err.message}`);
|
|
2099
|
-
next(err);
|
|
2100
|
-
return;
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
next();
|
|
2104
|
-
});
|
|
2244
|
+
createdServer = this.server;
|
|
2105
2245
|
await this.server.listen();
|
|
2106
2246
|
const address = this.server.httpServer?.address();
|
|
2107
2247
|
if (!address || typeof address === "string") throw new Error("Failed to start Vite dev server. Ensure no other process is using the port.");
|
|
@@ -2116,6 +2256,15 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2116
2256
|
port
|
|
2117
2257
|
};
|
|
2118
2258
|
} catch (error) {
|
|
2259
|
+
try {
|
|
2260
|
+
await createdServer?.close();
|
|
2261
|
+
} catch (closeError) {
|
|
2262
|
+
log.warn(`[ViteReactBundlerService] Failed to close server after startup failure: ${String(closeError)}`);
|
|
2263
|
+
}
|
|
2264
|
+
this.server = null;
|
|
2265
|
+
this.serverUrl = null;
|
|
2266
|
+
this.serverPort = null;
|
|
2267
|
+
this.currentAppPath = null;
|
|
2119
2268
|
const err = /* @__PURE__ */ new Error(`Failed to start dev server for ${resolvedAppPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2120
2269
|
err.cause = error;
|
|
2121
2270
|
throw err;
|
|
@@ -2134,7 +2283,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2134
2283
|
validateComponentPath(componentPath);
|
|
2135
2284
|
validateArgsSize(args);
|
|
2136
2285
|
const resolvedAppPath = node_path.default.isAbsolute(appPath) ? appPath : node_path.default.join(this.monorepoRoot, appPath);
|
|
2137
|
-
validateCssFiles(cssFiles, TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2286
|
+
validateCssFiles(cssFiles, this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2138
2287
|
if (this.currentAppPath !== resolvedAppPath) throw new Error(`Dev server is running for ${this.currentAppPath} but requested ${resolvedAppPath}.`);
|
|
2139
2288
|
const tmpDir = node_path.default.join(resolvedAppPath, TMP_DIR_NAME);
|
|
2140
2289
|
try {
|
|
@@ -2182,7 +2331,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2182
2331
|
validateComponentPath(componentPath);
|
|
2183
2332
|
validateArgsSize(args);
|
|
2184
2333
|
const resolvedAppPath = node_path.default.isAbsolute(appPath) ? appPath : node_path.default.join(this.monorepoRoot, appPath);
|
|
2185
|
-
validateCssFiles(cssFiles, TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2334
|
+
validateCssFiles(cssFiles, this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
|
|
2186
2335
|
const tmpDir = node_path.default.join(resolvedAppPath, TMP_DIR_NAME);
|
|
2187
2336
|
try {
|
|
2188
2337
|
await node_fs.promises.mkdir(tmpDir, { recursive: true });
|
|
@@ -2208,9 +2357,10 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2208
2357
|
* Subclasses can override to add platform-specific aliases (e.g., react-native-web).
|
|
2209
2358
|
*/
|
|
2210
2359
|
getViteResolveConfig(appPath) {
|
|
2211
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2360
|
+
const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2212
2361
|
const frontendPackagePath = (packageDir, sourcePath = "index.ts") => node_path.default.join(workspaceRoot, WORKSPACE_PACKAGE_DIR_PREFIX, "frontend", packageDir, APP_SOURCE_DIR$1, sourcePath);
|
|
2213
2362
|
return { alias: [
|
|
2363
|
+
...appReactAliases(appPath),
|
|
2214
2364
|
{
|
|
2215
2365
|
find: APP_SOURCE_ALIAS_PATTERN,
|
|
2216
2366
|
replacement: `${node_path.default.join(appPath, APP_SOURCE_DIR$1)}/`
|
|
@@ -2317,7 +2467,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
|
|
|
2317
2467
|
*/
|
|
2318
2468
|
async generateWrapperCss(appPath, cssFiles) {
|
|
2319
2469
|
const resolvedCssFiles = cssFiles.length > 0 ? cssFiles : await this.getDefaultCssFiles(appPath);
|
|
2320
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2470
|
+
const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(appPath);
|
|
2321
2471
|
const cssImportStatements = resolvedCssFiles.map((cssFile) => {
|
|
2322
2472
|
if (node_path.default.isAbsolute(cssFile)) return `@import '${cssFile}';`;
|
|
2323
2473
|
if (cssFile.startsWith(PACKAGE_IMPORT_PREFIX) || cssFile.startsWith(TAILWIND_IMPORT_PREFIX)) return `@import '${cssFile}';`;
|
|
@@ -2539,20 +2689,16 @@ mount();
|
|
|
2539
2689
|
*
|
|
2540
2690
|
* Extends ViteReactBundlerService with React Native → web aliases
|
|
2541
2691
|
* so native-ui components can be rendered in a browser via Playwright.
|
|
2542
|
-
* Uses React Native Web and
|
|
2692
|
+
* Uses the app's own React Native Web and Unistyles web runtime.
|
|
2543
2693
|
*
|
|
2544
|
-
*
|
|
2694
|
+
* Shim paths resolve relative to this package; React, react-dom and
|
|
2695
|
+
* react-native-web are peers and always resolve from the app.
|
|
2545
2696
|
*/
|
|
2546
2697
|
const __dirname$1 = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
2547
|
-
const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
2548
|
-
const REACT_PACKAGE = "react";
|
|
2549
|
-
const REACT_DOM_PACKAGE = "react-dom";
|
|
2550
2698
|
const REACT_NATIVE_PACKAGE = "react-native";
|
|
2551
2699
|
const REACT_NATIVE_WEB_PACKAGE = "react-native-web";
|
|
2552
|
-
const REACT_NATIVE_INDEX_SUBPATH = "react-native/index.js";
|
|
2553
2700
|
const REACT_NATIVE_UNISTYLES_PACKAGE = "react-native-unistyles";
|
|
2554
2701
|
const REACT_NATIVE_UNISTYLES_BABEL_PLUGIN = "react-native-unistyles/plugin";
|
|
2555
|
-
const REACT_NATIVE_REANIMATED_PACKAGE = "react-native-reanimated";
|
|
2556
2702
|
const REACT_NATIVE_REANIMATED_BABEL_PLUGIN = "react-native-reanimated/plugin";
|
|
2557
2703
|
const BABEL_CORE_PACKAGE = "@babel/core";
|
|
2558
2704
|
const BABEL_PLUGIN_SYNTAX_JSX = "@babel/plugin-syntax-jsx";
|
|
@@ -2566,8 +2712,7 @@ const APP_SOURCE_ALIAS = "@";
|
|
|
2566
2712
|
const APP_SOURCE_DIR = "src";
|
|
2567
2713
|
const VITE_PLUGIN_ENFORCE_PRE = "pre";
|
|
2568
2714
|
const VITE_REACT_NATIVE_BUNDLER_ID = "vite-react-native";
|
|
2569
|
-
const
|
|
2570
|
-
const REACT_NATIVE_REANIMATED_REDIRECT_PLUGIN_NAME = "vite-redirect-react-native-reanimated";
|
|
2715
|
+
const REACT_NATIVE_OPTIMIZE_DEPS_PLUGIN_NAME = "vite-react-native-optimize-deps";
|
|
2571
2716
|
const EXPO_ICONS_REDIRECT_PLUGIN_NAME = "vite-redirect-expo-vector-icons";
|
|
2572
2717
|
const NATIVE_BABEL_TRANSFORM_PLUGIN_NAME = "vite-react-native-babel";
|
|
2573
2718
|
const EXPO_VECTOR_ICONS_RESOLVED_MODULE_PREFIX = `\0virtual:style-system-expo-vector-icons`;
|
|
@@ -2578,10 +2723,8 @@ const EXPO_VECTOR_ICONS_SHIM_FILE = "expo-vector-icons.ts";
|
|
|
2578
2723
|
const EXPO_SYMBOLS_SHIM_FILE = "expo-symbols.ts";
|
|
2579
2724
|
const REQUIRED_REACT_NATIVE_SHIM_FILES = [EXPO_VECTOR_ICONS_SHIM_FILE, EXPO_SYMBOLS_SHIM_FILE];
|
|
2580
2725
|
const PACKAGE_JSON_SUBPATH = "package.json";
|
|
2581
|
-
const REACT_NATIVE_WEB_DIST_INDEX = "dist/index.js";
|
|
2582
2726
|
const REACT_NATIVE_UNISTYLES_WEB_INDEX = "lib/module/index.js";
|
|
2583
2727
|
const REACT_NATIVE_UNISTYLES_NATIVE_COMPONENTS = "lib/module/components/native";
|
|
2584
|
-
const REACT_NATIVE_REANIMATED_WEB_INDEX = "lib/module/index.js";
|
|
2585
2728
|
const EXPO_VECTOR_ICONS_GLYPHMAP_DIR = "build/vendor/react-native-vector-icons/glyphmaps";
|
|
2586
2729
|
const EXPO_VECTOR_ICONS_FONT_DIR = "build/vendor/react-native-vector-icons/Fonts";
|
|
2587
2730
|
const GLYPHMAP_EXTENSION = ".json";
|
|
@@ -2600,6 +2743,8 @@ const REACT_NATIVE_DEV_GLOBAL = "__DEV__";
|
|
|
2600
2743
|
const REACT_NATIVE_DEV_VALUE = "false";
|
|
2601
2744
|
const REACT_NATIVE_GLOBAL = "global";
|
|
2602
2745
|
const REACT_NATIVE_GLOBAL_VALUE = "globalThis";
|
|
2746
|
+
const EXPO_OS_DEFINE = "process.env.EXPO_OS";
|
|
2747
|
+
const EXPO_OS_WEB_VALUE = JSON.stringify("web");
|
|
2603
2748
|
const VITE_RESOLVE_EXTENSIONS = [
|
|
2604
2749
|
".web.tsx",
|
|
2605
2750
|
".web.ts",
|
|
@@ -2622,18 +2767,13 @@ const SHIMS_DIR_CANDIDATES = [
|
|
|
2622
2767
|
node_path.default.resolve(__dirname$1, "../src/shims")
|
|
2623
2768
|
];
|
|
2624
2769
|
const SHIMS_DIR = resolveShimsDir();
|
|
2625
|
-
const REACT_NATIVE_WEB_DIR = resolvePackageDir(REACT_NATIVE_WEB_PACKAGE);
|
|
2626
2770
|
function resolveExistingDir(candidates, label, isValidDir = () => true) {
|
|
2627
2771
|
const existing = candidates.find((candidate) => (0, node_fs.existsSync)(candidate) && isValidDir(candidate));
|
|
2628
2772
|
if (existing) return existing;
|
|
2629
2773
|
throw new Error(`Could not resolve ${label}. Tried: ${candidates.join(", ")}`);
|
|
2630
2774
|
}
|
|
2631
|
-
function resolvePackageDir(packageName) {
|
|
2632
|
-
return (0, node_path.dirname)(require$1.resolve(`${packageName}/${PACKAGE_JSON_SUBPATH}`));
|
|
2633
|
-
}
|
|
2634
2775
|
function resolvePackageDirFrom(fromPath, packageName) {
|
|
2635
|
-
|
|
2636
|
-
return (0, node_path.dirname)(fromRequire.resolve(`${packageName}/${PACKAGE_JSON_SUBPATH}`));
|
|
2776
|
+
return appPackageDir(fromPath, packageName);
|
|
2637
2777
|
}
|
|
2638
2778
|
function resolvePackageSubpathFrom(fromPath, subpath) {
|
|
2639
2779
|
return (0, node_module.createRequire)(node_path.default.join(fromPath, PACKAGE_JSON_SUBPATH)).resolve(subpath);
|
|
@@ -2728,6 +2868,7 @@ function createNativeBabelTransformPlugin(appPath) {
|
|
|
2728
2868
|
name: NATIVE_BABEL_TRANSFORM_PLUGIN_NAME,
|
|
2729
2869
|
enforce: VITE_PLUGIN_ENFORCE_PRE,
|
|
2730
2870
|
async transform(code, id) {
|
|
2871
|
+
id = id.split(VITE_QUERY_SEPARATOR)[0] ?? id;
|
|
2731
2872
|
const isFlowModule = toViteImportPath(id).includes("/@react-native-segmented-control/segmented-control/");
|
|
2732
2873
|
const isSupportedNodeModule = isFlowModule || id.includes(REACT_NATIVE_MARKDOWN_DISPLAY_PATH_SEGMENT);
|
|
2733
2874
|
if (id.includes(NODE_MODULES_PATH_SEGMENT) && !isSupportedNodeModule || !BABEL_TRANSFORM_EXTENSION_PATTERN.test(id)) return null;
|
|
@@ -2869,24 +3010,20 @@ var ViteReactNativeBundlerService = class ViteReactNativeBundlerService extends
|
|
|
2869
3010
|
return VITE_REACT_NATIVE_BUNDLER_ID;
|
|
2870
3011
|
}
|
|
2871
3012
|
getAdditionalVitePlugins(appPath) {
|
|
2872
|
-
const reactNativeWebEntry = node_path.default.join(REACT_NATIVE_WEB_DIR, REACT_NATIVE_WEB_DIST_INDEX);
|
|
2873
|
-
const reactNativeReanimatedEntry = appPath ? node_path.default.join(resolvePackageDirFrom(appPath, REACT_NATIVE_REANIMATED_PACKAGE), REACT_NATIVE_REANIMATED_WEB_INDEX) : null;
|
|
2874
3013
|
return [
|
|
2875
3014
|
{
|
|
2876
|
-
name:
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
return null;
|
|
2889
|
-
}
|
|
3015
|
+
name: REACT_NATIVE_OPTIMIZE_DEPS_PLUGIN_NAME,
|
|
3016
|
+
config: (config) => ({ optimizeDeps: {
|
|
3017
|
+
rolldownOptions: { resolve: { extensions: config.resolve?.extensions } },
|
|
3018
|
+
exclude: [
|
|
3019
|
+
"@react-native-segmented-control/segmented-control",
|
|
3020
|
+
SAFE_AREA_CONTEXT_PACKAGE,
|
|
3021
|
+
"expo-haptics",
|
|
3022
|
+
"expo-modules-core",
|
|
3023
|
+
"react-native-gesture-handler"
|
|
3024
|
+
],
|
|
3025
|
+
include: ["react-native-gesture-handler > hoist-non-react-statics", "react-native-gesture-handler > invariant"]
|
|
3026
|
+
} })
|
|
2890
3027
|
},
|
|
2891
3028
|
createExpoTypeDeclarationStubPlugin(),
|
|
2892
3029
|
createSvgComponentPlugin(),
|
|
@@ -2922,7 +3059,8 @@ const styleSystemSafeAreaMetrics = {
|
|
|
2922
3059
|
getViteDefineConfig() {
|
|
2923
3060
|
return {
|
|
2924
3061
|
[REACT_NATIVE_DEV_GLOBAL]: REACT_NATIVE_DEV_VALUE,
|
|
2925
|
-
[REACT_NATIVE_GLOBAL]: REACT_NATIVE_GLOBAL_VALUE
|
|
3062
|
+
[REACT_NATIVE_GLOBAL]: REACT_NATIVE_GLOBAL_VALUE,
|
|
3063
|
+
[EXPO_OS_DEFINE]: EXPO_OS_WEB_VALUE
|
|
2926
3064
|
};
|
|
2927
3065
|
}
|
|
2928
3066
|
generatePreviewStyles() {
|
|
@@ -2947,38 +3085,18 @@ const styleSystemSafeAreaMetrics = {
|
|
|
2947
3085
|
getViteResolveConfig(appPath) {
|
|
2948
3086
|
const reactNativeUnistylesDir = resolvePackageDirFrom(appPath, REACT_NATIVE_UNISTYLES_PACKAGE);
|
|
2949
3087
|
const reactNativeUnistylesEntry = node_path.default.join(reactNativeUnistylesDir, REACT_NATIVE_UNISTYLES_WEB_INDEX);
|
|
2950
|
-
const
|
|
2951
|
-
const reactDomDir = resolvePackageDirFrom(appPath, REACT_DOM_PACKAGE);
|
|
3088
|
+
const reactNativeWebDir = resolvePackageDirFrom(appPath, REACT_NATIVE_WEB_PACKAGE);
|
|
2952
3089
|
return {
|
|
2953
3090
|
extensions: [...VITE_RESOLVE_EXTENSIONS],
|
|
2954
3091
|
alias: [
|
|
2955
|
-
|
|
2956
|
-
find: /^react$/,
|
|
2957
|
-
replacement: reactDir
|
|
2958
|
-
},
|
|
2959
|
-
{
|
|
2960
|
-
find: /^react\/(.*)$/,
|
|
2961
|
-
replacement: node_path.default.join(reactDir, "$1")
|
|
2962
|
-
},
|
|
2963
|
-
{
|
|
2964
|
-
find: /^react-dom$/,
|
|
2965
|
-
replacement: reactDomDir
|
|
2966
|
-
},
|
|
2967
|
-
{
|
|
2968
|
-
find: /^react-dom\/(.*)$/,
|
|
2969
|
-
replacement: node_path.default.join(reactDomDir, "$1")
|
|
2970
|
-
},
|
|
3092
|
+
...appReactAliases(appPath),
|
|
2971
3093
|
{
|
|
2972
3094
|
find: APP_SOURCE_ALIAS,
|
|
2973
3095
|
replacement: node_path.default.join(appPath, APP_SOURCE_DIR)
|
|
2974
3096
|
},
|
|
2975
3097
|
{
|
|
2976
|
-
find:
|
|
2977
|
-
replacement:
|
|
2978
|
-
},
|
|
2979
|
-
{
|
|
2980
|
-
find: REACT_NATIVE_INDEX_SUBPATH,
|
|
2981
|
-
replacement: node_path.default.join(REACT_NATIVE_WEB_DIR, REACT_NATIVE_WEB_DIST_INDEX)
|
|
3098
|
+
find: /^react-native$/,
|
|
3099
|
+
replacement: reactNativeWebDir
|
|
2982
3100
|
},
|
|
2983
3101
|
{
|
|
2984
3102
|
find: /^react-native-unistyles\/components\/native\/(.+)$/,
|
|
@@ -3041,8 +3159,8 @@ const VALID_SERVICE_EXTENSIONS = [
|
|
|
3041
3159
|
* await bundler.startDevServer('apps/my-app');
|
|
3042
3160
|
* ```
|
|
3043
3161
|
*/
|
|
3044
|
-
function createDefaultBundlerService() {
|
|
3045
|
-
return ViteReactBundlerService.getInstance();
|
|
3162
|
+
function createDefaultBundlerService(workspaceRoot) {
|
|
3163
|
+
return workspaceRoot ? new ViteReactBundlerService({}, workspaceRoot) : ViteReactBundlerService.getInstance();
|
|
3046
3164
|
}
|
|
3047
3165
|
/**
|
|
3048
3166
|
* Registry of available bundler service factories.
|
|
@@ -3077,13 +3195,17 @@ bundlerRegistry.set("vite-react-native", () => ViteReactNativeBundlerService.get
|
|
|
3077
3195
|
* const bundler = getBundlerService('unknown'); // Returns ViteReactBundlerService
|
|
3078
3196
|
* ```
|
|
3079
3197
|
*/
|
|
3080
|
-
function getBundlerService(key) {
|
|
3198
|
+
function getBundlerService(key, workspaceRoot) {
|
|
3199
|
+
if (workspaceRoot) {
|
|
3200
|
+
if (key === "vite-react-native") return new ViteReactNativeBundlerService({}, workspaceRoot);
|
|
3201
|
+
if (key === "vite-react") return new ViteReactBundlerService({}, workspaceRoot);
|
|
3202
|
+
}
|
|
3081
3203
|
const factory = bundlerRegistry.get(key);
|
|
3082
3204
|
if (factory) return factory();
|
|
3083
|
-
return createDefaultBundlerService();
|
|
3205
|
+
return createDefaultBundlerService(workspaceRoot);
|
|
3084
3206
|
}
|
|
3085
3207
|
/** Cached bundler service instance loaded from config */
|
|
3086
|
-
let
|
|
3208
|
+
let cachedDefaultBundlerService = null;
|
|
3087
3209
|
/**
|
|
3088
3210
|
* Get bundler service based on the workspace style-system.config.yaml.
|
|
3089
3211
|
*
|
|
@@ -3107,25 +3229,31 @@ let cachedBundlerService = null;
|
|
|
3107
3229
|
* await bundler.startDevServer('apps/my-app');
|
|
3108
3230
|
* ```
|
|
3109
3231
|
*/
|
|
3110
|
-
async function getBundlerServiceFromConfig() {
|
|
3111
|
-
if (
|
|
3112
|
-
|
|
3232
|
+
async function getBundlerServiceFromConfig(options) {
|
|
3233
|
+
if (options) return loadBundlerServiceFromConfig(options);
|
|
3234
|
+
cachedDefaultBundlerService ??= await loadBundlerServiceFromConfig();
|
|
3235
|
+
return cachedDefaultBundlerService;
|
|
3236
|
+
}
|
|
3237
|
+
async function loadBundlerServiceFromConfig(options) {
|
|
3238
|
+
let cachedBundlerService = null;
|
|
3239
|
+
const fallback = options?.defaultFactory ?? (() => createDefaultBundlerService(options?.workspaceRoot));
|
|
3240
|
+
const config = options ? (await (options.configLoader ?? new StyleSystemConfigLoader(options.workspaceRoot)).loadRoot()).bundler : await getBundlerConfig();
|
|
3113
3241
|
if (!config?.customService) {
|
|
3114
|
-
cachedBundlerService =
|
|
3242
|
+
cachedBundlerService = fallback();
|
|
3115
3243
|
return cachedBundlerService;
|
|
3116
3244
|
}
|
|
3117
|
-
const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
3245
|
+
const monorepoRoot = options?.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
3118
3246
|
const customServicePath = node_path.default.resolve(monorepoRoot, config.customService);
|
|
3119
3247
|
const normalizedWorkspaceRoot = node_path.default.resolve(monorepoRoot);
|
|
3120
3248
|
if (!customServicePath.startsWith(normalizedWorkspaceRoot + node_path.default.sep)) {
|
|
3121
3249
|
log.error(`[BundlerServiceFactory] Security error: customService path "${config.customService}" resolves outside workspace root`);
|
|
3122
|
-
cachedBundlerService =
|
|
3250
|
+
cachedBundlerService = fallback();
|
|
3123
3251
|
return cachedBundlerService;
|
|
3124
3252
|
}
|
|
3125
3253
|
const ext = node_path.default.extname(customServicePath).toLowerCase();
|
|
3126
3254
|
if (!VALID_SERVICE_EXTENSIONS.includes(ext)) {
|
|
3127
3255
|
log.error(`[BundlerServiceFactory] Invalid file extension "${ext}" for customService. Expected one of: ${VALID_SERVICE_EXTENSIONS.join(", ")}`);
|
|
3128
|
-
cachedBundlerService =
|
|
3256
|
+
cachedBundlerService = fallback();
|
|
3129
3257
|
return cachedBundlerService;
|
|
3130
3258
|
}
|
|
3131
3259
|
try {
|
|
@@ -3157,7 +3285,7 @@ async function getBundlerServiceFromConfig() {
|
|
|
3157
3285
|
const message = error instanceof Error ? error.message : String(error);
|
|
3158
3286
|
log.error(`[BundlerServiceFactory] Failed to load custom bundler service: ${message}`);
|
|
3159
3287
|
log.info("[BundlerServiceFactory] Falling back to default ViteReactBundlerService");
|
|
3160
|
-
cachedBundlerService =
|
|
3288
|
+
cachedBundlerService = fallback();
|
|
3161
3289
|
return cachedBundlerService;
|
|
3162
3290
|
}
|
|
3163
3291
|
}
|
|
@@ -3202,8 +3330,8 @@ var ThemeService = class {
|
|
|
3202
3330
|
* Creates a new ThemeService instance
|
|
3203
3331
|
* @param config - Design system configuration
|
|
3204
3332
|
*/
|
|
3205
|
-
constructor(config) {
|
|
3206
|
-
this.monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
3333
|
+
constructor(config, workspaceRoot) {
|
|
3334
|
+
this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
3207
3335
|
this.config = config;
|
|
3208
3336
|
if (this.config.themeProvider) log.info(`[ThemeService] Using theme provider: ${this.config.themeProvider}`);
|
|
3209
3337
|
log.info(`[ThemeService] Design system type: ${this.config.type}`);
|
|
@@ -3366,7 +3494,8 @@ export { WrappedComponent };
|
|
|
3366
3494
|
*/
|
|
3367
3495
|
const DEFAULT_RENDER_WIDTH = 1280;
|
|
3368
3496
|
const DEFAULT_RENDER_HEIGHT = 800;
|
|
3369
|
-
|
|
3497
|
+
/** Settle time after the readiness check in takeScreenshot (mount, fonts, two frames). */
|
|
3498
|
+
const SCREENSHOT_WAIT_TIME_MS = 100;
|
|
3370
3499
|
const DEFAULT_CLEANUP_AGE_MS = 36e5;
|
|
3371
3500
|
const COMPONENT_FILE_PREFIX = "component-";
|
|
3372
3501
|
const FILE_URL_PREFIX = "file://";
|
|
@@ -3406,6 +3535,9 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3406
3535
|
themeService;
|
|
3407
3536
|
appPath;
|
|
3408
3537
|
bundlerFactory;
|
|
3538
|
+
log;
|
|
3539
|
+
preserveOutput;
|
|
3540
|
+
sharedBrowser;
|
|
3409
3541
|
/**
|
|
3410
3542
|
* Creates a new ComponentRendererService instance
|
|
3411
3543
|
* @param designSystemConfig - Design system configuration
|
|
@@ -3415,8 +3547,12 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3415
3547
|
constructor(designSystemConfig, appPath, options = {}) {
|
|
3416
3548
|
if (!appPath) throw new Error("appPath is required for ComponentRendererService");
|
|
3417
3549
|
this.appPath = appPath;
|
|
3418
|
-
this.
|
|
3419
|
-
this.
|
|
3550
|
+
this.sharedBrowser = options.sharedBrowser;
|
|
3551
|
+
this.tmpDir = options.temporaryDirectory ?? node_path.default.join(node_os.default.tmpdir(), "style-system");
|
|
3552
|
+
this.preserveOutput = options.temporaryDirectory !== void 0;
|
|
3553
|
+
if (options.temporaryDirectory !== void 0 && !node_path.default.isAbsolute(options.temporaryDirectory)) throw new Error("Renderer temporary directory must be absolute.");
|
|
3554
|
+
this.log = log;
|
|
3555
|
+
this.themeService = new ThemeService(designSystemConfig, options.workspaceRoot);
|
|
3420
3556
|
if (options.bundlerFactory) this.bundlerFactory = options.bundlerFactory;
|
|
3421
3557
|
else if (designSystemConfig.bundler) this.bundlerFactory = () => getBundlerService(designSystemConfig.bundler);
|
|
3422
3558
|
else this.bundlerFactory = createDefaultBundlerService;
|
|
@@ -3439,17 +3575,25 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3439
3575
|
async renderComponent(componentInfo, options = {}) {
|
|
3440
3576
|
const { storyName = componentInfo.stories[0] || "Default", args = {}, darkMode = false, width = DEFAULT_RENDER_WIDTH, height = DEFAULT_RENDER_HEIGHT } = options;
|
|
3441
3577
|
try {
|
|
3442
|
-
|
|
3443
|
-
|
|
3578
|
+
await node_fs.promises.mkdir(this.tmpDir, {
|
|
3579
|
+
recursive: true,
|
|
3580
|
+
mode: 448
|
|
3581
|
+
});
|
|
3582
|
+
if (this.preserveOutput) {
|
|
3583
|
+
const info = await node_fs.promises.lstat(this.tmpDir);
|
|
3584
|
+
if (!info.isDirectory() || info.isSymbolicLink() || await node_fs.promises.realpath(this.tmpDir) !== this.tmpDir) throw new Error("Unsafe renderer temporary directory.");
|
|
3585
|
+
if (this.log === log) this.log = createLog(this.tmpDir);
|
|
3586
|
+
}
|
|
3587
|
+
this.log.info(`[ComponentRendererService] Rendering ${componentInfo.title} - ${storyName}`);
|
|
3444
3588
|
const designSystemConfig = this.themeService.getConfig();
|
|
3445
|
-
log.info(`[ComponentRendererService] Using theme provider: ${designSystemConfig.themeProvider}`);
|
|
3446
|
-
log.info(`[ComponentRendererService] Design system type: ${designSystemConfig.type}`);
|
|
3447
|
-
if (!await this.themeService.validateThemeProvider()) log.warn(`[ComponentRendererService] Theme provider path may not exist: ${designSystemConfig.themeProvider}`);
|
|
3589
|
+
this.log.info(`[ComponentRendererService] Using theme provider: ${designSystemConfig.themeProvider}`);
|
|
3590
|
+
this.log.info(`[ComponentRendererService] Design system type: ${designSystemConfig.type}`);
|
|
3591
|
+
if (!await this.themeService.validateThemeProvider()) this.log.warn(`[ComponentRendererService] Theme provider path may not exist: ${designSystemConfig.themeProvider}`);
|
|
3448
3592
|
const bundlerService = this.getBundlerService();
|
|
3449
3593
|
let componentUrl;
|
|
3450
3594
|
let htmlFilePath;
|
|
3451
3595
|
if (bundlerService.isServerRunning()) {
|
|
3452
|
-
log.info("[ComponentRendererService] Using dev server for fast rendering");
|
|
3596
|
+
this.log.info("[ComponentRendererService] Using dev server for fast rendering");
|
|
3453
3597
|
try {
|
|
3454
3598
|
const result = await bundlerService.serveComponent({
|
|
3455
3599
|
componentPath: componentInfo.filePath,
|
|
@@ -3464,9 +3608,9 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3464
3608
|
});
|
|
3465
3609
|
componentUrl = result.url;
|
|
3466
3610
|
htmlFilePath = result.htmlFilePath;
|
|
3467
|
-
log.info(`[ComponentRendererService] Component served at: ${componentUrl}`);
|
|
3611
|
+
this.log.info(`[ComponentRendererService] Component served at: ${componentUrl}`);
|
|
3468
3612
|
} catch (devServerError) {
|
|
3469
|
-
log.warn(`[ComponentRendererService] Dev server failed, falling back to static build: ${devServerError instanceof Error ? devServerError.message : String(devServerError)}`);
|
|
3613
|
+
this.log.warn(`[ComponentRendererService] Dev server failed, falling back to static build: ${devServerError instanceof Error ? devServerError.message : String(devServerError)}`);
|
|
3470
3614
|
const result = await bundlerService.prerenderComponent({
|
|
3471
3615
|
componentPath: componentInfo.filePath,
|
|
3472
3616
|
storyName,
|
|
@@ -3480,10 +3624,10 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3480
3624
|
});
|
|
3481
3625
|
componentUrl = `${FILE_URL_PREFIX}${result.htmlFilePath}`;
|
|
3482
3626
|
htmlFilePath = result.htmlFilePath;
|
|
3483
|
-
log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3627
|
+
this.log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3484
3628
|
}
|
|
3485
3629
|
} else {
|
|
3486
|
-
log.info("[ComponentRendererService] No dev server running, using static build");
|
|
3630
|
+
this.log.info("[ComponentRendererService] No dev server running, using static build");
|
|
3487
3631
|
const result = await bundlerService.prerenderComponent({
|
|
3488
3632
|
componentPath: componentInfo.filePath,
|
|
3489
3633
|
storyName,
|
|
@@ -3497,7 +3641,7 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3497
3641
|
});
|
|
3498
3642
|
componentUrl = `${FILE_URL_PREFIX}${result.htmlFilePath}`;
|
|
3499
3643
|
htmlFilePath = result.htmlFilePath;
|
|
3500
|
-
log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3644
|
+
this.log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
|
|
3501
3645
|
}
|
|
3502
3646
|
const timestamp = Date.now();
|
|
3503
3647
|
const imageName = `${COMPONENT_FILE_PREFIX}${componentInfo.title.split("/").pop()}-${timestamp}-${(0, node_crypto.randomUUID)()}.png`;
|
|
@@ -3507,20 +3651,21 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3507
3651
|
output: imagePath,
|
|
3508
3652
|
width,
|
|
3509
3653
|
height,
|
|
3510
|
-
fullPage:
|
|
3654
|
+
fullPage: false,
|
|
3511
3655
|
browser: "chromium",
|
|
3512
3656
|
waitTime: SCREENSHOT_WAIT_TIME_MS,
|
|
3513
3657
|
darkMode,
|
|
3514
3658
|
mobile: false,
|
|
3515
3659
|
generateThumbnail: false,
|
|
3516
|
-
base64: false
|
|
3660
|
+
base64: false,
|
|
3661
|
+
sharedBrowser: this.sharedBrowser
|
|
3517
3662
|
});
|
|
3518
|
-
log.info(`[ComponentRendererService] Screenshot saved to: ${imagePath}`);
|
|
3663
|
+
this.log.info(`[ComponentRendererService] Screenshot saved to: ${imagePath}`);
|
|
3519
3664
|
let html = "";
|
|
3520
3665
|
if (htmlFilePath) {
|
|
3521
3666
|
html = await node_fs.promises.readFile(htmlFilePath, "utf-8");
|
|
3522
|
-
log.info(`[ComponentRendererService] HTML file kept at: ${htmlFilePath}`);
|
|
3523
|
-
} else log.warn("[ComponentRendererService] No HTML file path available, returning empty HTML content");
|
|
3667
|
+
this.log.info(`[ComponentRendererService] HTML file kept at: ${htmlFilePath}`);
|
|
3668
|
+
} else this.log.warn("[ComponentRendererService] No HTML file path available, returning empty HTML content");
|
|
3524
3669
|
return {
|
|
3525
3670
|
imagePath,
|
|
3526
3671
|
html,
|
|
@@ -3563,20 +3708,20 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3563
3708
|
};
|
|
3564
3709
|
}));
|
|
3565
3710
|
for (const result of statResults) if (result.status === "fulfilled") componentFiles.push(result.value);
|
|
3566
|
-
else log.warn("[ComponentRendererService] Failed to inspect component file during cleanup:", result.reason);
|
|
3711
|
+
else this.log.warn("[ComponentRendererService] Failed to inspect component file during cleanup:", result.reason);
|
|
3567
3712
|
const oldFiles = componentFiles.filter((f) => now - f.mtime > olderThanMs);
|
|
3568
|
-
await Promise.all(oldFiles.map((file) => node_fs.promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3713
|
+
await Promise.all(oldFiles.map((file) => node_fs.promises.unlink(file.path).catch((err) => this.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3569
3714
|
let deletedCount = oldFiles.length;
|
|
3570
3715
|
const remainingFiles = componentFiles.filter((f) => now - f.mtime <= olderThanMs);
|
|
3571
3716
|
if (remainingFiles.length > keepCount) {
|
|
3572
3717
|
remainingFiles.sort((a, b) => a.mtime - b.mtime);
|
|
3573
3718
|
const toDelete = remainingFiles.slice(0, remainingFiles.length - keepCount);
|
|
3574
|
-
await Promise.all(toDelete.map((file) => node_fs.promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3719
|
+
await Promise.all(toDelete.map((file) => node_fs.promises.unlink(file.path).catch((err) => this.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
3575
3720
|
deletedCount += toDelete.length;
|
|
3576
3721
|
}
|
|
3577
|
-
if (deletedCount > 0) log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
|
|
3722
|
+
if (deletedCount > 0) this.log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
|
|
3578
3723
|
} catch (error) {
|
|
3579
|
-
log.error("[ComponentRendererService] Cleanup error:", error);
|
|
3724
|
+
this.log.error("[ComponentRendererService] Cleanup error:", error);
|
|
3580
3725
|
}
|
|
3581
3726
|
}
|
|
3582
3727
|
/**
|
|
@@ -3586,11 +3731,11 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
3586
3731
|
*/
|
|
3587
3732
|
async dispose() {
|
|
3588
3733
|
try {
|
|
3589
|
-
await this.cleanup(void 0, ComponentRendererService.DISPOSE_KEEP_COUNT);
|
|
3734
|
+
if (!this.preserveOutput) await this.cleanup(void 0, ComponentRendererService.DISPOSE_KEEP_COUNT);
|
|
3590
3735
|
const bundlerService = this.getBundlerService();
|
|
3591
3736
|
if (!bundlerService.isServerRunning()) await bundlerService.cleanup();
|
|
3592
3737
|
} catch (error) {
|
|
3593
|
-
log.error("[ComponentRendererService] Dispose error:", error);
|
|
3738
|
+
this.log.error("[ComponentRendererService] Dispose error:", error);
|
|
3594
3739
|
}
|
|
3595
3740
|
}
|
|
3596
3741
|
};
|
|
@@ -3606,13 +3751,6 @@ const DEFAULT_GET_UI_COMPONENT_CONFIG = {
|
|
|
3606
3751
|
defaultHeight: 800
|
|
3607
3752
|
};
|
|
3608
3753
|
//#endregion
|
|
3609
|
-
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
|
|
3610
|
-
function __decorateParam(paramIndex, decorator) {
|
|
3611
|
-
return function(target, key) {
|
|
3612
|
-
decorator(target, key, paramIndex);
|
|
3613
|
-
};
|
|
3614
|
-
}
|
|
3615
|
-
//#endregion
|
|
3616
3754
|
//#region src/services/GetUiComponentService/GetUiComponentService.ts
|
|
3617
3755
|
/**
|
|
3618
3756
|
* GetUiComponentService
|
|
@@ -3637,6 +3775,7 @@ const NATIVE_DEFAULT_VIEWPORT = {
|
|
|
3637
3775
|
height: 852
|
|
3638
3776
|
};
|
|
3639
3777
|
let GetUiComponentService = class GetUiComponentService {
|
|
3778
|
+
configLoader;
|
|
3640
3779
|
config;
|
|
3641
3780
|
storiesIndexFactory;
|
|
3642
3781
|
rendererFactory;
|
|
@@ -3646,7 +3785,8 @@ let GetUiComponentService = class GetUiComponentService {
|
|
|
3646
3785
|
* @param storiesIndexFactory - Factory for creating StoriesIndexService (for DI/testing)
|
|
3647
3786
|
* @param rendererFactory - Factory for creating ComponentRendererService (for DI/testing)
|
|
3648
3787
|
*/
|
|
3649
|
-
constructor(config = {}, storiesIndexFactory = () => new StoriesIndexService(), rendererFactory = (c, p) => new ComponentRendererService(c, p)) {
|
|
3788
|
+
constructor(config = {}, storiesIndexFactory = () => new StoriesIndexService(), rendererFactory = (c, p) => new ComponentRendererService(c, p), configLoader) {
|
|
3789
|
+
this.configLoader = configLoader;
|
|
3650
3790
|
this.config = {
|
|
3651
3791
|
...DEFAULT_GET_UI_COMPONENT_CONFIG,
|
|
3652
3792
|
...config
|
|
@@ -3678,7 +3818,7 @@ let GetUiComponentService = class GetUiComponentService {
|
|
|
3678
3818
|
const storiesIndex = this.storiesIndexFactory();
|
|
3679
3819
|
let designSystemConfig;
|
|
3680
3820
|
try {
|
|
3681
|
-
[, designSystemConfig] = await Promise.all([storiesIndex.initialize(), getAppDesignSystemConfig(appPath)]);
|
|
3821
|
+
[, designSystemConfig] = await Promise.all([storiesIndex.initialize(), this.configLoader ? this.configLoader.resolveProject(appPath).then((resolved) => resolved.config) : getAppDesignSystemConfig(appPath)]);
|
|
3682
3822
|
} catch (error) {
|
|
3683
3823
|
throw new Error(`Failed to initialize stories index or design system config: ${error instanceof Error ? error.message : String(error)}`);
|
|
3684
3824
|
}
|
|
@@ -3778,10 +3918,13 @@ GetUiComponentService = __decorate([
|
|
|
3778
3918
|
__decorateParam(1, (0, inversify.optional)()),
|
|
3779
3919
|
__decorateParam(2, (0, inversify.inject)(STYLE_SYSTEM_TYPES.RendererFactory)),
|
|
3780
3920
|
__decorateParam(2, (0, inversify.optional)()),
|
|
3921
|
+
__decorateParam(3, (0, inversify.inject)(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
3922
|
+
__decorateParam(3, (0, inversify.optional)()),
|
|
3781
3923
|
__decorateMetadata("design:paramtypes", [
|
|
3782
3924
|
Object,
|
|
3783
3925
|
Object,
|
|
3784
|
-
Object
|
|
3926
|
+
Object,
|
|
3927
|
+
typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader
|
|
3785
3928
|
])
|
|
3786
3929
|
], GetUiComponentService);
|
|
3787
3930
|
//#endregion
|
|
@@ -3810,12 +3953,14 @@ const DEFAULT_APP_COMPONENTS_CONFIG = { pageSize: 50 };
|
|
|
3810
3953
|
* - Direct tool implementation (services should be tool-agnostic)
|
|
3811
3954
|
*/
|
|
3812
3955
|
let AppComponentsService = class AppComponentsService {
|
|
3956
|
+
workspaceRoot;
|
|
3813
3957
|
config;
|
|
3814
3958
|
/**
|
|
3815
3959
|
* Creates a new AppComponentsService instance.
|
|
3816
3960
|
* @param config - Service configuration options
|
|
3817
3961
|
*/
|
|
3818
|
-
constructor(config = {}) {
|
|
3962
|
+
constructor(config = {}, workspaceRoot) {
|
|
3963
|
+
this.workspaceRoot = workspaceRoot;
|
|
3819
3964
|
this.config = {
|
|
3820
3965
|
...DEFAULT_APP_COMPONENTS_CONFIG,
|
|
3821
3966
|
...config
|
|
@@ -3832,14 +3977,14 @@ let AppComponentsService = class AppComponentsService {
|
|
|
3832
3977
|
if (input.cursor !== void 0 && typeof input.cursor !== "string") throw new Error("cursor must be a string");
|
|
3833
3978
|
const { appPath, cursor } = input;
|
|
3834
3979
|
const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
|
|
3835
|
-
const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
3980
|
+
const monorepoRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
|
|
3836
3981
|
const resolvedAppPath = node_path.default.isAbsolute(appPath) ? appPath : node_path.default.join(monorepoRoot, appPath);
|
|
3837
3982
|
try {
|
|
3838
3983
|
await node_fs.promises.access(resolvedAppPath);
|
|
3839
3984
|
} catch {
|
|
3840
3985
|
throw new Error(`App path does not exist: ${resolvedAppPath}`);
|
|
3841
3986
|
}
|
|
3842
|
-
const storiesIndex = new StoriesIndexService();
|
|
3987
|
+
const storiesIndex = new StoriesIndexService({ workspaceRoot: monorepoRoot });
|
|
3843
3988
|
const [appName, workspaceDependencies, packageMap] = await Promise.all([
|
|
3844
3989
|
this.getAppName(resolvedAppPath),
|
|
3845
3990
|
this.getWorkspaceDependencies(resolvedAppPath),
|
|
@@ -4042,18 +4187,42 @@ AppComponentsService = __decorate([
|
|
|
4042
4187
|
(0, inversify.injectable)(),
|
|
4043
4188
|
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.AppComponentsServiceConfig)),
|
|
4044
4189
|
__decorateParam(0, (0, inversify.optional)()),
|
|
4045
|
-
|
|
4190
|
+
__decorateParam(1, (0, inversify.inject)(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
|
|
4191
|
+
__decorateParam(1, (0, inversify.optional)()),
|
|
4192
|
+
__decorateMetadata("design:paramtypes", [Object, String])
|
|
4046
4193
|
], AppComponentsService);
|
|
4047
4194
|
//#endregion
|
|
4048
4195
|
//#region src/tools/ListThemesTool.ts
|
|
4196
|
+
/**
|
|
4197
|
+
* ListThemesTool
|
|
4198
|
+
*
|
|
4199
|
+
* DESIGN PATTERNS:
|
|
4200
|
+
* - Tool pattern with getDefinition() and execute() methods
|
|
4201
|
+
* - Factory pattern for service creation
|
|
4202
|
+
* - JSON Schema validation for inputs
|
|
4203
|
+
*
|
|
4204
|
+
* CODING STANDARDS:
|
|
4205
|
+
* - Implement Tool interface from ../types
|
|
4206
|
+
* - Use TOOL_NAME constant with snake_case (e.g., 'list_themes')
|
|
4207
|
+
* - Return CallToolResult with content array
|
|
4208
|
+
* - Handle errors with isError flag
|
|
4209
|
+
* - Delegate complex logic to services
|
|
4210
|
+
*
|
|
4211
|
+
* AVOID:
|
|
4212
|
+
* - Complex business logic in execute method
|
|
4213
|
+
* - Unhandled promise rejections
|
|
4214
|
+
* - Missing input validation
|
|
4215
|
+
*/
|
|
4049
4216
|
var _ListThemesTool;
|
|
4050
4217
|
let ListThemesTool = class ListThemesTool {
|
|
4051
4218
|
static {
|
|
4052
4219
|
_ListThemesTool = this;
|
|
4053
4220
|
}
|
|
4221
|
+
configLoader;
|
|
4054
4222
|
static TOOL_NAME = "list_themes";
|
|
4055
4223
|
serviceFactory;
|
|
4056
|
-
constructor(serviceFactory) {
|
|
4224
|
+
constructor(serviceFactory, configLoader) {
|
|
4225
|
+
this.configLoader = configLoader;
|
|
4057
4226
|
this.serviceFactory = serviceFactory;
|
|
4058
4227
|
}
|
|
4059
4228
|
getDefinition() {
|
|
@@ -4075,12 +4244,13 @@ let ListThemesTool = class ListThemesTool {
|
|
|
4075
4244
|
let themePath;
|
|
4076
4245
|
let cssFiles;
|
|
4077
4246
|
if (input.appPath) {
|
|
4078
|
-
const appConfig = await getAppDesignSystemConfig(input.appPath);
|
|
4247
|
+
const appConfig = this.configLoader ? (await this.configLoader.resolveProject(input.appPath)).config : await getAppDesignSystemConfig(input.appPath);
|
|
4079
4248
|
themePath = appConfig.themePath ? await resolveStyleSystemThemePath({
|
|
4080
4249
|
appPath: input.appPath,
|
|
4081
|
-
themePath: appConfig.themePath
|
|
4250
|
+
themePath: appConfig.themePath,
|
|
4251
|
+
workspaceRoot: this.configLoader?.workspaceRoot
|
|
4082
4252
|
}) : void 0;
|
|
4083
|
-
cssFiles = themePath ? void 0 : appConfig.cssFiles;
|
|
4253
|
+
cssFiles = themePath ? void 0 : appConfig.cssFiles?.map((file) => node_path.default.resolve(this.configLoader?.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(), input.appPath, file));
|
|
4084
4254
|
}
|
|
4085
4255
|
const result = await (await this.serviceFactory.createService({
|
|
4086
4256
|
themePath,
|
|
@@ -4104,7 +4274,9 @@ let ListThemesTool = class ListThemesTool {
|
|
|
4104
4274
|
ListThemesTool = _ListThemesTool = __decorate([
|
|
4105
4275
|
(0, inversify.injectable)(),
|
|
4106
4276
|
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.ThemeServiceFactory)),
|
|
4107
|
-
|
|
4277
|
+
__decorateParam(1, (0, inversify.inject)(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
4278
|
+
__decorateParam(1, (0, inversify.optional)()),
|
|
4279
|
+
__decorateMetadata("design:paramtypes", [typeof ThemeServiceFactory === "undefined" ? Object : ThemeServiceFactory, typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader])
|
|
4108
4280
|
], ListThemesTool);
|
|
4109
4281
|
//#endregion
|
|
4110
4282
|
//#region src/tools/GetCSSClassesTool.ts
|
|
@@ -4131,6 +4303,7 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4131
4303
|
static {
|
|
4132
4304
|
_GetCSSClassesTool = this;
|
|
4133
4305
|
}
|
|
4306
|
+
configLoader;
|
|
4134
4307
|
static TOOL_NAME = "get_css_classes";
|
|
4135
4308
|
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.";
|
|
4136
4309
|
serviceFactory;
|
|
@@ -4140,7 +4313,8 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4140
4313
|
* @param serviceFactory - Injected CSSClassesServiceFactory
|
|
4141
4314
|
* @param defaultThemePath - Default path to theme CSS file (relative to workspace root)
|
|
4142
4315
|
*/
|
|
4143
|
-
constructor(serviceFactory, defaultThemePath) {
|
|
4316
|
+
constructor(serviceFactory, defaultThemePath, configLoader) {
|
|
4317
|
+
this.configLoader = configLoader;
|
|
4144
4318
|
this.serviceFactory = serviceFactory;
|
|
4145
4319
|
this.defaultThemePath = defaultThemePath;
|
|
4146
4320
|
}
|
|
@@ -4185,7 +4359,7 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4185
4359
|
const category = input.category || "all";
|
|
4186
4360
|
if (!isValidCategory(category)) throw new Error(`Invalid category: '${category}'. Must be one of: ${VALID_CATEGORIES.join(", ")}`);
|
|
4187
4361
|
const themePath = await this.resolveThemePath(input.appPath);
|
|
4188
|
-
const workspaceConfig = await getGetCssClassesConfig();
|
|
4362
|
+
const workspaceConfig = this.configLoader ? (await this.configLoader.loadRoot()).getCssClasses : await getGetCssClassesConfig();
|
|
4189
4363
|
const result = await (await this.serviceFactory.createService({ customServicePath: workspaceConfig?.customService })).extractClasses(category, themePath);
|
|
4190
4364
|
return { content: [{
|
|
4191
4365
|
type: "text",
|
|
@@ -4214,13 +4388,17 @@ let GetCSSClassesTool = class GetCSSClassesTool {
|
|
|
4214
4388
|
*/
|
|
4215
4389
|
async resolveThemePath(appPath) {
|
|
4216
4390
|
if (appPath) {
|
|
4217
|
-
const config = await getAppDesignSystemConfig(appPath);
|
|
4391
|
+
const config = this.configLoader ? (await this.configLoader.resolveProject(appPath)).config : await getAppDesignSystemConfig(appPath);
|
|
4218
4392
|
if (config.themePath) return await resolveStyleSystemThemePath({
|
|
4219
4393
|
appPath,
|
|
4220
|
-
themePath: config.themePath
|
|
4394
|
+
themePath: config.themePath,
|
|
4395
|
+
workspaceRoot: this.configLoader?.workspaceRoot
|
|
4221
4396
|
});
|
|
4222
4397
|
}
|
|
4223
|
-
if (this.defaultThemePath) return await resolveStyleSystemThemePath({
|
|
4398
|
+
if (this.defaultThemePath) return await resolveStyleSystemThemePath({
|
|
4399
|
+
themePath: this.defaultThemePath,
|
|
4400
|
+
workspaceRoot: this.configLoader?.workspaceRoot
|
|
4401
|
+
});
|
|
4224
4402
|
throw new Error("No theme CSS path configured. Pass --theme-path, or set themePath in the target app style-system.config.yaml.");
|
|
4225
4403
|
}
|
|
4226
4404
|
};
|
|
@@ -4229,156 +4407,15 @@ GetCSSClassesTool = _GetCSSClassesTool = __decorate([
|
|
|
4229
4407
|
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory)),
|
|
4230
4408
|
__decorateParam(1, (0, inversify.inject)(STYLE_SYSTEM_TYPES.DefaultThemePath)),
|
|
4231
4409
|
__decorateParam(1, (0, inversify.optional)()),
|
|
4232
|
-
|
|
4410
|
+
__decorateParam(2, (0, inversify.inject)(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
4411
|
+
__decorateParam(2, (0, inversify.optional)()),
|
|
4412
|
+
__decorateMetadata("design:paramtypes", [
|
|
4413
|
+
typeof CSSClassesServiceFactory === "undefined" ? Object : CSSClassesServiceFactory,
|
|
4414
|
+
String,
|
|
4415
|
+
typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader
|
|
4416
|
+
])
|
|
4233
4417
|
], GetCSSClassesTool);
|
|
4234
4418
|
//#endregion
|
|
4235
|
-
//#region src/services/AntigravityCliService.ts
|
|
4236
|
-
const DEFAULT_MODEL = "gemini-3.6-flash-high";
|
|
4237
|
-
const DEFAULT_TIMEOUT = 18e5;
|
|
4238
|
-
const MAX_BUFFER = 104857600;
|
|
4239
|
-
const FAILURE_SNIPPET_LENGTH = 400;
|
|
4240
|
-
const ANTIGRAVITY_CLI_COMMAND = "agy";
|
|
4241
|
-
const ANTIGRAVITY_PROVIDER_NAME = "antigravity";
|
|
4242
|
-
const ANTIGRAVITY_UNAVAILABLE_MESSAGE = "Antigravity CLI not found. Run `agy install` to configure it.";
|
|
4243
|
-
function sanitizePrompt(raw) {
|
|
4244
|
-
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, "");
|
|
4245
|
-
}
|
|
4246
|
-
function tailSnippet(value) {
|
|
4247
|
-
const trimmed = value.trim();
|
|
4248
|
-
if (!trimmed) return "";
|
|
4249
|
-
return trimmed.length <= FAILURE_SNIPPET_LENGTH ? trimmed : `...${trimmed.slice(-400)}`;
|
|
4250
|
-
}
|
|
4251
|
-
var AntigravityCliService = class {
|
|
4252
|
-
isAvailable() {
|
|
4253
|
-
try {
|
|
4254
|
-
(0, node_child_process.execSync)(`${ANTIGRAVITY_CLI_COMMAND} --version`, {
|
|
4255
|
-
stdio: "ignore",
|
|
4256
|
-
timeout: 5e3,
|
|
4257
|
-
env: process.env
|
|
4258
|
-
});
|
|
4259
|
-
return true;
|
|
4260
|
-
} catch {
|
|
4261
|
-
return false;
|
|
4262
|
-
}
|
|
4263
|
-
}
|
|
4264
|
-
async edit(params) {
|
|
4265
|
-
if (!this.isAvailable()) throw new Error(ANTIGRAVITY_UNAVAILABLE_MESSAGE);
|
|
4266
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
4267
|
-
const model = params.model ?? DEFAULT_MODEL;
|
|
4268
|
-
const timeout = params.timeout ?? DEFAULT_TIMEOUT;
|
|
4269
|
-
let fullPrompt = sanitizePrompt(params.prompt);
|
|
4270
|
-
if (params.systemPrompt) fullPrompt = `${sanitizePrompt(params.systemPrompt)}\n\n${fullPrompt}`;
|
|
4271
|
-
if (params.screenshotPaths?.length) fullPrompt = `${params.screenshotPaths.map((path) => `@${(0, node_path.basename)(path)}`).join(" ")}\n\n${fullPrompt}`;
|
|
4272
|
-
const args = [
|
|
4273
|
-
"--new-project",
|
|
4274
|
-
"--dangerously-skip-permissions",
|
|
4275
|
-
"--print",
|
|
4276
|
-
"--print-timeout",
|
|
4277
|
-
`${Math.ceil(timeout / 1e3)}s`,
|
|
4278
|
-
"--model",
|
|
4279
|
-
model,
|
|
4280
|
-
fullPrompt
|
|
4281
|
-
];
|
|
4282
|
-
return {
|
|
4283
|
-
content: (await this.execAntigravity(args, workspaceRoot, model, timeout)).trim(),
|
|
4284
|
-
model
|
|
4285
|
-
};
|
|
4286
|
-
}
|
|
4287
|
-
execAntigravity(args, cwd, model, timeout) {
|
|
4288
|
-
return new Promise((resolve, reject) => {
|
|
4289
|
-
const chunks = [];
|
|
4290
|
-
let totalBytes = 0;
|
|
4291
|
-
let settled = false;
|
|
4292
|
-
let stderrContent = "";
|
|
4293
|
-
const registeredArgs = [...args];
|
|
4294
|
-
registeredArgs[registeredArgs.length - 1] = "<prompt>";
|
|
4295
|
-
const child = (0, node_child_process.spawn)(ANTIGRAVITY_CLI_COMMAND, args, {
|
|
4296
|
-
stdio: [
|
|
4297
|
-
"ignore",
|
|
4298
|
-
"pipe",
|
|
4299
|
-
"pipe"
|
|
4300
|
-
],
|
|
4301
|
-
env: {
|
|
4302
|
-
...process.env,
|
|
4303
|
-
OTEL_SERVICE_NAME: ANTIGRAVITY_PROVIDER_NAME
|
|
4304
|
-
},
|
|
4305
|
-
cwd
|
|
4306
|
-
});
|
|
4307
|
-
let releaseChild;
|
|
4308
|
-
let registrationPromise;
|
|
4309
|
-
if (child.pid) registrationPromise = (0, _agimon_ai_foundation_exec.registerManagedProcess)({
|
|
4310
|
-
repositoryPath: cwd,
|
|
4311
|
-
serviceName: `style-system-antigravity-${child.pid}`,
|
|
4312
|
-
serviceType: "tool",
|
|
4313
|
-
environment: process.env["NODE_ENV"] ?? "development",
|
|
4314
|
-
pid: child.pid,
|
|
4315
|
-
command: ANTIGRAVITY_CLI_COMMAND,
|
|
4316
|
-
args: registeredArgs,
|
|
4317
|
-
metadata: {
|
|
4318
|
-
model,
|
|
4319
|
-
parentPid: process.pid
|
|
4320
|
-
},
|
|
4321
|
-
killOnRelease: true,
|
|
4322
|
-
releasePortOnRelease: false
|
|
4323
|
-
}).then((release) => {
|
|
4324
|
-
releaseChild = release;
|
|
4325
|
-
}).catch((error) => {
|
|
4326
|
-
log.debug(`[AntigravityCliService] Process registration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4327
|
-
});
|
|
4328
|
-
const finalize = async (callback) => {
|
|
4329
|
-
try {
|
|
4330
|
-
await registrationPromise;
|
|
4331
|
-
await releaseChild?.();
|
|
4332
|
-
} catch (error) {
|
|
4333
|
-
log.debug(`[AntigravityCliService] Process cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4334
|
-
} finally {
|
|
4335
|
-
callback();
|
|
4336
|
-
}
|
|
4337
|
-
};
|
|
4338
|
-
const timer = setTimeout(() => {
|
|
4339
|
-
if (settled) return;
|
|
4340
|
-
settled = true;
|
|
4341
|
-
child.kill("SIGTERM");
|
|
4342
|
-
finalize(() => reject(/* @__PURE__ */ new Error(`Antigravity CLI timed out after ${timeout}ms.`)));
|
|
4343
|
-
}, timeout);
|
|
4344
|
-
child.stdout.on("data", (chunk) => {
|
|
4345
|
-
totalBytes += chunk.length;
|
|
4346
|
-
if (totalBytes > MAX_BUFFER) {
|
|
4347
|
-
if (!settled) {
|
|
4348
|
-
settled = true;
|
|
4349
|
-
child.kill("SIGTERM");
|
|
4350
|
-
finalize(() => reject(/* @__PURE__ */ new Error("Antigravity CLI output exceeded maximum buffer size.")));
|
|
4351
|
-
}
|
|
4352
|
-
return;
|
|
4353
|
-
}
|
|
4354
|
-
chunks.push(chunk);
|
|
4355
|
-
});
|
|
4356
|
-
child.stderr.on("data", (chunk) => {
|
|
4357
|
-
stderrContent += chunk.toString();
|
|
4358
|
-
});
|
|
4359
|
-
child.on("error", (error) => {
|
|
4360
|
-
clearTimeout(timer);
|
|
4361
|
-
if (settled) return;
|
|
4362
|
-
settled = true;
|
|
4363
|
-
const message = error.code === "ENOENT" ? ANTIGRAVITY_UNAVAILABLE_MESSAGE : `Failed to invoke Antigravity CLI: ${error.message}`;
|
|
4364
|
-
finalize(() => reject(new Error(message)));
|
|
4365
|
-
});
|
|
4366
|
-
child.on("close", (code) => {
|
|
4367
|
-
clearTimeout(timer);
|
|
4368
|
-
if (settled) return;
|
|
4369
|
-
settled = true;
|
|
4370
|
-
const stdoutContent = Buffer.concat(chunks).toString("utf-8");
|
|
4371
|
-
if (code !== 0) {
|
|
4372
|
-
const detail = tailSnippet(stderrContent) || tailSnippet(stdoutContent) || `exit code ${code}`;
|
|
4373
|
-
finalize(() => reject(new Error(detail)));
|
|
4374
|
-
return;
|
|
4375
|
-
}
|
|
4376
|
-
finalize(() => resolve(stdoutContent));
|
|
4377
|
-
});
|
|
4378
|
-
});
|
|
4379
|
-
}
|
|
4380
|
-
};
|
|
4381
|
-
//#endregion
|
|
4382
4419
|
//#region src/tools/GetComponentVisualTool.ts
|
|
4383
4420
|
var _GetComponentVisualTool;
|
|
4384
4421
|
/**
|
|
@@ -4507,11 +4544,13 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
4507
4544
|
static {
|
|
4508
4545
|
_ListSharedComponentsTool = this;
|
|
4509
4546
|
}
|
|
4547
|
+
configLoader;
|
|
4510
4548
|
static TOOL_NAME = "list_shared_components";
|
|
4511
4549
|
static PAGE_SIZE = 50;
|
|
4512
4550
|
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.";
|
|
4513
4551
|
storiesIndexFactory;
|
|
4514
|
-
constructor(storiesIndexFactory) {
|
|
4552
|
+
constructor(storiesIndexFactory, configLoader) {
|
|
4553
|
+
this.configLoader = configLoader;
|
|
4515
4554
|
this.storiesIndexFactory = storiesIndexFactory;
|
|
4516
4555
|
}
|
|
4517
4556
|
/**
|
|
@@ -4570,7 +4609,7 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
4570
4609
|
const { cursor, tags: inputTags } = input;
|
|
4571
4610
|
const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
|
|
4572
4611
|
const storiesIndex = this.storiesIndexFactory();
|
|
4573
|
-
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
|
|
4612
|
+
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), this.configLoader ? this.configLoader.loadRoot().then((root) => root.sharedComponentTags ?? DEFAULT_SHARED_COMPONENT_TAGS) : getSharedComponentTags()]);
|
|
4574
4613
|
const availableTags = storiesIndex.getAllTags();
|
|
4575
4614
|
const filterTags = inputTags !== void 0 ? inputTags : defaultTags;
|
|
4576
4615
|
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();
|
|
@@ -4608,7 +4647,9 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
4608
4647
|
ListSharedComponentsTool = _ListSharedComponentsTool = __decorate([
|
|
4609
4648
|
(0, inversify.injectable)(),
|
|
4610
4649
|
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.StoriesIndexFactory)),
|
|
4611
|
-
|
|
4650
|
+
__decorateParam(1, (0, inversify.inject)(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
|
|
4651
|
+
__decorateParam(1, (0, inversify.optional)()),
|
|
4652
|
+
__decorateMetadata("design:paramtypes", [Object, typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader])
|
|
4612
4653
|
], ListSharedComponentsTool);
|
|
4613
4654
|
//#endregion
|
|
4614
4655
|
//#region src/tools/ListAppComponentsTool.ts
|
|
@@ -4681,309 +4722,31 @@ ListAppComponentsTool = _ListAppComponentsTool = __decorate([
|
|
|
4681
4722
|
__decorateMetadata("design:paramtypes", [typeof AppComponentsService === "undefined" ? Object : AppComponentsService])
|
|
4682
4723
|
], ListAppComponentsTool);
|
|
4683
4724
|
//#endregion
|
|
4684
|
-
//#region src/tools/PolishComponentTool.ts
|
|
4685
|
-
/**
|
|
4686
|
-
* PolishComponentTool
|
|
4687
|
-
*
|
|
4688
|
-
* DESIGN PATTERNS:
|
|
4689
|
-
* - Tool pattern with getDefinition() and execute() methods
|
|
4690
|
-
* - Service delegation for business logic
|
|
4691
|
-
* - JSON Schema validation for inputs
|
|
4692
|
-
*
|
|
4693
|
-
* CODING STANDARDS:
|
|
4694
|
-
* - Implement Tool interface from ../types
|
|
4695
|
-
* - Use TOOL_NAME constant with snake_case
|
|
4696
|
-
* - Return CallToolResult with content array
|
|
4697
|
-
* - Handle errors with isError flag
|
|
4698
|
-
* - Delegate complex logic to services
|
|
4699
|
-
*
|
|
4700
|
-
* AVOID:
|
|
4701
|
-
* - Complex business logic in execute method
|
|
4702
|
-
* - Unhandled promise rejections
|
|
4703
|
-
* - Missing input validation
|
|
4704
|
-
*/
|
|
4705
|
-
var _PolishComponentTool;
|
|
4706
|
-
let PolishComponentTool = class PolishComponentTool {
|
|
4707
|
-
static {
|
|
4708
|
-
_PolishComponentTool = this;
|
|
4709
|
-
}
|
|
4710
|
-
static TOOL_NAME = "polish_component";
|
|
4711
|
-
antigravityService;
|
|
4712
|
-
getUiComponentService;
|
|
4713
|
-
cssClassesServiceFactory;
|
|
4714
|
-
storiesIndexFactory;
|
|
4715
|
-
constructor(getUiComponentService, cssClassesServiceFactory, storiesIndexFactory) {
|
|
4716
|
-
this.antigravityService = new AntigravityCliService();
|
|
4717
|
-
this.getUiComponentService = getUiComponentService;
|
|
4718
|
-
this.cssClassesServiceFactory = cssClassesServiceFactory;
|
|
4719
|
-
this.storiesIndexFactory = storiesIndexFactory;
|
|
4720
|
-
}
|
|
4721
|
-
getDefinition() {
|
|
4722
|
-
return {
|
|
4723
|
-
name: _PolishComponentTool.TOOL_NAME,
|
|
4724
|
-
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.",
|
|
4725
|
-
inputSchema: {
|
|
4726
|
-
type: "object",
|
|
4727
|
-
properties: {
|
|
4728
|
-
filePath: {
|
|
4729
|
-
type: "string",
|
|
4730
|
-
minLength: 1,
|
|
4731
|
-
description: "Path to the component .tsx file to polish (e.g., \"packages/frontend/web-atoms/src/components/Button/index.tsx\")"
|
|
4732
|
-
},
|
|
4733
|
-
instructions: {
|
|
4734
|
-
type: "string",
|
|
4735
|
-
description: "Specific polishing instructions (e.g., \"improve spacing\", \"add hover states\", \"fix dark mode\")"
|
|
4736
|
-
}
|
|
4737
|
-
},
|
|
4738
|
-
required: ["filePath"],
|
|
4739
|
-
additionalProperties: false
|
|
4740
|
-
}
|
|
4741
|
-
};
|
|
4742
|
-
}
|
|
4743
|
-
async execute(input) {
|
|
4744
|
-
try {
|
|
4745
|
-
if (!this.antigravityService.isAvailable()) return {
|
|
4746
|
-
content: [{
|
|
4747
|
-
type: "text",
|
|
4748
|
-
text: "Error: Antigravity CLI is not installed. Run `agy install` to configure it."
|
|
4749
|
-
}],
|
|
4750
|
-
isError: true
|
|
4751
|
-
};
|
|
4752
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
4753
|
-
const { instructions } = input;
|
|
4754
|
-
const filePath = node_path.default.isAbsolute(input.filePath) ? input.filePath : node_path.default.join(workspaceRoot, input.filePath);
|
|
4755
|
-
const componentDir = node_path.default.dirname(filePath);
|
|
4756
|
-
const storyFile = await this.findStoriesFile(componentDir);
|
|
4757
|
-
if (!storyFile) return {
|
|
4758
|
-
content: [{
|
|
4759
|
-
type: "text",
|
|
4760
|
-
text: `Error: No .stories.tsx file found in ${componentDir}`
|
|
4761
|
-
}],
|
|
4762
|
-
isError: true
|
|
4763
|
-
};
|
|
4764
|
-
const componentName = node_path.default.basename(componentDir);
|
|
4765
|
-
const appPath = await this.findAppPath(filePath, workspaceRoot);
|
|
4766
|
-
const dsConfig = appPath ? await getAppDesignSystemConfig(appPath).catch((error) => {
|
|
4767
|
-
log.debug(`[PolishComponentTool] Could not load design system config for ${appPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4768
|
-
return null;
|
|
4769
|
-
}) : null;
|
|
4770
|
-
const platform = this.detectPlatform(dsConfig);
|
|
4771
|
-
log.info(`[PolishComponentTool] Polishing ${filePath}, stories: ${storyFile}, appPath: ${appPath}, platform: ${platform}`);
|
|
4772
|
-
const [componentVisual, designPrinciples, sharedComponents, componentSource, designTokens] = await Promise.allSettled([
|
|
4773
|
-
appPath ? this.getComponentVisual(componentName, appPath) : Promise.reject(/* @__PURE__ */ new Error("No appPath")),
|
|
4774
|
-
appPath ? this.getDesignPrinciples(appPath) : Promise.reject(/* @__PURE__ */ new Error("No appPath")),
|
|
4775
|
-
this.getSharedComponents(),
|
|
4776
|
-
node_fs.promises.readFile(filePath, "utf-8"),
|
|
4777
|
-
this.getDesignTokens(platform, dsConfig, appPath)
|
|
4778
|
-
]);
|
|
4779
|
-
const contextSections = [];
|
|
4780
|
-
if (componentSource.status === "fulfilled") contextSections.push(`## Component Source\n\nFile: ${filePath}\n\n\`\`\`tsx\n${componentSource.value}\n\`\`\``);
|
|
4781
|
-
const storyContent = await node_fs.promises.readFile(storyFile, "utf-8").catch((error) => {
|
|
4782
|
-
log.debug(`[PolishComponentTool] Could not read stories file ${storyFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4783
|
-
return null;
|
|
4784
|
-
});
|
|
4785
|
-
if (storyContent) contextSections.push(`## Stories File\n\nFile: ${storyFile}\n\n\`\`\`tsx\n${storyContent}\n\`\`\``);
|
|
4786
|
-
if (designPrinciples.status === "fulfilled" && designPrinciples.value.length > 0) {
|
|
4787
|
-
const principlesText = designPrinciples.value.map((p) => `### ${p.variant}\n${js_yaml.dump(p.content, {
|
|
4788
|
-
indent: 2,
|
|
4789
|
-
lineWidth: 120
|
|
4790
|
-
})}`).join("\n");
|
|
4791
|
-
contextSections.push(`## Design Principles\n\n${principlesText}`);
|
|
4792
|
-
}
|
|
4793
|
-
if (designTokens.status === "fulfilled") contextSections.push(designTokens.value);
|
|
4794
|
-
if (sharedComponents.status === "fulfilled") contextSections.push(`## Available Shared Components\n\nReuse these existing components where possible:\n${sharedComponents.value}`);
|
|
4795
|
-
const systemPrompt = this.buildSystemPrompt(platform);
|
|
4796
|
-
const userPrompt = [
|
|
4797
|
-
`# Polish Component`,
|
|
4798
|
-
"",
|
|
4799
|
-
instructions ? `## Instructions\n\n${instructions}\n` : "",
|
|
4800
|
-
...contextSections,
|
|
4801
|
-
"",
|
|
4802
|
-
`Edit the component file at: ${filePath}`,
|
|
4803
|
-
"Polish this component following the design system. Edit the files directly."
|
|
4804
|
-
].filter(Boolean).join("\n");
|
|
4805
|
-
const screenshotPaths = componentVisual.status === "fulfilled" ? [componentVisual.value.imagePath] : [];
|
|
4806
|
-
log.info(`[PolishComponentTool] Running Antigravity to edit component...`);
|
|
4807
|
-
const response = await this.antigravityService.edit({
|
|
4808
|
-
prompt: userPrompt,
|
|
4809
|
-
systemPrompt,
|
|
4810
|
-
screenshotPaths
|
|
4811
|
-
});
|
|
4812
|
-
log.info(`[PolishComponentTool] Antigravity finished editing (model: ${response.model})`);
|
|
4813
|
-
const reviewPrompt = [
|
|
4814
|
-
"\n## Review the changes",
|
|
4815
|
-
"",
|
|
4816
|
-
"Antigravity has edited the component files directly. Review the result with `pnpm exec style-system get-ui-component`:",
|
|
4817
|
-
` - componentName: "${componentName}"`,
|
|
4818
|
-
appPath ? ` - appPath: "${appPath}"` : "",
|
|
4819
|
-
"",
|
|
4820
|
-
`Component file: ${filePath}`,
|
|
4821
|
-
`Stories file: ${storyFile}`
|
|
4822
|
-
].filter(Boolean).join("\n");
|
|
4823
|
-
return { content: [{
|
|
4824
|
-
type: "text",
|
|
4825
|
-
text: response.content
|
|
4826
|
-
}, {
|
|
4827
|
-
type: "text",
|
|
4828
|
-
text: reviewPrompt
|
|
4829
|
-
}] };
|
|
4830
|
-
} catch (error) {
|
|
4831
|
-
return {
|
|
4832
|
-
content: [{
|
|
4833
|
-
type: "text",
|
|
4834
|
-
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
4835
|
-
}],
|
|
4836
|
-
isError: true
|
|
4837
|
-
};
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
detectPlatform(config) {
|
|
4841
|
-
if (config?.bundler?.includes("native")) return "native";
|
|
4842
|
-
return "web";
|
|
4843
|
-
}
|
|
4844
|
-
buildSystemPrompt(platform) {
|
|
4845
|
-
const base = [
|
|
4846
|
-
"You are a senior frontend designer and developer.",
|
|
4847
|
-
"You polish UI components to improve their visual quality, consistency, and adherence to the design system.",
|
|
4848
|
-
"You receive the component source code, a screenshot of its current rendering, the design principles, available design tokens, and shared components.",
|
|
4849
|
-
"Your job is to directly edit the component files to improve them.",
|
|
4850
|
-
"",
|
|
4851
|
-
"CRITICAL RULES:"
|
|
4852
|
-
];
|
|
4853
|
-
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.");
|
|
4854
|
-
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.");
|
|
4855
|
-
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");
|
|
4856
|
-
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");
|
|
4857
|
-
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.");
|
|
4858
|
-
return base.join("\n");
|
|
4859
|
-
}
|
|
4860
|
-
/**
|
|
4861
|
-
* Read design tokens from config.themePath.
|
|
4862
|
-
* For web: extracts Tailwind CSS classes. For native: reads the TypeScript types file.
|
|
4863
|
-
*/
|
|
4864
|
-
async getDesignTokens(platform, config, appPath) {
|
|
4865
|
-
if (!config?.themePath) return `No themePath configured in style-system.config.yaml.`;
|
|
4866
|
-
const themePath = await resolveStyleSystemThemePath({
|
|
4867
|
-
appPath,
|
|
4868
|
-
themePath: config.themePath
|
|
4869
|
-
});
|
|
4870
|
-
if (platform === "native") return `## Unistyles Theme Tokens\n\nUse these tokens via \`StyleSheet.create((theme) => ({...}))\`:\n\n\`\`\`typescript\n${await node_fs.promises.readFile(themePath, "utf-8")}\n\`\`\``;
|
|
4871
|
-
const toolkitConfig = await getGetCssClassesConfig();
|
|
4872
|
-
const result = await (await this.cssClassesServiceFactory.createService({ customServicePath: toolkitConfig?.customService })).extractClasses("all", themePath);
|
|
4873
|
-
return `## Available CSS Classes (Design Tokens)\n\n${JSON.stringify(result, null, 2)}`;
|
|
4874
|
-
}
|
|
4875
|
-
async findStoriesFile(dir) {
|
|
4876
|
-
for (const pattern of [
|
|
4877
|
-
"*.stories.tsx",
|
|
4878
|
-
"*.stories.ts",
|
|
4879
|
-
"*.stories.jsx",
|
|
4880
|
-
"*.stories.js"
|
|
4881
|
-
]) {
|
|
4882
|
-
const matches = await (0, glob.glob)(pattern, {
|
|
4883
|
-
cwd: dir,
|
|
4884
|
-
absolute: true
|
|
4885
|
-
});
|
|
4886
|
-
if (matches.length > 0) return matches[0];
|
|
4887
|
-
}
|
|
4888
|
-
return null;
|
|
4889
|
-
}
|
|
4890
|
-
/**
|
|
4891
|
-
* Find the app that owns a file, for style-system context.
|
|
4892
|
-
* Only apps declaring their own style-system.config.yaml are eligible.
|
|
4893
|
-
*/
|
|
4894
|
-
async findAppPath(filePath, workspaceRoot) {
|
|
4895
|
-
const parts = node_path.default.relative(workspaceRoot, filePath).split(node_path.default.sep);
|
|
4896
|
-
if (parts[0] === "apps" && parts.length > 1) {
|
|
4897
|
-
const appPath = node_path.default.join(parts[0], parts[1]);
|
|
4898
|
-
if (await hasStyleSystemProjectConfig(appPath)) return appPath;
|
|
4899
|
-
log.debug(`[PolishComponentTool] ${appPath} has no style-system config`);
|
|
4900
|
-
}
|
|
4901
|
-
const appsDir = node_path.default.join(workspaceRoot, "apps");
|
|
4902
|
-
let candidates;
|
|
4903
|
-
try {
|
|
4904
|
-
candidates = (await node_fs.promises.readdir(appsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory() && entry.name.endsWith("-app")).map((entry) => entry.name).sort();
|
|
4905
|
-
} catch (error) {
|
|
4906
|
-
log.debug(`[PolishComponentTool] Could not scan apps dir: ${error instanceof Error ? error.message : String(error)}`);
|
|
4907
|
-
return null;
|
|
4908
|
-
}
|
|
4909
|
-
for (const name of candidates) {
|
|
4910
|
-
const appPath = `apps/${name}`;
|
|
4911
|
-
if (await hasStyleSystemProjectConfig(appPath)) return appPath;
|
|
4912
|
-
log.debug(`[PolishComponentTool] ${appPath} has no style-system config`);
|
|
4913
|
-
}
|
|
4914
|
-
return null;
|
|
4915
|
-
}
|
|
4916
|
-
async getComponentVisual(componentName, appPath) {
|
|
4917
|
-
return { imagePath: (await this.getUiComponentService.getComponent({
|
|
4918
|
-
componentName,
|
|
4919
|
-
appPath,
|
|
4920
|
-
darkMode: true
|
|
4921
|
-
})).imagePath };
|
|
4922
|
-
}
|
|
4923
|
-
async getDesignPrinciples(appPath) {
|
|
4924
|
-
const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
|
|
4925
|
-
const product = node_path.default.basename(appPath).replace(/-app$|-native-app$|-mcp-app$|-e2e$/, "");
|
|
4926
|
-
const principlesDir = node_path.default.join(workspaceRoot, "docs", "architecture", product, "design-principles");
|
|
4927
|
-
const results = [];
|
|
4928
|
-
try {
|
|
4929
|
-
const files = await node_fs.promises.readdir(principlesDir);
|
|
4930
|
-
for (const file of files) {
|
|
4931
|
-
if (!file.endsWith(".yaml")) continue;
|
|
4932
|
-
try {
|
|
4933
|
-
const content = await node_fs.promises.readFile(node_path.default.join(principlesDir, file), "utf-8");
|
|
4934
|
-
const parsed = js_yaml.load(content);
|
|
4935
|
-
const variant = file.replace(".yaml", "");
|
|
4936
|
-
results.push({
|
|
4937
|
-
variant,
|
|
4938
|
-
content: parsed
|
|
4939
|
-
});
|
|
4940
|
-
} catch (error) {
|
|
4941
|
-
log.debug(`[PolishComponentTool] Skipping unreadable design principle ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4942
|
-
}
|
|
4943
|
-
}
|
|
4944
|
-
} catch (error) {
|
|
4945
|
-
log.debug(`[PolishComponentTool] No design principles at ${principlesDir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4946
|
-
}
|
|
4947
|
-
return results;
|
|
4948
|
-
}
|
|
4949
|
-
async getSharedComponents() {
|
|
4950
|
-
const storiesIndex = this.storiesIndexFactory();
|
|
4951
|
-
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
|
|
4952
|
-
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");
|
|
4953
|
-
}
|
|
4954
|
-
};
|
|
4955
|
-
PolishComponentTool = _PolishComponentTool = __decorate([
|
|
4956
|
-
(0, inversify.injectable)(),
|
|
4957
|
-
__decorateParam(0, (0, inversify.inject)(STYLE_SYSTEM_TYPES.GetUiComponentService)),
|
|
4958
|
-
__decorateParam(1, (0, inversify.inject)(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory)),
|
|
4959
|
-
__decorateParam(2, (0, inversify.inject)(STYLE_SYSTEM_TYPES.StoriesIndexFactory)),
|
|
4960
|
-
__decorateMetadata("design:paramtypes", [
|
|
4961
|
-
typeof GetUiComponentService === "undefined" ? Object : GetUiComponentService,
|
|
4962
|
-
typeof CSSClassesServiceFactory === "undefined" ? Object : CSSClassesServiceFactory,
|
|
4963
|
-
Object
|
|
4964
|
-
])
|
|
4965
|
-
], PolishComponentTool);
|
|
4966
|
-
//#endregion
|
|
4967
4725
|
//#region src/container/module.ts
|
|
4968
4726
|
function createStyleSystemModule(optionsInput = {}) {
|
|
4969
4727
|
return new inversify.ContainerModule((options) => {
|
|
4728
|
+
options.bind(STYLE_SYSTEM_TYPES.WorkspaceRoot).toConstantValue(optionsInput.workspaceRoot);
|
|
4970
4729
|
options.bind(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader).to(StyleSystemConfigLoader).inSingletonScope();
|
|
4971
4730
|
options.bind(STYLE_SYSTEM_TYPES.DefaultThemePath).toConstantValue(optionsInput.defaultThemePath);
|
|
4972
4731
|
options.bind(STYLE_SYSTEM_TYPES.GetUiComponentServiceConfig).toConstantValue(DEFAULT_GET_UI_COMPONENT_CONFIG);
|
|
4973
4732
|
options.bind(STYLE_SYSTEM_TYPES.AppComponentsServiceConfig).toConstantValue(DEFAULT_APP_COMPONENTS_CONFIG);
|
|
4974
4733
|
options.bind(STYLE_SYSTEM_TYPES.ThemeServiceFactory).to(ThemeServiceFactory).inSingletonScope();
|
|
4975
4734
|
options.bind(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory).to(CSSClassesServiceFactory).inSingletonScope();
|
|
4976
|
-
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexService).
|
|
4735
|
+
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexService).toDynamicValue(() => new StoriesIndexService({ workspaceRoot: optionsInput.workspaceRoot }));
|
|
4977
4736
|
options.bind(STYLE_SYSTEM_TYPES.AppComponentsService).to(AppComponentsService).inTransientScope();
|
|
4978
4737
|
options.bind(STYLE_SYSTEM_TYPES.GetUiComponentService).to(GetUiComponentService).inSingletonScope();
|
|
4979
|
-
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexFactory).toConstantValue(() => new StoriesIndexService());
|
|
4980
|
-
options.bind(STYLE_SYSTEM_TYPES.RendererFactory).toConstantValue((config, appPath) => new ComponentRendererService(config, appPath
|
|
4738
|
+
options.bind(STYLE_SYSTEM_TYPES.StoriesIndexFactory).toConstantValue(() => new StoriesIndexService({ workspaceRoot: optionsInput.workspaceRoot }));
|
|
4739
|
+
options.bind(STYLE_SYSTEM_TYPES.RendererFactory).toConstantValue((config, appPath) => new ComponentRendererService(config, appPath, {
|
|
4740
|
+
workspaceRoot: optionsInput.workspaceRoot,
|
|
4741
|
+
temporaryDirectory: optionsInput.temporaryDirectory,
|
|
4742
|
+
sharedBrowser: optionsInput.sharedBrowser,
|
|
4743
|
+
...optionsInput.bundlerFactory ? { bundlerFactory: () => optionsInput.bundlerFactory(config) } : {}
|
|
4744
|
+
}));
|
|
4981
4745
|
options.bind(STYLE_SYSTEM_TYPES.ListThemesTool).to(ListThemesTool).inSingletonScope();
|
|
4982
4746
|
options.bind(STYLE_SYSTEM_TYPES.GetCSSClassesTool).to(GetCSSClassesTool).inSingletonScope();
|
|
4983
4747
|
options.bind(STYLE_SYSTEM_TYPES.GetComponentVisualTool).to(GetComponentVisualTool).inSingletonScope();
|
|
4984
4748
|
options.bind(STYLE_SYSTEM_TYPES.ListSharedComponentsTool).to(ListSharedComponentsTool).inSingletonScope();
|
|
4985
4749
|
options.bind(STYLE_SYSTEM_TYPES.ListAppComponentsTool).to(ListAppComponentsTool).inSingletonScope();
|
|
4986
|
-
options.bind(STYLE_SYSTEM_TYPES.PolishComponentTool).to(PolishComponentTool).inSingletonScope();
|
|
4987
4750
|
});
|
|
4988
4751
|
}
|
|
4989
4752
|
//#endregion
|
|
@@ -4995,7 +4758,7 @@ function createContainer(options) {
|
|
|
4995
4758
|
}
|
|
4996
4759
|
//#endregion
|
|
4997
4760
|
//#region package.json
|
|
4998
|
-
var version = "0.2.14-alpha.
|
|
4761
|
+
var version = "0.2.14-alpha.21";
|
|
4999
4762
|
//#endregion
|
|
5000
4763
|
//#region src/metadata.ts
|
|
5001
4764
|
const STYLE_SYSTEM_CLI_NAME = "style-system";
|
|
@@ -5029,7 +4792,6 @@ function createServer(containerOrThemePath) {
|
|
|
5029
4792
|
const getComponentVisualTool = container.get(STYLE_SYSTEM_TYPES.GetComponentVisualTool);
|
|
5030
4793
|
const listSharedComponentsTool = container.get(STYLE_SYSTEM_TYPES.ListSharedComponentsTool);
|
|
5031
4794
|
const listAppComponentsTool = container.get(STYLE_SYSTEM_TYPES.ListAppComponentsTool);
|
|
5032
|
-
const polishComponentTool = container.get(STYLE_SYSTEM_TYPES.PolishComponentTool);
|
|
5033
4795
|
server.setRequestHandler("tools/list", async () => {
|
|
5034
4796
|
return { tools: [
|
|
5035
4797
|
withCapabilities(listThemesTool.getDefinition(), [
|
|
@@ -5057,12 +4819,6 @@ function createServer(containerOrThemePath) {
|
|
|
5057
4819
|
"components",
|
|
5058
4820
|
"app-components",
|
|
5059
4821
|
"component-discovery"
|
|
5060
|
-
]),
|
|
5061
|
-
withCapabilities(polishComponentTool.getDefinition(), [
|
|
5062
|
-
"components",
|
|
5063
|
-
"design-review",
|
|
5064
|
-
"polish",
|
|
5065
|
-
"antigravity"
|
|
5066
4822
|
])
|
|
5067
4823
|
].map((tool) => {
|
|
5068
4824
|
if (tool.inputSchema.type !== "object") throw new Error(`Invalid schema for ${tool.name}`);
|
|
@@ -5079,8 +4835,7 @@ function createServer(containerOrThemePath) {
|
|
|
5079
4835
|
getCSSClassesTool,
|
|
5080
4836
|
getComponentVisualTool,
|
|
5081
4837
|
listSharedComponentsTool,
|
|
5082
|
-
listAppComponentsTool
|
|
5083
|
-
polishComponentTool
|
|
4838
|
+
listAppComponentsTool
|
|
5084
4839
|
].find((candidate) => candidate.getDefinition().name === name);
|
|
5085
4840
|
if (!tool) return {
|
|
5086
4841
|
content: [{
|
|
@@ -5333,10 +5088,10 @@ Object.defineProperty(exports, "ListThemesTool", {
|
|
|
5333
5088
|
return ListThemesTool;
|
|
5334
5089
|
}
|
|
5335
5090
|
});
|
|
5336
|
-
Object.defineProperty(exports, "
|
|
5091
|
+
Object.defineProperty(exports, "MAX_STYLE_SYSTEM_VIEWPORT_DIMENSION", {
|
|
5337
5092
|
enumerable: true,
|
|
5338
5093
|
get: function() {
|
|
5339
|
-
return
|
|
5094
|
+
return MAX_STYLE_SYSTEM_VIEWPORT_DIMENSION;
|
|
5340
5095
|
}
|
|
5341
5096
|
});
|
|
5342
5097
|
Object.defineProperty(exports, "STYLE_SYSTEM_CLI_NAME", {
|
|
@@ -5399,6 +5154,12 @@ Object.defineProperty(exports, "ViteReactBundlerService", {
|
|
|
5399
5154
|
return ViteReactBundlerService;
|
|
5400
5155
|
}
|
|
5401
5156
|
});
|
|
5157
|
+
Object.defineProperty(exports, "ViteReactNativeBundlerService", {
|
|
5158
|
+
enumerable: true,
|
|
5159
|
+
get: function() {
|
|
5160
|
+
return ViteReactNativeBundlerService;
|
|
5161
|
+
}
|
|
5162
|
+
});
|
|
5402
5163
|
Object.defineProperty(exports, "__toESM", {
|
|
5403
5164
|
enumerable: true,
|
|
5404
5165
|
get: function() {
|
|
@@ -5477,6 +5238,12 @@ Object.defineProperty(exports, "isContained", {
|
|
|
5477
5238
|
return isContained;
|
|
5478
5239
|
}
|
|
5479
5240
|
});
|
|
5241
|
+
Object.defineProperty(exports, "launchBrowserWithFallback", {
|
|
5242
|
+
enumerable: true,
|
|
5243
|
+
get: function() {
|
|
5244
|
+
return launchBrowserWithFallback;
|
|
5245
|
+
}
|
|
5246
|
+
});
|
|
5480
5247
|
Object.defineProperty(exports, "log", {
|
|
5481
5248
|
enumerable: true,
|
|
5482
5249
|
get: function() {
|