xatriumcss 1.0.2 → 1.0.5
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/package.json +1 -1
- package/src/cli.js +1 -1
- package/src/collectClasses.js +72 -12
- package/src/parser.js +10 -214
package/package.json
CHANGED
package/src/cli.js
CHANGED
package/src/collectClasses.js
CHANGED
|
@@ -3,9 +3,10 @@ const { glob } = require('glob');
|
|
|
3
3
|
|
|
4
4
|
let nativeScanner;
|
|
5
5
|
try {
|
|
6
|
-
nativeScanner = require('
|
|
6
|
+
nativeScanner = require('@xatriumcss/oxide');
|
|
7
|
+
console.log("using native scanner")
|
|
7
8
|
} catch (e) {
|
|
8
|
-
console.
|
|
9
|
+
console.log("using fallback scanner")
|
|
9
10
|
nativeScanner = null;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -98,20 +99,67 @@ function collectClasses(contentPaths) {
|
|
|
98
99
|
// Use native scanner if available
|
|
99
100
|
if (nativeScanner) {
|
|
100
101
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// Add patterns
|
|
102
|
+
// Build paths array
|
|
104
103
|
let pathsArray = sourceConfig.disableAuto
|
|
105
104
|
? sourceConfig.explicitPaths
|
|
106
105
|
: [...(Array.isArray(contentPaths) ? contentPaths : [contentPaths]), ...sourceConfig.explicitPaths];
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
// Filter out any null/undefined values
|
|
108
|
+
pathsArray = pathsArray.filter(p => p != null);
|
|
109
|
+
|
|
110
|
+
if (pathsArray.length === 0) {
|
|
111
|
+
console.warn('⚠️ No valid patterns to scan, falling back to JS scanner');
|
|
112
|
+
throw new Error('No patterns provided');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Convert patterns to SourceEntry format
|
|
116
|
+
const sources = [];
|
|
117
|
+
|
|
118
|
+
// Add positive patterns
|
|
119
|
+
pathsArray.forEach(pattern => {
|
|
120
|
+
sources.push({
|
|
121
|
+
base: process.cwd(),
|
|
122
|
+
pattern: pattern,
|
|
123
|
+
negated: false
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Add negative patterns (ignore paths)
|
|
128
|
+
sourceConfig.ignorePaths.forEach(pattern => {
|
|
129
|
+
sources.push({
|
|
130
|
+
base: process.cwd(),
|
|
131
|
+
pattern: pattern,
|
|
132
|
+
negated: true
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
console.log('🚀 Using native scanner (Rust) with patterns:', pathsArray);
|
|
137
|
+
|
|
138
|
+
// Create scanner with options
|
|
139
|
+
const scanner = new nativeScanner.Scanner({ sources });
|
|
110
140
|
|
|
111
|
-
|
|
112
|
-
const
|
|
141
|
+
// Get the files that were scanned
|
|
142
|
+
const files = scanner.files;
|
|
113
143
|
|
|
114
|
-
|
|
144
|
+
console.log(`📄 Native scanner found ${files.length} files`);
|
|
145
|
+
|
|
146
|
+
// Read files and extract quoted strings manually to preserve grouping
|
|
147
|
+
const quotedStrings = [];
|
|
148
|
+
files.forEach(filePath => {
|
|
149
|
+
try {
|
|
150
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
151
|
+
// Extract strings inside quotes (single, double, or backticks)
|
|
152
|
+
const matches = fileContent.matchAll(/(['"`])(.*?)\1/gs);
|
|
153
|
+
for (const match of matches) {
|
|
154
|
+
quotedStrings.push(match[2]);
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
console.warn(`⚠️ Error reading file ${filePath}:`, err.message);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
console.log(`✅ Native scanner extracted ${quotedStrings.length} quoted strings`);
|
|
162
|
+
return processClasses(quotedStrings);
|
|
115
163
|
} catch (error) {
|
|
116
164
|
console.warn('⚠️ Native scanner failed, falling back:', error.message);
|
|
117
165
|
}
|
|
@@ -122,9 +170,21 @@ function collectClasses(contentPaths) {
|
|
|
122
170
|
}
|
|
123
171
|
|
|
124
172
|
function processClasses(rawClasses) {
|
|
173
|
+
// Additional safety check
|
|
174
|
+
if (!rawClasses || !Array.isArray(rawClasses)) {
|
|
175
|
+
console.warn('⚠️ processClasses received invalid input:', typeof rawClasses);
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
|
|
125
179
|
const allClasses = new Set();
|
|
126
180
|
|
|
127
181
|
rawClasses.forEach(part => {
|
|
182
|
+
// Ensure part is a string
|
|
183
|
+
if (typeof part !== 'string') {
|
|
184
|
+
console.warn('⚠️ Skipping non-string class:', typeof part, part);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
128
188
|
let cls = part.trim();
|
|
129
189
|
if (!cls) return;
|
|
130
190
|
|
|
@@ -330,8 +390,8 @@ function collectClassesJS(contentPaths) {
|
|
|
330
390
|
files.forEach(filePath => {
|
|
331
391
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
332
392
|
|
|
333
|
-
// Grab only strings inside single
|
|
334
|
-
const parts = Array.from(fileContent.matchAll(/(['"])(.*?)\1/
|
|
393
|
+
// Grab only strings inside single quotes, double quotes, or backticks
|
|
394
|
+
const parts = Array.from(fileContent.matchAll(/(['"`])(.*?)\1/gs), m => m[2]);
|
|
335
395
|
|
|
336
396
|
parts.forEach(part => {
|
|
337
397
|
let cls = part.trim();
|
package/src/parser.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { glob } = require('glob');
|
|
3
|
+
const collectClasses = require('./collectClasses.js');
|
|
3
4
|
|
|
4
5
|
// --- 1. Fill this array with all your CSS properties ---
|
|
5
6
|
const allCSSProperties = [
|
|
@@ -17408,7 +17409,7 @@ function _parseClassInternal(cls, isFromGrouped = false, needed, isGlobal) {
|
|
|
17408
17409
|
|
|
17409
17410
|
const shadowVal = parseColor(afterShadow).color;
|
|
17410
17411
|
|
|
17411
|
-
return buildParsed(isInset ? "--xt-shadow-inset-color" : "--xt-shadow-color",
|
|
17412
|
+
return buildParsed(isInset ? "--xt-shadow-inset-color" : "--xt-shadow-color", shadowVal)
|
|
17412
17413
|
}
|
|
17413
17414
|
|
|
17414
17415
|
if (textShadowRegex.test(cls)) {
|
|
@@ -22876,216 +22877,6 @@ function parseXatriumCSSConfig(inputCssPath) {
|
|
|
22876
22877
|
xatriumSourceInlines.notInline = Array.from(new Set(xatriumSourceInlines.notInline));
|
|
22877
22878
|
}
|
|
22878
22879
|
|
|
22879
|
-
function collectClasses(contentPaths) {
|
|
22880
|
-
const allClasses = new Set();
|
|
22881
|
-
|
|
22882
|
-
// Get source configuration
|
|
22883
|
-
const sourceConfig = global.xatriumSourceConfig || {
|
|
22884
|
-
basePath: null,
|
|
22885
|
-
disableAuto: false,
|
|
22886
|
-
explicitPaths: [],
|
|
22887
|
-
ignorePaths: []
|
|
22888
|
-
};
|
|
22889
|
-
|
|
22890
|
-
let pathsArray = [];
|
|
22891
|
-
|
|
22892
|
-
// If source(none) is set, only use explicit paths
|
|
22893
|
-
if (sourceConfig.disableAuto) {
|
|
22894
|
-
pathsArray = sourceConfig.explicitPaths;
|
|
22895
|
-
console.log('🔒 Auto-detection disabled - using explicit paths only:', pathsArray);
|
|
22896
|
-
} else {
|
|
22897
|
-
// Use provided contentPaths or defaults
|
|
22898
|
-
pathsArray = Array.isArray(contentPaths) ? contentPaths : (contentPaths ? [contentPaths] : []);
|
|
22899
|
-
|
|
22900
|
-
// Add explicit paths from @source directives
|
|
22901
|
-
if (sourceConfig.explicitPaths.length > 0) {
|
|
22902
|
-
pathsArray = [...pathsArray, ...sourceConfig.explicitPaths];
|
|
22903
|
-
console.log('➕ Added explicit paths:', sourceConfig.explicitPaths);
|
|
22904
|
-
}
|
|
22905
|
-
}
|
|
22906
|
-
|
|
22907
|
-
// Apply base path if set
|
|
22908
|
-
if (sourceConfig.basePath) {
|
|
22909
|
-
const path = require('path');
|
|
22910
|
-
pathsArray = pathsArray.map(p => {
|
|
22911
|
-
const resolved = path.resolve(path.dirname(process.cwd()), sourceConfig.basePath, p);
|
|
22912
|
-
return resolved;
|
|
22913
|
-
});
|
|
22914
|
-
console.log('📂 Applied base path:', sourceConfig.basePath);
|
|
22915
|
-
}
|
|
22916
|
-
|
|
22917
|
-
console.log('🔍 Scanning paths:', pathsArray);
|
|
22918
|
-
|
|
22919
|
-
// Scan all files matching the patterns
|
|
22920
|
-
let files = [];
|
|
22921
|
-
pathsArray.forEach(pattern => {
|
|
22922
|
-
try {
|
|
22923
|
-
const matches = glob.sync(pattern, {
|
|
22924
|
-
ignore: sourceConfig.ignorePaths.length > 0 ? sourceConfig.ignorePaths : undefined
|
|
22925
|
-
});
|
|
22926
|
-
files.push(...matches);
|
|
22927
|
-
|
|
22928
|
-
if (sourceConfig.ignorePaths.length > 0) {
|
|
22929
|
-
console.log(`🚫 Ignoring paths:`, sourceConfig.ignorePaths);
|
|
22930
|
-
}
|
|
22931
|
-
} catch (err) {
|
|
22932
|
-
console.error(`❌ Error scanning pattern ${pattern}:`, err.message);
|
|
22933
|
-
}
|
|
22934
|
-
});
|
|
22935
|
-
|
|
22936
|
-
console.log(`📄 Found ${files.length} files to scan`);
|
|
22937
|
-
|
|
22938
|
-
if (files.length === 0) {
|
|
22939
|
-
return [];
|
|
22940
|
-
}
|
|
22941
|
-
|
|
22942
|
-
files.forEach(filePath => {
|
|
22943
|
-
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
22944
|
-
|
|
22945
|
-
// Grab only strings inside single or double quotes
|
|
22946
|
-
const parts = Array.from(fileContent.matchAll(/(['"])(.*?)\1/g), m => m[2]);
|
|
22947
|
-
|
|
22948
|
-
parts.forEach(part => {
|
|
22949
|
-
let cls = part.trim();
|
|
22950
|
-
if (!cls) return;
|
|
22951
|
-
|
|
22952
|
-
// Normalize whitespace to single spaces, but preserve inside ( [ { " '
|
|
22953
|
-
let normalizedCls = "";
|
|
22954
|
-
let parenDepth = 0;
|
|
22955
|
-
let bracketDepth = 0;
|
|
22956
|
-
let braceDepth = 0;
|
|
22957
|
-
let inSingleQuote = false;
|
|
22958
|
-
let inDoubleQuote = false;
|
|
22959
|
-
|
|
22960
|
-
for (let i = 0; i < cls.length; i++) {
|
|
22961
|
-
const char = cls[i];
|
|
22962
|
-
|
|
22963
|
-
// Track quote state
|
|
22964
|
-
if (char === "'" && !inDoubleQuote) inSingleQuote = !inSingleQuote;
|
|
22965
|
-
else if (char === '"' && !inSingleQuote) inDoubleQuote = !inDoubleQuote;
|
|
22966
|
-
|
|
22967
|
-
const inQuotes = inSingleQuote || inDoubleQuote;
|
|
22968
|
-
|
|
22969
|
-
// Track bracket depths (only if not in quotes)
|
|
22970
|
-
if (!inQuotes) {
|
|
22971
|
-
if (char === "(") parenDepth++;
|
|
22972
|
-
else if (char === ")") parenDepth--;
|
|
22973
|
-
else if (char === "[") bracketDepth++;
|
|
22974
|
-
else if (char === "]") bracketDepth--;
|
|
22975
|
-
else if (char === "{") braceDepth++;
|
|
22976
|
-
else if (char === "}") braceDepth--;
|
|
22977
|
-
}
|
|
22978
|
-
|
|
22979
|
-
const insideGroup = parenDepth > 0 || bracketDepth > 0 || braceDepth > 0 || inQuotes;
|
|
22980
|
-
|
|
22981
|
-
// Normalize whitespace only when NOT inside groups
|
|
22982
|
-
if (/\s/.test(char)) {
|
|
22983
|
-
if (insideGroup) {
|
|
22984
|
-
normalizedCls += char;
|
|
22985
|
-
} else {
|
|
22986
|
-
// Collapse consecutive whitespace to single space
|
|
22987
|
-
if (normalizedCls.length > 0 && !/\s/.test(normalizedCls[normalizedCls.length - 1])) {
|
|
22988
|
-
normalizedCls += " ";
|
|
22989
|
-
}
|
|
22990
|
-
}
|
|
22991
|
-
} else {
|
|
22992
|
-
normalizedCls += char;
|
|
22993
|
-
}
|
|
22994
|
-
}
|
|
22995
|
-
|
|
22996
|
-
normalizedCls = normalizedCls.trim();
|
|
22997
|
-
const innerParts = smartSplitNoBrace(normalizedCls, " ");
|
|
22998
|
-
|
|
22999
|
-
const spawnShorthandRegex = /^(.*?:)?spawn-((?:from|to|back|half|\[[^\]]+\])(?:\/(?:from|to|back|half|\[[^\]]+\]))*):(.+)$/;
|
|
23000
|
-
const scrollShorthandRegex = /^(.*?:)?scroll-((?:from|to|back|half|\[[^\]]+\])(?:\/(?:from|to|back|half|\[[^\]]+\]))*):(.+)$/;
|
|
23001
|
-
const scrollPrefixRegex = /^(.*?:)?scroll:(.+)$/;
|
|
23002
|
-
|
|
23003
|
-
// Group by prefix for spawn/scroll classes
|
|
23004
|
-
const spawnByPrefix = {};
|
|
23005
|
-
const scrollByPrefix = {};
|
|
23006
|
-
const nonShorthandParts = [];
|
|
23007
|
-
|
|
23008
|
-
innerParts.forEach(part => {
|
|
23009
|
-
if (spawnShorthandRegex.test(part)) {
|
|
23010
|
-
const match = part.match(spawnShorthandRegex);
|
|
23011
|
-
const prefix = match[1] || ''; // Empty string for no prefix
|
|
23012
|
-
|
|
23013
|
-
if (!spawnByPrefix[prefix]) {
|
|
23014
|
-
spawnByPrefix[prefix] = [];
|
|
23015
|
-
}
|
|
23016
|
-
spawnByPrefix[prefix].push(part);
|
|
23017
|
-
} else if (scrollShorthandRegex.test(part) || scrollPrefixRegex.test(part)) {
|
|
23018
|
-
const match = part.match(scrollShorthandRegex) || part.match(scrollPrefixRegex);
|
|
23019
|
-
const prefix = match[1] || ''; // Empty string for no prefix
|
|
23020
|
-
|
|
23021
|
-
if (!scrollByPrefix[prefix]) {
|
|
23022
|
-
scrollByPrefix[prefix] = [];
|
|
23023
|
-
}
|
|
23024
|
-
scrollByPrefix[prefix].push(part);
|
|
23025
|
-
} else {
|
|
23026
|
-
nonShorthandParts.push(part);
|
|
23027
|
-
}
|
|
23028
|
-
});
|
|
23029
|
-
|
|
23030
|
-
// Add non-shorthand classes normally
|
|
23031
|
-
nonShorthandParts.forEach(c => allClasses.add(c));
|
|
23032
|
-
|
|
23033
|
-
// Add spawn classes grouped by prefix
|
|
23034
|
-
Object.values(spawnByPrefix).forEach(group => {
|
|
23035
|
-
if (group.length > 0) {
|
|
23036
|
-
allClasses.add(group.join(' '));
|
|
23037
|
-
}
|
|
23038
|
-
});
|
|
23039
|
-
|
|
23040
|
-
// Add scroll classes grouped by prefix
|
|
23041
|
-
Object.values(scrollByPrefix).forEach(group => {
|
|
23042
|
-
if (group.length > 0) {
|
|
23043
|
-
allClasses.add(group.join(' '));
|
|
23044
|
-
}
|
|
23045
|
-
});
|
|
23046
|
-
});
|
|
23047
|
-
});
|
|
23048
|
-
|
|
23049
|
-
// Group and sort motion classes by prefix length
|
|
23050
|
-
const motionRegex = /^(.*?)motion-\[([^\]]+)\]:/;
|
|
23051
|
-
const motionByName = {};
|
|
23052
|
-
const nonMotionClasses = [];
|
|
23053
|
-
|
|
23054
|
-
Array.from(allClasses).forEach(cls => {
|
|
23055
|
-
const match = cls.match(motionRegex);
|
|
23056
|
-
if (match) {
|
|
23057
|
-
const prefix = match[1]; // Everything before "motion-["
|
|
23058
|
-
const animName = match[2];
|
|
23059
|
-
|
|
23060
|
-
if (!motionByName[animName]) {
|
|
23061
|
-
motionByName[animName] = [];
|
|
23062
|
-
}
|
|
23063
|
-
|
|
23064
|
-
motionByName[animName].push({
|
|
23065
|
-
cls: cls,
|
|
23066
|
-
prefix: prefix,
|
|
23067
|
-
prefixLength: prefix.length
|
|
23068
|
-
});
|
|
23069
|
-
} else {
|
|
23070
|
-
nonMotionClasses.push(cls);
|
|
23071
|
-
}
|
|
23072
|
-
});
|
|
23073
|
-
|
|
23074
|
-
// Sort each animation's classes by prefix length (shortest first)
|
|
23075
|
-
const sortedMotionClasses = [];
|
|
23076
|
-
Object.values(motionByName).forEach(group => {
|
|
23077
|
-
group.sort((a, b) => a.prefixLength - b.prefixLength);
|
|
23078
|
-
sortedMotionClasses.push(...group.map(item => item.cls));
|
|
23079
|
-
});
|
|
23080
|
-
|
|
23081
|
-
// Rebuild allClasses: non-motion first, then sorted motion
|
|
23082
|
-
allClasses.clear();
|
|
23083
|
-
nonMotionClasses.forEach(c => allClasses.add(c));
|
|
23084
|
-
sortedMotionClasses.forEach(c => allClasses.add(c));
|
|
23085
|
-
|
|
23086
|
-
return Array.from(allClasses);
|
|
23087
|
-
}
|
|
23088
|
-
|
|
23089
22880
|
const parseMediaConditions = (input, isContainer = false, isSupports = false) => {
|
|
23090
22881
|
// ✅ Detect custom: prefix at the start and return as-is
|
|
23091
22882
|
if (input.startsWith('custom:')) {
|
|
@@ -25938,8 +25729,10 @@ function runParser(config) {
|
|
|
25938
25729
|
}
|
|
25939
25730
|
|
|
25940
25731
|
if (result.type === 'first') {
|
|
25941
|
-
|
|
25732
|
+
const finalCSS = removeNewlines(result.css);
|
|
25733
|
+
fs.writeFileSync(outputPath, finalCSS, 'utf8');
|
|
25942
25734
|
isFirstRun = false;
|
|
25735
|
+
return finalCSS;
|
|
25943
25736
|
} else {
|
|
25944
25737
|
// Incremental: append to existing layers
|
|
25945
25738
|
let currentCSS = fs.readFileSync(outputPath, 'utf8');
|
|
@@ -25951,8 +25744,10 @@ function runParser(config) {
|
|
|
25951
25744
|
previouslyParsedClasses = [];
|
|
25952
25745
|
previouslyGeneratedProps.clear();
|
|
25953
25746
|
const fullResult = generateCSSString(collectClasses(), false, isFirstRun);
|
|
25954
|
-
|
|
25955
|
-
|
|
25747
|
+
const finalCSS = removeNewlines(fullResult.css); // ← Use fullResult, not result
|
|
25748
|
+
fs.writeFileSync(outputPath, finalCSS, 'utf8');
|
|
25749
|
+
isFirstRun = false; // ← Reset the flag after rebuild
|
|
25750
|
+
return finalCSS; // ← Return the CSS instead of undefined
|
|
25956
25751
|
}
|
|
25957
25752
|
|
|
25958
25753
|
result.layers.forEach(layer => {
|
|
@@ -26104,6 +25899,7 @@ function runParser(config) {
|
|
|
26104
25899
|
});
|
|
26105
25900
|
|
|
26106
25901
|
fs.writeFileSync(outputPath, removeNewlines(currentCSS), 'utf8');
|
|
25902
|
+
return removeNewlines(currentCSS); // ← ADD THIS
|
|
26107
25903
|
}
|
|
26108
25904
|
}
|
|
26109
25905
|
|