@melcanz85/chaincss 1.5.10 → 1.6.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/.github/workflows/publish.yml +22 -0
- package/README.md +159 -110
- package/chaincss.js +224 -41
- package/package.json +31 -2
- package/prefixer.js +297 -0
- package/transpiler.js +74 -72
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Publish Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: '24'
|
|
20
|
+
registry-url: 'https://registry.npmjs.org'
|
|
21
|
+
- run: npm ci
|
|
22
|
+
- run: npm publish
|
package/README.md
CHANGED
|
@@ -8,18 +8,19 @@ A simple JavaScript-to-CSS transpiler that converts JS objects into optimized CS
|
|
|
8
8
|
## 🚀 Installation
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npm install @melcanz85/chaincss
|
|
12
|
-
|
|
11
|
+
npm install @melcanz85/chaincss
|
|
12
|
+
```
|
|
13
13
|
📦 Usage (Node.js)
|
|
14
|
-
Quick Setup
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
Quick Setup
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
### Install development dependencies:
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
```bash
|
|
20
|
+
npm install --save-dev nodemon concurrently
|
|
21
|
+
```
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
### Update your package.json scripts:
|
|
23
24
|
|
|
24
25
|
json
|
|
25
26
|
|
|
@@ -27,6 +28,27 @@ json
|
|
|
27
28
|
"start": "concurrently \"nodemon server.js\" \"nodemon --watch chaincss/*.jcss --watch processor.js --exec 'node processor.js'\""
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
|
|
32
|
+
## 🔧 CSS Prefixing
|
|
33
|
+
|
|
34
|
+
ChainCSS offers two prefixing modes:
|
|
35
|
+
|
|
36
|
+
### 1. Lightweight Mode (Default, ~50KB)
|
|
37
|
+
Built-in prefixer that handles the most common CSS properties:
|
|
38
|
+
- Flexbox & Grid
|
|
39
|
+
- Transforms & Animations
|
|
40
|
+
- Filters & Effects
|
|
41
|
+
- Text effects
|
|
42
|
+
- Box properties
|
|
43
|
+
|
|
44
|
+
No additional installation needed!
|
|
45
|
+
|
|
46
|
+
### 2. Full Mode (Uses Autoprefixer)
|
|
47
|
+
For complete prefixing coverage of all CSS properties:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install autoprefixer postcss browserslist caniuse-db
|
|
51
|
+
```
|
|
30
52
|
Project Structure
|
|
31
53
|
|
|
32
54
|
Create this folder structure in your project:
|
|
@@ -48,93 +70,118 @@ The Initialization processor Setup
|
|
|
48
70
|
|
|
49
71
|
In chaincss/processor.js:
|
|
50
72
|
|
|
51
|
-
const chain = require("@melcanz85/chaincss");
|
|
73
|
+
const chain = require("@melcanz85/chaincss");
|
|
52
74
|
|
|
53
|
-
try {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} catch (err) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
75
|
+
try {
|
|
76
|
+
// Process main file and output CSS
|
|
77
|
+
chain.processor('./chaincss/main.jcss', './public/style.css');
|
|
78
|
+
} catch (err) {
|
|
79
|
+
console.error('Error processing chainCSS file:', err.stack);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
60
82
|
|
|
61
83
|
💻 Code Examples
|
|
62
84
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
85
|
+
//--Chaining File (chaincss/chain.jcss):
|
|
86
|
+
|
|
87
|
+
### This is where the chaining happens all codes must be in javascript syntax.
|
|
88
|
+
The chain methods are the same as the css properties but in camelCase mode
|
|
89
|
+
and the exception of the background property which is shorten to 'bg' only for
|
|
90
|
+
example background-color is bgColor() in chaincss. The value of the block()
|
|
91
|
+
method is the css selector which is always at the end of the chain or block.
|
|
92
|
+
|
|
93
|
+
// Variables for consistent styling
|
|
94
|
+
const bodyBg = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
|
95
|
+
const headerBg = 'rgba(255, 255, 255, 0.95)';
|
|
96
|
+
const bodyFontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
|
97
|
+
Ubuntu, sans-serif";
|
|
98
|
+
const headerBoxShadow = '0 2px 20px rgba(0,0,0,0.1)';
|
|
99
|
+
const logoFontSize = '1.8rem';
|
|
100
|
+
|
|
101
|
+
const reset = chain
|
|
102
|
+
.margin('0')
|
|
103
|
+
.padding('0')
|
|
104
|
+
.boxSizing('border-box')
|
|
105
|
+
.block('*');
|
|
106
|
+
|
|
107
|
+
const body = chain
|
|
108
|
+
.fontFamily(bodyFontFamily)
|
|
109
|
+
.lineHeight('1.6')
|
|
110
|
+
.color('#1e293b')
|
|
111
|
+
.bg(bodyBg)
|
|
112
|
+
.block('body');
|
|
113
|
+
|
|
114
|
+
/* Header/Navigation */
|
|
115
|
+
const navbar = chain
|
|
116
|
+
.bg(headerBg)
|
|
117
|
+
.backdropFilter('blur(10px)')
|
|
118
|
+
.padding('1rem 5%')
|
|
119
|
+
.position('fixed')
|
|
120
|
+
.width('100%')
|
|
121
|
+
.top('0')
|
|
122
|
+
.zIndex('1000')
|
|
123
|
+
.boxShadow(headerBoxShadow)
|
|
124
|
+
.block('.navbar');
|
|
125
|
+
|
|
126
|
+
const nav_container = chain
|
|
127
|
+
.maxWidth('1200px')
|
|
128
|
+
.margin('0 auto')
|
|
129
|
+
.display('flex')
|
|
130
|
+
.justifyContent('space-between')
|
|
131
|
+
.alignItems('center')
|
|
132
|
+
.block('.nav-container');
|
|
133
|
+
|
|
134
|
+
const logo = chain
|
|
135
|
+
.fontSize(logoFontSize)
|
|
136
|
+
.fontWeight('700')
|
|
137
|
+
.bg('linear-gradient(135deg, #667eea, #764ba2)')
|
|
138
|
+
.backgroundClip('text')
|
|
139
|
+
.textFillColor('transparent')
|
|
140
|
+
.letterSpacing('-0.5px')
|
|
141
|
+
.block('.logo');
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
reset,
|
|
145
|
+
navbar,
|
|
146
|
+
nav_container,
|
|
147
|
+
logo
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
//--Main File (chaincss/main.jcss):
|
|
152
|
+
|
|
153
|
+
<@
|
|
154
|
+
// Import chaining definitions
|
|
155
|
+
const style = get('./chain.jcss');
|
|
156
|
+
|
|
157
|
+
// Override specific styles
|
|
158
|
+
style.logo.fontSize = '2rem';
|
|
159
|
+
|
|
160
|
+
// Compile to CSS
|
|
161
|
+
compile(style);
|
|
162
|
+
@>
|
|
163
|
+
|
|
164
|
+
@keyframes fadeInUp {
|
|
165
|
+
<@
|
|
166
|
+
run(
|
|
167
|
+
chain.opacity('0').transform('translateY(20px)').block('from'),
|
|
168
|
+
chain.opacity('1').transform('translateY(0)').block('to'),
|
|
169
|
+
);
|
|
170
|
+
@>
|
|
171
|
+
}
|
|
137
172
|
|
|
173
|
+
/* Responsive */
|
|
174
|
+
@media (max-width: 768px) {
|
|
175
|
+
<@
|
|
176
|
+
run(
|
|
177
|
+
chain.fontSize('2.5rem').block('.hero h1'),
|
|
178
|
+
chain.flexDirection('column').gap('1rem').block('.stats'),
|
|
179
|
+
chain.flexDirection('column').alignItems('center').block('.cta-buttons'),
|
|
180
|
+
chain.gridTemplateColumns('1fr').block('.example-container'),
|
|
181
|
+
chain.display('none').block('.nav-links')
|
|
182
|
+
);
|
|
183
|
+
@>
|
|
184
|
+
}
|
|
138
185
|
|
|
139
186
|
📝 Notes
|
|
140
187
|
|
|
@@ -142,13 +189,16 @@ module.exports = {
|
|
|
142
189
|
|
|
143
190
|
But chainCSS syntax must be wrapped in <@ @> delimiters.
|
|
144
191
|
|
|
145
|
-
The get() function imports chaining definitions from
|
|
192
|
+
The get() function imports chaining definitions from your chain.jcss file
|
|
146
193
|
|
|
147
|
-
|
|
194
|
+
You can modify your style in between get() and compile() in the
|
|
195
|
+
main file it will overwrite the styles in the chain file.
|
|
148
196
|
|
|
149
197
|
🎨 Editor Support
|
|
150
198
|
|
|
151
|
-
Since .jcss files are just JavaScript files with ChainCSS syntax, you can
|
|
199
|
+
Since .jcss files are just JavaScript files with ChainCSS syntax, you can
|
|
200
|
+
easily enable proper syntax highlighting in your editor:
|
|
201
|
+
|
|
152
202
|
VS Code
|
|
153
203
|
|
|
154
204
|
Add this to your project's .vscode/settings.json:
|
|
@@ -170,13 +220,13 @@ WebStorm / IntelliJ IDEA
|
|
|
170
220
|
Vim / Neovim
|
|
171
221
|
|
|
172
222
|
Add to your .vimrc or init.vim:
|
|
173
|
-
vim
|
|
174
223
|
|
|
175
|
-
au BufRead,BufNewFile *.jcss setfiletype javascript
|
|
224
|
+
au BufRead,BufNewFile *.jcss setfiletype javascript
|
|
176
225
|
|
|
177
226
|
Sublime Text
|
|
178
227
|
|
|
179
|
-
Create or edit ~/Library/Application Support/Sublime Text/Packages/User/JCSS.sublime-settings:
|
|
228
|
+
Create or edit ~/Library/Application Support/Sublime Text/Packages/User/JCSS.sublime-settings:
|
|
229
|
+
|
|
180
230
|
json
|
|
181
231
|
|
|
182
232
|
{
|
|
@@ -189,12 +239,13 @@ Atom
|
|
|
189
239
|
Add to your config.cson:
|
|
190
240
|
coffeescript
|
|
191
241
|
|
|
192
|
-
"*":
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
242
|
+
"*":
|
|
243
|
+
core:
|
|
244
|
+
customFileTypes:
|
|
245
|
+
"source.js": [
|
|
246
|
+
"jcss"
|
|
247
|
+
]
|
|
248
|
+
|
|
198
249
|
|
|
199
250
|
Other Editors
|
|
200
251
|
|
|
@@ -207,19 +258,17 @@ Status Feature Description
|
|
|
207
258
|
|
|
208
259
|
✅ Basic JS → CSS Convert plain JS objects to CSS
|
|
209
260
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
🚧 Vendor prefixing Auto-add -webkit-, -moz-, etc.
|
|
213
|
-
|
|
214
|
-
🚧 Source maps Debug generated CSS
|
|
261
|
+
✅ Vendor prefixing Auto-add -webkit-, -moz-, etc.
|
|
215
262
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
✅
|
|
263
|
+
✅ Keyframe animations @keyframes support
|
|
264
|
+
|
|
265
|
+
✅ Source maps Debug generated CSS
|
|
219
266
|
|
|
267
|
+
✅ Watch mode Auto-recompile on file changes
|
|
220
268
|
|
|
221
269
|
👨💻 Contributing
|
|
222
|
-
|
|
270
|
+
|
|
271
|
+
Contributions are welcome! Feel free to open issues or submit pull requests.
|
|
223
272
|
|
|
224
273
|
📄 License
|
|
225
274
|
|
package/chaincss.js
CHANGED
|
@@ -7,38 +7,29 @@ const fs = require('fs');
|
|
|
7
7
|
const chokidar = require('chokidar');
|
|
8
8
|
const CleanCSS = require('clean-css');
|
|
9
9
|
const transpilerModule = require('./transpiler.js');
|
|
10
|
+
const ChainCSSPrefixer = require('./prefixer.js');
|
|
11
|
+
|
|
12
|
+
let prefixerConfig = {
|
|
13
|
+
enabled: true,
|
|
14
|
+
browsers: ['> 0.5%', 'last 2 versions', 'not dead'],
|
|
15
|
+
mode: 'auto' // 'auto', 'lightweight', or 'full'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const prefixer = new ChainCSSPrefixer(prefixerConfig);
|
|
10
19
|
|
|
11
|
-
// FUNCTION TO PROCESS CHUNKS OF SCRIPTS FROM THE INPUT FILE USING THE VM MODULE
|
|
12
20
|
const processScript = (scriptBlock) => {
|
|
13
21
|
const context = vm.createContext({
|
|
14
|
-
...transpilerModule
|
|
22
|
+
...transpilerModule
|
|
15
23
|
});
|
|
16
|
-
|
|
24
|
+
|
|
25
|
+
const jsCode = scriptBlock.trim();
|
|
17
26
|
const chainScript = new vm.Script(jsCode);
|
|
18
27
|
chainScript.runInContext(context);
|
|
19
|
-
return context.chain.cssOutput;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// CSS Minification Function
|
|
23
|
-
const minifyCss = (css) => {
|
|
24
|
-
const output = new CleanCSS().minify(css);
|
|
25
|
-
if (output.errors.length > 0) {
|
|
26
|
-
console.error('CSS Minification Errors:', output.errors);
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
return output.styles;
|
|
28
|
+
return context.chain.cssOutput;
|
|
30
29
|
};
|
|
31
30
|
|
|
32
|
-
// FUNCTION TO CONVERT JS CODES TO CSS CODE
|
|
33
|
-
const
|
|
34
|
-
const allowedExtensions = ['.jcss'];
|
|
35
|
-
const fileExt = path.extname(inputFile).toLowerCase();
|
|
36
|
-
|
|
37
|
-
if (!allowedExtensions.includes(fileExt)) {
|
|
38
|
-
throw new Error(`Invalid file extension: ${fileExt}. Only .jcss files are allowed.`);
|
|
39
|
-
}
|
|
40
|
-
const input = path.resolve(inputFile);
|
|
41
|
-
const content = fs.readFileSync(input, 'utf8');
|
|
31
|
+
// FUNCTION TO CONVERT JS CODES TO CSS CODE (FIRST STEP)
|
|
32
|
+
const processJavascriptBlocks = (content) => {
|
|
42
33
|
const blocks = content.split(/<@([\s\S]*?)@>/gm);
|
|
43
34
|
let outputCSS = '';
|
|
44
35
|
|
|
@@ -53,43 +44,235 @@ const processor = (inputFile, outputFile) => {
|
|
|
53
44
|
outputCSS += outputProcessScript;
|
|
54
45
|
}
|
|
55
46
|
} catch (err) {
|
|
56
|
-
console.error(`Error processing script block
|
|
57
|
-
throw err;
|
|
47
|
+
console.error(`Error processing script block:`, err.stack);
|
|
48
|
+
throw err;
|
|
58
49
|
}
|
|
59
50
|
}
|
|
60
51
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
52
|
+
|
|
53
|
+
return outputCSS.trim();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// NEW: Validate CSS (check for unclosed blocks)
|
|
57
|
+
const validateCSS = (css) => {
|
|
58
|
+
const openBraces = (css.match(/{/g) || []).length;
|
|
59
|
+
const closeBraces = (css.match(/}/g) || []).length;
|
|
60
|
+
|
|
61
|
+
if (openBraces !== closeBraces) {
|
|
62
|
+
console.error(`CSS syntax error: Unclosed blocks (${openBraces} opening vs ${closeBraces} closing braces)`);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Modified minification function with source map support
|
|
69
|
+
|
|
70
|
+
const processAndMinifyCss = async (css, inputFile, outputFile) => {
|
|
71
|
+
// First validate the CSS
|
|
72
|
+
if (!validateCSS(css)) {
|
|
73
|
+
throw new Error('Invalid CSS syntax - check for missing braces');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Step 1: Apply prefixer (if enabled)
|
|
77
|
+
let processedCss = css;
|
|
78
|
+
let sourceMap = null;
|
|
79
|
+
|
|
80
|
+
if (prefixerConfig.enabled) {
|
|
81
|
+
try {
|
|
82
|
+
const result = await prefixer.process(css, {
|
|
83
|
+
from: inputFile,
|
|
84
|
+
to: outputFile,
|
|
85
|
+
map: prefixerConfig.sourceMap !== false
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
processedCss = result.css;
|
|
89
|
+
sourceMap = result.map;
|
|
90
|
+
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error('⚠Prefixer error:', err.message);
|
|
93
|
+
processedCss = css;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Step 2: Minify
|
|
98
|
+
const output = new CleanCSS().minify(processedCss);
|
|
99
|
+
if (output.errors.length > 0) {
|
|
100
|
+
console.error('CSS Minification Errors:', output.errors);
|
|
101
|
+
return { css: null, map: null };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let finalCss = output.styles;
|
|
105
|
+
|
|
106
|
+
if (sourceMap && !prefixerConfig.sourceMapInline) {
|
|
107
|
+
const mapFileName = path.basename(`${outputFile}.map`);
|
|
108
|
+
finalCss += `\n/*# sourceMappingURL=${mapFileName} */`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { css: finalCss, map: sourceMap };
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Main processor function - FIXED ORDER
|
|
115
|
+
|
|
116
|
+
const processor = async (inputFile, outputFile) => {
|
|
117
|
+
try {
|
|
118
|
+
const input = path.resolve(inputFile);
|
|
119
|
+
const output = path.resolve(outputFile);
|
|
120
|
+
const content = fs.readFileSync(input, 'utf8');
|
|
121
|
+
|
|
122
|
+
// STEP 1: Process JavaScript blocks first
|
|
123
|
+
const processedCSS = processJavascriptBlocks(content);
|
|
124
|
+
|
|
125
|
+
// STEP 2: Validate the CSS
|
|
126
|
+
if (!validateCSS(processedCSS)) {
|
|
127
|
+
throw new Error('Invalid CSS syntax');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// STEP 3: Apply prefixer and minify with source maps
|
|
131
|
+
|
|
132
|
+
const result = await processAndMinifyCss(processedCSS, input, output);
|
|
133
|
+
if (result.css) {
|
|
134
|
+
fs.writeFileSync(output, result.css, 'utf8');
|
|
135
|
+
|
|
136
|
+
//Write source map if generated
|
|
137
|
+
if (result.map) {
|
|
138
|
+
const mapFile = `${output}.map`;
|
|
139
|
+
fs.writeFileSync(mapFile, result.map, 'utf8');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (prefixerConfig.enabled) {
|
|
143
|
+
console.log(` Prefixer: ${prefixerConfig.mode} mode (${prefixerConfig.browsers.join(', ')})`);
|
|
144
|
+
}
|
|
145
|
+
//Show source map status
|
|
146
|
+
if (result.map) {
|
|
147
|
+
console.log(` Source maps: enabled`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.error(`Failed to process ${inputFile}:`, err.message);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
65
154
|
};
|
|
66
155
|
|
|
67
156
|
// Watch function
|
|
68
157
|
function watch(inputFile, outputFile) {
|
|
69
158
|
console.log(`Watching for changes in ${inputFile}...`);
|
|
70
|
-
chokidar.watch(inputFile).on('change', () => {
|
|
159
|
+
chokidar.watch(inputFile).on('change', async () => {
|
|
71
160
|
console.log(`File changed: ${inputFile}`);
|
|
72
|
-
|
|
161
|
+
try {
|
|
162
|
+
await processor(inputFile, outputFile);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error('Error during watch processing:', err);
|
|
165
|
+
}
|
|
73
166
|
});
|
|
74
167
|
}
|
|
75
168
|
|
|
169
|
+
// Parse CLI arguments
|
|
170
|
+
|
|
171
|
+
function parseArgs(args) {
|
|
172
|
+
const result = {
|
|
173
|
+
inputFile: null,
|
|
174
|
+
outputFile: null,
|
|
175
|
+
watchMode: false,
|
|
176
|
+
noPrefix: false,
|
|
177
|
+
browsers: null,
|
|
178
|
+
prefixerMode: 'auto',
|
|
179
|
+
// NEW: Add these
|
|
180
|
+
sourceMap: true, // Enable source maps by default
|
|
181
|
+
sourceMapInline: false
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
for (let i = 0; i < args.length; i++) {
|
|
185
|
+
const arg = args[i];
|
|
186
|
+
|
|
187
|
+
if (arg === '--watch') {
|
|
188
|
+
result.watchMode = true;
|
|
189
|
+
} else if (arg === '--no-prefix') {
|
|
190
|
+
result.noPrefix = true;
|
|
191
|
+
} else if (arg === '--prefixer-mode' && args[i + 1]) {
|
|
192
|
+
result.prefixerMode = args[i + 1];
|
|
193
|
+
i++;
|
|
194
|
+
} else if (arg === '--browsers' && args[i + 1]) {
|
|
195
|
+
result.browsers = args[i + 1].split(',');
|
|
196
|
+
i++;
|
|
197
|
+
// NEW: Add these two
|
|
198
|
+
} else if (arg === '--no-source-map') {
|
|
199
|
+
result.sourceMap = false;
|
|
200
|
+
} else if (arg === '--source-map-inline') {
|
|
201
|
+
result.sourceMapInline = true;
|
|
202
|
+
} else if (!result.inputFile) {
|
|
203
|
+
result.inputFile = arg;
|
|
204
|
+
} else if (!result.outputFile) {
|
|
205
|
+
result.outputFile = arg;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
|
|
76
212
|
// Main CLI logic
|
|
77
213
|
if (require.main === module) {
|
|
78
214
|
const args = process.argv.slice(2);
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
215
|
+
const cliOptions = parseArgs(args);
|
|
216
|
+
|
|
217
|
+
if (!cliOptions.inputFile || !cliOptions.outputFile) {
|
|
218
|
+
console.log(`
|
|
219
|
+
ChainCSS - JavaScript-powered CSS preprocessor
|
|
82
220
|
|
|
83
|
-
|
|
84
|
-
|
|
221
|
+
Usage:
|
|
222
|
+
chaincss <inputFile> <outputFile> [options]
|
|
223
|
+
|
|
224
|
+
Options:
|
|
225
|
+
--watch Watch for changes
|
|
226
|
+
--no-prefix Disable automatic prefixing
|
|
227
|
+
--browsers <list> Browser support list (comma-separated)
|
|
228
|
+
Example: --browsers ">1%,last 2 versions,not IE 11"
|
|
229
|
+
|
|
230
|
+
Examples:
|
|
231
|
+
chaincss style.jcss style.css
|
|
232
|
+
chaincss style.jcss style.css --watch
|
|
233
|
+
chaincss style.jcss style.css --browsers ">5%,last 2 safari versions"
|
|
234
|
+
chaincss style.jcss style.css --no-prefix
|
|
235
|
+
`);
|
|
85
236
|
process.exit(1);
|
|
86
237
|
}
|
|
87
238
|
|
|
88
|
-
|
|
239
|
+
// sourceMap
|
|
240
|
+
if (cliOptions.sourceMap !== undefined) {
|
|
241
|
+
prefixerConfig.sourceMap = cliOptions.sourceMap;
|
|
242
|
+
}
|
|
243
|
+
if (cliOptions.sourceMapInline) {
|
|
244
|
+
prefixerConfig.sourceMapInline = true;
|
|
245
|
+
}
|
|
89
246
|
|
|
90
|
-
|
|
91
|
-
|
|
247
|
+
// Then apply to prefixer:
|
|
248
|
+
if (cliOptions.prefixerMode) {
|
|
249
|
+
prefixerConfig.mode = cliOptions.prefixerMode;
|
|
92
250
|
}
|
|
251
|
+
|
|
252
|
+
// Apply CLI options
|
|
253
|
+
if (cliOptions.noPrefix) {
|
|
254
|
+
prefixerConfig.enabled = false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (cliOptions.browsers) {
|
|
258
|
+
prefixerConfig.browsers = cliOptions.browsers;
|
|
259
|
+
// Re-initialize prefixer with new config
|
|
260
|
+
Object.assign(prefixer, new ChainCSSPrefixer(prefixerConfig));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Run processor
|
|
264
|
+
(async () => {
|
|
265
|
+
try {
|
|
266
|
+
await processor(cliOptions.inputFile, cliOptions.outputFile);
|
|
267
|
+
|
|
268
|
+
if (cliOptions.watchMode) {
|
|
269
|
+
watch(cliOptions.inputFile, cliOptions.outputFile);
|
|
270
|
+
}
|
|
271
|
+
} catch (err) {
|
|
272
|
+
console.error('Fatal error:', err);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
})();
|
|
93
276
|
}
|
|
94
277
|
|
|
95
|
-
module.exports = { processor, watch };
|
|
278
|
+
module.exports = { processor, watch };
|
package/package.json
CHANGED
|
@@ -1,15 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@melcanz85/chaincss",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A simple package transpiler for js to css",
|
|
5
5
|
"main": "chaincss.js",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public",
|
|
8
|
+
"registry": "https://registry.npmjs.org/"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/melcanz08/chaincss.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/melcanz08/chaincss",
|
|
6
15
|
"bin": {
|
|
7
|
-
"chaincss": "
|
|
16
|
+
"chaincss": "chaincss.js"
|
|
8
17
|
},
|
|
9
18
|
"dependencies": {
|
|
10
19
|
"chokidar": "^3.5.3",
|
|
11
20
|
"clean-css": "^5.3.3"
|
|
12
21
|
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"autoprefixer": "^10.0.0",
|
|
24
|
+
"browserslist": "^4.28.1",
|
|
25
|
+
"caniuse-db": "^1.0.30001770",
|
|
26
|
+
"postcss": "^8.5.6"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"autoprefixer": {
|
|
30
|
+
"optional": true
|
|
31
|
+
},
|
|
32
|
+
"postcss": {
|
|
33
|
+
"optional": true
|
|
34
|
+
},
|
|
35
|
+
"browserslist": {
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
"caniuse-db": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
13
42
|
"keywords": [
|
|
14
43
|
"css",
|
|
15
44
|
"js",
|
package/prefixer.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
let postcss, browserslist, caniuse, autoprefixer;
|
|
2
|
+
|
|
3
|
+
// Try to load optional dependencies
|
|
4
|
+
try {
|
|
5
|
+
postcss = require('postcss');
|
|
6
|
+
browserslist = require('browserslist');
|
|
7
|
+
caniuse = require('caniuse-db/fulldata-json/data-2.0.json');
|
|
8
|
+
} catch (err) {
|
|
9
|
+
// Optional deps not installed - will use lightweight mode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Try to load Autoprefixer (optional)
|
|
13
|
+
try {
|
|
14
|
+
autoprefixer = require('autoprefixer');
|
|
15
|
+
} catch (err) {
|
|
16
|
+
// Autoprefixer not installed - will use built-in
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class ChainCSSPrefixer {
|
|
20
|
+
constructor(config = {}) {
|
|
21
|
+
this.config = {
|
|
22
|
+
browsers: config.browsers || ['> 0.5%', 'last 2 versions', 'not dead'],
|
|
23
|
+
enabled: config.enabled !== false,
|
|
24
|
+
mode: config.mode || 'auto',
|
|
25
|
+
sourceMap: config.sourceMap !== false, // Enable source maps by default
|
|
26
|
+
sourceMapInline: config.sourceMapInline || false,
|
|
27
|
+
...config
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Check what's available
|
|
31
|
+
this.hasBuiltInDeps = !!(postcss && browserslist && caniuse);
|
|
32
|
+
this.hasAutoprefixer = !!autoprefixer;
|
|
33
|
+
|
|
34
|
+
// Determine which mode to use
|
|
35
|
+
this.prefixerMode = this.determineMode();
|
|
36
|
+
|
|
37
|
+
// Built-in prefixer data
|
|
38
|
+
this.caniuseData = caniuse ? caniuse.data : null;
|
|
39
|
+
this.commonProperties = this.getCommonProperties();
|
|
40
|
+
this.specialValues = {
|
|
41
|
+
'display': ['flex', 'inline-flex', 'grid', 'inline-grid'],
|
|
42
|
+
'background-clip': ['text'],
|
|
43
|
+
'position': ['sticky']
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
this.browserPrefixMap = {
|
|
47
|
+
'chrome': 'webkit', 'safari': 'webkit', 'firefox': 'moz',
|
|
48
|
+
'ie': 'ms', 'edge': 'webkit', 'ios_saf': 'webkit',
|
|
49
|
+
'and_chr': 'webkit', 'android': 'webkit', 'opera': 'webkit',
|
|
50
|
+
'op_mob': 'webkit', 'samsung': 'webkit', 'and_ff': 'moz'
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
this.targetBrowsers = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
determineMode() {
|
|
58
|
+
// User explicitly wants full mode but Autoprefixer not installed
|
|
59
|
+
if (this.config.mode === 'full' && !this.hasAutoprefixer) {
|
|
60
|
+
console.warn('⚠️ Full mode requested but autoprefixer not installed. Falling back to lightweight mode.');
|
|
61
|
+
console.warn(' To use full mode: npm install autoprefixer postcss');
|
|
62
|
+
return 'lightweight';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// User explicitly wants lightweight mode
|
|
66
|
+
if (this.config.mode === 'lightweight') {
|
|
67
|
+
return 'lightweight';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// User wants full mode and it's available
|
|
71
|
+
if (this.config.mode === 'full' && this.hasAutoprefixer) {
|
|
72
|
+
return 'full';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Auto mode: use full if available, otherwise lightweight
|
|
76
|
+
if (this.config.mode === 'auto') {
|
|
77
|
+
return this.hasAutoprefixer ? 'full' : 'lightweight';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return 'lightweight';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async process(cssString, options = {}) {
|
|
84
|
+
if (!this.config.enabled) {
|
|
85
|
+
return { css: cssString, map: null };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
// Set up source map options
|
|
90
|
+
const mapOptions = {
|
|
91
|
+
inline: this.config.sourceMapInline,
|
|
92
|
+
annotation: false, // We'll add the comment ourselves
|
|
93
|
+
sourcesContent: true
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (this.prefixerMode === 'full') {
|
|
97
|
+
return await this.processWithAutoprefixer(cssString, options, mapOptions);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return await this.processWithBuiltIn(cssString, options, mapOptions);
|
|
101
|
+
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error('⚠️ Prefixer error:', err.message);
|
|
104
|
+
return { css: cssString, map: null };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 🚀 Full mode with Autoprefixer
|
|
109
|
+
async processWithAutoprefixer(cssString, options, mapOptions) {
|
|
110
|
+
const from = options.from || 'input.css';
|
|
111
|
+
const to = options.to || 'output.css';
|
|
112
|
+
|
|
113
|
+
const result = await postcss([
|
|
114
|
+
autoprefixer({ overrideBrowserslist: this.config.browsers })
|
|
115
|
+
]).process(cssString, {
|
|
116
|
+
from,
|
|
117
|
+
to,
|
|
118
|
+
map: this.config.sourceMap ? mapOptions : false
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
css: result.css,
|
|
123
|
+
map: result.map ? result.map.toString() : null
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 🔧 Lightweight mode with built-in prefixer
|
|
128
|
+
async processWithBuiltIn(cssString, options, mapOptions) {
|
|
129
|
+
if (!this.hasBuiltInDeps) {
|
|
130
|
+
return { css: cssString, map: null };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.targetBrowsers = browserslist(this.config.browsers);
|
|
134
|
+
|
|
135
|
+
const from = options.from || 'input.css';
|
|
136
|
+
const to = options.to || 'output.css';
|
|
137
|
+
|
|
138
|
+
const result = await postcss([
|
|
139
|
+
this.createBuiltInPlugin()
|
|
140
|
+
]).process(cssString, {
|
|
141
|
+
from,
|
|
142
|
+
to,
|
|
143
|
+
map: this.config.sourceMap ? mapOptions : false
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
css: result.css,
|
|
148
|
+
map: result.map ? result.map.toString() : null
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
createBuiltInPlugin() {
|
|
153
|
+
return (root) => {
|
|
154
|
+
root.walkDecls(decl => {
|
|
155
|
+
this.processBuiltInDeclaration(decl);
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
processBuiltInDeclaration(decl) {
|
|
161
|
+
const { prop, value } = decl;
|
|
162
|
+
|
|
163
|
+
if (this.commonProperties.includes(prop)) {
|
|
164
|
+
this.addPrefixesFromCaniuse(decl);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (this.specialValues[prop]?.includes(value)) {
|
|
168
|
+
this.addSpecialValuePrefixes(decl);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
addPrefixesFromCaniuse(decl) {
|
|
173
|
+
if (!this.caniuseData) return;
|
|
174
|
+
|
|
175
|
+
const feature = this.findFeature(decl.prop);
|
|
176
|
+
if (!feature) return;
|
|
177
|
+
|
|
178
|
+
const prefixes = new Set();
|
|
179
|
+
|
|
180
|
+
this.targetBrowsers.forEach(browser => {
|
|
181
|
+
const [id, versionStr] = browser.split(' ');
|
|
182
|
+
const version = parseFloat(versionStr.split('-')[0]);
|
|
183
|
+
const stats = feature.stats[id];
|
|
184
|
+
|
|
185
|
+
if (stats) {
|
|
186
|
+
const versions = Object.keys(stats)
|
|
187
|
+
.map(v => parseFloat(v.split('-')[0]))
|
|
188
|
+
.filter(v => !isNaN(v))
|
|
189
|
+
.sort((a, b) => a - b);
|
|
190
|
+
|
|
191
|
+
const closestVersion = versions.find(v => v <= version) || versions[0];
|
|
192
|
+
|
|
193
|
+
if (closestVersion) {
|
|
194
|
+
const support = stats[closestVersion.toString()];
|
|
195
|
+
if (support && support.includes('x')) {
|
|
196
|
+
const prefix = this.browserPrefixMap[id.split('-')[0]];
|
|
197
|
+
if (prefix) prefixes.add(prefix);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
prefixes.forEach(prefix => {
|
|
204
|
+
decl.cloneBefore({
|
|
205
|
+
prop: `-${prefix}-${decl.prop}`,
|
|
206
|
+
value: decl.value
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
addSpecialValuePrefixes(decl) {
|
|
212
|
+
const { prop, value } = decl;
|
|
213
|
+
|
|
214
|
+
if (prop === 'display') {
|
|
215
|
+
if (value === 'flex' || value === 'inline-flex') {
|
|
216
|
+
decl.cloneBefore({ prop: 'display', value: `-webkit-${value}` });
|
|
217
|
+
decl.cloneBefore({
|
|
218
|
+
prop: 'display',
|
|
219
|
+
value: value === 'flex' ? '-ms-flexbox' : '-ms-inline-flexbox'
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (value === 'grid' || value === 'inline-grid') {
|
|
223
|
+
decl.cloneBefore({
|
|
224
|
+
prop: 'display',
|
|
225
|
+
value: value === 'grid' ? '-ms-grid' : '-ms-inline-grid'
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (prop === 'background-clip' && value === 'text') {
|
|
231
|
+
decl.cloneBefore({ prop: '-webkit-background-clip', value: 'text' });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (prop === 'position' && value === 'sticky') {
|
|
235
|
+
decl.cloneBefore({ prop: 'position', value: '-webkit-sticky' });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
findFeature(property) {
|
|
240
|
+
if (!this.caniuseData) return null;
|
|
241
|
+
|
|
242
|
+
const featureMap = {
|
|
243
|
+
'transform': 'transforms2d',
|
|
244
|
+
'transform-origin': 'transforms2d',
|
|
245
|
+
'transform-style': 'transforms3d',
|
|
246
|
+
'perspective': 'transforms3d',
|
|
247
|
+
'backface-visibility': 'transforms3d',
|
|
248
|
+
'transition': 'css-transitions',
|
|
249
|
+
'animation': 'css-animation',
|
|
250
|
+
'backdrop-filter': 'backdrop-filter',
|
|
251
|
+
'filter': 'css-filters',
|
|
252
|
+
'user-select': 'user-select-none',
|
|
253
|
+
'appearance': 'css-appearance',
|
|
254
|
+
'mask-image': 'css-masks',
|
|
255
|
+
'box-shadow': 'css-boxshadow',
|
|
256
|
+
'border-radius': 'border-radius',
|
|
257
|
+
'text-fill-color': 'text-stroke',
|
|
258
|
+
'text-stroke': 'text-stroke',
|
|
259
|
+
'background-clip': 'background-img-opts',
|
|
260
|
+
'flex': 'flexbox',
|
|
261
|
+
'flex-grow': 'flexbox',
|
|
262
|
+
'flex-shrink': 'flexbox',
|
|
263
|
+
'flex-basis': 'flexbox',
|
|
264
|
+
'justify-content': 'flexbox',
|
|
265
|
+
'align-items': 'flexbox',
|
|
266
|
+
'grid': 'css-grid',
|
|
267
|
+
'grid-template': 'css-grid',
|
|
268
|
+
'grid-column': 'css-grid',
|
|
269
|
+
'grid-row': 'css-grid'
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const featureId = featureMap[property];
|
|
273
|
+
return featureId ? this.caniuseData[featureId] : null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
getCommonProperties() {
|
|
277
|
+
return [
|
|
278
|
+
'transform', 'transform-origin', 'transform-style',
|
|
279
|
+
'transition', 'transition-property', 'transition-duration', 'transition-timing-function',
|
|
280
|
+
'animation', 'animation-name', 'animation-duration', 'animation-timing-function',
|
|
281
|
+
'animation-delay', 'animation-iteration-count', 'animation-direction',
|
|
282
|
+
'animation-fill-mode', 'animation-play-state',
|
|
283
|
+
'backdrop-filter', 'filter',
|
|
284
|
+
'user-select', 'appearance',
|
|
285
|
+
'text-fill-color', 'text-stroke', 'text-stroke-color', 'text-stroke-width',
|
|
286
|
+
'background-clip',
|
|
287
|
+
'mask-image', 'mask-clip', 'mask-composite', 'mask-origin',
|
|
288
|
+
'mask-position', 'mask-repeat', 'mask-size',
|
|
289
|
+
'box-shadow', 'border-radius', 'box-sizing',
|
|
290
|
+
'display', 'flex', 'flex-grow', 'flex-shrink', 'flex-basis',
|
|
291
|
+
'justify-content', 'align-items', 'align-self', 'align-content',
|
|
292
|
+
'grid', 'grid-template', 'grid-column', 'grid-row'
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
module.exports = ChainCSSPrefixer;
|
package/transpiler.js
CHANGED
|
@@ -12,6 +12,54 @@ const chain = {
|
|
|
12
12
|
bgAttachment(bga){ this.catcher.backgroundAttachment = bga; return this; },
|
|
13
13
|
bgPosition(bgp){ this.catcher.backgroundPosition = bgp; return this; },
|
|
14
14
|
|
|
15
|
+
// Border
|
|
16
|
+
border(b){ this.catcher.border = b; return this; },
|
|
17
|
+
borderStyle(bs){ this.catcher.borderStyle = bs; return this; },
|
|
18
|
+
borderWidth(bw){ this.catcher.borderWidth = bw; return this; },
|
|
19
|
+
borderColor(bc){ this.catcher.borderColor = bc; return this; },
|
|
20
|
+
borderRadius(br){ this.catcher.borderRadius = br; return this; },
|
|
21
|
+
borderSideStyle(side, value){
|
|
22
|
+
if (side === 'top') {
|
|
23
|
+
this.catcher.borderTopStyle = value;
|
|
24
|
+
} else if (side === 'right') {
|
|
25
|
+
this.catcher.borderRightStyle = value;
|
|
26
|
+
} else if (side === 'bottom') {
|
|
27
|
+
this.catcher.borderBottomStyle = value;
|
|
28
|
+
} else if (side === 'left') {
|
|
29
|
+
this.catcher.borderLeftStyle = value;
|
|
30
|
+
}
|
|
31
|
+
return this;
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// Margin
|
|
35
|
+
margin(m){ this.catcher.margin = m; return this; },
|
|
36
|
+
marginTop(mt){ this.catcher.marginTop = mt; return this; },
|
|
37
|
+
marginRight(mr){ this.catcher.marginRight = mr; return this; },
|
|
38
|
+
marginBottom(mb){ this.catcher.marginBottom = mb; return this; },
|
|
39
|
+
marginLeft(ml){ this.catcher.marginLeft = ml; return this; },
|
|
40
|
+
|
|
41
|
+
// Padding
|
|
42
|
+
padding(p){ this.catcher.padding = p; return this; },
|
|
43
|
+
paddingTop(pt){ this.catcher.paddingTop = pt; return this; },
|
|
44
|
+
paddingRight(pr){ this.catcher.paddingRight = pr; return this; },
|
|
45
|
+
paddingBottom(pb){ this.catcher.paddingBottom = pb; return this; },
|
|
46
|
+
paddingLeft(pl){ this.catcher.paddingLeft = pl; return this; },
|
|
47
|
+
|
|
48
|
+
// Height, Width and Max-width
|
|
49
|
+
width(w){ this.catcher.width = w; return this; },
|
|
50
|
+
minWidth(mnw){ this.catcher.minWidth = mnw; return this; },
|
|
51
|
+
maxWidth(mxw){ this.catcher.maxWidth = mxw; return this; },
|
|
52
|
+
height(h){ this.catcher.height = h; return this; },
|
|
53
|
+
minHeight(mnh){ this.catcher.minHeight = mnh; return this; },
|
|
54
|
+
maxHeight(mxh){ this.catcher.maxHeight = mxh; return this; },
|
|
55
|
+
|
|
56
|
+
// Outline
|
|
57
|
+
outline(o){ this.catcher.outline = o; return this; },
|
|
58
|
+
outlineColor(oc){ this.catcher.outlineColor = oc; return this; },
|
|
59
|
+
outlineStyle(os){ this.catcher.outlineStyle = os; return this; },
|
|
60
|
+
outlineWidth(ow){ this.catcher.outlineWidth = ow; return this; },
|
|
61
|
+
outlineOffset(oo){ this.catcher.outlineOffset = oo; return this; },
|
|
62
|
+
|
|
15
63
|
// Text
|
|
16
64
|
color(c){ this.catcher.color = c; return this; },
|
|
17
65
|
direction(d){ this.catcher.direction = d; return this; },
|
|
@@ -37,31 +85,19 @@ const chain = {
|
|
|
37
85
|
wordSpacing(ws){ this.catcher.wordSpacing = ws; return this; },
|
|
38
86
|
whiteSpace(sws){ this.catcher.whiteSpace = sws; return this; },
|
|
39
87
|
|
|
40
|
-
// Border
|
|
41
|
-
border(b){ this.catcher.border = b; return this; },
|
|
42
|
-
borderStyle(bs){ this.catcher.borderStyle = bs; return this; },
|
|
43
|
-
borderWidth(bw){ this.catcher.borderWidth = bw; return this; },
|
|
44
|
-
borderColor(bc){ this.catcher.borderColor = bc; return this; },
|
|
45
|
-
borderRadius(br){ this.catcher.borderRadius = br; return this; },
|
|
46
|
-
borderSideStyle(side, value){
|
|
47
|
-
if (side === 'top') {
|
|
48
|
-
this.catcher.borderTopStyle = value;
|
|
49
|
-
} else if (side === 'right') {
|
|
50
|
-
this.catcher.borderRightStyle = value;
|
|
51
|
-
} else if (side === 'bottom') {
|
|
52
|
-
this.catcher.borderBottomStyle = value;
|
|
53
|
-
} else if (side === 'left') {
|
|
54
|
-
this.catcher.borderLeftStyle = value;
|
|
55
|
-
}
|
|
56
|
-
return this;
|
|
57
|
-
},
|
|
58
|
-
|
|
59
88
|
// Font
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
font(f){ this.catcher.font = f; return this; },
|
|
90
|
+
fontFamily(ff){ this.catcher.fontFamily = ff; return this; },
|
|
91
|
+
fontStyle(fs){ this.catcher.fontStyle = fs; return this; },
|
|
92
|
+
fontWeight(fw){ this.catcher.fontWeight = fw; return this; },
|
|
93
|
+
fontVariant(fv){ this.catcher.fontVariant = fv; return this; },
|
|
94
|
+
fontSize(fsz){ this.catcher.fontSize = fsz; return this; },
|
|
95
|
+
|
|
96
|
+
// List Style
|
|
97
|
+
listStyle(ls){ this.catcher.listStyle = ls; return this; },
|
|
98
|
+
listStyleType(lst){ this.catcher.listStyleType = lst; return this; },
|
|
99
|
+
listStyleImage(lsi){ this.catcher.listStyleImage = lsi; return this; },
|
|
100
|
+
listStylePosition(lsp){ this.catcher.listStylePosition = lsp; return this; },
|
|
65
101
|
|
|
66
102
|
// Display
|
|
67
103
|
display(d){ this.catcher.display = d; return this; },
|
|
@@ -76,27 +112,11 @@ const chain = {
|
|
|
76
112
|
order(o){ this.catcher.order = o; return this; },
|
|
77
113
|
visibility(v){ this.catcher.visibility = v; return this; },
|
|
78
114
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
minHeight(mnh){ this.catcher.minHeight = mnh; return this; },
|
|
85
|
-
maxHeight(mxh){ this.catcher.maxHeight = mxh; return this; },
|
|
86
|
-
|
|
87
|
-
// Padding
|
|
88
|
-
padding(p){ this.catcher.padding = p; return this; },
|
|
89
|
-
paddingTop(pt){ this.catcher.paddingTop = pt; return this; },
|
|
90
|
-
paddingRight(pr){ this.catcher.paddingRight = pr; return this; },
|
|
91
|
-
paddingBottom(pb){ this.catcher.paddingBottom = pb; return this; },
|
|
92
|
-
paddingLeft(pl){ this.catcher.paddingLeft = pl; return this; },
|
|
93
|
-
|
|
94
|
-
// Margin
|
|
95
|
-
margin(m){ this.catcher.margin = m; return this; },
|
|
96
|
-
marginTop(mt){ this.catcher.marginTop = mt; return this; },
|
|
97
|
-
marginRight(mr){ this.catcher.marginRight = mr; return this; },
|
|
98
|
-
marginBottom(mb){ this.catcher.marginBottom = mb; return this; },
|
|
99
|
-
marginLeft(ml){ this.catcher.marginLeft = ml; return this; },
|
|
115
|
+
// Position
|
|
116
|
+
position(p){ this.catcher.position = p; return this; },
|
|
117
|
+
top(t){ this.catcher.top = t; return this; },
|
|
118
|
+
left(l){ this.catcher.left = l; return this; },
|
|
119
|
+
bottom(b){ this.catcher.bottom = b; return this; },
|
|
100
120
|
|
|
101
121
|
// Overflow
|
|
102
122
|
overflow(o){ this.catcher.overflow = o; return this; },
|
|
@@ -104,28 +124,19 @@ const chain = {
|
|
|
104
124
|
overflowY(oy){ this.catcher.overflowY = oy; return this; },
|
|
105
125
|
overflowWrap(ow){ this.catcher.overflowWrap = ow; return this; },
|
|
106
126
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
outlineStyle(os){ this.catcher.outlineStyle = os; return this; },
|
|
117
|
-
outlineWidth(ow){ this.catcher.outlineWidth = ow; return this; },
|
|
118
|
-
outlineOffset(oo){ this.catcher.outlineOffset = oo; return this; },
|
|
127
|
+
animation(a){ this.catcher.animation = a; return this; },
|
|
128
|
+
textFillColor(tfc){ this.catcher.textFillColor = tfc; return this; },
|
|
129
|
+
backgroundClip(bc){ this.catcher.backgroundClip = bc; return this; },
|
|
130
|
+
gridTemplateColumns(gtc){ this.catcher.gridTemplateColumns = gtc; return this; },
|
|
131
|
+
right(r){ this.catcher.right = r; return this; },
|
|
132
|
+
transform(tf){ this.catcher.transform = tf; return this; },
|
|
133
|
+
boxShadow(bs){ this.catcher.boxShadow = bs; return this; },
|
|
134
|
+
backdropFilter(bf){ this.catcher.backdropFilter = bf; return this; },
|
|
135
|
+
gap(g){ this.catcher.gap = g; return this; },
|
|
119
136
|
|
|
120
137
|
// Float
|
|
121
138
|
float(f){ this.catcher.float = f; return this; },
|
|
122
139
|
clear(c){ this.catcher.clear = c; return this; },
|
|
123
|
-
|
|
124
|
-
// Position
|
|
125
|
-
position(p){ this.catcher.position = p; return this; },
|
|
126
|
-
top(t){ this.catcher.top = t; return this; },
|
|
127
|
-
left(l){ this.catcher.left = l; return this; },
|
|
128
|
-
bottom(b){ this.catcher.bottom = b; return this; },
|
|
129
140
|
|
|
130
141
|
// Z-index
|
|
131
142
|
zIndex(zi){ this.catcher.zIndex = zi; return this; },
|
|
@@ -197,9 +208,6 @@ const compile = (obj) => {
|
|
|
197
208
|
const element = obj[key];
|
|
198
209
|
let selectors = element.selectors || []; // Provide default empty array if selectors is undefined
|
|
199
210
|
let elementCSS = '';
|
|
200
|
-
console.log('Problematic element:', element);
|
|
201
|
-
console.log('Type of element:', typeof element);
|
|
202
|
-
console.log('Is element null?', element === null);
|
|
203
211
|
for (let prop in element) {
|
|
204
212
|
|
|
205
213
|
if (element.hasOwnProperty(prop) && prop !== 'selectors') {
|
|
@@ -218,9 +226,6 @@ const compile = (obj) => {
|
|
|
218
226
|
};
|
|
219
227
|
|
|
220
228
|
const get = (filename) => {
|
|
221
|
-
console.log('get() called with:', filename);
|
|
222
|
-
console.log('Current working directory:', process.cwd());
|
|
223
|
-
|
|
224
229
|
const fileExt = path.extname(filename).toLowerCase();
|
|
225
230
|
if (fileExt !== '.jcss') {
|
|
226
231
|
throw new Error(`Import error: ${filename} must have .jcss extension`);
|
|
@@ -228,11 +233,9 @@ const get = (filename) => {
|
|
|
228
233
|
|
|
229
234
|
// Try to resolve the path
|
|
230
235
|
const resolvedPath = path.resolve(process.cwd(), filename);
|
|
231
|
-
console.log('Resolved path:', resolvedPath);
|
|
232
236
|
|
|
233
237
|
// Check if file exists
|
|
234
238
|
const exists = fs.existsSync(resolvedPath);
|
|
235
|
-
console.log('File exists?', exists);
|
|
236
239
|
|
|
237
240
|
if (!exists) {
|
|
238
241
|
throw new Error(`File not found: ${filename} (resolved to: ${resolvedPath})`);
|
|
@@ -245,7 +248,6 @@ if (typeof global !== 'undefined') {
|
|
|
245
248
|
global.chain = chain;
|
|
246
249
|
}
|
|
247
250
|
|
|
248
|
-
|
|
249
251
|
module.exports = {
|
|
250
252
|
chain,
|
|
251
253
|
run,
|