tw-framework 0.2.3__tar.gz

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.
Files changed (57) hide show
  1. tw_framework-0.2.3/PKG-INFO +8 -0
  2. tw_framework-0.2.3/README.md +91 -0
  3. tw_framework-0.2.3/pyproject.toml +25 -0
  4. tw_framework-0.2.3/setup.cfg +4 -0
  5. tw_framework-0.2.3/tests/test_deployment.py +106 -0
  6. tw_framework-0.2.3/tw_framework/__init__.py +13 -0
  7. tw_framework-0.2.3/tw_framework/advanced_diagnostics.py +25 -0
  8. tw_framework-0.2.3/tw_framework/asset_optimizer.py +30 -0
  9. tw_framework-0.2.3/tw_framework/ast_nodes.py +283 -0
  10. tw_framework-0.2.3/tw_framework/build.py +7 -0
  11. tw_framework-0.2.3/tw_framework/build_performance.py +26 -0
  12. tw_framework-0.2.3/tw_framework/build_report.py +84 -0
  13. tw_framework-0.2.3/tw_framework/cli.py +1258 -0
  14. tw_framework-0.2.3/tw_framework/code_splitting.py +76 -0
  15. tw_framework-0.2.3/tw_framework/common.py +22 -0
  16. tw_framework-0.2.3/tw_framework/compiler.py +4817 -0
  17. tw_framework-0.2.3/tw_framework/compiler_stats.py +51 -0
  18. tw_framework-0.2.3/tw_framework/dead_code.py +44 -0
  19. tw_framework-0.2.3/tw_framework/dependency_graph.py +91 -0
  20. tw_framework-0.2.3/tw_framework/deploy.py +7 -0
  21. tw_framework-0.2.3/tw_framework/dev.py +9 -0
  22. tw_framework-0.2.3/tw_framework/diagnostics.py +98 -0
  23. tw_framework-0.2.3/tw_framework/dynamic_imports.py +51 -0
  24. tw_framework-0.2.3/tw_framework/error_formatter.py +53 -0
  25. tw_framework-0.2.3/tw_framework/framework.py +3216 -0
  26. tw_framework-0.2.3/tw_framework/hydration.py +129 -0
  27. tw_framework-0.2.3/tw_framework/incremental_cache.py +53 -0
  28. tw_framework-0.2.3/tw_framework/interpreter.py +42 -0
  29. tw_framework-0.2.3/tw_framework/ir.py +171 -0
  30. tw_framework-0.2.3/tw_framework/lexer.py +52 -0
  31. tw_framework-0.2.3/tw_framework/lowering.py +68 -0
  32. tw_framework-0.2.3/tw_framework/parser.py +167 -0
  33. tw_framework-0.2.3/tw_framework/partial_rebuild.py +24 -0
  34. tw_framework-0.2.3/tw_framework/performance_analyzer.py +37 -0
  35. tw_framework-0.2.3/tw_framework/plugin_runtime.py +196 -0
  36. tw_framework-0.2.3/tw_framework/production_optimizer.py +82 -0
  37. tw_framework-0.2.3/tw_framework/reactivity.py +349 -0
  38. tw_framework-0.2.3/tw_framework/render_css.py +22 -0
  39. tw_framework-0.2.3/tw_framework/render_html.py +259 -0
  40. tw_framework-0.2.3/tw_framework/route_optimizer.py +40 -0
  41. tw_framework-0.2.3/tw_framework/router.py +7 -0
  42. tw_framework-0.2.3/tw_framework/runtime_model.py +23 -0
  43. tw_framework-0.2.3/tw_framework/runtime_values.py +31 -0
  44. tw_framework-0.2.3/tw_framework/semantic.py +162 -0
  45. tw_framework-0.2.3/tw_framework/server.py +532 -0
  46. tw_framework-0.2.3/tw_framework/signature.py +51 -0
  47. tw_framework-0.2.3/tw_framework/static_dynamic_auto.py +32 -0
  48. tw_framework-0.2.3/tw_framework/streaming.py +135 -0
  49. tw_framework-0.2.3/tw_framework/test_build_cli_pytest.py +1078 -0
  50. tw_framework-0.2.3/tw_framework/tree_shaking.py +48 -0
  51. tw_framework-0.2.3/tw_framework/twm_parser.py +274 -0
  52. tw_framework-0.2.3/tw_framework.egg-info/PKG-INFO +8 -0
  53. tw_framework-0.2.3/tw_framework.egg-info/SOURCES.txt +55 -0
  54. tw_framework-0.2.3/tw_framework.egg-info/dependency_links.txt +1 -0
  55. tw_framework-0.2.3/tw_framework.egg-info/entry_points.txt +2 -0
  56. tw_framework-0.2.3/tw_framework.egg-info/requires.txt +3 -0
  57. tw_framework-0.2.3/tw_framework.egg-info/top_level.txt +1 -0
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: tw-framework
3
+ Version: 0.2.3
4
+ Summary: TW framework CLI with create, dev, build, and deploy commands
5
+ Author: TW Framework
6
+ Requires-Python: >=3.9
7
+ Provides-Extra: dev
8
+ Requires-Dist: pytest>=7; extra == "dev"
@@ -0,0 +1,91 @@
1
+ # TW Framework
2
+
3
+ TW is a custom language + framework for building websites with a Next.js‑like developer experience.
4
+
5
+ ## Zero‑Config Deployment
6
+
7
+ 1. Create a GitHub repository with your TW project (containing `tw.config`, `pages/`, `components/`, `public/`).
8
+ 2. Connect the repository to Vercel or Netlify.
9
+ 3. The platform automatically detects `tw.config` and runs `tw build`.
10
+ 4. Your site goes live – no terminal commands required.
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ # Create a new project
16
+ tw init my-site
17
+ cd my-site
18
+
19
+ # Development server
20
+ tw dev
21
+
22
+ # Production build
23
+ tw build
24
+
25
+ # Preview the build
26
+ tw preview
27
+ ```
28
+
29
+ ## CLI Commands
30
+
31
+ | Command | Description |
32
+ |---------|-------------|
33
+ | `tw init [name]` | Create a new TW project in the current directory |
34
+ | `tw dev` | Start the development server with live reload |
35
+ | `tw build` | Generate a production build |
36
+ | `tw preview` | Preview the built output |
37
+ | `tw export` | Generate a static export |
38
+ | `tw deploy` | Build and deploy to a hosting provider |
39
+ | `tw doctor` | Run project health checks and deployment compatibility |
40
+ | `tw ast file.tw` | Print the AST JSON for a TW source file |
41
+ | `tw ir file.tw` | Print the IR JSON for a TW source file |
42
+ | `tw run file.tw` | Interpret a TW file and output HTML |
43
+ | `tw check file.tw` | Print diagnostics for a TW file |
44
+ | `tw tokens file.tw` | Print the token stream JSON for a TW source file |
45
+ | `tw clean` | Clean dist and hidden cache folders |
46
+ | `tw info` | Show a project summary |
47
+ | `tw login` | Save deploy provider configuration |
48
+
49
+ ## Deployment Metadata
50
+
51
+ After a build, the file `dist/tw.deploy.json` is automatically generated. It contains:
52
+
53
+ ```json
54
+ {
55
+ "framework": "tw",
56
+ "version": "1.0.0",
57
+ "build": "tw build",
58
+ "output": "dist",
59
+ "runtime": "ssr"
60
+ }
61
+ ```
62
+
63
+ Deployment platforms can read this file to detect the framework and configure the build.
64
+
65
+ ## Adapters
66
+
67
+ TW Framework includes built‑in adapters for Vercel and Netlify. They provide:
68
+
69
+ - Framework detection (`detect()` function)
70
+ - Automatic generation of `vercel.json` / `netlify.toml` when missing
71
+ - Build command, output directory, install command, and SSR handling
72
+
73
+ ## Documentation
74
+
75
+ - [Getting Started](docs/getting-started.md)
76
+ - [Deployment to Vercel](docs/deployment/vercel.md)
77
+ - [Deployment to Netlify](docs/deployment/netlify.md)
78
+
79
+ ## Testing
80
+
81
+ ```bash
82
+ python3 -m unittest discover -s tests -q
83
+ python3 run_tests.py
84
+ ```
85
+
86
+ ## Debug Flags
87
+
88
+ - `TW_WARN_LITERAL_PARSE=1` – warn when literal parsing falls back to string
89
+ - `TW_STRICT_EVAL=1` – re‑raise runtime expression errors instead of warning
90
+
91
+ These flags are mainly useful while debugging parser/runtime edge cases or tightening CI checks around expression handling.
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tw-framework"
7
+ version = "0.2.3"
8
+ description = "TW framework CLI with create, dev, build, and deploy commands"
9
+ requires-python = ">=3.9"
10
+ authors = [
11
+ { name = "TW Framework" }
12
+ ]
13
+ dependencies = []
14
+
15
+ [project.optional-dependencies]
16
+ # Dev / contributor tooling (not required to use the framework)
17
+ dev = [
18
+ "pytest>=7",
19
+ ]
20
+
21
+ [project.scripts]
22
+ tw = "tw_framework.cli:main"
23
+
24
+ [tool.setuptools]
25
+ packages = ["tw_framework"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,106 @@
1
+ """Tests for deployment metadata, adapters, and zero‑config detection."""
2
+
3
+ import json
4
+ import os
5
+ import tempfile
6
+ import unittest
7
+ from pathlib import Path
8
+
9
+ from tw_framework import compiler
10
+ from tw_framework.adapters import vercel, netlify
11
+ from tw_framework.framework import generate_deploy_metadata
12
+
13
+
14
+ class TestDeploymentMetadata(unittest.TestCase):
15
+ def setUp(self):
16
+ self.tmpdir = tempfile.mkdtemp()
17
+ self.output_dir = os.path.join(self.tmpdir, "dist")
18
+ os.makedirs(self.output_dir, exist_ok=True)
19
+
20
+ def tearDown(self):
21
+ import shutil
22
+ shutil.rmtree(self.tmpdir)
23
+
24
+ def test_generate_deploy_metadata(self):
25
+ config = {"name": "Test"}
26
+ generate_deploy_metadata(self.output_dir, config)
27
+ deploy_path = os.path.join(self.output_dir, "tw.deploy.json")
28
+ self.assertTrue(os.path.isfile(deploy_path))
29
+ with open(deploy_path, "r") as f:
30
+ data = json.load(f)
31
+ self.assertEqual(data["framework"], "tw")
32
+ self.assertEqual(data["build"], "tw build")
33
+ self.assertEqual(data["output"], "dist")
34
+ self.assertEqual(data["runtime"], "ssr")
35
+ self.assertIn("version", data)
36
+
37
+ def test_detect_tw_project(self):
38
+ # Create a fake tw.config
39
+ config_path = os.path.join(self.tmpdir, "tw.config")
40
+ with open(config_path, "w") as f:
41
+ f.write("name: Test\n")
42
+ self.assertTrue(compiler.detect_tw_project(self.tmpdir))
43
+ # Without tw.config
44
+ empty_dir = os.path.join(self.tmpdir, "empty")
45
+ os.makedirs(empty_dir, exist_ok=True)
46
+ self.assertFalse(compiler.detect_tw_project(empty_dir))
47
+
48
+ def test_vercel_adapter_detect(self):
49
+ meta = vercel.detect()
50
+ self.assertEqual(meta["framework"], "tw")
51
+ self.assertEqual(meta["buildCommand"], "tw build")
52
+ self.assertEqual(meta["outputDirectory"], "dist")
53
+
54
+ def test_netlify_adapter_detect(self):
55
+ meta = netlify.detect()
56
+ self.assertEqual(meta["framework"], "tw")
57
+ self.assertEqual(meta["buildCommand"], "tw build")
58
+ self.assertEqual(meta["publish"], "dist")
59
+
60
+ def test_vercel_auto_generate_json(self):
61
+ # Simulate generate_vercel_output with missing vercel.json
62
+ from tw_framework.adapters.vercel import generate_vercel_output
63
+ config = {"name": "Test"}
64
+ # Create a minimal dist directory
65
+ dist_dir = os.path.join(self.tmpdir, "dist")
66
+ os.makedirs(dist_dir, exist_ok=True)
67
+ # The function expects dist_dir to exist
68
+ generate_vercel_output(dist_dir, config, self.tmpdir)
69
+ vercel_json = os.path.join(self.tmpdir, "vercel.json")
70
+ self.assertTrue(os.path.isfile(vercel_json))
71
+ with open(vercel_json, "r") as f:
72
+ data = json.load(f)
73
+ self.assertEqual(data["framework"], "tw")
74
+
75
+ def test_netlify_auto_generate_toml(self):
76
+ from tw_framework.adapters.netlify import generate_netlify_output
77
+ config = {"name": "Test"}
78
+ dist_dir = os.path.join(self.tmpdir, "dist")
79
+ os.makedirs(dist_dir, exist_ok=True)
80
+ generate_netlify_output(dist_dir, config, self.tmpdir)
81
+ netlify_toml = os.path.join(self.tmpdir, "netlify.toml")
82
+ self.assertTrue(os.path.isfile(netlify_toml))
83
+ with open(netlify_toml, "r") as f:
84
+ content = f.read()
85
+ self.assertIn("tw build", content)
86
+ self.assertIn('publish = "dist"', content)
87
+
88
+ def test_streaming_render(self):
89
+ """Test that streaming render produces valid HTML."""
90
+ from tw_framework.streaming import render_program_streaming
91
+ from tw_framework.ir import IRProgram, IRText
92
+ program = IRProgram(
93
+ meta={"title": "Test"},
94
+ head={},
95
+ lets={},
96
+ state={},
97
+ body=[IRText("Hello World")],
98
+ )
99
+ chunks = list(render_program_streaming(program))
100
+ html = "".join(chunks)
101
+ self.assertIn("Hello World", html)
102
+ self.assertIn("<!DOCTYPE html>", html)
103
+
104
+
105
+ if __name__ == "__main__":
106
+ unittest.main()
@@ -0,0 +1,13 @@
1
+ """TW Framework package."""
2
+
3
+ __version__ = "0.4.0"
4
+
5
+ from .server import run_production_server, SSRCache # noqa: F401
6
+ from .reactivity import ( # noqa: F401
7
+ has_reactivity,
8
+ parse_state_block,
9
+ get_reactivity_runtime_js,
10
+ transform_reactive_attrs,
11
+ )
12
+ from .compiler import compile_file_pipeline, compile_text_pipeline # noqa: F401
13
+ from .interpreter import Interpreter # noqa: F401
@@ -0,0 +1,25 @@
1
+ """
2
+ Advanced compiler diagnostics for TW Framework.
3
+
4
+ Detects duplicate attributes, unused variables, circular imports,
5
+ recursive components, invalid nesting, duplicate IDs, duplicate routes,
6
+ and missing exports.
7
+ """
8
+
9
+ import logging
10
+ from typing import Dict, List
11
+
12
+ from . import compiler
13
+ from .diagnostics import Diagnostic, DiagnosticBag
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def run_advanced_diagnostics(project_root: str) -> DiagnosticBag:
19
+ """Run all advanced diagnostics and return the results."""
20
+ bag = DiagnosticBag()
21
+ # TODO: Implement actual detection logic
22
+ return bag
23
+
24
+
25
+ __all__ = ["run_advanced_diagnostics"]
@@ -0,0 +1,30 @@
1
+ """
2
+ Smart asset optimization for TW Framework.
3
+
4
+ Automatically compresses images, converts to WebP, lazy loads images,
5
+ preloads critical assets, and removes duplicate CSS/JS.
6
+ """
7
+
8
+ import logging
9
+ import os
10
+ from typing import Dict, List, Optional
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def optimize_assets(project_root: str, output_dir: str) -> Dict[str, List[str]]:
16
+ """Optimize assets in the output directory."""
17
+ optimized = {
18
+ "images_compressed": [],
19
+ "images_converted_to_webp": [],
20
+ "images_lazy_loaded": [],
21
+ "critical_assets_preloaded": [],
22
+ "duplicate_css_removed": [],
23
+ "duplicate_js_removed": [],
24
+ }
25
+
26
+ # TODO: Implement actual optimization logic
27
+ return optimized
28
+
29
+
30
+ __all__ = ["optimize_assets"]
@@ -0,0 +1,283 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass, field, is_dataclass
4
+ from typing import Any, Dict, List, Optional
5
+
6
+
7
+ @dataclass
8
+ class Attribute:
9
+ name: str
10
+ value: Any
11
+
12
+
13
+ def attribute_to_dict(attribute: Any) -> Dict[str, Any]:
14
+ if isinstance(attribute, Attribute):
15
+ return {
16
+ "name": attribute.name,
17
+ "value": serialize_value(attribute.value),
18
+ }
19
+ return {
20
+ "name": getattr(attribute, "name", ""),
21
+ "value": serialize_value(getattr(attribute, "value", None)),
22
+ }
23
+
24
+
25
+ def serialize_value(value: Any) -> Any:
26
+ if value is None or isinstance(value, (str, int, float, bool)):
27
+ return value
28
+ if isinstance(value, Attribute):
29
+ return attribute_to_dict(value)
30
+ if isinstance(value, dict):
31
+ return {str(key): serialize_value(item) for key, item in value.items()}
32
+ if isinstance(value, (list, tuple, set)):
33
+ return [serialize_value(item) for item in value]
34
+ if is_dataclass(value):
35
+ return serialize_value(asdict(value))
36
+ if hasattr(value, "to_dict") and callable(value.to_dict):
37
+ try:
38
+ return serialize_value(value.to_dict())
39
+ except Exception:
40
+ pass
41
+ if hasattr(value, "rules") and isinstance(getattr(value, "rules"), list):
42
+ return {
43
+ "kind": value.__class__.__name__,
44
+ "rules": [serialize_value(rule) for rule in getattr(value, "rules", [])],
45
+ }
46
+ if hasattr(value, "selector") and hasattr(value, "declarations"):
47
+ return {
48
+ "kind": value.__class__.__name__,
49
+ "selector": getattr(value, "selector", ""),
50
+ "declarations": [serialize_value(item) for item in getattr(value, "declarations", [])],
51
+ "children": [serialize_value(item) for item in getattr(value, "children", [])],
52
+ }
53
+ if hasattr(value, "__dict__"):
54
+ raw = {}
55
+ for key, item in vars(value).items():
56
+ if key.startswith("_"):
57
+ continue
58
+ raw[key] = serialize_value(item)
59
+ if raw:
60
+ raw.setdefault("kind", value.__class__.__name__)
61
+ return raw
62
+ return repr(value)
63
+
64
+
65
+ @dataclass
66
+ class HeadModel:
67
+ metas: List[Dict[str, Any]] = field(default_factory=list)
68
+ icon: Optional[str] = None
69
+ seo: Dict[str, Any] = field(default_factory=dict)
70
+
71
+
72
+ @dataclass
73
+ class PageMeta:
74
+ title: str = ""
75
+ layout: Optional[str] = None
76
+ layouts: List[str] = field(default_factory=list)
77
+ render_mode: str = "static"
78
+ revalidate: Optional[int] = None
79
+ redirect_to: Optional[str] = None
80
+ rewrite_to: Optional[str] = None
81
+ responsive: bool = False
82
+
83
+
84
+ @dataclass
85
+ class BaseNode:
86
+ kind: str
87
+
88
+
89
+ @dataclass
90
+ class TextNode(BaseNode):
91
+ value: str
92
+
93
+ def __init__(self, value: str):
94
+ super().__init__("text")
95
+ self.value = value
96
+
97
+
98
+ @dataclass
99
+ class LetNode(BaseNode):
100
+ name: str
101
+ value: Any
102
+
103
+ def __init__(self, name: str, value: Any):
104
+ super().__init__("let")
105
+ self.name = name
106
+ self.value = value
107
+
108
+
109
+ @dataclass
110
+ class IfNode(BaseNode):
111
+ condition: str
112
+ children: List[BaseNode] = field(default_factory=list)
113
+ else_children: List[BaseNode] = field(default_factory=list)
114
+
115
+ def __init__(self, condition: str, children: Optional[List[BaseNode]] = None, else_children: Optional[List[BaseNode]] = None):
116
+ super().__init__("if")
117
+ self.condition = condition
118
+ self.children = children or []
119
+ self.else_children = else_children or []
120
+
121
+
122
+ @dataclass
123
+ class ForNode(BaseNode):
124
+ var_name: str
125
+ iterable: str
126
+ children: List[BaseNode] = field(default_factory=list)
127
+
128
+ def __init__(self, var_name: str, iterable: str, children: Optional[List[BaseNode]] = None):
129
+ super().__init__("for")
130
+ self.var_name = var_name
131
+ self.iterable = iterable
132
+ self.children = children or []
133
+
134
+
135
+ @dataclass
136
+ class ScriptNode(BaseNode):
137
+ raw_js: str
138
+
139
+ def __init__(self, raw_js: str):
140
+ super().__init__("script")
141
+ self.raw_js = raw_js
142
+
143
+
144
+ @dataclass
145
+ class ElementNode(BaseNode):
146
+ tag: str
147
+ text: Optional[str] = None
148
+ attrs: List[Attribute] = field(default_factory=list)
149
+ styles: List[Attribute] = field(default_factory=list)
150
+ events: List[Attribute] = field(default_factory=list)
151
+ router: Dict[str, Any] = field(default_factory=dict)
152
+ children: List[BaseNode] = field(default_factory=list)
153
+
154
+ def __init__(
155
+ self,
156
+ tag: str,
157
+ text: Optional[str] = None,
158
+ attrs: Optional[List[Attribute]] = None,
159
+ styles: Optional[List[Attribute]] = None,
160
+ events: Optional[List[Attribute]] = None,
161
+ router: Optional[Dict[str, Any]] = None,
162
+ children: Optional[List[BaseNode]] = None,
163
+ ):
164
+ super().__init__("element")
165
+ self.tag = tag
166
+ self.text = text
167
+ self.attrs = attrs or []
168
+ self.styles = styles or []
169
+ self.events = events or []
170
+ self.router = router or {}
171
+ self.children = children or []
172
+
173
+
174
+ @dataclass
175
+ class ComponentNode(BaseNode):
176
+ name: str
177
+ props: List[Attribute] = field(default_factory=list)
178
+ children: List[BaseNode] = field(default_factory=list)
179
+
180
+ def __init__(self, name: str, props: Optional[List[Attribute]] = None, children: Optional[List[BaseNode]] = None):
181
+ super().__init__("component")
182
+ self.name = name
183
+ self.props = props or []
184
+ self.children = children or []
185
+
186
+
187
+ @dataclass
188
+ class Program:
189
+ meta: PageMeta = field(default_factory=PageMeta)
190
+ head: HeadModel = field(default_factory=HeadModel)
191
+ lets: Dict[str, Any] = field(default_factory=dict)
192
+ state: Dict[str, Any] = field(default_factory=dict)
193
+ body: List[BaseNode] = field(default_factory=list)
194
+ loaded_sheets: List[Any] = field(default_factory=list)
195
+ loaded_json: List[Dict[str, Any]] = field(default_factory=list)
196
+ source_path: str = ""
197
+ legacy_page: Any = None
198
+
199
+ def to_dict(self) -> Dict[str, Any]:
200
+ return {
201
+ "meta": {
202
+ "title": self.meta.title,
203
+ "layout": self.meta.layout,
204
+ "layouts": list(self.meta.layouts),
205
+ "render_mode": self.meta.render_mode,
206
+ "revalidate": self.meta.revalidate,
207
+ "redirect_to": self.meta.redirect_to,
208
+ "rewrite_to": self.meta.rewrite_to,
209
+ "responsive": self.meta.responsive,
210
+ },
211
+ "head": {
212
+ "metas": list(self.head.metas),
213
+ "icon": self.head.icon,
214
+ "seo": dict(self.head.seo),
215
+ },
216
+ "lets": serialize_value(self.lets),
217
+ "state": serialize_value(self.state),
218
+ "body": [node_to_dict(node) for node in self.body],
219
+ "loaded_sheets": serialize_value(self.loaded_sheets),
220
+ "loaded_json": serialize_value(self.loaded_json),
221
+ "source_path": self.source_path,
222
+ }
223
+
224
+
225
+ def node_to_dict(node: BaseNode) -> Dict[str, Any]:
226
+ if isinstance(node, TextNode):
227
+ return {"kind": node.kind, "value": node.value}
228
+ if isinstance(node, LetNode):
229
+ return {"kind": node.kind, "name": node.name, "value": serialize_value(node.value)}
230
+ if isinstance(node, IfNode):
231
+ return {
232
+ "kind": node.kind,
233
+ "condition": node.condition,
234
+ "children": [node_to_dict(child) for child in node.children],
235
+ "else_children": [node_to_dict(child) for child in node.else_children],
236
+ }
237
+ if isinstance(node, ForNode):
238
+ return {
239
+ "kind": node.kind,
240
+ "var_name": node.var_name,
241
+ "iterable": node.iterable,
242
+ "children": [node_to_dict(child) for child in node.children],
243
+ }
244
+ if isinstance(node, ScriptNode):
245
+ return {"kind": node.kind, "raw_js": node.raw_js}
246
+ if isinstance(node, ElementNode):
247
+ return {
248
+ "kind": node.kind,
249
+ "tag": node.tag,
250
+ "text": node.text,
251
+ "attrs": [attribute_to_dict(attr) for attr in node.attrs],
252
+ "styles": [attribute_to_dict(attr) for attr in node.styles],
253
+ "events": [attribute_to_dict(attr) for attr in node.events],
254
+ "router": serialize_value(node.router),
255
+ "children": [node_to_dict(child) for child in node.children],
256
+ }
257
+ if isinstance(node, ComponentNode):
258
+ return {
259
+ "kind": node.kind,
260
+ "name": node.name,
261
+ "props": [attribute_to_dict(prop) for prop in node.props],
262
+ "children": [node_to_dict(child) for child in node.children],
263
+ }
264
+ return {"kind": getattr(node, "kind", "unknown"), "repr": repr(node)}
265
+
266
+
267
+ __all__ = [
268
+ "Attribute",
269
+ "BaseNode",
270
+ "ComponentNode",
271
+ "ElementNode",
272
+ "ForNode",
273
+ "HeadModel",
274
+ "IfNode",
275
+ "LetNode",
276
+ "PageMeta",
277
+ "Program",
278
+ "ScriptNode",
279
+ "TextNode",
280
+ "attribute_to_dict",
281
+ "node_to_dict",
282
+ "serialize_value",
283
+ ]
@@ -0,0 +1,7 @@
1
+ from .framework import ( # noqa: F401
2
+ BuildSummary,
3
+ build_hidden_site,
4
+ clean_project_outputs,
5
+ doctor_project,
6
+ inspect_project,
7
+ )
@@ -0,0 +1,26 @@
1
+ """
2
+ Build performance optimization for TW Framework.
3
+
4
+ Parallelizes compilation, uses multiple CPU cores, and avoids recompiling unchanged files.
5
+ """
6
+
7
+ import logging
8
+ import os
9
+ from typing import List, Optional
10
+
11
+ from . import compiler
12
+ from .compiler_stats import CompilerStats
13
+ from .incremental_cache import IncrementalCache
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def optimize_build(project_root: str, output_dir: str, force: bool = False, workers: Optional[int] = None) -> CompilerStats:
19
+ """Run an optimized build with parallelism and caching."""
20
+ stats = CompilerStats()
21
+ cache = IncrementalCache(project_root)
22
+ # TODO: Implement actual parallel build logic
23
+ return stats
24
+
25
+
26
+ __all__ = ["optimize_build"]