skullmaster 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +902 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +3 -3
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,902 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cac } from "cac";
|
|
3
|
+
import { serve } from "@hono/node-server";
|
|
4
|
+
import { Hono } from "hono";
|
|
5
|
+
import { cors } from "hono/cors";
|
|
6
|
+
import { Project, QuoteKind, ScriptKind, SyntaxKind } from "ts-morph";
|
|
7
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import z from "zod";
|
|
9
|
+
import { JSDOM } from "jsdom";
|
|
10
|
+
import pkg from "@babel/generator";
|
|
11
|
+
import * as t from "@babel/types";
|
|
12
|
+
import { jsxText } from "@babel/types";
|
|
13
|
+
import { cancel, isCancel, select, text } from "@clack/prompts";
|
|
14
|
+
import { readFile as readFile$1, writeFile as writeFile$1 } from "fs/promises";
|
|
15
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
16
|
+
//#region src/shared/constants.ts
|
|
17
|
+
const DEFAULT_PORT = 8008;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/shared/schema.ts
|
|
20
|
+
const SkeletonPayloadInputSchema = z.record(z.string(), z.object({
|
|
21
|
+
component: z.string(),
|
|
22
|
+
html: z.string()
|
|
23
|
+
}));
|
|
24
|
+
const SkeletonCacheEntrySchema = z.record(z.string(), z.object({
|
|
25
|
+
component: z.string(),
|
|
26
|
+
html: z.string(),
|
|
27
|
+
hash: z.string()
|
|
28
|
+
}));
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/shared/log.ts
|
|
31
|
+
const log = {
|
|
32
|
+
success: (message) => console.log(`\x1b[32m[💀] ${message}\x1b[0m`),
|
|
33
|
+
info: (message) => console.log(`\x1b[36m[💀] ${message}\x1b[0m`),
|
|
34
|
+
warn: (message) => console.log(`\x1b[33m[💀] ${message}\x1b[0m`),
|
|
35
|
+
error: (message) => console.log(`\x1b[31m[💀] ${message}\x1b[0m`),
|
|
36
|
+
gray: (message) => console.log(`\x1b[90m[💀] ${message}\x1b[0m`)
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/cache/index.ts
|
|
40
|
+
var SkeletonCacheDB = class SkeletonCacheDB {
|
|
41
|
+
data = {};
|
|
42
|
+
config;
|
|
43
|
+
constructor(config) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
}
|
|
46
|
+
static async create(config) {
|
|
47
|
+
const db = new SkeletonCacheDB(config);
|
|
48
|
+
await db.load();
|
|
49
|
+
return db;
|
|
50
|
+
}
|
|
51
|
+
async load() {
|
|
52
|
+
try {
|
|
53
|
+
const file = await readFile(this.config.getCachePath(), "utf8");
|
|
54
|
+
const parsed = JSON.parse(file);
|
|
55
|
+
this.data = SkeletonCacheEntrySchema.parse(parsed);
|
|
56
|
+
} catch {
|
|
57
|
+
this.data = {};
|
|
58
|
+
await this.config.cache({
|
|
59
|
+
type: "replace",
|
|
60
|
+
cacheEntry: {}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
get(key) {
|
|
65
|
+
return this.data[key];
|
|
66
|
+
}
|
|
67
|
+
getComponentNames() {
|
|
68
|
+
return Object.values(this.data).map((entry) => entry.component);
|
|
69
|
+
}
|
|
70
|
+
async set(key, value) {
|
|
71
|
+
this.data[key] = value;
|
|
72
|
+
this.config.cache({
|
|
73
|
+
type: "update",
|
|
74
|
+
componentName: key,
|
|
75
|
+
payload: value
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/generate-target/index.ts
|
|
81
|
+
function generateTarget({ project, filePath, componentName, body }) {
|
|
82
|
+
project.manipulationSettings.set({ quoteKind: QuoteKind.Double });
|
|
83
|
+
const file = project.createSourceFile(filePath, "", {
|
|
84
|
+
overwrite: true,
|
|
85
|
+
scriptKind: ScriptKind.TSX
|
|
86
|
+
});
|
|
87
|
+
file.insertText(0, `/* eslint-disable */
|
|
88
|
+
/* prettier-ignore-start */
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Auto-generated by Skullmaster.
|
|
92
|
+
*
|
|
93
|
+
* DO NOT EDIT THIS FILE MANUALLY.
|
|
94
|
+
* Changes may be overwritten on the next generation.
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
`);
|
|
98
|
+
file.addFunction({
|
|
99
|
+
name: componentName,
|
|
100
|
+
isDefaultExport: true,
|
|
101
|
+
statements: [`return (${body});`]
|
|
102
|
+
});
|
|
103
|
+
file.addStatements(`
|
|
104
|
+
/* prettier-ignore-end */
|
|
105
|
+
`);
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region ../excarnate/dist/index.mjs
|
|
109
|
+
function buildExecutor(rules) {
|
|
110
|
+
return (ctx) => {
|
|
111
|
+
let current = ctx;
|
|
112
|
+
for (const rule of rules) {
|
|
113
|
+
if (!rule.match(current)) continue;
|
|
114
|
+
current = rule.transform(current);
|
|
115
|
+
if (rule.skipAllRest) break;
|
|
116
|
+
}
|
|
117
|
+
return current;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function createJsxStringAttribute(name, value) {
|
|
121
|
+
return t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(value));
|
|
122
|
+
}
|
|
123
|
+
function createJsxExpressionAttribute(name, expression) {
|
|
124
|
+
return t.jsxAttribute(t.jsxIdentifier(name), t.jsxExpressionContainer(expression));
|
|
125
|
+
}
|
|
126
|
+
function findJsxAttribute(attributes, name) {
|
|
127
|
+
return attributes.find((attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === name);
|
|
128
|
+
}
|
|
129
|
+
function findStringJsxAttribute(attributes, name) {
|
|
130
|
+
const attr = findJsxAttribute(attributes, name);
|
|
131
|
+
if (!attr?.value || !t.isStringLiteral(attr.value)) return;
|
|
132
|
+
return attr;
|
|
133
|
+
}
|
|
134
|
+
function createJsxElement(tagName, children, attributes = []) {
|
|
135
|
+
return t.jsxElement(t.jsxOpeningElement(t.jsxIdentifier(tagName), attributes, false), t.jsxClosingElement(t.jsxIdentifier(tagName)), children, false);
|
|
136
|
+
}
|
|
137
|
+
const DEPTH_ATTRIBUTE = "data-depth";
|
|
138
|
+
const TEXT_CLASSNAME = "empty-set__text";
|
|
139
|
+
const SKELETON_CLASSNAME = "empty-set__skeleton";
|
|
140
|
+
var TargetElementMismatchError = class extends Error {
|
|
141
|
+
constructor(expected, actual) {
|
|
142
|
+
super(`Expected target element to be ${expected}, but got ${actual}`);
|
|
143
|
+
this.name = "TargetElementMismatchError";
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const ATTRIBUTE_MAP = {
|
|
147
|
+
class: "className",
|
|
148
|
+
for: "htmlFor",
|
|
149
|
+
tabindex: "tabIndex",
|
|
150
|
+
readonly: "readOnly",
|
|
151
|
+
maxlength: "maxLength",
|
|
152
|
+
minlength: "minLength",
|
|
153
|
+
colspan: "colSpan",
|
|
154
|
+
rowspan: "rowSpan",
|
|
155
|
+
contenteditable: "contentEditable",
|
|
156
|
+
autocapitalize: "autoCapitalize",
|
|
157
|
+
autocomplete: "autoComplete",
|
|
158
|
+
autofocus: "autoFocus",
|
|
159
|
+
spellcheck: "spellCheck",
|
|
160
|
+
crossorigin: "crossOrigin",
|
|
161
|
+
referrerpolicy: "referrerPolicy",
|
|
162
|
+
strokewidth: "strokeWidth",
|
|
163
|
+
"stroke-width": "strokeWidth",
|
|
164
|
+
strokelinecap: "strokeLinecap",
|
|
165
|
+
"stroke-linecap": "strokeLinecap",
|
|
166
|
+
strokelinejoin: "strokeLinejoin",
|
|
167
|
+
"stroke-linejoin": "strokeLinejoin",
|
|
168
|
+
strokemiterlimit: "strokeMiterlimit",
|
|
169
|
+
"stroke-miterlimit": "strokeMiterlimit",
|
|
170
|
+
strokedasharray: "strokeDasharray",
|
|
171
|
+
"stroke-dasharray": "strokeDasharray",
|
|
172
|
+
strokedashoffset: "strokeDashoffset",
|
|
173
|
+
"stroke-dashoffset": "strokeDashoffset",
|
|
174
|
+
strokeopacity: "strokeOpacity",
|
|
175
|
+
"stroke-opacity": "strokeOpacity",
|
|
176
|
+
fillopacity: "fillOpacity",
|
|
177
|
+
"fill-opacity": "fillOpacity",
|
|
178
|
+
fillrule: "fillRule",
|
|
179
|
+
"fill-rule": "fillRule",
|
|
180
|
+
cliprule: "clipRule",
|
|
181
|
+
"clip-rule": "clipRule",
|
|
182
|
+
clippath: "clipPath",
|
|
183
|
+
"clip-path": "clipPath",
|
|
184
|
+
stopcolor: "stopColor",
|
|
185
|
+
"stop-color": "stopColor",
|
|
186
|
+
stopopacity: "stopOpacity",
|
|
187
|
+
"stop-opacity": "stopOpacity",
|
|
188
|
+
viewbox: "viewBox",
|
|
189
|
+
xlinkhref: "xlinkHref",
|
|
190
|
+
"xlink:href": "xlinkHref",
|
|
191
|
+
xmlspace: "xmlSpace",
|
|
192
|
+
"xml:space": "xmlSpace"
|
|
193
|
+
};
|
|
194
|
+
function normalizeAttributeName(name) {
|
|
195
|
+
const lower = name.toLowerCase();
|
|
196
|
+
if (ATTRIBUTE_MAP[lower]) return ATTRIBUTE_MAP[lower];
|
|
197
|
+
if (lower.startsWith("data-") || lower.startsWith("aria-")) return lower;
|
|
198
|
+
return name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
199
|
+
}
|
|
200
|
+
const STYLE_ATTRIBUTES = new Set([
|
|
201
|
+
"class",
|
|
202
|
+
"style",
|
|
203
|
+
"type",
|
|
204
|
+
"fill",
|
|
205
|
+
"fill-opacity",
|
|
206
|
+
"fill-rule",
|
|
207
|
+
"src",
|
|
208
|
+
"stroke",
|
|
209
|
+
"stroke-width",
|
|
210
|
+
"stroke-opacity",
|
|
211
|
+
"stroke-linecap",
|
|
212
|
+
"stroke-linejoin",
|
|
213
|
+
"stroke-dasharray",
|
|
214
|
+
"stroke-dashoffset",
|
|
215
|
+
"opacity",
|
|
216
|
+
"role",
|
|
217
|
+
"width",
|
|
218
|
+
"height",
|
|
219
|
+
"x",
|
|
220
|
+
"y",
|
|
221
|
+
"cx",
|
|
222
|
+
"cy",
|
|
223
|
+
"r",
|
|
224
|
+
"rx",
|
|
225
|
+
"ry",
|
|
226
|
+
"viewbox",
|
|
227
|
+
"transform",
|
|
228
|
+
"d",
|
|
229
|
+
"points"
|
|
230
|
+
]);
|
|
231
|
+
function shouldKeepAttribute(name) {
|
|
232
|
+
const lowerName = name.toLowerCase();
|
|
233
|
+
return lowerName.startsWith("data-") || lowerName.startsWith("aria-") || STYLE_ATTRIBUTES.has(lowerName);
|
|
234
|
+
}
|
|
235
|
+
function parseStyle(style) {
|
|
236
|
+
const properties = [];
|
|
237
|
+
for (const rule of style.split(";")) {
|
|
238
|
+
const trimmedRule = rule.trim();
|
|
239
|
+
if (!trimmedRule) continue;
|
|
240
|
+
const colonIndex = trimmedRule.indexOf(":");
|
|
241
|
+
if (colonIndex === -1) continue;
|
|
242
|
+
const rawKey = trimmedRule.slice(0, colonIndex).trim();
|
|
243
|
+
const rawValue = trimmedRule.slice(colonIndex + 1).trim();
|
|
244
|
+
const key = rawKey.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
245
|
+
const keyNode = key.startsWith("--") || key.includes("-") ? t.stringLiteral(key) : t.identifier(key);
|
|
246
|
+
properties.push(t.objectProperty(keyNode, t.stringLiteral(rawValue)));
|
|
247
|
+
}
|
|
248
|
+
return t.objectExpression(properties);
|
|
249
|
+
}
|
|
250
|
+
const populateAttrs = {
|
|
251
|
+
id: "populate-attrs",
|
|
252
|
+
match: (ctx) => ctx.element.nodeType === 1,
|
|
253
|
+
transform: (ctx) => {
|
|
254
|
+
const element = ctx.element;
|
|
255
|
+
const attributes = [];
|
|
256
|
+
const tagName = element.tagName.toLowerCase();
|
|
257
|
+
if (ctx.target?.element && ctx.target?.element !== tagName) throw new TargetElementMismatchError(tagName, ctx.target.element);
|
|
258
|
+
if (!element.hasAttribute("data-depth")) attributes.push(createJsxStringAttribute(DEPTH_ATTRIBUTE, String(ctx.depth)));
|
|
259
|
+
for (const attr of element.attributes) {
|
|
260
|
+
if (!shouldKeepAttribute(attr.name)) continue;
|
|
261
|
+
const name = normalizeAttributeName(attr.name);
|
|
262
|
+
const value = attr.value;
|
|
263
|
+
if (name === "style") {
|
|
264
|
+
attributes.push(createJsxExpressionAttribute("style", parseStyle(value)));
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
attributes.push(createJsxStringAttribute(name, value));
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
...ctx,
|
|
271
|
+
target: {
|
|
272
|
+
element: tagName,
|
|
273
|
+
attributes
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const atDepth = (depth) => (ctx) => ctx.depth === depth;
|
|
279
|
+
const isTagNamesIn = (elements) => (ctx) => ctx.element.nodeType === 1 && elements.includes(ctx.element.tagName.toLowerCase());
|
|
280
|
+
const processSvg = {
|
|
281
|
+
id: "process-svg",
|
|
282
|
+
skipAllRest: true,
|
|
283
|
+
match: isTagNamesIn(["svg"]),
|
|
284
|
+
transform: (ctx) => {
|
|
285
|
+
return {
|
|
286
|
+
...ctx,
|
|
287
|
+
target: {
|
|
288
|
+
...ctx.target,
|
|
289
|
+
element: "svg",
|
|
290
|
+
children: []
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const removeReduntantNode = {
|
|
296
|
+
id: "remove-node",
|
|
297
|
+
skipAllRest: true,
|
|
298
|
+
match: isTagNamesIn([
|
|
299
|
+
"html",
|
|
300
|
+
"script",
|
|
301
|
+
"style",
|
|
302
|
+
"head",
|
|
303
|
+
"meta",
|
|
304
|
+
"link",
|
|
305
|
+
"noscript",
|
|
306
|
+
"template",
|
|
307
|
+
"audio",
|
|
308
|
+
"video",
|
|
309
|
+
"source",
|
|
310
|
+
"track",
|
|
311
|
+
"iframe",
|
|
312
|
+
"embed",
|
|
313
|
+
"object",
|
|
314
|
+
"title",
|
|
315
|
+
"base",
|
|
316
|
+
"defs",
|
|
317
|
+
"symbol",
|
|
318
|
+
"html",
|
|
319
|
+
"canvas",
|
|
320
|
+
"map",
|
|
321
|
+
"area",
|
|
322
|
+
"picture",
|
|
323
|
+
"portal"
|
|
324
|
+
]),
|
|
325
|
+
transform: (ctx) => {
|
|
326
|
+
return {
|
|
327
|
+
...ctx,
|
|
328
|
+
target: null
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const addRootSkeletonsAttrs = {
|
|
333
|
+
id: "add-root-skeleton-attrs",
|
|
334
|
+
match: (ctx) => ctx.element.nodeType === 1 && atDepth(0)(ctx),
|
|
335
|
+
transform: (ctx) => {
|
|
336
|
+
const tagName = ctx.element.tagName.toLowerCase();
|
|
337
|
+
if (ctx.target?.element && ctx.target.element !== tagName) throw new TargetElementMismatchError(tagName, ctx.target.element);
|
|
338
|
+
const existingTarget = ctx.target ?? {
|
|
339
|
+
element: tagName,
|
|
340
|
+
attributes: []
|
|
341
|
+
};
|
|
342
|
+
const existingAttrs = existingTarget.attributes ?? [];
|
|
343
|
+
const classNameAttr = findStringJsxAttribute(existingAttrs, "className");
|
|
344
|
+
const attributes = [...classNameAttr ? existingAttrs.map((attr) => attr === classNameAttr ? {
|
|
345
|
+
...attr,
|
|
346
|
+
value: {
|
|
347
|
+
...classNameAttr.value,
|
|
348
|
+
value: `${classNameAttr.value.value} ${SKELETON_CLASSNAME}`
|
|
349
|
+
}
|
|
350
|
+
} : attr) : [...existingAttrs, createJsxStringAttribute("className", SKELETON_CLASSNAME)]];
|
|
351
|
+
if (!findJsxAttribute(attributes, "role")) attributes.push(createJsxStringAttribute("role", "status"));
|
|
352
|
+
if (!findJsxAttribute(attributes, "aria-live")) attributes.push(createJsxStringAttribute("aria-live", "polite"));
|
|
353
|
+
if (!findJsxAttribute(attributes, "aria-busy")) attributes.push(createJsxStringAttribute("aria-busy", "true"));
|
|
354
|
+
return {
|
|
355
|
+
...ctx,
|
|
356
|
+
target: {
|
|
357
|
+
...existingTarget,
|
|
358
|
+
element: tagName,
|
|
359
|
+
attributes
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const DATA_NAT_H = "data-natural-h";
|
|
365
|
+
const DATA_NAT_W = "data-natural-w";
|
|
366
|
+
const DATA_IMAGE_SKELETON = "data-image-skeleton";
|
|
367
|
+
function createSkeletonSrc(width, height, color = "#e5e7eb") {
|
|
368
|
+
const w = Number(width) || 400;
|
|
369
|
+
const h = Number(height) || 300;
|
|
370
|
+
return `data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}" preserveAspectRatio="none"><rect width="100%" height="100%" fill="${color}"/></svg>`)}`;
|
|
371
|
+
}
|
|
372
|
+
function hasAttribute(attrs, name) {
|
|
373
|
+
return attrs.some((attr) => t.isJSXIdentifier(attr.name) && attr.name.name === name);
|
|
374
|
+
}
|
|
375
|
+
const transformImg = {
|
|
376
|
+
id: "transform-img",
|
|
377
|
+
match: (ctx) => ctx.element.nodeType === 1 && ctx.element.tagName.toLowerCase() === "img",
|
|
378
|
+
transform: (ctx) => {
|
|
379
|
+
if (!ctx.target) throw new Error("Target must be defined before transforming an image element.");
|
|
380
|
+
const attrs = ctx.target.attributes ?? [];
|
|
381
|
+
const naturalWidthAttr = findStringJsxAttribute(attrs, DATA_NAT_W);
|
|
382
|
+
const naturalHeightAttr = findStringJsxAttribute(attrs, DATA_NAT_H);
|
|
383
|
+
const naturalWidth = naturalWidthAttr?.value?.value;
|
|
384
|
+
const naturalHeight = naturalHeightAttr?.value?.value;
|
|
385
|
+
const placeholderSrc = createSkeletonSrc(naturalWidth, naturalHeight);
|
|
386
|
+
const filteredAttrs = attrs.filter((attr) => {
|
|
387
|
+
if (!t.isJSXAttribute(attr)) return false;
|
|
388
|
+
const name = t.isJSXIdentifier(attr.name) ? attr.name.name : void 0;
|
|
389
|
+
return ![
|
|
390
|
+
"src",
|
|
391
|
+
"srcSet",
|
|
392
|
+
"sizes",
|
|
393
|
+
"loading",
|
|
394
|
+
"decoding",
|
|
395
|
+
"fetchPriority",
|
|
396
|
+
DATA_NAT_W,
|
|
397
|
+
DATA_NAT_H,
|
|
398
|
+
DATA_IMAGE_SKELETON
|
|
399
|
+
].includes(name ?? "");
|
|
400
|
+
});
|
|
401
|
+
const attributes = [
|
|
402
|
+
...filteredAttrs,
|
|
403
|
+
createJsxStringAttribute("src", placeholderSrc),
|
|
404
|
+
createJsxStringAttribute(DATA_IMAGE_SKELETON, "true")
|
|
405
|
+
];
|
|
406
|
+
if (naturalWidth && naturalHeight) {
|
|
407
|
+
if (!hasAttribute(filteredAttrs, "width")) attributes.push(createJsxStringAttribute("width", naturalWidth));
|
|
408
|
+
if (!hasAttribute(filteredAttrs, "height")) attributes.push(createJsxStringAttribute("height", naturalHeight));
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
...ctx,
|
|
412
|
+
target: {
|
|
413
|
+
...ctx.target,
|
|
414
|
+
attributes
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
const INTERACTIVE_TAGS = new Set([
|
|
420
|
+
"input",
|
|
421
|
+
"textarea",
|
|
422
|
+
"select",
|
|
423
|
+
"option",
|
|
424
|
+
"optgroup",
|
|
425
|
+
"button",
|
|
426
|
+
"label",
|
|
427
|
+
"fieldset",
|
|
428
|
+
"legend",
|
|
429
|
+
"output",
|
|
430
|
+
"datalist",
|
|
431
|
+
"a",
|
|
432
|
+
"details",
|
|
433
|
+
"summary",
|
|
434
|
+
"audio",
|
|
435
|
+
"video",
|
|
436
|
+
"iframe",
|
|
437
|
+
"embed",
|
|
438
|
+
"object",
|
|
439
|
+
"dialog"
|
|
440
|
+
]);
|
|
441
|
+
const INTERACTIVE_ROLES = new Set([
|
|
442
|
+
"button",
|
|
443
|
+
"link",
|
|
444
|
+
"tab",
|
|
445
|
+
"menuitem",
|
|
446
|
+
"checkbox",
|
|
447
|
+
"radio",
|
|
448
|
+
"switch",
|
|
449
|
+
"option",
|
|
450
|
+
"combobox",
|
|
451
|
+
"textbox",
|
|
452
|
+
"searchbox",
|
|
453
|
+
"slider",
|
|
454
|
+
"spinbutton",
|
|
455
|
+
"listbox",
|
|
456
|
+
"treeitem",
|
|
457
|
+
"gridcell",
|
|
458
|
+
"rowheader",
|
|
459
|
+
"columnheader"
|
|
460
|
+
]);
|
|
461
|
+
const ARIA_ATTRIBUTES_TO_REMOVE = new Set([
|
|
462
|
+
"role",
|
|
463
|
+
"aria-label",
|
|
464
|
+
"aria-labelledby",
|
|
465
|
+
"aria-describedby",
|
|
466
|
+
"aria-controls",
|
|
467
|
+
"aria-expanded",
|
|
468
|
+
"aria-haspopup",
|
|
469
|
+
"aria-pressed",
|
|
470
|
+
"aria-selected",
|
|
471
|
+
"aria-checked",
|
|
472
|
+
"aria-current",
|
|
473
|
+
"contenteditable",
|
|
474
|
+
"tabindex",
|
|
475
|
+
"aria-hidden",
|
|
476
|
+
"data-skeleton-interactive"
|
|
477
|
+
]);
|
|
478
|
+
const transformInteractiveElements = {
|
|
479
|
+
id: "transform-interactive-elements",
|
|
480
|
+
match: (ctx) => {
|
|
481
|
+
if (ctx.element.nodeType !== 1) return false;
|
|
482
|
+
const element = ctx.element;
|
|
483
|
+
const tag = element.tagName.toLowerCase();
|
|
484
|
+
if (INTERACTIVE_TAGS.has(tag)) return true;
|
|
485
|
+
const role = element.getAttribute("role")?.toLowerCase();
|
|
486
|
+
if (role && INTERACTIVE_ROLES.has(role)) return true;
|
|
487
|
+
if (element.hasAttribute("contenteditable")) return true;
|
|
488
|
+
if (element.hasAttribute("tabindex")) return true;
|
|
489
|
+
return false;
|
|
490
|
+
},
|
|
491
|
+
transform: (ctx) => {
|
|
492
|
+
if (!ctx.target) return ctx;
|
|
493
|
+
const attributes = (ctx.target.attributes ?? []).filter((attribute) => {
|
|
494
|
+
const name = attribute.type === "JSXAttribute" && attribute.name?.name ? String(attribute.name.name).toLowerCase() : "";
|
|
495
|
+
return !ARIA_ATTRIBUTES_TO_REMOVE.has(name);
|
|
496
|
+
});
|
|
497
|
+
attributes.push(createJsxStringAttribute("data-skeleton-interactive", "true"), createJsxStringAttribute("aria-hidden", "true"), createJsxExpressionAttribute("tabIndex", t.numericLiteral(-1)));
|
|
498
|
+
return {
|
|
499
|
+
...ctx,
|
|
500
|
+
target: {
|
|
501
|
+
...ctx.target,
|
|
502
|
+
attributes
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
const SAFE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
|
|
508
|
+
function spacePreservedObfuscator(input) {
|
|
509
|
+
if (!input) return null;
|
|
510
|
+
let seed = 2166136261;
|
|
511
|
+
for (const char of input) {
|
|
512
|
+
seed ^= char.charCodeAt(0);
|
|
513
|
+
seed = Math.imul(seed, 16777619);
|
|
514
|
+
}
|
|
515
|
+
const len = 66;
|
|
516
|
+
return [...input].map((char, i) => {
|
|
517
|
+
if (char === " ") return " ";
|
|
518
|
+
seed ^= seed << 13;
|
|
519
|
+
seed ^= seed >>> 17;
|
|
520
|
+
seed ^= seed << 5;
|
|
521
|
+
return SAFE_CHARS[Math.abs(seed + i) % len];
|
|
522
|
+
}).join("");
|
|
523
|
+
}
|
|
524
|
+
const executor = buildExecutor([
|
|
525
|
+
{
|
|
526
|
+
id: "transform-text",
|
|
527
|
+
skipAllRest: true,
|
|
528
|
+
match: (ctx) => ctx.element.nodeType === 3,
|
|
529
|
+
transform: (ctx) => {
|
|
530
|
+
const text = spacePreservedObfuscator(ctx.element.textContent?.trim());
|
|
531
|
+
if (!text) return ctx;
|
|
532
|
+
const isHeading = ctx.parentNode?.nodeName.toLowerCase().startsWith("h");
|
|
533
|
+
if (!["p", "label"].includes(ctx.parentNode?.nodeName.toLowerCase() ?? "") && !isHeading) return {
|
|
534
|
+
...ctx,
|
|
535
|
+
target: {
|
|
536
|
+
element: "TEXT",
|
|
537
|
+
children: [jsxText(text)]
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
return {
|
|
541
|
+
...ctx,
|
|
542
|
+
target: {
|
|
543
|
+
element: "span",
|
|
544
|
+
attributes: [
|
|
545
|
+
createJsxStringAttribute("className", TEXT_CLASSNAME),
|
|
546
|
+
createJsxStringAttribute("data-text-node", "true"),
|
|
547
|
+
createJsxStringAttribute(DEPTH_ATTRIBUTE, ctx.parentDepth ?? String(ctx.depth))
|
|
548
|
+
],
|
|
549
|
+
children: [jsxText(text)]
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
removeReduntantNode,
|
|
555
|
+
populateAttrs,
|
|
556
|
+
processSvg,
|
|
557
|
+
transformImg,
|
|
558
|
+
transformInteractiveElements,
|
|
559
|
+
addRootSkeletonsAttrs
|
|
560
|
+
]);
|
|
561
|
+
function excarnate(element, depth, parentNode = null, parentDepth) {
|
|
562
|
+
const target = executor({
|
|
563
|
+
element,
|
|
564
|
+
depth,
|
|
565
|
+
parentNode,
|
|
566
|
+
parentDepth
|
|
567
|
+
}).target;
|
|
568
|
+
if (!target) return null;
|
|
569
|
+
const { children, element: tagName, attributes } = target;
|
|
570
|
+
if (tagName === "TEXT") return children?.[0] ?? null;
|
|
571
|
+
if (children) return createJsxElement(tagName, children, attributes);
|
|
572
|
+
const { childNodes } = element ?? { childNodes: [] };
|
|
573
|
+
return createJsxElement(tagName, Array.from(childNodes).map((child) => excarnate(child, depth + 1, element, depth.toString())).filter(Boolean), attributes);
|
|
574
|
+
}
|
|
575
|
+
//#endregion
|
|
576
|
+
//#region src/transform/index.ts
|
|
577
|
+
const generate = pkg.default;
|
|
578
|
+
function transformInput(html) {
|
|
579
|
+
const root = new JSDOM(html).window.document.body.firstElementChild;
|
|
580
|
+
if (!root) throw new Error("No root element found");
|
|
581
|
+
const jsxAst = excarnate(root, 0);
|
|
582
|
+
if (!jsxAst) throw new Error("Failed to generate JSX AST");
|
|
583
|
+
return generate(jsxAst, { jsescOption: { minimal: true } }).code;
|
|
584
|
+
}
|
|
585
|
+
//#endregion
|
|
586
|
+
//#region src/utils/to-pascal-case.ts
|
|
587
|
+
function toPascalCase(input) {
|
|
588
|
+
return input.trim().replace(/['"]/g, "").split(/[^a-zA-Z0-9]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
589
|
+
}
|
|
590
|
+
//#endregion
|
|
591
|
+
//#region src/utils/fnv1a.ts
|
|
592
|
+
function fnv1a(str) {
|
|
593
|
+
let hash = 2166136261;
|
|
594
|
+
for (let i = 0; i < str.length; i++) {
|
|
595
|
+
hash ^= str.charCodeAt(i);
|
|
596
|
+
hash = Math.imul(hash, 16777619);
|
|
597
|
+
}
|
|
598
|
+
return hash >>> 0;
|
|
599
|
+
}
|
|
600
|
+
//#endregion
|
|
601
|
+
//#region src/commands/serve.ts
|
|
602
|
+
async function serveCommand(config, port) {
|
|
603
|
+
const app = new Hono();
|
|
604
|
+
app.use("*", cors({
|
|
605
|
+
origin: "*",
|
|
606
|
+
allowMethods: [
|
|
607
|
+
"GET",
|
|
608
|
+
"POST",
|
|
609
|
+
"OPTIONS"
|
|
610
|
+
],
|
|
611
|
+
allowHeaders: ["Content-Type", "Authorization"]
|
|
612
|
+
}));
|
|
613
|
+
const db = await SkeletonCacheDB.create(config);
|
|
614
|
+
app.get("/health", (c) => {
|
|
615
|
+
return c.json({ ok: true });
|
|
616
|
+
});
|
|
617
|
+
app.post("/skeletons", async (c) => {
|
|
618
|
+
const body = await c.req.json();
|
|
619
|
+
const result = SkeletonPayloadInputSchema.parse(body);
|
|
620
|
+
const project = new Project();
|
|
621
|
+
const entries = Object.entries(result).map(async ([key, value]) => {
|
|
622
|
+
const componentName = toPascalCase(value.component);
|
|
623
|
+
const transformedHtml = transformInput(value.html);
|
|
624
|
+
const hash = fnv1a(transformedHtml).toString();
|
|
625
|
+
const cached = db.get(key);
|
|
626
|
+
if (cached && cached.hash === hash) return;
|
|
627
|
+
generateTarget({
|
|
628
|
+
project,
|
|
629
|
+
filePath: `${config.getSkeletonPath()}/${componentName}.${config.getExt()}`,
|
|
630
|
+
componentName,
|
|
631
|
+
body: transformedHtml
|
|
632
|
+
});
|
|
633
|
+
await db.set(key, {
|
|
634
|
+
component: value.component,
|
|
635
|
+
html: transformedHtml,
|
|
636
|
+
hash
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
await Promise.all(entries);
|
|
640
|
+
config.registry({
|
|
641
|
+
type: "add",
|
|
642
|
+
components: db.getComponentNames()
|
|
643
|
+
});
|
|
644
|
+
await project.save();
|
|
645
|
+
return c.json({ sucess: true });
|
|
646
|
+
});
|
|
647
|
+
serve({
|
|
648
|
+
fetch: app.fetch,
|
|
649
|
+
port
|
|
650
|
+
}, (info) => {
|
|
651
|
+
log.info(`Server running at http://localhost:${info.port}`);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
//#endregion
|
|
655
|
+
//#region src/init/is-file-exists.ts
|
|
656
|
+
async function fileExists(path) {
|
|
657
|
+
try {
|
|
658
|
+
await access(path);
|
|
659
|
+
return true;
|
|
660
|
+
} catch {
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
//#endregion
|
|
665
|
+
//#region src/constants.ts
|
|
666
|
+
const CONFIG_PATH = "skullmaster.config.json";
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/init/collect-preferences.ts
|
|
669
|
+
async function collectPreferences() {
|
|
670
|
+
if (await fileExists("skullmaster.config.json")) return JSON.parse(await readFile$1(`./${CONFIG_PATH}`, "utf-8"));
|
|
671
|
+
const outDir = await text({
|
|
672
|
+
message: "Where do you want to output the generated files?",
|
|
673
|
+
placeholder: "src/skeletons",
|
|
674
|
+
initialValue: "src/skeletons",
|
|
675
|
+
validate(value) {
|
|
676
|
+
if (value?.length === 0) return `Value is required!`;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
if (isCancel(outDir)) {
|
|
680
|
+
cancel("Operation cancelled.");
|
|
681
|
+
process.exit(0);
|
|
682
|
+
}
|
|
683
|
+
const projectType = await select({
|
|
684
|
+
message: "Pick a project type.",
|
|
685
|
+
initialValue: "react-ts",
|
|
686
|
+
options: [{
|
|
687
|
+
value: "react-ts",
|
|
688
|
+
label: "React with TypeScript"
|
|
689
|
+
}, {
|
|
690
|
+
value: "react-js",
|
|
691
|
+
label: "React"
|
|
692
|
+
}]
|
|
693
|
+
});
|
|
694
|
+
if (isCancel(projectType)) {
|
|
695
|
+
cancel("Operation cancelled.");
|
|
696
|
+
process.exit(0);
|
|
697
|
+
}
|
|
698
|
+
const preferences = {
|
|
699
|
+
outDir,
|
|
700
|
+
project: projectType.toString()
|
|
701
|
+
};
|
|
702
|
+
await writeFile$1(`./${CONFIG_PATH}`, JSON.stringify(preferences, null, 2), "utf-8");
|
|
703
|
+
return preferences;
|
|
704
|
+
}
|
|
705
|
+
//#endregion
|
|
706
|
+
//#region src/init/ensure-lazy-imports.ts
|
|
707
|
+
function ensureLazyImport(sourceFile) {
|
|
708
|
+
const reactImport = sourceFile.getImportDeclaration((d) => d.getModuleSpecifierValue() === "react");
|
|
709
|
+
if (!reactImport) {
|
|
710
|
+
sourceFile.addImportDeclaration({
|
|
711
|
+
moduleSpecifier: "react",
|
|
712
|
+
namedImports: ["lazy"]
|
|
713
|
+
});
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (!reactImport.getNamedImports().some((i) => i.getName() === "lazy")) reactImport.addNamedImport("lazy");
|
|
717
|
+
}
|
|
718
|
+
//#endregion
|
|
719
|
+
//#region src/init/registry.ts
|
|
720
|
+
async function generateInitialRegistry(outDir, projectType) {
|
|
721
|
+
const isTs = projectType.endsWith("ts");
|
|
722
|
+
const ext = isTs ? "tsx" : "jsx";
|
|
723
|
+
const skeletonPropsType = isTs ? `
|
|
724
|
+
type SkeletonProps = {
|
|
725
|
+
name: string;
|
|
726
|
+
};
|
|
727
|
+
` : "";
|
|
728
|
+
await writeFile(`${outDir}/registry.${ext}`, `
|
|
729
|
+
/* eslint-disable */
|
|
730
|
+
/* oxlint-disable */
|
|
731
|
+
/* biome-ignore-all lint */
|
|
732
|
+
/* @ts-nocheck */
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Auto-generated by Skullmaster.
|
|
736
|
+
*
|
|
737
|
+
* Do not edit this file manually.
|
|
738
|
+
* Use Skullmaster commands to add, update, or remove skeletons.
|
|
739
|
+
*
|
|
740
|
+
* To remove unused skeletons:
|
|
741
|
+
* pnpm skeleton prune
|
|
742
|
+
*/
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
import DefaultBone from "./skeletons/DefaultBone";
|
|
746
|
+
|
|
747
|
+
const registry${isTs ? ": Record<string, any>" : ""} = {};
|
|
748
|
+
|
|
749
|
+
${skeletonPropsType}
|
|
750
|
+
|
|
751
|
+
export default function Skeleton({
|
|
752
|
+
name,
|
|
753
|
+
}${isTs ? ": SkeletonProps" : ""}) {
|
|
754
|
+
const Component = registry[name];
|
|
755
|
+
|
|
756
|
+
if (!Component) {
|
|
757
|
+
return <DefaultBone />;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return <Component />;
|
|
761
|
+
}
|
|
762
|
+
`, "utf8");
|
|
763
|
+
}
|
|
764
|
+
async function generateRegistry(operation, isTs, registryPath) {
|
|
765
|
+
let project;
|
|
766
|
+
if (isTs) project = new Project({ tsConfigFilePath: "tsconfig.json" });
|
|
767
|
+
else project = new Project();
|
|
768
|
+
const sourceFile = project.addSourceFileAtPath(registryPath);
|
|
769
|
+
ensureLazyImport(sourceFile);
|
|
770
|
+
const registry = sourceFile.getVariableDeclaration("registry")?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
771
|
+
if (!registry) throw new Error("Unable to locate registry object, have you ran skullmaster init??");
|
|
772
|
+
const existingComponents = new Set(registry.getProperties().filter((property) => property.getKind() === SyntaxKind.PropertyAssignment).map((property) => property.asKindOrThrow(SyntaxKind.PropertyAssignment)).map((property) => property.getName().replace(/['"]/g, "")));
|
|
773
|
+
for (const component of operation.components) {
|
|
774
|
+
if (existingComponents.has(component)) continue;
|
|
775
|
+
registry.addPropertyAssignment({
|
|
776
|
+
name: `"${component}"`,
|
|
777
|
+
initializer: `lazy(() => import("./skeletons/${component}"))`
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
sourceFile.formatText();
|
|
781
|
+
await sourceFile.save();
|
|
782
|
+
project.saveSync();
|
|
783
|
+
}
|
|
784
|
+
//#endregion
|
|
785
|
+
//#region src/init/cache-registry.ts
|
|
786
|
+
async function updateCacheRegistry(operation, outDir) {
|
|
787
|
+
const cacheFilePath = outDir;
|
|
788
|
+
let cacheEntries = {};
|
|
789
|
+
if (!await fileExists(cacheFilePath)) await writeFile(cacheFilePath, JSON.stringify({}), "utf8");
|
|
790
|
+
else cacheEntries = SkeletonCacheEntrySchema.parse(JSON.parse(await readFile(cacheFilePath, "utf8")));
|
|
791
|
+
switch (operation.type) {
|
|
792
|
+
case "replace":
|
|
793
|
+
await writeFile(cacheFilePath, JSON.stringify(operation.cacheEntry), "utf8");
|
|
794
|
+
break;
|
|
795
|
+
case "delete":
|
|
796
|
+
delete cacheEntries[operation.componentName];
|
|
797
|
+
await writeFile(cacheFilePath, JSON.stringify(cacheEntries), "utf8");
|
|
798
|
+
break;
|
|
799
|
+
case "update":
|
|
800
|
+
cacheEntries[operation.componentName] = {
|
|
801
|
+
...operation.payload,
|
|
802
|
+
hash: fnv1a(operation.payload.html).toString()
|
|
803
|
+
};
|
|
804
|
+
await writeFile(cacheFilePath, JSON.stringify(cacheEntries), "utf8");
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
//#endregion
|
|
808
|
+
//#region src/init/default-bone.ts
|
|
809
|
+
async function defaultBone(target) {
|
|
810
|
+
await writeFile(target, `/* eslint-disable */
|
|
811
|
+
/* oxlint-disable */
|
|
812
|
+
/* biome-ignore-all lint */
|
|
813
|
+
/* @ts-nocheck */
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Auto-generated by Skullmaster.
|
|
817
|
+
*
|
|
818
|
+
* This is the default fallback skeleton component displayed when a
|
|
819
|
+
* matching skeleton implementation cannot be found in the registry.
|
|
820
|
+
*
|
|
821
|
+
* You may freely customize or replace this component to match the
|
|
822
|
+
* loading experience of your application.
|
|
823
|
+
*/
|
|
824
|
+
export default function DefaultBone() {
|
|
825
|
+
return <div>loading...</div>;
|
|
826
|
+
}
|
|
827
|
+
`, "utf8");
|
|
828
|
+
}
|
|
829
|
+
//#endregion
|
|
830
|
+
//#region src/init/preferences.ts
|
|
831
|
+
var Config = class {
|
|
832
|
+
outDir;
|
|
833
|
+
projectType;
|
|
834
|
+
constructor(outDir, projectType) {
|
|
835
|
+
this.outDir = outDir;
|
|
836
|
+
this.projectType = projectType;
|
|
837
|
+
}
|
|
838
|
+
async initialize() {
|
|
839
|
+
log.info("Initializing project structure...");
|
|
840
|
+
if (!await fileExists(this.outDir)) {
|
|
841
|
+
mkdirSync(this.outDir, { recursive: true });
|
|
842
|
+
log.success(`Created output directory: ${this.outDir}`);
|
|
843
|
+
} else log.gray(`Output directory exists: ${this.outDir}`);
|
|
844
|
+
const skeletonsDirectoryPath = this.getSkeletonPath();
|
|
845
|
+
if (!await fileExists(skeletonsDirectoryPath)) {
|
|
846
|
+
mkdirSync(skeletonsDirectoryPath, { recursive: true });
|
|
847
|
+
log.success(`Created skeletons directory: ${skeletonsDirectoryPath}`);
|
|
848
|
+
} else log.gray(`Skeletons directory exists: ${skeletonsDirectoryPath}`);
|
|
849
|
+
const registryFilePath = this.getRegistryPath();
|
|
850
|
+
if (!await fileExists(registryFilePath)) {
|
|
851
|
+
await generateInitialRegistry(this.outDir, this.projectType);
|
|
852
|
+
log.success(`Created registry: ${registryFilePath}`);
|
|
853
|
+
} else log.gray(`Registry exists: ${registryFilePath}`);
|
|
854
|
+
const cachePath = this.getCachePath();
|
|
855
|
+
if (!await fileExists(cachePath)) {
|
|
856
|
+
writeFileSync(cachePath, JSON.stringify({}), "utf8");
|
|
857
|
+
log.success(`Created cache: ${cachePath}`);
|
|
858
|
+
} else log.gray(`Cache exists: ${cachePath}`);
|
|
859
|
+
const defaultBonePath = this.getDefSkeletonPath();
|
|
860
|
+
if (!await fileExists(defaultBonePath)) {
|
|
861
|
+
await defaultBone(defaultBonePath);
|
|
862
|
+
log.success(`Created default bone: ${defaultBonePath}`);
|
|
863
|
+
} else log.gray(`Default bone exists: ${defaultBonePath}`);
|
|
864
|
+
log.success("Initialization complete");
|
|
865
|
+
}
|
|
866
|
+
async cache(op) {
|
|
867
|
+
return await updateCacheRegistry(op, this.getCachePath());
|
|
868
|
+
}
|
|
869
|
+
async registry(op) {
|
|
870
|
+
return generateRegistry(op, this.projectType.endsWith("ts"), this.getRegistryPath());
|
|
871
|
+
}
|
|
872
|
+
getCachePath() {
|
|
873
|
+
return `${this.outDir}/cache.json`;
|
|
874
|
+
}
|
|
875
|
+
getSkeletonPath() {
|
|
876
|
+
return `${this.outDir}/skeletons`;
|
|
877
|
+
}
|
|
878
|
+
getDefSkeletonPath() {
|
|
879
|
+
return `${this.getSkeletonPath()}/DefaultBone.${this.getExt()}`;
|
|
880
|
+
}
|
|
881
|
+
getExt() {
|
|
882
|
+
return this.projectType.endsWith("ts") ? "tsx" : "jsx";
|
|
883
|
+
}
|
|
884
|
+
getRegistryPath() {
|
|
885
|
+
return `${this.outDir}/registry.${this.getExt()}`;
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
//#endregion
|
|
889
|
+
//#region src/cli.ts
|
|
890
|
+
const cli = cac("💀");
|
|
891
|
+
cli.command("serve", "Start 💀 dev server").option("--port <port>", "Port", { default: DEFAULT_PORT }).action(async (options) => {
|
|
892
|
+
const preferences = await collectPreferences();
|
|
893
|
+
const config = new Config(preferences.outDir, preferences.project);
|
|
894
|
+
await config.initialize();
|
|
895
|
+
await serveCommand(config, Number(options.port));
|
|
896
|
+
});
|
|
897
|
+
cli.help();
|
|
898
|
+
cli.parse();
|
|
899
|
+
//#endregion
|
|
900
|
+
export {};
|
|
901
|
+
|
|
902
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["htmlNodeToJsx","readFile","writeFile"],"sources":["../src/shared/constants.ts","../src/shared/schema.ts","../src/shared/log.ts","../src/cache/index.ts","../src/generate-target/index.ts","../../excarnate/dist/index.mjs","../src/transform/index.ts","../src/utils/to-pascal-case.ts","../src/utils/fnv1a.ts","../src/commands/serve.ts","../src/init/is-file-exists.ts","../src/constants.ts","../src/init/collect-preferences.ts","../src/init/ensure-lazy-imports.ts","../src/init/registry.ts","../src/init/cache-registry.ts","../src/init/default-bone.ts","../src/init/preferences.ts","../src/cli.ts"],"sourcesContent":["export const DEFAULT_PORT = 8008;\nexport const EMPTY_SET_DEFAULT_DIR = \"skullmaster\";\nexport const DATA_NAT_H = \"data-natural-h\";\nexport const DATA_NAT_W = \"data-natural-w\";\n","import z from \"zod\";\n\nexport const SkeletonPayloadInputSchema = z.record(\n z.string(),\n z.object({\n component: z.string(),\n html: z.string(),\n }),\n);\n\nexport const SkeletonCacheEntrySchema = z.record(\n z.string(),\n z.object({\n component: z.string(),\n html: z.string(),\n hash: z.string(),\n }),\n);\n\nexport type SkeletonPayloadInput = z.infer<typeof SkeletonPayloadInputSchema>;\n\nexport type SkeletonCacheEntry = z.infer<typeof SkeletonCacheEntrySchema>;\n","export const log = {\n success: (message: string) => console.log(`\\x1b[32m[💀] ${message}\\x1b[0m`),\n info: (message: string) => console.log(`\\x1b[36m[💀] ${message}\\x1b[0m`),\n warn: (message: string) => console.log(`\\x1b[33m[💀] ${message}\\x1b[0m`),\n error: (message: string) => console.log(`\\x1b[31m[💀] ${message}\\x1b[0m`),\n gray: (message: string) => console.log(`\\x1b[90m[💀] ${message}\\x1b[0m`),\n};\n","import { readFile } from \"node:fs/promises\";\nimport type { Config } from \"../init/preferences\";\nimport { type SkeletonCacheEntry, SkeletonCacheEntrySchema } from \"../shared\";\n\nexport class SkeletonCacheDB {\n private data: SkeletonCacheEntry = {};\n config: Config;\n private constructor(config: Config) {\n this.config = config;\n }\n\n static async create(config: Config) {\n const db = new SkeletonCacheDB(config);\n\n await db.load();\n\n return db;\n }\n\n private async load() {\n try {\n const file = await readFile(this.config.getCachePath(), \"utf8\");\n\n const parsed = JSON.parse(file);\n\n this.data = SkeletonCacheEntrySchema.parse(parsed);\n } catch {\n this.data = {};\n\n await this.config.cache({ type: \"replace\", cacheEntry: {} });\n }\n }\n\n get(key: string) {\n return this.data[key];\n }\n getComponentNames(): string[] {\n return Object.values(this.data).map((entry) => entry.component);\n }\n\n async set(key: string, value: SkeletonCacheEntry[string]) {\n this.data[key] = value;\n this.config.cache({ type: \"update\", componentName: key, payload: value });\n }\n}\n","import { Project, QuoteKind, ScriptKind } from \"ts-morph\";\n\ntype GenerateTargetOptions = {\n project: Project;\n filePath: string;\n componentName: string;\n body: string;\n};\n\nexport default function generateTarget({\n project,\n filePath,\n componentName,\n body,\n}: GenerateTargetOptions) {\n project.manipulationSettings.set({\n quoteKind: QuoteKind.Double,\n });\n\n const file = project.createSourceFile(filePath, \"\", {\n overwrite: true,\n scriptKind: ScriptKind.TSX,\n });\n\n file.insertText(\n 0,\n `/* eslint-disable */\n/* prettier-ignore-start */\n\n/**\n * Auto-generated by Skullmaster.\n *\n * DO NOT EDIT THIS FILE MANUALLY.\n * Changes may be overwritten on the next generation.\n */\n\n`,\n );\n\n file.addFunction({\n name: componentName,\n isDefaultExport: true,\n statements: [`return (${body});`],\n });\n\n file.addStatements(`\n/* prettier-ignore-end */\n`);\n}\n","import * as t from \"@babel/types\";\nimport { jsxText } from \"@babel/types\";\n//#region src/executor.ts\nfunction buildExecutor(rules) {\n\treturn (ctx) => {\n\t\tlet current = ctx;\n\t\tfor (const rule of rules) {\n\t\t\tif (!rule.match(current)) continue;\n\t\t\tcurrent = rule.transform(current);\n\t\t\tif (rule.skipAllRest) break;\n\t\t}\n\t\treturn current;\n\t};\n}\n//#endregion\n//#region src/helpers/jsx.ts\nfunction createJsxStringAttribute(name, value) {\n\treturn t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(value));\n}\nfunction createJsxExpressionAttribute(name, expression) {\n\treturn t.jsxAttribute(t.jsxIdentifier(name), t.jsxExpressionContainer(expression));\n}\nfunction findJsxAttribute(attributes, name) {\n\treturn attributes.find((attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === name);\n}\nfunction findStringJsxAttribute(attributes, name) {\n\tconst attr = findJsxAttribute(attributes, name);\n\tif (!attr?.value || !t.isStringLiteral(attr.value)) return;\n\treturn attr;\n}\nfunction createJsxElement(tagName, children, attributes = []) {\n\treturn t.jsxElement(t.jsxOpeningElement(t.jsxIdentifier(tagName), attributes, false), t.jsxClosingElement(t.jsxIdentifier(tagName)), children, false);\n}\n//#endregion\n//#region src/constants.ts\nconst DEPTH_ATTRIBUTE = \"data-depth\";\nconst TEXT_CLASSNAME = \"empty-set__text\";\nconst SKELETON_CLASSNAME = \"empty-set__skeleton\";\n//#endregion\n//#region src/exceptions/target-mismatch.ts\nvar TargetElementMismatchError = class extends Error {\n\tconstructor(expected, actual) {\n\t\tsuper(`Expected target element to be ${expected}, but got ${actual}`);\n\t\tthis.name = \"TargetElementMismatchError\";\n\t}\n};\n//#endregion\n//#region src/helpers/attribute-replace-map.ts\nconst ATTRIBUTE_MAP = {\n\tclass: \"className\",\n\tfor: \"htmlFor\",\n\ttabindex: \"tabIndex\",\n\treadonly: \"readOnly\",\n\tmaxlength: \"maxLength\",\n\tminlength: \"minLength\",\n\tcolspan: \"colSpan\",\n\trowspan: \"rowSpan\",\n\tcontenteditable: \"contentEditable\",\n\tautocapitalize: \"autoCapitalize\",\n\tautocomplete: \"autoComplete\",\n\tautofocus: \"autoFocus\",\n\tspellcheck: \"spellCheck\",\n\tcrossorigin: \"crossOrigin\",\n\treferrerpolicy: \"referrerPolicy\",\n\tstrokewidth: \"strokeWidth\",\n\t\"stroke-width\": \"strokeWidth\",\n\tstrokelinecap: \"strokeLinecap\",\n\t\"stroke-linecap\": \"strokeLinecap\",\n\tstrokelinejoin: \"strokeLinejoin\",\n\t\"stroke-linejoin\": \"strokeLinejoin\",\n\tstrokemiterlimit: \"strokeMiterlimit\",\n\t\"stroke-miterlimit\": \"strokeMiterlimit\",\n\tstrokedasharray: \"strokeDasharray\",\n\t\"stroke-dasharray\": \"strokeDasharray\",\n\tstrokedashoffset: \"strokeDashoffset\",\n\t\"stroke-dashoffset\": \"strokeDashoffset\",\n\tstrokeopacity: \"strokeOpacity\",\n\t\"stroke-opacity\": \"strokeOpacity\",\n\tfillopacity: \"fillOpacity\",\n\t\"fill-opacity\": \"fillOpacity\",\n\tfillrule: \"fillRule\",\n\t\"fill-rule\": \"fillRule\",\n\tcliprule: \"clipRule\",\n\t\"clip-rule\": \"clipRule\",\n\tclippath: \"clipPath\",\n\t\"clip-path\": \"clipPath\",\n\tstopcolor: \"stopColor\",\n\t\"stop-color\": \"stopColor\",\n\tstopopacity: \"stopOpacity\",\n\t\"stop-opacity\": \"stopOpacity\",\n\tviewbox: \"viewBox\",\n\txlinkhref: \"xlinkHref\",\n\t\"xlink:href\": \"xlinkHref\",\n\txmlspace: \"xmlSpace\",\n\t\"xml:space\": \"xmlSpace\"\n};\n//#endregion\n//#region src/helpers/attr-normalize.ts\nfunction normalizeAttributeName(name) {\n\tconst lower = name.toLowerCase();\n\tif (ATTRIBUTE_MAP[lower]) return ATTRIBUTE_MAP[lower];\n\tif (lower.startsWith(\"data-\") || lower.startsWith(\"aria-\")) return lower;\n\treturn name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());\n}\n//#endregion\n//#region src/helpers/is-style.ts\nconst STYLE_ATTRIBUTES = new Set([\n\t\"class\",\n\t\"style\",\n\t\"type\",\n\t\"fill\",\n\t\"fill-opacity\",\n\t\"fill-rule\",\n\t\"src\",\n\t\"stroke\",\n\t\"stroke-width\",\n\t\"stroke-opacity\",\n\t\"stroke-linecap\",\n\t\"stroke-linejoin\",\n\t\"stroke-dasharray\",\n\t\"stroke-dashoffset\",\n\t\"opacity\",\n\t\"role\",\n\t\"width\",\n\t\"height\",\n\t\"x\",\n\t\"y\",\n\t\"cx\",\n\t\"cy\",\n\t\"r\",\n\t\"rx\",\n\t\"ry\",\n\t\"viewbox\",\n\t\"transform\",\n\t\"d\",\n\t\"points\"\n]);\nfunction shouldKeepAttribute(name) {\n\tconst lowerName = name.toLowerCase();\n\treturn lowerName.startsWith(\"data-\") || lowerName.startsWith(\"aria-\") || STYLE_ATTRIBUTES.has(lowerName);\n}\n//#endregion\n//#region src/helpers/parse-style.ts\nfunction parseStyle(style) {\n\tconst properties = [];\n\tfor (const rule of style.split(\";\")) {\n\t\tconst trimmedRule = rule.trim();\n\t\tif (!trimmedRule) continue;\n\t\tconst colonIndex = trimmedRule.indexOf(\":\");\n\t\tif (colonIndex === -1) continue;\n\t\tconst rawKey = trimmedRule.slice(0, colonIndex).trim();\n\t\tconst rawValue = trimmedRule.slice(colonIndex + 1).trim();\n\t\tconst key = rawKey.replace(/-([a-z])/g, (_, char) => char.toUpperCase());\n\t\tconst keyNode = key.startsWith(\"--\") || key.includes(\"-\") ? t.stringLiteral(key) : t.identifier(key);\n\t\tproperties.push(t.objectProperty(keyNode, t.stringLiteral(rawValue)));\n\t}\n\treturn t.objectExpression(properties);\n}\n//#endregion\n//#region src/rules/populate-attrs.ts\nconst populateAttrs = {\n\tid: \"populate-attrs\",\n\tmatch: (ctx) => ctx.element.nodeType === 1,\n\ttransform: (ctx) => {\n\t\tconst element = ctx.element;\n\t\tconst attributes = [];\n\t\tconst tagName = element.tagName.toLowerCase();\n\t\tif (ctx.target?.element && ctx.target?.element !== tagName) throw new TargetElementMismatchError(tagName, ctx.target.element);\n\t\tif (!element.hasAttribute(\"data-depth\")) attributes.push(createJsxStringAttribute(DEPTH_ATTRIBUTE, String(ctx.depth)));\n\t\tfor (const attr of element.attributes) {\n\t\t\tif (!shouldKeepAttribute(attr.name)) continue;\n\t\t\tconst name = normalizeAttributeName(attr.name);\n\t\t\tconst value = attr.value;\n\t\t\tif (name === \"style\") {\n\t\t\t\tattributes.push(createJsxExpressionAttribute(\"style\", parseStyle(value)));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tattributes.push(createJsxStringAttribute(name, value));\n\t\t}\n\t\treturn {\n\t\t\t...ctx,\n\t\t\ttarget: {\n\t\t\t\telement: tagName,\n\t\t\t\tattributes\n\t\t\t}\n\t\t};\n\t}\n};\n//#endregion\n//#region src/matchers.ts\nconst atDepth = (depth) => (ctx) => ctx.depth === depth;\nconst isTagNamesIn = (elements) => (ctx) => ctx.element.nodeType === 1 && elements.includes(ctx.element.tagName.toLowerCase());\n//#endregion\n//#region src/rules/process-svg.ts\nconst processSvg = {\n\tid: \"process-svg\",\n\tskipAllRest: true,\n\tmatch: isTagNamesIn([\"svg\"]),\n\ttransform: (ctx) => {\n\t\treturn {\n\t\t\t...ctx,\n\t\t\ttarget: {\n\t\t\t\t...ctx.target,\n\t\t\t\telement: \"svg\",\n\t\t\t\tchildren: []\n\t\t\t}\n\t\t};\n\t}\n};\n//#endregion\n//#region src/rules/remove-node.ts\nconst removeReduntantNode = {\n\tid: \"remove-node\",\n\tskipAllRest: true,\n\tmatch: isTagNamesIn([\n\t\t\"html\",\n\t\t\"script\",\n\t\t\"style\",\n\t\t\"head\",\n\t\t\"meta\",\n\t\t\"link\",\n\t\t\"noscript\",\n\t\t\"template\",\n\t\t\"audio\",\n\t\t\"video\",\n\t\t\"source\",\n\t\t\"track\",\n\t\t\"iframe\",\n\t\t\"embed\",\n\t\t\"object\",\n\t\t\"title\",\n\t\t\"base\",\n\t\t\"defs\",\n\t\t\"symbol\",\n\t\t\"html\",\n\t\t\"canvas\",\n\t\t\"map\",\n\t\t\"area\",\n\t\t\"picture\",\n\t\t\"portal\"\n\t]),\n\ttransform: (ctx) => {\n\t\treturn {\n\t\t\t...ctx,\n\t\t\ttarget: null\n\t\t};\n\t}\n};\n//#endregion\n//#region src/rules/root-skeleton-attrs.ts\nconst addRootSkeletonsAttrs = {\n\tid: \"add-root-skeleton-attrs\",\n\tmatch: (ctx) => ctx.element.nodeType === 1 && atDepth(0)(ctx),\n\ttransform: (ctx) => {\n\t\tconst tagName = ctx.element.tagName.toLowerCase();\n\t\tif (ctx.target?.element && ctx.target.element !== tagName) throw new TargetElementMismatchError(tagName, ctx.target.element);\n\t\tconst existingTarget = ctx.target ?? {\n\t\t\telement: tagName,\n\t\t\tattributes: []\n\t\t};\n\t\tconst existingAttrs = existingTarget.attributes ?? [];\n\t\tconst classNameAttr = findStringJsxAttribute(existingAttrs, \"className\");\n\t\tconst attributes = [...classNameAttr ? existingAttrs.map((attr) => attr === classNameAttr ? {\n\t\t\t...attr,\n\t\t\tvalue: {\n\t\t\t\t...classNameAttr.value,\n\t\t\t\tvalue: `${classNameAttr.value.value} ${SKELETON_CLASSNAME}`\n\t\t\t}\n\t\t} : attr) : [...existingAttrs, createJsxStringAttribute(\"className\", SKELETON_CLASSNAME)]];\n\t\tif (!findJsxAttribute(attributes, \"role\")) attributes.push(createJsxStringAttribute(\"role\", \"status\"));\n\t\tif (!findJsxAttribute(attributes, \"aria-live\")) attributes.push(createJsxStringAttribute(\"aria-live\", \"polite\"));\n\t\tif (!findJsxAttribute(attributes, \"aria-busy\")) attributes.push(createJsxStringAttribute(\"aria-busy\", \"true\"));\n\t\treturn {\n\t\t\t...ctx,\n\t\t\ttarget: {\n\t\t\t\t...existingTarget,\n\t\t\t\telement: tagName,\n\t\t\t\tattributes\n\t\t\t}\n\t\t};\n\t}\n};\n//#endregion\n//#region src/rules/transform-img.ts\nconst DATA_NAT_H = \"data-natural-h\";\nconst DATA_NAT_W = \"data-natural-w\";\nconst DATA_IMAGE_SKELETON = \"data-image-skeleton\";\nfunction createSkeletonSrc(width, height, color = \"#e5e7eb\") {\n\tconst w = Number(width) || 400;\n\tconst h = Number(height) || 300;\n\treturn `data:image/svg+xml,${encodeURIComponent(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${w}\" height=\"${h}\" viewBox=\"0 0 ${w} ${h}\" preserveAspectRatio=\"none\"><rect width=\"100%\" height=\"100%\" fill=\"${color}\"/></svg>`)}`;\n}\nfunction hasAttribute(attrs, name) {\n\treturn attrs.some((attr) => t.isJSXIdentifier(attr.name) && attr.name.name === name);\n}\nconst transformImg = {\n\tid: \"transform-img\",\n\tmatch: (ctx) => ctx.element.nodeType === 1 && ctx.element.tagName.toLowerCase() === \"img\",\n\ttransform: (ctx) => {\n\t\tif (!ctx.target) throw new Error(\"Target must be defined before transforming an image element.\");\n\t\tconst attrs = ctx.target.attributes ?? [];\n\t\tconst naturalWidthAttr = findStringJsxAttribute(attrs, DATA_NAT_W);\n\t\tconst naturalHeightAttr = findStringJsxAttribute(attrs, DATA_NAT_H);\n\t\tconst naturalWidth = naturalWidthAttr?.value?.value;\n\t\tconst naturalHeight = naturalHeightAttr?.value?.value;\n\t\tconst placeholderSrc = createSkeletonSrc(naturalWidth, naturalHeight);\n\t\tconst filteredAttrs = attrs.filter((attr) => {\n\t\t\tif (!t.isJSXAttribute(attr)) return false;\n\t\t\tconst name = t.isJSXIdentifier(attr.name) ? attr.name.name : void 0;\n\t\t\treturn ![\n\t\t\t\t\"src\",\n\t\t\t\t\"srcSet\",\n\t\t\t\t\"sizes\",\n\t\t\t\t\"loading\",\n\t\t\t\t\"decoding\",\n\t\t\t\t\"fetchPriority\",\n\t\t\t\tDATA_NAT_W,\n\t\t\t\tDATA_NAT_H,\n\t\t\t\tDATA_IMAGE_SKELETON\n\t\t\t].includes(name ?? \"\");\n\t\t});\n\t\tconst attributes = [\n\t\t\t...filteredAttrs,\n\t\t\tcreateJsxStringAttribute(\"src\", placeholderSrc),\n\t\t\tcreateJsxStringAttribute(DATA_IMAGE_SKELETON, \"true\")\n\t\t];\n\t\tif (naturalWidth && naturalHeight) {\n\t\t\tif (!hasAttribute(filteredAttrs, \"width\")) attributes.push(createJsxStringAttribute(\"width\", naturalWidth));\n\t\t\tif (!hasAttribute(filteredAttrs, \"height\")) attributes.push(createJsxStringAttribute(\"height\", naturalHeight));\n\t\t}\n\t\treturn {\n\t\t\t...ctx,\n\t\t\ttarget: {\n\t\t\t\t...ctx.target,\n\t\t\t\tattributes\n\t\t\t}\n\t\t};\n\t}\n};\n//#endregion\n//#region src/rules/transform-interactive.ts\nconst INTERACTIVE_TAGS = new Set([\n\t\"input\",\n\t\"textarea\",\n\t\"select\",\n\t\"option\",\n\t\"optgroup\",\n\t\"button\",\n\t\"label\",\n\t\"fieldset\",\n\t\"legend\",\n\t\"output\",\n\t\"datalist\",\n\t\"a\",\n\t\"details\",\n\t\"summary\",\n\t\"audio\",\n\t\"video\",\n\t\"iframe\",\n\t\"embed\",\n\t\"object\",\n\t\"dialog\"\n]);\nconst INTERACTIVE_ROLES = new Set([\n\t\"button\",\n\t\"link\",\n\t\"tab\",\n\t\"menuitem\",\n\t\"checkbox\",\n\t\"radio\",\n\t\"switch\",\n\t\"option\",\n\t\"combobox\",\n\t\"textbox\",\n\t\"searchbox\",\n\t\"slider\",\n\t\"spinbutton\",\n\t\"listbox\",\n\t\"treeitem\",\n\t\"gridcell\",\n\t\"rowheader\",\n\t\"columnheader\"\n]);\nconst ARIA_ATTRIBUTES_TO_REMOVE = new Set([\n\t\"role\",\n\t\"aria-label\",\n\t\"aria-labelledby\",\n\t\"aria-describedby\",\n\t\"aria-controls\",\n\t\"aria-expanded\",\n\t\"aria-haspopup\",\n\t\"aria-pressed\",\n\t\"aria-selected\",\n\t\"aria-checked\",\n\t\"aria-current\",\n\t\"contenteditable\",\n\t\"tabindex\",\n\t\"aria-hidden\",\n\t\"data-skeleton-interactive\"\n]);\nconst transformInteractiveElements = {\n\tid: \"transform-interactive-elements\",\n\tmatch: (ctx) => {\n\t\tif (ctx.element.nodeType !== 1) return false;\n\t\tconst element = ctx.element;\n\t\tconst tag = element.tagName.toLowerCase();\n\t\tif (INTERACTIVE_TAGS.has(tag)) return true;\n\t\tconst role = element.getAttribute(\"role\")?.toLowerCase();\n\t\tif (role && INTERACTIVE_ROLES.has(role)) return true;\n\t\tif (element.hasAttribute(\"contenteditable\")) return true;\n\t\tif (element.hasAttribute(\"tabindex\")) return true;\n\t\treturn false;\n\t},\n\ttransform: (ctx) => {\n\t\tif (!ctx.target) return ctx;\n\t\tconst attributes = (ctx.target.attributes ?? []).filter((attribute) => {\n\t\t\tconst name = attribute.type === \"JSXAttribute\" && attribute.name?.name ? String(attribute.name.name).toLowerCase() : \"\";\n\t\t\treturn !ARIA_ATTRIBUTES_TO_REMOVE.has(name);\n\t\t});\n\t\tattributes.push(createJsxStringAttribute(\"data-skeleton-interactive\", \"true\"), createJsxStringAttribute(\"aria-hidden\", \"true\"), createJsxExpressionAttribute(\"tabIndex\", t.numericLiteral(-1)));\n\t\treturn {\n\t\t\t...ctx,\n\t\t\ttarget: {\n\t\t\t\t...ctx.target,\n\t\t\t\tattributes\n\t\t\t}\n\t\t};\n\t}\n};\n//#endregion\n//#region src/helpers/hash.ts\nconst SAFE_CHARS = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~\";\nfunction spacePreservedObfuscator(input) {\n\tif (!input) return null;\n\tlet seed = 2166136261;\n\tfor (const char of input) {\n\t\tseed ^= char.charCodeAt(0);\n\t\tseed = Math.imul(seed, 16777619);\n\t}\n\tconst len = 66;\n\treturn [...input].map((char, i) => {\n\t\tif (char === \" \") return \" \";\n\t\tseed ^= seed << 13;\n\t\tseed ^= seed >>> 17;\n\t\tseed ^= seed << 5;\n\t\treturn SAFE_CHARS[Math.abs(seed + i) % len];\n\t}).join(\"\");\n}\n//#endregion\n//#region src/index.ts\nconst executor = buildExecutor([\n\t{\n\t\tid: \"transform-text\",\n\t\tskipAllRest: true,\n\t\tmatch: (ctx) => ctx.element.nodeType === 3,\n\t\ttransform: (ctx) => {\n\t\t\tconst text = spacePreservedObfuscator(ctx.element.textContent?.trim());\n\t\t\tif (!text) return ctx;\n\t\t\tconst isHeading = ctx.parentNode?.nodeName.toLowerCase().startsWith(\"h\");\n\t\t\tif (![\"p\", \"label\"].includes(ctx.parentNode?.nodeName.toLowerCase() ?? \"\") && !isHeading) return {\n\t\t\t\t...ctx,\n\t\t\t\ttarget: {\n\t\t\t\t\telement: \"TEXT\",\n\t\t\t\t\tchildren: [jsxText(text)]\n\t\t\t\t}\n\t\t\t};\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\ttarget: {\n\t\t\t\t\telement: \"span\",\n\t\t\t\t\tattributes: [\n\t\t\t\t\t\tcreateJsxStringAttribute(\"className\", TEXT_CLASSNAME),\n\t\t\t\t\t\tcreateJsxStringAttribute(\"data-text-node\", \"true\"),\n\t\t\t\t\t\tcreateJsxStringAttribute(DEPTH_ATTRIBUTE, ctx.parentDepth ?? String(ctx.depth))\n\t\t\t\t\t],\n\t\t\t\t\tchildren: [jsxText(text)]\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t},\n\tremoveReduntantNode,\n\tpopulateAttrs,\n\tprocessSvg,\n\ttransformImg,\n\ttransformInteractiveElements,\n\taddRootSkeletonsAttrs\n]);\nfunction excarnate(element, depth, parentNode = null, parentDepth) {\n\tconst target = executor({\n\t\telement,\n\t\tdepth,\n\t\tparentNode,\n\t\tparentDepth\n\t}).target;\n\tif (!target) return null;\n\tconst { children, element: tagName, attributes } = target;\n\tif (tagName === \"TEXT\") return children?.[0] ?? null;\n\tif (children) return createJsxElement(tagName, children, attributes);\n\tconst { childNodes } = element ?? { childNodes: [] };\n\treturn createJsxElement(tagName, Array.from(childNodes).map((child) => excarnate(child, depth + 1, element, depth.toString())).filter(Boolean), attributes);\n}\n//#endregion\nexport { excarnate as default };\n\n//# sourceMappingURL=index.mjs.map","import { JSDOM } from \"jsdom\";\nimport pkg from \"@babel/generator\";\n\n// @ts-ignore\nconst generate = pkg.default;\n\nimport htmlNodeToJsx from \"@skullmaster/excarnate\";\n\nexport function transformInput(html: string) {\n const dom = new JSDOM(html);\n\n const root = dom.window.document.body.firstElementChild;\n\n if (!root) {\n throw new Error(\"No root element found\");\n }\n\n const jsxAst = htmlNodeToJsx(root, 0);\n\n if (!jsxAst) {\n throw new Error(\"Failed to generate JSX AST\");\n }\n\n const result = generate(jsxAst, {\n jsescOption: {\n minimal: true,\n },\n });\n\n return result.code;\n}\n","export function toPascalCase(input: string): string {\n return input\n .trim()\n .replace(/['\"]/g, \"\")\n .split(/[^a-zA-Z0-9]+/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\"\");\n}\n","export function fnv1a(str: string): number {\n let hash = 0x811c9dc5;\n\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n\n return hash >>> 0;\n}\n","import { serve } from \"@hono/node-server\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { Project } from \"ts-morph\";\nimport { SkeletonCacheDB } from \"../cache\";\nimport generateTarget from \"../generate-target\";\nimport { transformInput } from \"../transform\";\nimport { toPascalCase } from \"../utils/to-pascal-case\";\nimport { fnv1a } from \"../utils/fnv1a\";\nimport type { Config } from \"../init/preferences\";\nimport { log, SkeletonPayloadInputSchema } from \"../shared\";\n\nexport async function serveCommand(config: Config, port: number) {\n const app = new Hono();\n\n app.use(\n \"*\",\n cors({\n origin: \"*\",\n allowMethods: [\"GET\", \"POST\", \"OPTIONS\"],\n allowHeaders: [\"Content-Type\", \"Authorization\"],\n }),\n );\n\n const db = await SkeletonCacheDB.create(config);\n\n app.get(\"/health\", (c) => {\n return c.json({\n ok: true,\n });\n });\n\n app.post(\"/skeletons\", async (c) => {\n const body = await c.req.json();\n\n const result = SkeletonPayloadInputSchema.parse(body);\n\n const project = new Project();\n const entries = Object.entries(result).map(async ([key, value]) => {\n const componentName = toPascalCase(value.component);\n const transformedHtml = transformInput(value.html);\n const hash = fnv1a(transformedHtml).toString();\n const cached = db.get(key);\n const shouldSkip = cached && cached.hash === hash;\n\n if (shouldSkip) {\n return;\n }\n\n generateTarget({\n project,\n filePath: `${config.getSkeletonPath()}/${componentName}.${config.getExt()}`,\n componentName,\n body: transformedHtml,\n });\n\n await db.set(key, {\n component: value.component,\n html: transformedHtml,\n hash,\n });\n });\n await Promise.all(entries);\n config.registry({ type: \"add\", components: db.getComponentNames() });\n await project.save();\n\n return c.json({\n sucess: true,\n });\n });\n\n serve(\n {\n fetch: app.fetch,\n port,\n },\n (info) => {\n log.info(`Server running at http://localhost:${info.port}`);\n },\n );\n}\n","import { access } from \"node:fs/promises\";\n\nexport default async function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n","export const CONFIG_PATH = \"skullmaster.config.json\";\n","import { cancel, isCancel, select, text } from \"@clack/prompts\";\nimport fileExists from \"./is-file-exists\";\nimport { readFile, writeFile } from \"fs/promises\";\nimport { CONFIG_PATH } from \"../constants\";\n\nexport type Preferences = {\n outDir: string;\n project: string;\n};\n\nexport default async function collectPreferences(): Promise<Preferences> {\n const isAlreadyInitialized = await fileExists(CONFIG_PATH);\n if (isAlreadyInitialized) {\n return JSON.parse(await readFile(`./${CONFIG_PATH}`, \"utf-8\"));\n }\n\n const outDir = await text({\n message: \"Where do you want to output the generated files?\",\n placeholder: \"src/skeletons\",\n initialValue: \"src/skeletons\",\n validate(value) {\n if (value?.length === 0) return `Value is required!`;\n },\n });\n if (isCancel(outDir)) {\n cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const projectType = await select({\n message: \"Pick a project type.\",\n initialValue: \"react-ts\",\n options: [\n { value: \"react-ts\", label: \"React with TypeScript\" },\n { value: \"react-js\", label: \"React\" },\n ],\n });\n\n if (isCancel(projectType)) {\n cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n const preferences = {\n outDir,\n project: projectType.toString(),\n };\n await writeFile(`./${CONFIG_PATH}`, JSON.stringify(preferences, null, 2), \"utf-8\");\n\n return preferences;\n}\n","import { SourceFile } from \"ts-morph\";\n\nexport function ensureLazyImport(sourceFile: SourceFile) {\n const reactImport = sourceFile.getImportDeclaration(\n (d) => d.getModuleSpecifierValue() === \"react\",\n );\n\n if (!reactImport) {\n sourceFile.addImportDeclaration({\n moduleSpecifier: \"react\",\n namedImports: [\"lazy\"],\n });\n\n return;\n }\n\n const hasLazy = reactImport.getNamedImports().some((i) => i.getName() === \"lazy\");\n\n if (!hasLazy) {\n reactImport.addNamedImport(\"lazy\");\n }\n}\n","import { Project, SyntaxKind } from \"ts-morph\";\nimport { writeFile } from \"node:fs/promises\";\nimport { ensureLazyImport } from \"./ensure-lazy-imports\";\n\nexport async function generateInitialRegistry(outDir: string, projectType: string) {\n const isTs = projectType.endsWith(\"ts\");\n\n const ext = isTs ? \"tsx\" : \"jsx\";\n\n const skeletonPropsType = isTs\n ? `\ntype SkeletonProps = {\n name: string;\n};\n`\n : \"\";\n\n await writeFile(\n `${outDir}/registry.${ext}`,\n `\n/* eslint-disable */\n/* oxlint-disable */\n/* biome-ignore-all lint */\n/* @ts-nocheck */\n\n /**\n * Auto-generated by Skullmaster.\n *\n * Do not edit this file manually.\n * Use Skullmaster commands to add, update, or remove skeletons.\n *\n * To remove unused skeletons:\n * pnpm skeleton prune\n */\n\n\nimport DefaultBone from \"./skeletons/DefaultBone\";\n\nconst registry${isTs ? \": Record<string, any>\" : \"\"} = {};\n\n${skeletonPropsType}\n\nexport default function Skeleton({\n name,\n}${isTs ? \": SkeletonProps\" : \"\"}) {\n const Component = registry[name];\n\n if (!Component) {\n return <DefaultBone />;\n }\n\n return <Component />;\n}\n`,\n \"utf8\",\n );\n}\n\nexport type RegistryOperation =\n | { type: \"init\"; components: string[] }\n | { type: \"add\"; components: string[] };\n\nexport async function generateRegistry(\n operation: RegistryOperation,\n isTs: boolean,\n registryPath: string,\n) {\n let project: Project;\n if (isTs) {\n project = new Project({\n tsConfigFilePath: \"tsconfig.json\",\n });\n } else {\n project = new Project();\n }\n\n const sourceFile = project.addSourceFileAtPath(registryPath);\n\n ensureLazyImport(sourceFile);\n\n const registry = sourceFile\n .getVariableDeclaration(\"registry\")\n ?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n\n if (!registry) {\n throw new Error(\"Unable to locate registry object, have you ran skullmaster init??\");\n }\n\n const existingComponents = new Set(\n registry\n .getProperties()\n .filter((property) => property.getKind() === SyntaxKind.PropertyAssignment)\n .map((property) => property.asKindOrThrow(SyntaxKind.PropertyAssignment))\n .map((property) => property.getName().replace(/['\"]/g, \"\")),\n );\n\n for (const component of operation.components) {\n if (existingComponents.has(component)) {\n continue;\n }\n\n registry.addPropertyAssignment({\n name: `\"${component}\"`,\n initializer: `lazy(() => import(\"./skeletons/${component}\"))`,\n });\n }\n\n sourceFile.formatText();\n\n await sourceFile.save();\n\n project.saveSync();\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport z from \"zod\";\nimport fileExists from \"./is-file-exists\";\nimport { fnv1a } from \"../utils/fnv1a\";\nimport { SkeletonCacheEntrySchema } from \"../shared\";\n\nexport type CacheOperation =\n | { type: \"replace\"; cacheEntry: z.infer<typeof SkeletonCacheEntrySchema> }\n | { type: \"delete\"; componentName: string }\n | {\n type: \"update\";\n componentName: string;\n payload: z.infer<typeof SkeletonCacheEntrySchema>[string];\n };\n\nexport async function updateCacheRegistry(operation: CacheOperation, outDir: string) {\n const cacheFilePath = outDir;\n let cacheEntries: z.infer<typeof SkeletonCacheEntrySchema> = {};\n if (!(await fileExists(cacheFilePath))) {\n await writeFile(cacheFilePath, JSON.stringify({}), \"utf8\");\n } else {\n cacheEntries = SkeletonCacheEntrySchema.parse(\n JSON.parse(await readFile(cacheFilePath, \"utf8\")),\n );\n }\n switch (operation.type) {\n case \"replace\":\n await writeFile(cacheFilePath, JSON.stringify(operation.cacheEntry), \"utf8\");\n break;\n case \"delete\":\n delete cacheEntries[operation.componentName];\n await writeFile(cacheFilePath, JSON.stringify(cacheEntries), \"utf8\");\n break;\n case \"update\":\n cacheEntries[operation.componentName] = {\n ...operation.payload,\n hash: fnv1a(operation.payload.html).toString(),\n };\n await writeFile(cacheFilePath, JSON.stringify(cacheEntries), \"utf8\");\n }\n}\n","import { writeFile } from \"node:fs/promises\";\n\nexport default async function defaultBone(target: string) {\n await writeFile(\n target,\n `/* eslint-disable */\n/* oxlint-disable */\n/* biome-ignore-all lint */\n/* @ts-nocheck */\n\n/**\n * Auto-generated by Skullmaster.\n *\n * This is the default fallback skeleton component displayed when a\n * matching skeleton implementation cannot be found in the registry.\n *\n * You may freely customize or replace this component to match the\n * loading experience of your application.\n */\nexport default function DefaultBone() {\n return <div>loading...</div>;\n}\n`,\n \"utf8\",\n );\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport fileExists from \"./is-file-exists\";\nimport { generateInitialRegistry, generateRegistry, type RegistryOperation } from \"./registry\";\nimport { updateCacheRegistry, type CacheOperation } from \"./cache-registry\";\nimport defaultBone from \"./default-bone\";\nimport { log } from \"../shared\";\n\nexport class Config {\n outDir: string;\n projectType: string;\n\n constructor(outDir: string, projectType: string) {\n this.outDir = outDir;\n this.projectType = projectType;\n }\n async initialize() {\n log.info(\"Initializing project structure...\");\n if (!(await fileExists(this.outDir))) {\n mkdirSync(this.outDir, { recursive: true });\n log.success(`Created output directory: ${this.outDir}`);\n } else {\n log.gray(`Output directory exists: ${this.outDir}`);\n }\n const skeletonsDirectoryPath = this.getSkeletonPath();\n if (!(await fileExists(skeletonsDirectoryPath))) {\n mkdirSync(skeletonsDirectoryPath, { recursive: true });\n log.success(`Created skeletons directory: ${skeletonsDirectoryPath}`);\n } else {\n log.gray(`Skeletons directory exists: ${skeletonsDirectoryPath}`);\n }\n const registryFilePath = this.getRegistryPath();\n if (!(await fileExists(registryFilePath))) {\n await generateInitialRegistry(this.outDir, this.projectType);\n log.success(`Created registry: ${registryFilePath}`);\n } else {\n log.gray(`Registry exists: ${registryFilePath}`);\n }\n const cachePath = this.getCachePath();\n if (!(await fileExists(cachePath))) {\n writeFileSync(cachePath, JSON.stringify({}), \"utf8\");\n log.success(`Created cache: ${cachePath}`);\n } else {\n log.gray(`Cache exists: ${cachePath}`);\n }\n const defaultBonePath = this.getDefSkeletonPath();\n if (!(await fileExists(defaultBonePath))) {\n await defaultBone(defaultBonePath);\n log.success(`Created default bone: ${defaultBonePath}`);\n } else {\n log.gray(`Default bone exists: ${defaultBonePath}`);\n }\n log.success(\"Initialization complete\");\n }\n async cache(op: CacheOperation) {\n return await updateCacheRegistry(op, this.getCachePath());\n }\n async registry(op: RegistryOperation) {\n return generateRegistry(op, this.projectType.endsWith(\"ts\"), this.getRegistryPath());\n }\n getCachePath() {\n return `${this.outDir}/cache.json`;\n }\n getSkeletonPath() {\n return `${this.outDir}/skeletons`;\n }\n getDefSkeletonPath() {\n return `${this.getSkeletonPath()}/DefaultBone.${this.getExt()}`;\n }\n getExt() {\n return this.projectType.endsWith(\"ts\") ? \"tsx\" : \"jsx\";\n }\n getRegistryPath() {\n return `${this.outDir}/registry.${this.getExt()}`;\n }\n}\n","#!/usr/bin/env node\n\nimport { cac } from \"cac\";\nimport { serveCommand } from \"./commands/serve\";\nimport collectPreferences from \"./init/collect-preferences\";\nimport { Config } from \"./init/preferences\";\nimport { DEFAULT_PORT } from \"./shared\";\n\nconst cli = cac(\"💀\");\n\ncli\n .command(\"serve\", \"Start 💀 dev server\")\n .option(\"--port <port>\", \"Port\", {\n default: DEFAULT_PORT,\n })\n .action(async (options) => {\n const preferences = await collectPreferences();\n const config = new Config(preferences.outDir, preferences.project);\n await config.initialize();\n await serveCommand(config, Number(options.port));\n });\n\ncli.help();\ncli.parse();\n"],"mappings":"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"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";iBAAgB,EAAA,CAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export function fn() {\n return \"Hello, tsdown!\";\n}\n"],"mappings":";AAAA,SAAgB,KAAK;CACnB,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skullmaster",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A simple example library built by tsdown",
|
|
6
6
|
"repository": {
|
|
@@ -37,8 +37,7 @@
|
|
|
37
37
|
"lodash": "^4.18.1",
|
|
38
38
|
"oxfmt": "^0.52.0",
|
|
39
39
|
"ts-morph": "^28.0.0",
|
|
40
|
-
"zod": "^4.4.3"
|
|
41
|
-
"@skullmaster/excarnate": "0.1.2"
|
|
40
|
+
"zod": "^4.4.3"
|
|
42
41
|
},
|
|
43
42
|
"devDependencies": {
|
|
44
43
|
"@types/babel__generator": "^7.27.0",
|
|
@@ -47,6 +46,7 @@
|
|
|
47
46
|
"oxlint": "^1.0.0",
|
|
48
47
|
"tsdown": "^0.22.0",
|
|
49
48
|
"typescript": "^6.0.3",
|
|
49
|
+
"@skullmaster/excarnate": "0.1.3",
|
|
50
50
|
"@skullmaster/oxlint-config": "0.0.0",
|
|
51
51
|
"@skullmaster/typescript-config": "0.0.0"
|
|
52
52
|
},
|