@melcanz85/chaincss 1.7.3 → 1.9.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 +323 -348
- package/atomic-optimizer.js +275 -0
- package/cache-manager.js +68 -0
- package/chaincss.js +183 -16
- package/index.js +24 -0
- package/index.react.js +4 -0
- package/package.json +25 -2
- package/prefixer.js +2 -2
- package/react-hooks.js +175 -0
- package/tokens.js +256 -0
- package/transpiler.js +155 -204
- package/types.d.ts +148 -0
- package/.github/workflows/publish.yml +0 -22
- package/publish.sh +0 -7
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
class AtomicOptimizer {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = {
|
|
7
|
+
enabled: true,
|
|
8
|
+
threshold: 3,
|
|
9
|
+
naming: 'hash',
|
|
10
|
+
cache: true,
|
|
11
|
+
cachePath: './.chaincss-cache',
|
|
12
|
+
minify: true,
|
|
13
|
+
...options
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
this.usageCount = new Map();
|
|
17
|
+
this.atomicClasses = new Map();
|
|
18
|
+
this.stats = {
|
|
19
|
+
totalStyles: 0,
|
|
20
|
+
atomicStyles: 0,
|
|
21
|
+
standardStyles: 0,
|
|
22
|
+
uniqueProperties: 0
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
this.cache = null;
|
|
26
|
+
if (this.options.cache) {
|
|
27
|
+
this.loadCache();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
loadCache() {
|
|
32
|
+
try {
|
|
33
|
+
if (fs.existsSync(this.options.cachePath)) {
|
|
34
|
+
const data = fs.readFileSync(this.options.cachePath, 'utf8');
|
|
35
|
+
const cache = JSON.parse(data);
|
|
36
|
+
|
|
37
|
+
// Check version compatibility
|
|
38
|
+
if (cache.version === '1.0.0') {
|
|
39
|
+
this.atomicClasses = new Map(cache.atomicClasses || []);
|
|
40
|
+
this.stats = cache.stats || this.stats;
|
|
41
|
+
|
|
42
|
+
const cacheTime = new Date(cache.timestamp).toLocaleString();
|
|
43
|
+
console.log(`Loaded ${this.atomicClasses.size} atomic classes from cache (${cacheTime})`);
|
|
44
|
+
|
|
45
|
+
// Verify config matches
|
|
46
|
+
if (cache.config) {
|
|
47
|
+
if (cache.config.threshold !== this.options.threshold) {
|
|
48
|
+
console.log(`Cache threshold (${cache.config.threshold}) differs from current (${this.options.threshold})`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
console.log('Cache version mismatch, creating new cache');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.log('Could not load cache:', err.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
saveCache() {
|
|
61
|
+
try {
|
|
62
|
+
const cache = {
|
|
63
|
+
version: '1.0.0',
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
atomicClasses: Array.from(this.atomicClasses.entries()),
|
|
66
|
+
stats: this.stats
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Keep only last 5 cache files for rollback
|
|
70
|
+
const cacheDir = path.dirname(this.options.cachePath);
|
|
71
|
+
if (fs.existsSync(cacheDir)) {
|
|
72
|
+
const files = fs.readdirSync(cacheDir)
|
|
73
|
+
.filter(f => f.startsWith('.chaincss-cache'))
|
|
74
|
+
.map(f => ({
|
|
75
|
+
name: f,
|
|
76
|
+
time: fs.statSync(path.join(cacheDir, f)).mtime.getTime()
|
|
77
|
+
}))
|
|
78
|
+
.sort((a, b) => b.time - a.time);
|
|
79
|
+
|
|
80
|
+
files.slice(4).forEach(f => {
|
|
81
|
+
fs.unlinkSync(path.join(cacheDir, f.name));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fs.writeFileSync(this.options.cachePath, JSON.stringify(cache, null, 2), 'utf8');
|
|
86
|
+
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.log('Could not save cache:', err.message);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
trackStyles(styles) {
|
|
93
|
+
Object.values(styles).forEach(style => {
|
|
94
|
+
Object.entries(style).forEach(([prop, value]) => {
|
|
95
|
+
if (prop === 'selectors') return;
|
|
96
|
+
const key = `${prop}:${value}`;
|
|
97
|
+
this.usageCount.set(key, (this.usageCount.get(key) || 0) + 1);
|
|
98
|
+
this.stats.totalStyles++;
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
this.stats.uniqueProperties = this.usageCount.size;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
shouldBeAtomic(prop, value) {
|
|
105
|
+
const key = `${prop}:${value}`;
|
|
106
|
+
const usage = this.usageCount.get(key) || 0;
|
|
107
|
+
|
|
108
|
+
// Don't atomic-ify important layout properties that affect order
|
|
109
|
+
const criticalProps = ['position', 'display', 'flex', 'grid', 'z-index'];
|
|
110
|
+
const isCritical = criticalProps.some(p => prop.includes(p));
|
|
111
|
+
|
|
112
|
+
if (isCritical && usage < this.options.threshold * 2) {
|
|
113
|
+
return false; // Keep critical styles in place
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return usage >= this.options.threshold;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
generateClassName(prop, value) {
|
|
120
|
+
const key = `${prop}:${value}`;
|
|
121
|
+
|
|
122
|
+
if (this.options.naming === 'hash') {
|
|
123
|
+
let hash = 0;
|
|
124
|
+
for (let i = 0; i < key.length; i++) {
|
|
125
|
+
hash = ((hash << 5) - hash) + key.charCodeAt(i);
|
|
126
|
+
hash |= 0;
|
|
127
|
+
}
|
|
128
|
+
return `_${Math.abs(hash).toString(36).substring(0, 6)}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const kebabProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
132
|
+
const safeValue = value.replace(/[^a-zA-Z0-9-]/g, '-');
|
|
133
|
+
return `${kebabProp}-${safeValue}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getOrCreateAtomic(prop, value) {
|
|
137
|
+
const key = `${prop}:${value}`;
|
|
138
|
+
|
|
139
|
+
if (!this.atomicClasses.has(key)) {
|
|
140
|
+
const className = this.generateClassName(prop, value);
|
|
141
|
+
this.atomicClasses.set(key, {
|
|
142
|
+
className,
|
|
143
|
+
prop,
|
|
144
|
+
value,
|
|
145
|
+
usageCount: this.usageCount.get(key) || 0
|
|
146
|
+
});
|
|
147
|
+
this.stats.atomicStyles++;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return this.atomicClasses.get(key).className;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
findKeyByClassName(className) {
|
|
154
|
+
for (let [key, value] of this.atomicClasses.entries()) {
|
|
155
|
+
if (value.className === className) return key;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
generateAtomicCSS() {
|
|
161
|
+
let css = '';
|
|
162
|
+
|
|
163
|
+
const sortedClasses = Array.from(this.atomicClasses.values())
|
|
164
|
+
.sort((a, b) => b.usageCount - a.usageCount);
|
|
165
|
+
|
|
166
|
+
sortedClasses.forEach(atomic => {
|
|
167
|
+
const kebabProp = atomic.prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
168
|
+
css += `.${atomic.className} { ${kebabProp}: ${atomic.value}; }\n`;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return css;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
generateComponentCSS(componentName, style, selectors) {
|
|
175
|
+
const atomicClasses = [];
|
|
176
|
+
const standardStyles = {};
|
|
177
|
+
|
|
178
|
+
// Separate atomic and standard styles
|
|
179
|
+
Object.entries(style).forEach(([prop, value]) => {
|
|
180
|
+
if (prop === 'selectors') return;
|
|
181
|
+
|
|
182
|
+
if (this.shouldBeAtomic(prop, value)) {
|
|
183
|
+
const className = this.getOrCreateAtomic(prop, value);
|
|
184
|
+
atomicClasses.push(className);
|
|
185
|
+
} else {
|
|
186
|
+
standardStyles[prop] = value;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
let componentCSS = '';
|
|
191
|
+
|
|
192
|
+
if (atomicClasses.length > 0 || Object.keys(standardStyles).length > 0) {
|
|
193
|
+
componentCSS += `${selectors.join(', ')} {\n`;
|
|
194
|
+
|
|
195
|
+
// EXPAND atomic classes into actual properties
|
|
196
|
+
atomicClasses.forEach(className => {
|
|
197
|
+
const key = this.findKeyByClassName(className);
|
|
198
|
+
if (key) {
|
|
199
|
+
const atomic = this.atomicClasses.get(key);
|
|
200
|
+
const kebabProp = atomic.prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
201
|
+
componentCSS += ` ${kebabProp}: ${atomic.value};\n`;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Add standard styles (these will override atomic if needed)
|
|
206
|
+
Object.entries(standardStyles).forEach(([prop, value]) => {
|
|
207
|
+
const kebabProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
208
|
+
componentCSS += ` ${kebabProp}: ${value};\n`;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
componentCSS += `}\n`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return componentCSS;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
validateStyleOrder(originalStyles, atomicStyles) {
|
|
218
|
+
// Compare original vs atomic to ensure no missing styles
|
|
219
|
+
const originalProps = new Set();
|
|
220
|
+
const atomicProps = new Set();
|
|
221
|
+
|
|
222
|
+
Object.values(originalStyles).forEach(style => {
|
|
223
|
+
Object.keys(style).forEach(prop => {
|
|
224
|
+
if (prop !== 'selectors') originalProps.add(prop);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Check atomic classes for missing props
|
|
229
|
+
this.atomicClasses.forEach(atomic => {
|
|
230
|
+
atomicProps.add(atomic.prop);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const missingProps = [...originalProps].filter(p => !atomicProps.has(p));
|
|
234
|
+
if (missingProps.length > 0) {
|
|
235
|
+
console.warn('Missing atomic classes for:', missingProps);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
optimize(styles) {
|
|
240
|
+
console.log('ChainCSS Atomic Optimizer running...');
|
|
241
|
+
|
|
242
|
+
// Track usage first
|
|
243
|
+
this.trackStyles(styles);
|
|
244
|
+
|
|
245
|
+
// Generate atomic CSS
|
|
246
|
+
let atomicCSS = this.generateAtomicCSS();
|
|
247
|
+
|
|
248
|
+
// Generate component CSS with expanded styles
|
|
249
|
+
let componentCSS = '';
|
|
250
|
+
|
|
251
|
+
// Process components in original order to maintain specificity
|
|
252
|
+
Object.entries(styles).forEach(([name, style]) => {
|
|
253
|
+
const selectors = style.selectors || [`.${name}`];
|
|
254
|
+
componentCSS += this.generateComponentCSS(name, style, selectors);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Calculate savings
|
|
258
|
+
const savings = ((this.stats.totalStyles - this.atomicClasses.size) / this.stats.totalStyles * 100).toFixed(1);
|
|
259
|
+
|
|
260
|
+
//console.log(`Optimization complete:`);
|
|
261
|
+
//console.log(`Atomic classes created: ${this.atomicClasses.size}`);
|
|
262
|
+
//console.log(`CSS size reduction: ~${savings}%`);
|
|
263
|
+
|
|
264
|
+
// Save cache if enabled
|
|
265
|
+
if (this.options.cache) {
|
|
266
|
+
this.saveCache();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Return atomic CSS first, then component CSS
|
|
270
|
+
// This ensures atomic classes are defined before they're used
|
|
271
|
+
return atomicCSS + componentCSS;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
module.exports = { AtomicOptimizer };
|
package/cache-manager.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class CacheManager {
|
|
5
|
+
constructor(cachePath = './.chaincss-cache') {
|
|
6
|
+
this.cachePath = path.resolve(process.cwd(), cachePath);
|
|
7
|
+
this.cacheDir = path.dirname(this.cachePath);
|
|
8
|
+
this.cache = {};
|
|
9
|
+
this.load();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Load cache from disk
|
|
13
|
+
load() {
|
|
14
|
+
try {
|
|
15
|
+
if (fs.existsSync(this.cachePath)) {
|
|
16
|
+
const data = fs.readFileSync(this.cachePath, 'utf8');
|
|
17
|
+
this.cache = JSON.parse(data);
|
|
18
|
+
//console.log(`Loaded cache from ${this.cachePath}`);
|
|
19
|
+
} else {
|
|
20
|
+
// Ensure cache directory exists
|
|
21
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
22
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
this.cache = {
|
|
25
|
+
version: '1.0',
|
|
26
|
+
created: new Date().toISOString(),
|
|
27
|
+
atomic: {},
|
|
28
|
+
usage: {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.warn('Could not load cache, starting fresh:', error.message);
|
|
33
|
+
this.cache = {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get value from cache
|
|
38
|
+
get(key) {
|
|
39
|
+
return this.cache[key];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Set value in cache
|
|
43
|
+
set(key, value) {
|
|
44
|
+
this.cache[key] = value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Save cache to disk
|
|
48
|
+
save() {
|
|
49
|
+
try {
|
|
50
|
+
const data = JSON.stringify(this.cache, null, 2);
|
|
51
|
+
fs.writeFileSync(this.cachePath, data, 'utf8');
|
|
52
|
+
//console.log(`Saved cache to ${this.cachePath}`);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.warn('Could not save cache:', error.message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Clear cache
|
|
59
|
+
clear() {
|
|
60
|
+
this.cache = {};
|
|
61
|
+
if (fs.existsSync(this.cachePath)) {
|
|
62
|
+
fs.unlinkSync(this.cachePath);
|
|
63
|
+
}
|
|
64
|
+
console.log('Cache cleared');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { CacheManager };
|
package/chaincss.js
CHANGED
|
@@ -1,30 +1,190 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {NodeVM} = require('vm2');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const chokidar = require('chokidar');
|
|
7
7
|
const CleanCSS = require('clean-css');
|
|
8
|
-
const transpilerModule = require('./transpiler.js');
|
|
9
8
|
const ChainCSSPrefixer = require('./prefixer.js');
|
|
9
|
+
const fileCache = new Map();
|
|
10
10
|
|
|
11
11
|
let prefixerConfig = {
|
|
12
12
|
enabled: true,
|
|
13
13
|
browsers: ['> 0.5%', 'last 2 versions', 'not dead'],
|
|
14
14
|
mode: 'auto' // 'auto', 'lightweight', or 'full'
|
|
15
15
|
};
|
|
16
|
-
|
|
17
16
|
const prefixer = new ChainCSSPrefixer(prefixerConfig);
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// IMPORT THE CORE FROM TRANSPILER - use aliasing
|
|
19
|
+
const { $, run, compile: originalCompile, chain } = require('./transpiler');
|
|
20
|
+
|
|
21
|
+
// Import atomic optimizer
|
|
22
|
+
const { AtomicOptimizer } = require('./atomic-optimizer');
|
|
23
|
+
const { CacheManager } = require('./cache-manager');
|
|
24
|
+
|
|
25
|
+
// Atomic optimizer instance
|
|
26
|
+
let atomicOptimizer = null;
|
|
27
|
+
|
|
28
|
+
// Configuration
|
|
29
|
+
let config = {
|
|
30
|
+
atomic: {
|
|
31
|
+
enabled: false, // Default off for backward compatibility
|
|
32
|
+
threshold: 3,
|
|
33
|
+
naming: 'hash',
|
|
34
|
+
cache: true,
|
|
35
|
+
minify: true
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Try .cjs first (for ES Module projects)
|
|
41
|
+
let configPath = path.join(process.cwd(), 'chaincss.config.cjs');
|
|
42
|
+
|
|
43
|
+
if (fs.existsSync(configPath)) {
|
|
44
|
+
const userConfig = require(configPath);
|
|
45
|
+
config = { ...config, ...userConfig };
|
|
46
|
+
} else {
|
|
47
|
+
// Fall back to .js
|
|
48
|
+
configPath = path.join(process.cwd(), 'chaincss.config.js');
|
|
49
|
+
|
|
50
|
+
if (fs.existsSync(configPath)) {
|
|
51
|
+
const userConfig = require(configPath);
|
|
52
|
+
config = { ...config, ...userConfig };
|
|
53
|
+
} else {
|
|
54
|
+
console.log('No config file found');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.log('Error loading config:', err.message);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Initialize atomic optimizer if enabled
|
|
63
|
+
if (config.atomic.enabled) {
|
|
64
|
+
atomicOptimizer = new AtomicOptimizer(config.atomic);
|
|
65
|
+
} else {
|
|
66
|
+
console.log('Atomic optimizer disabled (config.atomic.enabled =', config.atomic.enabled, ')');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// Create the wrapped compile function
|
|
71
|
+
const compile = (obj) => {
|
|
72
|
+
// First, do standard compilation to get styles
|
|
73
|
+
originalCompile(obj);
|
|
74
|
+
|
|
75
|
+
// If atomic is enabled, optimize
|
|
76
|
+
if (atomicOptimizer && config.atomic.enabled) {
|
|
77
|
+
const optimized = atomicOptimizer.optimize(obj);
|
|
78
|
+
chain.cssOutput = optimized;
|
|
79
|
+
return optimized;
|
|
80
|
+
}
|
|
81
|
+
return chain.cssOutput;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Create a combined module for VM sandbox
|
|
85
|
+
const transpilerModule = {
|
|
86
|
+
$,
|
|
87
|
+
run,
|
|
88
|
+
compile,
|
|
89
|
+
chain
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
// Recursive file processing function
|
|
94
|
+
const processJCSSFile = (filePath) => {
|
|
95
|
+
// Check cache first
|
|
96
|
+
if (fileCache.has(filePath)) {
|
|
97
|
+
return fileCache.get(filePath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check if file exists
|
|
101
|
+
if (!fs.existsSync(filePath)) {
|
|
102
|
+
throw new Error(`File not found: ${filePath}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
106
|
+
|
|
107
|
+
// Create a new VM instance for this file
|
|
108
|
+
const vm = new NodeVM({
|
|
109
|
+
console: 'inherit',
|
|
110
|
+
timeout: 5000,
|
|
111
|
+
sandbox: {
|
|
112
|
+
...transpilerModule,
|
|
113
|
+
get: (relativePath) => {
|
|
114
|
+
const baseDir = path.dirname(filePath);
|
|
115
|
+
const targetPath = path.resolve(baseDir, relativePath);
|
|
116
|
+
return processJCSSFile(targetPath);
|
|
117
|
+
},
|
|
118
|
+
__filename: filePath,
|
|
119
|
+
__dirname: path.dirname(filePath),
|
|
120
|
+
module: { exports: {} },
|
|
121
|
+
exports: {}
|
|
122
|
+
},
|
|
123
|
+
require: {
|
|
124
|
+
external: true,
|
|
125
|
+
builtin: ['path', 'fs'],
|
|
126
|
+
root: './'
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Wrap the content - DON'T redeclare module!
|
|
131
|
+
const wrappedContent = `
|
|
132
|
+
// Clear any existing exports
|
|
133
|
+
module.exports = {};
|
|
134
|
+
|
|
135
|
+
// Run the actual file content
|
|
136
|
+
${content}
|
|
137
|
+
|
|
138
|
+
// Return the exports
|
|
139
|
+
module.exports;
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const exports = vm.run(wrappedContent, filePath);
|
|
144
|
+
fileCache.set(filePath, exports);
|
|
145
|
+
return exports;
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error(`Error processing ${filePath}:`, err.message);
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const processScript = (scriptBlock,filename) => {
|
|
153
|
+
|
|
154
|
+
const vm = new NodeVM({
|
|
155
|
+
console: 'inherit',
|
|
156
|
+
timeout: 5000,
|
|
157
|
+
sandbox: {
|
|
158
|
+
...transpilerModule,
|
|
159
|
+
get: (relativePath) => {
|
|
160
|
+
const baseDir = path.dirname(filename);
|
|
161
|
+
const targetPath = path.resolve(baseDir, relativePath);
|
|
162
|
+
return processJCSSFile(targetPath);
|
|
163
|
+
},
|
|
164
|
+
__filename: filename,
|
|
165
|
+
__dirname: path.dirname(filename),
|
|
166
|
+
module: { exports: {} },
|
|
167
|
+
require: (path) => require(path)
|
|
168
|
+
},
|
|
169
|
+
require: {
|
|
170
|
+
external: true, // Allow some external modules
|
|
171
|
+
builtin: ['path', 'fs'], // Allow specific Node built-ins
|
|
172
|
+
root: './' // Restrict to project root
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
21
176
|
const jsCode = scriptBlock.trim();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const result = vm.run(jsCode, filename);
|
|
180
|
+
return transpilerModule.chain.cssOutput;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error(`Error processing script in ${filename}:`, err.message);
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
25
185
|
};
|
|
26
186
|
|
|
27
|
-
const processJavascriptBlocks = (content) => {
|
|
187
|
+
const processJavascriptBlocks = (content, inputpath) => {
|
|
28
188
|
const blocks = content.split(/<@([\s\S]*?)@>/gm);
|
|
29
189
|
let outputCSS = '';
|
|
30
190
|
for (let i = 0; i < blocks.length; i++) {
|
|
@@ -33,7 +193,8 @@ const processJavascriptBlocks = (content) => {
|
|
|
33
193
|
} else {
|
|
34
194
|
const scriptBlock = blocks[i];
|
|
35
195
|
try {
|
|
36
|
-
const outputProcessScript = processScript(scriptBlock);
|
|
196
|
+
const outputProcessScript = processScript(scriptBlock,inputpath);
|
|
197
|
+
|
|
37
198
|
if (typeof outputProcessScript !== 'object' && typeof outputProcessScript !== 'undefined') {
|
|
38
199
|
outputCSS += outputProcessScript;
|
|
39
200
|
}
|
|
@@ -103,8 +264,7 @@ const processor = async (inputFile, outputFile) => {
|
|
|
103
264
|
const content = fs.readFileSync(input, 'utf8');
|
|
104
265
|
|
|
105
266
|
// STEP 1: Process JavaScript blocks first
|
|
106
|
-
const processedCSS = processJavascriptBlocks(content);
|
|
107
|
-
|
|
267
|
+
const processedCSS = processJavascriptBlocks(content, input);
|
|
108
268
|
// STEP 2: Validate the CSS
|
|
109
269
|
if (!validateCSS(processedCSS)) {
|
|
110
270
|
throw new Error('Invalid CSS syntax');
|
|
@@ -155,8 +315,7 @@ function parseArgs(args) {
|
|
|
155
315
|
noPrefix: false,
|
|
156
316
|
browsers: null,
|
|
157
317
|
prefixerMode: 'auto',
|
|
158
|
-
|
|
159
|
-
sourceMap: true, // Enable source maps by default
|
|
318
|
+
sourceMap: true,
|
|
160
319
|
sourceMapInline: false
|
|
161
320
|
};
|
|
162
321
|
|
|
@@ -173,7 +332,6 @@ function parseArgs(args) {
|
|
|
173
332
|
} else if (arg === '--browsers' && args[i + 1]) {
|
|
174
333
|
result.browsers = args[i + 1].split(',');
|
|
175
334
|
i++;
|
|
176
|
-
// NEW: Add these two
|
|
177
335
|
} else if (arg === '--no-source-map') {
|
|
178
336
|
result.sourceMap = false;
|
|
179
337
|
} else if (arg === '--source-map-inline') {
|
|
@@ -253,4 +411,13 @@ Examples:
|
|
|
253
411
|
})();
|
|
254
412
|
}
|
|
255
413
|
|
|
256
|
-
module.exports = {
|
|
414
|
+
module.exports = {
|
|
415
|
+
processor,
|
|
416
|
+
watch,
|
|
417
|
+
$,
|
|
418
|
+
run,
|
|
419
|
+
compile,
|
|
420
|
+
chain,
|
|
421
|
+
atomicOptimizer,
|
|
422
|
+
config
|
|
423
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { $, run, compile, chain, tokens, createTokens } = require('./transpiler');
|
|
2
|
+
const { processor, watch } = require('./chaincss');
|
|
3
|
+
|
|
4
|
+
// Conditionally export React hooks if in React environment
|
|
5
|
+
let reactHooks = {};
|
|
6
|
+
try {
|
|
7
|
+
// Check if React is available
|
|
8
|
+
require.resolve('react');
|
|
9
|
+
reactHooks = require('./react-hooks');
|
|
10
|
+
} catch {
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
$,
|
|
16
|
+
run,
|
|
17
|
+
compile,
|
|
18
|
+
processor,
|
|
19
|
+
watch,
|
|
20
|
+
chain,
|
|
21
|
+
tokens,
|
|
22
|
+
createTokens,
|
|
23
|
+
...reactHooks
|
|
24
|
+
};
|
package/index.react.js
ADDED
package/package.json
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@melcanz85/chaincss",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "A simple package transpiler for js to css",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"module": "index.react.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./index.js",
|
|
10
|
+
"import": "./index.react.js"
|
|
11
|
+
},
|
|
12
|
+
"./react": {
|
|
13
|
+
"import": "./index.react.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"types": "types.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"index.js",
|
|
19
|
+
"index.react.js",
|
|
20
|
+
"chaincss.js",
|
|
21
|
+
"transpiler.js",
|
|
22
|
+
"tokens.js",
|
|
23
|
+
"react-hooks.js",
|
|
24
|
+
"atomic-optimizer.js",
|
|
25
|
+
"cache-manager.js",
|
|
26
|
+
"prefixer.js",
|
|
27
|
+
"types.d.ts"
|
|
28
|
+
],
|
|
6
29
|
"publishConfig": {
|
|
7
30
|
"access": "public",
|
|
8
31
|
"registry": "https://registry.npmjs.org/"
|
package/prefixer.js
CHANGED
|
@@ -89,7 +89,7 @@ class ChainCSSPrefixer {
|
|
|
89
89
|
// Set up source map options
|
|
90
90
|
const mapOptions = {
|
|
91
91
|
inline: this.config.sourceMapInline,
|
|
92
|
-
annotation: false,
|
|
92
|
+
annotation: false,
|
|
93
93
|
sourcesContent: true
|
|
94
94
|
};
|
|
95
95
|
|
|
@@ -100,7 +100,7 @@ class ChainCSSPrefixer {
|
|
|
100
100
|
return await this.processWithBuiltIn(cssString, options, mapOptions);
|
|
101
101
|
|
|
102
102
|
} catch (err) {
|
|
103
|
-
console.error('
|
|
103
|
+
console.error('Prefixer error:', err.message);
|
|
104
104
|
return { css: cssString, map: null };
|
|
105
105
|
}
|
|
106
106
|
}
|