xatriumcss 1.0.3 → 1.0.6

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.3",
3
+ "version": "1.0.6",
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,11 @@ const { glob } = require('glob');
3
3
 
4
4
  let nativeScanner;
5
5
  try {
6
- nativeScanner = require('../scripts/index.js');
6
+ nativeScanner = require('../index');
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")
10
+ console.log("ERROR:", e.message);
9
11
  nativeScanner = null;
10
12
  }
11
13
 
@@ -98,22 +100,68 @@ function collectClasses(contentPaths) {
98
100
  // Use native scanner if available
99
101
  if (nativeScanner) {
100
102
  try {
101
- const scanner = new nativeScanner.Scanner();
102
-
103
- // Add patterns
103
+ // Build paths array
104
104
  let pathsArray = sourceConfig.disableAuto
105
105
  ? sourceConfig.explicitPaths
106
106
  : [...(Array.isArray(contentPaths) ? contentPaths : [contentPaths]), ...sourceConfig.explicitPaths];
107
107
 
108
- pathsArray.forEach(pattern => scanner.addPattern(pattern));
109
- sourceConfig.ignorePaths.forEach(pattern => scanner.addIgnorePattern(pattern));
108
+ // Filter out any null/undefined values
109
+ pathsArray = pathsArray.filter(p => p != null);
110
+
111
+ if (pathsArray.length === 0) {
112
+ console.warn('⚠️ No valid patterns to scan, falling back to JS scanner');
113
+ throw new Error('No patterns provided');
114
+ }
115
+
116
+ // Convert patterns to SourceEntry format
117
+ const sources = [];
118
+
119
+ // Add positive patterns
120
+ pathsArray.forEach(pattern => {
121
+ sources.push({
122
+ base: process.cwd(),
123
+ pattern: pattern,
124
+ negated: false
125
+ });
126
+ });
127
+
128
+ // Add negative patterns (ignore paths)
129
+ sourceConfig.ignorePaths.forEach(pattern => {
130
+ sources.push({
131
+ base: process.cwd(),
132
+ pattern: pattern,
133
+ negated: true
134
+ });
135
+ });
136
+
137
+ console.log('🚀 Using native scanner (Rust) for file discovery');
138
+ console.log('📋 Patterns:', pathsArray);
139
+
140
+ // Create scanner with options
141
+ let scanner;
142
+ try {
143
+ scanner = new nativeScanner.Scanner({ sources });
144
+ } catch (error) {
145
+ console.warn('⚠️ Native scanner failed, falling back to JS:', error.message);
146
+ }
147
+
148
+ // Get the files that were matched by the scanner
149
+ const files = scanner.files;
150
+
151
+ if (!files || files.length === 0) {
152
+ console.warn('⚠️ Native scanner found no files');
153
+ throw new Error('No files found');
154
+ }
155
+
156
+ console.log(`📄 Native scanner found ${files.length} files`);
110
157
 
111
- console.log('🚀 Using native scanner (Rust)');
112
- const rawClasses = scanner.scanAndExtract();
158
+ // Read files and extract quoted strings manually to preserve grouping
159
+ const quotedStrings = scanner.scan(); // Rust does EVERYTHING!
113
160
 
114
- return processClasses(rawClasses);
161
+ console.log(`✅ Extracted ${quotedStrings.length} quoted strings from files`);
162
+ return processClasses(quotedStrings);
115
163
  } catch (error) {
116
- console.warn('⚠️ Native scanner failed, falling back:', error.message);
164
+ console.warn('⚠️ Native scanner failed, falling back to JS:', error.message);
117
165
  }
118
166
  }
119
167
 
@@ -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 = [
@@ -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:')) {
@@ -25952,7 +25743,7 @@ function runParser(config) {
25952
25743
  isFirstRun = true;
25953
25744
  previouslyParsedClasses = [];
25954
25745
  previouslyGeneratedProps.clear();
25955
- const fullResult = generateCSSString(collectClasses(), false, isFirstRun);
25746
+ const fullResult = generateCSSString(collectClasses(config.content), false, isFirstRun);
25956
25747
  const finalCSS = removeNewlines(fullResult.css); // ← Use fullResult, not result
25957
25748
  fs.writeFileSync(outputPath, finalCSS, 'utf8');
25958
25749
  isFirstRun = false; // ← Reset the flag after rebuild
Binary file
Binary file