create-caspian-app 1.0.0 → 1.0.2
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/dist/caspian.js +1 -1
- package/dist/index.js +1 -1
- package/dist/public/js/main.js +12 -12
- package/dist/settings/_component_imports.py +70 -70
- package/dist/settings/browser_log.py +498 -498
- package/dist/settings/bs-config.json +6 -6
- package/dist/settings/build-static.py +290 -290
- package/dist/settings/check.py +415 -415
- package/dist/settings/check_templates.py +274 -274
- package/dist/settings/component-map.ts +6 -6
- package/dist/settings/dev-log-bridge.ts +585 -585
- package/dist/settings/fix.py +95 -95
- package/dist/settings/python-server.ts +31 -31
- package/dist/settings/run-postcss.ts +317 -317
- package/dist/settings/serve-static.py +188 -188
- package/dist/tests/README.md +135 -135
- package/dist/tests/conftest.py +89 -89
- package/dist/tests/test_health_route.py +44 -44
- package/dist/tests/test_main_helpers.py +112 -112
- package/package.json +1 -1
package/dist/settings/fix.py
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
"""Safe auto-fixer for the app (`npm run check:fix`).
|
|
2
|
-
|
|
3
|
-
Removes genuinely dead imports and applies ruff's other safe fixes, then runs
|
|
4
|
-
the gate to report what remains. The catch it exists to handle: `pyproject.toml`
|
|
5
|
-
marks `F401` unfixable so a plain `ruff check --fix` (which anyone might run)
|
|
6
|
-
can never silently delete a component import that is used only as an `<x-*>` tag
|
|
7
|
-
(casp resolves those from module globals at render time). That safety also
|
|
8
|
-
blocks auto-removal of *ordinary* dead imports, so this script re-enables F401
|
|
9
|
-
removal — but only for files that contain no component-tag import, so component
|
|
10
|
-
imports are never at risk.
|
|
11
|
-
|
|
12
|
-
Flow:
|
|
13
|
-
1. List F401 findings under the project config (respects include/exclude).
|
|
14
|
-
2. Split the owning files into "component-guarded" (has >=1 import used as an
|
|
15
|
-
`<x-*>` tag) and the rest.
|
|
16
|
-
3. Remove dead imports only in the non-guarded files, via an isolated ruff run
|
|
17
|
-
(`--isolated` makes F401 fixable again; `--select F401` limits it to import
|
|
18
|
-
removal; explicit file args keep the run scoped).
|
|
19
|
-
4. Apply every other safe fix under the real project config.
|
|
20
|
-
5. Run the gate (`check.py`) and exit with its status.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
from __future__ import annotations
|
|
24
|
-
|
|
25
|
-
import json
|
|
26
|
-
import subprocess
|
|
27
|
-
import sys
|
|
28
|
-
from pathlib import Path
|
|
29
|
-
|
|
30
|
-
import _component_imports as ci
|
|
31
|
-
|
|
32
|
-
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _run(cmd: list[str]) -> subprocess.CompletedProcess[str]:
|
|
36
|
-
return subprocess.run(
|
|
37
|
-
cmd,
|
|
38
|
-
cwd=PROJECT_ROOT,
|
|
39
|
-
capture_output=True,
|
|
40
|
-
text=True,
|
|
41
|
-
encoding="utf-8",
|
|
42
|
-
errors="replace",
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _ruff(*args: str) -> subprocess.CompletedProcess[str]:
|
|
47
|
-
return _run([sys.executable, "-m", "ruff", "check", *args])
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _remove_dead_imports() -> list[str]:
|
|
51
|
-
"""Delete genuinely unused imports; never touch component-tag imports.
|
|
52
|
-
|
|
53
|
-
Returns the list of files that were auto-fixed.
|
|
54
|
-
"""
|
|
55
|
-
proc = _ruff(".", "--select", "F401", "--output-format", "json")
|
|
56
|
-
try:
|
|
57
|
-
findings = json.loads(proc.stdout or "[]")
|
|
58
|
-
except json.JSONDecodeError:
|
|
59
|
-
return []
|
|
60
|
-
|
|
61
|
-
guarded: set[str] = set()
|
|
62
|
-
owning_files: set[str] = set()
|
|
63
|
-
for f in findings:
|
|
64
|
-
path = f.get("filename", "")
|
|
65
|
-
if not path:
|
|
66
|
-
continue
|
|
67
|
-
owning_files.add(path)
|
|
68
|
-
if ci.is_component_tag_f401(f.get("message", ""), path):
|
|
69
|
-
guarded.add(path)
|
|
70
|
-
|
|
71
|
-
# A file with even one template-driven import is skipped whole: we can't
|
|
72
|
-
# remove just its dead imports without risking the load-bearing ones, and
|
|
73
|
-
# the gate still reports any real deadwood there for manual removal.
|
|
74
|
-
fixable_files = sorted(owning_files - guarded)
|
|
75
|
-
if fixable_files:
|
|
76
|
-
_ruff(*fixable_files, "--select", "F401", "--fix-only", "--isolated")
|
|
77
|
-
return fixable_files
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def main() -> int:
|
|
81
|
-
_remove_dead_imports()
|
|
82
|
-
# Every other safe fix under the real project config. F401 stays unfixable
|
|
83
|
-
# here (dead imports were already handled above), so component imports are
|
|
84
|
-
# untouched.
|
|
85
|
-
_ruff(".", "--fix-only")
|
|
86
|
-
|
|
87
|
-
gate = subprocess.run(
|
|
88
|
-
[sys.executable, str(PROJECT_ROOT / "settings" / "check.py")],
|
|
89
|
-
cwd=PROJECT_ROOT,
|
|
90
|
-
)
|
|
91
|
-
return gate.returncode
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if __name__ == "__main__":
|
|
95
|
-
raise SystemExit(main())
|
|
1
|
+
"""Safe auto-fixer for the app (`npm run check:fix`).
|
|
2
|
+
|
|
3
|
+
Removes genuinely dead imports and applies ruff's other safe fixes, then runs
|
|
4
|
+
the gate to report what remains. The catch it exists to handle: `pyproject.toml`
|
|
5
|
+
marks `F401` unfixable so a plain `ruff check --fix` (which anyone might run)
|
|
6
|
+
can never silently delete a component import that is used only as an `<x-*>` tag
|
|
7
|
+
(casp resolves those from module globals at render time). That safety also
|
|
8
|
+
blocks auto-removal of *ordinary* dead imports, so this script re-enables F401
|
|
9
|
+
removal — but only for files that contain no component-tag import, so component
|
|
10
|
+
imports are never at risk.
|
|
11
|
+
|
|
12
|
+
Flow:
|
|
13
|
+
1. List F401 findings under the project config (respects include/exclude).
|
|
14
|
+
2. Split the owning files into "component-guarded" (has >=1 import used as an
|
|
15
|
+
`<x-*>` tag) and the rest.
|
|
16
|
+
3. Remove dead imports only in the non-guarded files, via an isolated ruff run
|
|
17
|
+
(`--isolated` makes F401 fixable again; `--select F401` limits it to import
|
|
18
|
+
removal; explicit file args keep the run scoped).
|
|
19
|
+
4. Apply every other safe fix under the real project config.
|
|
20
|
+
5. Run the gate (`check.py`) and exit with its status.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import subprocess
|
|
27
|
+
import sys
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
import _component_imports as ci
|
|
31
|
+
|
|
32
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _run(cmd: list[str]) -> subprocess.CompletedProcess[str]:
|
|
36
|
+
return subprocess.run(
|
|
37
|
+
cmd,
|
|
38
|
+
cwd=PROJECT_ROOT,
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
encoding="utf-8",
|
|
42
|
+
errors="replace",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _ruff(*args: str) -> subprocess.CompletedProcess[str]:
|
|
47
|
+
return _run([sys.executable, "-m", "ruff", "check", *args])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _remove_dead_imports() -> list[str]:
|
|
51
|
+
"""Delete genuinely unused imports; never touch component-tag imports.
|
|
52
|
+
|
|
53
|
+
Returns the list of files that were auto-fixed.
|
|
54
|
+
"""
|
|
55
|
+
proc = _ruff(".", "--select", "F401", "--output-format", "json")
|
|
56
|
+
try:
|
|
57
|
+
findings = json.loads(proc.stdout or "[]")
|
|
58
|
+
except json.JSONDecodeError:
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
guarded: set[str] = set()
|
|
62
|
+
owning_files: set[str] = set()
|
|
63
|
+
for f in findings:
|
|
64
|
+
path = f.get("filename", "")
|
|
65
|
+
if not path:
|
|
66
|
+
continue
|
|
67
|
+
owning_files.add(path)
|
|
68
|
+
if ci.is_component_tag_f401(f.get("message", ""), path):
|
|
69
|
+
guarded.add(path)
|
|
70
|
+
|
|
71
|
+
# A file with even one template-driven import is skipped whole: we can't
|
|
72
|
+
# remove just its dead imports without risking the load-bearing ones, and
|
|
73
|
+
# the gate still reports any real deadwood there for manual removal.
|
|
74
|
+
fixable_files = sorted(owning_files - guarded)
|
|
75
|
+
if fixable_files:
|
|
76
|
+
_ruff(*fixable_files, "--select", "F401", "--fix-only", "--isolated")
|
|
77
|
+
return fixable_files
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def main() -> int:
|
|
81
|
+
_remove_dead_imports()
|
|
82
|
+
# Every other safe fix under the real project config. F401 stays unfixable
|
|
83
|
+
# here (dead imports were already handled above), so component imports are
|
|
84
|
+
# untouched.
|
|
85
|
+
_ruff(".", "--fix-only")
|
|
86
|
+
|
|
87
|
+
gate = subprocess.run(
|
|
88
|
+
[sys.executable, str(PROJECT_ROOT / "settings" / "check.py")],
|
|
89
|
+
cwd=PROJECT_ROOT,
|
|
90
|
+
)
|
|
91
|
+
return gate.returncode
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
raise SystemExit(main())
|
|
@@ -17,13 +17,13 @@ function getVenvPythonPath(): string {
|
|
|
17
17
|
: join(".venv", "bin", "python");
|
|
18
18
|
|
|
19
19
|
if (!existsSync(venvPython)) {
|
|
20
|
-
console.warn("Warning: Virtual environment not found, using system python");
|
|
20
|
+
console.warn("Warning: Virtual environment not found, using system python");
|
|
21
21
|
return isWindows() ? "python" : "python3";
|
|
22
22
|
}
|
|
23
23
|
return venvPython;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function waitForPort(port: number, timeout = 10000): Promise<boolean> {
|
|
26
|
+
export function waitForPort(port: number, timeout = 10000): Promise<boolean> {
|
|
27
27
|
const start = Date.now();
|
|
28
28
|
return new Promise((resolve) => {
|
|
29
29
|
const check = () => {
|
|
@@ -118,7 +118,7 @@ function spawnPython(port: number, browserSyncPort?: number): ChildProcess {
|
|
|
118
118
|
const pythonPath = getVenvPythonPath();
|
|
119
119
|
const args = ["-u", "main.py"];
|
|
120
120
|
|
|
121
|
-
console.log(`-> Starting Python server on port ${port}...`);
|
|
121
|
+
console.log(`-> Starting Python server on port ${port}...`);
|
|
122
122
|
|
|
123
123
|
const env = {
|
|
124
124
|
...process.env,
|
|
@@ -156,7 +156,7 @@ export async function restartPythonServer(
|
|
|
156
156
|
isRestarting = true;
|
|
157
157
|
|
|
158
158
|
try {
|
|
159
|
-
console.log("-> Restarting Python server...");
|
|
159
|
+
console.log("-> Restarting Python server...");
|
|
160
160
|
const prev = pythonProcess;
|
|
161
161
|
pythonProcess = null;
|
|
162
162
|
|
|
@@ -171,30 +171,30 @@ export async function restartPythonServer(
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
export function stopPythonServer(): void {
|
|
175
|
-
const prev = pythonProcess;
|
|
176
|
-
pythonProcess = null;
|
|
177
|
-
if (prev) killProcessTree(prev);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export async function waitForHttpHealth(
|
|
181
|
-
port: number,
|
|
182
|
-
timeout = 15000,
|
|
183
|
-
): Promise<boolean> {
|
|
184
|
-
const startedAt = Date.now();
|
|
185
|
-
|
|
186
|
-
while (Date.now() - startedAt <= timeout) {
|
|
187
|
-
try {
|
|
188
|
-
const response = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
189
|
-
cache: "no-store",
|
|
190
|
-
signal: AbortSignal.timeout(1000),
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
if (response.ok) return true;
|
|
194
|
-
} catch {}
|
|
195
|
-
|
|
196
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
174
|
+
export function stopPythonServer(): void {
|
|
175
|
+
const prev = pythonProcess;
|
|
176
|
+
pythonProcess = null;
|
|
177
|
+
if (prev) killProcessTree(prev);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function waitForHttpHealth(
|
|
181
|
+
port: number,
|
|
182
|
+
timeout = 15000,
|
|
183
|
+
): Promise<boolean> {
|
|
184
|
+
const startedAt = Date.now();
|
|
185
|
+
|
|
186
|
+
while (Date.now() - startedAt <= timeout) {
|
|
187
|
+
try {
|
|
188
|
+
const response = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
189
|
+
cache: "no-store",
|
|
190
|
+
signal: AbortSignal.timeout(1000),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (response.ok) return true;
|
|
194
|
+
} catch {}
|
|
195
|
+
|
|
196
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return false;
|
|
200
|
+
}
|