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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xatriumcss",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "description": "Ultra-fast utility-first CSS framework",
5
5
  "main": "./src/index.js",
6
6
  "files": [
package/src/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const { runParser, hasXatriumImport } = require('./parser');
4
4
  const path = require('path');
@@ -3,9 +3,10 @@ const { glob } = require('glob');
3
3
 
4
4
  let nativeScanner;
5
5
  try {
6
- nativeScanner = require('../scripts/index.js');
6
+ nativeScanner = require('@xatriumcss/oxide');
7
+ console.log("using native scanner")
7
8
  } catch (e) {
8
- console.warn('⚠️ Native scanner not available, falling back to JavaScript');
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
- const scanner = new nativeScanner.Scanner();
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
- pathsArray.forEach(pattern => scanner.addPattern(pattern));
109
- sourceConfig.ignorePaths.forEach(pattern => scanner.addIgnorePattern(pattern));
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
- console.log('🚀 Using native scanner (Rust)');
112
- const rawClasses = scanner.scanAndExtract();
141
+ // Get the files that were scanned
142
+ const files = scanner.files;
113
143
 
114
- return processClasses(rawClasses);
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 or double quotes
334
- const parts = Array.from(fileContent.matchAll(/(['"])(.*?)\1/g), m => m[2]);
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", shadowValue)
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
- fs.writeFileSync(outputPath, removeNewlines(result.css), 'utf8');
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
- fs.writeFileSync(outputPath, removeNewlines(result.css), 'utf8');
25955
- return;
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