xatriumcss 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/bin/xatriumcss +2 -0
- package/index.js +315 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +44 -0
- package/src/cli.js +184 -0
- package/src/collectClasses.js +477 -0
- package/src/index.js +26 -0
- package/src/parser.js +26836 -0
- package/src/postcss.js +22 -0
- package/src/vite.js +40 -0
- package/xatriumcss.win32-x64-msvc.node +0 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { glob } = require('glob');
|
|
3
|
+
|
|
4
|
+
let nativeScanner;
|
|
5
|
+
try {
|
|
6
|
+
nativeScanner = require('../scripts/index.js');
|
|
7
|
+
} catch (e) {
|
|
8
|
+
console.warn('⚠️ Native scanner not available, falling back to JavaScript');
|
|
9
|
+
nativeScanner = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function smartSplitNoBrace(str, ...splitChars) {
|
|
13
|
+
if (splitChars.length === 0) splitChars = [' '];
|
|
14
|
+
|
|
15
|
+
const result = [];
|
|
16
|
+
let current = '';
|
|
17
|
+
let bracketDepth = 0;
|
|
18
|
+
let parenDepth = 0;
|
|
19
|
+
let braceDepth = 0;
|
|
20
|
+
let inQuote = null;
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < str.length; i++) {
|
|
23
|
+
const char = str[i];
|
|
24
|
+
const prevChar = str[i - 1];
|
|
25
|
+
|
|
26
|
+
// Handle quotes (not escaped)
|
|
27
|
+
if ((char === "'" || char === '"') && prevChar !== '\\') {
|
|
28
|
+
if (!inQuote) {
|
|
29
|
+
inQuote = char;
|
|
30
|
+
} else if (inQuote === char) {
|
|
31
|
+
inQuote = null;
|
|
32
|
+
}
|
|
33
|
+
current += char;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Track depths only when not in quotes
|
|
38
|
+
if (!inQuote) {
|
|
39
|
+
if (char === '[') bracketDepth++;
|
|
40
|
+
if (char === ']' && bracketDepth > 0) bracketDepth--;
|
|
41
|
+
if (char === '(') parenDepth++;
|
|
42
|
+
if (char === ')' && parenDepth > 0) parenDepth--;
|
|
43
|
+
|
|
44
|
+
// Track brace depth with prefix check
|
|
45
|
+
if (char === '{') {
|
|
46
|
+
// Check if there's content in current token (means there's a prefix)
|
|
47
|
+
const hasPrefix = current.trim().length > 0;
|
|
48
|
+
|
|
49
|
+
// Always increment brace depth
|
|
50
|
+
braceDepth++;
|
|
51
|
+
|
|
52
|
+
// But if there's a prefix, we're in "keep together" mode
|
|
53
|
+
// The depth will prevent splitting until we hit the closing }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (char === '}' && braceDepth > 0) {
|
|
57
|
+
braceDepth--;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ✅ Split on specified characters only if all depths are 0 and not in quotes
|
|
62
|
+
let shouldSplit = false;
|
|
63
|
+
if (bracketDepth === 0 && parenDepth === 0 && braceDepth === 0) {
|
|
64
|
+
// Check single character splits
|
|
65
|
+
if (splitChars.includes(char)) {
|
|
66
|
+
shouldSplit = true;
|
|
67
|
+
}
|
|
68
|
+
// Check multi-character splits (like "::")
|
|
69
|
+
for (let splitStr of splitChars) {
|
|
70
|
+
if (splitStr.length > 1 && str.slice(i, i + splitStr.length) === splitStr) {
|
|
71
|
+
shouldSplit = true;
|
|
72
|
+
i += splitStr.length - 1; // Skip ahead
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (shouldSplit) {
|
|
79
|
+
if (current) result.push(current);
|
|
80
|
+
current = '';
|
|
81
|
+
} else {
|
|
82
|
+
current += char;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (current) result.push(current);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function collectClasses(contentPaths) {
|
|
91
|
+
const sourceConfig = global.xatriumSourceConfig || {
|
|
92
|
+
basePath: null,
|
|
93
|
+
disableAuto: false,
|
|
94
|
+
explicitPaths: [],
|
|
95
|
+
ignorePaths: []
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Use native scanner if available
|
|
99
|
+
if (nativeScanner) {
|
|
100
|
+
try {
|
|
101
|
+
const scanner = new nativeScanner.Scanner();
|
|
102
|
+
|
|
103
|
+
// Add patterns
|
|
104
|
+
let pathsArray = sourceConfig.disableAuto
|
|
105
|
+
? sourceConfig.explicitPaths
|
|
106
|
+
: [...(Array.isArray(contentPaths) ? contentPaths : [contentPaths]), ...sourceConfig.explicitPaths];
|
|
107
|
+
|
|
108
|
+
pathsArray.forEach(pattern => scanner.addPattern(pattern));
|
|
109
|
+
sourceConfig.ignorePaths.forEach(pattern => scanner.addIgnorePattern(pattern));
|
|
110
|
+
|
|
111
|
+
console.log('🚀 Using native scanner (Rust)');
|
|
112
|
+
const rawClasses = scanner.scanAndExtract();
|
|
113
|
+
|
|
114
|
+
return processClasses(rawClasses);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.warn('⚠️ Native scanner failed, falling back:', error.message);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Fallback to JavaScript implementation
|
|
121
|
+
return collectClassesJS(contentPaths);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function processClasses(rawClasses) {
|
|
125
|
+
const allClasses = new Set();
|
|
126
|
+
|
|
127
|
+
rawClasses.forEach(part => {
|
|
128
|
+
let cls = part.trim();
|
|
129
|
+
if (!cls) return;
|
|
130
|
+
|
|
131
|
+
// Normalize whitespace to single spaces, but preserve inside ( [ { " '
|
|
132
|
+
let normalizedCls = "";
|
|
133
|
+
let parenDepth = 0;
|
|
134
|
+
let bracketDepth = 0;
|
|
135
|
+
let braceDepth = 0;
|
|
136
|
+
let inSingleQuote = false;
|
|
137
|
+
let inDoubleQuote = false;
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < cls.length; i++) {
|
|
140
|
+
const char = cls[i];
|
|
141
|
+
|
|
142
|
+
// Track quote state
|
|
143
|
+
if (char === "'" && !inDoubleQuote) inSingleQuote = !inSingleQuote;
|
|
144
|
+
else if (char === '"' && !inSingleQuote) inDoubleQuote = !inDoubleQuote;
|
|
145
|
+
|
|
146
|
+
const inQuotes = inSingleQuote || inDoubleQuote;
|
|
147
|
+
|
|
148
|
+
// Track bracket depths (only if not in quotes)
|
|
149
|
+
if (!inQuotes) {
|
|
150
|
+
if (char === "(") parenDepth++;
|
|
151
|
+
else if (char === ")") parenDepth--;
|
|
152
|
+
else if (char === "[") bracketDepth++;
|
|
153
|
+
else if (char === "]") bracketDepth--;
|
|
154
|
+
else if (char === "{") braceDepth++;
|
|
155
|
+
else if (char === "}") braceDepth--;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const insideGroup = parenDepth > 0 || bracketDepth > 0 || braceDepth > 0 || inQuotes;
|
|
159
|
+
|
|
160
|
+
// Normalize whitespace only when NOT inside groups
|
|
161
|
+
if (/\s/.test(char)) {
|
|
162
|
+
if (insideGroup) {
|
|
163
|
+
normalizedCls += char;
|
|
164
|
+
} else {
|
|
165
|
+
// Collapse consecutive whitespace to single space
|
|
166
|
+
if (normalizedCls.length > 0 && !/\s/.test(normalizedCls[normalizedCls.length - 1])) {
|
|
167
|
+
normalizedCls += " ";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
normalizedCls += char;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
normalizedCls = normalizedCls.trim();
|
|
176
|
+
const innerParts = smartSplitNoBrace(normalizedCls, " ");
|
|
177
|
+
|
|
178
|
+
const spawnShorthandRegex = /^(.*?:)?spawn-((?:from|to|back|half|\[[^\]]+\])(?:\/(?:from|to|back|half|\[[^\]]+\]))*):(.+)$/;
|
|
179
|
+
const scrollShorthandRegex = /^(.*?:)?scroll-((?:from|to|back|half|\[[^\]]+\])(?:\/(?:from|to|back|half|\[[^\]]+\]))*):(.+)$/;
|
|
180
|
+
const scrollPrefixRegex = /^(.*?:)?scroll:(.+)$/;
|
|
181
|
+
|
|
182
|
+
// Group by prefix for spawn/scroll classes
|
|
183
|
+
const spawnByPrefix = {};
|
|
184
|
+
const scrollByPrefix = {};
|
|
185
|
+
const nonShorthandParts = [];
|
|
186
|
+
|
|
187
|
+
innerParts.forEach(part => {
|
|
188
|
+
if (spawnShorthandRegex.test(part)) {
|
|
189
|
+
const match = part.match(spawnShorthandRegex);
|
|
190
|
+
const prefix = match[1] || ''; // Empty string for no prefix
|
|
191
|
+
|
|
192
|
+
if (!spawnByPrefix[prefix]) {
|
|
193
|
+
spawnByPrefix[prefix] = [];
|
|
194
|
+
}
|
|
195
|
+
spawnByPrefix[prefix].push(part);
|
|
196
|
+
} else if (scrollShorthandRegex.test(part) || scrollPrefixRegex.test(part)) {
|
|
197
|
+
const match = part.match(scrollShorthandRegex) || part.match(scrollPrefixRegex);
|
|
198
|
+
const prefix = match[1] || ''; // Empty string for no prefix
|
|
199
|
+
|
|
200
|
+
if (!scrollByPrefix[prefix]) {
|
|
201
|
+
scrollByPrefix[prefix] = [];
|
|
202
|
+
}
|
|
203
|
+
scrollByPrefix[prefix].push(part);
|
|
204
|
+
} else {
|
|
205
|
+
nonShorthandParts.push(part);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Add non-shorthand classes normally
|
|
210
|
+
nonShorthandParts.forEach(c => allClasses.add(c));
|
|
211
|
+
|
|
212
|
+
// Add spawn classes grouped by prefix
|
|
213
|
+
Object.values(spawnByPrefix).forEach(group => {
|
|
214
|
+
if (group.length > 0) {
|
|
215
|
+
allClasses.add(group.join(' '));
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Add scroll classes grouped by prefix
|
|
220
|
+
Object.values(scrollByPrefix).forEach(group => {
|
|
221
|
+
if (group.length > 0) {
|
|
222
|
+
allClasses.add(group.join(' '));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Group and sort motion classes by prefix length
|
|
228
|
+
const motionRegex = /^(.*?)motion-\[([^\]]+)\]:/;
|
|
229
|
+
const motionByName = {};
|
|
230
|
+
const nonMotionClasses = [];
|
|
231
|
+
|
|
232
|
+
Array.from(allClasses).forEach(cls => {
|
|
233
|
+
const match = cls.match(motionRegex);
|
|
234
|
+
if (match) {
|
|
235
|
+
const prefix = match[1]; // Everything before "motion-["
|
|
236
|
+
const animName = match[2];
|
|
237
|
+
|
|
238
|
+
if (!motionByName[animName]) {
|
|
239
|
+
motionByName[animName] = [];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
motionByName[animName].push({
|
|
243
|
+
cls: cls,
|
|
244
|
+
prefix: prefix,
|
|
245
|
+
prefixLength: prefix.length
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
nonMotionClasses.push(cls);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Sort each animation's classes by prefix length (shortest first)
|
|
253
|
+
const sortedMotionClasses = [];
|
|
254
|
+
Object.values(motionByName).forEach(group => {
|
|
255
|
+
group.sort((a, b) => a.prefixLength - b.prefixLength);
|
|
256
|
+
sortedMotionClasses.push(...group.map(item => item.cls));
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Rebuild allClasses: non-motion first, then sorted motion
|
|
260
|
+
allClasses.clear();
|
|
261
|
+
nonMotionClasses.forEach(c => allClasses.add(c));
|
|
262
|
+
sortedMotionClasses.forEach(c => allClasses.add(c));
|
|
263
|
+
|
|
264
|
+
return Array.from(allClasses);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function collectClassesJS(contentPaths) {
|
|
268
|
+
const allClasses = new Set();
|
|
269
|
+
|
|
270
|
+
// Get source configuration
|
|
271
|
+
const sourceConfig = global.xatriumSourceConfig || {
|
|
272
|
+
basePath: null,
|
|
273
|
+
disableAuto: false,
|
|
274
|
+
explicitPaths: [],
|
|
275
|
+
ignorePaths: []
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
let pathsArray = [];
|
|
279
|
+
|
|
280
|
+
// If source(none) is set, only use explicit paths
|
|
281
|
+
if (sourceConfig.disableAuto) {
|
|
282
|
+
pathsArray = sourceConfig.explicitPaths;
|
|
283
|
+
console.log('🔒 Auto-detection disabled - using explicit paths only:', pathsArray);
|
|
284
|
+
} else {
|
|
285
|
+
// Use provided contentPaths or defaults
|
|
286
|
+
pathsArray = Array.isArray(contentPaths) ? contentPaths : (contentPaths ? [contentPaths] : []);
|
|
287
|
+
|
|
288
|
+
// Add explicit paths from @source directives
|
|
289
|
+
if (sourceConfig.explicitPaths.length > 0) {
|
|
290
|
+
pathsArray = [...pathsArray, ...sourceConfig.explicitPaths];
|
|
291
|
+
console.log('➕ Added explicit paths:', sourceConfig.explicitPaths);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Apply base path if set
|
|
296
|
+
if (sourceConfig.basePath) {
|
|
297
|
+
const path = require('path');
|
|
298
|
+
pathsArray = pathsArray.map(p => {
|
|
299
|
+
const resolved = path.resolve(path.dirname(process.cwd()), sourceConfig.basePath, p);
|
|
300
|
+
return resolved;
|
|
301
|
+
});
|
|
302
|
+
console.log('📂 Applied base path:', sourceConfig.basePath);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
console.log('🔍 Scanning paths:', pathsArray);
|
|
306
|
+
|
|
307
|
+
// Scan all files matching the patterns
|
|
308
|
+
let files = [];
|
|
309
|
+
pathsArray.forEach(pattern => {
|
|
310
|
+
try {
|
|
311
|
+
const matches = glob.sync(pattern, {
|
|
312
|
+
ignore: sourceConfig.ignorePaths.length > 0 ? sourceConfig.ignorePaths : undefined
|
|
313
|
+
});
|
|
314
|
+
files.push(...matches);
|
|
315
|
+
|
|
316
|
+
if (sourceConfig.ignorePaths.length > 0) {
|
|
317
|
+
console.log(`🚫 Ignoring paths:`, sourceConfig.ignorePaths);
|
|
318
|
+
}
|
|
319
|
+
} catch (err) {
|
|
320
|
+
console.error(`❌ Error scanning pattern ${pattern}:`, err.message);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
console.log(`📄 Found ${files.length} files to scan`);
|
|
325
|
+
|
|
326
|
+
if (files.length === 0) {
|
|
327
|
+
return [];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
files.forEach(filePath => {
|
|
331
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
332
|
+
|
|
333
|
+
// Grab only strings inside single or double quotes
|
|
334
|
+
const parts = Array.from(fileContent.matchAll(/(['"])(.*?)\1/g), m => m[2]);
|
|
335
|
+
|
|
336
|
+
parts.forEach(part => {
|
|
337
|
+
let cls = part.trim();
|
|
338
|
+
if (!cls) return;
|
|
339
|
+
|
|
340
|
+
// Normalize whitespace to single spaces, but preserve inside ( [ { " '
|
|
341
|
+
let normalizedCls = "";
|
|
342
|
+
let parenDepth = 0;
|
|
343
|
+
let bracketDepth = 0;
|
|
344
|
+
let braceDepth = 0;
|
|
345
|
+
let inSingleQuote = false;
|
|
346
|
+
let inDoubleQuote = false;
|
|
347
|
+
|
|
348
|
+
for (let i = 0; i < cls.length; i++) {
|
|
349
|
+
const char = cls[i];
|
|
350
|
+
|
|
351
|
+
// Track quote state
|
|
352
|
+
if (char === "'" && !inDoubleQuote) inSingleQuote = !inSingleQuote;
|
|
353
|
+
else if (char === '"' && !inSingleQuote) inDoubleQuote = !inDoubleQuote;
|
|
354
|
+
|
|
355
|
+
const inQuotes = inSingleQuote || inDoubleQuote;
|
|
356
|
+
|
|
357
|
+
// Track bracket depths (only if not in quotes)
|
|
358
|
+
if (!inQuotes) {
|
|
359
|
+
if (char === "(") parenDepth++;
|
|
360
|
+
else if (char === ")") parenDepth--;
|
|
361
|
+
else if (char === "[") bracketDepth++;
|
|
362
|
+
else if (char === "]") bracketDepth--;
|
|
363
|
+
else if (char === "{") braceDepth++;
|
|
364
|
+
else if (char === "}") braceDepth--;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const insideGroup = parenDepth > 0 || bracketDepth > 0 || braceDepth > 0 || inQuotes;
|
|
368
|
+
|
|
369
|
+
// Normalize whitespace only when NOT inside groups
|
|
370
|
+
if (/\s/.test(char)) {
|
|
371
|
+
if (insideGroup) {
|
|
372
|
+
normalizedCls += char;
|
|
373
|
+
} else {
|
|
374
|
+
// Collapse consecutive whitespace to single space
|
|
375
|
+
if (normalizedCls.length > 0 && !/\s/.test(normalizedCls[normalizedCls.length - 1])) {
|
|
376
|
+
normalizedCls += " ";
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
normalizedCls += char;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
normalizedCls = normalizedCls.trim();
|
|
385
|
+
const innerParts = smartSplitNoBrace(normalizedCls, " ");
|
|
386
|
+
|
|
387
|
+
const spawnShorthandRegex = /^(.*?:)?spawn-((?:from|to|back|half|\[[^\]]+\])(?:\/(?:from|to|back|half|\[[^\]]+\]))*):(.+)$/;
|
|
388
|
+
const scrollShorthandRegex = /^(.*?:)?scroll-((?:from|to|back|half|\[[^\]]+\])(?:\/(?:from|to|back|half|\[[^\]]+\]))*):(.+)$/;
|
|
389
|
+
const scrollPrefixRegex = /^(.*?:)?scroll:(.+)$/;
|
|
390
|
+
|
|
391
|
+
// Group by prefix for spawn/scroll classes
|
|
392
|
+
const spawnByPrefix = {};
|
|
393
|
+
const scrollByPrefix = {};
|
|
394
|
+
const nonShorthandParts = [];
|
|
395
|
+
|
|
396
|
+
innerParts.forEach(part => {
|
|
397
|
+
if (spawnShorthandRegex.test(part)) {
|
|
398
|
+
const match = part.match(spawnShorthandRegex);
|
|
399
|
+
const prefix = match[1] || ''; // Empty string for no prefix
|
|
400
|
+
|
|
401
|
+
if (!spawnByPrefix[prefix]) {
|
|
402
|
+
spawnByPrefix[prefix] = [];
|
|
403
|
+
}
|
|
404
|
+
spawnByPrefix[prefix].push(part);
|
|
405
|
+
} else if (scrollShorthandRegex.test(part) || scrollPrefixRegex.test(part)) {
|
|
406
|
+
const match = part.match(scrollShorthandRegex) || part.match(scrollPrefixRegex);
|
|
407
|
+
const prefix = match[1] || ''; // Empty string for no prefix
|
|
408
|
+
|
|
409
|
+
if (!scrollByPrefix[prefix]) {
|
|
410
|
+
scrollByPrefix[prefix] = [];
|
|
411
|
+
}
|
|
412
|
+
scrollByPrefix[prefix].push(part);
|
|
413
|
+
} else {
|
|
414
|
+
nonShorthandParts.push(part);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Add non-shorthand classes normally
|
|
419
|
+
nonShorthandParts.forEach(c => allClasses.add(c));
|
|
420
|
+
|
|
421
|
+
// Add spawn classes grouped by prefix
|
|
422
|
+
Object.values(spawnByPrefix).forEach(group => {
|
|
423
|
+
if (group.length > 0) {
|
|
424
|
+
allClasses.add(group.join(' '));
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// Add scroll classes grouped by prefix
|
|
429
|
+
Object.values(scrollByPrefix).forEach(group => {
|
|
430
|
+
if (group.length > 0) {
|
|
431
|
+
allClasses.add(group.join(' '));
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// Group and sort motion classes by prefix length
|
|
438
|
+
const motionRegex = /^(.*?)motion-\[([^\]]+)\]:/;
|
|
439
|
+
const motionByName = {};
|
|
440
|
+
const nonMotionClasses = [];
|
|
441
|
+
|
|
442
|
+
Array.from(allClasses).forEach(cls => {
|
|
443
|
+
const match = cls.match(motionRegex);
|
|
444
|
+
if (match) {
|
|
445
|
+
const prefix = match[1]; // Everything before "motion-["
|
|
446
|
+
const animName = match[2];
|
|
447
|
+
|
|
448
|
+
if (!motionByName[animName]) {
|
|
449
|
+
motionByName[animName] = [];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
motionByName[animName].push({
|
|
453
|
+
cls: cls,
|
|
454
|
+
prefix: prefix,
|
|
455
|
+
prefixLength: prefix.length
|
|
456
|
+
});
|
|
457
|
+
} else {
|
|
458
|
+
nonMotionClasses.push(cls);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Sort each animation's classes by prefix length (shortest first)
|
|
463
|
+
const sortedMotionClasses = [];
|
|
464
|
+
Object.values(motionByName).forEach(group => {
|
|
465
|
+
group.sort((a, b) => a.prefixLength - b.prefixLength);
|
|
466
|
+
sortedMotionClasses.push(...group.map(item => item.cls));
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Rebuild allClasses: non-motion first, then sorted motion
|
|
470
|
+
allClasses.clear();
|
|
471
|
+
nonMotionClasses.forEach(c => allClasses.add(c));
|
|
472
|
+
sortedMotionClasses.forEach(c => allClasses.add(c));
|
|
473
|
+
|
|
474
|
+
return Array.from(allClasses);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
module.exports = collectClasses;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// scripts/index.js
|
|
2
|
+
const { platform, arch } = process;
|
|
3
|
+
|
|
4
|
+
const platformMap = {
|
|
5
|
+
'win32-x64': 'win32-x64-msvc',
|
|
6
|
+
'darwin-x64': 'darwin-x64',
|
|
7
|
+
'darwin-arm64': 'darwin-arm64',
|
|
8
|
+
'linux-x64': 'linux-x64-gnu',
|
|
9
|
+
'linux-arm64': 'linux-arm64-gnu'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const platformKey = `${platform}-${arch}`;
|
|
13
|
+
const nativeName = platformMap[platformKey];
|
|
14
|
+
|
|
15
|
+
if (!nativeName) {
|
|
16
|
+
throw new Error(`Unsupported platform: ${platformKey}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let native;
|
|
20
|
+
try {
|
|
21
|
+
native = require(`./xatriumcss.${nativeName}.node`);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
throw new Error(`Failed to load native module for ${platformKey}: ${err.message}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = native;
|