boss-css 0.0.6 → 0.0.7

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.
Files changed (43) hide show
  1. package/dist/ai/server.cjs +433 -0
  2. package/dist/ai/server.mjs +422 -0
  3. package/dist/ai/skills.cjs +344 -0
  4. package/dist/ai/skills.mjs +343 -0
  5. package/dist/api/file/text.cjs +11 -0
  6. package/dist/api/file/text.mjs +11 -0
  7. package/dist/cli/tasks/init.cjs +11 -2
  8. package/dist/cli/tasks/init.mjs +12 -3
  9. package/dist/compile/jsx.cjs +77 -5
  10. package/dist/compile/jsx.mjs +77 -5
  11. package/dist/compile/transform.cjs +38 -2
  12. package/dist/compile/transform.mjs +38 -2
  13. package/dist/dev/plugin/server.cjs +19 -1
  14. package/dist/dev/plugin/server.mjs +19 -1
  15. package/dist/dev/server.cjs +84 -1
  16. package/dist/dev/server.mjs +84 -1
  17. package/dist/parser/jsx/server.cjs +32 -8
  18. package/dist/parser/jsx/server.mjs +32 -8
  19. package/dist/prop/at/server.cjs +22 -0
  20. package/dist/prop/at/server.mjs +22 -0
  21. package/dist/prop/bosswind/server.cjs +27 -0
  22. package/dist/prop/bosswind/server.mjs +28 -1
  23. package/dist/prop/bosswind/shared.cjs +15 -0
  24. package/dist/prop/bosswind/shared.mjs +15 -1
  25. package/dist/prop/pseudo/server.cjs +9 -0
  26. package/dist/prop/pseudo/server.mjs +10 -1
  27. package/dist/prop/pseudo/shared.cjs +2 -1
  28. package/dist/prop/pseudo/shared.mjs +1 -1
  29. package/dist/shared/customCss.cjs +66 -10
  30. package/dist/shared/customCss.mjs +66 -10
  31. package/dist/tasks/build.cjs +24 -1
  32. package/dist/tasks/build.mjs +24 -1
  33. package/dist/tasks/postcss.cjs +15 -0
  34. package/dist/tasks/postcss.mjs +15 -0
  35. package/dist/use/token/browser.cjs +14 -2
  36. package/dist/use/token/browser.mjs +14 -2
  37. package/dist/use/token/propMap.cjs +109 -0
  38. package/dist/use/token/propMap.mjs +105 -0
  39. package/dist/use/token/runtime-only.cjs +13 -80
  40. package/dist/use/token/runtime-only.mjs +13 -79
  41. package/dist/use/token/server.cjs +100 -97
  42. package/dist/use/token/server.mjs +93 -94
  43. package/package.json +11 -2
@@ -0,0 +1,433 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_text = require('../api/file/text.cjs');
3
+ const require_ai_skills = require('./skills.cjs');
4
+ let node_fs_promises = require("node:fs/promises");
5
+ node_fs_promises = require_rolldown_runtime.__toESM(node_fs_promises);
6
+ let node_path = require("node:path");
7
+ node_path = require_rolldown_runtime.__toESM(node_path);
8
+
9
+ //#region src/ai/server.ts
10
+ var server_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
11
+ name: () => name,
12
+ onBoot: () => onBoot,
13
+ onMetaData: () => onMetaData,
14
+ onSession: () => onSession
15
+ });
16
+ const name = "ai";
17
+ const STATIC_DOC = `### Syntax
18
+ - Use <$$ ...> for Boss props in JSX.
19
+ - Use $$.\$({ ... }) to mark prop objects for compile/runtime.
20
+ - Classname tokens look like color:red or hover:color:red (mostly 1:1 with CSS props).
21
+
22
+ ### Tokens
23
+ - Prefer token keys (color="primary") over $$.token.* when available.
24
+ - Token overrides can be passed via the tokens prop at runtime.
25
+ - Prefer token shorthands for shadows (boxShadow="md") when available.
26
+
27
+ ### Selectors
28
+ - Use pseudos for state (hover, focus, etc.).
29
+ - Use @at for breakpoints and media queries.
30
+ - Use child for nested selectors.
31
+ - Prefer shorthand keys (mobile:...) over at:mobile:...
32
+ - Prefer the device breakpoint for mobile + small screens when available.
33
+
34
+ ### Values
35
+ - Prefer unitless numbers (padding:12) and use _ for space-separated values (padding:10_20).
36
+ - Prefer arrays for shorthands (padding={[0, 10]}).
37
+ - For custom shadows, use arrays (boxShadow={[1, 1, 0, '#fff']}).
38
+
39
+ ### Globals
40
+ - Use $$.css() for global CSS and arbitrary selectors (body, [data-*], etc.).
41
+
42
+ ### Bosswind
43
+ - When Bosswind is enabled, prefer shorthands (flex vs display:flex).
44
+
45
+ ### Strategies
46
+ - inline-first: default for mixed static/dynamic props.
47
+ - classname-first: smaller inline styles; use function values for dynamics.
48
+ - runtime-only: client CSS injection only.
49
+ - compile pruning: keep props static when you want runtime-free output.
50
+ `;
51
+ const EVENT_NAMES = [
52
+ "onBoot",
53
+ "onReady",
54
+ "onParse",
55
+ "onPropTree",
56
+ "onProp",
57
+ "onCompileProp",
58
+ "onInit",
59
+ "onBrowserObjectStart",
60
+ "onSession",
61
+ "onMetaData"
62
+ ];
63
+ const metaStore = /* @__PURE__ */ new Map();
64
+ let metaStoreApi = null;
65
+ let llmsFile = null;
66
+ let llmsFilePath = null;
67
+ const getAiConfig = (api) => {
68
+ return api.ai ?? api.userConfig?.ai ?? {};
69
+ };
70
+ const getBaseDir = (api, session) => {
71
+ if (session?.baseDir) return session.baseDir;
72
+ if (typeof api.baseDir === "string") return api.baseDir;
73
+ return process.cwd();
74
+ };
75
+ const formatLines = (lines) => {
76
+ const filtered = lines.filter(Boolean);
77
+ if (!filtered.length) return "- (none)";
78
+ return filtered.map((line) => `- ${line}`).join("\n");
79
+ };
80
+ const renderSection = (title, body) => {
81
+ const trimmed = body.trim();
82
+ if (!trimmed) return `## ${title}\n\n- (none)`;
83
+ return `## ${title}\n\n${trimmed}`;
84
+ };
85
+ const renderSectionBlock = (id, body) => {
86
+ return `<!-- boss:ai:${id}:start -->\n${body.trim()}\n<!-- boss:ai:${id}:end -->`;
87
+ };
88
+ const updateSectionBlocks = (input, sections) => {
89
+ let text = input;
90
+ if (!/<!-- boss:ai:/.test(text)) {
91
+ text = text.trim();
92
+ if (!text.length) text = "# Boss CSS AI\n";
93
+ else text += "\n\n";
94
+ }
95
+ for (const [id, body] of Object.entries(sections)) {
96
+ const block = renderSectionBlock(id, body);
97
+ const pattern = /* @__PURE__ */ new RegExp(`<!-- boss:ai:${id}:start -->[\\s\\S]*?<!-- boss:ai:${id}:end -->`);
98
+ if (pattern.test(text)) text = text.replace(pattern, block);
99
+ else text += `\n\n${block}`;
100
+ }
101
+ return text.trim() + "\n";
102
+ };
103
+ const readFileSafe = async (filePath) => {
104
+ try {
105
+ return await node_fs_promises.default.readFile(filePath, "utf8");
106
+ } catch {
107
+ return "";
108
+ }
109
+ };
110
+ const writeFileIfChanged = async (filePath, content) => {
111
+ if (await readFileSafe(filePath) === content) return;
112
+ await node_fs_promises.default.mkdir(node_path.default.dirname(filePath), { recursive: true });
113
+ await node_fs_promises.default.writeFile(filePath, content, "utf8");
114
+ };
115
+ const isValidSkillName = (value) => /^[\p{Ll}\p{Nd}]+(?:-[\p{Ll}\p{Nd}]+)*$/u.test(value) && value.length <= 64;
116
+ const yamlValue = (value) => JSON.stringify(value);
117
+ const unquoteYamlValue = (value) => {
118
+ const trimmed = value.trim();
119
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
120
+ return trimmed;
121
+ };
122
+ const renderSkill = (skill) => {
123
+ const lines = [
124
+ "---",
125
+ `name: ${skill.name}`,
126
+ `description: ${yamlValue(skill.description)}`
127
+ ];
128
+ if (skill.license) lines.push(`license: ${yamlValue(skill.license)}`);
129
+ if (skill.compatibility) lines.push(`compatibility: ${yamlValue(skill.compatibility)}`);
130
+ if (skill.allowedTools) lines.push(`allowed-tools: ${yamlValue(skill.allowedTools)}`);
131
+ if (skill.metadata && Object.keys(skill.metadata).length) {
132
+ lines.push("metadata:");
133
+ for (const [key, value] of Object.entries(skill.metadata)) lines.push(` ${key}: ${yamlValue(value)}`);
134
+ }
135
+ lines.push("---", skill.body.trim(), "");
136
+ return lines.join("\n");
137
+ };
138
+ const resolvePackageVersion = async (baseDir) => {
139
+ try {
140
+ const pkgPath = node_path.default.join(baseDir, "package.json");
141
+ const raw = await node_fs_promises.default.readFile(pkgPath, "utf8");
142
+ const parsed = JSON.parse(raw);
143
+ return typeof parsed.version === "string" ? parsed.version : null;
144
+ } catch {
145
+ return null;
146
+ }
147
+ };
148
+ const parseSkillSummary = (content) => {
149
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?/);
150
+ if (!match) return null;
151
+ const lines = match[1].split("\n");
152
+ let name = "";
153
+ let description = "";
154
+ for (const line of lines) {
155
+ const trimmed = line.trim();
156
+ if (!trimmed || trimmed.startsWith("#")) continue;
157
+ if (trimmed.startsWith("name:")) name = unquoteYamlValue(trimmed.slice(5).trim());
158
+ if (trimmed.startsWith("description:")) description = unquoteYamlValue(trimmed.slice(12).trim());
159
+ }
160
+ if (!name || !description) return null;
161
+ return {
162
+ name,
163
+ description
164
+ };
165
+ };
166
+ const fileExists = async (filePath) => {
167
+ try {
168
+ await node_fs_promises.default.access(filePath);
169
+ return true;
170
+ } catch {
171
+ return false;
172
+ }
173
+ };
174
+ const loadCustomSkills = async (outputPath, baseDir, existingNames, log) => {
175
+ const entries = await node_fs_promises.default.readdir(outputPath, { withFileTypes: true });
176
+ const results = [];
177
+ for (const entry of entries) {
178
+ if (!entry.isDirectory()) continue;
179
+ const skillFile = node_path.default.join(outputPath, entry.name, "SKILL.md");
180
+ if (!await fileExists(skillFile)) continue;
181
+ const summary = parseSkillSummary(await readFileSafe(skillFile));
182
+ if (!summary) {
183
+ log.child("ai").log(`Skipping custom skill without valid frontmatter: ${skillFile}`);
184
+ continue;
185
+ }
186
+ if (!isValidSkillName(summary.name)) {
187
+ log.child("ai").log(`Invalid skill name "${summary.name}" in ${skillFile}`);
188
+ continue;
189
+ }
190
+ if (existingNames.has(summary.name)) continue;
191
+ results.push({
192
+ name: summary.name,
193
+ description: summary.description,
194
+ path: node_path.default.relative(baseDir, skillFile),
195
+ source: "custom"
196
+ });
197
+ }
198
+ results.sort((a, b) => a.name.localeCompare(b.name));
199
+ return results;
200
+ };
201
+ const buildSections = (api, session) => {
202
+ const config = getAiConfig(api);
203
+ const baseDir = getBaseDir(api, session);
204
+ const defaultLlmsPath = node_path.default.join(api.folder ?? api.configDir ?? ".bo$$", "LLMS.md");
205
+ const llmsPath = config.llms?.path ?? defaultLlmsPath;
206
+ const llmsFullPath = node_path.default.isAbsolute(llmsPath) ? llmsPath : node_path.default.join(baseDir, llmsPath);
207
+ const pluginNames = api.plugins.map((plugin) => plugin.name ?? "unknown");
208
+ const pluginLines = api.plugins.map((plugin) => {
209
+ const hooks = EVENT_NAMES.filter((eventName) => Boolean(plugin[eventName]));
210
+ return `${plugin.name ?? "unknown"} (${hooks.join(", ") || "no hooks"})`;
211
+ });
212
+ const summaryBody = formatLines([
213
+ `Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
214
+ `Base dir: ${baseDir}`,
215
+ `Config path: ${session?.configPath ?? "(unknown)"}`,
216
+ `LLMS path: ${llmsFullPath}`,
217
+ `Runtime strategy: ${api.strategy ?? "(default)"}`,
218
+ `Plugins: ${pluginNames.join(", ")}`
219
+ ]);
220
+ const configBody = formatLines([
221
+ `content: ${Array.isArray(api.content) ? api.content.join(", ") : api.content ?? "(none)"}`,
222
+ `selectorPrefix: ${api.selectorPrefix ?? "(none)"}`,
223
+ `selectorScope: ${api.selectorScope ?? "(none)"}`,
224
+ `unit: ${api.unit ?? "px"}`,
225
+ `runtime.only: ${api.runtime?.only === true ? "true" : "false"}`,
226
+ `runtime.strategy: ${api.runtime?.strategy ?? "(default)"}`
227
+ ]);
228
+ const outputsBody = formatLines([
229
+ `runtime: ${node_path.default.join(api.folder ?? api.configDir ?? ".bo$$", "index.js")}`,
230
+ `types: ${node_path.default.join(api.folder ?? api.configDir ?? ".bo$$", "index.d.ts")}`,
231
+ api.file.native?.hasContent ? `native runtime: ${node_path.default.join(api.folder ?? api.configDir ?? ".bo$$", "native.js")}` : "",
232
+ api.file.native?.hasContent ? `native types: ${node_path.default.join(api.folder ?? api.configDir ?? ".bo$$", "native.d.ts")}` : "",
233
+ `styles: ${api.stylesheetPath ?? node_path.default.join(api.configDir ?? ".bo$$", "styles.css")}`
234
+ ]);
235
+ const customPropLines = [];
236
+ const seenDescriptors = /* @__PURE__ */ new Set();
237
+ for (const descriptor of api.dictionary.values()) {
238
+ if (!descriptor || descriptor.isCSSProp !== false) continue;
239
+ if (seenDescriptors.has(descriptor)) continue;
240
+ seenDescriptors.add(descriptor);
241
+ const alias = descriptor.aliases?.[0] ?? descriptor.property;
242
+ if (!alias) continue;
243
+ const description = descriptor.description ?? descriptor.usage ?? "";
244
+ customPropLines.push(description ? `${alias} — ${description}` : alias);
245
+ }
246
+ const customPropsBody = formatLines(customPropLines.sort((a, b) => a.localeCompare(b)));
247
+ const frameworkBody = formatLines([
248
+ `framework: ${typeof api.framework === "string" ? api.framework : typeof api.framework === "object" ? api.framework.name ?? "unknown" : "unknown"}`,
249
+ typeof api.framework === "object" && api.framework?.className ? `className: ${api.framework.className}` : "",
250
+ typeof api.framework === "object" && api.framework?.fileType ? `fileType: ${api.framework.fileType}` : ""
251
+ ]);
252
+ const compileBody = formatLines([
253
+ `compile.enabled: ${api.compile ? "true" : "false"}`,
254
+ api.compile?.spread ? `compile.spread: true` : "",
255
+ api.compile?.stats ? `compile.stats: ${api.compile.stats}` : ""
256
+ ]);
257
+ const sections = {
258
+ static: renderSection("Boss CSS usage", STATIC_DOC),
259
+ summary: renderSection("Summary", summaryBody),
260
+ config: renderSection("Config", configBody),
261
+ plugins: renderSection("Plugins", formatLines(pluginLines)),
262
+ outputs: renderSection("Outputs", outputsBody),
263
+ framework: renderSection("Framework", frameworkBody),
264
+ props: renderSection("Custom props", customPropsBody),
265
+ compile: renderSection("Compile", compileBody)
266
+ };
267
+ const defaultTitles = {
268
+ tokens: "Tokens",
269
+ props: "Props",
270
+ prepared: "Prepared components",
271
+ pseudos: "Pseudos",
272
+ at: "At (breakpoints and media)",
273
+ bosswind: "Bosswind",
274
+ boundaries: "CSS boundaries",
275
+ "dev-server": "Dev server"
276
+ };
277
+ const metaOrder = [
278
+ "tokens",
279
+ "props",
280
+ "prepared",
281
+ "pseudos",
282
+ "at",
283
+ "bosswind",
284
+ "boundaries",
285
+ "dev-server"
286
+ ];
287
+ const orderedSections = [...metaOrder.filter((section) => metaStore.has(section)), ...Array.from(metaStore.keys()).filter((section) => !metaOrder.includes(section)).sort((a, b) => a.localeCompare(b))];
288
+ for (const section of orderedSections) {
289
+ const entries = metaStore.get(section);
290
+ if (!entries || entries.length === 0) continue;
291
+ const sorted = [...entries].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
292
+ const title = sorted.find((entry) => entry.title)?.title ?? defaultTitles[section] ?? section;
293
+ const content = sorted.map((entry) => entry.content.trim()).filter(Boolean).join("\n\n");
294
+ if (!content) continue;
295
+ sections[section] = renderSection(title, content);
296
+ }
297
+ return {
298
+ sections,
299
+ llmsFullPath
300
+ };
301
+ };
302
+ const getLlmsFile = (api, filePath) => {
303
+ if (!llmsFile || llmsFilePath !== filePath) {
304
+ llmsFile = new require_text.default(api, { path: filePath });
305
+ llmsFilePath = filePath;
306
+ }
307
+ return llmsFile;
308
+ };
309
+ const writeLlms = async (api, session) => {
310
+ if (getAiConfig(api).llms?.enabled === false) return;
311
+ const { sections, llmsFullPath } = buildSections(api, session);
312
+ const updated = updateSectionBlocks(await readFileSafe(llmsFullPath), sections);
313
+ const baseDir = getBaseDir(api, session);
314
+ const folder = api.folder ?? ".bo$$";
315
+ const folderPath = node_path.default.isAbsolute(folder) ? folder : node_path.default.join(baseDir, folder);
316
+ const relativePath = node_path.default.relative(folderPath, llmsFullPath);
317
+ if (relativePath && !relativePath.startsWith("..") && !node_path.default.isAbsolute(relativePath)) {
318
+ const file = getLlmsFile(api, relativePath);
319
+ file.set("body", "llms", updated);
320
+ await file.write();
321
+ return;
322
+ }
323
+ await writeFileIfChanged(llmsFullPath, updated);
324
+ };
325
+ const writeSkills = async (api, session) => {
326
+ const config = getAiConfig(api);
327
+ if (config.skills?.enabled === false) return;
328
+ const baseDir = getBaseDir(api, session);
329
+ const outputDir = config.skills?.outputDir ?? node_path.default.join(api.folder ?? api.configDir ?? ".bo$$", "skills");
330
+ const outputPath = node_path.default.isAbsolute(outputDir) ? outputDir : node_path.default.join(baseDir, outputDir);
331
+ const includeBuiltins = config.skills?.includeBuiltins !== false;
332
+ await node_fs_promises.default.mkdir(outputPath, { recursive: true });
333
+ const skills = [];
334
+ const packageVersion = await resolvePackageVersion(baseDir);
335
+ if (includeBuiltins) skills.push(...require_ai_skills.builtInSkills.map((skill) => ({
336
+ name: skill.name,
337
+ description: skill.description,
338
+ body: skill.body,
339
+ compatibility: skill.compatibility,
340
+ license: skill.license,
341
+ metadata: packageVersion ? {
342
+ ...skill.metadata ?? {},
343
+ version: packageVersion
344
+ } : skill.metadata,
345
+ allowedTools: skill.allowedTools,
346
+ source: "builtin"
347
+ })));
348
+ const indexEntries = [];
349
+ const builtinNames = new Set(skills.map((skill) => skill.name));
350
+ for (const skill of skills) {
351
+ if (!isValidSkillName(skill.name)) {
352
+ api.log.child("ai").log(`Invalid skill name "${skill.name}"`);
353
+ continue;
354
+ }
355
+ const skillDir = node_path.default.join(outputPath, skill.name);
356
+ const filePath = node_path.default.join(skillDir, "SKILL.md");
357
+ await node_fs_promises.default.mkdir(skillDir, { recursive: true });
358
+ await writeFileIfChanged(filePath, renderSkill(skill));
359
+ indexEntries.push({
360
+ name: skill.name,
361
+ description: skill.description,
362
+ path: node_path.default.relative(baseDir, filePath),
363
+ source: skill.source
364
+ });
365
+ }
366
+ const customEntries = await loadCustomSkills(outputPath, baseDir, builtinNames, api.log);
367
+ indexEntries.push(...customEntries);
368
+ await writeFileIfChanged(node_path.default.join(outputPath, "index.json"), JSON.stringify(indexEntries, null, 2) + "\n");
369
+ };
370
+ const onBoot = async (api) => {
371
+ if (metaStoreApi !== api) {
372
+ metaStore.clear();
373
+ metaStoreApi = api ?? null;
374
+ }
375
+ llmsFile = null;
376
+ llmsFilePath = null;
377
+ };
378
+ const onMetaData = async (_api, payload) => {
379
+ if (!payload || payload.kind !== "ai") return;
380
+ if (_api && metaStoreApi !== _api) {
381
+ metaStore.clear();
382
+ metaStoreApi = _api;
383
+ }
384
+ const type = typeof payload.type === "string" ? payload.type : "";
385
+ const data = payload.data;
386
+ const shouldReplace = payload.replace === true || data && typeof data === "object" && data.replace === true;
387
+ let entry = null;
388
+ if (type === "skill") {
389
+ const content = typeof data === "string" ? data : data && typeof data.content === "string" ? String(data.content) : data && typeof data.body === "string" ? String(data.body) : "";
390
+ if (content) entry = {
391
+ section: "skills",
392
+ title: data && typeof data.title === "string" ? String(data.title) : typeof payload.label === "string" ? payload.label : "Skills",
393
+ content,
394
+ order: typeof data?.order === "number" ? Number(data.order) : void 0
395
+ };
396
+ } else if (data && typeof data === "object") {
397
+ const section = data.section;
398
+ const content = data.content;
399
+ if (typeof section === "string" && typeof content === "string") entry = {
400
+ section,
401
+ content,
402
+ title: typeof data.title === "string" ? String(data.title) : void 0,
403
+ order: typeof data.order === "number" ? Number(data.order) : void 0
404
+ };
405
+ }
406
+ if (!entry) return;
407
+ if (!/^[a-zA-Z0-9._-]+$/.test(entry.section)) return;
408
+ if (shouldReplace) {
409
+ metaStore.set(entry.section, [entry]);
410
+ return;
411
+ }
412
+ const list = metaStore.get(entry.section) ?? [];
413
+ list.push(entry);
414
+ metaStore.set(entry.section, list);
415
+ };
416
+ const onSession = async (api, session) => {
417
+ if (!session) return;
418
+ if (session.phase !== "run" && session.phase !== "stop") return;
419
+ await writeLlms(api, session);
420
+ await writeSkills(api, session);
421
+ };
422
+
423
+ //#endregion
424
+ exports.name = name;
425
+ exports.onBoot = onBoot;
426
+ exports.onMetaData = onMetaData;
427
+ exports.onSession = onSession;
428
+ Object.defineProperty(exports, 'server_exports', {
429
+ enumerable: true,
430
+ get: function () {
431
+ return server_exports;
432
+ }
433
+ });