@canvasengine/compiler 2.0.0-beta.41 → 2.0.0-beta.43
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/grammar.pegjs +217 -14
- package/dist/index.js +123 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/grammar.pegjs
CHANGED
|
@@ -291,14 +291,14 @@ simpleDynamicPart "simple dynamic part"
|
|
|
291
291
|
}
|
|
292
292
|
/ "{" _ expr:attributeValue _ "}" {
|
|
293
293
|
// Handle single brace expressions like {item.name} or {@text}
|
|
294
|
-
if (expr.trim().match(
|
|
294
|
+
if (expr.trim().match(/^(@?[a-zA-Z_][a-zA-Z0-9_]*)(\.@?[a-zA-Z_][a-zA-Z0-9_]*)*$/)) {
|
|
295
295
|
let foundSignal = false;
|
|
296
|
-
const computedValue = expr.replace(/@?[a-zA-Z_][a-zA-Z0-9_]*(
|
|
296
|
+
const computedValue = expr.replace(/@?([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*:)/g, (match, p1) => {
|
|
297
297
|
if (match.startsWith('@')) {
|
|
298
|
-
return
|
|
298
|
+
return p1;
|
|
299
299
|
}
|
|
300
300
|
foundSignal = true;
|
|
301
|
-
return `${
|
|
301
|
+
return `${p1}()`;
|
|
302
302
|
});
|
|
303
303
|
if (foundSignal) {
|
|
304
304
|
return `computed(() => ${computedValue})`;
|
|
@@ -743,24 +743,97 @@ dotFunctionChain
|
|
|
743
743
|
|
|
744
744
|
condition "condition expression"
|
|
745
745
|
= functionCall
|
|
746
|
+
/ functionCallWithArgs
|
|
746
747
|
/ text_condition:$([^)]*) {
|
|
747
748
|
const originalText = text_condition.trim();
|
|
748
749
|
|
|
750
|
+
// Handle expressions with @ literals (like @item.@id)
|
|
751
|
+
// First, process dot notation expressions with @ literals
|
|
752
|
+
let processedText = originalText;
|
|
753
|
+
const dotNotationReplacements = new Map();
|
|
754
|
+
let replacementCounter = 0;
|
|
755
|
+
|
|
756
|
+
// Process dot notation expressions like @item.@id, @item.id, item.@id
|
|
757
|
+
// Only process expressions that contain at least one @
|
|
758
|
+
processedText = processedText.replace(/(@[a-zA-Z_][a-zA-Z0-9_]*)(\.@?[a-zA-Z_][a-zA-Z0-9_]*)+|([a-zA-Z_][a-zA-Z0-9_]*)(\.@[a-zA-Z_][a-zA-Z0-9_]*)+/g, (match) => {
|
|
759
|
+
// Split by dots to handle each part separately
|
|
760
|
+
const parts = match.split('.');
|
|
761
|
+
const allLiterals = parts.every(part => part.trim().startsWith('@'));
|
|
762
|
+
|
|
763
|
+
let replacement;
|
|
764
|
+
if (allLiterals) {
|
|
765
|
+
// All parts are literals, just remove @ prefixes (no signal transformation)
|
|
766
|
+
replacement = parts.map(part => part.trim().replace('@', '')).join('.');
|
|
767
|
+
} else {
|
|
768
|
+
// Transform each part individually
|
|
769
|
+
// Note: In conditions with operators, even @ literals in the first part
|
|
770
|
+
// should be transformed to signals for comparison
|
|
771
|
+
replacement = parts.map((part, index) => {
|
|
772
|
+
const trimmedPart = part.trim();
|
|
773
|
+
if (trimmedPart.startsWith('@')) {
|
|
774
|
+
// For the first part in conditions with operators, we still want to transform to signal
|
|
775
|
+
// For later parts, keep as literal
|
|
776
|
+
if (index === 0) {
|
|
777
|
+
// First part: remove @ but will be transformed to signal later
|
|
778
|
+
return trimmedPart.substring(1);
|
|
779
|
+
} else {
|
|
780
|
+
// Later parts: remove @ and keep as literal (no signal)
|
|
781
|
+
return trimmedPart.substring(1);
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
// Don't transform keywords
|
|
785
|
+
if (['true', 'false', 'null'].includes(trimmedPart)) {
|
|
786
|
+
return trimmedPart;
|
|
787
|
+
}
|
|
788
|
+
// Check if already a function call
|
|
789
|
+
if (trimmedPart.includes('(')) {
|
|
790
|
+
return trimmedPart;
|
|
791
|
+
}
|
|
792
|
+
// Transform to signal
|
|
793
|
+
return `${trimmedPart}()`;
|
|
794
|
+
}
|
|
795
|
+
}).join('.');
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Store replacement and use a temporary marker
|
|
799
|
+
const marker = `__DOT_NOTATION_${replacementCounter++}__`;
|
|
800
|
+
dotNotationReplacements.set(marker, replacement);
|
|
801
|
+
return marker;
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
// Now handle standalone @ identifiers (not in dot notation)
|
|
805
|
+
processedText = processedText.replace(/@([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*\.)/g, (match, p1) => {
|
|
806
|
+
return p1; // Remove @ prefix for standalone literals
|
|
807
|
+
});
|
|
808
|
+
|
|
749
809
|
// Transform simple identifiers to function calls like "foo" to "foo()"
|
|
750
810
|
// This regex matches identifiers not followed by an opening parenthesis.
|
|
751
811
|
// This transformation should only apply if we are wrapping in 'computed'.
|
|
752
|
-
if (
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
812
|
+
if (processedText.includes('!') || processedText.includes('&&') || processedText.includes('||') ||
|
|
813
|
+
processedText.includes('>=') || processedText.includes('<=') || processedText.includes('===') ||
|
|
814
|
+
processedText.includes('!==') || processedText.includes('==') || processedText.includes('!=') ||
|
|
815
|
+
processedText.includes('>') || processedText.includes('<')) {
|
|
816
|
+
// Replace dot notation markers with their processed values BEFORE transforming identifiers
|
|
817
|
+
// This way, expressions like @item.id become item.id() and then item() is transformed
|
|
818
|
+
let textWithReplacements = processedText;
|
|
819
|
+
dotNotationReplacements.forEach((value, marker) => {
|
|
820
|
+
textWithReplacements = textWithReplacements.replace(new RegExp(marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), value);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
const transformedText = textWithReplacements.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*\()/g, (match, p1, offset) => {
|
|
757
824
|
// Do not transform keywords (true, false, null) or numeric literals
|
|
758
825
|
if (['true', 'false', 'null'].includes(match) || /^\d+(\.\d+)?$/.test(match)) {
|
|
759
826
|
return match;
|
|
760
827
|
}
|
|
828
|
+
|
|
829
|
+
// Check if this is a marker (starts with __DOT_NOTATION_)
|
|
830
|
+
if (match.startsWith('__DOT_NOTATION_')) {
|
|
831
|
+
return match; // Don't transform markers
|
|
832
|
+
}
|
|
833
|
+
|
|
761
834
|
// Check if the match is inside quotes
|
|
762
|
-
const beforeMatch =
|
|
763
|
-
const afterMatch =
|
|
835
|
+
const beforeMatch = processedText.substring(0, offset);
|
|
836
|
+
const afterMatch = processedText.substring(offset + match.length);
|
|
764
837
|
const singleQuotesBefore = (beforeMatch.match(/'/g) || []).length;
|
|
765
838
|
const doubleQuotesBefore = (beforeMatch.match(/"/g) || []).length;
|
|
766
839
|
|
|
@@ -769,13 +842,64 @@ condition "condition expression"
|
|
|
769
842
|
return match;
|
|
770
843
|
}
|
|
771
844
|
|
|
845
|
+
// Check if this identifier is part of a dot notation expression
|
|
846
|
+
const charBefore = offset > 0 ? textWithReplacements[offset - 1] : '';
|
|
847
|
+
const charAfter = offset + match.length < textWithReplacements.length ? textWithReplacements[offset + match.length] : '';
|
|
848
|
+
|
|
849
|
+
// If there's a dot before or after, this is part of dot notation
|
|
850
|
+
if (charBefore === '.' || charAfter === '.') {
|
|
851
|
+
// Check if this dot notation expression was a marker (had @ in original)
|
|
852
|
+
const beforeContext = originalText.substring(Math.max(0, offset - 20), offset);
|
|
853
|
+
const afterContext = originalText.substring(offset, Math.min(originalText.length, offset + match.length + 20));
|
|
854
|
+
const fullContext = beforeContext + afterContext;
|
|
855
|
+
|
|
856
|
+
// Check if this identifier had @ in the original
|
|
857
|
+
const hadAt = fullContext.includes('@' + match) || fullContext.match(new RegExp('@' + match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\b'));
|
|
858
|
+
|
|
859
|
+
if (hadAt) {
|
|
860
|
+
// Check if ALL parts of the dot notation had @ (all literals)
|
|
861
|
+
// Find the full dot notation expression in the original
|
|
862
|
+
const dotExprMatch = originalText.match(/(@?[a-zA-Z_][a-zA-Z0-9_]*)(\.@?[a-zA-Z_][a-zA-Z0-9_]*)+/);
|
|
863
|
+
if (dotExprMatch) {
|
|
864
|
+
const dotExpr = dotExprMatch[0];
|
|
865
|
+
const allPartsHadAt = dotExpr.split('.').every(part => part.trim().startsWith('@'));
|
|
866
|
+
if (allPartsHadAt) {
|
|
867
|
+
// All parts were literals (@item.@id), don't transform
|
|
868
|
+
return match;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// Only some parts had @ (@item.id or item.@id)
|
|
873
|
+
if (charAfter === '.') {
|
|
874
|
+
// First part: transform to signal even if it had @
|
|
875
|
+
return `${match}()`;
|
|
876
|
+
} else if (charBefore === '.') {
|
|
877
|
+
// Later part: already processed in marker, don't retransform
|
|
878
|
+
return match;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// In conditions with operators, transform dot notation expressions to signals
|
|
883
|
+
// (e.g., user.role becomes user().role())
|
|
884
|
+
// This applies to regular dot notation, not markers (which are already processed)
|
|
885
|
+
return `${match}()`;
|
|
886
|
+
}
|
|
887
|
+
|
|
772
888
|
return `${match}()`;
|
|
773
889
|
});
|
|
890
|
+
|
|
774
891
|
return `computed(() => ${transformedText})`;
|
|
775
892
|
}
|
|
776
|
-
// For simple conditions (no !, &&, ||), return the
|
|
777
|
-
// Cases like `myFunction()` are handled by the `
|
|
778
|
-
|
|
893
|
+
// For simple conditions (no !, &&, ||), return the processed text as is.
|
|
894
|
+
// Cases like `myFunction()` are handled by the `functionCallWithArgs` rule.
|
|
895
|
+
|
|
896
|
+
// Replace dot notation markers with their processed values
|
|
897
|
+
let finalText = processedText;
|
|
898
|
+
dotNotationReplacements.forEach((value, marker) => {
|
|
899
|
+
finalText = finalText.replace(new RegExp(marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), value);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
return finalText;
|
|
779
903
|
}
|
|
780
904
|
|
|
781
905
|
functionCall "function call"
|
|
@@ -783,16 +907,95 @@ functionCall "function call"
|
|
|
783
907
|
return `${name}(${args || ''})`;
|
|
784
908
|
}
|
|
785
909
|
|
|
910
|
+
functionCallWithArgs "function call with complex args"
|
|
911
|
+
= name:identifier "(" args:complexFunctionArgs? ")" {
|
|
912
|
+
return `${name}(${args || ''})`;
|
|
913
|
+
}
|
|
914
|
+
|
|
786
915
|
functionArgs
|
|
787
916
|
= arg:functionArg rest:("," _ functionArg)* {
|
|
788
917
|
return [arg].concat(rest.map(r => r[2])).join(', ');
|
|
789
918
|
}
|
|
790
919
|
|
|
920
|
+
complexFunctionArgs
|
|
921
|
+
= arg:complexFunctionArg rest:("," _ complexFunctionArg)* {
|
|
922
|
+
return [arg].concat(rest.map(r => r[2])).join(', ');
|
|
923
|
+
}
|
|
924
|
+
|
|
791
925
|
functionArg
|
|
792
926
|
= _ value:(identifier / number / string) _ {
|
|
793
927
|
return value;
|
|
794
928
|
}
|
|
795
929
|
|
|
930
|
+
complexFunctionArg "complex function argument"
|
|
931
|
+
= _ value:complexArgExpression _ {
|
|
932
|
+
// Process @ literals and transform identifiers to signals
|
|
933
|
+
// Handle dot notation with @ literals like @item.@id, @item.id, item.@id
|
|
934
|
+
// Similar logic to simpleDynamicPart but for function arguments
|
|
935
|
+
|
|
936
|
+
let processed = value.trim();
|
|
937
|
+
const original = processed;
|
|
938
|
+
|
|
939
|
+
// Check if it's a dot notation expression
|
|
940
|
+
if (processed.match(/^(@?[a-zA-Z_][a-zA-Z0-9_]*)(\.@?[a-zA-Z_][a-zA-Z0-9_]*)*$/)) {
|
|
941
|
+
// Split by dots to handle each part separately
|
|
942
|
+
const parts = processed.split('.');
|
|
943
|
+
const allLiterals = parts.every(part => part.trim().startsWith('@'));
|
|
944
|
+
|
|
945
|
+
let computedValue;
|
|
946
|
+
|
|
947
|
+
if (allLiterals) {
|
|
948
|
+
// All parts are literals, just remove @ prefixes
|
|
949
|
+
computedValue = parts.map(part => part.trim().replace('@', '')).join('.');
|
|
950
|
+
} else {
|
|
951
|
+
// Transform each part individually
|
|
952
|
+
computedValue = parts.map(part => {
|
|
953
|
+
const trimmedPart = part.trim();
|
|
954
|
+
if (trimmedPart.startsWith('@')) {
|
|
955
|
+
return trimmedPart.substring(1); // Remove @ prefix for literals
|
|
956
|
+
} else {
|
|
957
|
+
// Don't transform keywords
|
|
958
|
+
if (['true', 'false', 'null'].includes(trimmedPart)) {
|
|
959
|
+
return trimmedPart;
|
|
960
|
+
}
|
|
961
|
+
// Check if it's already a function call
|
|
962
|
+
if (trimmedPart.includes('(')) {
|
|
963
|
+
return trimmedPart;
|
|
964
|
+
}
|
|
965
|
+
// Transform to signal
|
|
966
|
+
return `${trimmedPart}()`;
|
|
967
|
+
}
|
|
968
|
+
}).join('.');
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
return computedValue;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Handle standalone identifiers (not dot notation)
|
|
975
|
+
// If it starts with @, remove @ prefix (literal)
|
|
976
|
+
if (processed.startsWith('@')) {
|
|
977
|
+
return processed.substring(1);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// Don't transform keywords or numbers
|
|
981
|
+
if (['true', 'false', 'null'].includes(processed) || /^\d+(\.\d+)?$/.test(processed)) {
|
|
982
|
+
return processed;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Check if it's already a function call
|
|
986
|
+
if (processed.includes('(')) {
|
|
987
|
+
return processed;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// Transform identifier to signal
|
|
991
|
+
return `${processed}()`;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
complexArgExpression "complex argument expression"
|
|
995
|
+
= $([^,)]* ("(" [^)]* ")" [^,)]*)*) {
|
|
996
|
+
return text().trim();
|
|
997
|
+
}
|
|
998
|
+
|
|
796
999
|
number
|
|
797
1000
|
= [0-9]+ ("." [0-9]+)? { return text(); }
|
|
798
1001
|
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,21 @@ import * as ts from "typescript";
|
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
var { generate } = pkg;
|
|
10
10
|
var DEV_SRC = "../../src";
|
|
11
|
+
function generateHash(str) {
|
|
12
|
+
let hash = 0;
|
|
13
|
+
for (let i = 0; i < str.length; i++) {
|
|
14
|
+
const char = str.charCodeAt(i);
|
|
15
|
+
hash = (hash << 5) - hash + char;
|
|
16
|
+
hash = hash & hash;
|
|
17
|
+
}
|
|
18
|
+
const positiveHash = Math.abs(hash);
|
|
19
|
+
let result = "";
|
|
20
|
+
for (let i = 0; i < 8; i++) {
|
|
21
|
+
const letterIndex = (positiveHash + i * 31) % 26;
|
|
22
|
+
result += String.fromCharCode(97 + letterIndex);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
11
26
|
function showErrorMessage(template, error) {
|
|
12
27
|
if (!error.location) {
|
|
13
28
|
return `Syntax error: ${error.message}`;
|
|
@@ -22,6 +37,72 @@ ${errorLine}
|
|
|
22
37
|
${pointer}
|
|
23
38
|
`;
|
|
24
39
|
}
|
|
40
|
+
function scopeCSS(css, scopeClass) {
|
|
41
|
+
const scopeSelector = `.${scopeClass}`;
|
|
42
|
+
let result = "";
|
|
43
|
+
let i = 0;
|
|
44
|
+
let depth = 0;
|
|
45
|
+
let inRule = false;
|
|
46
|
+
let selectorBuffer = "";
|
|
47
|
+
while (i < css.length) {
|
|
48
|
+
const char = css[i];
|
|
49
|
+
if (char === "@" && !inRule && selectorBuffer === "") {
|
|
50
|
+
const atRuleStart = i;
|
|
51
|
+
i++;
|
|
52
|
+
while (i < css.length && css[i] !== "{") {
|
|
53
|
+
i++;
|
|
54
|
+
}
|
|
55
|
+
if (i < css.length) {
|
|
56
|
+
depth = 1;
|
|
57
|
+
i++;
|
|
58
|
+
while (i < css.length && depth > 0) {
|
|
59
|
+
if (css[i] === "{") depth++;
|
|
60
|
+
else if (css[i] === "}") depth--;
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
result += css.substring(atRuleStart, i);
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (char === "{" && !inRule) {
|
|
68
|
+
const selectorText = selectorBuffer.trim();
|
|
69
|
+
if (selectorText) {
|
|
70
|
+
const scopedSelectors = selectorText.split(",").map((sel) => {
|
|
71
|
+
const trimmed = sel.trim();
|
|
72
|
+
return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;
|
|
73
|
+
}).join(", ");
|
|
74
|
+
result += scopedSelectors;
|
|
75
|
+
}
|
|
76
|
+
result += " {";
|
|
77
|
+
inRule = true;
|
|
78
|
+
depth = 1;
|
|
79
|
+
selectorBuffer = "";
|
|
80
|
+
} else if (char === "{" && inRule) {
|
|
81
|
+
result += char;
|
|
82
|
+
depth++;
|
|
83
|
+
} else if (char === "}" && inRule) {
|
|
84
|
+
result += char;
|
|
85
|
+
depth--;
|
|
86
|
+
if (depth === 0) {
|
|
87
|
+
inRule = false;
|
|
88
|
+
}
|
|
89
|
+
} else if (!inRule) {
|
|
90
|
+
selectorBuffer += char;
|
|
91
|
+
} else {
|
|
92
|
+
result += char;
|
|
93
|
+
if (char === "{") depth++;
|
|
94
|
+
}
|
|
95
|
+
i++;
|
|
96
|
+
}
|
|
97
|
+
if (selectorBuffer.trim()) {
|
|
98
|
+
const scopedSelectors = selectorBuffer.trim().split(",").map((sel) => {
|
|
99
|
+
const trimmed = sel.trim();
|
|
100
|
+
return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;
|
|
101
|
+
}).join(", ");
|
|
102
|
+
result += scopedSelectors;
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
25
106
|
function shaderLoader() {
|
|
26
107
|
const filter = createFilter(/\.(frag|vert|wgsl)$/);
|
|
27
108
|
return {
|
|
@@ -54,6 +135,7 @@ function canvasengine() {
|
|
|
54
135
|
"Viewport",
|
|
55
136
|
"Graphics",
|
|
56
137
|
"Container",
|
|
138
|
+
"FocusContainer",
|
|
57
139
|
"ImageMap",
|
|
58
140
|
"NineSliceSprite",
|
|
59
141
|
"Rect",
|
|
@@ -76,7 +158,15 @@ function canvasengine() {
|
|
|
76
158
|
if (!filter(id)) return null;
|
|
77
159
|
const scriptMatch = code.match(/<script>([\s\S]*?)<\/script>/);
|
|
78
160
|
let scriptContent = scriptMatch ? scriptMatch[1].trim() : "";
|
|
79
|
-
|
|
161
|
+
const styleTagMatch = code.match(/<style([^>]*)>([\s\S]*?)<\/style>/);
|
|
162
|
+
let styleContent = "";
|
|
163
|
+
let isScoped = false;
|
|
164
|
+
if (styleTagMatch) {
|
|
165
|
+
const styleAttributes = styleTagMatch[1].trim();
|
|
166
|
+
styleContent = styleTagMatch[2].trim();
|
|
167
|
+
isScoped = /scoped(?:\s|>|$)/.test(styleAttributes);
|
|
168
|
+
}
|
|
169
|
+
let template = code.replace(/<script>[\s\S]*?<\/script>/, "").replace(/<style[^>]*>[\s\S]*?<\/style>/, "").replace(/^\s+|\s+$/g, "");
|
|
80
170
|
let parsedTemplate;
|
|
81
171
|
try {
|
|
82
172
|
parsedTemplate = parser.parse(template);
|
|
@@ -135,10 +225,41 @@ ${importsCode}`;
|
|
|
135
225
|
${importsCode}`;
|
|
136
226
|
}
|
|
137
227
|
});
|
|
228
|
+
let processedStyleContent = styleContent;
|
|
229
|
+
let scopeClass = "";
|
|
230
|
+
if (isScoped && styleContent) {
|
|
231
|
+
const fileHash = generateHash(id);
|
|
232
|
+
scopeClass = fileHash;
|
|
233
|
+
processedStyleContent = scopeCSS(styleContent, scopeClass);
|
|
234
|
+
parsedTemplate = parsedTemplate.replace(
|
|
235
|
+
/h\(DOMContainer\s*,\s*(\{([^}]*)\}|null)\s*(,\s*[^)]*)?\)/g,
|
|
236
|
+
(match, propsPart, propsContent, childrenPart) => {
|
|
237
|
+
if (propsPart === "null") {
|
|
238
|
+
return `h(DOMContainer, { _scopeClass: '${scopeClass}' }${childrenPart || ""})`;
|
|
239
|
+
} else {
|
|
240
|
+
return `h(DOMContainer, { _scopeClass: '${scopeClass}', ${propsContent || ""} }${childrenPart || ""})`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
parsedTemplate = parsedTemplate.replace(
|
|
245
|
+
/h\(DOMContainer\s*\)(?!\s*\()/g,
|
|
246
|
+
`h(DOMContainer, { _scopeClass: '${scopeClass}' })`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const escapedStyleContent = processedStyleContent.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
250
|
+
const styleId = `ce-style-${id.replace(/[^a-zA-Z0-9]/g, "-")}`;
|
|
251
|
+
const styleInjectionCode = styleContent ? `// Inject CSS styles into the document head
|
|
252
|
+
if (typeof document !== 'undefined' && !document.getElementById('${styleId}')) {
|
|
253
|
+
const styleElement = document.createElement('style');
|
|
254
|
+
styleElement.id = '${styleId}';
|
|
255
|
+
styleElement.textContent = '${escapedStyleContent}';
|
|
256
|
+
document.head.appendChild(styleElement);
|
|
257
|
+
}
|
|
258
|
+
` : "";
|
|
138
259
|
const output = String.raw`
|
|
139
260
|
${importsCode}
|
|
140
261
|
import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : "'canvasengine'"}
|
|
141
|
-
|
|
262
|
+
${styleInjectionCode}
|
|
142
263
|
export default function component($$props) {
|
|
143
264
|
const $props = useProps($$props)
|
|
144
265
|
const defineProps = useDefineProps($$props)
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts"],"sourcesContent":["import { createFilter } from \"vite\";\nimport { parse } from \"acorn\";\nimport fs from \"fs\";\nimport pkg from \"peggy\";\nimport path from \"path\";\nimport * as ts from \"typescript\";\nimport { fileURLToPath } from 'url';\n\nconst { generate } = pkg;\n\nconst DEV_SRC = \"../../src\"\n\n/**\n * Formats a syntax error message with visual pointer to the error location\n * \n * @param {string} template - The template content that failed to parse\n * @param {object} error - The error object with location information\n * @returns {string} - Formatted error message with a visual pointer\n * \n * @example\n * ```\n * const errorMessage = showErrorMessage(\"<Canvas>test(d)</Canvas>\", syntaxError);\n * // Returns a formatted error message with an arrow pointing to 'd'\n * ```\n */\nfunction showErrorMessage(template: string, error: any): string {\n if (!error.location) {\n return `Syntax error: ${error.message}`;\n }\n\n const lines = template.split('\\n');\n const { line, column } = error.location.start;\n const errorLine = lines[line - 1] || '';\n \n // Create a visual pointer with an arrow\n const pointer = ' '.repeat(column - 1) + '^';\n \n return `Syntax error at line ${line}, column ${column}: ${error.message}\\n\\n` +\n `${errorLine}\\n${pointer}\\n`;\n}\n\n/**\n * Vite plugin to load shader files (.frag, .vert, .wgsl) as text strings\n * \n * This plugin allows importing shader files directly as string literals in your code.\n * It supports fragment shaders (.frag), vertex shaders (.vert), and WebGPU shaders (.wgsl).\n * The content is loaded as a raw string and can be used directly with graphics APIs.\n * \n * @returns {object} - Vite plugin configuration object\n * \n * @example\n * ```typescript\n * // In your vite.config.ts\n * import { shaderLoader } from './path/to/compiler'\n * \n * export default defineConfig({\n * plugins: [shaderLoader()]\n * })\n * \n * // In your code\n * import fragmentShader from './shader.frag'\n * import vertexShader from './shader.vert'\n * import computeShader from './shader.wgsl'\n * \n * console.log(fragmentShader) // Raw shader code as string\n * ```\n */\nexport function shaderLoader() {\n const filter = createFilter(/\\.(frag|vert|wgsl)$/);\n\n return {\n name: \"vite-plugin-shader-loader\",\n transform(code: string, id: string) {\n if (!filter(id)) return;\n\n // Escape the shader code to be safely embedded in a JavaScript string\n const escapedCode = code\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/`/g, '\\\\`') // Escape backticks\n .replace(/\\$/g, '\\\\$'); // Escape dollar signs\n\n // Return the shader content as a default export string\n return {\n code: `export default \\`${escapedCode}\\`;`,\n map: null,\n };\n },\n };\n}\n\nexport default function canvasengine() {\n const filter = createFilter(\"**/*.ce\");\n\n // Convert import.meta.url to a file path\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n const grammar = fs.readFileSync(\n path.join(__dirname, \"grammar.pegjs\"),\n \"utf8\"\n );\n const parser = generate(grammar);\n const isDev = process.env.NODE_ENV === \"dev\";\n const FLAG_COMMENT = \"/*--[TPL]--*/\";\n\n const PRIMITIVE_COMPONENTS = [\n \"Canvas\",\n \"Sprite\",\n \"Text\",\n \"Viewport\",\n \"Graphics\",\n \"Container\",\n \"ImageMap\",\n \"NineSliceSprite\",\n \"Rect\",\n \"Circle\",\n \"Ellipse\",\n \"Triangle\",\n \"TilingSprite\",\n \"svg\",\n \"Video\",\n \"Mesh\",\n \"Svg\",\n \"DOMContainer\",\n \"DOMElement\",\n \"Button\",\n \"Joystick\"\n ];\n\n return {\n name: \"vite-plugin-ce\",\n transform(code: string, id: string) {\n if (!filter(id)) return null;\n\n // Extract the script content\n const scriptMatch = code.match(/<script>([\\s\\S]*?)<\\/script>/);\n let scriptContent = scriptMatch ? scriptMatch[1].trim() : \"\";\n \n // Transform SVG tags to Svg components\n let template = code.replace(/<script>[\\s\\S]*?<\\/script>/, \"\")\n .replace(/^\\s+|\\s+$/g, '');\n\n let parsedTemplate;\n try {\n parsedTemplate = parser.parse(template);\n } catch (error) {\n const errorMsg = showErrorMessage(template, error);\n throw new Error(`Error parsing template in file ${id}:\\n${errorMsg}`);\n }\n\n // trick to avoid typescript remove imports in scriptContent\n scriptContent += FLAG_COMMENT + parsedTemplate\n\n let transpiledCode = ts.transpileModule(scriptContent, {\n compilerOptions: {\n module: ts.ModuleKind.Preserve,\n },\n }).outputText;\n\n // remove code after /*---*/\n transpiledCode = transpiledCode.split(FLAG_COMMENT)[0]\n\n // Use Acorn to parse the script content\n const parsed = parse(transpiledCode, {\n sourceType: \"module\",\n ecmaVersion: 2020,\n });\n\n // Extract imports\n const imports = parsed.body.filter(\n (node) => node.type === \"ImportDeclaration\"\n );\n\n // Extract non-import statements from scriptContent\n const nonImportCode = parsed.body\n .filter((node) => node.type !== \"ImportDeclaration\")\n .map((node) => transpiledCode.slice(node.start, node.end))\n .join(\"\\n\");\n\n let importsCode = imports\n .map((imp) => {\n let importCode = transpiledCode.slice(imp.start, imp.end);\n if (isDev && importCode.includes(\"from 'canvasengine'\")) {\n importCode = importCode.replace(\n \"from 'canvasengine'\",\n `from '${DEV_SRC}'`\n );\n }\n return importCode;\n })\n .join(\"\\n\");\n\n // Define an array for required imports\n const requiredImports = [\"h\", \"computed\", \"cond\", \"loop\"];\n\n // Check for missing imports\n const missingImports = requiredImports.filter(\n (importName) =>\n !imports.some(\n (imp) =>\n imp.specifiers &&\n imp.specifiers.some(\n (spec) =>\n spec.type === \"ImportSpecifier\" &&\n spec.imported && \n 'name' in spec.imported &&\n spec.imported.name === importName\n )\n )\n );\n\n // Add missing imports\n if (missingImports.length > 0) {\n const additionalImportCode = `import { ${missingImports.join(\n \", \"\n )} } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"};`;\n importsCode = `${additionalImportCode}\\n${importsCode}`;\n }\n\n // Check for primitive components in parsedTemplate\n const primitiveImports = PRIMITIVE_COMPONENTS.filter((component) =>\n parsedTemplate.includes(`h(${component}`)\n );\n\n // Add missing imports for primitive components\n primitiveImports.forEach((component) => {\n const importStatement = `import { ${component} } from ${\n isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"\n };`;\n if (!importsCode.includes(importStatement)) {\n importsCode = `${importStatement}\\n${importsCode}`;\n }\n });\n\n // Generate the output\n const output = String.raw`\n ${importsCode}\n import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"}\n\n export default function component($$props) {\n const $props = useProps($$props)\n const defineProps = useDefineProps($$props)\n ${nonImportCode}\n let $this = ${parsedTemplate}\n return $this\n }\n `;\n\n return {\n code: output,\n map: null,\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../index.ts"],"sourcesContent":["import { createFilter } from \"vite\";\nimport { parse } from \"acorn\";\nimport fs from \"fs\";\nimport pkg from \"peggy\";\nimport path from \"path\";\nimport * as ts from \"typescript\";\nimport { fileURLToPath } from 'url';\n\nconst { generate } = pkg;\n\nconst DEV_SRC = \"../../src\"\n\n/**\n * Generates a short hash (8 characters, letters only) from a string\n * \n * @param {string} str - The string to hash\n * @returns {string} - An 8-character hash containing only lowercase letters (a-z)\n */\nfunction generateHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n // Convert to positive number and map to letters only (a-z)\n // Use modulo to map to 26 letters, then convert to character\n const positiveHash = Math.abs(hash);\n let result = '';\n for (let i = 0; i < 8; i++) {\n const letterIndex = (positiveHash + i * 31) % 26; // 31 is a prime to spread values\n result += String.fromCharCode(97 + letterIndex); // 97 is 'a'\n }\n return result;\n}\n\n/**\n * Formats a syntax error message with visual pointer to the error location\n * \n * @param {string} template - The template content that failed to parse\n * @param {object} error - The error object with location information\n * @returns {string} - Formatted error message with a visual pointer\n * \n * @example\n * ```\n * const errorMessage = showErrorMessage(\"<Canvas>test(d)</Canvas>\", syntaxError);\n * // Returns a formatted error message with an arrow pointing to 'd'\n * ```\n */\nfunction showErrorMessage(template: string, error: any): string {\n if (!error.location) {\n return `Syntax error: ${error.message}`;\n }\n\n const lines = template.split('\\n');\n const { line, column } = error.location.start;\n const errorLine = lines[line - 1] || '';\n \n // Create a visual pointer with an arrow\n const pointer = ' '.repeat(column - 1) + '^';\n \n return `Syntax error at line ${line}, column ${column}: ${error.message}\\n\\n` +\n `${errorLine}\\n${pointer}\\n`;\n}\n\n/**\n * Scopes CSS selectors by prefixing them with a class selector\n * \n * This function prefixes all CSS rule selectors (not @rules) with a class\n * selector to scope the styles to a specific component instance.\n * \n * @param {string} css - The CSS content to scope\n * @param {string} scopeClass - The unique scope class to use (without the dot)\n * @returns {string} - The scoped CSS content\n * \n * @example\n * ```\n * const scoped = scopeCSS('.my-class { color: red; }', 'ce-scope-abc123');\n * // Returns: '.ce-scope-abc123 .my-class { color: red; }'\n * ```\n */\nfunction scopeCSS(css: string, scopeClass: string): string {\n const scopeSelector = `.${scopeClass}`;\n \n // Process CSS by finding rule blocks while skipping @rules\n let result = '';\n let i = 0;\n let depth = 0;\n let inRule = false;\n let selectorBuffer = '';\n \n while (i < css.length) {\n const char = css[i];\n \n if (char === '@' && !inRule && selectorBuffer === '') {\n // Found @rule - copy it as-is until matching closing brace\n const atRuleStart = i;\n i++; // Skip '@'\n \n // Find the opening brace\n while (i < css.length && css[i] !== '{') {\n i++;\n }\n \n if (i < css.length) {\n // Found opening brace, now find matching closing brace\n depth = 1;\n i++; // Skip '{'\n \n while (i < css.length && depth > 0) {\n if (css[i] === '{') depth++;\n else if (css[i] === '}') depth--;\n i++;\n }\n \n // Copy entire @rule as-is\n result += css.substring(atRuleStart, i);\n }\n continue;\n }\n \n if (char === '{' && !inRule) {\n // Start of a rule block - scope the selector we just collected\n const selectorText = selectorBuffer.trim();\n \n if (selectorText) {\n // Split selectors by comma and scope each one\n const scopedSelectors = selectorText\n .split(',')\n .map(sel => {\n const trimmed = sel.trim();\n return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;\n })\n .join(', ');\n \n result += scopedSelectors;\n }\n result += ' {';\n inRule = true;\n depth = 1;\n selectorBuffer = '';\n } else if (char === '{' && inRule) {\n // Nested brace\n result += char;\n depth++;\n } else if (char === '}' && inRule) {\n result += char;\n depth--;\n if (depth === 0) {\n inRule = false;\n }\n } else if (!inRule) {\n // Collecting selector\n selectorBuffer += char;\n } else {\n // Inside rule block\n result += char;\n if (char === '{') depth++;\n }\n \n i++;\n }\n \n // Add any remaining selector (shouldn't happen in valid CSS, but handle it)\n if (selectorBuffer.trim()) {\n const scopedSelectors = selectorBuffer.trim()\n .split(',')\n .map(sel => {\n const trimmed = sel.trim();\n return trimmed ? `${scopeSelector} ${trimmed}` : trimmed;\n })\n .join(', ');\n result += scopedSelectors;\n }\n \n return result;\n}\n\n/**\n * Vite plugin to load shader files (.frag, .vert, .wgsl) as text strings\n * \n * This plugin allows importing shader files directly as string literals in your code.\n * It supports fragment shaders (.frag), vertex shaders (.vert), and WebGPU shaders (.wgsl).\n * The content is loaded as a raw string and can be used directly with graphics APIs.\n * \n * @returns {object} - Vite plugin configuration object\n * \n * @example\n * ```typescript\n * // In your vite.config.ts\n * import { shaderLoader } from './path/to/compiler'\n * \n * export default defineConfig({\n * plugins: [shaderLoader()]\n * })\n * \n * // In your code\n * import fragmentShader from './shader.frag'\n * import vertexShader from './shader.vert'\n * import computeShader from './shader.wgsl'\n * \n * console.log(fragmentShader) // Raw shader code as string\n * ```\n */\nexport function shaderLoader() {\n const filter = createFilter(/\\.(frag|vert|wgsl)$/);\n\n return {\n name: \"vite-plugin-shader-loader\",\n transform(code: string, id: string) {\n if (!filter(id)) return;\n\n // Escape the shader code to be safely embedded in a JavaScript string\n const escapedCode = code\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/`/g, '\\\\`') // Escape backticks\n .replace(/\\$/g, '\\\\$'); // Escape dollar signs\n\n // Return the shader content as a default export string\n return {\n code: `export default \\`${escapedCode}\\`;`,\n map: null,\n };\n },\n };\n}\n\nexport default function canvasengine() {\n const filter = createFilter(\"**/*.ce\");\n\n // Convert import.meta.url to a file path\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n const grammar = fs.readFileSync(\n path.join(__dirname, \"grammar.pegjs\"),\n \"utf8\"\n );\n const parser = generate(grammar);\n const isDev = process.env.NODE_ENV === \"dev\";\n const FLAG_COMMENT = \"/*--[TPL]--*/\";\n\n const PRIMITIVE_COMPONENTS = [\n \"Canvas\",\n \"Sprite\",\n \"Text\",\n \"Viewport\",\n \"Graphics\",\n \"Container\",\n \"FocusContainer\",\n \"ImageMap\",\n \"NineSliceSprite\",\n \"Rect\",\n \"Circle\",\n \"Ellipse\",\n \"Triangle\",\n \"TilingSprite\",\n \"svg\",\n \"Video\",\n \"Mesh\",\n \"Svg\",\n \"DOMContainer\",\n \"DOMElement\",\n \"Button\",\n \"Joystick\"\n ];\n\n return {\n name: \"vite-plugin-ce\",\n transform(code: string, id: string) {\n if (!filter(id)) return null;\n\n // Extract the script content\n const scriptMatch = code.match(/<script>([\\s\\S]*?)<\\/script>/);\n let scriptContent = scriptMatch ? scriptMatch[1].trim() : \"\";\n \n // Extract the style tag with attributes and content\n const styleTagMatch = code.match(/<style([^>]*)>([\\s\\S]*?)<\\/style>/);\n let styleContent = \"\";\n let isScoped = false;\n \n if (styleTagMatch) {\n const styleAttributes = styleTagMatch[1].trim();\n styleContent = styleTagMatch[2].trim();\n \n // Check if scoped attribute is present\n isScoped = /scoped(?:\\s|>|$)/.test(styleAttributes);\n }\n \n // Remove script and style tags from template before parsing\n let template = code\n .replace(/<script>[\\s\\S]*?<\\/script>/, \"\")\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/, \"\")\n .replace(/^\\s+|\\s+$/g, '');\n\n let parsedTemplate;\n try {\n parsedTemplate = parser.parse(template);\n } catch (error) {\n const errorMsg = showErrorMessage(template, error);\n throw new Error(`Error parsing template in file ${id}:\\n${errorMsg}`);\n }\n\n // trick to avoid typescript remove imports in scriptContent\n scriptContent += FLAG_COMMENT + parsedTemplate\n\n let transpiledCode = ts.transpileModule(scriptContent, {\n compilerOptions: {\n module: ts.ModuleKind.Preserve,\n },\n }).outputText;\n\n // remove code after /*---*/\n transpiledCode = transpiledCode.split(FLAG_COMMENT)[0]\n\n // Use Acorn to parse the script content\n const parsed = parse(transpiledCode, {\n sourceType: \"module\",\n ecmaVersion: 2020,\n });\n\n // Extract imports\n const imports = parsed.body.filter(\n (node) => node.type === \"ImportDeclaration\"\n );\n\n // Extract non-import statements from scriptContent\n const nonImportCode = parsed.body\n .filter((node) => node.type !== \"ImportDeclaration\")\n .map((node) => transpiledCode.slice(node.start, node.end))\n .join(\"\\n\");\n\n let importsCode = imports\n .map((imp) => {\n let importCode = transpiledCode.slice(imp.start, imp.end);\n if (isDev && importCode.includes(\"from 'canvasengine'\")) {\n importCode = importCode.replace(\n \"from 'canvasengine'\",\n `from '${DEV_SRC}'`\n );\n }\n return importCode;\n })\n .join(\"\\n\");\n\n // Define an array for required imports\n const requiredImports = [\"h\", \"computed\", \"cond\", \"loop\"];\n\n // Check for missing imports\n const missingImports = requiredImports.filter(\n (importName) =>\n !imports.some(\n (imp) =>\n imp.specifiers &&\n imp.specifiers.some(\n (spec) =>\n spec.type === \"ImportSpecifier\" &&\n spec.imported && \n 'name' in spec.imported &&\n spec.imported.name === importName\n )\n )\n );\n\n // Add missing imports\n if (missingImports.length > 0) {\n const additionalImportCode = `import { ${missingImports.join(\n \", \"\n )} } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"};`;\n importsCode = `${additionalImportCode}\\n${importsCode}`;\n }\n\n // Check for primitive components in parsedTemplate\n const primitiveImports = PRIMITIVE_COMPONENTS.filter((component) =>\n parsedTemplate.includes(`h(${component}`)\n );\n\n // Add missing imports for primitive components\n primitiveImports.forEach((component) => {\n const importStatement = `import { ${component} } from ${\n isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"\n };`;\n if (!importsCode.includes(importStatement)) {\n importsCode = `${importStatement}\\n${importsCode}`;\n }\n });\n\n // Process CSS: scope it if scoped attribute is present\n let processedStyleContent = styleContent;\n let scopeClass = '';\n \n if (isScoped && styleContent) {\n // Generate short hash (8 characters) based on file path\n const fileHash = generateHash(id);\n scopeClass = fileHash;\n processedStyleContent = scopeCSS(styleContent, scopeClass);\n \n // Add _scopeClass prop to all DOMContainer in the template\n // Pattern: h(DOMContainer, { ... }) or h(DOMContainer) or h(DOMContainer, null, ...)\n parsedTemplate = parsedTemplate.replace(\n /h\\(DOMContainer\\s*,\\s*(\\{([^}]*)\\}|null)\\s*(,\\s*[^)]*)?\\)/g,\n (match, propsPart, propsContent, childrenPart) => {\n if (propsPart === 'null') {\n // h(DOMContainer, null, ...) -> h(DOMContainer, { _scopeClass: '...' }, ...)\n return `h(DOMContainer, { _scopeClass: '${scopeClass}' }${childrenPart || ''})`;\n } else {\n // h(DOMContainer, { ... }, ...) -> h(DOMContainer, { _scopeClass: '...', ... }, ...)\n // Need to insert _scopeClass at the beginning of the props object\n return `h(DOMContainer, { _scopeClass: '${scopeClass}', ${propsContent || ''} }${childrenPart || ''})`;\n }\n }\n );\n \n // Also handle h(DOMContainer) without props\n parsedTemplate = parsedTemplate.replace(\n /h\\(DOMContainer\\s*\\)(?!\\s*\\()/g,\n `h(DOMContainer, { _scopeClass: '${scopeClass}' })`\n );\n }\n \n // Escape style content for safe embedding in JavaScript string (using single quotes)\n // We need to escape: backslashes, single quotes, and line breaks\n const escapedStyleContent = processedStyleContent\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes first\n .replace(/'/g, \"\\\\'\") // Escape single quotes\n .replace(/\\n/g, '\\\\n') // Escape newlines\n .replace(/\\r/g, '\\\\r'); // Escape carriage returns\n\n // Generate unique ID for style element based on file path\n const styleId = `ce-style-${id.replace(/[^a-zA-Z0-9]/g, '-')}`;\n\n // Generate CSS injection code if style content exists\n // Use single quotes to avoid escaping issues with backticks\n const styleInjectionCode = styleContent ? \n '// Inject CSS styles into the document head\\n' +\n `if (typeof document !== 'undefined' && !document.getElementById('${styleId}')) {\\n` +\n ' const styleElement = document.createElement(\\'style\\');\\n' +\n ` styleElement.id = '${styleId}';\\n` +\n ` styleElement.textContent = '${escapedStyleContent}';\\n` +\n ' document.head.appendChild(styleElement);\\n' +\n '}\\n'\n : '';\n \n\n // Generate the output\n const output = String.raw`\n ${importsCode}\n import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : \"'canvasengine'\"}\n ${styleInjectionCode}\n export default function component($$props) {\n const $props = useProps($$props)\n const defineProps = useDefineProps($$props)\n ${nonImportCode}\n let $this = ${parsedTemplate}\n return $this\n }\n `;\n\n return {\n code: output,\n map: null,\n };\n },\n };\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,aAAa;AACtB,OAAO,QAAQ;AACf,OAAO,SAAS;AAChB,OAAO,UAAU;AACjB,YAAY,QAAQ;AACpB,SAAS,qBAAqB;AAE9B,IAAM,EAAE,SAAS,IAAI;AAErB,IAAM,UAAU;AAQhB,SAAS,aAAa,KAAqB;AACzC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,YAAS,QAAQ,KAAK,OAAQ;AAC9B,WAAO,OAAO;AAAA,EAChB;AAGA,QAAM,eAAe,KAAK,IAAI,IAAI;AAClC,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,eAAe,eAAe,IAAI,MAAM;AAC9C,cAAU,OAAO,aAAa,KAAK,WAAW;AAAA,EAChD;AACA,SAAO;AACT;AAeA,SAAS,iBAAiB,UAAkB,OAAoB;AAC9D,MAAI,CAAC,MAAM,UAAU;AACnB,WAAO,iBAAiB,MAAM,OAAO;AAAA,EACvC;AAEA,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,QAAM,EAAE,MAAM,OAAO,IAAI,MAAM,SAAS;AACxC,QAAM,YAAY,MAAM,OAAO,CAAC,KAAK;AAGrC,QAAM,UAAU,IAAI,OAAO,SAAS,CAAC,IAAI;AAEzC,SAAO,wBAAwB,IAAI,YAAY,MAAM,KAAK,MAAM,OAAO;AAAA;AAAA,EAC7D,SAAS;AAAA,EAAK,OAAO;AAAA;AACjC;AAkBA,SAAS,SAAS,KAAa,YAA4B;AACzD,QAAM,gBAAgB,IAAI,UAAU;AAGpC,MAAI,SAAS;AACb,MAAI,IAAI;AACR,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,MAAI,iBAAiB;AAErB,SAAO,IAAI,IAAI,QAAQ;AACrB,UAAM,OAAO,IAAI,CAAC;AAElB,QAAI,SAAS,OAAO,CAAC,UAAU,mBAAmB,IAAI;AAEpD,YAAM,cAAc;AACpB;AAGA,aAAO,IAAI,IAAI,UAAU,IAAI,CAAC,MAAM,KAAK;AACvC;AAAA,MACF;AAEA,UAAI,IAAI,IAAI,QAAQ;AAElB,gBAAQ;AACR;AAEA,eAAO,IAAI,IAAI,UAAU,QAAQ,GAAG;AAClC,cAAI,IAAI,CAAC,MAAM,IAAK;AAAA,mBACX,IAAI,CAAC,MAAM,IAAK;AACzB;AAAA,QACF;AAGA,kBAAU,IAAI,UAAU,aAAa,CAAC;AAAA,MACxC;AACA;AAAA,IACF;AAEA,QAAI,SAAS,OAAO,CAAC,QAAQ;AAE3B,YAAM,eAAe,eAAe,KAAK;AAEzC,UAAI,cAAc;AAEhB,cAAM,kBAAkB,aACrB,MAAM,GAAG,EACT,IAAI,SAAO;AACV,gBAAM,UAAU,IAAI,KAAK;AACzB,iBAAO,UAAU,GAAG,aAAa,IAAI,OAAO,KAAK;AAAA,QACnD,CAAC,EACA,KAAK,IAAI;AAEZ,kBAAU;AAAA,MACZ;AACA,gBAAU;AACV,eAAS;AACT,cAAQ;AACR,uBAAiB;AAAA,IACnB,WAAW,SAAS,OAAO,QAAQ;AAEjC,gBAAU;AACV;AAAA,IACF,WAAW,SAAS,OAAO,QAAQ;AACjC,gBAAU;AACV;AACA,UAAI,UAAU,GAAG;AACf,iBAAS;AAAA,MACX;AAAA,IACF,WAAW,CAAC,QAAQ;AAElB,wBAAkB;AAAA,IACpB,OAAO;AAEL,gBAAU;AACV,UAAI,SAAS,IAAK;AAAA,IACpB;AAEA;AAAA,EACF;AAGA,MAAI,eAAe,KAAK,GAAG;AACzB,UAAM,kBAAkB,eAAe,KAAK,EACzC,MAAM,GAAG,EACT,IAAI,SAAO;AACV,YAAM,UAAU,IAAI,KAAK;AACzB,aAAO,UAAU,GAAG,aAAa,IAAI,OAAO,KAAK;AAAA,IACnD,CAAC,EACA,KAAK,IAAI;AACZ,cAAU;AAAA,EACZ;AAEA,SAAO;AACT;AA4BO,SAAS,eAAe;AAC7B,QAAM,SAAS,aAAa,qBAAqB;AAEjD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,MAAc,IAAY;AAClC,UAAI,CAAC,OAAO,EAAE,EAAG;AAGjB,YAAM,cAAc,KACjB,QAAQ,OAAO,MAAM,EACrB,QAAQ,MAAM,KAAK,EACnB,QAAQ,OAAO,KAAK;AAGvB,aAAO;AAAA,QACL,MAAM,oBAAoB,WAAW;AAAA,QACrC,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;AAEe,SAAR,eAAgC;AACrC,QAAM,SAAS,aAAa,SAAS;AAGrC,QAAM,aAAa,cAAc,YAAY,GAAG;AAChD,QAAM,YAAY,KAAK,QAAQ,UAAU;AAEzC,QAAM,UAAU,GAAG;AAAA,IACjB,KAAK,KAAK,WAAW,eAAe;AAAA,IACpC;AAAA,EACF;AACA,QAAM,SAAS,SAAS,OAAO;AAC/B,QAAM,QAAQ,QAAQ,IAAI,aAAa;AACvC,QAAM,eAAe;AAErB,QAAM,uBAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,MAAc,IAAY;AAClC,UAAI,CAAC,OAAO,EAAE,EAAG,QAAO;AAGxB,YAAM,cAAc,KAAK,MAAM,8BAA8B;AAC7D,UAAI,gBAAgB,cAAc,YAAY,CAAC,EAAE,KAAK,IAAI;AAG1D,YAAM,gBAAgB,KAAK,MAAM,mCAAmC;AACpE,UAAI,eAAe;AACnB,UAAI,WAAW;AAEf,UAAI,eAAe;AACjB,cAAM,kBAAkB,cAAc,CAAC,EAAE,KAAK;AAC9C,uBAAe,cAAc,CAAC,EAAE,KAAK;AAGrC,mBAAW,mBAAmB,KAAK,eAAe;AAAA,MACpD;AAGA,UAAI,WAAW,KACZ,QAAQ,8BAA8B,EAAE,EACxC,QAAQ,iCAAiC,EAAE,EAC3C,QAAQ,cAAc,EAAE;AAE3B,UAAI;AACJ,UAAI;AACF,yBAAiB,OAAO,MAAM,QAAQ;AAAA,MACxC,SAAS,OAAO;AACd,cAAM,WAAW,iBAAiB,UAAU,KAAK;AACjD,cAAM,IAAI,MAAM,kCAAkC,EAAE;AAAA,EAAM,QAAQ,EAAE;AAAA,MACtE;AAGA,uBAAiB,eAAe;AAEhC,UAAI,iBAAoB,mBAAgB,eAAe;AAAA,QACrD,iBAAiB;AAAA,UACf,QAAW,cAAW;AAAA,QACxB;AAAA,MACF,CAAC,EAAE;AAGH,uBAAiB,eAAe,MAAM,YAAY,EAAE,CAAC;AAGrD,YAAM,SAAS,MAAM,gBAAgB;AAAA,QACnC,YAAY;AAAA,QACZ,aAAa;AAAA,MACf,CAAC;AAGD,YAAM,UAAU,OAAO,KAAK;AAAA,QAC1B,CAAC,SAAS,KAAK,SAAS;AAAA,MAC1B;AAGA,YAAM,gBAAgB,OAAO,KAC1B,OAAO,CAAC,SAAS,KAAK,SAAS,mBAAmB,EAClD,IAAI,CAAC,SAAS,eAAe,MAAM,KAAK,OAAO,KAAK,GAAG,CAAC,EACxD,KAAK,IAAI;AAEZ,UAAI,cAAc,QACf,IAAI,CAAC,QAAQ;AACZ,YAAI,aAAa,eAAe,MAAM,IAAI,OAAO,IAAI,GAAG;AACxD,YAAI,SAAS,WAAW,SAAS,qBAAqB,GAAG;AACvD,uBAAa,WAAW;AAAA,YACtB;AAAA,YACA,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC,EACA,KAAK,IAAI;AAGZ,YAAM,kBAAkB,CAAC,KAAK,YAAY,QAAQ,MAAM;AAGxD,YAAM,iBAAiB,gBAAgB;AAAA,QACrC,CAAC,eACC,CAAC,QAAQ;AAAA,UACP,CAAC,QACC,IAAI,cACJ,IAAI,WAAW;AAAA,YACb,CAAC,SACC,KAAK,SAAS,qBACd,KAAK,YACL,UAAU,KAAK,YACf,KAAK,SAAS,SAAS;AAAA,UAC3B;AAAA,QACJ;AAAA,MACJ;AAGA,UAAI,eAAe,SAAS,GAAG;AAC7B,cAAM,uBAAuB,YAAY,eAAe;AAAA,UACtD;AAAA,QACF,CAAC,WAAW,QAAQ,IAAI,OAAO,MAAM,gBAAgB;AACrD,sBAAc,GAAG,oBAAoB;AAAA,EAAK,WAAW;AAAA,MACvD;AAGA,YAAM,mBAAmB,qBAAqB;AAAA,QAAO,CAAC,cACpD,eAAe,SAAS,KAAK,SAAS,EAAE;AAAA,MAC1C;AAGA,uBAAiB,QAAQ,CAAC,cAAc;AACtC,cAAM,kBAAkB,YAAY,SAAS,WAC3C,QAAQ,IAAI,OAAO,MAAM,gBAC3B;AACA,YAAI,CAAC,YAAY,SAAS,eAAe,GAAG;AAC1C,wBAAc,GAAG,eAAe;AAAA,EAAK,WAAW;AAAA,QAClD;AAAA,MACF,CAAC;AAGD,UAAI,wBAAwB;AAC5B,UAAI,aAAa;AAEjB,UAAI,YAAY,cAAc;AAE5B,cAAM,WAAW,aAAa,EAAE;AAChC,qBAAa;AACb,gCAAwB,SAAS,cAAc,UAAU;AAIzD,yBAAiB,eAAe;AAAA,UAC9B;AAAA,UACA,CAAC,OAAO,WAAW,cAAc,iBAAiB;AAChD,gBAAI,cAAc,QAAQ;AAExB,qBAAO,mCAAmC,UAAU,MAAM,gBAAgB,EAAE;AAAA,YAC9E,OAAO;AAGL,qBAAO,mCAAmC,UAAU,MAAM,gBAAgB,EAAE,KAAK,gBAAgB,EAAE;AAAA,YACrG;AAAA,UACF;AAAA,QACF;AAGA,yBAAiB,eAAe;AAAA,UAC9B;AAAA,UACA,mCAAmC,UAAU;AAAA,QAC/C;AAAA,MACF;AAIA,YAAM,sBAAsB,sBACzB,QAAQ,OAAO,MAAM,EACrB,QAAQ,MAAM,KAAK,EACnB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK;AAGvB,YAAM,UAAU,YAAY,GAAG,QAAQ,iBAAiB,GAAG,CAAC;AAI5D,YAAM,qBAAqB,eACzB;AAAA,mEACoE,OAAO;AAAA;AAAA,uBAEnD,OAAO;AAAA,gCACE,mBAAmB;AAAA;AAAA;AAAA,IAGlD;AAIJ,YAAM,SAAS,OAAO;AAAA,QACpB,WAAW;AAAA,iDAC8B,QAAQ,IAAI,OAAO,MAAM,gBAAgB;AAAA,QAClF,kBAAkB;AAAA;AAAA;AAAA;AAAA,UAIhB,aAAa;AAAA,sBACD,cAAc;AAAA;AAAA;AAAA;AAK9B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|