reactxpy 0.1.0__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.
- reactxpy-0.1.0/MANIFEST.in +2 -0
- reactxpy-0.1.0/PKG-INFO +7 -0
- reactxpy-0.1.0/README.md +152 -0
- reactxpy-0.1.0/pyproject.toml +3 -0
- reactxpy-0.1.0/reactxpy/__init__.py +0 -0
- reactxpy-0.1.0/reactxpy/cli.py +36 -0
- reactxpy-0.1.0/reactxpy/create_app.py +375 -0
- reactxpy-0.1.0/reactxpy.egg-info/PKG-INFO +7 -0
- reactxpy-0.1.0/reactxpy.egg-info/SOURCES.txt +24 -0
- reactxpy-0.1.0/reactxpy.egg-info/dependency_links.txt +1 -0
- reactxpy-0.1.0/reactxpy.egg-info/entry_points.txt +3 -0
- reactxpy-0.1.0/reactxpy.egg-info/not-zip-safe +1 -0
- reactxpy-0.1.0/reactxpy.egg-info/top_level.txt +1 -0
- reactxpy-0.1.0/setup.cfg +4 -0
- reactxpy-0.1.0/setup.py +61 -0
- reactxpy-0.1.0/src/ast.h +66 -0
- reactxpy-0.1.0/src/generator.cpp +407 -0
- reactxpy-0.1.0/src/generator.h +8 -0
- reactxpy-0.1.0/src/lexer.cpp +274 -0
- reactxpy-0.1.0/src/lexer.h +69 -0
- reactxpy-0.1.0/src/linker.cpp +40 -0
- reactxpy-0.1.0/src/linker.h +9 -0
- reactxpy-0.1.0/src/main.cpp +112 -0
- reactxpy-0.1.0/src/parser.cpp +203 -0
- reactxpy-0.1.0/src/parser.h +29 -0
- reactxpy-0.1.0/src/test_parser.cpp +142 -0
reactxpy-0.1.0/PKG-INFO
ADDED
reactxpy-0.1.0/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# š ReactXPy (Python Syntax for React)
|
|
2
|
+
|
|
3
|
+
Welcome to **ReactXPy**, a revolutionary framework that bridges the elegance of Python with the raw power of the React component ecosystem. ReactXPy is an ultra-fast, C++ based transpiler that allows developers to write feature-rich, interactive web applications using clean and readable Python syntax, entirely eliminating the configuration hell of modern JavaScript bundles.
|
|
4
|
+
|
|
5
|
+
If you love Python's indentation-based readability but need the dynamic, component-driven architecture of React, ReactXPy gives you both without needing Webpack, Vite, Babel, or Node.js running on your machine.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ⨠Core Features (v2)
|
|
10
|
+
|
|
11
|
+
- **Pure Python Syntax for Components:** Define your UI components using standard `def ComponentName(props):` definitions, free of brackets and JavaScript boilerplate.
|
|
12
|
+
- **Native Inline JSX:** Seamlessly blend standard HTML/JSX tags directly inside your Python functions. The C++ parser intelligently distinguishes Python control flow from DOM declarations.
|
|
13
|
+
- **State & Hooks Engine (NEW):** ReactXPy v2 natively supports React Hooks! Utilize `useState` and `useEffect` with standard Python assignments and lambdas.
|
|
14
|
+
- **Dynamic Props & Interpolation:** Pass data across components naturally using `{props.value}` syntax. Evaluate complex math or logic inside interpolations instantly.
|
|
15
|
+
- **Native Event Handling:** Bind Python functions directly to DOM events like `onClick={triggerFunc}` without dealing with `this` binding or context scoping.
|
|
16
|
+
- **Powerful Conditional Rendering:** Drive UI states with intuitive boolean logic such as `{isActive && <div class="badge">Online</div>}` right inside the tree.
|
|
17
|
+
- **Zero-Dependency CLI Scaffold:** Get started in zero seconds. The built-in `create-reactxpy-app` generator instantly architectures a complete project for you.
|
|
18
|
+
- **Cross-Platform C++ Core:** The transpiler is compiled natively onto your machine (Windows `.exe`, macOS, or Linux) enabling lightning-fast, concurrent build times.
|
|
19
|
+
- **Local Dev Server Hook:** ReactXPy ships with a multi-threaded, hot-reloading native Python watcher (`dev.py`), replacing the need for NPM servers.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## š¦ Installation
|
|
24
|
+
|
|
25
|
+
ReactXPy is distributed globally via standard Python pip packages. Before beginning, ensure you are running Python 3.7+.
|
|
26
|
+
|
|
27
|
+
To install the framework globally onto your machine:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install reactxpy
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Verify your installation was successful and the native C++ binary was compiled by checking the CLI tool:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
reactxpy --version
|
|
37
|
+
# Output: reactxpy version 0.1.0
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## š ļø Quick Start
|
|
43
|
+
|
|
44
|
+
ReactXPy ships with a state-of-the-art interactive CLI to generate standalone applications mimicking the best practices of modern frontend development.
|
|
45
|
+
|
|
46
|
+
1. **Scaffold a Project**
|
|
47
|
+
Run the project generator from anywhere in your terminal:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
create-reactxpy-app my-awesome-app
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. **Launch the Live Server**
|
|
54
|
+
Navigate into your new application and execute the pure-Python hot reloader:
|
|
55
|
+
```bash
|
|
56
|
+
cd my-awesome-app
|
|
57
|
+
python3 dev.py
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**That's it!** The `dev.py` script automatically watches your `src/` directory for filesystem changes. It instantly hot-recompiles your `.reactxpy` components into deeply-linked browser-safe Javascript bundles (`dist/bundle.js`) and serves it live at `http://localhost:3000`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## š Comprehensive Syntax Guide
|
|
65
|
+
|
|
66
|
+
### 1. Components & Standard JSX
|
|
67
|
+
|
|
68
|
+
ReactXPy treats components naturally. You declare them identically to standard Python functions. When you return HTML DOM tags, the compiler natively intercepts it and translates the structure into optimized `React.createElement` syntax trees.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# Greet.reactxpy
|
|
72
|
+
def Greet(name):
|
|
73
|
+
return <div class="greeting-card">
|
|
74
|
+
<h3>Hello {name}!</h3>
|
|
75
|
+
<p>Welcome to the ReactXPy ecosystem.</p>
|
|
76
|
+
</div>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. State Hooks (`useState` & `useEffect`)
|
|
80
|
+
|
|
81
|
+
ReactXPy v2 fully embraces the React runtime. You can manage encapsulated component states easily by destructuring `useState` tuples, and orchestrate browser lifecycle features like API calls or Local Storage using `useEffect`.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# CounterApp.reactxpy
|
|
85
|
+
def CounterApp():
|
|
86
|
+
# Native Python Tuple Destructuring
|
|
87
|
+
count, setCount = useState(0)
|
|
88
|
+
|
|
89
|
+
# Persist the count to the browser's storage whenever the `count` dependency updates
|
|
90
|
+
useEffect(lambda: localStorage.setItem("clicks", JSON.stringify(count)), [count])
|
|
91
|
+
|
|
92
|
+
def triggerIncrement():
|
|
93
|
+
setCount(count + 1)
|
|
94
|
+
|
|
95
|
+
return <div class="counter-panel">
|
|
96
|
+
<p>Total Clicks: {count}</p>
|
|
97
|
+
<button onClick={triggerIncrement}>Increment Value</button>
|
|
98
|
+
</div>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. Conditional UI & Component Composition
|
|
102
|
+
|
|
103
|
+
ReactXPy supports infinitely nested component architectures. You can import modules seamlessly without Node syntax. It also securely translates HTML attributes (like `class`) into DOM-safe syntax (`className`) automatically.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# App.reactxpy
|
|
107
|
+
import CounterApp
|
|
108
|
+
|
|
109
|
+
def App():
|
|
110
|
+
isAdmin = True
|
|
111
|
+
|
|
112
|
+
return <main class="dashboard-wrapper">
|
|
113
|
+
<h1>Admin Control Panel</h1>
|
|
114
|
+
|
|
115
|
+
{isAdmin && <div class="secure-badge">Verified Session</div>}
|
|
116
|
+
|
|
117
|
+
<div class="widgets">
|
|
118
|
+
<CounterApp />
|
|
119
|
+
</div>
|
|
120
|
+
</main>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## āļø How the Architecture Works
|
|
126
|
+
|
|
127
|
+
1. **Native OS Compilation:** When you `pip install reactxpy`, your computer leverages its local `g++` compiler to build a hyper-optimized `.exe` (Windows) or binary (Unix) of the ReactXPy transpiler directly within your Python packages folder.
|
|
128
|
+
2. **Lexical Analysis Engine:** The C++ compiler scans your `.reactxpy` files. It fundamentally understands the boundary between Python variable assignments, indentations, scopes, and HTML tag delimiters.
|
|
129
|
+
3. **AST Transpilation:** The framework safely merges Javascript operators (like `||` and `&&`), mathematical calculations, and JSX scopes into raw, standalone Javascript mappings.
|
|
130
|
+
4. **Dependency-Free Bundle:** The compiler bridges your states via a lightweight `runtime.js` hook memory wrapper. It drops ES module syntax entirelyāso you don't even need a server! You can double-click `index.html` directly from a USB stick and the app will flawlessly function offline in standard browsers.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## š® Development Roadmap
|
|
135
|
+
|
|
136
|
+
ReactXPy is an actively maintained open-source framework. The current horizon features include:
|
|
137
|
+
|
|
138
|
+
- [x] Transpilation of Standard JSX directly to `React` API calls.
|
|
139
|
+
- [x] Interactive State Management (`useState` Hooks).
|
|
140
|
+
- [x] Application Lifecycle Memory (`useEffect` Hooks).
|
|
141
|
+
- [x] Zero-Dependency Build Pipelines (`build.py` & `dev.py`).
|
|
142
|
+
- [x] Native Microsoft Windows (`.exe`) Subprocess Compilation.
|
|
143
|
+
- [ ] List Comprehension Syntax (e.g., `<item for item in array>`).
|
|
144
|
+
- [ ] Asynchronous API Fetching (`async/await` components).
|
|
145
|
+
- [ ] Deep Integrated Server-Side Python Routing.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
### License
|
|
151
|
+
|
|
152
|
+
MIT License - Have fun building!
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
# Handle version flags directly in the wrapper
|
|
7
|
+
if len(sys.argv) > 1 and sys.argv[1] in ["-v", "--version"]:
|
|
8
|
+
print("reactxpy version 0.1.0")
|
|
9
|
+
sys.exit(0)
|
|
10
|
+
|
|
11
|
+
# Find the compiled C++ compiler binary relative to this script
|
|
12
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
13
|
+
is_windows = sys.platform == "win32"
|
|
14
|
+
binary_name = "reactxpy_compiler.exe" if is_windows else "reactxpy_compiler"
|
|
15
|
+
compiler_path = os.path.join(current_dir, binary_name)
|
|
16
|
+
|
|
17
|
+
if not os.path.exists(compiler_path):
|
|
18
|
+
print(f"Error: ReactXPy compiler binary not found at {compiler_path}")
|
|
19
|
+
print("Please ensure the package was built correctly.")
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
# Pass all arguments directly to the C++ compiler
|
|
23
|
+
args = [compiler_path] + sys.argv[1:]
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Replaces the current process with the compiler
|
|
27
|
+
if is_windows:
|
|
28
|
+
sys.exit(subprocess.call(args))
|
|
29
|
+
else:
|
|
30
|
+
os.execv(compiler_path, args)
|
|
31
|
+
except OSError as e:
|
|
32
|
+
print(f"Failed to execute compiler: {e}")
|
|
33
|
+
sys.exit(1)
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
main()
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def create_file(path, content):
|
|
5
|
+
with open(path, 'w') as f:
|
|
6
|
+
f.write(content.strip() + '\n')
|
|
7
|
+
print(f"Created {path}")
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help"]:
|
|
11
|
+
print("Usage: create-reactxpy-app [project-name]")
|
|
12
|
+
print("\\nOptions:")
|
|
13
|
+
print(" -h, --help Show this help message and exit.")
|
|
14
|
+
print(" -v, --version Show the version of create-reactxpy-app.")
|
|
15
|
+
print("\\nExample:")
|
|
16
|
+
print(" create-reactxpy-app my-new-project")
|
|
17
|
+
sys.exit(0)
|
|
18
|
+
|
|
19
|
+
if len(sys.argv) > 1 and sys.argv[1] in ["-v", "--version"]:
|
|
20
|
+
print("create-reactxpy-app version 0.1.0")
|
|
21
|
+
sys.exit(0)
|
|
22
|
+
|
|
23
|
+
print("\\033[0;34mWelcome to create-reactxpy-app! Let's scaffold your new project.\\033[0m\\n")
|
|
24
|
+
|
|
25
|
+
project_name = ""
|
|
26
|
+
if len(sys.argv) > 1:
|
|
27
|
+
project_name = sys.argv[1]
|
|
28
|
+
|
|
29
|
+
while not project_name:
|
|
30
|
+
try:
|
|
31
|
+
project_name = input("? What is your project named? ")
|
|
32
|
+
except (KeyboardInterrupt, EOFError):
|
|
33
|
+
print("\\nOperation cancelled.")
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
|
|
36
|
+
project_name = project_name.strip()
|
|
37
|
+
if not project_name:
|
|
38
|
+
print("Error: Project name cannot be empty.")
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
if os.path.exists(project_name):
|
|
42
|
+
print(f"Error: Directory '{project_name}' already exists.")
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
|
|
45
|
+
print(f"\\nš Creating a new ReactXPy project in {os.path.abspath(project_name)}...")
|
|
46
|
+
|
|
47
|
+
# Create directory structure
|
|
48
|
+
dirs = [
|
|
49
|
+
project_name,
|
|
50
|
+
os.path.join(project_name, "public"),
|
|
51
|
+
os.path.join(project_name, "src"),
|
|
52
|
+
os.path.join(project_name, "src", "components"),
|
|
53
|
+
os.path.join(project_name, "runtime")
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
for d in dirs:
|
|
57
|
+
os.makedirs(d, exist_ok=True)
|
|
58
|
+
|
|
59
|
+
# App.pysx
|
|
60
|
+
app_pysx = """
|
|
61
|
+
import CounterApp
|
|
62
|
+
|
|
63
|
+
def App():
|
|
64
|
+
isAdmin = true
|
|
65
|
+
|
|
66
|
+
return <main class="dashboard-wrapper">
|
|
67
|
+
<h1>Admin Control Panel</h1>
|
|
68
|
+
|
|
69
|
+
{isAdmin && <div class="secure-badge">Verified Session</div>}
|
|
70
|
+
|
|
71
|
+
<div class="widgets">
|
|
72
|
+
<CounterApp />
|
|
73
|
+
</div>
|
|
74
|
+
</main>
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
# CounterApp.pysx
|
|
78
|
+
counterapp_pysx = """
|
|
79
|
+
def CounterApp():
|
|
80
|
+
count, setCount = useState(0)
|
|
81
|
+
|
|
82
|
+
useEffect(lambda: localStorage.setItem("clicks", JSON.stringify(count)), [count])
|
|
83
|
+
|
|
84
|
+
return <div class="counter-panel">
|
|
85
|
+
<p>Total Clicks: {count}</p>
|
|
86
|
+
<button onClick={lambda: setCount(count + 1)}>Increment Value</button>
|
|
87
|
+
</div>
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
# runtime.js
|
|
91
|
+
runtime_js = """
|
|
92
|
+
// runtime/runtime.js
|
|
93
|
+
|
|
94
|
+
// -----------------------------
|
|
95
|
+
// Hook wrappers (simple bridge)
|
|
96
|
+
// -----------------------------
|
|
97
|
+
function useState(initial) {
|
|
98
|
+
return React.useState(initial);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function useEffect(effect, deps) {
|
|
102
|
+
return React.useEffect(effect, deps);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// -----------------------------
|
|
106
|
+
// App runner
|
|
107
|
+
// -----------------------------
|
|
108
|
+
function run(App) {
|
|
109
|
+
|
|
110
|
+
const rootElement =
|
|
111
|
+
document.getElementById("root") ||
|
|
112
|
+
document.body;
|
|
113
|
+
|
|
114
|
+
const root = ReactDOM.createRoot(rootElement);
|
|
115
|
+
|
|
116
|
+
root.render(
|
|
117
|
+
React.createElement(App)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// -----------------------------
|
|
122
|
+
// Global API
|
|
123
|
+
// -----------------------------
|
|
124
|
+
window.ReactXPy = {
|
|
125
|
+
run,
|
|
126
|
+
useState,
|
|
127
|
+
useEffect
|
|
128
|
+
};
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
# index.html
|
|
132
|
+
index_html = """
|
|
133
|
+
<!DOCTYPE html>
|
|
134
|
+
<html>
|
|
135
|
+
<head>
|
|
136
|
+
<meta charset="UTF-8">
|
|
137
|
+
<title>ReactXPy App</title>
|
|
138
|
+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
|
139
|
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
|
140
|
+
<link rel="stylesheet" href="./style.css">
|
|
141
|
+
</head>
|
|
142
|
+
<body>
|
|
143
|
+
<div id="root"></div>
|
|
144
|
+
<script src="../runtime/runtime.js"></script>
|
|
145
|
+
<script src="../dist/bundle.js"></script>
|
|
146
|
+
<script>
|
|
147
|
+
ReactXPy.run(App);
|
|
148
|
+
|
|
149
|
+
// Live Reload Hook
|
|
150
|
+
let currentVersion = null;
|
|
151
|
+
setInterval(() => {
|
|
152
|
+
fetch('/version').then(res => res.text()).then(version => {
|
|
153
|
+
if (currentVersion === null) currentVersion = version;
|
|
154
|
+
else if (currentVersion !== version) location.reload();
|
|
155
|
+
}).catch(() => {});
|
|
156
|
+
}, 1000);
|
|
157
|
+
</script>
|
|
158
|
+
</body>
|
|
159
|
+
</html>
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
# style.css
|
|
163
|
+
style_css = """
|
|
164
|
+
:root {
|
|
165
|
+
--bg-main: #f8fafc;
|
|
166
|
+
--card-bg: #ffffff;
|
|
167
|
+
--text-primary: #0f172a;
|
|
168
|
+
--text-secondary: #64748b;
|
|
169
|
+
--accent: #4f46e5;
|
|
170
|
+
--accent-hover: #4338ca;
|
|
171
|
+
--success-bg: #dcfce7;
|
|
172
|
+
--success-text: #166534;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
body {
|
|
176
|
+
margin: 0;
|
|
177
|
+
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
178
|
+
background-color: var(--bg-main);
|
|
179
|
+
color: var(--text-primary);
|
|
180
|
+
display: flex;
|
|
181
|
+
justify-content: center;
|
|
182
|
+
padding-top: 60px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.dashboard-wrapper {
|
|
186
|
+
background: var(--card-bg);
|
|
187
|
+
padding: 40px;
|
|
188
|
+
border-radius: 16px;
|
|
189
|
+
box-shadow: 0 10px 25px rgba(0,0,0,0.05);
|
|
190
|
+
text-align: center;
|
|
191
|
+
max-width: 400px;
|
|
192
|
+
width: 100%;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
h1 {
|
|
196
|
+
font-size: 1.5rem;
|
|
197
|
+
margin-top: 0;
|
|
198
|
+
margin-bottom: 12px;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.secure-badge {
|
|
202
|
+
display: inline-block;
|
|
203
|
+
background: var(--success-bg);
|
|
204
|
+
color: var(--success-text);
|
|
205
|
+
padding: 6px 12px;
|
|
206
|
+
border-radius: 20px;
|
|
207
|
+
font-size: 0.85rem;
|
|
208
|
+
font-weight: 600;
|
|
209
|
+
margin-bottom: 30px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.counter-panel {
|
|
213
|
+
background: #f1f5f9;
|
|
214
|
+
padding: 24px;
|
|
215
|
+
border-radius: 12px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.counter-panel p {
|
|
219
|
+
font-size: 1.25rem;
|
|
220
|
+
font-weight: 600;
|
|
221
|
+
margin-top: 0;
|
|
222
|
+
color: var(--text-secondary);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
button {
|
|
226
|
+
background: var(--accent);
|
|
227
|
+
color: white;
|
|
228
|
+
border: none;
|
|
229
|
+
padding: 12px 24px;
|
|
230
|
+
border-radius: 8px;
|
|
231
|
+
font-weight: 600;
|
|
232
|
+
font-size: 1rem;
|
|
233
|
+
cursor: pointer;
|
|
234
|
+
transition: 0.2s;
|
|
235
|
+
width: 100%;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
button:hover {
|
|
239
|
+
background: var(--accent-hover);
|
|
240
|
+
transform: translateY(-2px);
|
|
241
|
+
}
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
# build.py
|
|
245
|
+
build_py = """
|
|
246
|
+
import os
|
|
247
|
+
import subprocess
|
|
248
|
+
from pathlib import Path
|
|
249
|
+
|
|
250
|
+
def main():
|
|
251
|
+
print("š Building ReactXPy App...")
|
|
252
|
+
|
|
253
|
+
os.makedirs("dist", exist_ok=True)
|
|
254
|
+
compiled_files = []
|
|
255
|
+
|
|
256
|
+
from shutil import which
|
|
257
|
+
if which("reactxpy") is None:
|
|
258
|
+
print("ā Error: reactxpy compiler not found. Please install the reactxpy pip package globally.")
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
for file in Path("src").rglob("*.pysx"):
|
|
262
|
+
basename = file.stem
|
|
263
|
+
output_path = f"dist/{basename}.js"
|
|
264
|
+
|
|
265
|
+
print(f"ā” Compiling: {file} -> {output_path}")
|
|
266
|
+
subprocess.run(["reactxpy", str(file), "-o", output_path], check=True)
|
|
267
|
+
compiled_files.append(output_path)
|
|
268
|
+
|
|
269
|
+
if compiled_files:
|
|
270
|
+
print("\\nš Linking into dist/bundle.js...")
|
|
271
|
+
subprocess.run(["reactxpy", "--bundle"] + compiled_files + ["dist/bundle.js"], check=True)
|
|
272
|
+
print("\\nā
Success! Build complete.")
|
|
273
|
+
else:
|
|
274
|
+
print("ā Error: No .pysx files found in src directory.")
|
|
275
|
+
|
|
276
|
+
if __name__ == "__main__":
|
|
277
|
+
main()
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
# dev.py
|
|
281
|
+
dev_py = """
|
|
282
|
+
import os
|
|
283
|
+
import sys
|
|
284
|
+
import time
|
|
285
|
+
import subprocess
|
|
286
|
+
import threading
|
|
287
|
+
from pathlib import Path
|
|
288
|
+
|
|
289
|
+
BUILD_VERSION = 1
|
|
290
|
+
|
|
291
|
+
def get_mtimes():
|
|
292
|
+
mtimes = {}
|
|
293
|
+
for p in Path("src").rglob("*.pysx"):
|
|
294
|
+
mtimes[str(p)] = p.stat().st_mtime
|
|
295
|
+
return mtimes
|
|
296
|
+
|
|
297
|
+
def serve():
|
|
298
|
+
from http.server import SimpleHTTPRequestHandler
|
|
299
|
+
from socketserver import TCPServer
|
|
300
|
+
|
|
301
|
+
class QuietHandler(SimpleHTTPRequestHandler):
|
|
302
|
+
def log_message(self, format, *args):
|
|
303
|
+
pass # Keep terminal clean
|
|
304
|
+
|
|
305
|
+
def do_GET(self):
|
|
306
|
+
if self.path == '/version':
|
|
307
|
+
self.send_response(200)
|
|
308
|
+
self.send_header('Content-type', 'text/plain')
|
|
309
|
+
self.send_header('Access-Control-Allow-Origin', '*')
|
|
310
|
+
self.end_headers()
|
|
311
|
+
self.wfile.write(str(BUILD_VERSION).encode())
|
|
312
|
+
else:
|
|
313
|
+
super().do_GET()
|
|
314
|
+
|
|
315
|
+
TCPServer.allow_reuse_address = True
|
|
316
|
+
try:
|
|
317
|
+
httpd = TCPServer(("", 3000), QuietHandler)
|
|
318
|
+
print("š Live Server running at http://localhost:3000/public/index.html")
|
|
319
|
+
httpd.serve_forever()
|
|
320
|
+
except OSError as e:
|
|
321
|
+
print(f"ā Could not start dev server: {e}")
|
|
322
|
+
sys.exit(1)
|
|
323
|
+
|
|
324
|
+
def main():
|
|
325
|
+
global BUILD_VERSION
|
|
326
|
+
print("š Starting ReactXPy Live Development Server...")
|
|
327
|
+
subprocess.run([sys.executable, "build.py"], check=False)
|
|
328
|
+
|
|
329
|
+
server_thread = threading.Thread(target=serve, daemon=True)
|
|
330
|
+
server_thread.start()
|
|
331
|
+
|
|
332
|
+
print("š Watching for file changes in src/ directory...")
|
|
333
|
+
last_mtimes = get_mtimes()
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
while True:
|
|
337
|
+
time.sleep(0.5)
|
|
338
|
+
current_mtimes = get_mtimes()
|
|
339
|
+
|
|
340
|
+
if current_mtimes != last_mtimes:
|
|
341
|
+
print("\\n⨠File change detected! Rebuilding...")
|
|
342
|
+
subprocess.run([sys.executable, "build.py"], check=False)
|
|
343
|
+
last_mtimes = current_mtimes
|
|
344
|
+
BUILD_VERSION += 1
|
|
345
|
+
|
|
346
|
+
except KeyboardInterrupt:
|
|
347
|
+
print("\\nš Stopping dev server...")
|
|
348
|
+
sys.exit(0)
|
|
349
|
+
|
|
350
|
+
if __name__ == "__main__":
|
|
351
|
+
main()
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
create_file(os.path.join(project_name, "src", "App.pysx"), app_pysx)
|
|
355
|
+
create_file(os.path.join(project_name, "src", "components", "CounterApp.pysx"), counterapp_pysx)
|
|
356
|
+
create_file(os.path.join(project_name, "runtime", "runtime.js"), runtime_js)
|
|
357
|
+
create_file(os.path.join(project_name, "public", "index.html"), index_html)
|
|
358
|
+
create_file(os.path.join(project_name, "public", "style.css"), style_css)
|
|
359
|
+
|
|
360
|
+
build_script = os.path.join(project_name, "build.py")
|
|
361
|
+
create_file(build_script, build_py)
|
|
362
|
+
|
|
363
|
+
dev_script = os.path.join(project_name, "dev.py")
|
|
364
|
+
create_file(dev_script, dev_py)
|
|
365
|
+
|
|
366
|
+
os.chmod(build_script, 0o755) # Make executable
|
|
367
|
+
os.chmod(dev_script, 0o755)
|
|
368
|
+
|
|
369
|
+
print(f"\\nā
Project '{project_name}' created successfully!")
|
|
370
|
+
print("\\nNext steps:")
|
|
371
|
+
print(f" cd {project_name}")
|
|
372
|
+
print(" python3 dev.py")
|
|
373
|
+
|
|
374
|
+
if __name__ == "__main__":
|
|
375
|
+
main()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
reactxpy/__init__.py
|
|
6
|
+
reactxpy/cli.py
|
|
7
|
+
reactxpy/create_app.py
|
|
8
|
+
reactxpy.egg-info/PKG-INFO
|
|
9
|
+
reactxpy.egg-info/SOURCES.txt
|
|
10
|
+
reactxpy.egg-info/dependency_links.txt
|
|
11
|
+
reactxpy.egg-info/entry_points.txt
|
|
12
|
+
reactxpy.egg-info/not-zip-safe
|
|
13
|
+
reactxpy.egg-info/top_level.txt
|
|
14
|
+
src/ast.h
|
|
15
|
+
src/generator.cpp
|
|
16
|
+
src/generator.h
|
|
17
|
+
src/lexer.cpp
|
|
18
|
+
src/lexer.h
|
|
19
|
+
src/linker.cpp
|
|
20
|
+
src/linker.h
|
|
21
|
+
src/main.cpp
|
|
22
|
+
src/parser.cpp
|
|
23
|
+
src/parser.h
|
|
24
|
+
src/test_parser.cpp
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
reactxpy
|
reactxpy-0.1.0/setup.cfg
ADDED