@design-embed/plugin-figma-html 0.1.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/compilers/compilerUtils.d.mts +11 -0
- package/dist/compilers/compilerUtils.mjs +119 -0
- package/dist/compilers/htmlCompiler.d.mts +6 -0
- package/dist/compilers/htmlCompiler.mjs +31 -0
- package/dist/compilers/index.d.mts +11 -0
- package/dist/compilers/index.mjs +17 -0
- package/dist/compilers/index.test.d.mts +1 -0
- package/dist/compilers/index.test.mjs +45 -0
- package/dist/compilers/reactCompiler.d.mts +6 -0
- package/dist/compilers/reactCompiler.mjs +47 -0
- package/dist/compilers/vanjsCompiler.d.mts +6 -0
- package/dist/compilers/vanjsCompiler.mjs +47 -0
- package/dist/design-embed/src/core/diagnostics/diagnostic.d.mts +16 -0
- package/dist/design-embed/src/core/nodes.d.mts +14 -0
- package/dist/design-embed/src/core/plugins/pluginApi.d.mts +34 -0
- package/dist/external/figmaApi.d.mts +17 -0
- package/dist/external/figmaApi.mjs +55 -0
- package/dist/external/figmaApi.test.d.mts +1 -0
- package/dist/external/figmaApi.test.mjs +101 -0
- package/dist/external/imageDownloader.d.mts +17 -0
- package/dist/external/imageDownloader.mjs +66 -0
- package/dist/external/imageDownloader.test.d.mts +1 -0
- package/dist/external/imageDownloader.test.mjs +42 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.mjs +7 -0
- package/dist/plugin.d.mts +16 -0
- package/dist/plugin.mjs +43 -0
- package/dist/types.d.mts +84 -0
- package/package.json +12 -10
- package/src/plugin.ts +2 -3
- package/dist/compilers/compilerUtils.js +0 -182
- package/dist/compilers/htmlCompiler.js +0 -35
- package/dist/compilers/index.js +0 -17
- package/dist/compilers/reactCompiler.js +0 -58
- package/dist/compilers/vanjsCompiler.js +0 -55
- package/dist/external/figmaApi.js +0 -74
- package/dist/external/imageDownloader.js +0 -82
- package/dist/index.js +0 -3
- package/dist/plugin.js +0 -56
- package/node_modules/@design-embed/config/README.md +0 -5
- package/node_modules/@design-embed/config/dist/index.js +0 -283
- package/node_modules/@design-embed/config/package.json +0 -19
- package/node_modules/@design-embed/config/src/index.ts +0 -518
- package/node_modules/@design-embed/core/README.md +0 -5
- package/node_modules/@design-embed/core/dist/diagnostics/diagnostic.js +0 -3
- package/node_modules/@design-embed/core/dist/diagnostics/jsonDiagnostic.js +0 -35
- package/node_modules/@design-embed/core/dist/index.js +0 -351
- package/node_modules/@design-embed/core/dist/pipeline/checkMode.js +0 -29
- package/node_modules/@design-embed/core/dist/plugins/pluginApi.js +0 -1
- package/node_modules/@design-embed/core/dist/plugins/pluginRegistry.js +0 -25
- package/node_modules/@design-embed/core/package.json +0 -19
- package/node_modules/@design-embed/core/src/diagnostics/diagnostic.ts +0 -18
- package/node_modules/@design-embed/core/src/diagnostics/jsonDiagnostic.ts +0 -51
- package/node_modules/@design-embed/core/src/index.ts +0 -591
- package/node_modules/@design-embed/core/src/pipeline/checkMode.ts +0 -46
- package/node_modules/@design-embed/core/src/plugins/pluginApi.ts +0 -78
- package/node_modules/@design-embed/core/src/plugins/pluginRegistry.ts +0 -37
- /package/dist/{types.js → types.mjs} +0 -0
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
import { sortTransformers } from "./plugins/pluginRegistry.js";
|
|
2
|
-
export { formatDiagnosticText, toJsonDiagnostic, toJsonDiagnostics, } from "./diagnostics/jsonDiagnostic.js";
|
|
3
|
-
export { checkGeneratedFiles } from "./pipeline/checkMode.js";
|
|
4
|
-
export { PluginRegistry, sortTransformers, } from "./plugins/pluginRegistry.js";
|
|
5
|
-
/**
|
|
6
|
-
* The main compiler entry point.
|
|
7
|
-
* Parses HTML, applies component mappings, runs transformers, and emits files.
|
|
8
|
-
*
|
|
9
|
-
* @param input - The compilation input.
|
|
10
|
-
* @returns A promise resolving to the compilation result.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* const result = await embed({
|
|
14
|
-
* html: '<div class="btn">Click me</div>',
|
|
15
|
-
* config: myConfig,
|
|
16
|
-
* targetEmitter: reactEmitter
|
|
17
|
-
* });
|
|
18
|
-
*/
|
|
19
|
-
export async function embed(input) {
|
|
20
|
-
let ast = parseHtml(input.html);
|
|
21
|
-
const diagnostics = validateComponentMappings(input.config?.components ?? []);
|
|
22
|
-
if (diagnostics.some((d) => d.severity === "error")) {
|
|
23
|
-
return { files: [], diagnostics };
|
|
24
|
-
}
|
|
25
|
-
if (input.transformers && input.transformers.length > 0) {
|
|
26
|
-
ast = await applyTransformers(ast, input.config ?? {}, input.transformers, diagnostics);
|
|
27
|
-
if (diagnostics.some((d) => d.severity === "error")) {
|
|
28
|
-
return { files: [], diagnostics };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
const { files } = input.targetEmitter.emit({
|
|
32
|
-
nodes: ast,
|
|
33
|
-
css: input.css,
|
|
34
|
-
config: input.config,
|
|
35
|
-
diagnostics,
|
|
36
|
-
});
|
|
37
|
-
return { files, diagnostics };
|
|
38
|
-
}
|
|
39
|
-
async function applyTransformers(ast, config, transformers, diagnostics) {
|
|
40
|
-
let nextAst = ast;
|
|
41
|
-
for (const transformer of sortTransformers(transformers)) {
|
|
42
|
-
try {
|
|
43
|
-
const result = await transformer.transform({
|
|
44
|
-
ast: nextAst,
|
|
45
|
-
config,
|
|
46
|
-
diagnostics,
|
|
47
|
-
});
|
|
48
|
-
if (result.diagnostics) {
|
|
49
|
-
diagnostics.push(...result.diagnostics);
|
|
50
|
-
}
|
|
51
|
-
if (result.ast) {
|
|
52
|
-
nextAst = result.ast;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch (error) {
|
|
56
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
-
diagnostics.push({
|
|
58
|
-
code: "TRANSFORMER_FAILED",
|
|
59
|
-
message: `Transformer ${transformer.name} failed: ${message}`,
|
|
60
|
-
severity: "error",
|
|
61
|
-
details: { transformer: transformer.name },
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return nextAst;
|
|
66
|
-
}
|
|
67
|
-
export function applyComponentMappings(nodes, mappings, diagnostics = []) {
|
|
68
|
-
const parsedMappings = mappings
|
|
69
|
-
.map((mapping, index) => ({
|
|
70
|
-
index,
|
|
71
|
-
mapping,
|
|
72
|
-
selector: parseSelector(mapping.selector),
|
|
73
|
-
}))
|
|
74
|
-
.filter(({ selector }) => selector !== undefined);
|
|
75
|
-
return nodes.map((node) => {
|
|
76
|
-
if (node.kind !== "element") {
|
|
77
|
-
return node;
|
|
78
|
-
}
|
|
79
|
-
const match = parsedMappings.find(({ selector }) => matchesSelector(node, selector));
|
|
80
|
-
if (match) {
|
|
81
|
-
const props = extractProps(node, match.mapping, diagnostics);
|
|
82
|
-
return {
|
|
83
|
-
kind: "component",
|
|
84
|
-
component: match.mapping.importName ?? inferImportName(match.mapping),
|
|
85
|
-
importName: match.mapping.importName ?? inferImportName(match.mapping),
|
|
86
|
-
importPath: match.mapping.component,
|
|
87
|
-
props,
|
|
88
|
-
children: props.children?.kind === "children"
|
|
89
|
-
? undefined
|
|
90
|
-
: applyComponentMappings(node.children ?? [], mappings, diagnostics),
|
|
91
|
-
source: node.source,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
...node,
|
|
96
|
-
children: applyComponentMappings(node.children ?? [], mappings, diagnostics),
|
|
97
|
-
};
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
export function parseSelector(selector) {
|
|
101
|
-
const trimmed = selector.trim();
|
|
102
|
-
if (!trimmed || /[\s>+~,:]/.test(trimmed)) {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
const parsed = { classes: [], attributes: {} };
|
|
106
|
-
let rest = trimmed;
|
|
107
|
-
const tagMatch = rest.match(/^[a-zA-Z][a-zA-Z0-9-]*/);
|
|
108
|
-
if (tagMatch?.[0]) {
|
|
109
|
-
parsed.tagName = tagMatch[0].toLowerCase();
|
|
110
|
-
rest = rest.slice(tagMatch[0].length);
|
|
111
|
-
}
|
|
112
|
-
while (rest) {
|
|
113
|
-
if (rest.startsWith(".")) {
|
|
114
|
-
const match = rest.match(/^\.([a-zA-Z_][a-zA-Z0-9_-]*)/);
|
|
115
|
-
if (!match?.[1]) {
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
parsed.classes.push(match[1]);
|
|
119
|
-
rest = rest.slice(match[0].length);
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
if (rest.startsWith("#")) {
|
|
123
|
-
const match = rest.match(/^#([a-zA-Z_][a-zA-Z0-9_-]*)/);
|
|
124
|
-
if (!match?.[1] || parsed.id) {
|
|
125
|
-
return undefined;
|
|
126
|
-
}
|
|
127
|
-
parsed.id = match[1];
|
|
128
|
-
rest = rest.slice(match[0].length);
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (rest.startsWith("[")) {
|
|
132
|
-
const match = rest.match(/^\[([a-zA-Z_][a-zA-Z0-9_.:-]*)(?:=(?:"([^"]*)"|'([^']*)'|([^\]]+)))?\]/);
|
|
133
|
-
if (!match?.[1]) {
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
|
-
parsed.attributes[match[1]] = match[2] ?? match[3] ?? match[4] ?? "";
|
|
137
|
-
rest = rest.slice(match[0].length);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
return undefined;
|
|
141
|
-
}
|
|
142
|
-
return parsed;
|
|
143
|
-
}
|
|
144
|
-
export function matchesSelector(node, selector) {
|
|
145
|
-
if (node.kind !== "element") {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
const attributes = node.attributes ?? {};
|
|
149
|
-
if (selector.tagName && node.tagName !== selector.tagName) {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
if (selector.id && attributes.id !== selector.id) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
const classNames = new Set((attributes.class ?? "").split(/\s+/).filter(Boolean));
|
|
156
|
-
for (const className of selector.classes) {
|
|
157
|
-
if (!classNames.has(className)) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
for (const [name, value] of Object.entries(selector.attributes)) {
|
|
162
|
-
if (!(name in attributes)) {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
if (value !== "" && attributes[name] !== value) {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return true;
|
|
170
|
-
}
|
|
171
|
-
export function parseHtml(html) {
|
|
172
|
-
const root = {
|
|
173
|
-
kind: "element",
|
|
174
|
-
tagName: "root",
|
|
175
|
-
attributes: {},
|
|
176
|
-
styles: {},
|
|
177
|
-
children: [],
|
|
178
|
-
};
|
|
179
|
-
const stack = [root];
|
|
180
|
-
const tokens = html.matchAll(/<!--[\s\S]*?-->|<\/?[a-zA-Z][^>]*>|[^<]+/g);
|
|
181
|
-
for (const token of tokens) {
|
|
182
|
-
const value = token[0];
|
|
183
|
-
const offset = token.index;
|
|
184
|
-
const source = getSourceLocation(html, offset);
|
|
185
|
-
if (value.startsWith("<!--")) {
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (value.startsWith("</")) {
|
|
189
|
-
const tagName = value.slice(2, -1).trim().toLowerCase();
|
|
190
|
-
while (stack.length > 1) {
|
|
191
|
-
const node = stack.pop();
|
|
192
|
-
if (node?.tagName === tagName) {
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
if (value.startsWith("<")) {
|
|
199
|
-
const selfClosing = /\/>$/.test(value) || isVoidElement(value);
|
|
200
|
-
const node = parseElement(value, source);
|
|
201
|
-
currentParent(stack).children?.push(node);
|
|
202
|
-
if (!selfClosing) {
|
|
203
|
-
stack.push(node);
|
|
204
|
-
}
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (value.trim()) {
|
|
208
|
-
currentParent(stack).children?.push({
|
|
209
|
-
kind: "text",
|
|
210
|
-
text: collapseWhitespace(value),
|
|
211
|
-
source,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return root.children ?? [];
|
|
216
|
-
}
|
|
217
|
-
export function parseInlineStyle(style) {
|
|
218
|
-
const styles = {};
|
|
219
|
-
if (!style) {
|
|
220
|
-
return styles;
|
|
221
|
-
}
|
|
222
|
-
for (const declaration of style.split(";")) {
|
|
223
|
-
const [property, ...valueParts] = declaration.split(":");
|
|
224
|
-
const value = valueParts.join(":").trim();
|
|
225
|
-
if (!property?.trim() || !value) {
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
styles[property.trim().toLowerCase()] = value;
|
|
229
|
-
}
|
|
230
|
-
return styles;
|
|
231
|
-
}
|
|
232
|
-
function parseElement(rawTag, source) {
|
|
233
|
-
const tagBody = rawTag.replace(/^</, "").replace(/\/?>$/, "").trim();
|
|
234
|
-
const tagName = tagBody.split(/\s+/, 1)[0]?.toLowerCase() ?? "div";
|
|
235
|
-
const attributeSource = tagBody.slice(tagName.length).trim();
|
|
236
|
-
const attributes = parseAttributes(attributeSource);
|
|
237
|
-
const styles = parseInlineStyle(attributes.style);
|
|
238
|
-
return {
|
|
239
|
-
kind: "element",
|
|
240
|
-
tagName,
|
|
241
|
-
attributes,
|
|
242
|
-
styles,
|
|
243
|
-
children: [],
|
|
244
|
-
source,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
function parseAttributes(source) {
|
|
248
|
-
const attributes = {};
|
|
249
|
-
const attributePattern = /([:@a-zA-Z_][:@a-zA-Z0-9_.-]*)(?:\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
250
|
-
for (const match of source.matchAll(attributePattern)) {
|
|
251
|
-
const name = match[1];
|
|
252
|
-
if (!name) {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
attributes[name] = match[3] ?? match[4] ?? match[5] ?? "";
|
|
256
|
-
}
|
|
257
|
-
return attributes;
|
|
258
|
-
}
|
|
259
|
-
function validateComponentMappings(mappings) {
|
|
260
|
-
const diagnostics = [];
|
|
261
|
-
for (const [index, mapping] of mappings.entries()) {
|
|
262
|
-
if (mapping.selector && !parseSelector(mapping.selector)) {
|
|
263
|
-
diagnostics.push({
|
|
264
|
-
code: "SELECTOR_UNSUPPORTED",
|
|
265
|
-
message: `Component mapping ${index} uses an unsupported selector: ${mapping.selector}`,
|
|
266
|
-
severity: "error",
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
for (const [propName, expression] of Object.entries(mapping.props ?? {})) {
|
|
270
|
-
if (!isSupportedPropExpression(expression)) {
|
|
271
|
-
diagnostics.push({
|
|
272
|
-
code: "PROP_EXPRESSION_UNSUPPORTED",
|
|
273
|
-
message: `Component mapping ${index} prop "${propName}" uses an unsupported expression: ${expression}`,
|
|
274
|
-
severity: "error",
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return diagnostics;
|
|
280
|
-
}
|
|
281
|
-
function isSupportedPropExpression(expression) {
|
|
282
|
-
return (!expression.startsWith("$") ||
|
|
283
|
-
expression === "$text" ||
|
|
284
|
-
expression === "$children" ||
|
|
285
|
-
/^\$attr\.[a-zA-Z_][a-zA-Z0-9_.:-]*$/.test(expression));
|
|
286
|
-
}
|
|
287
|
-
function extractProps(node, mapping, diagnostics) {
|
|
288
|
-
const props = {};
|
|
289
|
-
for (const [propName, expression] of Object.entries(mapping.props ?? {})) {
|
|
290
|
-
if (expression === "$text") {
|
|
291
|
-
props[propName] = { kind: "text", value: collectText(node) };
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
if (expression === "$children") {
|
|
295
|
-
props[propName] = { kind: "children", value: node.children ?? [] };
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
if (expression.startsWith("$attr.")) {
|
|
299
|
-
const attributeName = expression.slice("$attr.".length);
|
|
300
|
-
const value = node.attributes?.[attributeName];
|
|
301
|
-
if (value === undefined) {
|
|
302
|
-
diagnostics.push({
|
|
303
|
-
code: "PROP_ATTRIBUTE_MISSING",
|
|
304
|
-
message: `Attribute "${attributeName}" is missing for prop "${propName}".`,
|
|
305
|
-
severity: "warning",
|
|
306
|
-
});
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
props[propName] = { kind: "literal", value };
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
props[propName] = { kind: "literal", value: expression };
|
|
313
|
-
}
|
|
314
|
-
return props;
|
|
315
|
-
}
|
|
316
|
-
function collectText(node) {
|
|
317
|
-
if (node.kind === "text") {
|
|
318
|
-
return node.text ?? "";
|
|
319
|
-
}
|
|
320
|
-
return (node.children ?? [])
|
|
321
|
-
.map((child) => collectText(child))
|
|
322
|
-
.filter(Boolean)
|
|
323
|
-
.join(" ")
|
|
324
|
-
.trim();
|
|
325
|
-
}
|
|
326
|
-
function inferImportName(mapping) {
|
|
327
|
-
const lastSegment = mapping.component.split("/").filter(Boolean).at(-1);
|
|
328
|
-
return mapping.importName ?? lastSegment ?? "Component";
|
|
329
|
-
}
|
|
330
|
-
function currentParent(stack) {
|
|
331
|
-
const parent = stack[stack.length - 1];
|
|
332
|
-
if (!parent) {
|
|
333
|
-
throw new Error("HTML parser stack is empty.");
|
|
334
|
-
}
|
|
335
|
-
return parent;
|
|
336
|
-
}
|
|
337
|
-
function getSourceLocation(source, offset) {
|
|
338
|
-
const before = source.slice(0, offset);
|
|
339
|
-
const lines = before.split(/\r?\n/);
|
|
340
|
-
return {
|
|
341
|
-
offset,
|
|
342
|
-
line: lines.length,
|
|
343
|
-
column: (lines[lines.length - 1]?.length ?? 0) + 1,
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
function collapseWhitespace(value) {
|
|
347
|
-
return value.replace(/\s+/g, " ").trim();
|
|
348
|
-
}
|
|
349
|
-
function isVoidElement(tag) {
|
|
350
|
-
return /^<(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)(\s|>|\/)/i.test(tag);
|
|
351
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
export function checkGeneratedFiles(input) {
|
|
3
|
-
const diagnostics = [];
|
|
4
|
-
for (const file of input.files) {
|
|
5
|
-
const absolutePath = resolve(input.cwd, file.path);
|
|
6
|
-
const current = input.readFile(absolutePath);
|
|
7
|
-
if (current === undefined) {
|
|
8
|
-
diagnostics.push({
|
|
9
|
-
code: "CHECK_FILE_MISSING",
|
|
10
|
-
message: `Generated file is missing: ${file.path}`,
|
|
11
|
-
severity: "error",
|
|
12
|
-
file: file.path,
|
|
13
|
-
});
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
if (current !== file.contents) {
|
|
17
|
-
diagnostics.push({
|
|
18
|
-
code: "CHECK_FILE_STALE",
|
|
19
|
-
message: `Generated file is stale: ${file.path}`,
|
|
20
|
-
severity: "error",
|
|
21
|
-
file: file.path,
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
ok: diagnostics.length === 0,
|
|
27
|
-
diagnostics,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export class PluginRegistry {
|
|
2
|
-
#sourcePlugins = new Map();
|
|
3
|
-
#transformers = [];
|
|
4
|
-
registerSource(plugin) {
|
|
5
|
-
this.#sourcePlugins.set(plugin.name, plugin);
|
|
6
|
-
}
|
|
7
|
-
getSource(name) {
|
|
8
|
-
return this.#sourcePlugins.get(name);
|
|
9
|
-
}
|
|
10
|
-
listSources() {
|
|
11
|
-
return [...this.#sourcePlugins.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
12
|
-
}
|
|
13
|
-
registerTransformer(plugin) {
|
|
14
|
-
this.#transformers.push(plugin);
|
|
15
|
-
}
|
|
16
|
-
listTransformers() {
|
|
17
|
-
return sortTransformers(this.#transformers);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
export function sortTransformers(transformers) {
|
|
21
|
-
return [...transformers].sort((left, right) => {
|
|
22
|
-
const orderDelta = (left.order ?? 0) - (right.order ?? 0);
|
|
23
|
-
return orderDelta || left.name.localeCompare(right.name);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@design-embed/core",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"type": "module",
|
|
5
|
-
"private": true,
|
|
6
|
-
"exports": {
|
|
7
|
-
".": {
|
|
8
|
-
"types": "./src/index.ts",
|
|
9
|
-
"development": "./src/index.ts",
|
|
10
|
-
"default": "./dist/index.js"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"dist",
|
|
15
|
-
"src",
|
|
16
|
-
"!src/**/*.test.ts",
|
|
17
|
-
"README.md"
|
|
18
|
-
]
|
|
19
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { SourceLocation } from "../index.ts";
|
|
2
|
-
|
|
3
|
-
export type DiagnosticSeverity = "error" | "warning" | "info";
|
|
4
|
-
|
|
5
|
-
export interface Diagnostic {
|
|
6
|
-
code: string;
|
|
7
|
-
message: string;
|
|
8
|
-
severity: DiagnosticSeverity;
|
|
9
|
-
file?: string;
|
|
10
|
-
source?: SourceLocation;
|
|
11
|
-
selector?: string;
|
|
12
|
-
property?: string;
|
|
13
|
-
details?: Record<string, unknown>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function hasErrorDiagnostics(diagnostics: Diagnostic[]): boolean {
|
|
17
|
-
return diagnostics.some((diagnostic) => diagnostic.severity === "error");
|
|
18
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { Diagnostic } from "./diagnostic.ts";
|
|
2
|
-
|
|
3
|
-
export interface JsonDiagnostic {
|
|
4
|
-
code: string;
|
|
5
|
-
severity: "error" | "warning" | "info";
|
|
6
|
-
message: string;
|
|
7
|
-
file?: string;
|
|
8
|
-
line?: number;
|
|
9
|
-
column?: number;
|
|
10
|
-
details?: Record<string, unknown>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function toJsonDiagnostic(diagnostic: Diagnostic): JsonDiagnostic {
|
|
14
|
-
const details = {
|
|
15
|
-
...diagnostic.details,
|
|
16
|
-
...(diagnostic.selector ? { selector: diagnostic.selector } : {}),
|
|
17
|
-
...(diagnostic.property ? { property: diagnostic.property } : {}),
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
code: diagnostic.code,
|
|
22
|
-
severity: diagnostic.severity,
|
|
23
|
-
message: redactSecrets(diagnostic.message),
|
|
24
|
-
...(diagnostic.file ? { file: diagnostic.file } : {}),
|
|
25
|
-
...(diagnostic.source ? { line: diagnostic.source.line } : {}),
|
|
26
|
-
...(diagnostic.source ? { column: diagnostic.source.column } : {}),
|
|
27
|
-
...(Object.keys(details).length > 0 ? { details } : {}),
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function toJsonDiagnostics(diagnostics: Diagnostic[]): JsonDiagnostic[] {
|
|
32
|
-
return diagnostics.map(toJsonDiagnostic);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function formatDiagnosticText(diagnostic: Diagnostic): string {
|
|
36
|
-
const location = [
|
|
37
|
-
diagnostic.file,
|
|
38
|
-
diagnostic.source?.line,
|
|
39
|
-
diagnostic.source?.column,
|
|
40
|
-
]
|
|
41
|
-
.filter((part) => part !== undefined)
|
|
42
|
-
.join(":");
|
|
43
|
-
const prefix = location ? `${location}: ` : "";
|
|
44
|
-
return `${prefix}${diagnostic.severity}: ${diagnostic.code}: ${redactSecrets(diagnostic.message)}`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function redactSecrets(value: string): string {
|
|
48
|
-
return value
|
|
49
|
-
.replace(/figma[_-]?token\s*[:=]\s*[^\s]+/gi, "FIGMA_TOKEN=[redacted]")
|
|
50
|
-
.replace(/bearer\s+[a-z0-9._-]+/gi, "Bearer [redacted]");
|
|
51
|
-
}
|