@kithinji/arcane 1.0.8 → 1.0.9
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/browser/index.mjs +15 -28
- package/dist/browser/index.mjs.map +2 -2
- package/dist/node/index.cjs +191 -49
- package/dist/node/index.cjs.map +3 -3
- package/dist/node/index.mjs +188 -49
- package/dist/node/index.mjs.map +3 -3
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.node.d.ts +1 -1
- package/dist/types/index.node.d.ts.map +1 -1
- package/dist/types/inline/index.d.ts +2 -0
- package/dist/types/inline/index.d.ts.map +1 -0
- package/dist/types/inline/inline.d.ts +17 -0
- package/dist/types/inline/inline.d.ts.map +1 -0
- package/dist/types/style/style.d.ts +2 -2
- package/dist/types/style/style.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.node.ts +1 -1
- package/src/index.ts +1 -1
- package/src/inline/inline.ts +320 -0
- package/src/style/style.ts +42 -58
- package/src/inline-file/inline.ts +0 -90
- /package/src/{inline-file → inline}/index.ts +0 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { MacroContext } from "@kithinji/pod";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
const MIME_TYPES: Record<string, string> = {
|
|
7
|
+
".png": "image/png",
|
|
8
|
+
".jpg": "image/jpeg",
|
|
9
|
+
".jpeg": "image/jpeg",
|
|
10
|
+
".gif": "image/gif",
|
|
11
|
+
".svg": "image/svg+xml",
|
|
12
|
+
".webp": "image/webp",
|
|
13
|
+
".ico": "image/x-icon",
|
|
14
|
+
".bmp": "image/bmp",
|
|
15
|
+
|
|
16
|
+
".woff": "font/woff",
|
|
17
|
+
".woff2": "font/woff2",
|
|
18
|
+
".ttf": "font/ttf",
|
|
19
|
+
".otf": "font/otf",
|
|
20
|
+
".eot": "application/vnd.ms-fontobject",
|
|
21
|
+
|
|
22
|
+
".mp3": "audio/mpeg",
|
|
23
|
+
".wav": "audio/wav",
|
|
24
|
+
".ogg": "audio/ogg",
|
|
25
|
+
".mp4": "video/mp4",
|
|
26
|
+
".webm": "video/webm",
|
|
27
|
+
|
|
28
|
+
".pdf": "application/pdf",
|
|
29
|
+
".zip": "application/zip",
|
|
30
|
+
|
|
31
|
+
".txt": "text/plain",
|
|
32
|
+
".html": "text/html",
|
|
33
|
+
".css": "text/css",
|
|
34
|
+
".js": "text/javascript",
|
|
35
|
+
".json": "application/json",
|
|
36
|
+
".xml": "application/xml",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function getMimeType(filePath: string): string {
|
|
40
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
41
|
+
return MIME_TYPES[ext] || "application/octet-stream";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveFilePath(
|
|
45
|
+
filePath: string,
|
|
46
|
+
context: MacroContext,
|
|
47
|
+
macroName: string,
|
|
48
|
+
): string {
|
|
49
|
+
const pathNode = filePath as unknown as ts.Node;
|
|
50
|
+
const resolvedPath = context.resolveNodeValue(pathNode);
|
|
51
|
+
|
|
52
|
+
if (typeof resolvedPath !== "string") {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`${macroName} requires a string literal path, got: ${typeof resolvedPath}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const sourceFile = context.sourceFile;
|
|
59
|
+
const sourceDir = path.dirname(sourceFile.fileName);
|
|
60
|
+
const absolutePath = path.resolve(sourceDir, resolvedPath);
|
|
61
|
+
|
|
62
|
+
return absolutePath;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface InlineFileOptions {
|
|
66
|
+
maxSize?: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface InlineDataURLOptions {
|
|
70
|
+
maxSize?: number;
|
|
71
|
+
mimeType?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface InlineBase64Options {
|
|
75
|
+
maxSize?: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function inlineFile$(
|
|
79
|
+
this: MacroContext,
|
|
80
|
+
filePath: string,
|
|
81
|
+
options?: InlineFileOptions,
|
|
82
|
+
): string {
|
|
83
|
+
const absolutePath = resolveFilePath(filePath, this, "inlineFile$");
|
|
84
|
+
const maxSize = options?.maxSize || 1024 * 1024;
|
|
85
|
+
|
|
86
|
+
let content: string;
|
|
87
|
+
try {
|
|
88
|
+
const stats = fs.statSync(absolutePath);
|
|
89
|
+
|
|
90
|
+
if (stats.size > maxSize) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`File "${absolutePath}" is too large (${stats.size} bytes). Maximum allowed: ${maxSize} bytes. Consider using uploadFile$ instead.`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
content = fs.readFileSync(absolutePath, "utf-8");
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`File not found: "${absolutePath}" (referenced in ${this.sourceFile.fileName})`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Failed to read file "${absolutePath}": ${
|
|
105
|
+
error instanceof Error ? error.message : String(error)
|
|
106
|
+
}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return this.factory.createStringLiteral(content) as any;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function inlineFiles$(
|
|
114
|
+
this: MacroContext,
|
|
115
|
+
filePaths: string[],
|
|
116
|
+
options?: InlineFileOptions,
|
|
117
|
+
): string[] {
|
|
118
|
+
const pathsNode = filePaths as unknown as ts.Node;
|
|
119
|
+
const resolvedPaths = this.resolveNodeValue(pathsNode);
|
|
120
|
+
|
|
121
|
+
if (!Array.isArray(resolvedPaths)) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`inlineFiles$ requires an array of string literals, got: ${typeof resolvedPaths}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const sourceFile = this.sourceFile;
|
|
128
|
+
const sourceDir = path.dirname(sourceFile.fileName);
|
|
129
|
+
const maxSize = options?.maxSize || 1024 * 1024;
|
|
130
|
+
|
|
131
|
+
const fileContents: string[] = [];
|
|
132
|
+
|
|
133
|
+
for (const filePath of resolvedPaths) {
|
|
134
|
+
if (typeof filePath !== "string") {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`inlineFiles$ requires all paths to be string literals, got: ${typeof filePath}`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const absolutePath = path.resolve(sourceDir, filePath);
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const stats = fs.statSync(absolutePath);
|
|
144
|
+
|
|
145
|
+
if (stats.size > maxSize) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`File "${absolutePath}" is too large (${stats.size} bytes). Maximum allowed: ${maxSize} bytes.`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const content = fs.readFileSync(absolutePath, "utf-8");
|
|
152
|
+
fileContents.push(content);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (
|
|
155
|
+
error instanceof Error &&
|
|
156
|
+
"code" in error &&
|
|
157
|
+
error.code === "ENOENT"
|
|
158
|
+
) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`File not found: "${absolutePath}" (referenced in ${sourceFile.fileName})`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Failed to read file "${absolutePath}": ${
|
|
165
|
+
error instanceof Error ? error.message : String(error)
|
|
166
|
+
}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const elements = fileContents.map((content) =>
|
|
172
|
+
this.factory.createStringLiteral(content),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
return this.factory.createArrayLiteralExpression(elements, true) as any;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function inlineFileBase64$(
|
|
179
|
+
this: MacroContext,
|
|
180
|
+
filePath: string,
|
|
181
|
+
options?: InlineBase64Options,
|
|
182
|
+
): string {
|
|
183
|
+
const absolutePath = resolveFilePath(filePath, this, "inlineFileBase64$");
|
|
184
|
+
const maxSize = options?.maxSize || 100 * 1024;
|
|
185
|
+
|
|
186
|
+
let content: string;
|
|
187
|
+
try {
|
|
188
|
+
const stats = fs.statSync(absolutePath);
|
|
189
|
+
|
|
190
|
+
if (stats.size > maxSize) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`File "${absolutePath}" is too large (${stats.size} bytes). Maximum allowed: ${maxSize} bytes. Consider using uploadFile$ instead.`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const buffer = fs.readFileSync(absolutePath);
|
|
197
|
+
content = buffer.toString("base64");
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`File not found: "${absolutePath}" (referenced in ${this.sourceFile.fileName})`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
throw new Error(
|
|
205
|
+
`Failed to read file "${absolutePath}": ${
|
|
206
|
+
error instanceof Error ? error.message : String(error)
|
|
207
|
+
}`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return this.factory.createStringLiteral(content) as any;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function inlineDataURL$(
|
|
215
|
+
this: MacroContext,
|
|
216
|
+
filePath: string,
|
|
217
|
+
options?: InlineDataURLOptions,
|
|
218
|
+
): string {
|
|
219
|
+
const absolutePath = resolveFilePath(filePath, this, "inlineDataURL$");
|
|
220
|
+
const maxSize = options?.maxSize || 100 * 1024;
|
|
221
|
+
|
|
222
|
+
let dataURL: string;
|
|
223
|
+
try {
|
|
224
|
+
const stats = fs.statSync(absolutePath);
|
|
225
|
+
|
|
226
|
+
if (stats.size > maxSize) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`File "${absolutePath}" is too large (${stats.size} bytes). Maximum allowed: ${maxSize} bytes. Consider using uploadFile$ instead.`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const mimeType = options?.mimeType || getMimeType(absolutePath);
|
|
233
|
+
const buffer = fs.readFileSync(absolutePath);
|
|
234
|
+
const base64 = buffer.toString("base64");
|
|
235
|
+
|
|
236
|
+
dataURL = `data:${mimeType};base64,${base64}`;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`File not found: "${absolutePath}" (referenced in ${this.sourceFile.fileName})`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
throw new Error(
|
|
244
|
+
`Failed to read file "${absolutePath}": ${
|
|
245
|
+
error instanceof Error ? error.message : String(error)
|
|
246
|
+
}`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return this.factory.createStringLiteral(dataURL) as any;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function inlineDataURLs$(
|
|
254
|
+
this: MacroContext,
|
|
255
|
+
filePaths: string[],
|
|
256
|
+
options?: InlineDataURLOptions,
|
|
257
|
+
): string[] {
|
|
258
|
+
const pathsNode = filePaths as unknown as ts.Node;
|
|
259
|
+
const resolvedPaths = this.resolveNodeValue(pathsNode);
|
|
260
|
+
|
|
261
|
+
if (!Array.isArray(resolvedPaths)) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
`inlineDataURLs$ requires an array of string literals, got: ${typeof resolvedPaths}`,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const sourceFile = this.sourceFile;
|
|
268
|
+
const sourceDir = path.dirname(sourceFile.fileName);
|
|
269
|
+
const maxSize = options?.maxSize || 100 * 1024;
|
|
270
|
+
|
|
271
|
+
const dataURLs: string[] = [];
|
|
272
|
+
|
|
273
|
+
for (const filePath of resolvedPaths) {
|
|
274
|
+
if (typeof filePath !== "string") {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`inlineDataURLs$ requires all paths to be string literals, got: ${typeof filePath}`,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const absolutePath = path.resolve(sourceDir, filePath);
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const stats = fs.statSync(absolutePath);
|
|
284
|
+
|
|
285
|
+
if (stats.size > maxSize) {
|
|
286
|
+
throw new Error(
|
|
287
|
+
`File "${absolutePath}" is too large (${stats.size} bytes). Maximum allowed: ${maxSize} bytes.`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const mimeType = options?.mimeType || getMimeType(absolutePath);
|
|
292
|
+
const buffer = fs.readFileSync(absolutePath);
|
|
293
|
+
const base64 = buffer.toString("base64");
|
|
294
|
+
|
|
295
|
+
const dataURL = `data:${mimeType};base64,${base64}`;
|
|
296
|
+
dataURLs.push(dataURL);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (
|
|
299
|
+
error instanceof Error &&
|
|
300
|
+
"code" in error &&
|
|
301
|
+
error.code === "ENOENT"
|
|
302
|
+
) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`File not found: "${absolutePath}" (referenced in ${sourceFile.fileName})`,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Failed to read file "${absolutePath}": ${
|
|
309
|
+
error instanceof Error ? error.message : String(error)
|
|
310
|
+
}`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const elements = dataURLs.map((dataURL) =>
|
|
316
|
+
this.factory.createStringLiteral(dataURL),
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
return this.factory.createArrayLiteralExpression(elements, true) as any;
|
|
320
|
+
}
|
package/src/style/style.ts
CHANGED
|
@@ -43,7 +43,7 @@ function simpleHash(str: string): string {
|
|
|
43
43
|
function hashProperty(
|
|
44
44
|
prop: string,
|
|
45
45
|
value: string | number,
|
|
46
|
-
cssRules: Map<string, string
|
|
46
|
+
cssRules: Map<string, string>,
|
|
47
47
|
): string {
|
|
48
48
|
const input = `${prop}:${value}`;
|
|
49
49
|
let hash = simpleHash(input);
|
|
@@ -80,7 +80,7 @@ function hashProperty(
|
|
|
80
80
|
|
|
81
81
|
if (attempt > 100) {
|
|
82
82
|
throw new Error(
|
|
83
|
-
`Hash collision limit exceeded for property ${prop}:${value}
|
|
83
|
+
`Hash collision limit exceeded for property ${prop}:${value}`,
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -123,13 +123,13 @@ function isPseudoOrMediaKey(key: string): boolean {
|
|
|
123
123
|
function validateStyleValue(value: any, path: string): void {
|
|
124
124
|
if (value === null) {
|
|
125
125
|
throw new Error(
|
|
126
|
-
`Invalid style value at ${path}: null is not allowed. Use undefined or omit the property
|
|
126
|
+
`Invalid style value at ${path}: null is not allowed. Use undefined or omit the property.`,
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
if (typeof value === "function") {
|
|
131
131
|
throw new Error(
|
|
132
|
-
`Invalid style value at ${path}: functions must be resolved at build time
|
|
132
|
+
`Invalid style value at ${path}: functions must be resolved at build time.`,
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
@@ -138,7 +138,7 @@ function processStyleValue(
|
|
|
138
138
|
prop: string,
|
|
139
139
|
value: any,
|
|
140
140
|
cssRules: Map<string, string>,
|
|
141
|
-
path: string = prop
|
|
141
|
+
path: string = prop,
|
|
142
142
|
): string {
|
|
143
143
|
validateStyleValue(value, path);
|
|
144
144
|
|
|
@@ -163,7 +163,7 @@ function processStyleValue(
|
|
|
163
163
|
if (nestedKey === "default") {
|
|
164
164
|
const normalizedValue = normalizeValue(
|
|
165
165
|
prop,
|
|
166
|
-
nestedValue as string | number
|
|
166
|
+
nestedValue as string | number,
|
|
167
167
|
);
|
|
168
168
|
const className = hashProperty(prop, normalizedValue, cssRules);
|
|
169
169
|
|
|
@@ -176,12 +176,12 @@ function processStyleValue(
|
|
|
176
176
|
} else if (isPseudoOrMediaKey(nestedKey)) {
|
|
177
177
|
const normalizedValue = normalizeValue(
|
|
178
178
|
prop,
|
|
179
|
-
nestedValue as string | number
|
|
179
|
+
nestedValue as string | number,
|
|
180
180
|
);
|
|
181
181
|
const className = hashProperty(
|
|
182
182
|
`${prop}${nestedKey}`,
|
|
183
183
|
normalizedValue,
|
|
184
|
-
cssRules
|
|
184
|
+
cssRules,
|
|
185
185
|
);
|
|
186
186
|
|
|
187
187
|
if (!cssRules.has(className)) {
|
|
@@ -205,7 +205,7 @@ function processStyleValue(
|
|
|
205
205
|
classes.push(className);
|
|
206
206
|
} else {
|
|
207
207
|
throw new Error(
|
|
208
|
-
`Invalid nested key "${nestedKey}" at ${path}. Expected "default", a pseudo-class (":hover"), media query ("@media"), or nesting selector ("&")
|
|
208
|
+
`Invalid nested key "${nestedKey}" at ${path}. Expected "default", a pseudo-class (":hover"), media query ("@media"), or nesting selector ("&").`,
|
|
209
209
|
);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
@@ -226,14 +226,14 @@ function processStyleValue(
|
|
|
226
226
|
|
|
227
227
|
function processStyleObject(
|
|
228
228
|
styleObj: Record<string, any>,
|
|
229
|
-
cssRules: Map<string, string
|
|
229
|
+
cssRules: Map<string, string>,
|
|
230
230
|
): Record<string, string> {
|
|
231
231
|
const result: Record<string, string> = {};
|
|
232
232
|
|
|
233
233
|
for (const [namespace, styles] of Object.entries(styleObj)) {
|
|
234
234
|
if (typeof styles !== "object" || Array.isArray(styles)) {
|
|
235
235
|
throw new Error(
|
|
236
|
-
`Invalid style namespace "${namespace}": expected an object, got ${typeof styles}
|
|
236
|
+
`Invalid style namespace "${namespace}": expected an object, got ${typeof styles}`,
|
|
237
237
|
);
|
|
238
238
|
}
|
|
239
239
|
|
|
@@ -246,7 +246,7 @@ function processStyleObject(
|
|
|
246
246
|
prop,
|
|
247
247
|
value,
|
|
248
248
|
cssRules,
|
|
249
|
-
`${namespace}.${prop}
|
|
249
|
+
`${namespace}.${prop}`,
|
|
250
250
|
);
|
|
251
251
|
|
|
252
252
|
if (className) {
|
|
@@ -261,78 +261,62 @@ function processStyleObject(
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
export function style$<const T extends Record<string, any>>(
|
|
264
|
+
this: MacroContext,
|
|
264
265
|
style: T,
|
|
265
|
-
context?: MacroContext
|
|
266
266
|
): MapNamespaces<T> {
|
|
267
|
-
if (!context) {
|
|
268
|
-
throw new Error(
|
|
269
|
-
"style$ macro requires MacroContext. Ensure you're using this as a build-time macro."
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
267
|
const rStyle = style as unknown as ts.Node;
|
|
274
|
-
const value =
|
|
268
|
+
const value = this.resolveNodeValue(rStyle);
|
|
275
269
|
|
|
276
270
|
if (value == undefined) {
|
|
277
271
|
throw new Error(
|
|
278
272
|
`Could not resolve style object at build time. ` +
|
|
279
|
-
`Ensure all values are statically analyzable (no runtime expressions, dynamic imports should be inlined)
|
|
273
|
+
`Ensure all values are statically analyzable (no runtime expressions, dynamic imports should be inlined).`,
|
|
280
274
|
);
|
|
281
275
|
}
|
|
282
276
|
|
|
283
277
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
284
278
|
throw new Error(
|
|
285
|
-
`style$ expects an object with style namespaces, got ${typeof value}
|
|
279
|
+
`style$ expects an object with style namespaces, got ${typeof value}`,
|
|
286
280
|
);
|
|
287
281
|
}
|
|
288
282
|
|
|
289
283
|
const cssRules = new Map<string, string>();
|
|
290
284
|
const classNameMap = processStyleObject(value, cssRules);
|
|
291
285
|
|
|
292
|
-
|
|
286
|
+
this.store.set("style_rules", Array.from(cssRules.values()));
|
|
293
287
|
|
|
294
288
|
const properties = Object.entries(classNameMap).map(([key, className]) =>
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
)
|
|
289
|
+
this.factory.createPropertyAssignment(
|
|
290
|
+
this.factory.createStringLiteral(key),
|
|
291
|
+
this.factory.createStringLiteral(className),
|
|
292
|
+
),
|
|
299
293
|
);
|
|
300
294
|
|
|
301
|
-
return
|
|
295
|
+
return this.factory.createObjectLiteralExpression(properties, true) as any;
|
|
302
296
|
}
|
|
303
297
|
|
|
304
298
|
type Fragment =
|
|
305
299
|
| { kind: "static"; value: string }
|
|
306
300
|
| { kind: "dynamic"; expr: ts.Expression; stringSafe: boolean };
|
|
307
301
|
|
|
308
|
-
export function apply$(...c: any[]) {
|
|
309
|
-
if (c.length < 1) {
|
|
310
|
-
throw new Error("apply$ requires at least one argument plus MacroContext");
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const context = c.pop() as MacroContext;
|
|
314
|
-
|
|
315
|
-
if (!context || !context.factory) {
|
|
316
|
-
throw new Error(
|
|
317
|
-
"apply$ macro requires MacroContext as the last argument. Ensure you're using this as a build-time macro."
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
|
|
302
|
+
export function apply$(this: MacroContext, ...c: any[]) {
|
|
321
303
|
const args = c as ts.Expression[];
|
|
322
304
|
|
|
305
|
+
const self = this;
|
|
306
|
+
|
|
323
307
|
if (args.length === 0) {
|
|
324
|
-
return
|
|
308
|
+
return self.factory.createObjectLiteralExpression(
|
|
325
309
|
[
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
310
|
+
self.factory.createPropertyAssignment(
|
|
311
|
+
self.factory.createIdentifier("className"),
|
|
312
|
+
self.factory.createStringLiteral(""),
|
|
329
313
|
),
|
|
330
314
|
],
|
|
331
|
-
false
|
|
315
|
+
false,
|
|
332
316
|
);
|
|
333
317
|
}
|
|
334
318
|
|
|
335
|
-
const f =
|
|
319
|
+
const f = self.factory;
|
|
336
320
|
|
|
337
321
|
function isTrue(e: ts.Expression): boolean {
|
|
338
322
|
return e.kind === ts.SyntaxKind.TrueKeyword;
|
|
@@ -366,8 +350,8 @@ export function apply$(...c: any[]) {
|
|
|
366
350
|
if (!ts.isIdentifier(cur)) return null;
|
|
367
351
|
chain.unshift(cur.text);
|
|
368
352
|
|
|
369
|
-
const root =
|
|
370
|
-
let value =
|
|
353
|
+
const root = self.resolveIdentifier(f.createIdentifier(chain[0]));
|
|
354
|
+
let value = self.resolveNodeValue(root);
|
|
371
355
|
|
|
372
356
|
if (value == null) return null;
|
|
373
357
|
|
|
@@ -412,7 +396,7 @@ export function apply$(...c: any[]) {
|
|
|
412
396
|
f.createStringLiteral(""),
|
|
413
397
|
f.createStringLiteral(rs),
|
|
414
398
|
]),
|
|
415
|
-
f.createPrefixUnaryExpression(ts.SyntaxKind.PlusToken, expr.left)
|
|
399
|
+
f.createPrefixUnaryExpression(ts.SyntaxKind.PlusToken, expr.left),
|
|
416
400
|
),
|
|
417
401
|
};
|
|
418
402
|
}
|
|
@@ -440,8 +424,8 @@ export function apply$(...c: any[]) {
|
|
|
440
424
|
]),
|
|
441
425
|
f.createPrefixUnaryExpression(
|
|
442
426
|
ts.SyntaxKind.PlusToken,
|
|
443
|
-
expr.condition
|
|
444
|
-
)
|
|
427
|
+
expr.condition,
|
|
428
|
+
),
|
|
445
429
|
),
|
|
446
430
|
};
|
|
447
431
|
}
|
|
@@ -485,7 +469,7 @@ export function apply$(...c: any[]) {
|
|
|
485
469
|
frags
|
|
486
470
|
.map((f) => f.value)
|
|
487
471
|
.join(" ")
|
|
488
|
-
.trim()
|
|
472
|
+
.trim(),
|
|
489
473
|
);
|
|
490
474
|
} else {
|
|
491
475
|
// Mixed static/dynamic - generate array.join(" ")
|
|
@@ -505,19 +489,19 @@ export function apply$(...c: any[]) {
|
|
|
505
489
|
undefined,
|
|
506
490
|
frag.expr,
|
|
507
491
|
undefined,
|
|
508
|
-
f.createStringLiteral("")
|
|
492
|
+
f.createStringLiteral(""),
|
|
509
493
|
);
|
|
510
|
-
})
|
|
494
|
+
}),
|
|
511
495
|
),
|
|
512
|
-
"join"
|
|
496
|
+
"join",
|
|
513
497
|
),
|
|
514
498
|
undefined,
|
|
515
|
-
[f.createStringLiteral(" ")]
|
|
499
|
+
[f.createStringLiteral(" ")],
|
|
516
500
|
);
|
|
517
501
|
}
|
|
518
502
|
|
|
519
503
|
return f.createObjectLiteralExpression(
|
|
520
504
|
[f.createPropertyAssignment(f.createIdentifier("className"), classExpr)],
|
|
521
|
-
false
|
|
505
|
+
false,
|
|
522
506
|
);
|
|
523
507
|
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { MacroContext } from "@kithinji/pod";
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import path from "path";
|
|
5
|
-
|
|
6
|
-
export function inlineFile$(filePath: string, context?: MacroContext): string {
|
|
7
|
-
if (!context) {
|
|
8
|
-
throw new Error(
|
|
9
|
-
"inlineFile$ macro requires MacroContext. Ensure you're using this as a build-time macro."
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const pathNode = filePath as unknown as ts.Node;
|
|
14
|
-
const resolvedPath = context.resolveNodeValue(pathNode);
|
|
15
|
-
|
|
16
|
-
if (typeof resolvedPath !== "string") {
|
|
17
|
-
throw new Error(
|
|
18
|
-
`inlineFile$ macro requires a string literal path, got: ${typeof resolvedPath}`
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const sourceFile = context.sourceFile;
|
|
23
|
-
const sourceDir = path.dirname(sourceFile.fileName);
|
|
24
|
-
const absolutePath = path.resolve(sourceDir, resolvedPath);
|
|
25
|
-
|
|
26
|
-
let content: string;
|
|
27
|
-
try {
|
|
28
|
-
content = fs.readFileSync(absolutePath, "utf-8");
|
|
29
|
-
} catch (error) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`Failed to read file "${absolutePath}": ${
|
|
32
|
-
error instanceof Error ? error.message : String(error)
|
|
33
|
-
}`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return context.factory.createStringLiteral(content) as any;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function inlineFiles$(
|
|
41
|
-
filePaths: string[],
|
|
42
|
-
context?: MacroContext
|
|
43
|
-
): string[] {
|
|
44
|
-
if (!context) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
"inlineFiles$ macro requires MacroContext. Ensure you're using this as a build-time macro."
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const pathsNode = filePaths as unknown as ts.Node;
|
|
51
|
-
const resolvedPaths = context.resolveNodeValue(pathsNode);
|
|
52
|
-
|
|
53
|
-
if (!Array.isArray(resolvedPaths)) {
|
|
54
|
-
throw new Error(
|
|
55
|
-
`inlineFiles$ macro requires an array of string literals, got: ${typeof resolvedPaths}`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const sourceFile = context.sourceFile;
|
|
60
|
-
const sourceDir = path.dirname(sourceFile.fileName);
|
|
61
|
-
|
|
62
|
-
const fileContents: string[] = [];
|
|
63
|
-
|
|
64
|
-
for (const filePath of resolvedPaths) {
|
|
65
|
-
if (typeof filePath !== "string") {
|
|
66
|
-
throw new Error(
|
|
67
|
-
`inlineFiles$ macro requires all paths to be string literals, got: ${typeof filePath}`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const absolutePath = path.resolve(sourceDir, filePath);
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
const content = fs.readFileSync(absolutePath, "utf-8");
|
|
75
|
-
fileContents.push(content);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
`Failed to read file "${absolutePath}": ${
|
|
79
|
-
error instanceof Error ? error.message : String(error)
|
|
80
|
-
}`
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const elements = fileContents.map((content) =>
|
|
86
|
-
context.factory.createStringLiteral(content)
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
return context.factory.createArrayLiteralExpression(elements, true) as any;
|
|
90
|
-
}
|
|
File without changes
|