typewritingclass-compiler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # typewritingclass-compiler
2
+
3
+ Rust-powered compiler and Vite plugin for Typewriting Class. Statically analyzes TypeScript source files, extracts CSS at build time, and replaces utility calls with generated class names — producing zero-runtime CSS output.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add -d typewritingclass-compiler
9
+ ```
10
+
11
+ ## Vite Plugin
12
+
13
+ ```ts
14
+ // vite.config.ts
15
+ import { defineConfig } from 'vite'
16
+ import twcPlugin from 'typewritingclass-compiler'
17
+
18
+ export default defineConfig({
19
+ plugins: [twcPlugin()],
20
+ })
21
+ ```
22
+
23
+ Then import the virtual CSS module:
24
+
25
+ ```ts
26
+ import 'virtual:twc.css'
27
+ ```
28
+
29
+ ## Options
30
+
31
+ ```ts
32
+ twcPlugin({
33
+ strict: true, // Error on dynamic values not wrapped with dynamic() (default: true)
34
+ theme: { // Custom theme input
35
+ colors: { brand: { 500: '#6366f1' } },
36
+ },
37
+ })
38
+ ```
39
+
40
+ ## How it works
41
+
42
+ 1. **Static analysis** — the Rust extractor scans TS/JS files for `cx()`, `when()`, and utility calls
43
+ 2. **CSS generation** — extracted rules are compiled into deterministic class names and CSS declarations
44
+ 3. **Code transform** — utility call sites are replaced with the generated class name strings
45
+ 4. **Virtual module** — `virtual:twc.css` aggregates all extracted CSS, respecting `@layer` ordering
46
+ 5. **Dynamic fallback** — values that can't be statically resolved fall back to runtime CSS custom properties
47
+
48
+ ## Strict mode
49
+
50
+ Strict mode (default) requires dynamic values to be explicitly wrapped with `dynamic()`. This makes the boundary between static and runtime CSS explicit. Set `strict: false` to allow implicit dynamic values.
51
+
52
+ ## Exports
53
+
54
+ | Export | Description |
55
+ |---|---|
56
+ | `default` | Vite plugin factory |
57
+ | `nativeTransform` | Core transform function (Rust via NAPI) |
58
+ | `generateCss` | CSS aggregation utility |
59
+ | `ThemeInput` | Theme configuration type |
60
+ | `TransformOutput` | Transform result type |
61
+ | `ExtractedRule` | Individual extracted rule type |
62
+ | `Diagnostic` | Compiler diagnostic type |
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "typewritingclass-compiler",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./src/index.ts",
8
+ "default": "./src/index.ts"
9
+ }
10
+ },
11
+ "files": [
12
+ "src"
13
+ ],
14
+ "napi": {
15
+ "name": "typewritingclass-compiler-native",
16
+ "triples": {}
17
+ },
18
+ "peerDependencies": {
19
+ "vite": "^6.0.0"
20
+ },
21
+ "dependencies": {
22
+ "magic-string": "^0.30.21",
23
+ "typewritingclass": "0.2.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^25.2.2",
27
+ "typescript": "^5.7.3",
28
+ "vite": "^6.0.0"
29
+ },
30
+ "scripts": {
31
+ "build:native": "napi build --release",
32
+ "build:native:debug": "napi build"
33
+ }
34
+ }
package/src/css.rs ADDED
@@ -0,0 +1,32 @@
1
+ use crate::style_rule::StyleRule;
2
+
3
+ /// Render a single rule to CSS text (without @layer wrapper)
4
+ pub fn render_rule(class_name: &str, rule: &StyleRule) -> String {
5
+ let decls: String = rule
6
+ .declarations
7
+ .iter()
8
+ .map(|(prop, val)| format!(" {}: {};", prop, val))
9
+ .collect::<Vec<_>>()
10
+ .join("\n");
11
+
12
+ let mut selector = format!(".{}", class_name);
13
+ for s in &rule.selectors {
14
+ selector.push_str(s);
15
+ }
16
+
17
+ let mut css = format!("{} {{\n{}\n}}", selector, decls);
18
+
19
+ for mq in &rule.media_queries {
20
+ css = format!("@media {} {{\n{}\n}}", mq, css);
21
+ }
22
+
23
+ css
24
+ }
25
+
26
+ /// Join multiple CSS rules into a single stylesheet.
27
+ pub fn wrap_in_layer(rules_css: &[String]) -> String {
28
+ if rules_css.is_empty() {
29
+ return String::new();
30
+ }
31
+ rules_css.join("\n\n")
32
+ }