@matthesketh/utopia-compiler 0.4.0 → 0.5.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 +34 -20
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +34 -20
- 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)) {
|
|
@@ -657,9 +669,7 @@ ${fnBody}
|
|
|
657
669
|
const anchorVar = this.freshVar();
|
|
658
670
|
this.helpers.add("createComment");
|
|
659
671
|
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
|
-
);
|
|
672
|
+
const forMatch = dir.expression.match(U_FOR_EXPR_RE);
|
|
663
673
|
if (!forMatch) {
|
|
664
674
|
throw new Error(`Invalid u-for expression: "${dir.expression}"`);
|
|
665
675
|
}
|
|
@@ -862,10 +872,10 @@ ${fnBody}
|
|
|
862
872
|
}
|
|
863
873
|
};
|
|
864
874
|
function isComponentTag(tag) {
|
|
865
|
-
return
|
|
875
|
+
return COMPONENT_TAG_RE.test(tag);
|
|
866
876
|
}
|
|
867
877
|
function escapeStr(s) {
|
|
868
|
-
return s.replace(
|
|
878
|
+
return s.replace(BACKSLASH_RE, "\\\\").replace(SINGLE_QUOTE_RE, "\\'");
|
|
869
879
|
}
|
|
870
880
|
var ENTITY_MAP = {
|
|
871
881
|
"&": "&",
|
|
@@ -894,7 +904,8 @@ var ENTITY_MAP = {
|
|
|
894
904
|
"÷": "\xF7"
|
|
895
905
|
};
|
|
896
906
|
function decodeEntities(text) {
|
|
897
|
-
|
|
907
|
+
HTML_ENTITY_RE.lastIndex = 0;
|
|
908
|
+
return text.replace(HTML_ENTITY_RE, (match, dec, hex, named) => {
|
|
898
909
|
if (dec) {
|
|
899
910
|
const code = parseInt(dec, 10);
|
|
900
911
|
if (code >= 0 && code <= 1114111) {
|
|
@@ -927,6 +938,9 @@ function generate(ast, options) {
|
|
|
927
938
|
}
|
|
928
939
|
|
|
929
940
|
// src/style-compiler.ts
|
|
941
|
+
var WHITESPACE_RE2 = /\s/;
|
|
942
|
+
var KEYFRAMES_RE = /^@(?:-\w+-)?keyframes\b/;
|
|
943
|
+
var PSEUDO_SELECTOR_RE = /(?:::?[\w-]+(?:\([^)]*\))?)+$/;
|
|
930
944
|
function compileStyle(options) {
|
|
931
945
|
const { source, filename, scoped, scopeId: overrideScopeId } = options;
|
|
932
946
|
if (!scoped) {
|
|
@@ -947,7 +961,7 @@ function scopeSelectors(css, scopeId) {
|
|
|
947
961
|
const result = [];
|
|
948
962
|
let pos = 0;
|
|
949
963
|
while (pos < css.length) {
|
|
950
|
-
if (
|
|
964
|
+
if (WHITESPACE_RE2.test(css[pos])) {
|
|
951
965
|
result.push(css[pos]);
|
|
952
966
|
pos++;
|
|
953
967
|
continue;
|
|
@@ -996,7 +1010,7 @@ function consumeAtRule(css, pos, scopeId) {
|
|
|
996
1010
|
return { text: css.slice(start), end: css.length };
|
|
997
1011
|
}
|
|
998
1012
|
const header = css.slice(start, headerEnd);
|
|
999
|
-
const isKeyframes =
|
|
1013
|
+
const isKeyframes = KEYFRAMES_RE.test(header.trim());
|
|
1000
1014
|
depth = 1;
|
|
1001
1015
|
let bodyStart = headerEnd + 1;
|
|
1002
1016
|
let bodyEnd = headerEnd + 1;
|
|
@@ -1039,8 +1053,7 @@ function consumeRuleSet(css, pos, scopeId) {
|
|
|
1039
1053
|
function scopeSingleSelector(selector, scopeId) {
|
|
1040
1054
|
if (!selector) return selector;
|
|
1041
1055
|
const attr = `[${scopeId}]`;
|
|
1042
|
-
const
|
|
1043
|
-
const pseudoMatch = selector.match(pseudoRe);
|
|
1056
|
+
const pseudoMatch = selector.match(PSEUDO_SELECTOR_RE);
|
|
1044
1057
|
if (pseudoMatch) {
|
|
1045
1058
|
const beforePseudo = selector.slice(0, pseudoMatch.index);
|
|
1046
1059
|
return `${beforePseudo}${attr}${pseudoMatch[0]}`;
|
|
@@ -1049,6 +1062,7 @@ function scopeSingleSelector(selector, scopeId) {
|
|
|
1049
1062
|
}
|
|
1050
1063
|
|
|
1051
1064
|
// src/a11y.ts
|
|
1065
|
+
var HEADING_LEVEL_RE = /^h([1-6])$/;
|
|
1052
1066
|
var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
|
|
1053
1067
|
"alert",
|
|
1054
1068
|
"alertdialog",
|
|
@@ -1218,7 +1232,7 @@ var rules = {
|
|
|
1218
1232
|
}
|
|
1219
1233
|
},
|
|
1220
1234
|
"heading-order"(node, warnings, ctx) {
|
|
1221
|
-
const match = node.tag.match(
|
|
1235
|
+
const match = node.tag.match(HEADING_LEVEL_RE);
|
|
1222
1236
|
if (!match) return;
|
|
1223
1237
|
const level = parseInt(match[1], 10);
|
|
1224
1238
|
if (ctx.lastHeadingLevel > 0 && level > ctx.lastHeadingLevel + 1) {
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
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)) {
|
|
@@ -624,9 +636,7 @@ ${fnBody}
|
|
|
624
636
|
const anchorVar = this.freshVar();
|
|
625
637
|
this.helpers.add("createComment");
|
|
626
638
|
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
|
-
);
|
|
639
|
+
const forMatch = dir.expression.match(U_FOR_EXPR_RE);
|
|
630
640
|
if (!forMatch) {
|
|
631
641
|
throw new Error(`Invalid u-for expression: "${dir.expression}"`);
|
|
632
642
|
}
|
|
@@ -829,10 +839,10 @@ ${fnBody}
|
|
|
829
839
|
}
|
|
830
840
|
};
|
|
831
841
|
function isComponentTag(tag) {
|
|
832
|
-
return
|
|
842
|
+
return COMPONENT_TAG_RE.test(tag);
|
|
833
843
|
}
|
|
834
844
|
function escapeStr(s) {
|
|
835
|
-
return s.replace(
|
|
845
|
+
return s.replace(BACKSLASH_RE, "\\\\").replace(SINGLE_QUOTE_RE, "\\'");
|
|
836
846
|
}
|
|
837
847
|
var ENTITY_MAP = {
|
|
838
848
|
"&": "&",
|
|
@@ -861,7 +871,8 @@ var ENTITY_MAP = {
|
|
|
861
871
|
"÷": "\xF7"
|
|
862
872
|
};
|
|
863
873
|
function decodeEntities(text) {
|
|
864
|
-
|
|
874
|
+
HTML_ENTITY_RE.lastIndex = 0;
|
|
875
|
+
return text.replace(HTML_ENTITY_RE, (match, dec, hex, named) => {
|
|
865
876
|
if (dec) {
|
|
866
877
|
const code = parseInt(dec, 10);
|
|
867
878
|
if (code >= 0 && code <= 1114111) {
|
|
@@ -894,6 +905,9 @@ function generate(ast, options) {
|
|
|
894
905
|
}
|
|
895
906
|
|
|
896
907
|
// src/style-compiler.ts
|
|
908
|
+
var WHITESPACE_RE2 = /\s/;
|
|
909
|
+
var KEYFRAMES_RE = /^@(?:-\w+-)?keyframes\b/;
|
|
910
|
+
var PSEUDO_SELECTOR_RE = /(?:::?[\w-]+(?:\([^)]*\))?)+$/;
|
|
897
911
|
function compileStyle(options) {
|
|
898
912
|
const { source, filename, scoped, scopeId: overrideScopeId } = options;
|
|
899
913
|
if (!scoped) {
|
|
@@ -914,7 +928,7 @@ function scopeSelectors(css, scopeId) {
|
|
|
914
928
|
const result = [];
|
|
915
929
|
let pos = 0;
|
|
916
930
|
while (pos < css.length) {
|
|
917
|
-
if (
|
|
931
|
+
if (WHITESPACE_RE2.test(css[pos])) {
|
|
918
932
|
result.push(css[pos]);
|
|
919
933
|
pos++;
|
|
920
934
|
continue;
|
|
@@ -963,7 +977,7 @@ function consumeAtRule(css, pos, scopeId) {
|
|
|
963
977
|
return { text: css.slice(start), end: css.length };
|
|
964
978
|
}
|
|
965
979
|
const header = css.slice(start, headerEnd);
|
|
966
|
-
const isKeyframes =
|
|
980
|
+
const isKeyframes = KEYFRAMES_RE.test(header.trim());
|
|
967
981
|
depth = 1;
|
|
968
982
|
let bodyStart = headerEnd + 1;
|
|
969
983
|
let bodyEnd = headerEnd + 1;
|
|
@@ -1006,8 +1020,7 @@ function consumeRuleSet(css, pos, scopeId) {
|
|
|
1006
1020
|
function scopeSingleSelector(selector, scopeId) {
|
|
1007
1021
|
if (!selector) return selector;
|
|
1008
1022
|
const attr = `[${scopeId}]`;
|
|
1009
|
-
const
|
|
1010
|
-
const pseudoMatch = selector.match(pseudoRe);
|
|
1023
|
+
const pseudoMatch = selector.match(PSEUDO_SELECTOR_RE);
|
|
1011
1024
|
if (pseudoMatch) {
|
|
1012
1025
|
const beforePseudo = selector.slice(0, pseudoMatch.index);
|
|
1013
1026
|
return `${beforePseudo}${attr}${pseudoMatch[0]}`;
|
|
@@ -1016,6 +1029,7 @@ function scopeSingleSelector(selector, scopeId) {
|
|
|
1016
1029
|
}
|
|
1017
1030
|
|
|
1018
1031
|
// src/a11y.ts
|
|
1032
|
+
var HEADING_LEVEL_RE = /^h([1-6])$/;
|
|
1019
1033
|
var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
|
|
1020
1034
|
"alert",
|
|
1021
1035
|
"alertdialog",
|
|
@@ -1185,7 +1199,7 @@ var rules = {
|
|
|
1185
1199
|
}
|
|
1186
1200
|
},
|
|
1187
1201
|
"heading-order"(node, warnings, ctx) {
|
|
1188
|
-
const match = node.tag.match(
|
|
1202
|
+
const match = node.tag.match(HEADING_LEVEL_RE);
|
|
1189
1203
|
if (!match) return;
|
|
1190
1204
|
const level = parseInt(match[1], 10);
|
|
1191
1205
|
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.5.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.5.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|