design-embed 0.1.0 → 0.2.0
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/README.md +98 -2
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +273 -0
- package/dist/core-BLV62TaX.mjs +907 -0
- package/dist/index.d.mts +273 -0
- package/dist/index.mjs +2 -0
- package/package.json +6 -19
- package/src/cli.ts +8 -16
- package/src/commands/compile.ts +25 -110
- package/src/commands/generateTests.ts +14 -96
- package/src/commands/init.ts +52 -55
- package/src/commands/plugin.ts +6 -21
- package/src/config/index.ts +302 -0
- package/{node_modules/@design-embed/core/src → src/core}/index.ts +151 -163
- package/src/core/nodes.ts +74 -0
- package/src/core/plugins/pluginApi.ts +44 -0
- package/src/core/types.ts +120 -0
- package/src/index.ts +48 -2
- package/src/targets/html.ts +621 -0
- package/dist/args.js +0 -36
- package/dist/cli.js +0 -35
- package/dist/commands/check.js +0 -4
- package/dist/commands/compile.js +0 -157
- package/dist/commands/generateTests.js +0 -113
- package/dist/commands/init.js +0 -102
- package/dist/commands/plugin.js +0 -68
- package/dist/index.js +0 -2
- 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/plugins/pluginApi.ts +0 -78
- package/node_modules/@design-embed/core/src/plugins/pluginRegistry.ts +0 -37
- /package/{node_modules/@design-embed/core/src → src/core}/diagnostics/diagnostic.ts +0 -0
- /package/{node_modules/@design-embed/core/src → src/core}/diagnostics/jsonDiagnostic.ts +0 -0
- /package/{node_modules/@design-embed/core/src → src/core}/pipeline/checkMode.ts +0 -0
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, relative, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { htmlTarget } from "../targets/html.ts";
|
|
5
|
+
import type { Diagnostic } from "./diagnostics/diagnostic.ts";
|
|
6
|
+
import type {
|
|
7
|
+
DesignNode,
|
|
8
|
+
ParsedSelector,
|
|
9
|
+
PropValue,
|
|
10
|
+
SourceLocation,
|
|
11
|
+
} from "./nodes.ts";
|
|
12
|
+
import type { GeneratedFile } from "./plugins/pluginApi.ts";
|
|
1
13
|
import type {
|
|
2
14
|
ComponentMapping,
|
|
3
15
|
DesignEmbedConfig,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { sortTransformers } from "./plugins/pluginRegistry.ts";
|
|
16
|
+
TargetEmitter,
|
|
17
|
+
TargetTestGenerator,
|
|
18
|
+
} from "./types.ts";
|
|
8
19
|
|
|
9
20
|
export type {
|
|
10
21
|
Diagnostic,
|
|
@@ -16,6 +27,12 @@ export {
|
|
|
16
27
|
toJsonDiagnostic,
|
|
17
28
|
toJsonDiagnostics,
|
|
18
29
|
} from "./diagnostics/jsonDiagnostic.ts";
|
|
30
|
+
export type {
|
|
31
|
+
DesignNode,
|
|
32
|
+
ParsedSelector,
|
|
33
|
+
PropValue,
|
|
34
|
+
SourceLocation,
|
|
35
|
+
} from "./nodes.ts";
|
|
19
36
|
export type {
|
|
20
37
|
CheckModeInput,
|
|
21
38
|
CheckModeResult,
|
|
@@ -23,122 +40,52 @@ export type {
|
|
|
23
40
|
export { checkGeneratedFiles } from "./pipeline/checkMode.ts";
|
|
24
41
|
export type {
|
|
25
42
|
GeneratedAsset,
|
|
43
|
+
GeneratedFile,
|
|
26
44
|
SourcePlugin,
|
|
27
45
|
SourcePluginInput,
|
|
28
46
|
SourcePluginResult,
|
|
29
|
-
TargetEmitInput,
|
|
30
47
|
TargetEmitResult,
|
|
48
|
+
TargetTestGenerateResult,
|
|
49
|
+
} from "./plugins/pluginApi.ts";
|
|
50
|
+
export type {
|
|
51
|
+
ComponentMapping,
|
|
52
|
+
DesignEmbedConfig,
|
|
53
|
+
NumericTokenGroup,
|
|
54
|
+
StyleMappings,
|
|
55
|
+
StyleMode,
|
|
56
|
+
TargetEmitInput,
|
|
31
57
|
TargetEmitter,
|
|
32
58
|
TargetTestGenerateInput,
|
|
33
|
-
TargetTestGenerateResult,
|
|
34
59
|
TargetTestGenerator,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
sortTransformers,
|
|
42
|
-
} from "./plugins/pluginRegistry.ts";
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Represents a file generated by the compiler.
|
|
46
|
-
*/
|
|
47
|
-
export interface GeneratedFile {
|
|
48
|
-
/** Relative path from the output directory. */
|
|
49
|
-
path: string;
|
|
50
|
-
/** File content. */
|
|
51
|
-
contents: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Location in the source HTML file.
|
|
56
|
-
*/
|
|
57
|
-
export interface SourceLocation {
|
|
58
|
-
/** Absolute offset in characters. */
|
|
59
|
-
offset: number;
|
|
60
|
-
/** 1-based line number. */
|
|
61
|
-
line: number;
|
|
62
|
-
/** 1-based column number. */
|
|
63
|
-
column: number;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* A normalized node in the design AST.
|
|
68
|
-
*/
|
|
69
|
-
export interface DesignNode {
|
|
70
|
-
/** The type of node. */
|
|
71
|
-
kind: "element" | "text" | "component";
|
|
72
|
-
/** HTML tag name (for element kind). */
|
|
73
|
-
tagName?: string;
|
|
74
|
-
/** HTML attributes (for element kind). */
|
|
75
|
-
attributes?: Record<string, string>;
|
|
76
|
-
/** Parsed inline styles (for element kind). */
|
|
77
|
-
styles?: Record<string, string>;
|
|
78
|
-
/** Utility classes to apply. */
|
|
79
|
-
generatedClassNames?: string[];
|
|
80
|
-
/** Child nodes. */
|
|
81
|
-
children?: DesignNode[];
|
|
82
|
-
/** Inner text content (for text kind). */
|
|
83
|
-
text?: string;
|
|
84
|
-
/** Original location in the source HTML. */
|
|
85
|
-
source?: SourceLocation;
|
|
86
|
-
/** Component name (for component kind). */
|
|
87
|
-
component?: string;
|
|
88
|
-
/** Named export of the component. */
|
|
89
|
-
importName?: string;
|
|
90
|
-
/** Mapped prop values for the component. */
|
|
91
|
-
props?: Record<string, PropValue>;
|
|
92
|
-
/** Import path of the component. */
|
|
93
|
-
importPath?: string;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* A value passed to a component prop.
|
|
98
|
-
*/
|
|
99
|
-
export type PropValue =
|
|
100
|
-
| { kind: "literal"; value: string | number | boolean }
|
|
101
|
-
| { kind: "text"; value: string }
|
|
102
|
-
| { kind: "children"; value: DesignNode[] };
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* A parsed CSS selector.
|
|
106
|
-
*/
|
|
107
|
-
export interface ParsedSelector {
|
|
108
|
-
/** Optional tag name. */
|
|
109
|
-
tagName?: string;
|
|
110
|
-
/** Optional ID selector. */
|
|
111
|
-
id?: string;
|
|
112
|
-
/** List of class names. */
|
|
113
|
-
classes: string[];
|
|
114
|
-
/** Attribute selectors. */
|
|
115
|
-
attributes: Record<string, string>;
|
|
116
|
-
}
|
|
60
|
+
TestAssertions,
|
|
61
|
+
TestGenerationConfig,
|
|
62
|
+
TestState,
|
|
63
|
+
TestViewport,
|
|
64
|
+
TokenConfig,
|
|
65
|
+
} from "./types.ts";
|
|
117
66
|
|
|
118
67
|
/**
|
|
119
68
|
* Input for the core embed function.
|
|
120
69
|
*/
|
|
121
70
|
export interface DesignEmbedInput {
|
|
122
|
-
/** The source design HTML. */
|
|
123
|
-
html: string;
|
|
124
|
-
/** Optional external CSS. */
|
|
125
|
-
css?: string;
|
|
126
|
-
/** Optional path to the config file (for resolution). */
|
|
127
|
-
configPath?: string;
|
|
128
71
|
/** The compiler configuration. */
|
|
129
72
|
config?: DesignEmbedConfig;
|
|
130
73
|
/** Working directory. */
|
|
131
74
|
cwd?: string;
|
|
132
|
-
/**
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
|
|
75
|
+
/** When true, skips writing output files to disk. Defaults to false. */
|
|
76
|
+
dryRun?: boolean;
|
|
77
|
+
/** When true, generates test files alongside output files. Defaults to false. */
|
|
78
|
+
generateTests?: boolean;
|
|
136
79
|
}
|
|
137
80
|
|
|
138
81
|
/**
|
|
139
82
|
* Result of the embedding process.
|
|
140
83
|
*/
|
|
141
84
|
export interface DesignEmbedResult {
|
|
85
|
+
/** Source HTML resolved from the config's source plugin. */
|
|
86
|
+
html: string;
|
|
87
|
+
/** Source CSS resolved from the config's source plugin. */
|
|
88
|
+
css?: string;
|
|
142
89
|
/** Generated files. */
|
|
143
90
|
files: GeneratedFile[];
|
|
144
91
|
/** Diagnostics reported during compilation. */
|
|
@@ -147,83 +94,124 @@ export interface DesignEmbedResult {
|
|
|
147
94
|
|
|
148
95
|
/**
|
|
149
96
|
* The main compiler entry point.
|
|
150
|
-
* Parses HTML, applies component mappings,
|
|
97
|
+
* Parses HTML, applies component mappings, and emits files.
|
|
151
98
|
*
|
|
152
99
|
* @param input - The compilation input.
|
|
153
100
|
* @returns A promise resolving to the compilation result.
|
|
154
101
|
*
|
|
155
|
-
* @example
|
|
156
|
-
* const result = await embed({
|
|
157
|
-
* html: '<div class="btn">Click me</div>',
|
|
158
|
-
* config: myConfig,
|
|
159
|
-
* targetEmitter: reactEmitter
|
|
160
|
-
* });
|
|
161
102
|
*/
|
|
162
103
|
export async function embed(
|
|
163
104
|
input: DesignEmbedInput,
|
|
164
105
|
): Promise<DesignEmbedResult> {
|
|
165
|
-
|
|
166
|
-
|
|
106
|
+
const cwd = input.cwd ?? process.cwd();
|
|
107
|
+
|
|
108
|
+
if (!input.config?.source) {
|
|
109
|
+
return {
|
|
110
|
+
html: "",
|
|
111
|
+
files: [],
|
|
112
|
+
diagnostics: [
|
|
113
|
+
{
|
|
114
|
+
code: "PLUGIN_REQUIRED",
|
|
115
|
+
message: "Config must include a source plugin.",
|
|
116
|
+
severity: "error",
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const sourceResult = await input.config.source.run({ cwd });
|
|
123
|
+
const diagnostics = [...sourceResult.diagnostics];
|
|
167
124
|
|
|
168
125
|
if (diagnostics.some((d) => d.severity === "error")) {
|
|
169
|
-
return { files: [], diagnostics };
|
|
126
|
+
return { html: "", files: [], diagnostics };
|
|
170
127
|
}
|
|
171
128
|
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
129
|
+
if (!sourceResult.html) {
|
|
130
|
+
return {
|
|
131
|
+
html: "",
|
|
132
|
+
files: [],
|
|
133
|
+
diagnostics: [
|
|
134
|
+
...diagnostics,
|
|
135
|
+
{
|
|
136
|
+
code: "PLUGIN_NO_HTML",
|
|
137
|
+
message: "Source plugin produced no HTML.",
|
|
138
|
+
severity: "error",
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
};
|
|
182
142
|
}
|
|
183
143
|
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
144
|
+
const html = sourceResult.html;
|
|
145
|
+
const css = sourceResult.css;
|
|
146
|
+
|
|
147
|
+
const config = patchOutputPaths(input.config as DesignEmbedConfig, cwd);
|
|
148
|
+
|
|
149
|
+
const target = config?.output?.target;
|
|
150
|
+
const targetObj =
|
|
151
|
+
!target || target === "html" ? htmlTarget : (target as TargetEmitter);
|
|
152
|
+
|
|
153
|
+
const mappingDiagnostics = validateComponentMappings(
|
|
154
|
+
config?.components ?? [],
|
|
155
|
+
);
|
|
156
|
+
diagnostics.push(...mappingDiagnostics);
|
|
157
|
+
|
|
158
|
+
if (diagnostics.some((d) => d.severity === "error")) {
|
|
159
|
+
return { html, files: [], diagnostics };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const ast = parseHtml(html);
|
|
163
|
+
const mappedNodes = applyComponentMappings(
|
|
164
|
+
ast,
|
|
165
|
+
config?.components ?? [],
|
|
166
|
+
diagnostics,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const { files } = targetObj.emit({
|
|
170
|
+
nodes: mappedNodes,
|
|
171
|
+
css,
|
|
172
|
+
config,
|
|
188
173
|
diagnostics,
|
|
189
174
|
});
|
|
190
175
|
|
|
191
|
-
|
|
192
|
-
|
|
176
|
+
if (input.generateTests && "generateTests" in targetObj) {
|
|
177
|
+
const testGen = targetObj as unknown as TargetTestGenerator;
|
|
178
|
+
const testResult = testGen.generateTests({ html, css, config });
|
|
179
|
+
diagnostics.push(...testResult.diagnostics);
|
|
180
|
+
if (!diagnostics.some((d) => d.severity === "error")) {
|
|
181
|
+
files.push(...testResult.files);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
193
184
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
): Promise<DesignNode[]> {
|
|
200
|
-
let nextAst = ast;
|
|
201
|
-
|
|
202
|
-
for (const transformer of sortTransformers(transformers)) {
|
|
203
|
-
try {
|
|
204
|
-
const result = await transformer.transform({
|
|
205
|
-
ast: nextAst,
|
|
206
|
-
config,
|
|
207
|
-
diagnostics,
|
|
208
|
-
});
|
|
209
|
-
if (result.diagnostics) {
|
|
210
|
-
diagnostics.push(...result.diagnostics);
|
|
211
|
-
}
|
|
212
|
-
if (result.ast) {
|
|
213
|
-
nextAst = result.ast;
|
|
214
|
-
}
|
|
215
|
-
} catch (error) {
|
|
216
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
217
|
-
diagnostics.push({
|
|
218
|
-
code: "TRANSFORMER_FAILED",
|
|
219
|
-
message: `Transformer ${transformer.name} failed: ${message}`,
|
|
220
|
-
severity: "error",
|
|
221
|
-
details: { transformer: transformer.name },
|
|
222
|
-
});
|
|
185
|
+
if (!input.dryRun) {
|
|
186
|
+
for (const file of files) {
|
|
187
|
+
const outPath = resolve(cwd, file.path);
|
|
188
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
189
|
+
writeFileSync(outPath, file.contents, "utf-8");
|
|
223
190
|
}
|
|
224
191
|
}
|
|
225
192
|
|
|
226
|
-
return
|
|
193
|
+
return { html, css, files, diagnostics };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function patchOutputPaths(
|
|
197
|
+
config: DesignEmbedConfig,
|
|
198
|
+
cwd: string,
|
|
199
|
+
): DesignEmbedConfig {
|
|
200
|
+
const viewsDir = config.output?.viewsDir;
|
|
201
|
+
if (!viewsDir) return config;
|
|
202
|
+
return {
|
|
203
|
+
...config,
|
|
204
|
+
output: { ...config.output, viewsDir: resolveDir(viewsDir, cwd) },
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function resolveDir(
|
|
209
|
+
dir: string | URL | undefined,
|
|
210
|
+
cwd: string,
|
|
211
|
+
): string | undefined {
|
|
212
|
+
if (!dir) return undefined;
|
|
213
|
+
if (dir instanceof URL) return relative(cwd, fileURLToPath(dir));
|
|
214
|
+
return dir;
|
|
227
215
|
}
|
|
228
216
|
|
|
229
217
|
export function applyComponentMappings(
|
|
@@ -255,9 +243,9 @@ export function applyComponentMappings(
|
|
|
255
243
|
const props = extractProps(node, match.mapping, diagnostics);
|
|
256
244
|
return {
|
|
257
245
|
kind: "component",
|
|
258
|
-
component: match.mapping.
|
|
259
|
-
importName: match.mapping.
|
|
260
|
-
importPath: match.mapping.component
|
|
246
|
+
component: match.mapping.component,
|
|
247
|
+
importName: match.mapping.component,
|
|
248
|
+
importPath: `./${match.mapping.component}.view`,
|
|
261
249
|
props,
|
|
262
250
|
children:
|
|
263
251
|
props.children?.kind === "children"
|
|
@@ -268,6 +256,7 @@ export function applyComponentMappings(
|
|
|
268
256
|
diagnostics,
|
|
269
257
|
),
|
|
270
258
|
source: node.source,
|
|
259
|
+
sourceElement: node,
|
|
271
260
|
};
|
|
272
261
|
}
|
|
273
262
|
|
|
@@ -537,7 +526,11 @@ function extractProps(
|
|
|
537
526
|
});
|
|
538
527
|
continue;
|
|
539
528
|
}
|
|
540
|
-
props[propName] = {
|
|
529
|
+
props[propName] = {
|
|
530
|
+
kind: "literal",
|
|
531
|
+
value,
|
|
532
|
+
attribute: attributeName,
|
|
533
|
+
};
|
|
541
534
|
continue;
|
|
542
535
|
}
|
|
543
536
|
|
|
@@ -557,11 +550,6 @@ function collectText(node: DesignNode): string {
|
|
|
557
550
|
.trim();
|
|
558
551
|
}
|
|
559
552
|
|
|
560
|
-
function inferImportName(mapping: ComponentMapping): string {
|
|
561
|
-
const lastSegment = mapping.component.split("/").filter(Boolean).at(-1);
|
|
562
|
-
return mapping.importName ?? lastSegment ?? "Component";
|
|
563
|
-
}
|
|
564
|
-
|
|
565
553
|
function currentParent(stack: DesignNode[]): DesignNode {
|
|
566
554
|
const parent = stack[stack.length - 1];
|
|
567
555
|
if (!parent) {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Location in the source HTML file.
|
|
3
|
+
*/
|
|
4
|
+
export interface SourceLocation {
|
|
5
|
+
/** Absolute offset in characters. */
|
|
6
|
+
offset: number;
|
|
7
|
+
/** 1-based line number. */
|
|
8
|
+
line: number;
|
|
9
|
+
/** 1-based column number. */
|
|
10
|
+
column: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A normalized node in the design AST.
|
|
15
|
+
*/
|
|
16
|
+
export interface DesignNode {
|
|
17
|
+
/** The type of node. */
|
|
18
|
+
kind: "element" | "text" | "component";
|
|
19
|
+
/** HTML tag name (for element kind). */
|
|
20
|
+
tagName?: string;
|
|
21
|
+
/** HTML attributes (for element kind). */
|
|
22
|
+
attributes?: Record<string, string>;
|
|
23
|
+
/** Parsed inline styles (for element kind). */
|
|
24
|
+
styles?: Record<string, string>;
|
|
25
|
+
/** Utility classes to apply. */
|
|
26
|
+
generatedClassNames?: string[];
|
|
27
|
+
/** Child nodes. */
|
|
28
|
+
children?: DesignNode[];
|
|
29
|
+
/** Inner text content (for text kind). */
|
|
30
|
+
text?: string;
|
|
31
|
+
/** Original location in the source HTML. */
|
|
32
|
+
source?: SourceLocation;
|
|
33
|
+
/** Component name (for component kind). */
|
|
34
|
+
component?: string;
|
|
35
|
+
/** Named export of the component. */
|
|
36
|
+
importName?: string;
|
|
37
|
+
/** Mapped prop values for the component. */
|
|
38
|
+
props?: Record<string, PropValue>;
|
|
39
|
+
/** Import path of the component. */
|
|
40
|
+
importPath?: string;
|
|
41
|
+
/**
|
|
42
|
+
* The original element node a component was mapped from. Retained so
|
|
43
|
+
* targets can reconstruct the element's structure when emitting the
|
|
44
|
+
* component implementation.
|
|
45
|
+
*/
|
|
46
|
+
sourceElement?: DesignNode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A value passed to a component prop.
|
|
51
|
+
*/
|
|
52
|
+
export type PropValue =
|
|
53
|
+
| {
|
|
54
|
+
kind: "literal";
|
|
55
|
+
value: string | number | boolean;
|
|
56
|
+
/** Source attribute name when the prop is bound to `$attr.*`. */
|
|
57
|
+
attribute?: string;
|
|
58
|
+
}
|
|
59
|
+
| { kind: "text"; value: string }
|
|
60
|
+
| { kind: "children"; value: DesignNode[] };
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A parsed CSS selector.
|
|
64
|
+
*/
|
|
65
|
+
export interface ParsedSelector {
|
|
66
|
+
/** Optional tag name. */
|
|
67
|
+
tagName?: string;
|
|
68
|
+
/** Optional ID selector. */
|
|
69
|
+
id?: string;
|
|
70
|
+
/** List of class names. */
|
|
71
|
+
classes: string[];
|
|
72
|
+
/** Attribute selectors. */
|
|
73
|
+
attributes: Record<string, string>;
|
|
74
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Diagnostic } from "../diagnostics/diagnostic.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a file generated by the compiler.
|
|
5
|
+
*/
|
|
6
|
+
export interface GeneratedFile {
|
|
7
|
+
/** Relative path from the output directory. */
|
|
8
|
+
path: string;
|
|
9
|
+
/** File content. */
|
|
10
|
+
contents: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface GeneratedAsset {
|
|
14
|
+
path: string;
|
|
15
|
+
contents?: string | Uint8Array;
|
|
16
|
+
sourceUrl?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SourcePlugin {
|
|
20
|
+
name: string;
|
|
21
|
+
run(input: SourcePluginInput): Promise<SourcePluginResult>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SourcePluginInput {
|
|
25
|
+
cwd: string;
|
|
26
|
+
config?: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SourcePluginResult {
|
|
30
|
+
html?: string;
|
|
31
|
+
css?: string;
|
|
32
|
+
assets?: GeneratedAsset[];
|
|
33
|
+
files?: GeneratedFile[];
|
|
34
|
+
diagnostics: Diagnostic[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TargetEmitResult {
|
|
38
|
+
files: GeneratedFile[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TargetTestGenerateResult {
|
|
42
|
+
files: GeneratedFile[];
|
|
43
|
+
diagnostics: Diagnostic[];
|
|
44
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Diagnostic } from "./diagnostics/diagnostic.ts";
|
|
2
|
+
import type { DesignNode } from "./nodes.ts";
|
|
3
|
+
import type {
|
|
4
|
+
SourcePlugin,
|
|
5
|
+
TargetEmitResult,
|
|
6
|
+
TargetTestGenerateResult,
|
|
7
|
+
} from "./plugins/pluginApi.ts";
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Target adapter interfaces
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
export interface TargetEmitInput {
|
|
14
|
+
nodes: DesignNode[];
|
|
15
|
+
css?: string;
|
|
16
|
+
config?: DesignEmbedConfig;
|
|
17
|
+
diagnostics: Diagnostic[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TargetEmitter {
|
|
21
|
+
emit(input: TargetEmitInput): TargetEmitResult;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TargetTestGenerateInput {
|
|
25
|
+
html: string;
|
|
26
|
+
css?: string;
|
|
27
|
+
config: DesignEmbedConfig;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TargetTestGenerator {
|
|
31
|
+
generateTests(input: TargetTestGenerateInput): TargetTestGenerateResult;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Configuration types
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
export type StyleMode = "inline" | "css-modules" | "tailwind";
|
|
39
|
+
|
|
40
|
+
export interface ComponentMapping {
|
|
41
|
+
selector: string;
|
|
42
|
+
component: string;
|
|
43
|
+
props?: Record<string, string>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface TokenConfig {
|
|
47
|
+
spacing?: {
|
|
48
|
+
unit?: "px" | "rem";
|
|
49
|
+
threshold?: number;
|
|
50
|
+
values?: Record<string, number>;
|
|
51
|
+
};
|
|
52
|
+
sizing?: NumericTokenGroup;
|
|
53
|
+
typography?: NumericTokenGroup;
|
|
54
|
+
radius?: Record<string, number>;
|
|
55
|
+
borderWidth?: Record<string, number>;
|
|
56
|
+
shadow?: Record<string, string>;
|
|
57
|
+
colors?: Record<string, string>;
|
|
58
|
+
colorThreshold?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface NumericTokenGroup {
|
|
62
|
+
unit?: "px" | "rem";
|
|
63
|
+
threshold?: number;
|
|
64
|
+
values?: Record<string, number>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type StyleMappings = Record<string, Record<string, string>>;
|
|
68
|
+
|
|
69
|
+
export interface TestGenerationConfig {
|
|
70
|
+
outputDir?: string;
|
|
71
|
+
runner?: "playwright";
|
|
72
|
+
viewports?: TestViewport[];
|
|
73
|
+
states?: TestState[];
|
|
74
|
+
assertions?: TestAssertions;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface TestViewport {
|
|
78
|
+
name?: string;
|
|
79
|
+
width: number;
|
|
80
|
+
height: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface TestState {
|
|
84
|
+
name: string;
|
|
85
|
+
hover?: string;
|
|
86
|
+
focus?: string;
|
|
87
|
+
click?: string;
|
|
88
|
+
waitFor?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface TestAssertions {
|
|
92
|
+
screenshot?: boolean;
|
|
93
|
+
layout?: boolean;
|
|
94
|
+
layoutTolerance?: number;
|
|
95
|
+
selectors?: string[];
|
|
96
|
+
/**
|
|
97
|
+
* Per-pixel color sensitivity (0-1) for the screenshot comparison. Smaller
|
|
98
|
+
* is stricter. Defaults to 0.2.
|
|
99
|
+
*/
|
|
100
|
+
screenshotThreshold?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Maximum number of differing pixels tolerated in the screenshot
|
|
103
|
+
* comparison. Defaults to 0 (byte-exact).
|
|
104
|
+
*/
|
|
105
|
+
screenshotMaxDiffPixels?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface DesignEmbedConfig {
|
|
109
|
+
output?: {
|
|
110
|
+
viewsDir?: string | URL;
|
|
111
|
+
target?: "html" | TargetEmitter;
|
|
112
|
+
viewName?: string;
|
|
113
|
+
styleMode?: StyleMode;
|
|
114
|
+
};
|
|
115
|
+
components?: ComponentMapping[];
|
|
116
|
+
tokens?: TokenConfig;
|
|
117
|
+
styleMappings?: StyleMappings;
|
|
118
|
+
source?: SourcePlugin;
|
|
119
|
+
tests?: TestGenerationConfig;
|
|
120
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Config
|
|
2
|
+
|
|
3
|
+
export type { LoadConfigResult } from "./config/index.ts";
|
|
4
|
+
export {
|
|
5
|
+
defineConfig,
|
|
6
|
+
fromFile,
|
|
7
|
+
loadConfig,
|
|
8
|
+
validateConfig,
|
|
9
|
+
} from "./config/index.ts";
|
|
10
|
+
// Config schema types
|
|
11
|
+
// Diagnostics
|
|
12
|
+
// Extension types — for authoring source plugins and custom targets
|
|
13
|
+
export type {
|
|
14
|
+
ComponentMapping,
|
|
15
|
+
DesignEmbedConfig,
|
|
16
|
+
DesignEmbedInput,
|
|
17
|
+
DesignEmbedResult,
|
|
18
|
+
DesignNode,
|
|
19
|
+
Diagnostic,
|
|
20
|
+
DiagnosticSeverity,
|
|
21
|
+
GeneratedAsset,
|
|
22
|
+
GeneratedFile,
|
|
23
|
+
NumericTokenGroup,
|
|
24
|
+
PropValue,
|
|
25
|
+
SourceLocation,
|
|
26
|
+
SourcePlugin,
|
|
27
|
+
SourcePluginInput,
|
|
28
|
+
SourcePluginResult,
|
|
29
|
+
StyleMappings,
|
|
30
|
+
StyleMode,
|
|
31
|
+
TargetEmitInput,
|
|
32
|
+
TargetEmitResult,
|
|
33
|
+
TargetEmitter,
|
|
34
|
+
TargetTestGenerateInput,
|
|
35
|
+
TargetTestGenerateResult,
|
|
36
|
+
TargetTestGenerator,
|
|
37
|
+
TestAssertions,
|
|
38
|
+
TestGenerationConfig,
|
|
39
|
+
TestState,
|
|
40
|
+
TestViewport,
|
|
41
|
+
TokenConfig,
|
|
42
|
+
} from "./core/index.ts";
|
|
43
|
+
// Embed
|
|
44
|
+
// AST utilities and types — for custom target authors
|
|
45
|
+
export { applyComponentMappings, embed, parseHtml } from "./core/index.ts";
|
|
46
|
+
export type { HtmlTargetOptions } from "./targets/html.ts";
|
|
47
|
+
// Built-in HTML target
|
|
48
|
+
export { HtmlTarget, htmlTarget } from "./targets/html.ts";
|