tw-framework 0.2.4__tar.gz → 0.2.6__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.
- {tw_framework-0.2.4 → tw_framework-0.2.6}/PKG-INFO +1 -1
- {tw_framework-0.2.4 → tw_framework-0.2.6}/pyproject.toml +1 -1
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/compiler.py +21 -1
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/framework.py +24 -10
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework.egg-info/PKG-INFO +1 -1
- {tw_framework-0.2.4 → tw_framework-0.2.6}/README.md +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/setup.cfg +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tests/test_deployment.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/__init__.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/adapters/__init__.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/adapters/cloudflare.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/adapters/netlify.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/adapters/vercel.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/adapters/vercel_functions.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/advanced_diagnostics.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/asset_optimizer.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/ast_nodes.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/build.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/build_performance.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/build_report.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/cli.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/code_splitting.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/common.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/compiler_stats.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/dead_code.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/dependency_graph.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/deploy.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/dev.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/diagnostics.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/dynamic_imports.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/error_formatter.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/hydration.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/incremental_cache.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/interpreter.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/ir.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/lexer.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/lowering.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/parser.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/partial_rebuild.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/performance_analyzer.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/plugin_runtime.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/production_optimizer.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/reactivity.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/render_css.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/render_html.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/route_optimizer.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/router.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/runtime_model.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/runtime_values.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/semantic.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/server.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/signature.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/static_dynamic_auto.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/streaming.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/test_build_cli_pytest.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/tree_shaking.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework/twm_parser.py +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework.egg-info/SOURCES.txt +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework.egg-info/dependency_links.txt +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework.egg-info/entry_points.txt +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework.egg-info/requires.txt +0 -0
- {tw_framework-0.2.4 → tw_framework-0.2.6}/tw_framework.egg-info/top_level.txt +0 -0
|
@@ -1136,7 +1136,22 @@ def tokenize(code, allow_inline_scripts=False):
|
|
|
1136
1136
|
start_line, start_col = line, col
|
|
1137
1137
|
word = []
|
|
1138
1138
|
# Read until whitespace, braces, quotes, or common operators.
|
|
1139
|
-
|
|
1139
|
+
# Exception: a `-` surrounded by alnum chars on both sides is kept as
|
|
1140
|
+
# part of the word (kebab-case identifiers like `aria-label`,
|
|
1141
|
+
# `data-foo`), since HTML/ARIA attribute names rely on this.
|
|
1142
|
+
while i < n:
|
|
1143
|
+
ch = code[i]
|
|
1144
|
+
if ch in ' \t\r\n{}"\'' or ch in ONE_CHAR_OPS:
|
|
1145
|
+
if (
|
|
1146
|
+
ch == "-"
|
|
1147
|
+
and word
|
|
1148
|
+
and word[-1].isalnum()
|
|
1149
|
+
and i + 1 < n
|
|
1150
|
+
and code[i + 1].isalnum()
|
|
1151
|
+
):
|
|
1152
|
+
word.append(advance_one())
|
|
1153
|
+
continue
|
|
1154
|
+
break
|
|
1140
1155
|
word.append(advance_one())
|
|
1141
1156
|
word = "".join(word)
|
|
1142
1157
|
if not word:
|
|
@@ -1569,6 +1584,11 @@ def parse_literal_value(raw):
|
|
|
1569
1584
|
if os.environ.get("TW_WARN_LITERAL_PARSE", "").strip().lower() in {"1", "true", "yes", "on"}:
|
|
1570
1585
|
log(f"⚠️ Literal parse failed, treating as string: {raw!r}", level="warning")
|
|
1571
1586
|
return raw
|
|
1587
|
+
if stripped.startswith('"') and stripped.endswith('"') and len(stripped) >= 2:
|
|
1588
|
+
try:
|
|
1589
|
+
return json.loads(stripped)
|
|
1590
|
+
except json.JSONDecodeError:
|
|
1591
|
+
return stripped[1:-1]
|
|
1572
1592
|
return raw
|
|
1573
1593
|
|
|
1574
1594
|
|
|
@@ -605,6 +605,20 @@ def parse_value_token(tokens, i):
|
|
|
605
605
|
return compiler.parse_literal_value(expr), j
|
|
606
606
|
|
|
607
607
|
|
|
608
|
+
def parse_single_value_token(tokens, i):
|
|
609
|
+
"""Parse exactly one literal token (STRING or WORD), without consuming
|
|
610
|
+
the rest of the line. Needed for directives that carry more than one
|
|
611
|
+
value on the same line, e.g. `auth "cookie" "redirect"`."""
|
|
612
|
+
if i >= len(tokens):
|
|
613
|
+
raise RuntimeError("Expected value token")
|
|
614
|
+
tok = tokens[i]
|
|
615
|
+
if tok.type == "STRING":
|
|
616
|
+
return tok.value, i + 1
|
|
617
|
+
if tok.type == "WORD" and tok.value not in {"{", "}", "[", "]", "(", ")"}:
|
|
618
|
+
return compiler.parse_literal_value(tok.value), i + 1
|
|
619
|
+
raise RuntimeError("Expected value token")
|
|
620
|
+
|
|
621
|
+
|
|
608
622
|
def parse_middleware_rules(project_root: str) -> List[dict]:
|
|
609
623
|
source_path = next((path for path in middleware_file_candidates(project_root) if os.path.exists(path)), None)
|
|
610
624
|
if not source_path:
|
|
@@ -890,16 +904,16 @@ def parse_middleware_rules(project_root: str) -> List[dict]:
|
|
|
890
904
|
if i < len(tokens) and tokens[i].type == "BRACE" and tokens[i].value == "{":
|
|
891
905
|
rule["auth_rule"], i = parse_auth_block(i)
|
|
892
906
|
else:
|
|
893
|
-
cookie_name, i =
|
|
894
|
-
redirect_to, i =
|
|
907
|
+
cookie_name, i = parse_single_value_token(tokens, i)
|
|
908
|
+
redirect_to, i = parse_single_value_token(tokens, i)
|
|
895
909
|
rule["auth"] = {"cookie": cookie_name, "redirect": redirect_to}
|
|
896
910
|
elif key == "header":
|
|
897
|
-
header_name, i =
|
|
898
|
-
header_value, i =
|
|
911
|
+
header_name, i = parse_single_value_token(tokens, i)
|
|
912
|
+
header_value, i = parse_single_value_token(tokens, i)
|
|
899
913
|
rule["headers"].append((str(header_name), str(header_value)))
|
|
900
914
|
elif key == "cookie":
|
|
901
|
-
cookie_name, i =
|
|
902
|
-
cookie_value, i =
|
|
915
|
+
cookie_name, i = parse_single_value_token(tokens, i)
|
|
916
|
+
cookie_value, i = parse_single_value_token(tokens, i)
|
|
903
917
|
rule["cookies"].append((str(cookie_name), str(cookie_value)))
|
|
904
918
|
elif key == "rate_limit":
|
|
905
919
|
rule["rate_limit"], i = parse_rate_limit_block(i)
|
|
@@ -1153,12 +1167,12 @@ def parse_api_route_file(tw_path: str) -> Dict[str, dict]:
|
|
|
1153
1167
|
elif key in {"json", "text", "html", "redirect"}:
|
|
1154
1168
|
spec[key], i = parse_value_token(tokens, i)
|
|
1155
1169
|
elif key == "header":
|
|
1156
|
-
header_name, i =
|
|
1157
|
-
header_value, i =
|
|
1170
|
+
header_name, i = parse_single_value_token(tokens, i)
|
|
1171
|
+
header_value, i = parse_single_value_token(tokens, i)
|
|
1158
1172
|
spec["headers"].append((header_name, header_value))
|
|
1159
1173
|
elif key == "cookie":
|
|
1160
|
-
cookie_name, i =
|
|
1161
|
-
cookie_value, i =
|
|
1174
|
+
cookie_name, i = parse_single_value_token(tokens, i)
|
|
1175
|
+
cookie_value, i = parse_single_value_token(tokens, i)
|
|
1162
1176
|
spec["cookies"].append((cookie_name, cookie_value))
|
|
1163
1177
|
else:
|
|
1164
1178
|
raise RuntimeError(f"Unsupported API key `{key}` in {tw_path}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|