@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.
Files changed (59) hide show
  1. package/LICENSE +1 -1
  2. package/dist/compilers/compilerUtils.d.mts +11 -0
  3. package/dist/compilers/compilerUtils.mjs +119 -0
  4. package/dist/compilers/htmlCompiler.d.mts +6 -0
  5. package/dist/compilers/htmlCompiler.mjs +31 -0
  6. package/dist/compilers/index.d.mts +11 -0
  7. package/dist/compilers/index.mjs +17 -0
  8. package/dist/compilers/index.test.d.mts +1 -0
  9. package/dist/compilers/index.test.mjs +45 -0
  10. package/dist/compilers/reactCompiler.d.mts +6 -0
  11. package/dist/compilers/reactCompiler.mjs +47 -0
  12. package/dist/compilers/vanjsCompiler.d.mts +6 -0
  13. package/dist/compilers/vanjsCompiler.mjs +47 -0
  14. package/dist/design-embed/src/core/diagnostics/diagnostic.d.mts +16 -0
  15. package/dist/design-embed/src/core/nodes.d.mts +14 -0
  16. package/dist/design-embed/src/core/plugins/pluginApi.d.mts +34 -0
  17. package/dist/external/figmaApi.d.mts +17 -0
  18. package/dist/external/figmaApi.mjs +55 -0
  19. package/dist/external/figmaApi.test.d.mts +1 -0
  20. package/dist/external/figmaApi.test.mjs +101 -0
  21. package/dist/external/imageDownloader.d.mts +17 -0
  22. package/dist/external/imageDownloader.mjs +66 -0
  23. package/dist/external/imageDownloader.test.d.mts +1 -0
  24. package/dist/external/imageDownloader.test.mjs +42 -0
  25. package/dist/index.d.mts +8 -0
  26. package/dist/index.mjs +7 -0
  27. package/dist/plugin.d.mts +16 -0
  28. package/dist/plugin.mjs +43 -0
  29. package/dist/types.d.mts +84 -0
  30. package/package.json +12 -10
  31. package/src/plugin.ts +2 -3
  32. package/dist/compilers/compilerUtils.js +0 -182
  33. package/dist/compilers/htmlCompiler.js +0 -35
  34. package/dist/compilers/index.js +0 -17
  35. package/dist/compilers/reactCompiler.js +0 -58
  36. package/dist/compilers/vanjsCompiler.js +0 -55
  37. package/dist/external/figmaApi.js +0 -74
  38. package/dist/external/imageDownloader.js +0 -82
  39. package/dist/index.js +0 -3
  40. package/dist/plugin.js +0 -56
  41. package/node_modules/@design-embed/config/README.md +0 -5
  42. package/node_modules/@design-embed/config/dist/index.js +0 -283
  43. package/node_modules/@design-embed/config/package.json +0 -19
  44. package/node_modules/@design-embed/config/src/index.ts +0 -518
  45. package/node_modules/@design-embed/core/README.md +0 -5
  46. package/node_modules/@design-embed/core/dist/diagnostics/diagnostic.js +0 -3
  47. package/node_modules/@design-embed/core/dist/diagnostics/jsonDiagnostic.js +0 -35
  48. package/node_modules/@design-embed/core/dist/index.js +0 -351
  49. package/node_modules/@design-embed/core/dist/pipeline/checkMode.js +0 -29
  50. package/node_modules/@design-embed/core/dist/plugins/pluginApi.js +0 -1
  51. package/node_modules/@design-embed/core/dist/plugins/pluginRegistry.js +0 -25
  52. package/node_modules/@design-embed/core/package.json +0 -19
  53. package/node_modules/@design-embed/core/src/diagnostics/diagnostic.ts +0 -18
  54. package/node_modules/@design-embed/core/src/diagnostics/jsonDiagnostic.ts +0 -51
  55. package/node_modules/@design-embed/core/src/index.ts +0 -591
  56. package/node_modules/@design-embed/core/src/pipeline/checkMode.ts +0 -46
  57. package/node_modules/@design-embed/core/src/plugins/pluginApi.ts +0 -78
  58. package/node_modules/@design-embed/core/src/plugins/pluginRegistry.ts +0 -37
  59. /package/dist/{types.js → types.mjs} +0 -0
@@ -1,55 +0,0 @@
1
- import { escapeHtml, escapeJsString, getNodeStyles, toCssText, } from "./compilerUtils.js";
2
- export const compileVanjs = (node) => [
3
- {
4
- path: "index.html",
5
- contents: `<!doctype html>
6
- <html lang="en">
7
- <head>
8
- <meta charset="UTF-8" />
9
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
10
- <title>${escapeHtml(node.name || "Figma Export")}</title>
11
- </head>
12
- <body>
13
- <div id="app"></div>
14
- <script type="module" src="./main.js"></script>
15
- </body>
16
- </html>
17
- `,
18
- },
19
- {
20
- path: "main.js",
21
- contents: `import van from 'https://cdn.jsdelivr.net/npm/vanjs-core@1.5.5/src/van.min.js';
22
-
23
- const { div, span } = van.tags;
24
-
25
- const App = () => (
26
- ${walkVanjs(node, undefined, 1).trimEnd()}
27
- );
28
-
29
- van.add(document.getElementById('app'), App());
30
- `,
31
- },
32
- ];
33
- function walkVanjs(node, parent, depth = 0) {
34
- if (!node || node.visible === false)
35
- return "null";
36
- const indent = " ".repeat(depth);
37
- const childIndent = " ".repeat(depth + 1);
38
- const tag = node.type === "TEXT" ? "span" : "div";
39
- const props = `{
40
- ${childIndent}style: ${escapeJsString(toCssText(getNodeStyles(node, parent)))},
41
- ${childIndent}"data-layer": ${escapeJsString(node.name || "LayoutBox")}
42
- ${indent}}`;
43
- if (node.type === "TEXT") {
44
- return `${indent}${tag}(${props}, ${escapeJsString(node.characters)})\n`;
45
- }
46
- const children = (node.children || [])
47
- .map((child) => walkVanjs(child, node, depth + 1))
48
- .filter((value) => value.trim() !== "null")
49
- .join(",");
50
- if (!children) {
51
- return `${indent}${tag}(${props})\n`;
52
- }
53
- return `${indent}${tag}(${props},
54
- ${children}${indent})\n`;
55
- }
@@ -1,74 +0,0 @@
1
- export function extractParamsFromURL(input) {
2
- const cleanInput = input.trim();
3
- const fileKeyPattern = /figma\.com\/(?:file|design)\/([^/]+)/;
4
- const fileKeyMatch = cleanInput.match(fileKeyPattern);
5
- const fileKey = fileKeyMatch?.[1] || cleanInput;
6
- let nodeId = null;
7
- try {
8
- if (cleanInput.includes("figma.com")) {
9
- const url = new URL(cleanInput);
10
- const rawNodeId = url.searchParams.get("node-id");
11
- if (rawNodeId) {
12
- nodeId = rawNodeId.replace(/-/g, ":");
13
- }
14
- }
15
- }
16
- catch {
17
- // Treat non-URL input as a raw Figma file key.
18
- }
19
- return { fileKey, nodeId };
20
- }
21
- export async function fetchFigmaNode(fileKey, nodeId, options) {
22
- const data = (await fetchFigmaApiResponse(fileKey, nodeId, options));
23
- const rootNode = nodeId ? data.nodes?.[nodeId]?.document : data.document;
24
- if (!rootNode) {
25
- throw new Error(`Could not find valid element tree for Node ID: ${nodeId}`);
26
- }
27
- const imageFills = await fetchFigmaImageFills(fileKey, options);
28
- attachImageFillUrls(rootNode, imageFills);
29
- return rootNode;
30
- }
31
- export async function fetchFigmaApiResponse(fileKey, nodeId, options) {
32
- const endpoint = buildFigmaNodeEndpoint(fileKey, nodeId);
33
- const fetcher = options.fetcher ?? fetch;
34
- const response = await fetcher(endpoint, {
35
- method: "GET",
36
- headers: { "X-Figma-Token": options.token },
37
- });
38
- if (!response.ok) {
39
- throw new Error(`Figma API Error: ${response.status} ${response.statusText}`);
40
- }
41
- return response.json();
42
- }
43
- export function buildFigmaNodeEndpoint(fileKey, nodeId) {
44
- if (!nodeId) {
45
- return `https://api.figma.com/v1/files/${fileKey}?depth=2`;
46
- }
47
- return `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${encodeURIComponent(nodeId)}`;
48
- }
49
- export async function fetchFigmaImageFills(fileKey, options) {
50
- const endpoint = buildFigmaImageFillsEndpoint(fileKey);
51
- const fetcher = options.fetcher ?? fetch;
52
- const response = await fetcher(endpoint, {
53
- method: "GET",
54
- headers: { "X-Figma-Token": options.token },
55
- });
56
- if (!response.ok) {
57
- throw new Error(`Figma image fills API Error: ${response.status} ${response.statusText}`);
58
- }
59
- const data = (await response.json());
60
- return Object.fromEntries(Object.entries(data.images || {}).filter((entry) => typeof entry[1] === "string"));
61
- }
62
- export function buildFigmaImageFillsEndpoint(fileKey) {
63
- return `https://api.figma.com/v1/files/${fileKey}/images`;
64
- }
65
- function attachImageFillUrls(node, imageFills) {
66
- for (const fill of node.fills || []) {
67
- if (fill.type === "IMAGE" && fill.imageRef) {
68
- fill.imageUrl = imageFills[fill.imageRef];
69
- }
70
- }
71
- for (const child of node.children || []) {
72
- attachImageFillUrls(child, imageFills);
73
- }
74
- }
@@ -1,82 +0,0 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
2
- import { join, posix } from "node:path";
3
- export async function downloadFigmaImageFills(rootNode, outDir, options = {}) {
4
- const targets = collectImageFillTargets(rootNode);
5
- const uniqueTargets = Array.from(new Map(targets.map((target) => [target.imageRef, target])).values());
6
- if (uniqueTargets.length === 0)
7
- return [];
8
- mkdirSync(outDir, { recursive: true });
9
- const downloadedImages = await Promise.all(uniqueTargets.map((target) => downloadImageFill(target, outDir, options)));
10
- const publicPathByRef = new Map(downloadedImages.map((image) => [image.imageRef, image.publicPath]));
11
- for (const target of targets) {
12
- const fill = target.fills[target.fillIndex];
13
- if (fill) {
14
- fill.imageLocalPath = publicPathByRef.get(target.imageRef);
15
- }
16
- }
17
- return downloadedImages;
18
- }
19
- function collectImageFillTargets(node) {
20
- const targets = [];
21
- node.fills?.forEach((fill, fillIndex, fills) => {
22
- if (fill.type === "IMAGE" && fill.imageRef && fill.imageUrl) {
23
- targets.push({
24
- imageRef: fill.imageRef,
25
- imageUrl: fill.imageUrl,
26
- fills,
27
- fillIndex,
28
- });
29
- }
30
- });
31
- for (const child of node.children || []) {
32
- targets.push(...collectImageFillTargets(child));
33
- }
34
- return targets;
35
- }
36
- async function downloadImageFill(target, outDir, options) {
37
- const fetcher = options.fetcher ?? fetch;
38
- const response = await fetcher(target.imageUrl);
39
- if (!response.ok) {
40
- throw new Error(`Figma image download failed for ${target.imageRef}: ${response.status} ${response.statusText}`);
41
- }
42
- const extension = extensionFromResponse(response, target.imageUrl);
43
- const filename = `${sanitizeFilename(target.imageRef)}.${extension}`;
44
- const filePath = join(outDir, filename);
45
- const publicPath = posix.join(options.publicPath || outDir, filename);
46
- writeFileSync(filePath, Buffer.from(await response.arrayBuffer()));
47
- return {
48
- imageRef: target.imageRef,
49
- sourceUrl: target.imageUrl,
50
- filePath,
51
- publicPath,
52
- };
53
- }
54
- function extensionFromResponse(response, url) {
55
- const contentType = response.headers.get("content-type")?.split(";")[0];
56
- switch (contentType) {
57
- case "image/jpeg":
58
- return "jpg";
59
- case "image/png":
60
- return "png";
61
- case "image/svg+xml":
62
- return "svg";
63
- case "image/webp":
64
- return "webp";
65
- case "image/gif":
66
- return "gif";
67
- }
68
- const pathname = safeUrlPathname(url);
69
- const extension = pathname.match(/\.([a-z0-9]+)$/i)?.[1]?.toLowerCase();
70
- return extension || "img";
71
- }
72
- function safeUrlPathname(url) {
73
- try {
74
- return new URL(url).pathname;
75
- }
76
- catch {
77
- return url;
78
- }
79
- }
80
- function sanitizeFilename(value) {
81
- return value.replace(/[^a-zA-Z0-9_-]/g, "_");
82
- }
package/dist/index.js DELETED
@@ -1,3 +0,0 @@
1
- export { compileHtml, compileReact, compileVanjs, getCompiler, isCompilerMode, } from "./compilers/index.js";
2
- export { extractParamsFromURL, fetchFigmaApiResponse, fetchFigmaNode, } from "./external/figmaApi.js";
3
- export { FigmaHtmlPlugin } from "./plugin.js";
package/dist/plugin.js DELETED
@@ -1,56 +0,0 @@
1
- import { join } from "node:path";
2
- import { compileHtml } from "./compilers/index.js";
3
- import { extractParamsFromURL, fetchFigmaNode } from "./external/figmaApi.js";
4
- import { downloadFigmaImageFills } from "./external/imageDownloader.js";
5
- export class FigmaHtmlPlugin {
6
- options;
7
- name = "figma-html";
8
- constructor(options) {
9
- this.options = options;
10
- }
11
- async run(input) {
12
- const { url, token: optionsToken, assetsDir = "assets" } = this.options;
13
- const token = optionsToken ?? process.env.FIGMA_TOKEN;
14
- if (!token) {
15
- return {
16
- diagnostics: [
17
- {
18
- code: "FIGMA_TOKEN_REQUIRED",
19
- message: "figma-html requires a Figma token. Pass token in the FigmaHtmlPlugin constructor or set the FIGMA_TOKEN environment variable.",
20
- severity: "error",
21
- },
22
- ],
23
- };
24
- }
25
- try {
26
- const { fileKey, nodeId } = extractParamsFromURL(url);
27
- const rootNode = await fetchFigmaNode(fileKey, nodeId, { token });
28
- const downloadedImages = await downloadFigmaImageFills(rootNode, join(input.cwd, assetsDir), { publicPath: assetsDir });
29
- const [htmlFile] = compileHtml(rootNode);
30
- return {
31
- html: htmlFile?.contents,
32
- diagnostics: downloadedImages.length > 0
33
- ? [
34
- {
35
- code: "FIGMA_ASSETS_DOWNLOADED",
36
- message: `Downloaded ${downloadedImages.length} image asset(s).`,
37
- severity: "info",
38
- },
39
- ]
40
- : [],
41
- };
42
- }
43
- catch (error) {
44
- const message = error instanceof Error ? error.message : String(error);
45
- return {
46
- diagnostics: [
47
- {
48
- code: "FIGMA_HTML_FAILED",
49
- message,
50
- severity: "error",
51
- },
52
- ],
53
- };
54
- }
55
- }
56
- }
@@ -1,5 +0,0 @@
1
- # @design-embed/config
2
-
3
- Internal configuration loading and validation for design-embed.
4
-
5
- It parses the project config format used by the compiler, validates supported output targets, style modes, component mappings, token settings, plugin settings, and local transformer declarations. It also reports config diagnostics in a stable shape so callers can fail early before compilation begins.
@@ -1,283 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { dirname, isAbsolute, resolve } from "node:path";
3
- import { pathToFileURL } from "node:url";
4
- /**
5
- * Helper to define configuration with type safety.
6
- *
7
- * @param config - The configuration object.
8
- * @returns The same configuration object.
9
- *
10
- * @example
11
- * export default defineConfig({
12
- * output: { target: 'react' }
13
- * });
14
- */
15
- export function defineConfig(config) {
16
- return config;
17
- }
18
- /**
19
- * Asynchronously loads a configuration file from disk.
20
- * Supports .ts, .js, and .mjs files via dynamic import.
21
- *
22
- * @param configPath - Path to the config file.
23
- * @param cwd - Current working directory.
24
- * @returns A promise resolving to the load result.
25
- */
26
- export async function loadConfig(configPath, cwd = process.cwd()) {
27
- const diagnostics = [];
28
- const resolvedPath = isAbsolute(configPath)
29
- ? configPath
30
- : resolve(cwd, configPath);
31
- if (!existsSync(resolvedPath)) {
32
- return {
33
- diagnostics: [
34
- {
35
- code: "CONFIG_NOT_FOUND",
36
- message: `Config file not found: ${resolvedPath}`,
37
- severity: "error",
38
- },
39
- ],
40
- };
41
- }
42
- if (!/\.(ts|js|mjs)$/.test(resolvedPath)) {
43
- return {
44
- diagnostics: [
45
- {
46
- code: "CONFIG_UNSUPPORTED_FORMAT",
47
- message: `Unsupported config format: ${resolvedPath}. Only .ts, .js, and .mjs are supported.`,
48
- severity: "error",
49
- },
50
- ],
51
- };
52
- }
53
- try {
54
- const module = await import(pathToFileURL(resolvedPath).href);
55
- const config = module.default ?? module.config;
56
- if (!config) {
57
- return {
58
- diagnostics: [
59
- {
60
- code: "CONFIG_INVALID",
61
- message: `Config file must export a default object or a named 'config' object: ${resolvedPath}`,
62
- severity: "error",
63
- },
64
- ],
65
- };
66
- }
67
- diagnostics.push(...validateConfig(config));
68
- diagnostics.push(...validateTransformerPaths(config, dirname(resolvedPath)));
69
- return { config, diagnostics };
70
- }
71
- catch (error) {
72
- const message = error instanceof Error ? error.message : String(error);
73
- return {
74
- diagnostics: [
75
- {
76
- code: "CONFIG_INVALID",
77
- message: `Failed to load config file: ${message}`,
78
- severity: "error",
79
- },
80
- ],
81
- };
82
- }
83
- }
84
- export function validateConfig(config) {
85
- const diagnostics = [];
86
- const target = config.output?.target;
87
- const styleMode = config.output?.styleMode;
88
- if (target && target !== "html" && target !== "react") {
89
- diagnostics.push({
90
- code: "UNSUPPORTED_TARGET",
91
- message: `Unsupported output target: ${target}`,
92
- severity: "error",
93
- });
94
- }
95
- if (styleMode &&
96
- styleMode !== "inline" &&
97
- styleMode !== "css-modules" &&
98
- styleMode !== "tailwind") {
99
- diagnostics.push({
100
- code: "STYLE_MODE_UNSUPPORTED",
101
- message: `Unsupported style mode: ${styleMode}`,
102
- severity: "error",
103
- });
104
- }
105
- for (const [index, component] of (config.components ?? []).entries()) {
106
- if (!component.selector || typeof component.selector !== "string") {
107
- diagnostics.push({
108
- code: "COMPONENT_SELECTOR_INVALID",
109
- message: `Component mapping ${index} must include a selector.`,
110
- severity: "error",
111
- });
112
- }
113
- if (!component.component || typeof component.component !== "string") {
114
- diagnostics.push({
115
- code: "COMPONENT_IMPORT_INVALID",
116
- message: `Component mapping ${index} must include a component path.`,
117
- severity: "error",
118
- });
119
- }
120
- }
121
- const spacing = config.tokens?.spacing;
122
- if (spacing?.unit && spacing.unit !== "px" && spacing.unit !== "rem") {
123
- diagnostics.push({
124
- code: "TOKEN_SPACING_UNIT_INVALID",
125
- message: `Unsupported spacing unit: ${spacing.unit}`,
126
- severity: "error",
127
- });
128
- }
129
- if (spacing?.threshold !== undefined && !Number.isFinite(spacing.threshold)) {
130
- diagnostics.push({
131
- code: "TOKEN_SPACING_THRESHOLD_INVALID",
132
- message: "Spacing threshold must be a finite number.",
133
- severity: "error",
134
- });
135
- }
136
- for (const [name, value] of Object.entries(spacing?.values ?? {})) {
137
- if (!Number.isFinite(value)) {
138
- diagnostics.push({
139
- code: "TOKEN_SPACING_VALUE_INVALID",
140
- message: `Spacing token ${name} must be a finite number.`,
141
- severity: "error",
142
- });
143
- }
144
- }
145
- for (const [name, value] of Object.entries(config.tokens?.colors ?? {})) {
146
- if (!/^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(value)) {
147
- diagnostics.push({
148
- code: "TOKEN_COLOR_INVALID",
149
- message: `Color token ${name} must be a hex color.`,
150
- severity: "error",
151
- });
152
- }
153
- }
154
- if (config.tokens?.colorThreshold !== undefined &&
155
- !Number.isFinite(config.tokens.colorThreshold)) {
156
- diagnostics.push({
157
- code: "TOKEN_COLOR_THRESHOLD_INVALID",
158
- message: "Color threshold must be a finite number.",
159
- severity: "error",
160
- });
161
- }
162
- for (const [groupName, group] of Object.entries({
163
- sizing: config.tokens?.sizing,
164
- typography: config.tokens?.typography,
165
- })) {
166
- if (!group) {
167
- continue;
168
- }
169
- if (group.unit && group.unit !== "px" && group.unit !== "rem") {
170
- diagnostics.push({
171
- code: "TOKEN_NUMERIC_UNIT_INVALID",
172
- message: `Unsupported ${groupName} unit: ${group.unit}`,
173
- severity: "error",
174
- });
175
- }
176
- if (group.threshold !== undefined && !Number.isFinite(group.threshold)) {
177
- diagnostics.push({
178
- code: "TOKEN_NUMERIC_THRESHOLD_INVALID",
179
- message: `${groupName} threshold must be a finite number.`,
180
- severity: "error",
181
- });
182
- }
183
- for (const [name, value] of Object.entries(group.values ?? {})) {
184
- if (!Number.isFinite(value)) {
185
- diagnostics.push({
186
- code: "TOKEN_NUMERIC_VALUE_INVALID",
187
- message: `${groupName} token ${name} must be a finite number.`,
188
- severity: "error",
189
- });
190
- }
191
- }
192
- }
193
- for (const [index, transformer] of (config.transformers ?? []).entries()) {
194
- if (!transformer.path || typeof transformer.path !== "string") {
195
- diagnostics.push({
196
- code: "TRANSFORMER_PATH_INVALID",
197
- message: `Transformer ${index} must include a path.`,
198
- severity: "error",
199
- });
200
- }
201
- if (transformer.order !== undefined &&
202
- !Number.isFinite(transformer.order)) {
203
- diagnostics.push({
204
- code: "TRANSFORMER_ORDER_INVALID",
205
- message: `Transformer ${index} order must be a finite number.`,
206
- severity: "error",
207
- });
208
- }
209
- }
210
- validateTestGeneration(config.tests, diagnostics);
211
- return diagnostics;
212
- }
213
- function validateTestGeneration(tests, diagnostics) {
214
- if (!tests) {
215
- return;
216
- }
217
- if (tests.runner && tests.runner !== "playwright") {
218
- diagnostics.push({
219
- code: "TEST_RUNNER_UNSUPPORTED",
220
- message: `Unsupported test runner: ${tests.runner}`,
221
- severity: "error",
222
- });
223
- }
224
- for (const [index, viewport] of (tests.viewports ?? []).entries()) {
225
- if (!Number.isFinite(viewport.width) || viewport.width <= 0) {
226
- diagnostics.push({
227
- code: "TEST_VIEWPORT_WIDTH_INVALID",
228
- message: `Test viewport ${index} width must be a positive finite number.`,
229
- severity: "error",
230
- });
231
- }
232
- if (!Number.isFinite(viewport.height) || viewport.height <= 0) {
233
- diagnostics.push({
234
- code: "TEST_VIEWPORT_HEIGHT_INVALID",
235
- message: `Test viewport ${index} height must be a positive finite number.`,
236
- severity: "error",
237
- });
238
- }
239
- }
240
- for (const [index, state] of (tests.states ?? []).entries()) {
241
- if (!state.name || typeof state.name !== "string") {
242
- diagnostics.push({
243
- code: "TEST_STATE_NAME_INVALID",
244
- message: `Test state ${index} must include a name.`,
245
- severity: "error",
246
- });
247
- }
248
- }
249
- if (tests.assertions?.layoutTolerance !== undefined &&
250
- (!Number.isFinite(tests.assertions.layoutTolerance) ||
251
- tests.assertions.layoutTolerance < 0)) {
252
- diagnostics.push({
253
- code: "TEST_LAYOUT_TOLERANCE_INVALID",
254
- message: "Test layout tolerance must be a finite number greater than or equal to 0.",
255
- severity: "error",
256
- });
257
- }
258
- }
259
- function isPackageName(path) {
260
- return !path.startsWith(".") && !isAbsolute(path);
261
- }
262
- function validateTransformerPaths(config, configDir) {
263
- const diagnostics = [];
264
- for (const transformer of config.transformers ?? []) {
265
- if (!transformer.path || typeof transformer.path !== "string") {
266
- continue;
267
- }
268
- if (isPackageName(transformer.path)) {
269
- continue;
270
- }
271
- const resolvedPath = isAbsolute(transformer.path)
272
- ? transformer.path
273
- : resolve(configDir, transformer.path);
274
- if (!existsSync(resolvedPath)) {
275
- diagnostics.push({
276
- code: "TRANSFORMER_NOT_FOUND",
277
- message: `Transformer file not found: ${resolvedPath}`,
278
- severity: "error",
279
- });
280
- }
281
- }
282
- return diagnostics;
283
- }
@@ -1,19 +0,0 @@
1
- {
2
- "name": "@design-embed/config",
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
- }