@damarkuncoro/meta-architecture-style-engine 0.1.1 → 0.1.3
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/README.md +279 -885
- package/lib/cjs/cli/index.js +1747 -61
- package/lib/cjs/cli/index.js.map +1 -1
- package/lib/cjs/index.d.ts +117 -65
- package/lib/cjs/index.js +1382 -41
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/integration/postcss/index.d.ts +41 -0
- package/lib/cjs/integration/postcss/index.js +296 -0
- package/lib/cjs/integration/postcss/index.js.map +1 -0
- package/lib/cjs/integration/tailwind/index.d.ts +40 -0
- package/lib/cjs/integration/tailwind/index.js +192 -0
- package/lib/cjs/integration/tailwind/index.js.map +1 -0
- package/lib/cjs/types-HxpHKBfo.d.ts +58 -0
- package/lib/esm/cli/index.js +1753 -61
- package/lib/esm/cli/index.js.map +1 -1
- package/lib/esm/index.d.mts +117 -65
- package/lib/esm/index.js +1379 -41
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/integration/postcss/index.d.mts +41 -0
- package/lib/esm/integration/postcss/index.js +269 -0
- package/lib/esm/integration/postcss/index.js.map +1 -0
- package/lib/esm/integration/tailwind/index.d.mts +40 -0
- package/lib/esm/integration/tailwind/index.js +165 -0
- package/lib/esm/integration/tailwind/index.js.map +1 -0
- package/lib/esm/types-HxpHKBfo.d.mts +58 -0
- package/package.json +29 -6
package/lib/cjs/cli/index.js
CHANGED
|
@@ -28,12 +28,175 @@ var import_commander6 = require("commander");
|
|
|
28
28
|
|
|
29
29
|
// src/cli/commands/init.ts
|
|
30
30
|
var import_commander = require("commander");
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
var fs = __toESM(require("fs"));
|
|
32
|
+
var path = __toESM(require("path"));
|
|
33
|
+
var readline = __toESM(require("readline"));
|
|
34
|
+
var POSTCSS_CONFIG = `module.exports = {
|
|
35
|
+
plugins: [
|
|
36
|
+
require('@damarkuncoro/meta-architecture-style-engine/postcss')({
|
|
37
|
+
prefix: '--mase-',
|
|
38
|
+
rootSelector: ':root',
|
|
39
|
+
})
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
`;
|
|
43
|
+
var TAILWIND_CONFIG = `const { createMasePreset } = require('@damarkuncoro/meta-architecture-style-engine/tailwind');
|
|
44
|
+
const fs = require('fs');
|
|
45
|
+
const path = require('path');
|
|
46
|
+
|
|
47
|
+
// Try to load local mase.tokens.js if available
|
|
48
|
+
let tokens;
|
|
49
|
+
const localTokensPath = path.resolve(__dirname, 'mase.tokens.js');
|
|
50
|
+
if (fs.existsSync(localTokensPath)) {
|
|
51
|
+
tokens = require(localTokensPath);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @type {import('tailwindcss').Config} */
|
|
55
|
+
module.exports = {
|
|
56
|
+
content: [
|
|
57
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
58
|
+
],
|
|
59
|
+
presets: [
|
|
60
|
+
createMasePreset({
|
|
61
|
+
tokens: tokens, // Use local tokens if found, otherwise defaults to internal
|
|
62
|
+
useCssVariables: true,
|
|
63
|
+
})
|
|
64
|
+
],
|
|
65
|
+
theme: {
|
|
66
|
+
extend: {},
|
|
67
|
+
},
|
|
68
|
+
plugins: [],
|
|
69
|
+
};
|
|
70
|
+
`;
|
|
71
|
+
var EXAMPLE_MASE = `contract DesignSystem {
|
|
72
|
+
domain color {
|
|
73
|
+
token primary: #007bff;
|
|
74
|
+
token secondary: #6c757d;
|
|
75
|
+
token success: #28a745;
|
|
76
|
+
token danger: #dc3545;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
domain typography {
|
|
80
|
+
token base: 16px;
|
|
81
|
+
token heading: 32px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
domain space {
|
|
85
|
+
token small: 4px;
|
|
86
|
+
token medium: 8px;
|
|
87
|
+
token large: 16px;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
var TOKENS_CONFIG = `/**
|
|
92
|
+
* MASE Tokens Configuration
|
|
93
|
+
*
|
|
94
|
+
* Defines your design tokens for the Atomic Engine.
|
|
95
|
+
* Use 'mase generate-css --format atomic' to generate utility classes.
|
|
96
|
+
*/
|
|
97
|
+
module.exports = {
|
|
98
|
+
// Colors
|
|
99
|
+
'color.primary': { id: 'color.primary', domain: 'color', value: '#2563eb' },
|
|
100
|
+
'color.secondary': { id: 'color.secondary', domain: 'color', value: '#4b5563' },
|
|
101
|
+
'color.accent': { id: 'color.accent', domain: 'color', value: '#f59e0b' },
|
|
102
|
+
|
|
103
|
+
// Theme Aware Colors
|
|
104
|
+
'surface.card': {
|
|
105
|
+
id: 'surface.card',
|
|
106
|
+
domain: 'color',
|
|
107
|
+
value: { light: '#ffffff', dark: '#1e293b' }
|
|
108
|
+
},
|
|
109
|
+
'text.base': {
|
|
110
|
+
id: 'text.base',
|
|
111
|
+
domain: 'color',
|
|
112
|
+
value: { light: '#111827', dark: '#f9fafb' }
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// Spacing
|
|
116
|
+
'space.sm': { id: 'space.sm', domain: 'layout', value: '0.5rem' },
|
|
117
|
+
'space.md': { id: 'space.md', domain: 'layout', value: '1rem' },
|
|
118
|
+
'space.lg': { id: 'space.lg', domain: 'layout', value: '1.5rem' },
|
|
119
|
+
'space.xl': { id: 'space.xl', domain: 'layout', value: '2rem' },
|
|
120
|
+
|
|
121
|
+
// Typography
|
|
122
|
+
'text.sm': { id: 'text.sm', domain: 'typography', value: '0.875rem' },
|
|
123
|
+
'text.lg': { id: 'text.lg', domain: 'typography', value: '1.125rem' },
|
|
124
|
+
'weight.bold': { id: 'weight.bold', domain: 'typography', value: '700' },
|
|
125
|
+
|
|
126
|
+
// Radius
|
|
127
|
+
'radius.md': { id: 'radius.md', domain: 'effect', value: '0.375rem' },
|
|
128
|
+
'radius.full': { id: 'radius.full', domain: 'effect', value: '9999px' },
|
|
129
|
+
};
|
|
130
|
+
`;
|
|
131
|
+
function askQuestion(query) {
|
|
132
|
+
const rl = readline.createInterface({
|
|
133
|
+
input: process.stdin,
|
|
134
|
+
output: process.stdout
|
|
135
|
+
});
|
|
136
|
+
return new Promise((resolve2) => rl.question(query, (ans) => {
|
|
137
|
+
rl.close();
|
|
138
|
+
resolve2(ans);
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
var initCommand = new import_commander.Command("init").description("Initialize a new MASE project configuration").option("-p, --postcss", "Generate postcss.config.js").option("-t, --tailwind", "Generate tailwind.config.js").option("-e, --example", "Generate example.mase file").option("-a, --atomic", "Generate mase.tokens.js for Atomic Engine").option("-y, --yes", "Skip prompts and generate all defaults").action(async (options) => {
|
|
142
|
+
const cwd = process.cwd();
|
|
143
|
+
console.log("\u{1F680} Initializing MASE project...\n");
|
|
144
|
+
let generatePostcss = options.postcss;
|
|
145
|
+
let generateTailwind = options.tailwind;
|
|
146
|
+
let generateExample = options.example;
|
|
147
|
+
let generateAtomic = options.atomic;
|
|
148
|
+
if (!options.yes && !options.postcss && !options.tailwind && !options.example && !options.atomic) {
|
|
149
|
+
const answerAtomic = await askQuestion("Generate mase.tokens.js for Atomic Engine? (Y/n) ");
|
|
150
|
+
generateAtomic = answerAtomic.toLowerCase() !== "n";
|
|
151
|
+
const answerPostcss = await askQuestion("Generate postcss.config.js? (Y/n) ");
|
|
152
|
+
generatePostcss = answerPostcss.toLowerCase() !== "n";
|
|
153
|
+
const answerTailwind = await askQuestion("Generate tailwind.config.js? (Y/n) ");
|
|
154
|
+
generateTailwind = answerTailwind.toLowerCase() !== "n";
|
|
155
|
+
const answerExample = await askQuestion("Generate example.mase? (Y/n) ");
|
|
156
|
+
generateExample = answerExample.toLowerCase() !== "n";
|
|
157
|
+
} else if (options.yes) {
|
|
158
|
+
generatePostcss = true;
|
|
159
|
+
generateTailwind = true;
|
|
160
|
+
generateExample = true;
|
|
161
|
+
generateAtomic = true;
|
|
162
|
+
}
|
|
163
|
+
if (generateAtomic) {
|
|
164
|
+
const filePath = path.join(cwd, "mase.tokens.js");
|
|
165
|
+
if (!fs.existsSync(filePath)) {
|
|
166
|
+
fs.writeFileSync(filePath, TOKENS_CONFIG);
|
|
167
|
+
console.log("\u2705 Created mase.tokens.js");
|
|
168
|
+
} else {
|
|
169
|
+
console.log("\u26A0\uFE0F mase.tokens.js already exists, skipping.");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (generatePostcss) {
|
|
173
|
+
const filePath = path.join(cwd, "postcss.config.js");
|
|
174
|
+
if (!fs.existsSync(filePath)) {
|
|
175
|
+
fs.writeFileSync(filePath, POSTCSS_CONFIG);
|
|
176
|
+
console.log("\u2705 Created postcss.config.js");
|
|
177
|
+
} else {
|
|
178
|
+
console.log("\u26A0\uFE0F postcss.config.js already exists, skipping.");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (generateTailwind) {
|
|
182
|
+
const filePath = path.join(cwd, "tailwind.config.js");
|
|
183
|
+
if (!fs.existsSync(filePath)) {
|
|
184
|
+
fs.writeFileSync(filePath, TAILWIND_CONFIG);
|
|
185
|
+
console.log("\u2705 Created tailwind.config.js");
|
|
186
|
+
} else {
|
|
187
|
+
console.log("\u26A0\uFE0F tailwind.config.js already exists, skipping.");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (generateExample) {
|
|
191
|
+
const filePath = path.join(cwd, "example.mase");
|
|
192
|
+
if (!fs.existsSync(filePath)) {
|
|
193
|
+
fs.writeFileSync(filePath, EXAMPLE_MASE);
|
|
194
|
+
console.log("\u2705 Created example.mase");
|
|
195
|
+
} else {
|
|
196
|
+
console.log("\u26A0\uFE0F example.mase already exists, skipping.");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
console.log("\n\u2728 Initialization complete!");
|
|
37
200
|
});
|
|
38
201
|
|
|
39
202
|
// src/cli/commands/compile.ts
|
|
@@ -530,10 +693,10 @@ var ContractDSLParser = class {
|
|
|
530
693
|
|
|
531
694
|
// src/dsl/validator/ValidationError.ts
|
|
532
695
|
var ValidationError = class extends Error {
|
|
533
|
-
constructor(message,
|
|
696
|
+
constructor(message, path3) {
|
|
534
697
|
super(message);
|
|
535
698
|
this.name = "ValidationError";
|
|
536
|
-
this.path =
|
|
699
|
+
this.path = path3;
|
|
537
700
|
}
|
|
538
701
|
};
|
|
539
702
|
|
|
@@ -647,22 +810,22 @@ var ContractDSLValidator = class {
|
|
|
647
810
|
* @param errors - Array to collect errors
|
|
648
811
|
*/
|
|
649
812
|
validateProperty(property, errors) {
|
|
650
|
-
const
|
|
813
|
+
const path3 = `contract.properties.${property.name}`;
|
|
651
814
|
if (!property.name || property.name.trim().length === 0) {
|
|
652
815
|
errors.push(new ValidationError(
|
|
653
816
|
"Property name cannot be empty",
|
|
654
|
-
`${
|
|
817
|
+
`${path3}.name`
|
|
655
818
|
));
|
|
656
819
|
}
|
|
657
|
-
this.validateAllows(property.allows, `${
|
|
820
|
+
this.validateAllows(property.allows, `${path3}.allows`, errors);
|
|
658
821
|
if (property.default) {
|
|
659
|
-
this.validateDefault(property.default, property.allows, `${
|
|
822
|
+
this.validateDefault(property.default, property.allows, `${path3}.default`, errors);
|
|
660
823
|
}
|
|
661
824
|
if (property.constraints) {
|
|
662
|
-
this.validateConstraints(property.constraints, property.allows, `${
|
|
825
|
+
this.validateConstraints(property.constraints, property.allows, `${path3}.constraints`, errors);
|
|
663
826
|
}
|
|
664
827
|
if (property.conflicts) {
|
|
665
|
-
this.validateConflicts(property.conflicts, `${
|
|
828
|
+
this.validateConflicts(property.conflicts, `${path3}.conflicts`, errors);
|
|
666
829
|
}
|
|
667
830
|
}
|
|
668
831
|
/**
|
|
@@ -671,11 +834,11 @@ var ContractDSLValidator = class {
|
|
|
671
834
|
* @param path - Path for error reporting
|
|
672
835
|
* @param errors - Array to collect errors
|
|
673
836
|
*/
|
|
674
|
-
validateAllows(allows,
|
|
837
|
+
validateAllows(allows, path3, errors) {
|
|
675
838
|
if (!allows || allows.length === 0) {
|
|
676
839
|
errors.push(new ValidationError(
|
|
677
840
|
"Property must have at least one allowed token",
|
|
678
|
-
|
|
841
|
+
path3
|
|
679
842
|
));
|
|
680
843
|
return;
|
|
681
844
|
}
|
|
@@ -684,11 +847,11 @@ var ContractDSLValidator = class {
|
|
|
684
847
|
if (tokenSet.has(token)) {
|
|
685
848
|
errors.push(new ValidationError(
|
|
686
849
|
`Duplicate allowed token: '${token}'`,
|
|
687
|
-
|
|
850
|
+
path3
|
|
688
851
|
));
|
|
689
852
|
}
|
|
690
853
|
tokenSet.add(token);
|
|
691
|
-
this.validateTokenFormat(token,
|
|
854
|
+
this.validateTokenFormat(token, path3, errors);
|
|
692
855
|
}
|
|
693
856
|
}
|
|
694
857
|
/**
|
|
@@ -698,12 +861,12 @@ var ContractDSLValidator = class {
|
|
|
698
861
|
* @param path - Path for error reporting
|
|
699
862
|
* @param errors - Array to collect errors
|
|
700
863
|
*/
|
|
701
|
-
validateDefault(defaultVal, allows,
|
|
702
|
-
this.validateTokenFormat(defaultVal,
|
|
864
|
+
validateDefault(defaultVal, allows, path3, errors) {
|
|
865
|
+
this.validateTokenFormat(defaultVal, path3, errors);
|
|
703
866
|
if (!allows.includes(defaultVal)) {
|
|
704
867
|
errors.push(new ValidationError(
|
|
705
868
|
`Default token '${defaultVal}' is not in allowed tokens`,
|
|
706
|
-
|
|
869
|
+
path3
|
|
707
870
|
));
|
|
708
871
|
}
|
|
709
872
|
}
|
|
@@ -714,7 +877,7 @@ var ContractDSLValidator = class {
|
|
|
714
877
|
* @param path - Path for error reporting
|
|
715
878
|
* @param errors - Array to collect errors
|
|
716
879
|
*/
|
|
717
|
-
validateConstraints(constraints, allows,
|
|
880
|
+
validateConstraints(constraints, allows, path3, errors) {
|
|
718
881
|
if (!constraints || constraints.length === 0) {
|
|
719
882
|
return;
|
|
720
883
|
}
|
|
@@ -723,11 +886,11 @@ var ContractDSLValidator = class {
|
|
|
723
886
|
if (contextSet.has(constraint.context)) {
|
|
724
887
|
errors.push(new ValidationError(
|
|
725
888
|
`Duplicate context: '${constraint.context}'`,
|
|
726
|
-
`${
|
|
889
|
+
`${path3}.${constraint.context}`
|
|
727
890
|
));
|
|
728
891
|
}
|
|
729
892
|
contextSet.add(constraint.context);
|
|
730
|
-
this.validateConstraint(constraint, allows, `${
|
|
893
|
+
this.validateConstraint(constraint, allows, `${path3}.${constraint.context}`, errors);
|
|
731
894
|
}
|
|
732
895
|
}
|
|
733
896
|
/**
|
|
@@ -737,17 +900,17 @@ var ContractDSLValidator = class {
|
|
|
737
900
|
* @param path - Path for error reporting
|
|
738
901
|
* @param errors - Array to collect errors
|
|
739
902
|
*/
|
|
740
|
-
validateConstraint(constraint, allows,
|
|
903
|
+
validateConstraint(constraint, allows, path3, errors) {
|
|
741
904
|
if (!this.validContexts.has(constraint.context)) {
|
|
742
905
|
errors.push(new ValidationError(
|
|
743
906
|
`Invalid context: '${constraint.context}'. Must be one of: ${Array.from(this.validContexts).join(", ")}`,
|
|
744
|
-
`${
|
|
907
|
+
`${path3}.context`
|
|
745
908
|
));
|
|
746
909
|
}
|
|
747
910
|
if (!constraint.allows || constraint.allows.length === 0) {
|
|
748
911
|
errors.push(new ValidationError(
|
|
749
912
|
"Constraint must have at least one allowed token",
|
|
750
|
-
`${
|
|
913
|
+
`${path3}.allows`
|
|
751
914
|
));
|
|
752
915
|
return;
|
|
753
916
|
}
|
|
@@ -755,10 +918,10 @@ var ContractDSLValidator = class {
|
|
|
755
918
|
if (!allows.includes(token)) {
|
|
756
919
|
errors.push(new ValidationError(
|
|
757
920
|
`Constraint token '${token}' is not in property allowed tokens`,
|
|
758
|
-
`${
|
|
921
|
+
`${path3}.allows`
|
|
759
922
|
));
|
|
760
923
|
}
|
|
761
|
-
this.validateTokenFormat(token, `${
|
|
924
|
+
this.validateTokenFormat(token, `${path3}.allows`, errors);
|
|
762
925
|
}
|
|
763
926
|
}
|
|
764
927
|
/**
|
|
@@ -767,7 +930,7 @@ var ContractDSLValidator = class {
|
|
|
767
930
|
* @param path - Path for error reporting
|
|
768
931
|
* @param errors - Array to collect errors
|
|
769
932
|
*/
|
|
770
|
-
validateConflicts(conflicts,
|
|
933
|
+
validateConflicts(conflicts, path3, errors) {
|
|
771
934
|
if (!conflicts || conflicts.length === 0) {
|
|
772
935
|
return;
|
|
773
936
|
}
|
|
@@ -776,14 +939,14 @@ var ContractDSLValidator = class {
|
|
|
776
939
|
if (propertySet.has(property)) {
|
|
777
940
|
errors.push(new ValidationError(
|
|
778
941
|
`Duplicate conflicting property: '${property}'`,
|
|
779
|
-
|
|
942
|
+
path3
|
|
780
943
|
));
|
|
781
944
|
}
|
|
782
945
|
propertySet.add(property);
|
|
783
946
|
if (!property || property.trim().length === 0) {
|
|
784
947
|
errors.push(new ValidationError(
|
|
785
948
|
"Conflicting property name cannot be empty",
|
|
786
|
-
|
|
949
|
+
path3
|
|
787
950
|
));
|
|
788
951
|
}
|
|
789
952
|
}
|
|
@@ -794,11 +957,11 @@ var ContractDSLValidator = class {
|
|
|
794
957
|
* @param path - Path for error reporting
|
|
795
958
|
* @param errors - Array to collect errors
|
|
796
959
|
*/
|
|
797
|
-
validateTokenFormat(token,
|
|
960
|
+
validateTokenFormat(token, path3, errors) {
|
|
798
961
|
if (!token || token.trim().length === 0) {
|
|
799
962
|
errors.push(new ValidationError(
|
|
800
963
|
"Token cannot be empty",
|
|
801
|
-
|
|
964
|
+
path3
|
|
802
965
|
));
|
|
803
966
|
return;
|
|
804
967
|
}
|
|
@@ -806,14 +969,14 @@ var ContractDSLValidator = class {
|
|
|
806
969
|
if (parts.length < 2) {
|
|
807
970
|
errors.push(new ValidationError(
|
|
808
971
|
`Invalid token format: '${token}'. Must be in format: 'category.subcategory.value'`,
|
|
809
|
-
|
|
972
|
+
path3
|
|
810
973
|
));
|
|
811
974
|
}
|
|
812
975
|
for (const part of parts) {
|
|
813
976
|
if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(part)) {
|
|
814
977
|
errors.push(new ValidationError(
|
|
815
978
|
`Invalid token part: '${part}'. Must start with a letter and contain only letters, digits, underscores, and hyphens.`,
|
|
816
|
-
|
|
979
|
+
path3
|
|
817
980
|
));
|
|
818
981
|
}
|
|
819
982
|
}
|
|
@@ -975,12 +1138,84 @@ var ContractDSLCompiler = class {
|
|
|
975
1138
|
}
|
|
976
1139
|
};
|
|
977
1140
|
|
|
1141
|
+
// src/cli/utils/ErrorHandler.ts
|
|
1142
|
+
var CLIError = class extends Error {
|
|
1143
|
+
constructor(message, code, suggestions = [], exitCode = 1) {
|
|
1144
|
+
super(message);
|
|
1145
|
+
this.code = code;
|
|
1146
|
+
this.suggestions = suggestions;
|
|
1147
|
+
this.exitCode = exitCode;
|
|
1148
|
+
this.name = "CLIError";
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
var ErrorHandler = class {
|
|
1152
|
+
static handle(error) {
|
|
1153
|
+
if (error instanceof CLIError) {
|
|
1154
|
+
console.error(`\u274C ${error.message}`);
|
|
1155
|
+
if (error.suggestions.length > 0) {
|
|
1156
|
+
console.log("\n\u{1F4A1} Suggestions:");
|
|
1157
|
+
error.suggestions.forEach((s) => {
|
|
1158
|
+
console.log(` - ${s.message}`);
|
|
1159
|
+
if (s.command) console.log(` Command: ${s.command}`);
|
|
1160
|
+
if (s.example) console.log(` Example: ${s.example}`);
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
process.exit(error.exitCode);
|
|
1164
|
+
}
|
|
1165
|
+
const code = error && typeof error === "object" ? error.code : void 0;
|
|
1166
|
+
const suggestions = code ? this.ERROR_SUGGESTIONS[code] : void 0;
|
|
1167
|
+
if (error instanceof Error) {
|
|
1168
|
+
console.error(`\u274C ${error.message}`);
|
|
1169
|
+
} else {
|
|
1170
|
+
console.error(`\u274C An unexpected error occurred: ${String(error)}`);
|
|
1171
|
+
}
|
|
1172
|
+
if (suggestions) {
|
|
1173
|
+
console.log("\n\u{1F4A1} Suggestions:");
|
|
1174
|
+
suggestions.forEach((s) => {
|
|
1175
|
+
console.log(` - ${s.message}`);
|
|
1176
|
+
if (s.example) console.log(` Example: ${s.example}`);
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
process.exit(1);
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
ErrorHandler.ERROR_SUGGESTIONS = {
|
|
1183
|
+
"ENOENT": [
|
|
1184
|
+
{
|
|
1185
|
+
message: "File not found. Check the file path and try again.",
|
|
1186
|
+
example: "mase compile ./contracts/Button.contract"
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
message: "Make sure the file exists in the specified location.",
|
|
1190
|
+
example: "ls -la ./contracts/"
|
|
1191
|
+
}
|
|
1192
|
+
],
|
|
1193
|
+
"EACCES": [
|
|
1194
|
+
{
|
|
1195
|
+
message: "Permission denied. Check file permissions.",
|
|
1196
|
+
example: "chmod +x ./contracts/Button.contract"
|
|
1197
|
+
}
|
|
1198
|
+
],
|
|
1199
|
+
"SYNTAX_ERROR": [
|
|
1200
|
+
{
|
|
1201
|
+
message: "Syntax error in contract file. Check for typos and missing brackets.",
|
|
1202
|
+
example: "contract Button {\n domain: color;\n padding {\n allows: [space.sm, space.md];\n }\n}"
|
|
1203
|
+
}
|
|
1204
|
+
],
|
|
1205
|
+
"VALIDATION_ERROR": [
|
|
1206
|
+
{
|
|
1207
|
+
message: "Contract validation failed. Check the constitution rules.",
|
|
1208
|
+
example: "mase validate ./contracts/Button.contract"
|
|
1209
|
+
}
|
|
1210
|
+
]
|
|
1211
|
+
};
|
|
1212
|
+
|
|
978
1213
|
// src/cli/commands/compile.ts
|
|
979
|
-
var
|
|
1214
|
+
var fs2 = __toESM(require("fs"));
|
|
980
1215
|
var compileCommand = new import_commander2.Command("compile").description("Compile a contract DSL file to TypeScript").argument("<contract-file>", "Contract DSL file to compile").option("-o, --output <file>", "Output file (default: stdout)").option("--format <format>", "Output format (ts, json, ast)", "ts").option("--watch", "Watch for changes", false).action((contractFile, options) => {
|
|
981
|
-
console.log(
|
|
1216
|
+
console.log(`\u2139\uFE0F Compiling ${contractFile}...`);
|
|
982
1217
|
try {
|
|
983
|
-
const source =
|
|
1218
|
+
const source = fs2.readFileSync(contractFile, "utf-8");
|
|
984
1219
|
const tokenizer = new ContractDSLTokenizer();
|
|
985
1220
|
const tokens = tokenizer.tokenize(source);
|
|
986
1221
|
const parser = new ContractDSLParser();
|
|
@@ -988,20 +1223,20 @@ var compileCommand = new import_commander2.Command("compile").description("Compi
|
|
|
988
1223
|
const validator = new ContractDSLValidator();
|
|
989
1224
|
const validationResult = validator.validate(ast);
|
|
990
1225
|
if (!validationResult.isValid) {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1226
|
+
const error = new CLIError(
|
|
1227
|
+
"Contract validation failed",
|
|
1228
|
+
"VALIDATION_ERROR"
|
|
1229
|
+
);
|
|
1230
|
+
ErrorHandler.handle(error);
|
|
996
1231
|
}
|
|
997
1232
|
const compiler = new ContractDSLCompiler();
|
|
998
1233
|
const result = compiler.compile(ast);
|
|
999
1234
|
if (result.errors.length > 0) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1235
|
+
const error = new CLIError(
|
|
1236
|
+
"Contract compilation failed",
|
|
1237
|
+
"COMPILATION_ERROR"
|
|
1238
|
+
);
|
|
1239
|
+
ErrorHandler.handle(error);
|
|
1005
1240
|
}
|
|
1006
1241
|
let output;
|
|
1007
1242
|
switch (options.format) {
|
|
@@ -1017,27 +1252,26 @@ var compileCommand = new import_commander2.Command("compile").description("Compi
|
|
|
1017
1252
|
break;
|
|
1018
1253
|
}
|
|
1019
1254
|
if (options.output) {
|
|
1020
|
-
|
|
1255
|
+
fs2.writeFileSync(options.output, output, "utf-8");
|
|
1021
1256
|
console.log(`\u2705 Compiled to ${options.output}`);
|
|
1022
1257
|
} else {
|
|
1023
1258
|
console.log(output);
|
|
1024
1259
|
}
|
|
1025
1260
|
if (options.watch) {
|
|
1026
|
-
console.log("\
|
|
1261
|
+
console.log("\u2139\uFE0F Watching for changes...");
|
|
1027
1262
|
}
|
|
1028
1263
|
} catch (error) {
|
|
1029
|
-
|
|
1030
|
-
process.exit(1);
|
|
1264
|
+
ErrorHandler.handle(error);
|
|
1031
1265
|
}
|
|
1032
1266
|
});
|
|
1033
1267
|
|
|
1034
1268
|
// src/cli/commands/validate.ts
|
|
1035
1269
|
var import_commander3 = require("commander");
|
|
1036
|
-
var
|
|
1270
|
+
var fs3 = __toESM(require("fs"));
|
|
1037
1271
|
var validateCommand = new import_commander3.Command("validate").description("Validate a contract DSL file").argument("<contract-file>", "Contract DSL file to validate").option("--strict", "Enable strict validation", false).option("--fix", "Auto-fix issues", false).option("--watch", "Watch for changes", false).action((contractFile, options) => {
|
|
1038
1272
|
console.log(`Validating ${contractFile}...`);
|
|
1039
1273
|
try {
|
|
1040
|
-
const source =
|
|
1274
|
+
const source = fs3.readFileSync(contractFile, "utf-8");
|
|
1041
1275
|
const tokenizer = new ContractDSLTokenizer();
|
|
1042
1276
|
const tokens = tokenizer.tokenize(source);
|
|
1043
1277
|
const parser = new ContractDSLParser();
|
|
@@ -1227,14 +1461,1466 @@ var CSSMaterializer = class {
|
|
|
1227
1461
|
}
|
|
1228
1462
|
};
|
|
1229
1463
|
|
|
1464
|
+
// src/integration/atomic/reset.ts
|
|
1465
|
+
var CSS_RESET = `/* MASE Grounding (Reset) */
|
|
1466
|
+
*, ::before, ::after {
|
|
1467
|
+
box-sizing: border-box;
|
|
1468
|
+
border-width: 0;
|
|
1469
|
+
border-style: solid;
|
|
1470
|
+
border-color: currentColor;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
html, :host {
|
|
1474
|
+
line-height: 1.5;
|
|
1475
|
+
-webkit-text-size-adjust: 100%;
|
|
1476
|
+
-moz-tab-size: 4;
|
|
1477
|
+
tab-size: 4;
|
|
1478
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
1479
|
+
font-feature-settings: normal;
|
|
1480
|
+
font-variation-settings: normal;
|
|
1481
|
+
-webkit-tap-highlight-color: transparent;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
body {
|
|
1485
|
+
margin: 0;
|
|
1486
|
+
line-height: inherit;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
hr {
|
|
1490
|
+
height: 0;
|
|
1491
|
+
color: inherit;
|
|
1492
|
+
border-top-width: 1px;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
abbr:where([title]) {
|
|
1496
|
+
text-decoration: underline dotted;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
h1, h2, h3, h4, h5, h6 {
|
|
1500
|
+
font-size: inherit;
|
|
1501
|
+
font-weight: inherit;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
a {
|
|
1505
|
+
color: inherit;
|
|
1506
|
+
text-decoration: inherit;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
b, strong {
|
|
1510
|
+
font-weight: bolder;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
code, kbd, samp, pre {
|
|
1514
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
1515
|
+
font-size: 1em;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
small {
|
|
1519
|
+
font-size: 80%;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
sub, sup {
|
|
1523
|
+
font-size: 75%;
|
|
1524
|
+
line-height: 0;
|
|
1525
|
+
position: relative;
|
|
1526
|
+
vertical-align: baseline;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
sub {
|
|
1530
|
+
bottom: -0.25em;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
sup {
|
|
1534
|
+
top: -0.5em;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
table {
|
|
1538
|
+
text-indent: 0;
|
|
1539
|
+
border-color: inherit;
|
|
1540
|
+
border-collapse: collapse;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
button, input, optgroup, select, textarea {
|
|
1544
|
+
font-family: inherit;
|
|
1545
|
+
font-feature-settings: inherit;
|
|
1546
|
+
font-variation-settings: inherit;
|
|
1547
|
+
font-size: 100%;
|
|
1548
|
+
font-weight: inherit;
|
|
1549
|
+
line-height: inherit;
|
|
1550
|
+
color: inherit;
|
|
1551
|
+
margin: 0;
|
|
1552
|
+
padding: 0;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
button, select {
|
|
1556
|
+
text-transform: none;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
button, [type="button"], [type="reset"], [type="submit"] {
|
|
1560
|
+
-webkit-appearance: button;
|
|
1561
|
+
background-color: transparent;
|
|
1562
|
+
background-image: none;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
:-moz-focusring {
|
|
1566
|
+
outline: auto;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
:-moz-ui-invalid {
|
|
1570
|
+
box-shadow: none;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
progress {
|
|
1574
|
+
vertical-align: baseline;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
|
|
1578
|
+
height: auto;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
[type="search"] {
|
|
1582
|
+
-webkit-appearance: textfield;
|
|
1583
|
+
outline-offset: -2px;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
::-webkit-search-decoration {
|
|
1587
|
+
-webkit-appearance: none;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
::-webkit-file-upload-button {
|
|
1591
|
+
-webkit-appearance: button;
|
|
1592
|
+
font: inherit;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
summary {
|
|
1596
|
+
display: list-item;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre {
|
|
1600
|
+
margin: 0;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
fieldset {
|
|
1604
|
+
margin: 0;
|
|
1605
|
+
padding: 0;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
legend {
|
|
1609
|
+
padding: 0;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
ol, ul, menu {
|
|
1613
|
+
list-style: none;
|
|
1614
|
+
margin: 0;
|
|
1615
|
+
padding: 0;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
textarea {
|
|
1619
|
+
resize: vertical;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
input::placeholder, textarea::placeholder {
|
|
1623
|
+
opacity: 1;
|
|
1624
|
+
color: #9ca3af;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
button, [role="button"] {
|
|
1628
|
+
cursor: pointer;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
:disabled {
|
|
1632
|
+
cursor: default;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
img, svg, video, canvas, audio, iframe, embed, object {
|
|
1636
|
+
display: block;
|
|
1637
|
+
vertical-align: middle;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
img, video {
|
|
1641
|
+
max-width: 100%;
|
|
1642
|
+
height: auto;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
[hidden] {
|
|
1646
|
+
display: none;
|
|
1647
|
+
}
|
|
1648
|
+
`;
|
|
1649
|
+
|
|
1650
|
+
// src/integration/atomic/core/BaseGenerator.ts
|
|
1651
|
+
var BaseGenerator = class {
|
|
1652
|
+
generate(includeReset = true) {
|
|
1653
|
+
if (!includeReset) return "";
|
|
1654
|
+
let css = "@layer base {\n";
|
|
1655
|
+
css += CSS_RESET;
|
|
1656
|
+
css += "}\n\n";
|
|
1657
|
+
return css;
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
|
|
1661
|
+
// src/integration/shared/utils.ts
|
|
1662
|
+
function formatVarName(tokenId, prefix = "--") {
|
|
1663
|
+
return `${prefix}${tokenId.replace(/\./g, "-")}`;
|
|
1664
|
+
}
|
|
1665
|
+
function formatSuffix(tokenId) {
|
|
1666
|
+
const parts = tokenId.split(".");
|
|
1667
|
+
return parts.length > 1 ? parts.slice(1).join("-") : parts[0];
|
|
1668
|
+
}
|
|
1669
|
+
function isThemeAware(value) {
|
|
1670
|
+
return typeof value === "object" && value !== null;
|
|
1671
|
+
}
|
|
1672
|
+
function getLightValue(token) {
|
|
1673
|
+
if (typeof token.value === "string") {
|
|
1674
|
+
return token.value;
|
|
1675
|
+
}
|
|
1676
|
+
if (isThemeAware(token.value)) {
|
|
1677
|
+
return token.value.light || Object.values(token.value)[0] || "";
|
|
1678
|
+
}
|
|
1679
|
+
return String(token.value);
|
|
1680
|
+
}
|
|
1681
|
+
function getDarkValue(token) {
|
|
1682
|
+
if (typeof token.value === "string") {
|
|
1683
|
+
return null;
|
|
1684
|
+
}
|
|
1685
|
+
if (isThemeAware(token.value)) {
|
|
1686
|
+
return token.value.dark || null;
|
|
1687
|
+
}
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
function createVarDeclaration(varName, value) {
|
|
1691
|
+
return `${varName}: ${value}`;
|
|
1692
|
+
}
|
|
1693
|
+
function createVarReference(varName) {
|
|
1694
|
+
return `var(${varName})`;
|
|
1695
|
+
}
|
|
1696
|
+
function isValidToken(token) {
|
|
1697
|
+
return !!token && !!token.id && token.value !== void 0 && token.value !== null;
|
|
1698
|
+
}
|
|
1699
|
+
function filterValidTokens(tokens) {
|
|
1700
|
+
return Object.values(tokens).filter(isValidToken);
|
|
1701
|
+
}
|
|
1702
|
+
function createCssRule(selector, properties, indent = 2) {
|
|
1703
|
+
if (properties.length === 0) return "";
|
|
1704
|
+
const spaces = " ".repeat(indent);
|
|
1705
|
+
const props = properties.map((p) => `${spaces}${p}`).join("\n");
|
|
1706
|
+
return `${selector} {
|
|
1707
|
+
${props}
|
|
1708
|
+
}`;
|
|
1709
|
+
}
|
|
1710
|
+
function createLayer(layerName, content) {
|
|
1711
|
+
return `@layer ${layerName} {
|
|
1712
|
+
${content}
|
|
1713
|
+
}
|
|
1714
|
+
`;
|
|
1715
|
+
}
|
|
1716
|
+
function sanitizeCssValue(value) {
|
|
1717
|
+
return value.replace(/[<>{}]/g, "");
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// src/integration/atomic/core/ThemeGenerator.ts
|
|
1721
|
+
var ThemeGenerator = class {
|
|
1722
|
+
/**
|
|
1723
|
+
* Mini function: Get prefix with CSS variable prefix
|
|
1724
|
+
*/
|
|
1725
|
+
getFullPrefix(context) {
|
|
1726
|
+
return `--${context.prefix}`;
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Mini function: Get dark mode selector
|
|
1730
|
+
*/
|
|
1731
|
+
getDarkModeSelector() {
|
|
1732
|
+
return '[data-theme="dark"]';
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* Mini function: Process a single token
|
|
1736
|
+
*/
|
|
1737
|
+
processToken(token, fullPrefix) {
|
|
1738
|
+
if (!isValidToken(token)) {
|
|
1739
|
+
return { default: "" };
|
|
1740
|
+
}
|
|
1741
|
+
const varName = formatVarName(token.id, fullPrefix);
|
|
1742
|
+
const lightValue = getLightValue(token);
|
|
1743
|
+
const darkValue = getDarkValue(token);
|
|
1744
|
+
const result = {
|
|
1745
|
+
default: createVarDeclaration(varName, lightValue)
|
|
1746
|
+
};
|
|
1747
|
+
if (darkValue) {
|
|
1748
|
+
result.dark = createVarDeclaration(varName, darkValue);
|
|
1749
|
+
}
|
|
1750
|
+
return result;
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Mini function: Collect variable declarations from tokens
|
|
1754
|
+
*/
|
|
1755
|
+
collectVariables(tokens, fullPrefix) {
|
|
1756
|
+
const validTokens = filterValidTokens(tokens);
|
|
1757
|
+
const defaultVars = [];
|
|
1758
|
+
const darkVars = [];
|
|
1759
|
+
validTokens.forEach((token) => {
|
|
1760
|
+
const processed = this.processToken(token, fullPrefix);
|
|
1761
|
+
if (processed.default) {
|
|
1762
|
+
defaultVars.push(` ${processed.default};`);
|
|
1763
|
+
}
|
|
1764
|
+
if (processed.dark) {
|
|
1765
|
+
darkVars.push(` ${processed.dark};`);
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1768
|
+
return { default: defaultVars, dark: darkVars };
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Mini function: Generate default theme block
|
|
1772
|
+
*/
|
|
1773
|
+
generateDefaultBlock(selector, vars) {
|
|
1774
|
+
if (vars.length === 0) return "";
|
|
1775
|
+
return createCssRule(selector, vars, 2);
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Mini function: Generate dark mode block
|
|
1779
|
+
*/
|
|
1780
|
+
generateDarkModeBlock(vars) {
|
|
1781
|
+
if (vars.length === 0) return "";
|
|
1782
|
+
return createCssRule(this.getDarkModeSelector(), vars, 2);
|
|
1783
|
+
}
|
|
1784
|
+
generate(tokens, context) {
|
|
1785
|
+
const fullPrefix = this.getFullPrefix(context);
|
|
1786
|
+
const { default: defaultVars, dark: darkVars } = this.collectVariables(tokens, fullPrefix);
|
|
1787
|
+
let content = "";
|
|
1788
|
+
content += this.generateDefaultBlock(context.selector, defaultVars);
|
|
1789
|
+
content += "\n";
|
|
1790
|
+
content += this.generateDarkModeBlock(darkVars);
|
|
1791
|
+
return createLayer("theme", content);
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
|
|
1795
|
+
// src/integration/atomic/handlers/BaseHandler.ts
|
|
1796
|
+
var BaseHandler = class {
|
|
1797
|
+
/**
|
|
1798
|
+
* Mini function: Validate token before processing
|
|
1799
|
+
*/
|
|
1800
|
+
validateToken(token) {
|
|
1801
|
+
return isValidToken(token);
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* Mini function: Sanitize CSS value
|
|
1805
|
+
*/
|
|
1806
|
+
sanitize(value) {
|
|
1807
|
+
return sanitizeCssValue(value);
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Mini function: Create CSS rule
|
|
1811
|
+
*/
|
|
1812
|
+
createRule(selector, property, value) {
|
|
1813
|
+
const sanitizedValue = this.sanitize(value);
|
|
1814
|
+
return ` .${selector} { ${property}: ${sanitizedValue}; }
|
|
1815
|
+
`;
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Mini function: Create multiple CSS rules
|
|
1819
|
+
*/
|
|
1820
|
+
createRules(rules) {
|
|
1821
|
+
return rules.map((rule) => this.createRule(rule.selector, rule.property, rule.value)).join("");
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Mini function: Extract suffix from token ID
|
|
1825
|
+
*/
|
|
1826
|
+
extractSuffix(tokenId) {
|
|
1827
|
+
const parts = tokenId.split(".");
|
|
1828
|
+
return parts.length > 1 ? parts.slice(1).join("-") : parts[0];
|
|
1829
|
+
}
|
|
1830
|
+
/**
|
|
1831
|
+
* Mini function: Check if token ID contains a keyword
|
|
1832
|
+
*/
|
|
1833
|
+
containsKeyword(tokenId, keyword) {
|
|
1834
|
+
return tokenId.toLowerCase().includes(keyword.toLowerCase());
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* Mini function: Generate directional rules (top, right, bottom, left, x, y, all)
|
|
1838
|
+
* DRY implementation for properties that have directional variants
|
|
1839
|
+
*/
|
|
1840
|
+
generateDirectionalRules(baseSelector, suffix, value, mappings) {
|
|
1841
|
+
const rules = [];
|
|
1842
|
+
for (const [dir, prop] of Object.entries(mappings)) {
|
|
1843
|
+
const selectorPart = dir ? `${baseSelector}${dir}` : baseSelector;
|
|
1844
|
+
const fullSelector = `${selectorPart}-${suffix}`;
|
|
1845
|
+
if (Array.isArray(prop)) {
|
|
1846
|
+
prop.forEach((p) => rules.push({ selector: fullSelector, property: p, value }));
|
|
1847
|
+
} else {
|
|
1848
|
+
rules.push({ selector: fullSelector, property: prop, value });
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
return this.createRules(rules);
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Mini function: Generate rules by matching token ID against a prefix map (Scenario B)
|
|
1855
|
+
* Use this when the token ID itself dictates the property (e.g. 'p-4' -> padding, 'mt-4' -> margin-top)
|
|
1856
|
+
*/
|
|
1857
|
+
generateRulesFromPrefix(token, varName, suffix, prefixMap) {
|
|
1858
|
+
const prefixes = Object.keys(prefixMap).sort((a, b) => b.length - a.length);
|
|
1859
|
+
for (const prefix of prefixes) {
|
|
1860
|
+
console.log("Checking prefix:", prefix);
|
|
1861
|
+
console.log("Token ID:", token.id);
|
|
1862
|
+
console.log("Suffix:", suffix);
|
|
1863
|
+
console.log("Match ID:", token.id === prefix || token.id.startsWith(`${prefix}-`));
|
|
1864
|
+
console.log("Match Suffix:", suffix === prefix || suffix.startsWith(`${prefix}-`));
|
|
1865
|
+
if (token.id === prefix || token.id.startsWith(`${prefix}-`) || suffix === prefix || suffix.startsWith(`${prefix}-`)) {
|
|
1866
|
+
const definition = prefixMap[prefix];
|
|
1867
|
+
const selector = suffix;
|
|
1868
|
+
if (typeof definition === "string") {
|
|
1869
|
+
return this.createRule(selector, definition, varName);
|
|
1870
|
+
} else if (Array.isArray(definition)) {
|
|
1871
|
+
return this.createRules(definition.map((p) => ({ selector, property: p, value: varName })));
|
|
1872
|
+
} else {
|
|
1873
|
+
const val = definition.valueTransform ? definition.valueTransform(varName) : varName;
|
|
1874
|
+
return this.createRule(selector, definition.property, val);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
return "";
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Mini function: Generate utilities based on a configuration map
|
|
1882
|
+
* This implements DRY by allowing handlers to define mappings instead of writing repetitive if-blocks.
|
|
1883
|
+
*/
|
|
1884
|
+
generateUtilities(token, varName, suffix, config) {
|
|
1885
|
+
const rules = [];
|
|
1886
|
+
for (const [match, definition] of Object.entries(config)) {
|
|
1887
|
+
if (this.containsKeyword(token.id, match)) {
|
|
1888
|
+
let property;
|
|
1889
|
+
let classPrefix;
|
|
1890
|
+
let valueMap;
|
|
1891
|
+
let valueTransform;
|
|
1892
|
+
if (typeof definition === "string") {
|
|
1893
|
+
property = definition;
|
|
1894
|
+
classPrefix = match;
|
|
1895
|
+
} else {
|
|
1896
|
+
property = definition.property;
|
|
1897
|
+
classPrefix = definition.classPrefix !== void 0 ? definition.classPrefix : match;
|
|
1898
|
+
valueMap = definition.valueMap;
|
|
1899
|
+
valueTransform = definition.valueTransform;
|
|
1900
|
+
}
|
|
1901
|
+
let finalSuffix = suffix;
|
|
1902
|
+
if (classPrefix === match && finalSuffix.startsWith(match)) {
|
|
1903
|
+
finalSuffix = finalSuffix.substring(match.length);
|
|
1904
|
+
if (finalSuffix.startsWith("-")) {
|
|
1905
|
+
finalSuffix = finalSuffix.substring(1);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
if (valueMap && valueMap[finalSuffix]) {
|
|
1909
|
+
finalSuffix = valueMap[finalSuffix];
|
|
1910
|
+
}
|
|
1911
|
+
const selector = finalSuffix ? classPrefix ? `${classPrefix}-${finalSuffix}` : finalSuffix : classPrefix || match;
|
|
1912
|
+
let finalValue = varName;
|
|
1913
|
+
if (valueTransform) {
|
|
1914
|
+
finalValue = valueTransform(varName);
|
|
1915
|
+
}
|
|
1916
|
+
rules.push({ selector, property, value: finalValue });
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
return this.createRules(rules);
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
|
|
1923
|
+
// src/integration/atomic/handlers/ColorHandler.ts
|
|
1924
|
+
var ColorHandler = class extends BaseHandler {
|
|
1925
|
+
canHandle(token) {
|
|
1926
|
+
return this.validateToken(token) && token.domain === "color" && !this.containsKeyword(token.id, "border") && !this.containsKeyword(token.id, "outline") && !this.containsKeyword(token.id, "ring");
|
|
1927
|
+
}
|
|
1928
|
+
generate(token, varName, suffix) {
|
|
1929
|
+
if (!this.validateToken(token)) {
|
|
1930
|
+
return "";
|
|
1931
|
+
}
|
|
1932
|
+
const standardRules = this.createRules([
|
|
1933
|
+
// Text Color
|
|
1934
|
+
{ selector: `text-${suffix}`, property: "color", value: varName },
|
|
1935
|
+
// Background Color
|
|
1936
|
+
{ selector: `bg-${suffix}`, property: "background-color", value: varName },
|
|
1937
|
+
// Outline Color
|
|
1938
|
+
{ selector: `outline-${suffix}`, property: "outline-color", value: varName },
|
|
1939
|
+
// Ring Color (Simplified)
|
|
1940
|
+
{ selector: `ring-${suffix}`, property: "--tw-ring-color", value: varName },
|
|
1941
|
+
{ selector: `ring-offset-${suffix}`, property: "outline-color", value: varName },
|
|
1942
|
+
// Decoration Color
|
|
1943
|
+
{ selector: `decoration-${suffix}`, property: "text-decoration-color", value: varName },
|
|
1944
|
+
// Accent Color
|
|
1945
|
+
{ selector: `accent-${suffix}`, property: "accent-color", value: varName },
|
|
1946
|
+
// Caret Color
|
|
1947
|
+
{ selector: `caret-${suffix}`, property: "caret-color", value: varName },
|
|
1948
|
+
// Placeholder Color
|
|
1949
|
+
{ selector: `placeholder-${suffix}::placeholder`, property: "color", value: varName }
|
|
1950
|
+
]);
|
|
1951
|
+
const borderRules = this.generateDirectionalRules("border", suffix, varName, {
|
|
1952
|
+
"": "border-color",
|
|
1953
|
+
"-t": "border-top-color",
|
|
1954
|
+
"-r": "border-right-color",
|
|
1955
|
+
"-b": "border-bottom-color",
|
|
1956
|
+
"-l": "border-left-color",
|
|
1957
|
+
"-x": ["border-left-color", "border-right-color"],
|
|
1958
|
+
"-y": ["border-top-color", "border-bottom-color"]
|
|
1959
|
+
});
|
|
1960
|
+
return standardRules + borderRules;
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1963
|
+
|
|
1964
|
+
// src/integration/atomic/handlers/SpaceHandler.ts
|
|
1965
|
+
var SpaceHandler = class extends BaseHandler {
|
|
1966
|
+
canHandle(token) {
|
|
1967
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "padding") || this.containsKeyword(token.id, "margin") || this.containsKeyword(token.id, "width") || this.containsKeyword(token.id, "height") || this.containsKeyword(token.id, "gap") || this.containsKeyword(token.id, "space") || // Shorthand prefixes (simplified check)
|
|
1968
|
+
/^[pmwh]([xytrbl]|in|ax)?-?/.test(token.id) || /^gap-?/.test(token.id) || /^space-?/.test(token.id));
|
|
1969
|
+
}
|
|
1970
|
+
generate(token, varName, suffix) {
|
|
1971
|
+
if (!this.validateToken(token)) return "";
|
|
1972
|
+
if (token.id.startsWith("space-x")) {
|
|
1973
|
+
return this.createRule(`space-x-${this.extractValueSuffix(token.id)}`, "margin-left", `calc(${varName} * var(--tw-space-x-reverse))`);
|
|
1974
|
+
}
|
|
1975
|
+
if (token.id.startsWith("space-y")) {
|
|
1976
|
+
return this.createRule(`space-y-${this.extractValueSuffix(token.id)}`, "margin-top", `calc(${varName} * var(--tw-space-y-reverse))`);
|
|
1977
|
+
}
|
|
1978
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
1979
|
+
// Padding
|
|
1980
|
+
"pt": "padding-top",
|
|
1981
|
+
"pr": "padding-right",
|
|
1982
|
+
"pb": "padding-bottom",
|
|
1983
|
+
"pl": "padding-left",
|
|
1984
|
+
"px": ["padding-left", "padding-right"],
|
|
1985
|
+
"py": ["padding-top", "padding-bottom"],
|
|
1986
|
+
"p": "padding",
|
|
1987
|
+
// Margin
|
|
1988
|
+
"mt": "margin-top",
|
|
1989
|
+
"mr": "margin-right",
|
|
1990
|
+
"mb": "margin-bottom",
|
|
1991
|
+
"ml": "margin-left",
|
|
1992
|
+
"mx": ["margin-left", "margin-right"],
|
|
1993
|
+
"my": ["margin-top", "margin-bottom"],
|
|
1994
|
+
"m": "margin",
|
|
1995
|
+
// Width/Height
|
|
1996
|
+
"min-w": "min-width",
|
|
1997
|
+
"max-w": "max-width",
|
|
1998
|
+
"min-h": "min-height",
|
|
1999
|
+
"max-h": "max-height",
|
|
2000
|
+
"w": "width",
|
|
2001
|
+
"h": "height",
|
|
2002
|
+
// Gap
|
|
2003
|
+
"gap-x": "column-gap",
|
|
2004
|
+
"gap-y": "row-gap",
|
|
2005
|
+
"gap": "gap"
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Helper to extract the value part of the suffix for space-x/y
|
|
2010
|
+
* e.g. space-x-4 -> 4
|
|
2011
|
+
*/
|
|
2012
|
+
extractValueSuffix(id) {
|
|
2013
|
+
const parts = id.split("-");
|
|
2014
|
+
return parts.slice(2).join("-");
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
|
|
2018
|
+
// src/integration/atomic/handlers/TypographyHandler.ts
|
|
2019
|
+
var TypographyHandler = class extends BaseHandler {
|
|
2020
|
+
canHandle(token) {
|
|
2021
|
+
return this.validateToken(token) && token.domain === "typography";
|
|
2022
|
+
}
|
|
2023
|
+
generate(token, varName, suffix) {
|
|
2024
|
+
if (!this.validateToken(token)) {
|
|
2025
|
+
return "";
|
|
2026
|
+
}
|
|
2027
|
+
const fullSuffix = token.id.replace(/\./g, "-");
|
|
2028
|
+
const rules = this.generateRulesFromPrefix(token, varName, fullSuffix, {
|
|
2029
|
+
// Font Weight
|
|
2030
|
+
"font-thin": { property: "font-weight" },
|
|
2031
|
+
"font-extralight": { property: "font-weight" },
|
|
2032
|
+
"font-light": { property: "font-weight" },
|
|
2033
|
+
"font-normal": { property: "font-weight" },
|
|
2034
|
+
"font-medium": { property: "font-weight" },
|
|
2035
|
+
"font-semibold": { property: "font-weight" },
|
|
2036
|
+
"font-bold": { property: "font-weight" },
|
|
2037
|
+
"font-extrabold": { property: "font-weight" },
|
|
2038
|
+
"font-black": { property: "font-weight" },
|
|
2039
|
+
// Font Family
|
|
2040
|
+
"font-sans": { property: "font-family" },
|
|
2041
|
+
"font-serif": { property: "font-family" },
|
|
2042
|
+
"font-mono": { property: "font-family" },
|
|
2043
|
+
// Font Size
|
|
2044
|
+
"text": { property: "font-size" },
|
|
2045
|
+
// Line Height
|
|
2046
|
+
"leading": { property: "line-height" },
|
|
2047
|
+
// Letter Spacing
|
|
2048
|
+
"tracking": { property: "letter-spacing" },
|
|
2049
|
+
// Font Style
|
|
2050
|
+
"italic": { property: "font-style", valueTransform: () => "italic" },
|
|
2051
|
+
"not-italic": { property: "font-style", valueTransform: () => "normal" },
|
|
2052
|
+
// Text Decoration
|
|
2053
|
+
"underline": { property: "text-decoration-line", valueTransform: () => "underline" },
|
|
2054
|
+
"no-underline": { property: "text-decoration-line", valueTransform: () => "none" },
|
|
2055
|
+
"line-through": { property: "text-decoration-line", valueTransform: () => "line-through" },
|
|
2056
|
+
"decoration": { property: "text-decoration-line" },
|
|
2057
|
+
// Text Transform
|
|
2058
|
+
"uppercase": { property: "text-transform", valueTransform: () => "uppercase" },
|
|
2059
|
+
"lowercase": { property: "text-transform", valueTransform: () => "lowercase" },
|
|
2060
|
+
"capitalize": { property: "text-transform", valueTransform: () => "capitalize" },
|
|
2061
|
+
"normal-case": { property: "text-transform", valueTransform: () => "none" }
|
|
2062
|
+
});
|
|
2063
|
+
if (rules) return rules;
|
|
2064
|
+
return "";
|
|
2065
|
+
}
|
|
2066
|
+
};
|
|
2067
|
+
|
|
2068
|
+
// src/integration/atomic/handlers/EffectHandler.ts
|
|
2069
|
+
var EffectHandler = class extends BaseHandler {
|
|
2070
|
+
canHandle(token) {
|
|
2071
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "shadow") || this.containsKeyword(token.id, "opacity"));
|
|
2072
|
+
}
|
|
2073
|
+
generate(token, varName, suffix) {
|
|
2074
|
+
if (!this.validateToken(token)) {
|
|
2075
|
+
return "";
|
|
2076
|
+
}
|
|
2077
|
+
if (this.containsKeyword(token.id, "drop-shadow")) return "";
|
|
2078
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2079
|
+
"shadow": "box-shadow",
|
|
2080
|
+
"opacity": "opacity"
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
};
|
|
2084
|
+
|
|
2085
|
+
// src/integration/atomic/handlers/LayoutHandler.ts
|
|
2086
|
+
var LayoutHandler = class extends BaseHandler {
|
|
2087
|
+
canHandle(token) {
|
|
2088
|
+
if (this.containsKeyword(token.id, "background")) return false;
|
|
2089
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "display") || this.containsKeyword(token.id, "position") || this.containsKeyword(token.id, "visibility") || this.containsKeyword(token.id, "z-index") || this.containsKeyword(token.id, "overflow") || this.containsKeyword(token.id, "isolation") || this.containsKeyword(token.id, "object") || // Positioning
|
|
2090
|
+
this.containsKeyword(token.id, "inset") || this.containsKeyword(token.id, "top") || this.containsKeyword(token.id, "right") || this.containsKeyword(token.id, "bottom") || this.containsKeyword(token.id, "left") || // Specific values for display/position/visibility
|
|
2091
|
+
this.containsKeyword(token.id, "block") || this.containsKeyword(token.id, "inline") || this.containsKeyword(token.id, "hidden") || this.containsKeyword(token.id, "static") || this.containsKeyword(token.id, "fixed") || this.containsKeyword(token.id, "absolute") || this.containsKeyword(token.id, "relative") || this.containsKeyword(token.id, "sticky") || this.containsKeyword(token.id, "visible") || this.containsKeyword(token.id, "invisible") || this.containsKeyword(token.id, "isolate") || this.containsKeyword(token.id, "z-"));
|
|
2092
|
+
}
|
|
2093
|
+
generate(token, varName, suffix) {
|
|
2094
|
+
if (!this.validateToken(token)) {
|
|
2095
|
+
return "";
|
|
2096
|
+
}
|
|
2097
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2098
|
+
// Positioning
|
|
2099
|
+
"inset-x": ["left", "right"],
|
|
2100
|
+
"inset-y": ["top", "bottom"],
|
|
2101
|
+
"inset": ["top", "right", "bottom", "left"],
|
|
2102
|
+
"top": "top",
|
|
2103
|
+
"right": "right",
|
|
2104
|
+
"bottom": "bottom",
|
|
2105
|
+
"left": "left",
|
|
2106
|
+
// Display
|
|
2107
|
+
"block": { property: "display", valueTransform: () => "block" },
|
|
2108
|
+
"inline-block": { property: "display", valueTransform: () => "inline-block" },
|
|
2109
|
+
"inline-flex": { property: "display", valueTransform: () => "inline-flex" },
|
|
2110
|
+
"flex": { property: "display", valueTransform: () => "flex" },
|
|
2111
|
+
"inline-grid": { property: "display", valueTransform: () => "inline-grid" },
|
|
2112
|
+
"grid": { property: "display", valueTransform: () => "grid" },
|
|
2113
|
+
"hidden": { property: "display", valueTransform: () => "none" },
|
|
2114
|
+
"display": { property: "display" },
|
|
2115
|
+
// Position
|
|
2116
|
+
"static": { property: "position", valueTransform: () => "static" },
|
|
2117
|
+
"fixed": { property: "position", valueTransform: () => "fixed" },
|
|
2118
|
+
"absolute": { property: "position", valueTransform: () => "absolute" },
|
|
2119
|
+
"relative": { property: "position", valueTransform: () => "relative" },
|
|
2120
|
+
"sticky": { property: "position", valueTransform: () => "sticky" },
|
|
2121
|
+
"position": { property: "position" },
|
|
2122
|
+
// Visibility
|
|
2123
|
+
"visible": { property: "visibility", valueTransform: () => "visible" },
|
|
2124
|
+
"invisible": { property: "visibility", valueTransform: () => "hidden" },
|
|
2125
|
+
"visibility": { property: "visibility" },
|
|
2126
|
+
// Z-Index
|
|
2127
|
+
"z": "z-index",
|
|
2128
|
+
"z-index": "z-index",
|
|
2129
|
+
// Overflow
|
|
2130
|
+
"overflow-x": "overflow-x",
|
|
2131
|
+
"overflow-y": "overflow-y",
|
|
2132
|
+
"overflow": "overflow",
|
|
2133
|
+
// Isolation
|
|
2134
|
+
"isolate": { property: "isolation", valueTransform: () => "isolate" },
|
|
2135
|
+
"isolation-auto": { property: "isolation", valueTransform: () => "auto" },
|
|
2136
|
+
"isolation": "isolation",
|
|
2137
|
+
// Object Fit
|
|
2138
|
+
"object-contain": { property: "object-fit", valueTransform: () => "contain" },
|
|
2139
|
+
"object-cover": { property: "object-fit", valueTransform: () => "cover" },
|
|
2140
|
+
"object-fill": { property: "object-fit", valueTransform: () => "fill" },
|
|
2141
|
+
"object-none": { property: "object-fit", valueTransform: () => "none" },
|
|
2142
|
+
"object-scale-down": { property: "object-fit", valueTransform: () => "scale-down" },
|
|
2143
|
+
// Object Position (fallback for other object-*)
|
|
2144
|
+
"object": { property: "object-position" }
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
|
|
2149
|
+
// src/integration/atomic/handlers/FlexboxHandler.ts
|
|
2150
|
+
var FlexboxHandler = class extends BaseHandler {
|
|
2151
|
+
canHandle(token) {
|
|
2152
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "flex") || this.containsKeyword(token.id, "justify") || this.containsKeyword(token.id, "align") || this.containsKeyword(token.id, "place") || this.containsKeyword(token.id, "order") && !this.containsKeyword(token.id, "border"));
|
|
2153
|
+
}
|
|
2154
|
+
generate(token, varName, suffix) {
|
|
2155
|
+
if (!this.validateToken(token)) {
|
|
2156
|
+
return "";
|
|
2157
|
+
}
|
|
2158
|
+
if (token.id === "flex") {
|
|
2159
|
+
return this.createRule(suffix, "display", "flex");
|
|
2160
|
+
}
|
|
2161
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2162
|
+
// Flex Direction
|
|
2163
|
+
"flex-row": { property: "flex-direction", valueTransform: () => "row" },
|
|
2164
|
+
"flex-row-reverse": { property: "flex-direction", valueTransform: () => "row-reverse" },
|
|
2165
|
+
"flex-col": { property: "flex-direction", valueTransform: () => "column" },
|
|
2166
|
+
"flex-col-reverse": { property: "flex-direction", valueTransform: () => "column-reverse" },
|
|
2167
|
+
// Flex Wrap
|
|
2168
|
+
"flex-wrap": { property: "flex-wrap", valueTransform: () => "wrap" },
|
|
2169
|
+
"flex-wrap-reverse": { property: "flex-wrap", valueTransform: () => "wrap-reverse" },
|
|
2170
|
+
"flex-nowrap": { property: "flex-wrap", valueTransform: () => "nowrap" },
|
|
2171
|
+
// Flex Grow/Shrink/Basis
|
|
2172
|
+
"flex-grow": "flex-grow",
|
|
2173
|
+
"grow": "flex-grow",
|
|
2174
|
+
// Alias
|
|
2175
|
+
"flex-shrink": "flex-shrink",
|
|
2176
|
+
"shrink": "flex-shrink",
|
|
2177
|
+
// Alias
|
|
2178
|
+
"flex-basis": "flex-basis",
|
|
2179
|
+
"basis": "flex-basis",
|
|
2180
|
+
// Alias
|
|
2181
|
+
// Alignment
|
|
2182
|
+
"justify-content": "justify-content",
|
|
2183
|
+
"justify-items": "justify-items",
|
|
2184
|
+
"justify-self": "justify-self",
|
|
2185
|
+
"justify": "justify-content",
|
|
2186
|
+
// Short alias
|
|
2187
|
+
"align-content": "align-content",
|
|
2188
|
+
"align-items": "align-items",
|
|
2189
|
+
"align-self": "align-self",
|
|
2190
|
+
"align": "align-items",
|
|
2191
|
+
// Short alias
|
|
2192
|
+
"items": "align-items",
|
|
2193
|
+
// Tailwind alias
|
|
2194
|
+
"content": "align-content",
|
|
2195
|
+
// Tailwind alias
|
|
2196
|
+
"self": "align-self",
|
|
2197
|
+
// Tailwind alias
|
|
2198
|
+
"place-content": "place-content",
|
|
2199
|
+
"place-items": "place-items",
|
|
2200
|
+
"place-self": "place-self",
|
|
2201
|
+
"order": "order",
|
|
2202
|
+
// Generic Flex (e.g. flex-1, flex-auto)
|
|
2203
|
+
// Must be checked after specific flex-* properties (handled by length sort in generateRulesFromPrefix)
|
|
2204
|
+
"flex": "flex"
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
};
|
|
2208
|
+
|
|
2209
|
+
// src/integration/atomic/handlers/GridHandler.ts
|
|
2210
|
+
var GridHandler = class extends BaseHandler {
|
|
2211
|
+
canHandle(token) {
|
|
2212
|
+
if (token.id.startsWith("flex-")) {
|
|
2213
|
+
return false;
|
|
2214
|
+
}
|
|
2215
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "grid") || this.containsKeyword(token.id, "col") && !this.containsKeyword(token.id, "color") && !this.containsKeyword(token.id, "collapse") || this.containsKeyword(token.id, "row"));
|
|
2216
|
+
}
|
|
2217
|
+
generate(token, varName, suffix) {
|
|
2218
|
+
if (!this.validateToken(token)) {
|
|
2219
|
+
return "";
|
|
2220
|
+
}
|
|
2221
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2222
|
+
// Grid Template
|
|
2223
|
+
"grid-cols": "grid-template-columns",
|
|
2224
|
+
"grid-rows": "grid-template-rows",
|
|
2225
|
+
"grid-template-columns": "grid-template-columns",
|
|
2226
|
+
"grid-template-rows": "grid-template-rows",
|
|
2227
|
+
// Auto Flow/Cols/Rows
|
|
2228
|
+
"grid-flow": "grid-auto-flow",
|
|
2229
|
+
"auto-cols": "grid-auto-columns",
|
|
2230
|
+
"auto-rows": "grid-auto-rows",
|
|
2231
|
+
"grid-auto-flow": "grid-auto-flow",
|
|
2232
|
+
"grid-auto-columns": "grid-auto-columns",
|
|
2233
|
+
"grid-auto-rows": "grid-auto-rows",
|
|
2234
|
+
// Grid Column
|
|
2235
|
+
"col-start": "grid-column-start",
|
|
2236
|
+
"col-end": "grid-column-end",
|
|
2237
|
+
"col": "grid-column",
|
|
2238
|
+
// Fallback for col-span-* or generic col-*
|
|
2239
|
+
// Grid Row
|
|
2240
|
+
"row-start": "grid-row-start",
|
|
2241
|
+
"row-end": "grid-row-end",
|
|
2242
|
+
"row": "grid-row",
|
|
2243
|
+
// Display Grid
|
|
2244
|
+
"grid": { property: "display", valueTransform: () => "grid" }
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2249
|
+
// src/integration/atomic/handlers/BackgroundHandler.ts
|
|
2250
|
+
var BackgroundHandler = class extends BaseHandler {
|
|
2251
|
+
canHandle(token) {
|
|
2252
|
+
if (token.domain === "color") {
|
|
2253
|
+
return false;
|
|
2254
|
+
}
|
|
2255
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "background") || this.containsKeyword(token.id, "bg") || this.containsKeyword(token.id, "gradient"));
|
|
2256
|
+
}
|
|
2257
|
+
generate(token, varName, suffix) {
|
|
2258
|
+
if (!this.validateToken(token)) {
|
|
2259
|
+
return "";
|
|
2260
|
+
}
|
|
2261
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2262
|
+
// Background Size
|
|
2263
|
+
"bg-cover": { property: "background-size", valueTransform: () => "cover" },
|
|
2264
|
+
"bg-contain": { property: "background-size", valueTransform: () => "contain" },
|
|
2265
|
+
"background-size": { property: "background-size" },
|
|
2266
|
+
// Background Position
|
|
2267
|
+
"bg-top": { property: "background-position", valueTransform: () => "top" },
|
|
2268
|
+
"bg-bottom": { property: "background-position", valueTransform: () => "bottom" },
|
|
2269
|
+
"bg-center": { property: "background-position", valueTransform: () => "center" },
|
|
2270
|
+
"bg-left": { property: "background-position", valueTransform: () => "left" },
|
|
2271
|
+
"bg-right": { property: "background-position", valueTransform: () => "right" },
|
|
2272
|
+
"background-position": { property: "background-position" },
|
|
2273
|
+
// Background Repeat
|
|
2274
|
+
"bg-no-repeat": { property: "background-repeat", valueTransform: () => "no-repeat" },
|
|
2275
|
+
"bg-repeat-x": { property: "background-repeat", valueTransform: () => "repeat-x" },
|
|
2276
|
+
"bg-repeat-y": { property: "background-repeat", valueTransform: () => "repeat-y" },
|
|
2277
|
+
"bg-repeat": { property: "background-repeat", valueTransform: () => "repeat" },
|
|
2278
|
+
"background-repeat": { property: "background-repeat" },
|
|
2279
|
+
// Background Attachment
|
|
2280
|
+
"bg-fixed": { property: "background-attachment", valueTransform: () => "fixed" },
|
|
2281
|
+
"bg-local": { property: "background-attachment", valueTransform: () => "local" },
|
|
2282
|
+
"bg-scroll": { property: "background-attachment", valueTransform: () => "scroll" },
|
|
2283
|
+
"background-attachment": { property: "background-attachment" },
|
|
2284
|
+
// Background Clip
|
|
2285
|
+
"bg-clip": { property: "background-clip" },
|
|
2286
|
+
"background-clip": { property: "background-clip" },
|
|
2287
|
+
// Background Origin
|
|
2288
|
+
"bg-origin": { property: "background-origin" },
|
|
2289
|
+
"background-origin": { property: "background-origin" },
|
|
2290
|
+
// Background Image / Gradient
|
|
2291
|
+
"bg-gradient": { property: "background-image" },
|
|
2292
|
+
"gradient": { property: "background-image" },
|
|
2293
|
+
"background-image": { property: "background-image" },
|
|
2294
|
+
// Generic fallback for other bg- properties (e.g. background-image)
|
|
2295
|
+
// Note: background-color is handled by ColorHandler
|
|
2296
|
+
"bg": { property: "background-image" },
|
|
2297
|
+
"background-color": { property: "background-color" }
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
// src/integration/atomic/handlers/BorderHandler.ts
|
|
2303
|
+
var BorderHandler = class extends BaseHandler {
|
|
2304
|
+
canHandle(token) {
|
|
2305
|
+
if (!this.validateToken(token)) return false;
|
|
2306
|
+
if (token.domain === "color") {
|
|
2307
|
+
return this.containsKeyword(token.id, "border") || this.containsKeyword(token.id, "outline") || this.containsKeyword(token.id, "ring");
|
|
2308
|
+
}
|
|
2309
|
+
if (this.containsKeyword(token.id, "background") || this.containsKeyword(token.id, "bg")) {
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2312
|
+
return this.containsKeyword(token.id, "border") && !this.containsKeyword(token.id, "collapse") || this.containsKeyword(token.id, "radius") || this.containsKeyword(token.id, "outline") || this.containsKeyword(token.id, "ring");
|
|
2313
|
+
}
|
|
2314
|
+
generate(token, varName, suffix) {
|
|
2315
|
+
if (!this.validateToken(token)) {
|
|
2316
|
+
return "";
|
|
2317
|
+
}
|
|
2318
|
+
if (this.containsKeyword(token.id, "border-radius") || this.containsKeyword(token.id, "radius")) {
|
|
2319
|
+
return this.generateDirectionalRules("rounded", suffix, varName, {
|
|
2320
|
+
"": "border-radius",
|
|
2321
|
+
"t": ["border-top-left-radius", "border-top-right-radius"],
|
|
2322
|
+
"r": ["border-top-right-radius", "border-bottom-right-radius"],
|
|
2323
|
+
"b": ["border-bottom-right-radius", "border-bottom-left-radius"],
|
|
2324
|
+
"l": ["border-bottom-left-radius", "border-top-left-radius"],
|
|
2325
|
+
"tl": "border-top-left-radius",
|
|
2326
|
+
"tr": "border-top-right-radius",
|
|
2327
|
+
"br": "border-bottom-right-radius",
|
|
2328
|
+
"bl": "border-bottom-left-radius"
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
if (token.domain !== "color") {
|
|
2332
|
+
const widthRules = this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2333
|
+
"border-t": "border-top-width",
|
|
2334
|
+
"border-r": "border-right-width",
|
|
2335
|
+
"border-b": "border-bottom-width",
|
|
2336
|
+
"border-l": "border-left-width",
|
|
2337
|
+
"border-x": ["border-left-width", "border-right-width"],
|
|
2338
|
+
"border-y": ["border-top-width", "border-bottom-width"],
|
|
2339
|
+
"border": "border-width"
|
|
2340
|
+
// Generic border width
|
|
2341
|
+
});
|
|
2342
|
+
if (widthRules) return widthRules;
|
|
2343
|
+
}
|
|
2344
|
+
if (token.domain === "color" && this.containsKeyword(token.id, "border")) {
|
|
2345
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2346
|
+
"border-t": "border-top-color",
|
|
2347
|
+
"border-r": "border-right-color",
|
|
2348
|
+
"border-b": "border-bottom-color",
|
|
2349
|
+
"border-l": "border-left-color",
|
|
2350
|
+
"border-x": ["border-left-color", "border-right-color"],
|
|
2351
|
+
"border-y": ["border-top-color", "border-bottom-color"],
|
|
2352
|
+
"border": "border-color"
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
if (this.containsKeyword(token.id, "border-style")) {
|
|
2356
|
+
const value = this.extractSuffix(token.id);
|
|
2357
|
+
return this.createRule(`border-${value}`, "border-style", value);
|
|
2358
|
+
}
|
|
2359
|
+
if (this.containsKeyword(token.id, "outline")) {
|
|
2360
|
+
const value = this.extractSuffix(token.id);
|
|
2361
|
+
if (this.containsKeyword(token.id, "width")) {
|
|
2362
|
+
return this.createRule(`outline-${value}`, "outline-width", varName);
|
|
2363
|
+
}
|
|
2364
|
+
if (this.containsKeyword(token.id, "color")) {
|
|
2365
|
+
return this.createRule(`outline-${value}`, "outline-color", varName);
|
|
2366
|
+
}
|
|
2367
|
+
if (this.containsKeyword(token.id, "style")) {
|
|
2368
|
+
return this.createRule(`outline-${value}`, "outline-style", varName);
|
|
2369
|
+
}
|
|
2370
|
+
if (this.containsKeyword(token.id, "offset")) {
|
|
2371
|
+
return this.createRule(`outline-offset-${value}`, "outline-offset", varName);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
if (this.containsKeyword(token.id, "ring")) {
|
|
2375
|
+
const fullSuffix = token.id.replace(/\./g, "-");
|
|
2376
|
+
return this.generateRulesFromPrefix(token, varName, fullSuffix, {
|
|
2377
|
+
"ring": { property: "box-shadow" }
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
return "";
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
|
|
2384
|
+
// src/integration/atomic/handlers/TransformHandler.ts
|
|
2385
|
+
var TransformHandler = class extends BaseHandler {
|
|
2386
|
+
canHandle(token) {
|
|
2387
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "transform") || this.containsKeyword(token.id, "scale") || this.containsKeyword(token.id, "rotate") || this.containsKeyword(token.id, "translate") || this.containsKeyword(token.id, "skew") || this.containsKeyword(token.id, "perspective") || this.containsKeyword(token.id, "origin"));
|
|
2388
|
+
}
|
|
2389
|
+
generate(token, varName, suffix) {
|
|
2390
|
+
if (!this.validateToken(token)) {
|
|
2391
|
+
return "";
|
|
2392
|
+
}
|
|
2393
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2394
|
+
// Scale
|
|
2395
|
+
"scale-x": { property: "transform", valueTransform: (v) => `scaleX(${v})` },
|
|
2396
|
+
"scale-y": { property: "transform", valueTransform: (v) => `scaleY(${v})` },
|
|
2397
|
+
"scale": { property: "transform", valueTransform: (v) => `scale(${v})` },
|
|
2398
|
+
// Rotate
|
|
2399
|
+
"rotate": { property: "transform", valueTransform: (v) => `rotate(${v})` },
|
|
2400
|
+
// Translate
|
|
2401
|
+
"translate-x": { property: "transform", valueTransform: (v) => `translateX(${v})` },
|
|
2402
|
+
"translate-y": { property: "transform", valueTransform: (v) => `translateY(${v})` },
|
|
2403
|
+
// Skew
|
|
2404
|
+
"skew-x": { property: "transform", valueTransform: (v) => `skewX(${v})` },
|
|
2405
|
+
"skew-y": { property: "transform", valueTransform: (v) => `skewY(${v})` },
|
|
2406
|
+
// Origin
|
|
2407
|
+
"origin-center": { property: "transform-origin", valueTransform: () => "center" },
|
|
2408
|
+
"origin-top": { property: "transform-origin", valueTransform: () => "top" },
|
|
2409
|
+
"origin-top-right": { property: "transform-origin", valueTransform: () => "top right" },
|
|
2410
|
+
"origin-right": { property: "transform-origin", valueTransform: () => "right" },
|
|
2411
|
+
"origin-bottom-right": { property: "transform-origin", valueTransform: () => "bottom right" },
|
|
2412
|
+
"origin-bottom": { property: "transform-origin", valueTransform: () => "bottom" },
|
|
2413
|
+
"origin-bottom-left": { property: "transform-origin", valueTransform: () => "bottom left" },
|
|
2414
|
+
"origin-left": { property: "transform-origin", valueTransform: () => "left" },
|
|
2415
|
+
"origin-top-left": { property: "transform-origin", valueTransform: () => "top left" },
|
|
2416
|
+
// Generic Origin (for arbitrary values)
|
|
2417
|
+
"origin": "transform-origin",
|
|
2418
|
+
// Perspective
|
|
2419
|
+
"perspective": "perspective"
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
};
|
|
2423
|
+
|
|
2424
|
+
// src/integration/atomic/handlers/TransitionHandler.ts
|
|
2425
|
+
var TransitionHandler = class extends BaseHandler {
|
|
2426
|
+
canHandle(token) {
|
|
2427
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "transition") || this.containsKeyword(token.id, "animation") || this.containsKeyword(token.id, "animate") || this.containsKeyword(token.id, "duration") || this.containsKeyword(token.id, "delay") || this.containsKeyword(token.id, "ease"));
|
|
2428
|
+
}
|
|
2429
|
+
generate(token, varName, suffix) {
|
|
2430
|
+
if (!this.validateToken(token)) {
|
|
2431
|
+
return "";
|
|
2432
|
+
}
|
|
2433
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2434
|
+
// Transition Property
|
|
2435
|
+
"transition-none": { property: "transition-property", valueTransform: () => "none" },
|
|
2436
|
+
"transition-all": { property: "transition-property", valueTransform: () => "all" },
|
|
2437
|
+
"transition-colors": { property: "transition-property", valueTransform: () => "color, background-color, border-color, text-decoration-color, fill, stroke" },
|
|
2438
|
+
"transition-opacity": { property: "transition-property", valueTransform: () => "opacity" },
|
|
2439
|
+
"transition-shadow": { property: "transition-property", valueTransform: () => "box-shadow" },
|
|
2440
|
+
"transition-transform": { property: "transition-property", valueTransform: () => "transform" },
|
|
2441
|
+
"transition": { property: "transition-property" },
|
|
2442
|
+
// Transition Behavior
|
|
2443
|
+
"transition-normal": { property: "transition-behavior", valueTransform: () => "normal" },
|
|
2444
|
+
"transition-allow-discrete": { property: "transition-behavior", valueTransform: () => "allow-discrete" },
|
|
2445
|
+
// Duration
|
|
2446
|
+
"duration": "transition-duration",
|
|
2447
|
+
"transition-duration": "transition-duration",
|
|
2448
|
+
// Timing Function (Ease)
|
|
2449
|
+
"ease-linear": { property: "transition-timing-function", valueTransform: () => "linear" },
|
|
2450
|
+
"ease-in": { property: "transition-timing-function", valueTransform: () => "cubic-bezier(0.4, 0, 1, 1)" },
|
|
2451
|
+
"ease-out": { property: "transition-timing-function", valueTransform: () => "cubic-bezier(0, 0, 0.2, 1)" },
|
|
2452
|
+
"ease-in-out": { property: "transition-timing-function", valueTransform: () => "cubic-bezier(0.4, 0, 0.2, 1)" },
|
|
2453
|
+
"ease": "transition-timing-function",
|
|
2454
|
+
// Delay
|
|
2455
|
+
"delay": "transition-delay",
|
|
2456
|
+
"transition-delay": "transition-delay",
|
|
2457
|
+
// Animation
|
|
2458
|
+
"animate-none": { property: "animation", valueTransform: () => "none" },
|
|
2459
|
+
"animate-spin": { property: "animation", valueTransform: () => "spin 1s linear infinite" },
|
|
2460
|
+
"animate-ping": { property: "animation", valueTransform: () => "ping 1s cubic-bezier(0, 0, 0.2, 1) infinite" },
|
|
2461
|
+
"animate-pulse": { property: "animation", valueTransform: () => "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite" },
|
|
2462
|
+
"animate-bounce": { property: "animation", valueTransform: () => "bounce 1s infinite" },
|
|
2463
|
+
"animate": "animation",
|
|
2464
|
+
"animation": "animation"
|
|
2465
|
+
});
|
|
2466
|
+
}
|
|
2467
|
+
};
|
|
2468
|
+
|
|
2469
|
+
// src/integration/atomic/handlers/InteractivityHandler.ts
|
|
2470
|
+
var InteractivityHandler = class extends BaseHandler {
|
|
2471
|
+
canHandle(token) {
|
|
2472
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "cursor") || this.containsKeyword(token.id, "pointer") || this.containsKeyword(token.id, "select") || this.containsKeyword(token.id, "scroll") || this.containsKeyword(token.id, "touch") || this.containsKeyword(token.id, "accent") || this.containsKeyword(token.id, "appearance") || this.containsKeyword(token.id, "caret") || this.containsKeyword(token.id, "resize") || this.containsKeyword(token.id, "will-change"));
|
|
2473
|
+
}
|
|
2474
|
+
generate(token, varName, suffix) {
|
|
2475
|
+
if (!this.validateToken(token)) {
|
|
2476
|
+
return "";
|
|
2477
|
+
}
|
|
2478
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2479
|
+
// Scroll Margin
|
|
2480
|
+
"scroll-m": "scroll-margin",
|
|
2481
|
+
"scroll-mx": "scroll-margin-inline",
|
|
2482
|
+
"scroll-my": "scroll-margin-block",
|
|
2483
|
+
"scroll-mt": "scroll-margin-top",
|
|
2484
|
+
"scroll-mr": "scroll-margin-right",
|
|
2485
|
+
"scroll-mb": "scroll-margin-bottom",
|
|
2486
|
+
"scroll-ml": "scroll-margin-left",
|
|
2487
|
+
// Scroll Padding
|
|
2488
|
+
"scroll-p": "scroll-padding",
|
|
2489
|
+
"scroll-px": "scroll-padding-inline",
|
|
2490
|
+
"scroll-py": "scroll-padding-block",
|
|
2491
|
+
"scroll-pt": "scroll-padding-top",
|
|
2492
|
+
"scroll-pr": "scroll-padding-right",
|
|
2493
|
+
"scroll-pb": "scroll-padding-bottom",
|
|
2494
|
+
"scroll-pl": "scroll-padding-left",
|
|
2495
|
+
// Resize
|
|
2496
|
+
"resize-none": { property: "resize", valueTransform: () => "none" },
|
|
2497
|
+
"resize-y": { property: "resize", valueTransform: () => "vertical" },
|
|
2498
|
+
"resize-x": { property: "resize", valueTransform: () => "horizontal" },
|
|
2499
|
+
"resize": { property: "resize", valueTransform: () => "both" },
|
|
2500
|
+
// Default resize
|
|
2501
|
+
// Accent
|
|
2502
|
+
"accent": "accent-color",
|
|
2503
|
+
// Appearance
|
|
2504
|
+
"appearance": "appearance",
|
|
2505
|
+
// Caret
|
|
2506
|
+
"caret": "caret-color",
|
|
2507
|
+
// Cursor
|
|
2508
|
+
"cursor": "cursor",
|
|
2509
|
+
// Pointer Events
|
|
2510
|
+
"pointer-events": "pointer-events",
|
|
2511
|
+
// Scroll Behavior
|
|
2512
|
+
"scroll-auto": { property: "scroll-behavior", valueTransform: () => "auto" },
|
|
2513
|
+
"scroll-smooth": { property: "scroll-behavior", valueTransform: () => "smooth" },
|
|
2514
|
+
// Scroll Snap
|
|
2515
|
+
"snap-start": { property: "scroll-snap-align", valueTransform: () => "start" },
|
|
2516
|
+
"snap-end": { property: "scroll-snap-align", valueTransform: () => "end" },
|
|
2517
|
+
"snap-center": { property: "scroll-snap-align", valueTransform: () => "center" },
|
|
2518
|
+
"snap-align-none": { property: "scroll-snap-align", valueTransform: () => "none" },
|
|
2519
|
+
"snap-normal": { property: "scroll-snap-stop", valueTransform: () => "normal" },
|
|
2520
|
+
"snap-always": { property: "scroll-snap-stop", valueTransform: () => "always" },
|
|
2521
|
+
"snap-none": { property: "scroll-snap-type", valueTransform: () => "none" },
|
|
2522
|
+
"snap-x": { property: "scroll-snap-type", valueTransform: () => "x var(--scroll-snap-strictness)" },
|
|
2523
|
+
"snap-y": { property: "scroll-snap-type", valueTransform: () => "y var(--scroll-snap-strictness)" },
|
|
2524
|
+
"snap-both": { property: "scroll-snap-type", valueTransform: () => "both var(--scroll-snap-strictness)" },
|
|
2525
|
+
"snap-mandatory": { property: "--scroll-snap-strictness", valueTransform: () => "mandatory" },
|
|
2526
|
+
"snap-proximity": { property: "--scroll-snap-strictness", valueTransform: () => "proximity" },
|
|
2527
|
+
// Touch Action
|
|
2528
|
+
"touch": "touch-action",
|
|
2529
|
+
// User Select
|
|
2530
|
+
"select": "user-select",
|
|
2531
|
+
// Will Change
|
|
2532
|
+
"will-change": "will-change"
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
};
|
|
2536
|
+
|
|
2537
|
+
// src/integration/atomic/handlers/TableHandler.ts
|
|
2538
|
+
var TableHandler = class extends BaseHandler {
|
|
2539
|
+
canHandle(token) {
|
|
2540
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "table") || this.containsKeyword(token.id, "border-collapse") || this.containsKeyword(token.id, "border-separate") || // Added explicit check
|
|
2541
|
+
this.containsKeyword(token.id, "border-spacing") || this.containsKeyword(token.id, "caption"));
|
|
2542
|
+
}
|
|
2543
|
+
generate(token, varName, suffix) {
|
|
2544
|
+
if (!this.validateToken(token)) {
|
|
2545
|
+
return "";
|
|
2546
|
+
}
|
|
2547
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2548
|
+
// Border Collapse
|
|
2549
|
+
"border-collapse": { property: "border-collapse", valueTransform: () => "collapse" },
|
|
2550
|
+
"border-separate": { property: "border-collapse", valueTransform: () => "separate" },
|
|
2551
|
+
// Border Spacing
|
|
2552
|
+
"border-spacing-x": { property: "border-spacing" },
|
|
2553
|
+
"border-spacing-y": { property: "border-spacing" },
|
|
2554
|
+
"border-spacing": { property: "border-spacing" },
|
|
2555
|
+
// Table Layout
|
|
2556
|
+
"table-auto": { property: "table-layout", valueTransform: () => "auto" },
|
|
2557
|
+
"table-fixed": { property: "table-layout", valueTransform: () => "fixed" },
|
|
2558
|
+
// Caption Side
|
|
2559
|
+
"caption-top": { property: "caption-side", valueTransform: () => "top" },
|
|
2560
|
+
"caption-bottom": { property: "caption-side", valueTransform: () => "bottom" }
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
};
|
|
2564
|
+
|
|
2565
|
+
// src/integration/atomic/handlers/SVGHandler.ts
|
|
2566
|
+
var SVGHandler = class extends BaseHandler {
|
|
2567
|
+
canHandle(token) {
|
|
2568
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "fill") || this.containsKeyword(token.id, "stroke"));
|
|
2569
|
+
}
|
|
2570
|
+
generate(token, varName, suffix) {
|
|
2571
|
+
if (!this.validateToken(token)) {
|
|
2572
|
+
return "";
|
|
2573
|
+
}
|
|
2574
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2575
|
+
"stroke-width": { property: "stroke-width" },
|
|
2576
|
+
"stroke": { property: "stroke" },
|
|
2577
|
+
"fill": { property: "fill" }
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
};
|
|
2581
|
+
|
|
2582
|
+
// src/integration/atomic/handlers/AccessibilityHandler.ts
|
|
2583
|
+
var AccessibilityHandler = class extends BaseHandler {
|
|
2584
|
+
canHandle(token) {
|
|
2585
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "forced-color") || this.containsKeyword(token.id, "accessibility") || this.containsKeyword(token.id, "a11y") || this.containsKeyword(token.id, "sr-only") || this.containsKeyword(token.id, "focus-visible") || this.containsKeyword(token.id, "focus-within"));
|
|
2586
|
+
}
|
|
2587
|
+
generate(token, varName, suffix) {
|
|
2588
|
+
if (!this.validateToken(token)) {
|
|
2589
|
+
return "";
|
|
2590
|
+
}
|
|
2591
|
+
const fullSuffix = token.id.replace(/\./g, "-");
|
|
2592
|
+
const standardOutput = this.generateRulesFromPrefix(token, varName, fullSuffix, {
|
|
2593
|
+
"forced-color-adjust": { property: "forced-color-adjust" },
|
|
2594
|
+
"focus-visible": { property: "outline", valueTransform: () => "2px solid var(--color-primary)" },
|
|
2595
|
+
"focus-within": { property: "outline", valueTransform: () => "2px solid var(--color-primary)" }
|
|
2596
|
+
});
|
|
2597
|
+
if (standardOutput) return standardOutput;
|
|
2598
|
+
if (this.containsKeyword(token.id, "sr-only")) {
|
|
2599
|
+
if (token.id === "not-sr-only" || suffix === "not-sr-only") {
|
|
2600
|
+
return this.createRule("not-sr-only", "position", "static;width:auto;height:auto;padding:0;margin:0;overflow:visible;clip:auto;white-space:normal");
|
|
2601
|
+
}
|
|
2602
|
+
return this.createRule("sr-only", "position", "absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0");
|
|
2603
|
+
}
|
|
2604
|
+
if (this.containsKeyword(token.id, "not-sr-only")) {
|
|
2605
|
+
return this.createRule("not-sr-only", "position", "static;width:auto;height:auto;padding:0;margin:0;overflow:visible;clip:auto;white-space:normal");
|
|
2606
|
+
}
|
|
2607
|
+
return "";
|
|
2608
|
+
}
|
|
2609
|
+
};
|
|
2610
|
+
|
|
2611
|
+
// src/integration/atomic/handlers/FilterHandler.ts
|
|
2612
|
+
var FilterHandler = class extends BaseHandler {
|
|
2613
|
+
canHandle(token) {
|
|
2614
|
+
return this.validateToken(token) && (this.containsKeyword(token.id, "filter") || this.containsKeyword(token.id, "blur") || this.containsKeyword(token.id, "brightness") || this.containsKeyword(token.id, "contrast") || this.containsKeyword(token.id, "drop-shadow") || this.containsKeyword(token.id, "grayscale") || this.containsKeyword(token.id, "hue-rotate") || this.containsKeyword(token.id, "invert") || this.containsKeyword(token.id, "saturate") || this.containsKeyword(token.id, "sepia") || this.containsKeyword(token.id, "backdrop"));
|
|
2615
|
+
}
|
|
2616
|
+
generate(token, varName, suffix) {
|
|
2617
|
+
if (!this.validateToken(token)) {
|
|
2618
|
+
return "";
|
|
2619
|
+
}
|
|
2620
|
+
return this.generateRulesFromPrefix(token, varName, suffix, {
|
|
2621
|
+
// Backdrop Filter
|
|
2622
|
+
"backdrop-blur": { property: "backdrop-filter", valueTransform: (v) => `blur(${v})` },
|
|
2623
|
+
"backdrop-brightness": { property: "backdrop-filter", valueTransform: (v) => `brightness(${v})` },
|
|
2624
|
+
"backdrop-contrast": { property: "backdrop-filter", valueTransform: (v) => `contrast(${v})` },
|
|
2625
|
+
"backdrop-grayscale": { property: "backdrop-filter", valueTransform: (v) => `grayscale(${v})` },
|
|
2626
|
+
"backdrop-hue-rotate": { property: "backdrop-filter", valueTransform: (v) => `hue-rotate(${v})` },
|
|
2627
|
+
"backdrop-invert": { property: "backdrop-filter", valueTransform: (v) => `invert(${v})` },
|
|
2628
|
+
"backdrop-opacity": { property: "backdrop-filter", valueTransform: (v) => `opacity(${v})` },
|
|
2629
|
+
"backdrop-saturate": { property: "backdrop-filter", valueTransform: (v) => `saturate(${v})` },
|
|
2630
|
+
"backdrop-sepia": { property: "backdrop-filter", valueTransform: (v) => `sepia(${v})` },
|
|
2631
|
+
// Filter
|
|
2632
|
+
"blur": { property: "filter", valueTransform: (v) => `blur(${v})` },
|
|
2633
|
+
"brightness": { property: "filter", valueTransform: (v) => `brightness(${v})` },
|
|
2634
|
+
"contrast": { property: "filter", valueTransform: (v) => `contrast(${v})` },
|
|
2635
|
+
"drop-shadow": { property: "filter", valueTransform: (v) => `drop-shadow(${v})` },
|
|
2636
|
+
"grayscale": { property: "filter", valueTransform: (v) => `grayscale(${v})` },
|
|
2637
|
+
"hue-rotate": { property: "filter", valueTransform: (v) => `hue-rotate(${v})` },
|
|
2638
|
+
"invert": { property: "filter", valueTransform: (v) => `invert(${v})` },
|
|
2639
|
+
"saturate": { property: "filter", valueTransform: (v) => `saturate(${v})` },
|
|
2640
|
+
"sepia": { property: "filter", valueTransform: (v) => `sepia(${v})` }
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
|
|
2645
|
+
// src/integration/atomic/core/UtilityGenerator.ts
|
|
2646
|
+
var UtilityGenerator = class {
|
|
2647
|
+
constructor() {
|
|
2648
|
+
this.handlers = [
|
|
2649
|
+
new FlexboxHandler(),
|
|
2650
|
+
new GridHandler(),
|
|
2651
|
+
new LayoutHandler(),
|
|
2652
|
+
new SVGHandler(),
|
|
2653
|
+
new BorderHandler(),
|
|
2654
|
+
new TypographyHandler(),
|
|
2655
|
+
new BackgroundHandler(),
|
|
2656
|
+
new ColorHandler(),
|
|
2657
|
+
new SpaceHandler(),
|
|
2658
|
+
new EffectHandler(),
|
|
2659
|
+
new FilterHandler(),
|
|
2660
|
+
new TableHandler(),
|
|
2661
|
+
new TransformHandler(),
|
|
2662
|
+
new TransitionHandler(),
|
|
2663
|
+
new InteractivityHandler(),
|
|
2664
|
+
new AccessibilityHandler()
|
|
2665
|
+
];
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* Mini function: Get full variable name with prefix
|
|
2669
|
+
*/
|
|
2670
|
+
getFullVarName(tokenId, prefix) {
|
|
2671
|
+
const varName = formatVarName(tokenId, `--${prefix}`);
|
|
2672
|
+
return createVarReference(varName);
|
|
2673
|
+
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Mini function: Find appropriate handler for token
|
|
2676
|
+
*/
|
|
2677
|
+
findHandler(token) {
|
|
2678
|
+
return this.handlers.find((h) => h.canHandle(token));
|
|
2679
|
+
}
|
|
2680
|
+
/**
|
|
2681
|
+
* Mini function: Process a single token
|
|
2682
|
+
*/
|
|
2683
|
+
processToken(token, context) {
|
|
2684
|
+
if (!isValidToken(token)) {
|
|
2685
|
+
return "";
|
|
2686
|
+
}
|
|
2687
|
+
const handler = this.findHandler(token);
|
|
2688
|
+
if (!handler) {
|
|
2689
|
+
return "";
|
|
2690
|
+
}
|
|
2691
|
+
const varName = this.getFullVarName(token.id, context.prefix);
|
|
2692
|
+
const suffix = formatSuffix(token.id);
|
|
2693
|
+
return handler.generate(token, varName, suffix);
|
|
2694
|
+
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Mini function: Collect utility classes from tokens
|
|
2697
|
+
*/
|
|
2698
|
+
collectUtilities(tokens, context) {
|
|
2699
|
+
const validTokens = filterValidTokens(tokens);
|
|
2700
|
+
const utilities = [];
|
|
2701
|
+
validTokens.forEach((token) => {
|
|
2702
|
+
const utility = this.processToken(token, context);
|
|
2703
|
+
if (utility) {
|
|
2704
|
+
utilities.push(utility);
|
|
2705
|
+
}
|
|
2706
|
+
});
|
|
2707
|
+
return utilities;
|
|
2708
|
+
}
|
|
2709
|
+
/**
|
|
2710
|
+
* Generate CSS for all tokens
|
|
2711
|
+
*/
|
|
2712
|
+
generate(tokens, context = { prefix: "tw", selector: ":root" }) {
|
|
2713
|
+
const utilities = this.collectUtilities(tokens, context);
|
|
2714
|
+
return createLayer("utilities", utilities.join("\n"));
|
|
2715
|
+
}
|
|
2716
|
+
};
|
|
2717
|
+
|
|
2718
|
+
// src/integration/atomic/AtomicCSSGenerator.ts
|
|
2719
|
+
var AtomicCSSGenerator = class {
|
|
2720
|
+
constructor() {
|
|
2721
|
+
this.baseGenerator = new BaseGenerator();
|
|
2722
|
+
this.themeGenerator = new ThemeGenerator();
|
|
2723
|
+
this.utilityGenerator = new UtilityGenerator();
|
|
2724
|
+
}
|
|
2725
|
+
generate(tokens, options = {}) {
|
|
2726
|
+
const context = this.createContext(options);
|
|
2727
|
+
const includeReset = options.includeReset !== false;
|
|
2728
|
+
return this.assembleCss(tokens, context, includeReset);
|
|
2729
|
+
}
|
|
2730
|
+
/**
|
|
2731
|
+
* Mini function: Create generator context
|
|
2732
|
+
*/
|
|
2733
|
+
createContext(options) {
|
|
2734
|
+
return {
|
|
2735
|
+
prefix: options.prefix || "mase-",
|
|
2736
|
+
selector: options.selector || ":root"
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Mini function: Assemble CSS parts
|
|
2741
|
+
*/
|
|
2742
|
+
assembleCss(tokens, context, includeReset) {
|
|
2743
|
+
let css = "/* Generated by MASE Atomic Engine */\n";
|
|
2744
|
+
css += "@layer base, theme, utilities;\n\n";
|
|
2745
|
+
css += this.baseGenerator.generate(includeReset);
|
|
2746
|
+
css += this.themeGenerator.generate(tokens, context);
|
|
2747
|
+
css += this.utilityGenerator.generate(tokens, context);
|
|
2748
|
+
return css;
|
|
2749
|
+
}
|
|
2750
|
+
};
|
|
2751
|
+
|
|
2752
|
+
// src/data/tokens.ts
|
|
2753
|
+
var TOKENS = {
|
|
2754
|
+
// Colors - Intent based
|
|
2755
|
+
"color.primary": { id: "color.primary", domain: "color", value: "#2563eb" },
|
|
2756
|
+
// blue-600
|
|
2757
|
+
"color.secondary": { id: "color.secondary", domain: "color", value: "#4b5563" },
|
|
2758
|
+
// gray-600
|
|
2759
|
+
"color.danger": { id: "color.danger", domain: "color", value: "#dc2626" },
|
|
2760
|
+
// red-600
|
|
2761
|
+
// Spacing - Scale based
|
|
2762
|
+
"space.sm": { id: "space.sm", domain: "layout", value: "0.5rem" },
|
|
2763
|
+
// 8px
|
|
2764
|
+
"space.md": { id: "space.md", domain: "layout", value: "1rem" },
|
|
2765
|
+
// 16px
|
|
2766
|
+
"space.lg": { id: "space.lg", domain: "layout", value: "1.5rem" },
|
|
2767
|
+
// 24px
|
|
2768
|
+
// Forbidden/Raw tokens (for demo)
|
|
2769
|
+
"raw.hex": { id: "raw.hex", domain: "color", value: "#123456" },
|
|
2770
|
+
// Illegal
|
|
2771
|
+
// Surface (Theme Aware!)
|
|
2772
|
+
"surface.card": {
|
|
2773
|
+
id: "surface.card",
|
|
2774
|
+
domain: "color",
|
|
2775
|
+
value: {
|
|
2776
|
+
light: "#ffffff",
|
|
2777
|
+
// white
|
|
2778
|
+
dark: "#1e293b"
|
|
2779
|
+
// slate-800
|
|
2780
|
+
}
|
|
2781
|
+
},
|
|
2782
|
+
"surface.ground": {
|
|
2783
|
+
id: "surface.ground",
|
|
2784
|
+
domain: "color",
|
|
2785
|
+
value: {
|
|
2786
|
+
light: "#f9fafb",
|
|
2787
|
+
// gray-50
|
|
2788
|
+
dark: "#0f172a"
|
|
2789
|
+
// slate-900
|
|
2790
|
+
}
|
|
2791
|
+
},
|
|
2792
|
+
"surface.paper": {
|
|
2793
|
+
id: "surface.paper",
|
|
2794
|
+
domain: "color",
|
|
2795
|
+
value: {
|
|
2796
|
+
light: "#ffffff",
|
|
2797
|
+
// white
|
|
2798
|
+
dark: "#334155"
|
|
2799
|
+
// slate-700
|
|
2800
|
+
}
|
|
2801
|
+
},
|
|
2802
|
+
// Text (Theme Aware!)
|
|
2803
|
+
"text.light": {
|
|
2804
|
+
id: "text.light",
|
|
2805
|
+
domain: "color",
|
|
2806
|
+
value: {
|
|
2807
|
+
light: "#ffffff",
|
|
2808
|
+
// white
|
|
2809
|
+
dark: "#f3f4f6"
|
|
2810
|
+
// gray-100
|
|
2811
|
+
}
|
|
2812
|
+
},
|
|
2813
|
+
"text.dark": {
|
|
2814
|
+
id: "text.dark",
|
|
2815
|
+
domain: "color",
|
|
2816
|
+
value: {
|
|
2817
|
+
light: "#111827",
|
|
2818
|
+
// gray-900
|
|
2819
|
+
dark: "#ffffff"
|
|
2820
|
+
// white
|
|
2821
|
+
}
|
|
2822
|
+
},
|
|
2823
|
+
"text.primary": {
|
|
2824
|
+
id: "text.primary",
|
|
2825
|
+
domain: "color",
|
|
2826
|
+
value: {
|
|
2827
|
+
light: "#111827",
|
|
2828
|
+
// gray-900
|
|
2829
|
+
dark: "#f9fafb"
|
|
2830
|
+
// gray-50
|
|
2831
|
+
}
|
|
2832
|
+
},
|
|
2833
|
+
"text.secondary": {
|
|
2834
|
+
id: "text.secondary",
|
|
2835
|
+
domain: "color",
|
|
2836
|
+
value: {
|
|
2837
|
+
light: "#6b7280",
|
|
2838
|
+
// gray-500
|
|
2839
|
+
dark: "#9ca3af"
|
|
2840
|
+
// gray-400
|
|
2841
|
+
}
|
|
2842
|
+
},
|
|
2843
|
+
// Effects
|
|
2844
|
+
"shadow.sm": { id: "shadow.sm", domain: "effect", value: "0 1px 2px 0 rgb(0 0 0 / 0.05)" },
|
|
2845
|
+
"shadow.md": { id: "shadow.md", domain: "effect", value: "0 4px 6px -1px rgb(0 0 0 / 0.1)" },
|
|
2846
|
+
"shadow.lg": { id: "shadow.lg", domain: "effect", value: "0 10px 15px -3px rgb(0 0 0 / 0.1)" },
|
|
2847
|
+
// Radius (Moved to effect domain for consistency)
|
|
2848
|
+
"radius.md": { id: "radius.md", domain: "effect", value: "0.375rem" },
|
|
2849
|
+
// 6px
|
|
2850
|
+
"radius.lg": { id: "radius.lg", domain: "effect", value: "0.5rem" },
|
|
2851
|
+
// 8px
|
|
2852
|
+
"radius.full": { id: "radius.full", domain: "effect", value: "9999px" },
|
|
2853
|
+
// Typography - Font Sizes
|
|
2854
|
+
"text.sm": { id: "text.sm", domain: "typography", value: "0.875rem" },
|
|
2855
|
+
"text.base": { id: "text.base", domain: "typography", value: "1rem" },
|
|
2856
|
+
"text.lg": { id: "text.lg", domain: "typography", value: "1.125rem" },
|
|
2857
|
+
"text.xl": { id: "text.xl", domain: "typography", value: "1.25rem" },
|
|
2858
|
+
// Typography - Font Weights
|
|
2859
|
+
"weight.normal": { id: "weight.normal", domain: "typography", value: "400" },
|
|
2860
|
+
"weight.medium": { id: "weight.medium", domain: "typography", value: "500" },
|
|
2861
|
+
"weight.bold": { id: "weight.bold", domain: "typography", value: "700" }
|
|
2862
|
+
};
|
|
2863
|
+
|
|
1230
2864
|
// src/cli/commands/generate-css.ts
|
|
1231
|
-
var
|
|
1232
|
-
var
|
|
1233
|
-
|
|
2865
|
+
var fs4 = __toESM(require("fs"));
|
|
2866
|
+
var path2 = __toESM(require("path"));
|
|
2867
|
+
var generateCssCommand = new import_commander4.Command("generate-css").description("Generate CSS from a contract DSL file or Token source").argument("[source]", "Contract DSL file or Tokens file (optional for atomic)").option("-o, --output <file>", "Output file (default: stdout)").option("--format <format>", "Output format (class, variable, inline, atomic)", "class").option("--theme <theme>", "Theme (light, dark)", "light").option("--device <device>", "Device (mobile, tablet, desktop)", "desktop").option("-w, --watch", "Watch for changes and regenerate", false).action(async (source, options) => {
|
|
2868
|
+
if (options.format === "atomic") {
|
|
2869
|
+
const runAtomicGeneration = () => {
|
|
2870
|
+
console.log("\u269B\uFE0F Generating Atomic CSS...");
|
|
2871
|
+
let tokens = TOKENS;
|
|
2872
|
+
if (source) {
|
|
2873
|
+
try {
|
|
2874
|
+
const fullPath = path2.resolve(process.cwd(), source);
|
|
2875
|
+
delete require.cache[require.resolve(fullPath)];
|
|
2876
|
+
if (fs4.existsSync(fullPath)) {
|
|
2877
|
+
const loaded = require(fullPath);
|
|
2878
|
+
tokens = loaded.default || loaded.TOKENS || loaded;
|
|
2879
|
+
console.log(`Loaded tokens from ${source}`);
|
|
2880
|
+
} else {
|
|
2881
|
+
console.warn(`\u26A0\uFE0F Source file ${source} not found. Using default tokens.`);
|
|
2882
|
+
}
|
|
2883
|
+
} catch (e) {
|
|
2884
|
+
console.warn("\u26A0\uFE0F Could not load tokens from file, using defaults:", e.message);
|
|
2885
|
+
}
|
|
2886
|
+
} else {
|
|
2887
|
+
console.log("\u2139\uFE0F No source provided, using default built-in tokens.");
|
|
2888
|
+
}
|
|
2889
|
+
const generator = new AtomicCSSGenerator();
|
|
2890
|
+
const css = generator.generate(tokens);
|
|
2891
|
+
if (options.output) {
|
|
2892
|
+
fs4.writeFileSync(options.output, css, "utf-8");
|
|
2893
|
+
console.log(`\u2705 Atomic CSS generated to ${options.output}`);
|
|
2894
|
+
} else {
|
|
2895
|
+
console.log(css);
|
|
2896
|
+
}
|
|
2897
|
+
};
|
|
2898
|
+
runAtomicGeneration();
|
|
2899
|
+
if (options.watch && source) {
|
|
2900
|
+
console.log(`\u{1F440} Watching for changes in ${source}...`);
|
|
2901
|
+
const fullPath = path2.resolve(process.cwd(), source);
|
|
2902
|
+
fs4.watch(fullPath, (eventType) => {
|
|
2903
|
+
if (eventType === "change") {
|
|
2904
|
+
console.log("\u{1F504} File changed, regenerating...");
|
|
2905
|
+
runAtomicGeneration();
|
|
2906
|
+
}
|
|
2907
|
+
});
|
|
2908
|
+
await new Promise(() => {
|
|
2909
|
+
});
|
|
2910
|
+
} else if (options.watch && !source) {
|
|
2911
|
+
console.warn("\u26A0\uFE0F Watch mode requires a source file. Ignoring --watch.");
|
|
2912
|
+
}
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
if (!source) {
|
|
2916
|
+
console.error("\u274C Error: Contract file argument is required for non-atomic format.");
|
|
2917
|
+
process.exit(1);
|
|
2918
|
+
}
|
|
2919
|
+
console.log(`Generating CSS from ${source}...`);
|
|
1234
2920
|
try {
|
|
1235
|
-
const
|
|
2921
|
+
const contractContent = fs4.readFileSync(source, "utf-8");
|
|
1236
2922
|
const tokenizer = new ContractDSLTokenizer();
|
|
1237
|
-
const tokens = tokenizer.tokenize(
|
|
2923
|
+
const tokens = tokenizer.tokenize(contractContent);
|
|
1238
2924
|
const parser = new ContractDSLParser();
|
|
1239
2925
|
const ast = parser.parse(tokens);
|
|
1240
2926
|
const validator = new ContractDSLValidator();
|
|
@@ -1276,7 +2962,7 @@ var generateCssCommand = new import_commander4.Command("generate-css").descripti
|
|
|
1276
2962
|
break;
|
|
1277
2963
|
}
|
|
1278
2964
|
if (options.output) {
|
|
1279
|
-
|
|
2965
|
+
fs4.writeFileSync(options.output, output, "utf-8");
|
|
1280
2966
|
console.log(`\u2705 CSS generated to ${options.output}`);
|
|
1281
2967
|
} else {
|
|
1282
2968
|
console.log(output);
|