@matthesketh/utopia-compiler 0.4.0 → 0.7.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/dist/index.cjs +51 -21
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +51 -21
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -32,16 +32,19 @@ __export(index_exports, {
|
|
|
32
32
|
module.exports = __toCommonJS(index_exports);
|
|
33
33
|
|
|
34
34
|
// src/parser.ts
|
|
35
|
+
var BLOCK_RE = /<(template|script|style|test)([\s][^>]*)?\s*>/g;
|
|
36
|
+
var ATTR_RE = /([a-zA-Z_][\w-]*)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
|
|
35
37
|
function parse(source, filename = "anonymous.utopia") {
|
|
36
38
|
const descriptor = {
|
|
37
39
|
template: null,
|
|
38
40
|
script: null,
|
|
39
41
|
style: null,
|
|
42
|
+
test: null,
|
|
40
43
|
filename
|
|
41
44
|
};
|
|
42
|
-
|
|
45
|
+
BLOCK_RE.lastIndex = 0;
|
|
43
46
|
let match;
|
|
44
|
-
while ((match =
|
|
47
|
+
while ((match = BLOCK_RE.exec(source)) !== null) {
|
|
45
48
|
const tagName = match[1];
|
|
46
49
|
const attrString = match[2] || "";
|
|
47
50
|
const openTagStart = match.index;
|
|
@@ -72,11 +75,10 @@ function parse(source, filename = "anonymous.utopia") {
|
|
|
72
75
|
);
|
|
73
76
|
}
|
|
74
77
|
descriptor[tagName] = block;
|
|
75
|
-
|
|
78
|
+
BLOCK_RE.lastIndex = blockEnd;
|
|
76
79
|
}
|
|
77
80
|
return descriptor;
|
|
78
81
|
}
|
|
79
|
-
var ATTR_RE = /([a-zA-Z_][\w-]*)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
|
|
80
82
|
function parseAttributes(raw) {
|
|
81
83
|
const attrs = {};
|
|
82
84
|
let m;
|
|
@@ -113,6 +115,16 @@ var SFCParseError = class extends Error {
|
|
|
113
115
|
};
|
|
114
116
|
|
|
115
117
|
// src/template-compiler.ts
|
|
118
|
+
var TAG_NAME_CHAR_RE = /[a-zA-Z0-9\-_]/;
|
|
119
|
+
var ATTR_NAME_CHAR_RE = /[a-zA-Z0-9\-_:@.]/;
|
|
120
|
+
var ATTR_VALUE_END_RE = /[\s/>]/;
|
|
121
|
+
var WHITESPACE_RE = /\s/;
|
|
122
|
+
var TAG_START_CHAR_RE = /[a-zA-Z]/;
|
|
123
|
+
var U_FOR_EXPR_RE = /^\s*(?:\(\s*(\w+)\s*(?:,\s*(\w+)\s*)?\)|(\w+))\s+in\s+(.+)$/;
|
|
124
|
+
var COMPONENT_TAG_RE = /^[A-Z][a-zA-Z0-9_$]*$/;
|
|
125
|
+
var BACKSLASH_RE = /\\/g;
|
|
126
|
+
var SINGLE_QUOTE_RE = /'/g;
|
|
127
|
+
var HTML_ENTITY_RE = /&(?:#(\d+)|#x([0-9a-fA-F]+)|(\w+));/g;
|
|
116
128
|
function compileTemplate(template, options = {}) {
|
|
117
129
|
const ast = parseTemplate(template);
|
|
118
130
|
return generate(ast, options);
|
|
@@ -294,7 +306,7 @@ var TemplateParser = class {
|
|
|
294
306
|
// ---- Low-level helpers --------------------------------------------------
|
|
295
307
|
readTagName() {
|
|
296
308
|
const start = this.pos;
|
|
297
|
-
while (this.pos < this.source.length &&
|
|
309
|
+
while (this.pos < this.source.length && TAG_NAME_CHAR_RE.test(this.source[this.pos])) {
|
|
298
310
|
this.pos++;
|
|
299
311
|
}
|
|
300
312
|
const name = this.source.slice(start, this.pos);
|
|
@@ -303,7 +315,7 @@ var TemplateParser = class {
|
|
|
303
315
|
}
|
|
304
316
|
readAttributeName() {
|
|
305
317
|
const start = this.pos;
|
|
306
|
-
while (this.pos < this.source.length &&
|
|
318
|
+
while (this.pos < this.source.length && ATTR_NAME_CHAR_RE.test(this.source[this.pos])) {
|
|
307
319
|
this.pos++;
|
|
308
320
|
}
|
|
309
321
|
return this.source.slice(start, this.pos);
|
|
@@ -320,13 +332,13 @@ var TemplateParser = class {
|
|
|
320
332
|
return value;
|
|
321
333
|
}
|
|
322
334
|
const start = this.pos;
|
|
323
|
-
while (this.pos < this.source.length &&
|
|
335
|
+
while (this.pos < this.source.length && !ATTR_VALUE_END_RE.test(this.source[this.pos])) {
|
|
324
336
|
this.pos++;
|
|
325
337
|
}
|
|
326
338
|
return this.source.slice(start, this.pos);
|
|
327
339
|
}
|
|
328
340
|
skipWhitespace() {
|
|
329
|
-
while (this.pos < this.source.length &&
|
|
341
|
+
while (this.pos < this.source.length && WHITESPACE_RE.test(this.source[this.pos])) {
|
|
330
342
|
this.pos++;
|
|
331
343
|
}
|
|
332
344
|
}
|
|
@@ -336,7 +348,7 @@ var TemplateParser = class {
|
|
|
336
348
|
/** Returns true when the char after `<` looks like the start of a tag name. */
|
|
337
349
|
peekTagStart() {
|
|
338
350
|
const next = this.source[this.pos + 1];
|
|
339
|
-
return next !== void 0 &&
|
|
351
|
+
return next !== void 0 && TAG_START_CHAR_RE.test(next);
|
|
340
352
|
}
|
|
341
353
|
expect(str) {
|
|
342
354
|
if (!this.lookingAt(str)) {
|
|
@@ -406,7 +418,7 @@ function classifyDirective(name, value) {
|
|
|
406
418
|
return null;
|
|
407
419
|
}
|
|
408
420
|
function isDirectiveKind(s) {
|
|
409
|
-
return s === "on" || s === "bind" || s === "if" || s === "else" || s === "else-if" || s === "for" || s === "model";
|
|
421
|
+
return s === "on" || s === "bind" || s === "if" || s === "else" || s === "else-if" || s === "for" || s === "model" || s === "transition";
|
|
410
422
|
}
|
|
411
423
|
var CodeGenerator = class {
|
|
412
424
|
constructor(options) {
|
|
@@ -535,6 +547,9 @@ ${fnBody}
|
|
|
535
547
|
case "model":
|
|
536
548
|
this.genModel(elVar, dir, scope);
|
|
537
549
|
break;
|
|
550
|
+
case "transition":
|
|
551
|
+
this.genTransition(elVar, dir);
|
|
552
|
+
break;
|
|
538
553
|
}
|
|
539
554
|
}
|
|
540
555
|
genOn(elVar, dir, scope) {
|
|
@@ -583,6 +598,19 @@ ${fnBody}
|
|
|
583
598
|
this.emit(`createEffect(() => setAttr(${elVar}, 'value', ${signalRef}()))`);
|
|
584
599
|
this.emit(`addEventListener(${elVar}, 'input', (e) => ${signalRef}.set(e.target.value))`);
|
|
585
600
|
}
|
|
601
|
+
// ---- u-transition ---------------------------------------------------------
|
|
602
|
+
genTransition(elVar, dir) {
|
|
603
|
+
this.helpers.add("createTransition");
|
|
604
|
+
const name = dir.arg || dir.expression || "fade";
|
|
605
|
+
let durationOpt = "";
|
|
606
|
+
for (const mod of dir.modifiers) {
|
|
607
|
+
if (mod.startsWith("duration-")) {
|
|
608
|
+
const ms = mod.slice("duration-".length);
|
|
609
|
+
durationOpt = `, duration: ${ms}`;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
this.emit(`createTransition(${elVar}, { name: '${escapeStr(name)}'${durationOpt} })`);
|
|
613
|
+
}
|
|
586
614
|
// ---- Structural: u-if ---------------------------------------------------
|
|
587
615
|
genIf(node, dir, scope, elseIfChain, elseNode) {
|
|
588
616
|
this.helpers.add("createIf");
|
|
@@ -657,9 +685,7 @@ ${fnBody}
|
|
|
657
685
|
const anchorVar = this.freshVar();
|
|
658
686
|
this.helpers.add("createComment");
|
|
659
687
|
this.emit(`const ${anchorVar} = createComment('u-for')`);
|
|
660
|
-
const forMatch = dir.expression.match(
|
|
661
|
-
/^\s*(?:\(\s*(\w+)\s*(?:,\s*(\w+)\s*)?\)|(\w+))\s+in\s+(.+)$/
|
|
662
|
-
);
|
|
688
|
+
const forMatch = dir.expression.match(U_FOR_EXPR_RE);
|
|
663
689
|
if (!forMatch) {
|
|
664
690
|
throw new Error(`Invalid u-for expression: "${dir.expression}"`);
|
|
665
691
|
}
|
|
@@ -862,10 +888,10 @@ ${fnBody}
|
|
|
862
888
|
}
|
|
863
889
|
};
|
|
864
890
|
function isComponentTag(tag) {
|
|
865
|
-
return
|
|
891
|
+
return COMPONENT_TAG_RE.test(tag);
|
|
866
892
|
}
|
|
867
893
|
function escapeStr(s) {
|
|
868
|
-
return s.replace(
|
|
894
|
+
return s.replace(BACKSLASH_RE, "\\\\").replace(SINGLE_QUOTE_RE, "\\'");
|
|
869
895
|
}
|
|
870
896
|
var ENTITY_MAP = {
|
|
871
897
|
"&": "&",
|
|
@@ -894,7 +920,8 @@ var ENTITY_MAP = {
|
|
|
894
920
|
"÷": "\xF7"
|
|
895
921
|
};
|
|
896
922
|
function decodeEntities(text) {
|
|
897
|
-
|
|
923
|
+
HTML_ENTITY_RE.lastIndex = 0;
|
|
924
|
+
return text.replace(HTML_ENTITY_RE, (match, dec, hex, named) => {
|
|
898
925
|
if (dec) {
|
|
899
926
|
const code = parseInt(dec, 10);
|
|
900
927
|
if (code >= 0 && code <= 1114111) {
|
|
@@ -927,6 +954,9 @@ function generate(ast, options) {
|
|
|
927
954
|
}
|
|
928
955
|
|
|
929
956
|
// src/style-compiler.ts
|
|
957
|
+
var WHITESPACE_RE2 = /\s/;
|
|
958
|
+
var KEYFRAMES_RE = /^@(?:-\w+-)?keyframes\b/;
|
|
959
|
+
var PSEUDO_SELECTOR_RE = /(?:::?[\w-]+(?:\([^)]*\))?)+$/;
|
|
930
960
|
function compileStyle(options) {
|
|
931
961
|
const { source, filename, scoped, scopeId: overrideScopeId } = options;
|
|
932
962
|
if (!scoped) {
|
|
@@ -947,7 +977,7 @@ function scopeSelectors(css, scopeId) {
|
|
|
947
977
|
const result = [];
|
|
948
978
|
let pos = 0;
|
|
949
979
|
while (pos < css.length) {
|
|
950
|
-
if (
|
|
980
|
+
if (WHITESPACE_RE2.test(css[pos])) {
|
|
951
981
|
result.push(css[pos]);
|
|
952
982
|
pos++;
|
|
953
983
|
continue;
|
|
@@ -996,7 +1026,7 @@ function consumeAtRule(css, pos, scopeId) {
|
|
|
996
1026
|
return { text: css.slice(start), end: css.length };
|
|
997
1027
|
}
|
|
998
1028
|
const header = css.slice(start, headerEnd);
|
|
999
|
-
const isKeyframes =
|
|
1029
|
+
const isKeyframes = KEYFRAMES_RE.test(header.trim());
|
|
1000
1030
|
depth = 1;
|
|
1001
1031
|
let bodyStart = headerEnd + 1;
|
|
1002
1032
|
let bodyEnd = headerEnd + 1;
|
|
@@ -1039,8 +1069,7 @@ function consumeRuleSet(css, pos, scopeId) {
|
|
|
1039
1069
|
function scopeSingleSelector(selector, scopeId) {
|
|
1040
1070
|
if (!selector) return selector;
|
|
1041
1071
|
const attr = `[${scopeId}]`;
|
|
1042
|
-
const
|
|
1043
|
-
const pseudoMatch = selector.match(pseudoRe);
|
|
1072
|
+
const pseudoMatch = selector.match(PSEUDO_SELECTOR_RE);
|
|
1044
1073
|
if (pseudoMatch) {
|
|
1045
1074
|
const beforePseudo = selector.slice(0, pseudoMatch.index);
|
|
1046
1075
|
return `${beforePseudo}${attr}${pseudoMatch[0]}`;
|
|
@@ -1049,6 +1078,7 @@ function scopeSingleSelector(selector, scopeId) {
|
|
|
1049
1078
|
}
|
|
1050
1079
|
|
|
1051
1080
|
// src/a11y.ts
|
|
1081
|
+
var HEADING_LEVEL_RE = /^h([1-6])$/;
|
|
1052
1082
|
var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
|
|
1053
1083
|
"alert",
|
|
1054
1084
|
"alertdialog",
|
|
@@ -1218,7 +1248,7 @@ var rules = {
|
|
|
1218
1248
|
}
|
|
1219
1249
|
},
|
|
1220
1250
|
"heading-order"(node, warnings, ctx) {
|
|
1221
|
-
const match = node.tag.match(
|
|
1251
|
+
const match = node.tag.match(HEADING_LEVEL_RE);
|
|
1222
1252
|
if (!match) return;
|
|
1223
1253
|
const level = parseInt(match[1], 10);
|
|
1224
1254
|
if (ctx.lastHeadingLevel > 0 && level > ctx.lastHeadingLevel + 1) {
|
package/dist/index.d.cts
CHANGED
|
@@ -14,6 +14,7 @@ interface SFCDescriptor {
|
|
|
14
14
|
template: SFCBlock | null;
|
|
15
15
|
script: SFCBlock | null;
|
|
16
16
|
style: SFCBlock | null;
|
|
17
|
+
test: SFCBlock | null;
|
|
17
18
|
filename: string;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
@@ -84,7 +85,7 @@ interface Directive {
|
|
|
84
85
|
expression: string;
|
|
85
86
|
modifiers: string[];
|
|
86
87
|
}
|
|
87
|
-
type DirectiveKind = 'on' | 'bind' | 'if' | 'else' | 'else-if' | 'for' | 'model';
|
|
88
|
+
type DirectiveKind = 'on' | 'bind' | 'if' | 'else' | 'else-if' | 'for' | 'model' | 'transition';
|
|
88
89
|
/** Exported for testing — parse a template string into an AST. */
|
|
89
90
|
declare function parseTemplate(source: string): TemplateNode[];
|
|
90
91
|
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ interface SFCDescriptor {
|
|
|
14
14
|
template: SFCBlock | null;
|
|
15
15
|
script: SFCBlock | null;
|
|
16
16
|
style: SFCBlock | null;
|
|
17
|
+
test: SFCBlock | null;
|
|
17
18
|
filename: string;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
@@ -84,7 +85,7 @@ interface Directive {
|
|
|
84
85
|
expression: string;
|
|
85
86
|
modifiers: string[];
|
|
86
87
|
}
|
|
87
|
-
type DirectiveKind = 'on' | 'bind' | 'if' | 'else' | 'else-if' | 'for' | 'model';
|
|
88
|
+
type DirectiveKind = 'on' | 'bind' | 'if' | 'else' | 'else-if' | 'for' | 'model' | 'transition';
|
|
88
89
|
/** Exported for testing — parse a template string into an AST. */
|
|
89
90
|
declare function parseTemplate(source: string): TemplateNode[];
|
|
90
91
|
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// src/parser.ts
|
|
2
|
+
var BLOCK_RE = /<(template|script|style|test)([\s][^>]*)?\s*>/g;
|
|
3
|
+
var ATTR_RE = /([a-zA-Z_][\w-]*)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
|
|
2
4
|
function parse(source, filename = "anonymous.utopia") {
|
|
3
5
|
const descriptor = {
|
|
4
6
|
template: null,
|
|
5
7
|
script: null,
|
|
6
8
|
style: null,
|
|
9
|
+
test: null,
|
|
7
10
|
filename
|
|
8
11
|
};
|
|
9
|
-
|
|
12
|
+
BLOCK_RE.lastIndex = 0;
|
|
10
13
|
let match;
|
|
11
|
-
while ((match =
|
|
14
|
+
while ((match = BLOCK_RE.exec(source)) !== null) {
|
|
12
15
|
const tagName = match[1];
|
|
13
16
|
const attrString = match[2] || "";
|
|
14
17
|
const openTagStart = match.index;
|
|
@@ -39,11 +42,10 @@ function parse(source, filename = "anonymous.utopia") {
|
|
|
39
42
|
);
|
|
40
43
|
}
|
|
41
44
|
descriptor[tagName] = block;
|
|
42
|
-
|
|
45
|
+
BLOCK_RE.lastIndex = blockEnd;
|
|
43
46
|
}
|
|
44
47
|
return descriptor;
|
|
45
48
|
}
|
|
46
|
-
var ATTR_RE = /([a-zA-Z_][\w-]*)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
|
|
47
49
|
function parseAttributes(raw) {
|
|
48
50
|
const attrs = {};
|
|
49
51
|
let m;
|
|
@@ -80,6 +82,16 @@ var SFCParseError = class extends Error {
|
|
|
80
82
|
};
|
|
81
83
|
|
|
82
84
|
// src/template-compiler.ts
|
|
85
|
+
var TAG_NAME_CHAR_RE = /[a-zA-Z0-9\-_]/;
|
|
86
|
+
var ATTR_NAME_CHAR_RE = /[a-zA-Z0-9\-_:@.]/;
|
|
87
|
+
var ATTR_VALUE_END_RE = /[\s/>]/;
|
|
88
|
+
var WHITESPACE_RE = /\s/;
|
|
89
|
+
var TAG_START_CHAR_RE = /[a-zA-Z]/;
|
|
90
|
+
var U_FOR_EXPR_RE = /^\s*(?:\(\s*(\w+)\s*(?:,\s*(\w+)\s*)?\)|(\w+))\s+in\s+(.+)$/;
|
|
91
|
+
var COMPONENT_TAG_RE = /^[A-Z][a-zA-Z0-9_$]*$/;
|
|
92
|
+
var BACKSLASH_RE = /\\/g;
|
|
93
|
+
var SINGLE_QUOTE_RE = /'/g;
|
|
94
|
+
var HTML_ENTITY_RE = /&(?:#(\d+)|#x([0-9a-fA-F]+)|(\w+));/g;
|
|
83
95
|
function compileTemplate(template, options = {}) {
|
|
84
96
|
const ast = parseTemplate(template);
|
|
85
97
|
return generate(ast, options);
|
|
@@ -261,7 +273,7 @@ var TemplateParser = class {
|
|
|
261
273
|
// ---- Low-level helpers --------------------------------------------------
|
|
262
274
|
readTagName() {
|
|
263
275
|
const start = this.pos;
|
|
264
|
-
while (this.pos < this.source.length &&
|
|
276
|
+
while (this.pos < this.source.length && TAG_NAME_CHAR_RE.test(this.source[this.pos])) {
|
|
265
277
|
this.pos++;
|
|
266
278
|
}
|
|
267
279
|
const name = this.source.slice(start, this.pos);
|
|
@@ -270,7 +282,7 @@ var TemplateParser = class {
|
|
|
270
282
|
}
|
|
271
283
|
readAttributeName() {
|
|
272
284
|
const start = this.pos;
|
|
273
|
-
while (this.pos < this.source.length &&
|
|
285
|
+
while (this.pos < this.source.length && ATTR_NAME_CHAR_RE.test(this.source[this.pos])) {
|
|
274
286
|
this.pos++;
|
|
275
287
|
}
|
|
276
288
|
return this.source.slice(start, this.pos);
|
|
@@ -287,13 +299,13 @@ var TemplateParser = class {
|
|
|
287
299
|
return value;
|
|
288
300
|
}
|
|
289
301
|
const start = this.pos;
|
|
290
|
-
while (this.pos < this.source.length &&
|
|
302
|
+
while (this.pos < this.source.length && !ATTR_VALUE_END_RE.test(this.source[this.pos])) {
|
|
291
303
|
this.pos++;
|
|
292
304
|
}
|
|
293
305
|
return this.source.slice(start, this.pos);
|
|
294
306
|
}
|
|
295
307
|
skipWhitespace() {
|
|
296
|
-
while (this.pos < this.source.length &&
|
|
308
|
+
while (this.pos < this.source.length && WHITESPACE_RE.test(this.source[this.pos])) {
|
|
297
309
|
this.pos++;
|
|
298
310
|
}
|
|
299
311
|
}
|
|
@@ -303,7 +315,7 @@ var TemplateParser = class {
|
|
|
303
315
|
/** Returns true when the char after `<` looks like the start of a tag name. */
|
|
304
316
|
peekTagStart() {
|
|
305
317
|
const next = this.source[this.pos + 1];
|
|
306
|
-
return next !== void 0 &&
|
|
318
|
+
return next !== void 0 && TAG_START_CHAR_RE.test(next);
|
|
307
319
|
}
|
|
308
320
|
expect(str) {
|
|
309
321
|
if (!this.lookingAt(str)) {
|
|
@@ -373,7 +385,7 @@ function classifyDirective(name, value) {
|
|
|
373
385
|
return null;
|
|
374
386
|
}
|
|
375
387
|
function isDirectiveKind(s) {
|
|
376
|
-
return s === "on" || s === "bind" || s === "if" || s === "else" || s === "else-if" || s === "for" || s === "model";
|
|
388
|
+
return s === "on" || s === "bind" || s === "if" || s === "else" || s === "else-if" || s === "for" || s === "model" || s === "transition";
|
|
377
389
|
}
|
|
378
390
|
var CodeGenerator = class {
|
|
379
391
|
constructor(options) {
|
|
@@ -502,6 +514,9 @@ ${fnBody}
|
|
|
502
514
|
case "model":
|
|
503
515
|
this.genModel(elVar, dir, scope);
|
|
504
516
|
break;
|
|
517
|
+
case "transition":
|
|
518
|
+
this.genTransition(elVar, dir);
|
|
519
|
+
break;
|
|
505
520
|
}
|
|
506
521
|
}
|
|
507
522
|
genOn(elVar, dir, scope) {
|
|
@@ -550,6 +565,19 @@ ${fnBody}
|
|
|
550
565
|
this.emit(`createEffect(() => setAttr(${elVar}, 'value', ${signalRef}()))`);
|
|
551
566
|
this.emit(`addEventListener(${elVar}, 'input', (e) => ${signalRef}.set(e.target.value))`);
|
|
552
567
|
}
|
|
568
|
+
// ---- u-transition ---------------------------------------------------------
|
|
569
|
+
genTransition(elVar, dir) {
|
|
570
|
+
this.helpers.add("createTransition");
|
|
571
|
+
const name = dir.arg || dir.expression || "fade";
|
|
572
|
+
let durationOpt = "";
|
|
573
|
+
for (const mod of dir.modifiers) {
|
|
574
|
+
if (mod.startsWith("duration-")) {
|
|
575
|
+
const ms = mod.slice("duration-".length);
|
|
576
|
+
durationOpt = `, duration: ${ms}`;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
this.emit(`createTransition(${elVar}, { name: '${escapeStr(name)}'${durationOpt} })`);
|
|
580
|
+
}
|
|
553
581
|
// ---- Structural: u-if ---------------------------------------------------
|
|
554
582
|
genIf(node, dir, scope, elseIfChain, elseNode) {
|
|
555
583
|
this.helpers.add("createIf");
|
|
@@ -624,9 +652,7 @@ ${fnBody}
|
|
|
624
652
|
const anchorVar = this.freshVar();
|
|
625
653
|
this.helpers.add("createComment");
|
|
626
654
|
this.emit(`const ${anchorVar} = createComment('u-for')`);
|
|
627
|
-
const forMatch = dir.expression.match(
|
|
628
|
-
/^\s*(?:\(\s*(\w+)\s*(?:,\s*(\w+)\s*)?\)|(\w+))\s+in\s+(.+)$/
|
|
629
|
-
);
|
|
655
|
+
const forMatch = dir.expression.match(U_FOR_EXPR_RE);
|
|
630
656
|
if (!forMatch) {
|
|
631
657
|
throw new Error(`Invalid u-for expression: "${dir.expression}"`);
|
|
632
658
|
}
|
|
@@ -829,10 +855,10 @@ ${fnBody}
|
|
|
829
855
|
}
|
|
830
856
|
};
|
|
831
857
|
function isComponentTag(tag) {
|
|
832
|
-
return
|
|
858
|
+
return COMPONENT_TAG_RE.test(tag);
|
|
833
859
|
}
|
|
834
860
|
function escapeStr(s) {
|
|
835
|
-
return s.replace(
|
|
861
|
+
return s.replace(BACKSLASH_RE, "\\\\").replace(SINGLE_QUOTE_RE, "\\'");
|
|
836
862
|
}
|
|
837
863
|
var ENTITY_MAP = {
|
|
838
864
|
"&": "&",
|
|
@@ -861,7 +887,8 @@ var ENTITY_MAP = {
|
|
|
861
887
|
"÷": "\xF7"
|
|
862
888
|
};
|
|
863
889
|
function decodeEntities(text) {
|
|
864
|
-
|
|
890
|
+
HTML_ENTITY_RE.lastIndex = 0;
|
|
891
|
+
return text.replace(HTML_ENTITY_RE, (match, dec, hex, named) => {
|
|
865
892
|
if (dec) {
|
|
866
893
|
const code = parseInt(dec, 10);
|
|
867
894
|
if (code >= 0 && code <= 1114111) {
|
|
@@ -894,6 +921,9 @@ function generate(ast, options) {
|
|
|
894
921
|
}
|
|
895
922
|
|
|
896
923
|
// src/style-compiler.ts
|
|
924
|
+
var WHITESPACE_RE2 = /\s/;
|
|
925
|
+
var KEYFRAMES_RE = /^@(?:-\w+-)?keyframes\b/;
|
|
926
|
+
var PSEUDO_SELECTOR_RE = /(?:::?[\w-]+(?:\([^)]*\))?)+$/;
|
|
897
927
|
function compileStyle(options) {
|
|
898
928
|
const { source, filename, scoped, scopeId: overrideScopeId } = options;
|
|
899
929
|
if (!scoped) {
|
|
@@ -914,7 +944,7 @@ function scopeSelectors(css, scopeId) {
|
|
|
914
944
|
const result = [];
|
|
915
945
|
let pos = 0;
|
|
916
946
|
while (pos < css.length) {
|
|
917
|
-
if (
|
|
947
|
+
if (WHITESPACE_RE2.test(css[pos])) {
|
|
918
948
|
result.push(css[pos]);
|
|
919
949
|
pos++;
|
|
920
950
|
continue;
|
|
@@ -963,7 +993,7 @@ function consumeAtRule(css, pos, scopeId) {
|
|
|
963
993
|
return { text: css.slice(start), end: css.length };
|
|
964
994
|
}
|
|
965
995
|
const header = css.slice(start, headerEnd);
|
|
966
|
-
const isKeyframes =
|
|
996
|
+
const isKeyframes = KEYFRAMES_RE.test(header.trim());
|
|
967
997
|
depth = 1;
|
|
968
998
|
let bodyStart = headerEnd + 1;
|
|
969
999
|
let bodyEnd = headerEnd + 1;
|
|
@@ -1006,8 +1036,7 @@ function consumeRuleSet(css, pos, scopeId) {
|
|
|
1006
1036
|
function scopeSingleSelector(selector, scopeId) {
|
|
1007
1037
|
if (!selector) return selector;
|
|
1008
1038
|
const attr = `[${scopeId}]`;
|
|
1009
|
-
const
|
|
1010
|
-
const pseudoMatch = selector.match(pseudoRe);
|
|
1039
|
+
const pseudoMatch = selector.match(PSEUDO_SELECTOR_RE);
|
|
1011
1040
|
if (pseudoMatch) {
|
|
1012
1041
|
const beforePseudo = selector.slice(0, pseudoMatch.index);
|
|
1013
1042
|
return `${beforePseudo}${attr}${pseudoMatch[0]}`;
|
|
@@ -1016,6 +1045,7 @@ function scopeSingleSelector(selector, scopeId) {
|
|
|
1016
1045
|
}
|
|
1017
1046
|
|
|
1018
1047
|
// src/a11y.ts
|
|
1048
|
+
var HEADING_LEVEL_RE = /^h([1-6])$/;
|
|
1019
1049
|
var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
|
|
1020
1050
|
"alert",
|
|
1021
1051
|
"alertdialog",
|
|
@@ -1185,7 +1215,7 @@ var rules = {
|
|
|
1185
1215
|
}
|
|
1186
1216
|
},
|
|
1187
1217
|
"heading-order"(node, warnings, ctx) {
|
|
1188
|
-
const match = node.tag.match(
|
|
1218
|
+
const match = node.tag.match(HEADING_LEVEL_RE);
|
|
1189
1219
|
if (!match) return;
|
|
1190
1220
|
const level = parseInt(match[1], 10);
|
|
1191
1221
|
if (ctx.lastHeadingLevel > 0 && level > ctx.lastHeadingLevel + 1) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matthesketh/utopia-compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Compiler for .utopia single-file components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@matthesketh/utopia-core": "0.
|
|
42
|
+
"@matthesketh/utopia-core": "0.7.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|