@react-hive/honey-css 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 React Hive
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # @react-hive/honey-css
2
+
3
+ A lightweight CSS tokenizer + parser that produces a minimal AST for custom CSS processing.
4
+
5
+ This package is designed as a small foundation for building **CSS transformers**, **preprocessors**, and **CSS-in-JS tooling** – without pulling in heavyweight dependencies like PostCSS.
6
+
7
+ ---
8
+
9
+ ## ✨ Why honey-css?
10
+
11
+ Most CSS parsers today are extremely powerful… and extremely complex.
12
+
13
+ They solve *everything*, but sometimes you only need:
14
+
15
+ - a small tokenizer
16
+ - a predictable AST
17
+ - support for nested rules
18
+ - a clean base for custom transformations
19
+
20
+ **honey-css** focuses on the sweet spot:
21
+
22
+ - 🍯 Small surface area
23
+ - 🎯 Predictable output
24
+ - 🌳 Minimal AST structure
25
+ - 🧩 Easy to extend
26
+ - ⚑ Perfect for custom styling engines
27
+
28
+ If you're building your own styling layer or transformer pipeline, this gives you the core building blocks – without unnecessary overhead.
29
+
30
+ ---
31
+
32
+ ## ✨ Features
33
+
34
+ - βœ… Tokenizes raw CSS into structured tokens
35
+ - βœ… Parses tokens into a minimal developer-friendly AST
36
+ - βœ… Supports nested rules and nested at-rules
37
+ - βœ… Handles common real-world CSS syntax:
38
+ - declarations (`color: red;`)
39
+ - selectors (`.btn:hover {}`)
40
+ - at-rules (`@media (...) {}`)
41
+ - params groups (`url(...)`, `var(...)`)
42
+ - quoted strings (`content: "hello"`)
43
+ - block comments (`/* ... */`)
44
+ - βœ… Tiny, fast, and easy to extend
45
+ - βœ… Built for CSS-in-JS engines and custom compilers
46
+
47
+ ---
48
+
49
+ ## πŸ“¦ Installation
50
+
51
+ Install with pnpm (recommended):
52
+
53
+ ```bash
54
+ pnpm add @react-hive/honey-css
55
+ ```
56
+
57
+ ## πŸš€ Quick Start
58
+
59
+ Tokenizing CSS
60
+
61
+ ```ts
62
+ import { tokenizeCss } from "@react-hive/honey-css";
63
+
64
+ const tokens = tokenizeCss(`
65
+ .btn {
66
+ color: red;
67
+ padding: 12px;
68
+ }
69
+ `);
70
+
71
+ console.log(tokens);
72
+ ```
73
+
74
+ Output:
75
+
76
+ ```json
77
+ [
78
+ { type: "text", value: ".btn" },
79
+ { type: "braceOpen" },
80
+
81
+ { type: "text", value: "color" },
82
+ { type: "colon" },
83
+ { type: "text", value: "red" },
84
+ { type: "semicolon" },
85
+
86
+ { type: "text", value: "padding" },
87
+ { type: "colon" },
88
+ { type: "text", value: "12px" },
89
+ { type: "semicolon" },
90
+
91
+ { type: "braceClose" }
92
+ ]
93
+ ```
94
+
95
+ ## Parsing CSS into AST
96
+
97
+ ```ts
98
+ import { parseCss } from "@react-hive/honey-css";
99
+
100
+ const ast = parseCss(`
101
+ .btn {
102
+ color: red;
103
+ }
104
+ `);
105
+
106
+ console.log(ast);
107
+ ```
108
+
109
+ Output:
110
+
111
+ ```json
112
+ {
113
+ type: "stylesheet",
114
+ body: [
115
+ {
116
+ type: "rule",
117
+ selector: ".btn",
118
+ body: [
119
+ {
120
+ type: "declaration",
121
+ prop: "color",
122
+ value: "red"
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+ }
128
+ ```
129
+
130
+ ## 🌳 AST Overview
131
+
132
+ The AST is intentionally minimal and easy to traverse.
133
+
134
+ **Stylesheet Root**
135
+
136
+ ```
137
+ {
138
+ type: "stylesheet",
139
+ body: HoneyCssNode[]
140
+ }
141
+ ```
142
+
143
+ **Declaration Node**
144
+
145
+ ```
146
+ {
147
+ type: "declaration",
148
+ prop: "padding",
149
+ value: "12px"
150
+ }
151
+ ```
152
+
153
+ Represents:
154
+ ```
155
+ padding: 12px;
156
+ ```
157
+
158
+ **Rule Node**
159
+
160
+ ```
161
+ {
162
+ type: "rule",
163
+ selector: ".child:hover",
164
+ body: [...]
165
+ }
166
+ ```
167
+
168
+ Represents:
169
+
170
+ ```
171
+ .child:hover {
172
+ opacity: 0.5;
173
+ }
174
+ ```
175
+
176
+ **At-Rule Node**
177
+
178
+ ```
179
+ {
180
+ type: "atRule",
181
+ name: "media",
182
+ params: "(max-width: 768px)",
183
+ body: [...]
184
+ }
185
+ ```
186
+
187
+ Represents:
188
+
189
+ ```
190
+ @media (max-width: 768px) {
191
+ color: red;
192
+ }
193
+ ```
194
+
195
+ ## 🎯 Use Cases
196
+
197
+ The **honey-css** is a great fit for:
198
+
199
+ - CSS-in-JS compilers
200
+ - Custom at-rule processors
201
+ - Design system engines
202
+ - Lightweight CSS preprocessors
203
+ - AST-based transformations
204
+
205
+ It is not intended to fully replace PostCSS or implement the full CSS specification – it’s a focused foundation.
206
+
207
+ ## πŸ“„ License
208
+
209
+ MIT Β© Mike Aliinyk
210
+
211
+ Part of the **[React Hive](https://github.com/React-Hive)** ecosystem 🐝
package/dist/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 React Hive
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # @react-hive/honey-css
2
+
3
+ A lightweight CSS tokenizer + parser that produces a minimal AST for custom CSS processing.
4
+
5
+ This package is designed as a small foundation for building **CSS transformers**, **preprocessors**, and **CSS-in-JS tooling** – without pulling in heavyweight dependencies like PostCSS.
6
+
7
+ ---
8
+
9
+ ## ✨ Why honey-css?
10
+
11
+ Most CSS parsers today are extremely powerful… and extremely complex.
12
+
13
+ They solve *everything*, but sometimes you only need:
14
+
15
+ - a small tokenizer
16
+ - a predictable AST
17
+ - support for nested rules
18
+ - a clean base for custom transformations
19
+
20
+ **honey-css** focuses on the sweet spot:
21
+
22
+ - 🍯 Small surface area
23
+ - 🎯 Predictable output
24
+ - 🌳 Minimal AST structure
25
+ - 🧩 Easy to extend
26
+ - ⚑ Perfect for custom styling engines
27
+
28
+ If you're building your own styling layer or transformer pipeline, this gives you the core building blocks – without unnecessary overhead.
29
+
30
+ ---
31
+
32
+ ## ✨ Features
33
+
34
+ - βœ… Tokenizes raw CSS into structured tokens
35
+ - βœ… Parses tokens into a minimal developer-friendly AST
36
+ - βœ… Supports nested rules and nested at-rules
37
+ - βœ… Handles common real-world CSS syntax:
38
+ - declarations (`color: red;`)
39
+ - selectors (`.btn:hover {}`)
40
+ - at-rules (`@media (...) {}`)
41
+ - params groups (`url(...)`, `var(...)`)
42
+ - quoted strings (`content: "hello"`)
43
+ - block comments (`/* ... */`)
44
+ - βœ… Tiny, fast, and easy to extend
45
+ - βœ… Built for CSS-in-JS engines and custom compilers
46
+
47
+ ---
48
+
49
+ ## πŸ“¦ Installation
50
+
51
+ Install with pnpm (recommended):
52
+
53
+ ```bash
54
+ pnpm add @react-hive/honey-css
55
+ ```
56
+
57
+ ## πŸš€ Quick Start
58
+
59
+ Tokenizing CSS
60
+
61
+ ```ts
62
+ import { tokenizeCss } from "@react-hive/honey-css";
63
+
64
+ const tokens = tokenizeCss(`
65
+ .btn {
66
+ color: red;
67
+ padding: 12px;
68
+ }
69
+ `);
70
+
71
+ console.log(tokens);
72
+ ```
73
+
74
+ Output:
75
+
76
+ ```json
77
+ [
78
+ { type: "text", value: ".btn" },
79
+ { type: "braceOpen" },
80
+
81
+ { type: "text", value: "color" },
82
+ { type: "colon" },
83
+ { type: "text", value: "red" },
84
+ { type: "semicolon" },
85
+
86
+ { type: "text", value: "padding" },
87
+ { type: "colon" },
88
+ { type: "text", value: "12px" },
89
+ { type: "semicolon" },
90
+
91
+ { type: "braceClose" }
92
+ ]
93
+ ```
94
+
95
+ ## Parsing CSS into AST
96
+
97
+ ```ts
98
+ import { parseCss } from "@react-hive/honey-css";
99
+
100
+ const ast = parseCss(`
101
+ .btn {
102
+ color: red;
103
+ }
104
+ `);
105
+
106
+ console.log(ast);
107
+ ```
108
+
109
+ Output:
110
+
111
+ ```json
112
+ {
113
+ type: "stylesheet",
114
+ body: [
115
+ {
116
+ type: "rule",
117
+ selector: ".btn",
118
+ body: [
119
+ {
120
+ type: "declaration",
121
+ prop: "color",
122
+ value: "red"
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+ }
128
+ ```
129
+
130
+ ## 🌳 AST Overview
131
+
132
+ The AST is intentionally minimal and easy to traverse.
133
+
134
+ **Stylesheet Root**
135
+
136
+ ```
137
+ {
138
+ type: "stylesheet",
139
+ body: HoneyCssNode[]
140
+ }
141
+ ```
142
+
143
+ **Declaration Node**
144
+
145
+ ```
146
+ {
147
+ type: "declaration",
148
+ prop: "padding",
149
+ value: "12px"
150
+ }
151
+ ```
152
+
153
+ Represents:
154
+ ```
155
+ padding: 12px;
156
+ ```
157
+
158
+ **Rule Node**
159
+
160
+ ```
161
+ {
162
+ type: "rule",
163
+ selector: ".child:hover",
164
+ body: [...]
165
+ }
166
+ ```
167
+
168
+ Represents:
169
+
170
+ ```
171
+ .child:hover {
172
+ opacity: 0.5;
173
+ }
174
+ ```
175
+
176
+ **At-Rule Node**
177
+
178
+ ```
179
+ {
180
+ type: "atRule",
181
+ name: "media",
182
+ params: "(max-width: 768px)",
183
+ body: [...]
184
+ }
185
+ ```
186
+
187
+ Represents:
188
+
189
+ ```
190
+ @media (max-width: 768px) {
191
+ color: red;
192
+ }
193
+ ```
194
+
195
+ ## 🎯 Use Cases
196
+
197
+ The **honey-css** is a great fit for:
198
+
199
+ - CSS-in-JS compilers
200
+ - Custom at-rule processors
201
+ - Design system engines
202
+ - Lightweight CSS preprocessors
203
+ - AST-based transformations
204
+
205
+ It is not intended to fully replace PostCSS or implement the full CSS specification – it’s a focused foundation.
206
+
207
+ ## πŸ“„ License
208
+
209
+ MIT Β© Mike Aliinyk
210
+
211
+ Part of the **[React Hive](https://github.com/React-Hive)** ecosystem 🐝
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ (()=>{"use strict";var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{tokenizeCss:()=>r});const n=e=>"{"===e||"}"===e||":"===e||";"===e||"@"===e||"("===e||'"'===e||"'"===e||"/"===e,r=e=>{const t=[];let r=0;const o=()=>r>=e.length,i=()=>o()?void 0:e[r],u=()=>r+1>=e.length?void 0:e[r+1],f=()=>{for(;;){const e=i();if(!e||!/\s/.test(e))return;r++}},s=()=>{if("/"!==i()||"*"!==u())return!1;for(r+=2;!o();){if("*"===i()&&"/"===u())return r+=2,!0;r++}return!0},c=()=>{const e=i();if(!e)return"";r++;let t="";for(;!o();){const n=i();if(!n)break;if("\\"===n){t+=n,r++;const e=i();e&&(t+=e,r++);continue}if(n===e){r++;break}t+=n,r++}return t},p=()=>{let e=0,t="";for(;!o();){const n=i();if(!n)break;if("("===n&&e++,")"===n&&e--,t+=n,r++,0===e)break}return t},a=()=>{let e="";for(;!o();){const t=i();if(!t)break;if(n(t))break;e+=t,r++}return e.trim()};for(;!o()&&(f(),!o());){if(s())continue;const e=i();if(!e)break;if("{"===e){t.push({type:"braceOpen"}),r++;continue}if("}"===e){t.push({type:"braceClose"}),r++;continue}if(":"===e){t.push({type:"colon"}),r++;continue}if(";"===e){t.push({type:"semicolon"}),r++;continue}if("@"===e){t.push({type:"at"}),r++;continue}if("("===e){t.push({type:"params",value:p()});continue}if('"'===e||"'"===e){t.push({type:"string",value:c()});continue}const n=a();n?t.push({type:"text",value:n}):r++}return t};module.exports=t})();
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","mappings":"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","sources":["webpack://@react-hive/honey-css/webpack/bootstrap","webpack://@react-hive/honey-css/webpack/runtime/define property getters","webpack://@react-hive/honey-css/webpack/runtime/hasOwnProperty shorthand","webpack://@react-hive/honey-css/webpack/runtime/make namespace object","webpack://@react-hive/honey-css/./src/tokenize-css.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { HoneyCssToken } from '~/types';\n\n/**\n * Determines whether a character should terminate a plain text read.\n *\n * These characters represent structural boundaries in CSS:\n * - Blocks: `{` `}`\n * - Declarations: `:` `;`\n * - At-rules: `@`\n * - Params: `(`\n * - Strings: `'` `\"`\n * - Comments: `/`\n */\nconst isBoundaryChar = (ch: string): boolean =>\n ch === '{' ||\n ch === '}' ||\n ch === ':' ||\n ch === ';' ||\n ch === '@' ||\n ch === '(' ||\n ch === '\"' ||\n ch === \"'\" ||\n ch === '/';\n\n/**\n * Tokenizes a CSS-like input string into a sequence of Honey CSS tokens.\n *\n * This tokenizer is intentionally lightweight and designed for parsing\n * Honey-specific CSS extensions such as:\n *\n * - Custom at-rules: `@honey-media (...) { ... }`\n * - Nested selectors: `&:hover { ... }`\n * - CSS variables: `--primary-color: red;`\n *\n * Supported syntax:\n * - Braces: `{` `}`\n * - Colons and semicolons: `:` `;`\n * - At-rule marker: `@`\n * - Parentheses groups: `(sm:down)`\n * - Quoted strings: `\"...\"`, `'...'`\n * - Block comments: `/* ... *\\/`\n *\n * @param input - Raw CSS string to tokenize.\n *\n * @returns An ordered array of tokens.\n */\nexport const tokenizeCss = (input: string): HoneyCssToken[] => {\n const tokens: HoneyCssToken[] = [];\n\n let index = 0;\n\n const isEof = (): boolean => index >= input.length;\n\n /**\n * Returns the current character without consuming it.\n */\n const peek = (): string | undefined => (isEof() ? undefined : input[index]);\n\n /**\n * Returns the next character without consuming it.\n */\n const peekNext = (): string | undefined =>\n index + 1 >= input.length ? undefined : input[index + 1];\n\n /**\n * Advances the cursor past any whitespace characters.\n */\n const skipWhitespace = () => {\n while (true) {\n const ch = peek();\n\n if (!ch || !/\\s/.test(ch)) {\n return;\n }\n\n index++;\n }\n };\n\n /**\n * Skips CSS block comments of the form:\n *\n * ```css\n * /* comment *\\/\n * ```\n *\n * If the comment is unterminated, tokenization stops safely.\n *\n * @returns `true` if a comment was skipped.\n */\n const skipComment = (): boolean => {\n if (peek() !== '/' || peekNext() !== '*') {\n return false;\n }\n\n index += 2; // skip \"/*\"\n\n while (!isEof()) {\n if (peek() === '*' && peekNext() === '/') {\n index += 2; // skip \"*/\"\n\n return true;\n }\n\n index++;\n }\n\n return true;\n };\n\n /**\n * Reads a quoted string token.\n *\n * Supports:\n * - Double quotes: `\"text\"`\n * - Single quotes: `'text'`\n * - Escaped characters: `\"a\\\\\\\"b\"`\n *\n * @returns The unwrapped string contents.\n */\n const readString = (): string => {\n const quote = peek();\n if (!quote) {\n return '';\n }\n\n index++; // skip opening quote\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n // Handle escape sequences\n if (ch === '\\\\') {\n result += ch;\n index++;\n\n const escaped = peek();\n if (escaped) {\n result += escaped;\n index++;\n }\n\n continue;\n }\n\n // Closing quote\n if (ch === quote) {\n index++;\n break;\n }\n\n result += ch;\n index++;\n }\n\n return result;\n };\n\n /**\n * Reads a balanced parentheses group.\n *\n * Examples:\n * - `(sm:down)`\n * - `(min-width: calc(100% - 1px))`\n *\n * Nested parentheses are supported.\n *\n * @returns The full params string including parentheses.\n */\n const readParamsGroup = (): string => {\n let depth = 0;\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (ch === '(') {\n depth++;\n }\n\n if (ch === ')') {\n depth--;\n }\n\n result += ch;\n index++;\n\n if (depth === 0) {\n break;\n }\n }\n\n return result;\n };\n\n /**\n * Reads plain text until a boundary character is reached.\n *\n * This is used for:\n * - Selectors (`.btn`, `&:hover`)\n * - Property names (`color`)\n * - Values (`red`, `12px`)\n *\n * @returns Trimmed text content.\n */\n const readText = (): string => {\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (isBoundaryChar(ch)) {\n break;\n }\n\n result += ch;\n index++;\n }\n\n return result.trim();\n };\n\n // ============================\n // Main Token Loop\n // ============================\n while (!isEof()) {\n skipWhitespace();\n\n if (isEof()) {\n break;\n }\n\n if (skipComment()) {\n continue;\n }\n\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (ch === '{') {\n tokens.push({\n type: 'braceOpen',\n });\n\n index++;\n continue;\n }\n\n if (ch === '}') {\n tokens.push({\n type: 'braceClose',\n });\n\n index++;\n continue;\n }\n\n if (ch === ':') {\n tokens.push({\n type: 'colon',\n });\n\n index++;\n continue;\n }\n\n if (ch === ';') {\n tokens.push({\n type: 'semicolon',\n });\n\n index++;\n continue;\n }\n\n if (ch === '@') {\n tokens.push({\n type: 'at',\n });\n\n index++;\n continue;\n }\n\n // Params group\n if (ch === '(') {\n tokens.push({\n type: 'params',\n value: readParamsGroup(),\n });\n\n continue;\n }\n\n // Strings\n if (ch === '\"' || ch === \"'\") {\n tokens.push({\n type: 'string',\n value: readString(),\n });\n\n continue;\n }\n\n // Text chunk\n const value = readText();\n if (value) {\n tokens.push({\n type: 'text',\n value,\n });\n } else {\n // Safety fallback to prevent infinite loops\n index++;\n }\n }\n\n return tokens;\n};\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","isBoundaryChar","ch","tokenizeCss","input","tokens","index","isEof","length","peek","undefined","peekNext","skipWhitespace","test","skipComment","readString","quote","result","escaped","readParamsGroup","depth","readText","trim","push","type"],"sourceRoot":""}
@@ -0,0 +1,2 @@
1
+ export * from './types';
2
+ export * from './tokenize-css';
@@ -0,0 +1,386 @@
1
+ /******/ (() => { // webpackBootstrap
2
+ /******/ "use strict";
3
+ /******/ var __webpack_modules__ = ({
4
+
5
+ /***/ "./src/tokenize-css.ts"
6
+ /*!*****************************!*\
7
+ !*** ./src/tokenize-css.ts ***!
8
+ \*****************************/
9
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
10
+
11
+ __webpack_require__.r(__webpack_exports__);
12
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
13
+ /* harmony export */ tokenizeCss: () => (/* binding */ tokenizeCss)
14
+ /* harmony export */ });
15
+ /**
16
+ * Determines whether a character should terminate a plain text read.
17
+ *
18
+ * These characters represent structural boundaries in CSS:
19
+ * - Blocks: `{` `}`
20
+ * - Declarations: `:` `;`
21
+ * - At-rules: `@`
22
+ * - Params: `(`
23
+ * - Strings: `'` `"`
24
+ * - Comments: `/`
25
+ */
26
+ const isBoundaryChar = (ch) => ch === '{' ||
27
+ ch === '}' ||
28
+ ch === ':' ||
29
+ ch === ';' ||
30
+ ch === '@' ||
31
+ ch === '(' ||
32
+ ch === '"' ||
33
+ ch === "'" ||
34
+ ch === '/';
35
+ /**
36
+ * Tokenizes a CSS-like input string into a sequence of Honey CSS tokens.
37
+ *
38
+ * This tokenizer is intentionally lightweight and designed for parsing
39
+ * Honey-specific CSS extensions such as:
40
+ *
41
+ * - Custom at-rules: `@honey-media (...) { ... }`
42
+ * - Nested selectors: `&:hover { ... }`
43
+ * - CSS variables: `--primary-color: red;`
44
+ *
45
+ * Supported syntax:
46
+ * - Braces: `{` `}`
47
+ * - Colons and semicolons: `:` `;`
48
+ * - At-rule marker: `@`
49
+ * - Parentheses groups: `(sm:down)`
50
+ * - Quoted strings: `"..."`, `'...'`
51
+ * - Block comments: `/* ... *\/`
52
+ *
53
+ * @param input - Raw CSS string to tokenize.
54
+ *
55
+ * @returns An ordered array of tokens.
56
+ */
57
+ const tokenizeCss = (input) => {
58
+ const tokens = [];
59
+ let index = 0;
60
+ const isEof = () => index >= input.length;
61
+ /**
62
+ * Returns the current character without consuming it.
63
+ */
64
+ const peek = () => (isEof() ? undefined : input[index]);
65
+ /**
66
+ * Returns the next character without consuming it.
67
+ */
68
+ const peekNext = () => index + 1 >= input.length ? undefined : input[index + 1];
69
+ /**
70
+ * Advances the cursor past any whitespace characters.
71
+ */
72
+ const skipWhitespace = () => {
73
+ while (true) {
74
+ const ch = peek();
75
+ if (!ch || !/\s/.test(ch)) {
76
+ return;
77
+ }
78
+ index++;
79
+ }
80
+ };
81
+ /**
82
+ * Skips CSS block comments of the form:
83
+ *
84
+ * ```css
85
+ * /* comment *\/
86
+ * ```
87
+ *
88
+ * If the comment is unterminated, tokenization stops safely.
89
+ *
90
+ * @returns `true` if a comment was skipped.
91
+ */
92
+ const skipComment = () => {
93
+ if (peek() !== '/' || peekNext() !== '*') {
94
+ return false;
95
+ }
96
+ index += 2; // skip "/*"
97
+ while (!isEof()) {
98
+ if (peek() === '*' && peekNext() === '/') {
99
+ index += 2; // skip "*/"
100
+ return true;
101
+ }
102
+ index++;
103
+ }
104
+ return true;
105
+ };
106
+ /**
107
+ * Reads a quoted string token.
108
+ *
109
+ * Supports:
110
+ * - Double quotes: `"text"`
111
+ * - Single quotes: `'text'`
112
+ * - Escaped characters: `"a\\\"b"`
113
+ *
114
+ * @returns The unwrapped string contents.
115
+ */
116
+ const readString = () => {
117
+ const quote = peek();
118
+ if (!quote) {
119
+ return '';
120
+ }
121
+ index++; // skip opening quote
122
+ let result = '';
123
+ while (!isEof()) {
124
+ const ch = peek();
125
+ if (!ch) {
126
+ break;
127
+ }
128
+ // Handle escape sequences
129
+ if (ch === '\\') {
130
+ result += ch;
131
+ index++;
132
+ const escaped = peek();
133
+ if (escaped) {
134
+ result += escaped;
135
+ index++;
136
+ }
137
+ continue;
138
+ }
139
+ // Closing quote
140
+ if (ch === quote) {
141
+ index++;
142
+ break;
143
+ }
144
+ result += ch;
145
+ index++;
146
+ }
147
+ return result;
148
+ };
149
+ /**
150
+ * Reads a balanced parentheses group.
151
+ *
152
+ * Examples:
153
+ * - `(sm:down)`
154
+ * - `(min-width: calc(100% - 1px))`
155
+ *
156
+ * Nested parentheses are supported.
157
+ *
158
+ * @returns The full params string including parentheses.
159
+ */
160
+ const readParamsGroup = () => {
161
+ let depth = 0;
162
+ let result = '';
163
+ while (!isEof()) {
164
+ const ch = peek();
165
+ if (!ch) {
166
+ break;
167
+ }
168
+ if (ch === '(') {
169
+ depth++;
170
+ }
171
+ if (ch === ')') {
172
+ depth--;
173
+ }
174
+ result += ch;
175
+ index++;
176
+ if (depth === 0) {
177
+ break;
178
+ }
179
+ }
180
+ return result;
181
+ };
182
+ /**
183
+ * Reads plain text until a boundary character is reached.
184
+ *
185
+ * This is used for:
186
+ * - Selectors (`.btn`, `&:hover`)
187
+ * - Property names (`color`)
188
+ * - Values (`red`, `12px`)
189
+ *
190
+ * @returns Trimmed text content.
191
+ */
192
+ const readText = () => {
193
+ let result = '';
194
+ while (!isEof()) {
195
+ const ch = peek();
196
+ if (!ch) {
197
+ break;
198
+ }
199
+ if (isBoundaryChar(ch)) {
200
+ break;
201
+ }
202
+ result += ch;
203
+ index++;
204
+ }
205
+ return result.trim();
206
+ };
207
+ // ============================
208
+ // Main Token Loop
209
+ // ============================
210
+ while (!isEof()) {
211
+ skipWhitespace();
212
+ if (isEof()) {
213
+ break;
214
+ }
215
+ if (skipComment()) {
216
+ continue;
217
+ }
218
+ const ch = peek();
219
+ if (!ch) {
220
+ break;
221
+ }
222
+ if (ch === '{') {
223
+ tokens.push({
224
+ type: 'braceOpen',
225
+ });
226
+ index++;
227
+ continue;
228
+ }
229
+ if (ch === '}') {
230
+ tokens.push({
231
+ type: 'braceClose',
232
+ });
233
+ index++;
234
+ continue;
235
+ }
236
+ if (ch === ':') {
237
+ tokens.push({
238
+ type: 'colon',
239
+ });
240
+ index++;
241
+ continue;
242
+ }
243
+ if (ch === ';') {
244
+ tokens.push({
245
+ type: 'semicolon',
246
+ });
247
+ index++;
248
+ continue;
249
+ }
250
+ if (ch === '@') {
251
+ tokens.push({
252
+ type: 'at',
253
+ });
254
+ index++;
255
+ continue;
256
+ }
257
+ // Params group
258
+ if (ch === '(') {
259
+ tokens.push({
260
+ type: 'params',
261
+ value: readParamsGroup(),
262
+ });
263
+ continue;
264
+ }
265
+ // Strings
266
+ if (ch === '"' || ch === "'") {
267
+ tokens.push({
268
+ type: 'string',
269
+ value: readString(),
270
+ });
271
+ continue;
272
+ }
273
+ // Text chunk
274
+ const value = readText();
275
+ if (value) {
276
+ tokens.push({
277
+ type: 'text',
278
+ value,
279
+ });
280
+ }
281
+ else {
282
+ // Safety fallback to prevent infinite loops
283
+ index++;
284
+ }
285
+ }
286
+ return tokens;
287
+ };
288
+
289
+
290
+ /***/ },
291
+
292
+ /***/ "./src/types.ts"
293
+ /*!**********************!*\
294
+ !*** ./src/types.ts ***!
295
+ \**********************/
296
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
297
+
298
+ __webpack_require__.r(__webpack_exports__);
299
+
300
+
301
+
302
+ /***/ }
303
+
304
+ /******/ });
305
+ /************************************************************************/
306
+ /******/ // The module cache
307
+ /******/ var __webpack_module_cache__ = {};
308
+ /******/
309
+ /******/ // The require function
310
+ /******/ function __webpack_require__(moduleId) {
311
+ /******/ // Check if module is in cache
312
+ /******/ var cachedModule = __webpack_module_cache__[moduleId];
313
+ /******/ if (cachedModule !== undefined) {
314
+ /******/ return cachedModule.exports;
315
+ /******/ }
316
+ /******/ // Check if module exists (development only)
317
+ /******/ if (__webpack_modules__[moduleId] === undefined) {
318
+ /******/ var e = new Error("Cannot find module '" + moduleId + "'");
319
+ /******/ e.code = 'MODULE_NOT_FOUND';
320
+ /******/ throw e;
321
+ /******/ }
322
+ /******/ // Create a new module (and put it into the cache)
323
+ /******/ var module = __webpack_module_cache__[moduleId] = {
324
+ /******/ // no module.id needed
325
+ /******/ // no module.loaded needed
326
+ /******/ exports: {}
327
+ /******/ };
328
+ /******/
329
+ /******/ // Execute the module function
330
+ /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
331
+ /******/
332
+ /******/ // Return the exports of the module
333
+ /******/ return module.exports;
334
+ /******/ }
335
+ /******/
336
+ /************************************************************************/
337
+ /******/ /* webpack/runtime/define property getters */
338
+ /******/ (() => {
339
+ /******/ // define getter functions for harmony exports
340
+ /******/ __webpack_require__.d = (exports, definition) => {
341
+ /******/ for(var key in definition) {
342
+ /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
343
+ /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
344
+ /******/ }
345
+ /******/ }
346
+ /******/ };
347
+ /******/ })();
348
+ /******/
349
+ /******/ /* webpack/runtime/hasOwnProperty shorthand */
350
+ /******/ (() => {
351
+ /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
352
+ /******/ })();
353
+ /******/
354
+ /******/ /* webpack/runtime/make namespace object */
355
+ /******/ (() => {
356
+ /******/ // define __esModule on exports
357
+ /******/ __webpack_require__.r = (exports) => {
358
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
359
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
360
+ /******/ }
361
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
362
+ /******/ };
363
+ /******/ })();
364
+ /******/
365
+ /************************************************************************/
366
+ var __webpack_exports__ = {};
367
+ // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
368
+ (() => {
369
+ /*!**********************!*\
370
+ !*** ./src/index.ts ***!
371
+ \**********************/
372
+ __webpack_require__.r(__webpack_exports__);
373
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
374
+ /* harmony export */ tokenizeCss: () => (/* reexport safe */ _tokenize_css__WEBPACK_IMPORTED_MODULE_1__.tokenizeCss)
375
+ /* harmony export */ });
376
+ /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./types */ "./src/types.ts");
377
+ /* harmony import */ var _tokenize_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./tokenize-css */ "./src/tokenize-css.ts");
378
+
379
+
380
+
381
+ })();
382
+
383
+ module.exports = __webpack_exports__;
384
+ /******/ })()
385
+ ;
386
+ //# sourceMappingURL=index.dev.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.dev.cjs","mappings":"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","sources":["webpack://@react-hive/honey-css/./src/tokenize-css.ts","webpack://@react-hive/honey-css/./src/types.ts","webpack://@react-hive/honey-css/webpack/bootstrap","webpack://@react-hive/honey-css/webpack/runtime/define property getters","webpack://@react-hive/honey-css/webpack/runtime/hasOwnProperty shorthand","webpack://@react-hive/honey-css/webpack/runtime/make namespace object","webpack://@react-hive/honey-css/./src/index.ts"],"sourcesContent":["import { HoneyCssToken } from '~/types';\n\n/**\n * Determines whether a character should terminate a plain text read.\n *\n * These characters represent structural boundaries in CSS:\n * - Blocks: `{` `}`\n * - Declarations: `:` `;`\n * - At-rules: `@`\n * - Params: `(`\n * - Strings: `'` `\"`\n * - Comments: `/`\n */\nconst isBoundaryChar = (ch: string): boolean =>\n ch === '{' ||\n ch === '}' ||\n ch === ':' ||\n ch === ';' ||\n ch === '@' ||\n ch === '(' ||\n ch === '\"' ||\n ch === \"'\" ||\n ch === '/';\n\n/**\n * Tokenizes a CSS-like input string into a sequence of Honey CSS tokens.\n *\n * This tokenizer is intentionally lightweight and designed for parsing\n * Honey-specific CSS extensions such as:\n *\n * - Custom at-rules: `@honey-media (...) { ... }`\n * - Nested selectors: `&:hover { ... }`\n * - CSS variables: `--primary-color: red;`\n *\n * Supported syntax:\n * - Braces: `{` `}`\n * - Colons and semicolons: `:` `;`\n * - At-rule marker: `@`\n * - Parentheses groups: `(sm:down)`\n * - Quoted strings: `\"...\"`, `'...'`\n * - Block comments: `/* ... *\\/`\n *\n * @param input - Raw CSS string to tokenize.\n *\n * @returns An ordered array of tokens.\n */\nexport const tokenizeCss = (input: string): HoneyCssToken[] => {\n const tokens: HoneyCssToken[] = [];\n\n let index = 0;\n\n const isEof = (): boolean => index >= input.length;\n\n /**\n * Returns the current character without consuming it.\n */\n const peek = (): string | undefined => (isEof() ? undefined : input[index]);\n\n /**\n * Returns the next character without consuming it.\n */\n const peekNext = (): string | undefined =>\n index + 1 >= input.length ? undefined : input[index + 1];\n\n /**\n * Advances the cursor past any whitespace characters.\n */\n const skipWhitespace = () => {\n while (true) {\n const ch = peek();\n\n if (!ch || !/\\s/.test(ch)) {\n return;\n }\n\n index++;\n }\n };\n\n /**\n * Skips CSS block comments of the form:\n *\n * ```css\n * /* comment *\\/\n * ```\n *\n * If the comment is unterminated, tokenization stops safely.\n *\n * @returns `true` if a comment was skipped.\n */\n const skipComment = (): boolean => {\n if (peek() !== '/' || peekNext() !== '*') {\n return false;\n }\n\n index += 2; // skip \"/*\"\n\n while (!isEof()) {\n if (peek() === '*' && peekNext() === '/') {\n index += 2; // skip \"*/\"\n\n return true;\n }\n\n index++;\n }\n\n return true;\n };\n\n /**\n * Reads a quoted string token.\n *\n * Supports:\n * - Double quotes: `\"text\"`\n * - Single quotes: `'text'`\n * - Escaped characters: `\"a\\\\\\\"b\"`\n *\n * @returns The unwrapped string contents.\n */\n const readString = (): string => {\n const quote = peek();\n if (!quote) {\n return '';\n }\n\n index++; // skip opening quote\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n // Handle escape sequences\n if (ch === '\\\\') {\n result += ch;\n index++;\n\n const escaped = peek();\n if (escaped) {\n result += escaped;\n index++;\n }\n\n continue;\n }\n\n // Closing quote\n if (ch === quote) {\n index++;\n break;\n }\n\n result += ch;\n index++;\n }\n\n return result;\n };\n\n /**\n * Reads a balanced parentheses group.\n *\n * Examples:\n * - `(sm:down)`\n * - `(min-width: calc(100% - 1px))`\n *\n * Nested parentheses are supported.\n *\n * @returns The full params string including parentheses.\n */\n const readParamsGroup = (): string => {\n let depth = 0;\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (ch === '(') {\n depth++;\n }\n\n if (ch === ')') {\n depth--;\n }\n\n result += ch;\n index++;\n\n if (depth === 0) {\n break;\n }\n }\n\n return result;\n };\n\n /**\n * Reads plain text until a boundary character is reached.\n *\n * This is used for:\n * - Selectors (`.btn`, `&:hover`)\n * - Property names (`color`)\n * - Values (`red`, `12px`)\n *\n * @returns Trimmed text content.\n */\n const readText = (): string => {\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (isBoundaryChar(ch)) {\n break;\n }\n\n result += ch;\n index++;\n }\n\n return result.trim();\n };\n\n // ============================\n // Main Token Loop\n // ============================\n while (!isEof()) {\n skipWhitespace();\n\n if (isEof()) {\n break;\n }\n\n if (skipComment()) {\n continue;\n }\n\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (ch === '{') {\n tokens.push({\n type: 'braceOpen',\n });\n\n index++;\n continue;\n }\n\n if (ch === '}') {\n tokens.push({\n type: 'braceClose',\n });\n\n index++;\n continue;\n }\n\n if (ch === ':') {\n tokens.push({\n type: 'colon',\n });\n\n index++;\n continue;\n }\n\n if (ch === ';') {\n tokens.push({\n type: 'semicolon',\n });\n\n index++;\n continue;\n }\n\n if (ch === '@') {\n tokens.push({\n type: 'at',\n });\n\n index++;\n continue;\n }\n\n // Params group\n if (ch === '(') {\n tokens.push({\n type: 'params',\n value: readParamsGroup(),\n });\n\n continue;\n }\n\n // Strings\n if (ch === '\"' || ch === \"'\") {\n tokens.push({\n type: 'string',\n value: readString(),\n });\n\n continue;\n }\n\n // Text chunk\n const value = readText();\n if (value) {\n tokens.push({\n type: 'text',\n value,\n });\n } else {\n // Safety fallback to prevent infinite loops\n index++;\n }\n }\n\n return tokens;\n};\n","/**\n * All supported token types produced by the Honey CSS tokenizer.\n *\n * These tokens represent the minimal syntax units needed for parsing:\n * - Structural braces (`{` / `}`)\n * - Punctuation (`:` / `;`)\n * - At-rules (`@`)\n * - Parenthesized groups (`(...)`)\n * - Strings (`\"...\"`, `'...'`)\n * - Plain text chunks (selectors, property names, values)\n */\nexport type HoneyCssTokenType =\n | 'braceOpen'\n | 'braceClose'\n | 'colon'\n | 'semicolon'\n | 'at'\n | 'params'\n | 'string'\n | 'text';\n\n/**\n * A single token produced by {@link tokenizeCss}.\n *\n * Some token types (like `text`, `params`, `string`) include a `value`,\n * while punctuation tokens do not.\n */\nexport type HoneyCssToken = {\n type: HoneyCssTokenType;\n value?: string;\n};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Check if module exists (development only)\n\tif (__webpack_modules__[moduleId] === undefined) {\n\t\tvar e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export * from './types'\nexport * from './tokenize-css'"],"names":[],"sourceRoot":""}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ const t=t=>"{"===t||"}"===t||":"===t||";"===t||"@"===t||"("===t||'"'===t||"'"===t||"/"===t,e=e=>{const n=[];let r=0;const i=()=>r>=e.length,o=()=>i()?void 0:e[r],u=()=>r+1>=e.length?void 0:e[r+1],f=()=>{for(;;){const t=o();if(!t||!/\s/.test(t))return;r++}},s=()=>{if("/"!==o()||"*"!==u())return!1;for(r+=2;!i();){if("*"===o()&&"/"===u())return r+=2,!0;r++}return!0},c=()=>{const t=o();if(!t)return"";r++;let e="";for(;!i();){const n=o();if(!n)break;if("\\"===n){e+=n,r++;const t=o();t&&(e+=t,r++);continue}if(n===t){r++;break}e+=n,r++}return e},p=()=>{let t=0,e="";for(;!i();){const n=o();if(!n)break;if("("===n&&t++,")"===n&&t--,e+=n,r++,0===t)break}return e},a=()=>{let e="";for(;!i();){const n=o();if(!n)break;if(t(n))break;e+=n,r++}return e.trim()};for(;!i()&&(f(),!i());){if(s())continue;const t=o();if(!t)break;if("{"===t){n.push({type:"braceOpen"}),r++;continue}if("}"===t){n.push({type:"braceClose"}),r++;continue}if(":"===t){n.push({type:"colon"}),r++;continue}if(";"===t){n.push({type:"semicolon"}),r++;continue}if("@"===t){n.push({type:"at"}),r++;continue}if("("===t){n.push({type:"params",value:p()});continue}if('"'===t||"'"===t){n.push({type:"string",value:c()});continue}const e=a();e?n.push({type:"text",value:e}):r++}return n};export{e as tokenizeCss};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","mappings":"AAaA,MAAMA,EAAkBC,GACf,MAAPA,GACO,MAAPA,GACO,MAAPA,GACO,MAAPA,GACO,MAAPA,GACO,MAAPA,GACO,MAAPA,GACO,MAAPA,GACO,MAAPA,EAwBWC,EAAeC,IAC1B,MAAMC,EAA0B,GAEhC,IAAIC,EAAQ,EAEZ,MAAMC,EAAQ,IAAeD,GAASF,EAAMI,OAKtCC,EAAO,IAA2BF,SAAUG,EAAYN,EAAME,GAK9DK,EAAW,IACfL,EAAQ,GAAKF,EAAMI,YAASE,EAAYN,EAAME,EAAQ,GAKlDM,EAAiB,KACrB,OAAa,CACX,MAAMV,EAAKO,IAEX,IAAKP,IAAO,KAAKW,KAAKX,GACpB,OAGFI,GACF,GAcIQ,EAAc,KAClB,GAAe,MAAXL,KAAiC,MAAfE,IACpB,OAAO,EAKT,IAFAL,GAAS,GAEDC,KAAS,CACf,GAAe,MAAXE,KAAiC,MAAfE,IAGpB,OAFAL,GAAS,GAEF,EAGTA,GACF,CAEA,OAAO,GAaHS,EAAa,KACjB,MAAMC,EAAQP,IACd,IAAKO,EACH,MAAO,GAGTV,IACA,IAAIW,EAAS,GAEb,MAAQV,KAAS,CACf,MAAML,EAAKO,IACX,IAAKP,EACH,MAIF,GAAW,OAAPA,EAAa,CACfe,GAAUf,EACVI,IAEA,MAAMY,EAAUT,IACZS,IACFD,GAAUC,EACVZ,KAGF,QACF,CAGA,GAAIJ,IAAOc,EAAO,CAChBV,IACA,KACF,CAEAW,GAAUf,EACVI,GACF,CAEA,OAAOW,GAcHE,EAAkB,KACtB,IAAIC,EAAQ,EACRH,EAAS,GAEb,MAAQV,KAAS,CACf,MAAML,EAAKO,IACX,IAAKP,EACH,MAcF,GAXW,MAAPA,GACFkB,IAGS,MAAPlB,GACFkB,IAGFH,GAAUf,EACVI,IAEc,IAAVc,EACF,KAEJ,CAEA,OAAOH,GAaHI,EAAW,KACf,IAAIJ,EAAS,GAEb,MAAQV,KAAS,CACf,MAAML,EAAKO,IACX,IAAKP,EACH,MAGF,GAAID,EAAeC,GACjB,MAGFe,GAAUf,EACVI,GACF,CAEA,OAAOW,EAAOK,QAMhB,MAAQf,MACNK,KAEIL,MAHW,CAOf,GAAIO,IACF,SAGF,MAAMZ,EAAKO,IACX,IAAKP,EACH,MAGF,GAAW,MAAPA,EAAY,CACdG,EAAOkB,KAAK,CACVC,KAAM,cAGRlB,IACA,QACF,CAEA,GAAW,MAAPJ,EAAY,CACdG,EAAOkB,KAAK,CACVC,KAAM,eAGRlB,IACA,QACF,CAEA,GAAW,MAAPJ,EAAY,CACdG,EAAOkB,KAAK,CACVC,KAAM,UAGRlB,IACA,QACF,CAEA,GAAW,MAAPJ,EAAY,CACdG,EAAOkB,KAAK,CACVC,KAAM,cAGRlB,IACA,QACF,CAEA,GAAW,MAAPJ,EAAY,CACdG,EAAOkB,KAAK,CACVC,KAAM,OAGRlB,IACA,QACF,CAGA,GAAW,MAAPJ,EAAY,CACdG,EAAOkB,KAAK,CACVC,KAAM,SACNC,MAAON,MAGT,QACF,CAGA,GAAW,MAAPjB,GAAqB,MAAPA,EAAY,CAC5BG,EAAOkB,KAAK,CACVC,KAAM,SACNC,MAAOV,MAGT,QACF,CAGA,MAAMU,EAAQJ,IACVI,EACFpB,EAAOkB,KAAK,CACVC,KAAM,OACNC,UAIFnB,GAEJ,CAEA,OAAOD,U","sources":["webpack://@react-hive/honey-css/./src/tokenize-css.ts"],"sourcesContent":["import { HoneyCssToken } from '~/types';\n\n/**\n * Determines whether a character should terminate a plain text read.\n *\n * These characters represent structural boundaries in CSS:\n * - Blocks: `{` `}`\n * - Declarations: `:` `;`\n * - At-rules: `@`\n * - Params: `(`\n * - Strings: `'` `\"`\n * - Comments: `/`\n */\nconst isBoundaryChar = (ch: string): boolean =>\n ch === '{' ||\n ch === '}' ||\n ch === ':' ||\n ch === ';' ||\n ch === '@' ||\n ch === '(' ||\n ch === '\"' ||\n ch === \"'\" ||\n ch === '/';\n\n/**\n * Tokenizes a CSS-like input string into a sequence of Honey CSS tokens.\n *\n * This tokenizer is intentionally lightweight and designed for parsing\n * Honey-specific CSS extensions such as:\n *\n * - Custom at-rules: `@honey-media (...) { ... }`\n * - Nested selectors: `&:hover { ... }`\n * - CSS variables: `--primary-color: red;`\n *\n * Supported syntax:\n * - Braces: `{` `}`\n * - Colons and semicolons: `:` `;`\n * - At-rule marker: `@`\n * - Parentheses groups: `(sm:down)`\n * - Quoted strings: `\"...\"`, `'...'`\n * - Block comments: `/* ... *\\/`\n *\n * @param input - Raw CSS string to tokenize.\n *\n * @returns An ordered array of tokens.\n */\nexport const tokenizeCss = (input: string): HoneyCssToken[] => {\n const tokens: HoneyCssToken[] = [];\n\n let index = 0;\n\n const isEof = (): boolean => index >= input.length;\n\n /**\n * Returns the current character without consuming it.\n */\n const peek = (): string | undefined => (isEof() ? undefined : input[index]);\n\n /**\n * Returns the next character without consuming it.\n */\n const peekNext = (): string | undefined =>\n index + 1 >= input.length ? undefined : input[index + 1];\n\n /**\n * Advances the cursor past any whitespace characters.\n */\n const skipWhitespace = () => {\n while (true) {\n const ch = peek();\n\n if (!ch || !/\\s/.test(ch)) {\n return;\n }\n\n index++;\n }\n };\n\n /**\n * Skips CSS block comments of the form:\n *\n * ```css\n * /* comment *\\/\n * ```\n *\n * If the comment is unterminated, tokenization stops safely.\n *\n * @returns `true` if a comment was skipped.\n */\n const skipComment = (): boolean => {\n if (peek() !== '/' || peekNext() !== '*') {\n return false;\n }\n\n index += 2; // skip \"/*\"\n\n while (!isEof()) {\n if (peek() === '*' && peekNext() === '/') {\n index += 2; // skip \"*/\"\n\n return true;\n }\n\n index++;\n }\n\n return true;\n };\n\n /**\n * Reads a quoted string token.\n *\n * Supports:\n * - Double quotes: `\"text\"`\n * - Single quotes: `'text'`\n * - Escaped characters: `\"a\\\\\\\"b\"`\n *\n * @returns The unwrapped string contents.\n */\n const readString = (): string => {\n const quote = peek();\n if (!quote) {\n return '';\n }\n\n index++; // skip opening quote\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n // Handle escape sequences\n if (ch === '\\\\') {\n result += ch;\n index++;\n\n const escaped = peek();\n if (escaped) {\n result += escaped;\n index++;\n }\n\n continue;\n }\n\n // Closing quote\n if (ch === quote) {\n index++;\n break;\n }\n\n result += ch;\n index++;\n }\n\n return result;\n };\n\n /**\n * Reads a balanced parentheses group.\n *\n * Examples:\n * - `(sm:down)`\n * - `(min-width: calc(100% - 1px))`\n *\n * Nested parentheses are supported.\n *\n * @returns The full params string including parentheses.\n */\n const readParamsGroup = (): string => {\n let depth = 0;\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (ch === '(') {\n depth++;\n }\n\n if (ch === ')') {\n depth--;\n }\n\n result += ch;\n index++;\n\n if (depth === 0) {\n break;\n }\n }\n\n return result;\n };\n\n /**\n * Reads plain text until a boundary character is reached.\n *\n * This is used for:\n * - Selectors (`.btn`, `&:hover`)\n * - Property names (`color`)\n * - Values (`red`, `12px`)\n *\n * @returns Trimmed text content.\n */\n const readText = (): string => {\n let result = '';\n\n while (!isEof()) {\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (isBoundaryChar(ch)) {\n break;\n }\n\n result += ch;\n index++;\n }\n\n return result.trim();\n };\n\n // ============================\n // Main Token Loop\n // ============================\n while (!isEof()) {\n skipWhitespace();\n\n if (isEof()) {\n break;\n }\n\n if (skipComment()) {\n continue;\n }\n\n const ch = peek();\n if (!ch) {\n break;\n }\n\n if (ch === '{') {\n tokens.push({\n type: 'braceOpen',\n });\n\n index++;\n continue;\n }\n\n if (ch === '}') {\n tokens.push({\n type: 'braceClose',\n });\n\n index++;\n continue;\n }\n\n if (ch === ':') {\n tokens.push({\n type: 'colon',\n });\n\n index++;\n continue;\n }\n\n if (ch === ';') {\n tokens.push({\n type: 'semicolon',\n });\n\n index++;\n continue;\n }\n\n if (ch === '@') {\n tokens.push({\n type: 'at',\n });\n\n index++;\n continue;\n }\n\n // Params group\n if (ch === '(') {\n tokens.push({\n type: 'params',\n value: readParamsGroup(),\n });\n\n continue;\n }\n\n // Strings\n if (ch === '\"' || ch === \"'\") {\n tokens.push({\n type: 'string',\n value: readString(),\n });\n\n continue;\n }\n\n // Text chunk\n const value = readText();\n if (value) {\n tokens.push({\n type: 'text',\n value,\n });\n } else {\n // Safety fallback to prevent infinite loops\n index++;\n }\n }\n\n return tokens;\n};\n"],"names":["isBoundaryChar","ch","tokenizeCss","input","tokens","index","isEof","length","peek","undefined","peekNext","skipWhitespace","test","skipComment","readString","quote","result","escaped","readParamsGroup","depth","readText","trim","push","type","value"],"sourceRoot":""}
@@ -0,0 +1,24 @@
1
+ import { HoneyCssToken } from '~/types';
2
+ /**
3
+ * Tokenizes a CSS-like input string into a sequence of Honey CSS tokens.
4
+ *
5
+ * This tokenizer is intentionally lightweight and designed for parsing
6
+ * Honey-specific CSS extensions such as:
7
+ *
8
+ * - Custom at-rules: `@honey-media (...) { ... }`
9
+ * - Nested selectors: `&:hover { ... }`
10
+ * - CSS variables: `--primary-color: red;`
11
+ *
12
+ * Supported syntax:
13
+ * - Braces: `{` `}`
14
+ * - Colons and semicolons: `:` `;`
15
+ * - At-rule marker: `@`
16
+ * - Parentheses groups: `(sm:down)`
17
+ * - Quoted strings: `"..."`, `'...'`
18
+ * - Block comments: `/* ... *\/`
19
+ *
20
+ * @param input - Raw CSS string to tokenize.
21
+ *
22
+ * @returns An ordered array of tokens.
23
+ */
24
+ export declare const tokenizeCss: (input: string) => HoneyCssToken[];
@@ -0,0 +1,22 @@
1
+ /**
2
+ * All supported token types produced by the Honey CSS tokenizer.
3
+ *
4
+ * These tokens represent the minimal syntax units needed for parsing:
5
+ * - Structural braces (`{` / `}`)
6
+ * - Punctuation (`:` / `;`)
7
+ * - At-rules (`@`)
8
+ * - Parenthesized groups (`(...)`)
9
+ * - Strings (`"..."`, `'...'`)
10
+ * - Plain text chunks (selectors, property names, values)
11
+ */
12
+ export type HoneyCssTokenType = 'braceOpen' | 'braceClose' | 'colon' | 'semicolon' | 'at' | 'params' | 'string' | 'text';
13
+ /**
14
+ * A single token produced by {@link tokenizeCss}.
15
+ *
16
+ * Some token types (like `text`, `params`, `string`) include a `value`,
17
+ * while punctuation tokens do not.
18
+ */
19
+ export type HoneyCssToken = {
20
+ type: HoneyCssTokenType;
21
+ value?: string;
22
+ };
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@react-hive/honey-css",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight CSS tokenizer and parser that produces a simple AST for custom CSS processing.",
5
+ "keywords": [
6
+ "css",
7
+ "parser",
8
+ "tokenizer",
9
+ "ast",
10
+ "css-ast",
11
+ "css-in-js",
12
+ "compiler",
13
+ "transformer",
14
+ "react-hive",
15
+ "honey-style"
16
+ ],
17
+ "homepage": "https://github.com/React-Hive/honey-css",
18
+ "bugs": {
19
+ "url": "https://github.com/React-Hive/honey-css/issues"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/React-Hive/honey-css.git"
24
+ },
25
+ "author": "Mike Aliinyk <m.aliynik@gmail.com>",
26
+ "license": "MIT",
27
+ "type": "module",
28
+ "main": "./dist/index.cjs",
29
+ "module": "./dist/index.mjs",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.mjs",
35
+ "require": "./dist/index.cjs"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "!dist/**/__mocks__",
41
+ "!dist/**/__tests__",
42
+ "!dist/**/jest*"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/js": "9.39.1",
52
+ "@mdx-js/loader": "3.1.1",
53
+ "@types/jest": "29.5.14",
54
+ "@types/node": "22.19.1",
55
+ "copy-webpack-plugin": "13.0.1",
56
+ "eslint": "9.39.1",
57
+ "eslint-plugin-react": "7.37.5",
58
+ "jest": "29.7.0",
59
+ "prettier": "3.6.2",
60
+ "ts-jest": "29.4.4",
61
+ "ts-loader": "9.5.4",
62
+ "typescript": "5.9.3",
63
+ "typescript-eslint": "8.47.0",
64
+ "webpack": "5.105.2",
65
+ "webpack-cli": "6.0.1"
66
+ },
67
+ "jest": {
68
+ "preset": "ts-jest",
69
+ "testMatch": [
70
+ "**/src/**/*.spec.ts"
71
+ ],
72
+ "moduleNameMapper": {
73
+ "^~/(.*)$": "<rootDir>/src/$1"
74
+ }
75
+ },
76
+ "scripts": {
77
+ "clean": "rm -rf dist coverage",
78
+ "build": "webpack --config webpack.config.mjs",
79
+ "test": "jest --coverage"
80
+ }
81
+ }