@melcanz85/chaincss 1.6.2 β 1.7.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 +218 -14
- package/chaincss.js +9 -31
- package/package.json +1 -1
- package/prefixer.js +2 -2
- package/publish.sh +7 -0
- package/transpiler.js +1 -4
package/README.md
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# @melcanz85/chaincss
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/js/@melcanz85%2Fchaincss)
|
|
3
|
+
[](https://badge.fury.io/js/@melcanz85%2Fchaincss)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
β‘ **Compile-time CSS-in-JS** - Runs during build, not in the browser!
|
|
7
|
+
|
|
8
|
+
A simple JavaScript-to-CSS transpiler that converts JS objects into optimized CSS **without runtime overhead**.
|
|
7
9
|
|
|
8
10
|
## π Installation
|
|
9
11
|
|
|
10
12
|
```bash
|
|
11
13
|
npm install @melcanz85/chaincss
|
|
12
14
|
```
|
|
15
|
+
|
|
13
16
|
π¦ Usage (Node.js)
|
|
14
17
|
|
|
15
18
|
Quick Setup
|
|
@@ -22,11 +25,9 @@ Quick Setup
|
|
|
22
25
|
|
|
23
26
|
### Update your package.json scripts:
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"start": "concurrently \"nodemon server.js\" \"nodemon --watch chaincss/*.jcss --watch processor.js --exec 'node processor.js'\""
|
|
29
|
-
}
|
|
28
|
+
"scripts": {
|
|
29
|
+
"start": "concurrently \"nodemon server.js\" \"nodemon --watch chaincss/*.jcss --watch processor.js --exec 'node processor.js'\""
|
|
30
|
+
}
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
## π§ CSS Prefixing
|
|
@@ -34,6 +35,7 @@ json
|
|
|
34
35
|
ChainCSS offers two prefixing modes:
|
|
35
36
|
|
|
36
37
|
### 1. Lightweight Mode (Default, ~50KB)
|
|
38
|
+
|
|
37
39
|
Built-in prefixer that handles the most common CSS properties:
|
|
38
40
|
- Flexbox & Grid
|
|
39
41
|
- Transforms & Animations
|
|
@@ -44,10 +46,11 @@ Built-in prefixer that handles the most common CSS properties:
|
|
|
44
46
|
No additional installation needed!
|
|
45
47
|
|
|
46
48
|
### 2. Full Mode (Uses Autoprefixer)
|
|
49
|
+
|
|
47
50
|
For complete prefixing coverage of all CSS properties:
|
|
48
51
|
|
|
49
52
|
```bash
|
|
50
|
-
npm install autoprefixer postcss browserslist caniuse-db
|
|
53
|
+
npm install autoprefixer postcss browserslist caniuse-db
|
|
51
54
|
```
|
|
52
55
|
Project Structure
|
|
53
56
|
|
|
@@ -183,12 +186,188 @@ In chaincss/processor.js:
|
|
|
183
186
|
@>
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
|
|
190
|
+
## βοΈ Using ChainCSS with React
|
|
191
|
+
|
|
192
|
+
ChainCSS works great with React and Vite! Here's how to set it up:
|
|
193
|
+
|
|
194
|
+
### Quick Start with React + Vite
|
|
195
|
+
|
|
196
|
+
1. **Create a new React project**
|
|
197
|
+
```bash
|
|
198
|
+
npm create vite@latest my-app -- --template react
|
|
199
|
+
cd my-app
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Project Structure (Component-First Approach)
|
|
203
|
+
|
|
204
|
+
my-react-app/
|
|
205
|
+
βββ src/
|
|
206
|
+
β βββ components/
|
|
207
|
+
β β βββ Navbar/
|
|
208
|
+
β β β βββ Navbar.jsx
|
|
209
|
+
β β β βββ navbar.jcss # Chain definitions with fluent API
|
|
210
|
+
β β βββ Button/
|
|
211
|
+
β β β βββ Button.jsx
|
|
212
|
+
β β β βββ button.jcss # Chain definitions with fluent API
|
|
213
|
+
β β βββ ...
|
|
214
|
+
β βββ style/
|
|
215
|
+
β β βββ global.css # Generated CSS
|
|
216
|
+
β βββ App.jsx
|
|
217
|
+
β βββ main.jsx
|
|
218
|
+
βββ chaincss/
|
|
219
|
+
β βββ main.jcss # Entry point - imports and compiles
|
|
220
|
+
β βββ processor.js # Processing script
|
|
221
|
+
βββ package.json
|
|
222
|
+
|
|
223
|
+
### How It Works
|
|
224
|
+
|
|
225
|
+
1. **Each component has its own `.jcss` file** with style definitions as JavaScript objects
|
|
226
|
+
2. **`main.jcss` imports all component styles** using `get()` function
|
|
227
|
+
3. **Styles are merged and compiled** into a single `global.css` file
|
|
228
|
+
4. **React components import the generated CSS** and use the class names
|
|
229
|
+
|
|
230
|
+
### Example: Button Component
|
|
231
|
+
|
|
232
|
+
**src/components/Nav/navbar.jcss**
|
|
233
|
+
|
|
234
|
+
const navbar = chain
|
|
235
|
+
.bg('rgba(255, 255, 255, 0.95)')
|
|
236
|
+
.backdropFilter('blur(10px)')
|
|
237
|
+
.padding('1rem 5%')
|
|
238
|
+
.position('fixed')
|
|
239
|
+
.width('100%')
|
|
240
|
+
.top('0')
|
|
241
|
+
.zIndex('1000')
|
|
242
|
+
.boxShadow('0 2px 20px rgba(0,0,0,0.1)')
|
|
243
|
+
.block('.navbar');
|
|
244
|
+
|
|
245
|
+
module.exports = {navbar}
|
|
246
|
+
|
|
247
|
+
**src/components/Hero/hero.jcss**
|
|
248
|
+
|
|
249
|
+
const hero = chain
|
|
250
|
+
.padding('120px 5% 80px')
|
|
251
|
+
.bg('linear-gradient(135deg, #667eea 0%, #764ba2 100%)')
|
|
252
|
+
.color('white')
|
|
253
|
+
.textAlign('center')
|
|
254
|
+
.block('.hero');
|
|
255
|
+
|
|
256
|
+
module.exports = {hero}
|
|
257
|
+
|
|
258
|
+
**chaincss/main.jcss**
|
|
259
|
+
|
|
260
|
+
// You can mix a default style using run() method or even vanilla css (without delimeters)
|
|
261
|
+
<@
|
|
262
|
+
run(
|
|
263
|
+
chain.margin('0').padding('0').boxSizing('border-box').block('*'),
|
|
264
|
+
chain
|
|
265
|
+
.fontFamily("-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif")
|
|
266
|
+
.lineHeight('1.6')
|
|
267
|
+
.color('#1e293b')
|
|
268
|
+
.bg('linear-gradient(135deg, #667eea 0%, #764ba2 100%)')
|
|
269
|
+
.block('body')
|
|
270
|
+
);
|
|
271
|
+
@>
|
|
272
|
+
|
|
273
|
+
.button {
|
|
274
|
+
background-color: #667eea;
|
|
275
|
+
color: white;
|
|
276
|
+
padding: 0.5rem 1rem;
|
|
277
|
+
border-radius: 4px;
|
|
278
|
+
border: none;
|
|
279
|
+
cursor: pointer;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
<@
|
|
283
|
+
// Import all component styles
|
|
284
|
+
const nav = get('./src/components/Navbar/navbar.jcss');
|
|
285
|
+
const hero = get('./src/components/Hero/hero.jcss');
|
|
286
|
+
|
|
287
|
+
// Merge for one compilation
|
|
288
|
+
const allStyles = Object.assign({},nav,hero);
|
|
289
|
+
|
|
290
|
+
// Overwrite padding in navbar chain!
|
|
291
|
+
nav.navbar.padding = '1rem 5%';
|
|
292
|
+
|
|
293
|
+
compile(allStyles);
|
|
294
|
+
@>
|
|
295
|
+
|
|
296
|
+
// you can add keyframes and media queries in this setup
|
|
297
|
+
@keyframes fadeInUp {
|
|
298
|
+
<@
|
|
299
|
+
run(
|
|
300
|
+
chain.opacity('0').transform('translateY(20px)').block('from'),
|
|
301
|
+
chain.opacity('1').transform('translateY(0)').block('to'),
|
|
302
|
+
);
|
|
303
|
+
@>
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* Responsive */
|
|
307
|
+
@media (max-width: 768px) {
|
|
308
|
+
<@
|
|
309
|
+
run(
|
|
310
|
+
chain.fontSize('2.5rem').block('.hero h1'),
|
|
311
|
+
chain.flexDirection('column').gap('1rem').block('.stats'),
|
|
312
|
+
chain.flexDirection('column').alignItems('center').block('.cta-buttons'),
|
|
313
|
+
chain.gridTemplateColumns('1fr').block('.example-container'),
|
|
314
|
+
chain.display('none').block('.nav-links')
|
|
315
|
+
);
|
|
316
|
+
@>
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
Benefits of This Approach
|
|
320
|
+
|
|
321
|
+
β
Component Co-location: Styles live next to their components
|
|
322
|
+
|
|
323
|
+
β
Single Source of Truth: Each component manages its own styles
|
|
324
|
+
|
|
325
|
+
β
Easy to Maintain: Add/remove components without touching a central style file
|
|
326
|
+
|
|
327
|
+
β
Scalable: Perfect for large React applications
|
|
328
|
+
|
|
329
|
+
β
No Redundancy: Styles are automatically merged and optimized
|
|
330
|
+
|
|
331
|
+
**This structure is much cleaner and follows React best practices! Each component owns its styles, and `main.jcss` simply orchestrates the compilation.**
|
|
332
|
+
|
|
333
|
+
π― Best Practices with React
|
|
334
|
+
|
|
335
|
+
1. Component-Specific Styles
|
|
336
|
+
|
|
337
|
+
* Keep styles close to components: Button/button.jcss
|
|
338
|
+
|
|
339
|
+
* Use descriptive class names that match your components
|
|
340
|
+
|
|
341
|
+
2. Global Styles
|
|
342
|
+
|
|
343
|
+
* Use main.jcss for global resets and animations
|
|
344
|
+
|
|
345
|
+
* Import generated CSS once in your root component
|
|
346
|
+
|
|
347
|
+
3. Dynamic Styles
|
|
348
|
+
|
|
349
|
+
// In your .jcss file
|
|
350
|
+
const theme = {
|
|
351
|
+
primary: '#667eea',
|
|
352
|
+
secondary: '#764ba2'
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const button = chain
|
|
356
|
+
.backgroundColor(theme.primary)
|
|
357
|
+
.block('.btn');
|
|
358
|
+
|
|
359
|
+
const buttonHover = chain
|
|
360
|
+
.backgroundColor(theme.secondary)
|
|
361
|
+
.block('.btn:hover');
|
|
362
|
+
|
|
186
363
|
π Notes
|
|
187
364
|
|
|
188
|
-
You can directly put css syntax code on your main file.
|
|
365
|
+
You can directly put css syntax code on your main.jcss file.
|
|
189
366
|
|
|
190
367
|
But chainCSS syntax must be wrapped in <@ @> delimiters.
|
|
191
368
|
|
|
369
|
+
run() and compile() method should be separate block <@ run() @> <@ compile @>
|
|
370
|
+
|
|
192
371
|
The get() function imports chaining definitions from your chain.jcss file
|
|
193
372
|
|
|
194
373
|
You can modify your style in between get() and compile() in the
|
|
@@ -203,11 +382,11 @@ VS Code
|
|
|
203
382
|
|
|
204
383
|
Add this to your project's .vscode/settings.json:
|
|
205
384
|
|
|
206
|
-
{
|
|
207
|
-
|
|
208
|
-
|
|
385
|
+
{
|
|
386
|
+
"files.associations": {
|
|
387
|
+
"*.jcss": "javascript"
|
|
388
|
+
}
|
|
209
389
|
}
|
|
210
|
-
}
|
|
211
390
|
|
|
212
391
|
WebStorm / IntelliJ IDEA
|
|
213
392
|
|
|
@@ -266,10 +445,35 @@ Status Feature Description
|
|
|
266
445
|
|
|
267
446
|
β
Watch mode Auto-recompile on file changes
|
|
268
447
|
|
|
448
|
+
|
|
449
|
+
## π Key Differentiators
|
|
450
|
+
|
|
451
|
+
- **βοΈ Compile-time, not runtime** - chaincss processes your styles during build, generating pure CSS files. Zero JavaScript execution in the browser means faster page loads!
|
|
452
|
+
|
|
453
|
+
- **ποΈ Performance by design** - Unlike runtime CSS-in-JS libraries, chaincss adds no bundle weight and causes no layout shifts
|
|
454
|
+
|
|
455
|
+
- **π§ Build-time processing** - Your `.jcss` files are transformed before deployment, not in the user's browser
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
## π chaincss vs Other Approaches
|
|
459
|
+
|
|
460
|
+
| Feature | chaincss | Runtime CSS-in-JS | Tailwind | Vanilla CSS |
|
|
461
|
+
|----------------|-------------------|---------------------|---------------|-------------|
|
|
462
|
+
| **When CSS is | βοΈ **Build time** | π Runtime (browser)| βοΈ Build time | π Already written |
|
|
463
|
+
generated**
|
|
464
|
+
|
|
465
|
+
| **Browser work**| None - just | Executes JS to | None - just | None |
|
|
466
|
+
serves CSS generate CSS serves CSS
|
|
467
|
+
|
|
468
|
+
| **Dynamic values**| β
Via JS at | β
Via props at | β οΈ Limited | β Manual |
|
|
469
|
+
build time runtime
|
|
470
|
+
|
|
471
|
+
| **Bundle size** | Just the CSS | CSS + JS runtime | Just the CSS | Just the CSS |
|
|
472
|
+
|
|
269
473
|
π¨βπ» Contributing
|
|
270
474
|
|
|
271
475
|
Contributions are welcome! Feel free to open issues or submit pull requests.
|
|
272
476
|
|
|
273
477
|
π License
|
|
274
478
|
|
|
275
|
-
MIT Β© Rommel
|
|
479
|
+
MIT Β© Rommel Caneos
|
package/chaincss.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// INCLUDE SEVERAL NODEJS MODULES
|
|
4
3
|
const vm = require('vm');
|
|
5
4
|
const path = require('path');
|
|
6
5
|
const fs = require('fs');
|
|
@@ -18,21 +17,16 @@ let prefixerConfig = {
|
|
|
18
17
|
const prefixer = new ChainCSSPrefixer(prefixerConfig);
|
|
19
18
|
|
|
20
19
|
const processScript = (scriptBlock) => {
|
|
21
|
-
const context = vm.createContext({
|
|
22
|
-
...transpilerModule
|
|
23
|
-
});
|
|
24
|
-
|
|
20
|
+
const context = vm.createContext({ ...transpilerModule});
|
|
25
21
|
const jsCode = scriptBlock.trim();
|
|
26
22
|
const chainScript = new vm.Script(jsCode);
|
|
27
23
|
chainScript.runInContext(context);
|
|
28
24
|
return context.chain.cssOutput;
|
|
29
25
|
};
|
|
30
26
|
|
|
31
|
-
// FUNCTION TO CONVERT JS CODES TO CSS CODE (FIRST STEP)
|
|
32
27
|
const processJavascriptBlocks = (content) => {
|
|
33
28
|
const blocks = content.split(/<@([\s\S]*?)@>/gm);
|
|
34
29
|
let outputCSS = '';
|
|
35
|
-
|
|
36
30
|
for (let i = 0; i < blocks.length; i++) {
|
|
37
31
|
if (i % 2 === 0) {
|
|
38
32
|
outputCSS += blocks[i]; // Write the existing CSS as is
|
|
@@ -49,15 +43,13 @@ const processJavascriptBlocks = (content) => {
|
|
|
49
43
|
}
|
|
50
44
|
}
|
|
51
45
|
}
|
|
52
|
-
|
|
53
46
|
return outputCSS.trim();
|
|
54
47
|
};
|
|
55
48
|
|
|
56
|
-
//
|
|
49
|
+
// Validate CSS (check for unclosed blocks)
|
|
57
50
|
const validateCSS = (css) => {
|
|
58
51
|
const openBraces = (css.match(/{/g) || []).length;
|
|
59
52
|
const closeBraces = (css.match(/}/g) || []).length;
|
|
60
|
-
|
|
61
53
|
if (openBraces !== closeBraces) {
|
|
62
54
|
console.error(`CSS syntax error: Unclosed blocks (${openBraces} opening vs ${closeBraces} closing braces)`);
|
|
63
55
|
return false;
|
|
@@ -66,17 +58,14 @@ const validateCSS = (css) => {
|
|
|
66
58
|
};
|
|
67
59
|
|
|
68
60
|
// Modified minification function with source map support
|
|
69
|
-
|
|
70
61
|
const processAndMinifyCss = async (css, inputFile, outputFile) => {
|
|
71
|
-
// First validate the CSS
|
|
72
62
|
if (!validateCSS(css)) {
|
|
73
63
|
throw new Error('Invalid CSS syntax - check for missing braces');
|
|
74
64
|
}
|
|
75
|
-
|
|
65
|
+
|
|
76
66
|
// Step 1: Apply prefixer (if enabled)
|
|
77
67
|
let processedCss = css;
|
|
78
68
|
let sourceMap = null;
|
|
79
|
-
|
|
80
69
|
if (prefixerConfig.enabled) {
|
|
81
70
|
try {
|
|
82
71
|
const result = await prefixer.process(css, {
|
|
@@ -84,10 +73,8 @@ const processAndMinifyCss = async (css, inputFile, outputFile) => {
|
|
|
84
73
|
to: outputFile,
|
|
85
74
|
map: prefixerConfig.sourceMap !== false
|
|
86
75
|
});
|
|
87
|
-
|
|
88
76
|
processedCss = result.css;
|
|
89
77
|
sourceMap = result.map;
|
|
90
|
-
|
|
91
78
|
} catch (err) {
|
|
92
79
|
console.error('β Prefixer error:', err.message);
|
|
93
80
|
processedCss = css;
|
|
@@ -100,19 +87,15 @@ const processAndMinifyCss = async (css, inputFile, outputFile) => {
|
|
|
100
87
|
console.error('CSS Minification Errors:', output.errors);
|
|
101
88
|
return { css: null, map: null };
|
|
102
89
|
}
|
|
103
|
-
|
|
104
90
|
let finalCss = output.styles;
|
|
105
|
-
|
|
106
91
|
if (sourceMap && !prefixerConfig.sourceMapInline) {
|
|
107
92
|
const mapFileName = path.basename(`${outputFile}.map`);
|
|
108
93
|
finalCss += `\n/*# sourceMappingURL=${mapFileName} */`;
|
|
109
94
|
}
|
|
110
|
-
|
|
111
95
|
return { css: finalCss, map: sourceMap };
|
|
112
96
|
};
|
|
113
97
|
|
|
114
98
|
// Main processor function - FIXED ORDER
|
|
115
|
-
|
|
116
99
|
const processor = async (inputFile, outputFile) => {
|
|
117
100
|
try {
|
|
118
101
|
const input = path.resolve(inputFile);
|
|
@@ -126,19 +109,18 @@ const processor = async (inputFile, outputFile) => {
|
|
|
126
109
|
if (!validateCSS(processedCSS)) {
|
|
127
110
|
throw new Error('Invalid CSS syntax');
|
|
128
111
|
}
|
|
129
|
-
|
|
112
|
+
|
|
130
113
|
// STEP 3: Apply prefixer and minify with source maps
|
|
131
|
-
|
|
132
|
-
const result = await processAndMinifyCss(processedCSS, input,
|
|
114
|
+
const stylePath = output + '/global.css';
|
|
115
|
+
const result = await processAndMinifyCss(processedCSS, input, stylePath);
|
|
133
116
|
if (result.css) {
|
|
134
|
-
fs.writeFileSync(
|
|
135
|
-
|
|
117
|
+
fs.writeFileSync(stylePath, result.css, 'utf8');
|
|
118
|
+
|
|
136
119
|
//Write source map if generated
|
|
137
120
|
if (result.map) {
|
|
138
|
-
const mapFile = `${
|
|
121
|
+
const mapFile = `${stylePath}.map`;
|
|
139
122
|
fs.writeFileSync(mapFile, result.map, 'utf8');
|
|
140
123
|
}
|
|
141
|
-
|
|
142
124
|
if (prefixerConfig.enabled) {
|
|
143
125
|
console.log(` Prefixer: ${prefixerConfig.mode} mode (${prefixerConfig.browsers.join(', ')})`);
|
|
144
126
|
}
|
|
@@ -155,9 +137,7 @@ const processor = async (inputFile, outputFile) => {
|
|
|
155
137
|
|
|
156
138
|
// Watch function
|
|
157
139
|
function watch(inputFile, outputFile) {
|
|
158
|
-
console.log(`Watching for changes in ${inputFile}...`);
|
|
159
140
|
chokidar.watch(inputFile).on('change', async () => {
|
|
160
|
-
console.log(`File changed: ${inputFile}`);
|
|
161
141
|
try {
|
|
162
142
|
await processor(inputFile, outputFile);
|
|
163
143
|
} catch (err) {
|
|
@@ -167,7 +147,6 @@ function watch(inputFile, outputFile) {
|
|
|
167
147
|
}
|
|
168
148
|
|
|
169
149
|
// Parse CLI arguments
|
|
170
|
-
|
|
171
150
|
function parseArgs(args) {
|
|
172
151
|
const result = {
|
|
173
152
|
inputFile: null,
|
|
@@ -213,7 +192,6 @@ function parseArgs(args) {
|
|
|
213
192
|
if (require.main === module) {
|
|
214
193
|
const args = process.argv.slice(2);
|
|
215
194
|
const cliOptions = parseArgs(args);
|
|
216
|
-
|
|
217
195
|
if (!cliOptions.inputFile || !cliOptions.outputFile) {
|
|
218
196
|
console.log(`
|
|
219
197
|
ChainCSS - JavaScript-powered CSS preprocessor
|
package/package.json
CHANGED
package/prefixer.js
CHANGED
|
@@ -105,7 +105,7 @@ class ChainCSSPrefixer {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
//
|
|
108
|
+
// Full mode with Autoprefixer
|
|
109
109
|
async processWithAutoprefixer(cssString, options, mapOptions) {
|
|
110
110
|
const from = options.from || 'input.css';
|
|
111
111
|
const to = options.to || 'output.css';
|
|
@@ -124,7 +124,7 @@ class ChainCSSPrefixer {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
//
|
|
127
|
+
// Lightweight mode with built-in prefixer
|
|
128
128
|
async processWithBuiltIn(cssString, options, mapOptions) {
|
|
129
129
|
if (!this.hasBuiltInDeps) {
|
|
130
130
|
return { css: cssString, map: null };
|
package/publish.sh
ADDED
package/transpiler.js
CHANGED
|
@@ -209,7 +209,6 @@ const compile = (obj) => {
|
|
|
209
209
|
let selectors = element.selectors || []; // Provide default empty array if selectors is undefined
|
|
210
210
|
let elementCSS = '';
|
|
211
211
|
for (let prop in element) {
|
|
212
|
-
|
|
213
212
|
if (element.hasOwnProperty(prop) && prop !== 'selectors') {
|
|
214
213
|
// Convert camelCase to kebab-case
|
|
215
214
|
const kebabKey = prop.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
@@ -218,7 +217,6 @@ const compile = (obj) => {
|
|
|
218
217
|
}
|
|
219
218
|
selectors = selectors.join();
|
|
220
219
|
cssString += `${selectors} {\n${elementCSS}}\n`;
|
|
221
|
-
|
|
222
220
|
}
|
|
223
221
|
}
|
|
224
222
|
|
|
@@ -230,7 +228,6 @@ const get = (filename) => {
|
|
|
230
228
|
if (fileExt !== '.jcss') {
|
|
231
229
|
throw new Error(`Import error: ${filename} must have .jcss extension`);
|
|
232
230
|
}
|
|
233
|
-
|
|
234
231
|
// Try to resolve the path
|
|
235
232
|
const resolvedPath = path.resolve(process.cwd(), filename);
|
|
236
233
|
|
|
@@ -238,7 +235,7 @@ const get = (filename) => {
|
|
|
238
235
|
const exists = fs.existsSync(resolvedPath);
|
|
239
236
|
|
|
240
237
|
if (!exists) {
|
|
241
|
-
throw new Error(
|
|
238
|
+
throw new Error('File not found: '+filename+'(resolved to: '+resolvedPath+')');
|
|
242
239
|
}
|
|
243
240
|
|
|
244
241
|
return require(resolvedPath);
|