chaincss 1.12.8

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.
@@ -0,0 +1,215 @@
1
+ // node/plugins/vite-plugin.js
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import { createRequire } from 'node:module';
5
+ import CleanCSS from 'clean-css';
6
+ import { $, run, compile as originalCompile, chain } from '../btt.js';
7
+
8
+ const require = createRequire(import.meta.url);
9
+
10
+ // Optional: Try to load prefixer
11
+ let prefixer;
12
+ try {
13
+ const ChainCSSPrefixer = (await import('../prefixer.js')).default;
14
+ prefixer = new ChainCSSPrefixer({ enabled: true });
15
+ } catch (err) {
16
+ console.warn('ChainCSS: Prefixer not available, autoprefixing disabled');
17
+ prefixer = { process: async (css) => ({ css }) };
18
+ }
19
+
20
+ // Cache for processed files
21
+ const fileCache = new Map();
22
+ const compiledCache = new Map();
23
+
24
+ // Helper to compile script without temp files
25
+ const compileScript = (scriptBlock, filename, get) => {
26
+ const dirname = path.dirname(filename);
27
+
28
+ // Reset CSS output
29
+ chain.cssOutput = '';
30
+
31
+ // Create a function from the script - no temp files!
32
+ const fn = new Function(
33
+ '$',
34
+ 'run',
35
+ 'compile',
36
+ 'chain',
37
+ 'get',
38
+ '__filename',
39
+ '__dirname',
40
+ scriptBlock
41
+ );
42
+
43
+ // Execute with helpers
44
+ fn($, run, originalCompile, chain, get, filename, dirname);
45
+
46
+ return chain.cssOutput || '';
47
+ };
48
+
49
+ const processJavascriptBlocks = (content, filename, get) => {
50
+ const blocks = content.split(/<@([\s\S]*?)@>/gm);
51
+ let output = '';
52
+
53
+ for (let i = 0; i < blocks.length; i++) {
54
+ if (i % 2 === 0) {
55
+ // Static content
56
+ output += blocks[i];
57
+ } else {
58
+ // JavaScript block
59
+ const css = compileScript(blocks[i], filename, get);
60
+ if (css && typeof css === 'string') {
61
+ output += css;
62
+ }
63
+ }
64
+ }
65
+
66
+ return output.trim();
67
+ };
68
+
69
+ const processJCSSFile = (filePath) => {
70
+ const abs = path.resolve(filePath);
71
+
72
+ // Return cached result if available
73
+ if (fileCache.has(abs)) return fileCache.get(abs);
74
+
75
+ // Check if file exists
76
+ if (!fs.existsSync(abs)) {
77
+ throw new Error(`ChainCSS: File not found: ${abs}`);
78
+ }
79
+
80
+ const content = fs.readFileSync(abs, 'utf8');
81
+ const dirname = path.dirname(abs);
82
+
83
+ // Create get function for this file
84
+ const get = (relativePath) => {
85
+ const targetPath = path.resolve(dirname, relativePath);
86
+ return processJCSSFile(targetPath);
87
+ };
88
+
89
+ // Process the file
90
+ const result = processJavascriptBlocks(content, abs, get);
91
+
92
+ // Cache the result
93
+ fileCache.set(abs, result);
94
+ return result;
95
+ };
96
+
97
+ // Minify and prefix CSS
98
+ const processCSS = async (css, filepath, options = {}) => {
99
+ const { minify = true, prefix = true } = options;
100
+ let processed = css;
101
+
102
+ // Add prefixing
103
+ if (prefix && prefixer) {
104
+ try {
105
+ const result = await prefixer.process(css, { from: filepath });
106
+ processed = result.css;
107
+ } catch (err) {
108
+ console.warn(`ChainCSS prefixer error in ${filepath}:`, err.message);
109
+ }
110
+ }
111
+
112
+ // Minify
113
+ if (minify) {
114
+ const minified = new CleanCSS({ level: 2 }).minify(processed);
115
+ if (minified.errors.length) {
116
+ console.warn(`ChainCSS minification errors in ${filepath}:`, minified.errors);
117
+ }
118
+ return minified.styles;
119
+ }
120
+
121
+ return processed;
122
+ };
123
+
124
+ export default function chaincssVite(opts = {}) {
125
+ const {
126
+ extension = '.jcss',
127
+ minify = process.env.NODE_ENV === 'production',
128
+ prefix = true,
129
+ hmr = true
130
+ } = opts;
131
+
132
+ return {
133
+ name: 'vite-plugin-chaincss',
134
+ enforce: 'pre',
135
+
136
+ // Transform .jcss files
137
+ async transform(code, id) {
138
+ if (!id.endsWith(extension)) return null;
139
+
140
+ try {
141
+ // Create get function for root file
142
+ const dirname = path.dirname(id);
143
+ const get = (relativePath) => {
144
+ const targetPath = path.resolve(dirname, relativePath);
145
+ return processJCSSFile(targetPath);
146
+ };
147
+
148
+ // Process the file
149
+ let css = processJavascriptBlocks(code, id, get);
150
+
151
+ // Process CSS (prefix + minify)
152
+ css = await processCSS(css, id, { minify, prefix });
153
+
154
+ // In development, inject CSS for HMR
155
+ if (process.env.NODE_ENV !== 'production') {
156
+ return {
157
+ code: `
158
+ // ChainCSS HMR
159
+ const id = ${JSON.stringify(id)};
160
+ const css = ${JSON.stringify(css)};
161
+
162
+ // Add style to head
163
+ let style = document.querySelector(\`style[data-chaincss="\${id}"]\`);
164
+ if (!style) {
165
+ style = document.createElement('style');
166
+ style.setAttribute('data-chaincss', id);
167
+ document.head.appendChild(style);
168
+ }
169
+ style.textContent = css;
170
+
171
+ // HMR handling
172
+ if (import.meta.hot) {
173
+ import.meta.hot.accept((newModule) => {
174
+ if (newModule?.default) {
175
+ style.textContent = newModule.default;
176
+ }
177
+ });
178
+
179
+ import.meta.hot.dispose(() => {
180
+ style.remove();
181
+ });
182
+ }
183
+
184
+ export default css;
185
+ `,
186
+ map: null
187
+ };
188
+ }
189
+
190
+ // Production: just export CSS
191
+ return {
192
+ code: `export default ${JSON.stringify(css)};`,
193
+ map: null
194
+ };
195
+
196
+ } catch (err) {
197
+ this.error(`ChainCSS error in ${id}: ${err.message}`);
198
+ return null;
199
+ }
200
+ },
201
+
202
+ // Handle HMR updates
203
+ handleHotUpdate({ file, server }) {
204
+ if (file.endsWith(extension)) {
205
+ // Invalidate cache for changed file
206
+ fileCache.delete(file);
207
+ // Trigger reload
208
+ server.ws.send({
209
+ type: 'full-reload',
210
+ path: '*'
211
+ });
212
+ }
213
+ }
214
+ };
215
+ }
@@ -0,0 +1,41 @@
1
+ // node/plugins/webpack-plugin.js
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+ const fs = require('fs');
5
+
6
+ class ChainCSSWebpackPlugin {
7
+ constructor(options = {}) {
8
+ this.options = {
9
+ atomic: process.env.NODE_ENV === 'production',
10
+ input: './src/styles/main.jcss',
11
+ output: './dist',
12
+ ...options
13
+ };
14
+ }
15
+
16
+ apply(compiler) {
17
+ compiler.hooks.beforeCompile.tapAsync('ChainCSSPlugin', async (params, callback) => {
18
+ try {
19
+ const inputPath = path.resolve(process.cwd(), this.options.input);
20
+ const outputPath = path.resolve(process.cwd(), this.options.output);
21
+
22
+ if (!fs.existsSync(inputPath)) {
23
+ console.warn('ChainCSS: No main.jcss file found, skipping...');
24
+ callback();
25
+ return;
26
+ }
27
+
28
+ const cmd = `node ${path.join(__dirname, '../chaincss.js')} ${inputPath} ${outputPath} ${this.options.atomic ? '--atomic' : ''}`;
29
+ execSync(cmd, { stdio: 'inherit' });
30
+
31
+ console.log('ChainCSS compiled successfully');
32
+ callback();
33
+ } catch (err) {
34
+ console.error('ChainCSS compilation failed:', err.message);
35
+ callback(err);
36
+ }
37
+ });
38
+ }
39
+ }
40
+
41
+ module.exports = ChainCSSWebpackPlugin;
@@ -0,0 +1,237 @@
1
+ let postcss, browserslist, caniuse, autoprefixer;
2
+ try {
3
+ postcss = require('postcss');
4
+ browserslist = require('browserslist');
5
+ caniuse = require('caniuse-db/fulldata-json/data-2.0.json');
6
+ } catch (err) {
7
+ }
8
+ try {
9
+ autoprefixer = require('autoprefixer');
10
+ } catch (err) {
11
+ }
12
+ class ChainCSSPrefixer {
13
+ constructor(config = {}) {
14
+ this.config = {
15
+ browsers: config.browsers || ['> 0.5%', 'last 2 versions', 'not dead'],
16
+ enabled: config.enabled !== false,
17
+ mode: config.mode || 'auto',
18
+ sourceMap: config.sourceMap !== false, // Enable source maps by default
19
+ sourceMapInline: config.sourceMapInline || false,
20
+ ...config
21
+ };
22
+ this.hasBuiltInDeps = !!(postcss && browserslist && caniuse);
23
+ this.hasAutoprefixer = !!autoprefixer;
24
+ this.prefixerMode = this.determineMode();
25
+ this.caniuseData = caniuse ? caniuse.data : null;
26
+ this.commonProperties = this.getCommonProperties();
27
+ this.specialValues = {
28
+ 'display': ['flex', 'inline-flex', 'grid', 'inline-grid'],
29
+ 'background-clip': ['text'],
30
+ 'position': ['sticky']
31
+ };
32
+ this.browserPrefixMap = {
33
+ 'chrome': 'webkit', 'safari': 'webkit', 'firefox': 'moz',
34
+ 'ie': 'ms', 'edge': 'webkit', 'ios_saf': 'webkit',
35
+ 'and_chr': 'webkit', 'android': 'webkit', 'opera': 'webkit',
36
+ 'op_mob': 'webkit', 'samsung': 'webkit', 'and_ff': 'moz'
37
+ };
38
+ this.targetBrowsers = null;
39
+ }
40
+ determineMode() {
41
+ if (this.config.mode === 'full' && !this.hasAutoprefixer) {
42
+ console.warn('⚠️ Full mode requested but autoprefixer not installed. Falling back to lightweight mode.');
43
+ console.warn(' To use full mode: npm install autoprefixer postcss caniuse-db browserslist\n');
44
+ return 'lightweight';
45
+ }
46
+ if (this.config.mode === 'lightweight') {
47
+ return 'lightweight';
48
+ }
49
+ if (this.config.mode === 'full' && this.hasAutoprefixer) {
50
+ return 'full';
51
+ }
52
+ if (this.config.mode === 'auto') {
53
+ return this.hasAutoprefixer ? 'full' : 'lightweight';
54
+ }
55
+ return 'lightweight';
56
+ }
57
+ async process(cssString, options = {}) {
58
+ if (!this.config.enabled) {
59
+ return { css: cssString, map: null };
60
+ }
61
+ try {
62
+ const mapOptions = {
63
+ inline: this.config.sourceMapInline,
64
+ annotation: false,
65
+ sourcesContent: true
66
+ };
67
+ if (this.prefixerMode === 'full') {
68
+ return await this.processWithAutoprefixer(cssString, options, mapOptions);
69
+ }
70
+ return await this.processWithBuiltIn(cssString, options, mapOptions);
71
+ } catch (err) {
72
+ console.error('Prefixer error:', err.message);
73
+ return { css: cssString, map: null };
74
+ }
75
+ }
76
+ async processWithAutoprefixer(cssString, options, mapOptions) {
77
+ const from = options.from || 'input.css';
78
+ const to = options.to || 'output.css';
79
+ const result = await postcss([
80
+ autoprefixer({ overrideBrowserslist: this.config.browsers })
81
+ ]).process(cssString, {
82
+ from,
83
+ to,
84
+ map: this.config.sourceMap ? mapOptions : false
85
+ });
86
+ return {
87
+ css: result.css,
88
+ map: result.map ? result.map.toString() : null
89
+ };
90
+ }
91
+ async processWithBuiltIn(cssString, options, mapOptions) {
92
+ if (!this.hasBuiltInDeps) {
93
+ return { css: cssString, map: null };
94
+ }
95
+ this.targetBrowsers = browserslist(this.config.browsers);
96
+ const from = options.from || 'input.css';
97
+ const to = options.to || 'output.css';
98
+ const result = await postcss([
99
+ this.createBuiltInPlugin()
100
+ ]).process(cssString, {
101
+ from,
102
+ to,
103
+ map: this.config.sourceMap ? mapOptions : false
104
+ });
105
+ return {
106
+ css: result.css,
107
+ map: result.map ? result.map.toString() : null
108
+ };
109
+ }
110
+ createBuiltInPlugin() {
111
+ return (root) => {
112
+ root.walkDecls(decl => {
113
+ this.processBuiltInDeclaration(decl);
114
+ });
115
+ };
116
+ }
117
+ processBuiltInDeclaration(decl) {
118
+ const { prop, value } = decl;
119
+ if (this.commonProperties.includes(prop)) {
120
+ this.addPrefixesFromCaniuse(decl);
121
+ }
122
+ if (this.specialValues[prop]?.includes(value)) {
123
+ this.addSpecialValuePrefixes(decl);
124
+ }
125
+ }
126
+ addPrefixesFromCaniuse(decl) {
127
+ if (!this.caniuseData) return;
128
+ const feature = this.findFeature(decl.prop);
129
+ if (!feature) return;
130
+ const prefixes = new Set();
131
+ this.targetBrowsers.forEach(browser => {
132
+ const [id, versionStr] = browser.split(' ');
133
+ const version = parseFloat(versionStr.split('-')[0]);
134
+ const stats = feature.stats[id];
135
+ if (stats) {
136
+ const versions = Object.keys(stats)
137
+ .map(v => parseFloat(v.split('-')[0]))
138
+ .filter(v => !isNaN(v))
139
+ .sort((a, b) => a - b);
140
+ const closestVersion = versions.find(v => v <= version) || versions[0];
141
+ if (closestVersion) {
142
+ const support = stats[closestVersion.toString()];
143
+ if (support && support.includes('x')) {
144
+ const prefix = this.browserPrefixMap[id.split('-')[0]];
145
+ if (prefix) prefixes.add(prefix);
146
+ }
147
+ }
148
+ }
149
+ });
150
+
151
+ prefixes.forEach(prefix => {
152
+ decl.cloneBefore({
153
+ prop: `-${prefix}-${decl.prop}`,
154
+ value: decl.value
155
+ });
156
+ });
157
+ }
158
+ addSpecialValuePrefixes(decl) {
159
+ const { prop, value } = decl;
160
+ if (prop === 'display') {
161
+ if (value === 'flex' || value === 'inline-flex') {
162
+ decl.cloneBefore({ prop: 'display', value: `-webkit-${value}` });
163
+ decl.cloneBefore({
164
+ prop: 'display',
165
+ value: value === 'flex' ? '-ms-flexbox' : '-ms-inline-flexbox'
166
+ });
167
+ }
168
+ if (value === 'grid' || value === 'inline-grid') {
169
+ decl.cloneBefore({
170
+ prop: 'display',
171
+ value: value === 'grid' ? '-ms-grid' : '-ms-inline-grid'
172
+ });
173
+ }
174
+ }
175
+ if (prop === 'background-clip' && value === 'text') {
176
+ decl.cloneBefore({ prop: '-webkit-background-clip', value: 'text' });
177
+ }
178
+ if (prop === 'position' && value === 'sticky') {
179
+ decl.cloneBefore({ prop: 'position', value: '-webkit-sticky' });
180
+ }
181
+ }
182
+
183
+ findFeature(property) {
184
+ if (!this.caniuseData) return null;
185
+ const featureMap = {
186
+ 'transform': 'transforms2d',
187
+ 'transform-origin': 'transforms2d',
188
+ 'transform-style': 'transforms3d',
189
+ 'perspective': 'transforms3d',
190
+ 'backface-visibility': 'transforms3d',
191
+ 'transition': 'css-transitions',
192
+ 'animation': 'css-animation',
193
+ 'backdrop-filter': 'backdrop-filter',
194
+ 'filter': 'css-filters',
195
+ 'user-select': 'user-select-none',
196
+ 'appearance': 'css-appearance',
197
+ 'mask-image': 'css-masks',
198
+ 'box-shadow': 'css-boxshadow',
199
+ 'border-radius': 'border-radius',
200
+ 'text-fill-color': 'text-stroke',
201
+ 'text-stroke': 'text-stroke',
202
+ 'background-clip': 'background-img-opts',
203
+ 'flex': 'flexbox',
204
+ 'flex-grow': 'flexbox',
205
+ 'flex-shrink': 'flexbox',
206
+ 'flex-basis': 'flexbox',
207
+ 'justify-content': 'flexbox',
208
+ 'align-items': 'flexbox',
209
+ 'grid': 'css-grid',
210
+ 'grid-template': 'css-grid',
211
+ 'grid-column': 'css-grid',
212
+ 'grid-row': 'css-grid'
213
+ };
214
+ const featureId = featureMap[property];
215
+ return featureId ? this.caniuseData[featureId] : null;
216
+ }
217
+ getCommonProperties() {
218
+ return [
219
+ 'transform', 'transform-origin', 'transform-style',
220
+ 'transition', 'transition-property', 'transition-duration', 'transition-timing-function',
221
+ 'animation', 'animation-name', 'animation-duration', 'animation-timing-function',
222
+ 'animation-delay', 'animation-iteration-count', 'animation-direction',
223
+ 'animation-fill-mode', 'animation-play-state',
224
+ 'backdrop-filter', 'filter',
225
+ 'user-select', 'appearance',
226
+ 'text-fill-color', 'text-stroke', 'text-stroke-color', 'text-stroke-width',
227
+ 'background-clip',
228
+ 'mask-image', 'mask-clip', 'mask-composite', 'mask-origin',
229
+ 'mask-position', 'mask-repeat', 'mask-size',
230
+ 'box-shadow', 'border-radius', 'box-sizing',
231
+ 'display', 'flex', 'flex-grow', 'flex-shrink', 'flex-basis',
232
+ 'justify-content', 'align-items', 'align-self', 'align-content',
233
+ 'grid', 'grid-template', 'grid-column', 'grid-row'
234
+ ];
235
+ }
236
+ }
237
+ module.exports = ChainCSSPrefixer;
package/node/strVal.js ADDED
@@ -0,0 +1,43 @@
1
+ const strVal = {
2
+ userConf: `// Project Configuration
3
+ module.exports = {
4
+ atomic: {
5
+ enabled: true,
6
+ threshold: 3,
7
+ naming: 'hash',
8
+ cache: true,
9
+ cachePath: './.chaincss-cache',
10
+ minify: true
11
+ },
12
+ prefixer: {
13
+ mode: 'auto',
14
+ browsers: ['> 0.5%', 'last 2 versions', 'not dead'],
15
+ enabled: true,
16
+ sourceMap: true,
17
+ sourceMapInline: false
18
+ }
19
+ };
20
+ `,
21
+ cli_opt_guide: `
22
+ ChainCSS - JavaScript-powered CSS preprocessor
23
+
24
+ Usage:
25
+ chaincss <inputFile> <outputFile> [options]
26
+
27
+ Options:
28
+ --watch Watch for changes
29
+ --no-prefix Disable automatic prefixing
30
+ --prefixer-mode <mode> Set prefixer mode (auto|lightweight|full)
31
+ --browsers <list> Browser support list (comma-separated)
32
+ --no-source-map Disable source maps
33
+ --source-map-inline Use inline source maps
34
+
35
+ Examples:
36
+ chaincss style.jcss style.css
37
+ chaincss style.jcss style.css --watch
38
+ chaincss style.jcss style.css --browsers ">5%,last 2 versions"
39
+ chaincss style.jcss style.css --no-prefix
40
+ `
41
+ }
42
+
43
+ module.exports = strVal;
package/package.json ADDED
@@ -0,0 +1,133 @@
1
+ {
2
+ "name": "chaincss",
3
+ "version": "1.12.8",
4
+ "description": "Chainable CSS-in-JS with build-time compilation, atomic CSS, and zero-runtime options",
5
+ "keywords": [
6
+ "css",
7
+ "css-in-js",
8
+ "chaincss",
9
+ "atomic-css",
10
+ "zero-runtime",
11
+ "design-tokens",
12
+ "react",
13
+ "nextjs",
14
+ "vite",
15
+ "webpack"
16
+ ],
17
+ "author": "Rommel Caneos",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/melcanz08/chaincss.git"
22
+ },
23
+ "homepage": "https://chaincss.dev",
24
+ "bugs": {
25
+ "url": "https://github.com/melcanz08/chaincss/issues"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./types.d.ts",
30
+ "browser": {
31
+ "import": "./browser/index.js",
32
+ "require": "./browser/index.js"
33
+ },
34
+ "node": {
35
+ "import": "./node/btt.js",
36
+ "require": "./node/btt.js"
37
+ },
38
+ "default": "./browser/index.js"
39
+ },
40
+ "./react": {
41
+ "types": "./types.d.ts",
42
+ "browser": {
43
+ "import": "./browser/react-hooks.js",
44
+ "require": "./browser/react-hooks.js"
45
+ },
46
+ "default": "./browser/react-hooks.js"
47
+ },
48
+ "./browser/*": {
49
+ "browser": {
50
+ "import": "./browser/*",
51
+ "require": "./browser/*"
52
+ }
53
+ },
54
+ "./node/*": {
55
+ "node": {
56
+ "import": "./node/*",
57
+ "require": "./node/*"
58
+ }
59
+ },
60
+ "./vite-plugin": {
61
+ "types": "./types.d.ts",
62
+ "import": "./node/plugins/vite-plugin.js",
63
+ "require": "./node/plugins/vite-plugin.js",
64
+ "default": "./node/plugins/vite-plugin.js"
65
+ },
66
+ "./webpack-plugin": {
67
+ "types": "./types.d.ts",
68
+ "import": "./node/plugins/webpack-plugin.js",
69
+ "require": "./node/plugins/webpack-plugin.js",
70
+ "default": "./node/plugins/webpack-plugin.js"
71
+ },
72
+ "./next-plugin": {
73
+ "types": "./types.d.ts",
74
+ "import": "./node/plugins/next-plugin.js",
75
+ "require": "./node/plugins/next-plugin.js",
76
+ "default": "./node/plugins/next-plugin.js"
77
+ },
78
+ "./loader": {
79
+ "types": "./types.d.ts",
80
+ "import": "./node/loaders/chaincss-loader.js",
81
+ "require": "./node/loaders/chaincss-loader.js",
82
+ "default": "./node/loaders/chaincss-loader.js"
83
+ }
84
+ },
85
+ "types": "types.d.ts",
86
+ "files": [
87
+ "browser/",
88
+ "node/",
89
+ "shared/",
90
+ "types.d.ts",
91
+ "README.md",
92
+ "LICENSE"
93
+ ],
94
+ "publishConfig": {
95
+ "access": "public",
96
+ "registry": "https://registry.npmjs.org/"
97
+ },
98
+ "bin": {
99
+ "chaincss": "./node/chaincss.js"
100
+ },
101
+ "dependencies": {
102
+ "chokidar": "^3.5.3",
103
+ "clean-css": "^5.3.3"
104
+ },
105
+ "peerDependencies": {
106
+ "react": ">=16.8.0",
107
+ "autoprefixer": "^10.0.0",
108
+ "postcss": "^8.5.6",
109
+ "browserslist": "^4.28.1",
110
+ "caniuse-db": "^1.0.30001770"
111
+ },
112
+ "peerDependenciesMeta": {
113
+ "react": {
114
+ "optional": true
115
+ },
116
+ "autoprefixer": {
117
+ "optional": true
118
+ },
119
+ "postcss": {
120
+ "optional": true
121
+ },
122
+ "browserslist": {
123
+ "optional": true
124
+ },
125
+ "caniuse-db": {
126
+ "optional": true
127
+ }
128
+ },
129
+ "engines": {
130
+ "node": ">=14.0.0"
131
+ },
132
+ "sideEffects": false
133
+ }