@anvil-works/anvil-cli 0.7.0-canary.17 → 0.7.0-canary.19
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 +9 -3
- package/dist/WatchSession.d.ts +6 -0
- package/dist/WatchSession.d.ts.map +1 -1
- package/dist/cli.js +342 -43
- package/dist/index.js +219 -11
- package/dist/program.d.ts.map +1 -1
- package/dist/validatePython.d.ts +10 -0
- package/dist/validatePython.d.ts.map +1 -0
- package/dist/validators.d.ts +2 -1
- package/dist/validators.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -170,17 +170,18 @@ Useful options:
|
|
|
170
170
|
|
|
171
171
|
### `anvil validate`
|
|
172
172
|
|
|
173
|
-
Check an Anvil app file before syncing, or when the CLI reports a validation error.
|
|
173
|
+
Check an Anvil app file or directory before syncing, or when the CLI reports a validation error.
|
|
174
174
|
|
|
175
175
|
```bash
|
|
176
|
-
anvil validate <
|
|
176
|
+
anvil validate <path>
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
When given a file, the command chooses the validator from the file path. When given a directory, it recursively validates supported files under that directory. It supports:
|
|
180
180
|
|
|
181
181
|
- `anvil.yaml`
|
|
182
182
|
- `.yaml` files under `client_code/`, including `client_code/**/form_template.yaml`
|
|
183
183
|
- `.html` files under `client_code/`, including `client_code/**/form_template.html` and module-form `client_code/<Form>.html`
|
|
184
|
+
- `.py` files under `client_code/` and `server_code/`
|
|
184
185
|
|
|
185
186
|
Common examples:
|
|
186
187
|
|
|
@@ -190,10 +191,15 @@ anvil validate client_code/Form1/form_template.yaml
|
|
|
190
191
|
anvil validate client_code/Form1.yaml
|
|
191
192
|
anvil validate client_code/Form1/form_template.html
|
|
192
193
|
anvil validate client_code/Form1.html
|
|
194
|
+
anvil validate client_code/Helpers.py
|
|
195
|
+
anvil validate server_code/ServerModule.py
|
|
196
|
+
anvil validate .
|
|
193
197
|
```
|
|
194
198
|
|
|
195
199
|
HTML validation parses the template with `@anvil-works/form-template-parser`, checks optional YAML frontmatter, and reports semantic issues such as duplicate explicit component names or invalid component types.
|
|
196
200
|
|
|
201
|
+
Python validation checks syntax using a local Python interpreter (`ANVIL_PYTHON`, `python3`, or `python`). Form Python under `client_code/` also checks for the matching designer-template import and form class inheritance when a sibling form template exists.
|
|
202
|
+
|
|
197
203
|
When the file is valid, the command prints a success message and exits with status `0`. When the file is invalid, it prints path-specific validation issues and exits with a non-zero status.
|
|
198
204
|
|
|
199
205
|
Use `--json` with `anvil validate` to emit structured NDJSON output for scripts or LLM tooling.
|
package/dist/WatchSession.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface WatchSessionEvents {
|
|
|
7
7
|
oldBranch: string;
|
|
8
8
|
newBranch: string;
|
|
9
9
|
};
|
|
10
|
+
"external-branch-changed": {
|
|
11
|
+
oldBranch: string;
|
|
12
|
+
newBranch: string;
|
|
13
|
+
};
|
|
10
14
|
"env-branch-switch-started": {
|
|
11
15
|
envPid: string;
|
|
12
16
|
oldBranch: string;
|
|
@@ -113,6 +117,7 @@ export declare class WatchSession extends Emitter<WatchSessionEvents> {
|
|
|
113
117
|
private environment?;
|
|
114
118
|
private envBranchChangeMode;
|
|
115
119
|
private isSwitchingForEnvironment;
|
|
120
|
+
private environmentSwitchTargetBranch;
|
|
116
121
|
private dependencyWatchEnabled;
|
|
117
122
|
private isRefreshingDependencies;
|
|
118
123
|
private hasPendingDependencyRefresh;
|
|
@@ -158,6 +163,7 @@ export declare class WatchSession extends Emitter<WatchSessionEvents> {
|
|
|
158
163
|
}>;
|
|
159
164
|
cleanup(): void;
|
|
160
165
|
startWatching(): Promise<void>;
|
|
166
|
+
private handleFileWatcherBranchChange;
|
|
161
167
|
startWatchingInBackground(onError?: (error: unknown) => void): void;
|
|
162
168
|
private initializeLocalChangeFingerprint;
|
|
163
169
|
private startLocalChangeFallbackPolling;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WatchSession.d.ts","sourceRoot":"","sources":["../src/WatchSession.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"WatchSession.d.ts","sourceRoot":"","sources":["../src/WatchSession.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -16288,6 +16288,167 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16288
16288
|
ok: true
|
|
16289
16289
|
};
|
|
16290
16290
|
}
|
|
16291
|
+
const PYTHON_VALIDATOR_SCRIPT = String.raw`
|
|
16292
|
+
import ast
|
|
16293
|
+
import json
|
|
16294
|
+
import sys
|
|
16295
|
+
|
|
16296
|
+
|
|
16297
|
+
def issue_path(error):
|
|
16298
|
+
if getattr(error, "lineno", None) is None:
|
|
16299
|
+
return "root"
|
|
16300
|
+
if getattr(error, "offset", None) is None:
|
|
16301
|
+
return f"line {error.lineno}"
|
|
16302
|
+
return f"line {error.lineno}, column {error.offset}"
|
|
16303
|
+
|
|
16304
|
+
|
|
16305
|
+
def base_name(node):
|
|
16306
|
+
if isinstance(node, ast.Name):
|
|
16307
|
+
return node.id
|
|
16308
|
+
if isinstance(node, ast.Attribute):
|
|
16309
|
+
return node.attr
|
|
16310
|
+
return None
|
|
16311
|
+
|
|
16312
|
+
|
|
16313
|
+
def imports_template_from_anvil_designer(node, template_name):
|
|
16314
|
+
if node.level < 1:
|
|
16315
|
+
return False
|
|
16316
|
+
if node.module not in ("_anvil_designer", None):
|
|
16317
|
+
return False
|
|
16318
|
+
return any(alias.name == template_name for alias in node.names)
|
|
16319
|
+
|
|
16320
|
+
|
|
16321
|
+
file_path = sys.argv[1]
|
|
16322
|
+
class_name = sys.argv[2]
|
|
16323
|
+
template_name = f"{class_name}Template" if class_name else ""
|
|
16324
|
+
source = sys.stdin.read()
|
|
16325
|
+
issues = []
|
|
16326
|
+
|
|
16327
|
+
try:
|
|
16328
|
+
compile(source, file_path, "exec")
|
|
16329
|
+
except SyntaxError as error:
|
|
16330
|
+
issues.append({"path": issue_path(error), "message": error.msg})
|
|
16331
|
+
print(json.dumps({"issues": issues}))
|
|
16332
|
+
sys.exit(0)
|
|
16333
|
+
|
|
16334
|
+
if class_name:
|
|
16335
|
+
tree = ast.parse(source, filename=file_path)
|
|
16336
|
+
has_template_import = False
|
|
16337
|
+
has_form_class = False
|
|
16338
|
+
|
|
16339
|
+
for node in ast.walk(tree):
|
|
16340
|
+
if isinstance(node, ast.ImportFrom) and imports_template_from_anvil_designer(node, template_name):
|
|
16341
|
+
has_template_import = True
|
|
16342
|
+
elif isinstance(node, ast.ClassDef) and node.name == class_name:
|
|
16343
|
+
if any(base_name(base) == template_name for base in node.bases):
|
|
16344
|
+
has_form_class = True
|
|
16345
|
+
|
|
16346
|
+
if not has_template_import:
|
|
16347
|
+
issues.append({
|
|
16348
|
+
"path": "root",
|
|
16349
|
+
"message": f"missing {chr(96)}from ._anvil_designer import {template_name}{chr(96)}",
|
|
16350
|
+
})
|
|
16351
|
+
if not has_form_class:
|
|
16352
|
+
issues.append({
|
|
16353
|
+
"path": "root",
|
|
16354
|
+
"message": f"missing class {chr(96)}{class_name}{chr(96)} inheriting from {chr(96)}{template_name}{chr(96)}",
|
|
16355
|
+
})
|
|
16356
|
+
|
|
16357
|
+
print(json.dumps({"issues": issues}))
|
|
16358
|
+
`;
|
|
16359
|
+
function findPythonInterpreter() {
|
|
16360
|
+
const candidates = [
|
|
16361
|
+
process.env.ANVIL_PYTHON,
|
|
16362
|
+
"python3",
|
|
16363
|
+
"python"
|
|
16364
|
+
].filter((candidate)=>"string" == typeof candidate && candidate.length > 0);
|
|
16365
|
+
for (const candidate of candidates){
|
|
16366
|
+
const result = (0, external_child_process_namespaceObject.spawnSync)(candidate, [
|
|
16367
|
+
"--version"
|
|
16368
|
+
], {
|
|
16369
|
+
encoding: "utf8"
|
|
16370
|
+
});
|
|
16371
|
+
if (!result.error && 0 === result.status) return candidate;
|
|
16372
|
+
}
|
|
16373
|
+
return null;
|
|
16374
|
+
}
|
|
16375
|
+
function isClientFormPython(filePath) {
|
|
16376
|
+
if ("__init__.py" === external_path_default().basename(filePath)) {
|
|
16377
|
+
const formDir = external_path_default().dirname(filePath);
|
|
16378
|
+
return external_fs_.existsSync(external_path_default().join(formDir, "form_template.html")) || external_fs_.existsSync(external_path_default().join(formDir, "form_template.yaml"));
|
|
16379
|
+
}
|
|
16380
|
+
const parsed = external_path_default().parse(filePath);
|
|
16381
|
+
return external_fs_.existsSync(external_path_default().join(parsed.dir, `${parsed.name}.html`)) || external_fs_.existsSync(external_path_default().join(parsed.dir, `${parsed.name}.yaml`));
|
|
16382
|
+
}
|
|
16383
|
+
function expectedFormClassName(filePath) {
|
|
16384
|
+
if (!isClientFormPython(filePath)) return null;
|
|
16385
|
+
if ("__init__.py" === external_path_default().basename(filePath)) return external_path_default().basename(external_path_default().dirname(filePath));
|
|
16386
|
+
return external_path_default().basename(filePath, ".py");
|
|
16387
|
+
}
|
|
16388
|
+
function isUnderDirectory(filePath, directoryPath) {
|
|
16389
|
+
const relativePath = external_path_default().relative(directoryPath, filePath);
|
|
16390
|
+
return relativePath.length > 0 && !relativePath.startsWith("..") && !external_path_default().isAbsolute(relativePath);
|
|
16391
|
+
}
|
|
16392
|
+
function validatePython(filePath, fileContent, appRoot) {
|
|
16393
|
+
const interpreter = findPythonInterpreter();
|
|
16394
|
+
if (!interpreter) return {
|
|
16395
|
+
ok: false,
|
|
16396
|
+
message: "Python file could not be validated",
|
|
16397
|
+
issues: [
|
|
16398
|
+
{
|
|
16399
|
+
path: "root",
|
|
16400
|
+
message: "Python validation requires python3 or python on PATH, or ANVIL_PYTHON to be set"
|
|
16401
|
+
}
|
|
16402
|
+
]
|
|
16403
|
+
};
|
|
16404
|
+
const absolutePath = external_path_default().resolve(filePath);
|
|
16405
|
+
const clientCodeRoot = appRoot ? external_path_default().join(appRoot, "client_code") : null;
|
|
16406
|
+
const formClassName = clientCodeRoot && isUnderDirectory(absolutePath, clientCodeRoot) ? expectedFormClassName(absolutePath) : null;
|
|
16407
|
+
const result = (0, external_child_process_namespaceObject.spawnSync)(interpreter, [
|
|
16408
|
+
"-c",
|
|
16409
|
+
PYTHON_VALIDATOR_SCRIPT,
|
|
16410
|
+
filePath,
|
|
16411
|
+
formClassName ?? ""
|
|
16412
|
+
], {
|
|
16413
|
+
encoding: "utf8",
|
|
16414
|
+
input: fileContent,
|
|
16415
|
+
maxBuffer: 1048576
|
|
16416
|
+
});
|
|
16417
|
+
if (result.error || 0 !== result.status) return {
|
|
16418
|
+
ok: false,
|
|
16419
|
+
message: "Python file could not be validated",
|
|
16420
|
+
issues: [
|
|
16421
|
+
{
|
|
16422
|
+
path: "root",
|
|
16423
|
+
message: result.error?.message ?? (result.stderr.trim() || "Python validator failed")
|
|
16424
|
+
}
|
|
16425
|
+
]
|
|
16426
|
+
};
|
|
16427
|
+
let parsed;
|
|
16428
|
+
try {
|
|
16429
|
+
parsed = JSON.parse(result.stdout);
|
|
16430
|
+
} catch (error) {
|
|
16431
|
+
return {
|
|
16432
|
+
ok: false,
|
|
16433
|
+
message: "Python file could not be validated",
|
|
16434
|
+
issues: [
|
|
16435
|
+
{
|
|
16436
|
+
path: "root",
|
|
16437
|
+
message: error instanceof Error ? error.message : String(error)
|
|
16438
|
+
}
|
|
16439
|
+
]
|
|
16440
|
+
};
|
|
16441
|
+
}
|
|
16442
|
+
const issues = parsed.issues ?? [];
|
|
16443
|
+
if (issues.length > 0) return {
|
|
16444
|
+
ok: false,
|
|
16445
|
+
message: "Python file is invalid for Anvil",
|
|
16446
|
+
issues
|
|
16447
|
+
};
|
|
16448
|
+
return {
|
|
16449
|
+
ok: true
|
|
16450
|
+
};
|
|
16451
|
+
}
|
|
16291
16452
|
const validators_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16292
16453
|
const DB_ACCESS_LEVELS = new Set([
|
|
16293
16454
|
"none",
|
|
@@ -16685,6 +16846,13 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16685
16846
|
const relativeToClientCode = normalizedPath.slice(startIndex + clientCodePrefix.length);
|
|
16686
16847
|
if (relativeToClientCode.endsWith(".yaml")) return "form_template.yaml";
|
|
16687
16848
|
if (relativeToClientCode.endsWith(".html")) return "form_template.html";
|
|
16849
|
+
if (relativeToClientCode.endsWith(".py")) return "python";
|
|
16850
|
+
}
|
|
16851
|
+
const serverCodePrefix = "server_code/";
|
|
16852
|
+
if (normalizedPath.startsWith(serverCodePrefix) || normalizedPath.includes(`/${serverCodePrefix}`)) {
|
|
16853
|
+
const startIndex = normalizedPath.lastIndexOf(serverCodePrefix);
|
|
16854
|
+
const relativeToServerCode = normalizedPath.slice(startIndex + serverCodePrefix.length);
|
|
16855
|
+
if (relativeToServerCode.endsWith(".py")) return "python";
|
|
16688
16856
|
}
|
|
16689
16857
|
return null;
|
|
16690
16858
|
}
|
|
@@ -16792,6 +16960,35 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16792
16960
|
target
|
|
16793
16961
|
};
|
|
16794
16962
|
}
|
|
16963
|
+
if ("python" === target) {
|
|
16964
|
+
if (existingPath && appRoot) {
|
|
16965
|
+
const clientCodeRoot = external_path_default().join(appRoot, "client_code");
|
|
16966
|
+
const serverCodeRoot = external_path_default().join(appRoot, "server_code");
|
|
16967
|
+
const relativeToClientCode = external_path_default().relative(clientCodeRoot, existingPath);
|
|
16968
|
+
const relativeToServerCode = external_path_default().relative(serverCodeRoot, existingPath);
|
|
16969
|
+
const isInsideClientCode = relativeToClientCode.length > 0 && !relativeToClientCode.startsWith("..") && !external_path_default().isAbsolute(relativeToClientCode);
|
|
16970
|
+
const isInsideServerCode = relativeToServerCode.length > 0 && !relativeToServerCode.startsWith("..") && !external_path_default().isAbsolute(relativeToServerCode);
|
|
16971
|
+
if (!isInsideClientCode && !isInsideServerCode || !existingPath.endsWith(".py")) return {
|
|
16972
|
+
ok: false,
|
|
16973
|
+
target,
|
|
16974
|
+
message: "File is not an Anvil app Python file",
|
|
16975
|
+
issues: [
|
|
16976
|
+
{
|
|
16977
|
+
path: "",
|
|
16978
|
+
message: `expected a .py file under ${clientCodeRoot} or ${serverCodeRoot}`
|
|
16979
|
+
}
|
|
16980
|
+
]
|
|
16981
|
+
};
|
|
16982
|
+
}
|
|
16983
|
+
const result = validatePython(filePath, fileContent, appRoot);
|
|
16984
|
+
return result.ok ? {
|
|
16985
|
+
ok: true,
|
|
16986
|
+
target
|
|
16987
|
+
} : {
|
|
16988
|
+
...result,
|
|
16989
|
+
target
|
|
16990
|
+
};
|
|
16991
|
+
}
|
|
16795
16992
|
return {
|
|
16796
16993
|
ok: false,
|
|
16797
16994
|
target: "unknown",
|
|
@@ -16799,7 +16996,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16799
16996
|
issues: [
|
|
16800
16997
|
{
|
|
16801
16998
|
path: "",
|
|
16802
|
-
message: "supported paths are anvil.yaml, client_code/**/form_template.yaml, client_code/**/*.yaml,
|
|
16999
|
+
message: "supported paths are anvil.yaml, client_code/**/form_template.yaml, client_code/**/*.yaml, client_code/**/*.html, client_code/**/*.py, and server_code/**/*.py"
|
|
16803
17000
|
}
|
|
16804
17001
|
]
|
|
16805
17002
|
};
|
|
@@ -18746,6 +18943,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
18746
18943
|
environment;
|
|
18747
18944
|
envBranchChangeMode;
|
|
18748
18945
|
isSwitchingForEnvironment = false;
|
|
18946
|
+
environmentSwitchTargetBranch = null;
|
|
18749
18947
|
dependencyWatchEnabled;
|
|
18750
18948
|
isRefreshingDependencies = false;
|
|
18751
18949
|
hasPendingDependencyRefresh = false;
|
|
@@ -18970,6 +19168,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
18970
19168
|
async switchToEnvironmentBranch(targetBranch, source) {
|
|
18971
19169
|
if (this.isCleanedUp) return;
|
|
18972
19170
|
this.isSwitchingForEnvironment = true;
|
|
19171
|
+
this.environmentSwitchTargetBranch = targetBranch;
|
|
18973
19172
|
const oldPausedState = this.isPausedForUserInput;
|
|
18974
19173
|
try {
|
|
18975
19174
|
const status = await this.gitService.getStatus();
|
|
@@ -19031,6 +19230,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
19031
19230
|
} finally{
|
|
19032
19231
|
this.isPausedForUserInput = oldPausedState;
|
|
19033
19232
|
this.isSwitchingForEnvironment = false;
|
|
19233
|
+
this.environmentSwitchTargetBranch = null;
|
|
19034
19234
|
}
|
|
19035
19235
|
const pendingSwitch = this.pendingEnvironmentBranchSwitch;
|
|
19036
19236
|
this.pendingEnvironmentBranchSwitch = null;
|
|
@@ -19118,16 +19318,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
19118
19318
|
getCurrentBranch: async ()=>this.gitService.getCurrentBranch()
|
|
19119
19319
|
});
|
|
19120
19320
|
this.fileWatcher.on("branch-change", ({ newBranch })=>{
|
|
19121
|
-
|
|
19122
|
-
logger_logger.verbose(external_chalk_default().gray(" Pausing sync for 2 seconds to allow branch to settle..."));
|
|
19123
|
-
this.currentBranch = newBranch;
|
|
19124
|
-
this.lastBranchChangeTime = Date.now();
|
|
19125
|
-
this.wsClient?.updateBranch(newBranch);
|
|
19126
|
-
this.queueDependencyRefresh("branch changed");
|
|
19127
|
-
this.emit("branch-changed", {
|
|
19128
|
-
oldBranch,
|
|
19129
|
-
newBranch
|
|
19130
|
-
});
|
|
19321
|
+
this.handleFileWatcherBranchChange(newBranch);
|
|
19131
19322
|
});
|
|
19132
19323
|
this.fileWatcher.on("index-change", ()=>{
|
|
19133
19324
|
this.saveProcessor?.queueSave();
|
|
@@ -19144,6 +19335,23 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
19144
19335
|
this.startLocalChangeFallbackPolling();
|
|
19145
19336
|
await new Promise(()=>{});
|
|
19146
19337
|
}
|
|
19338
|
+
handleFileWatcherBranchChange(newBranch) {
|
|
19339
|
+
if (this.isSwitchingForEnvironment && newBranch === this.environmentSwitchTargetBranch) return void logger_logger.debug(`Ignoring FileWatcher branch-change during environment switch: ${newBranch}`);
|
|
19340
|
+
const oldBranch = this.currentBranch;
|
|
19341
|
+
logger_logger.verbose(external_chalk_default().gray(" Pausing sync for 2 seconds to allow branch to settle..."));
|
|
19342
|
+
this.currentBranch = newBranch;
|
|
19343
|
+
this.lastBranchChangeTime = Date.now();
|
|
19344
|
+
this.wsClient?.updateBranch(newBranch);
|
|
19345
|
+
this.queueDependencyRefresh("branch changed");
|
|
19346
|
+
this.emit("external-branch-changed", {
|
|
19347
|
+
oldBranch,
|
|
19348
|
+
newBranch
|
|
19349
|
+
});
|
|
19350
|
+
this.emit("branch-changed", {
|
|
19351
|
+
oldBranch,
|
|
19352
|
+
newBranch
|
|
19353
|
+
});
|
|
19354
|
+
}
|
|
19147
19355
|
startWatchingInBackground(onError) {
|
|
19148
19356
|
this.startWatching().catch((error)=>{
|
|
19149
19357
|
this.cleanup();
|
|
@@ -22921,6 +23129,123 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
22921
23129
|
}
|
|
22922
23130
|
}).catch(()=>{});
|
|
22923
23131
|
}
|
|
23132
|
+
function collectValidationFiles(dir) {
|
|
23133
|
+
const files = [];
|
|
23134
|
+
const ignoredDirectories = new Set([
|
|
23135
|
+
".anvil",
|
|
23136
|
+
".git",
|
|
23137
|
+
"node_modules",
|
|
23138
|
+
"__pycache__"
|
|
23139
|
+
]);
|
|
23140
|
+
function walk(currentDir) {
|
|
23141
|
+
for (const entry of (0, external_fs_.readdirSync)(currentDir, {
|
|
23142
|
+
withFileTypes: true
|
|
23143
|
+
})){
|
|
23144
|
+
const entryPath = (0, external_path_namespaceObject.join)(currentDir, entry.name);
|
|
23145
|
+
if (entry.isDirectory()) {
|
|
23146
|
+
if (!ignoredDirectories.has(entry.name)) walk(entryPath);
|
|
23147
|
+
continue;
|
|
23148
|
+
}
|
|
23149
|
+
if (entry.isFile() && inferValidationTarget(entryPath)) files.push(entryPath);
|
|
23150
|
+
}
|
|
23151
|
+
}
|
|
23152
|
+
walk(dir);
|
|
23153
|
+
return files.sort();
|
|
23154
|
+
}
|
|
23155
|
+
function validateSingleFile(file) {
|
|
23156
|
+
let fileContent;
|
|
23157
|
+
try {
|
|
23158
|
+
fileContent = (0, external_fs_.readFileSync)(file, "utf8");
|
|
23159
|
+
} catch (error) {
|
|
23160
|
+
return {
|
|
23161
|
+
file,
|
|
23162
|
+
type: "unknown",
|
|
23163
|
+
valid: false,
|
|
23164
|
+
error: `Failed to read ${file}: ${error.message}`,
|
|
23165
|
+
issues: [
|
|
23166
|
+
{
|
|
23167
|
+
path: "",
|
|
23168
|
+
message: error.message
|
|
23169
|
+
}
|
|
23170
|
+
]
|
|
23171
|
+
};
|
|
23172
|
+
}
|
|
23173
|
+
const result = validatePath(file, fileContent);
|
|
23174
|
+
if (result.ok) return {
|
|
23175
|
+
file,
|
|
23176
|
+
type: result.target,
|
|
23177
|
+
valid: true
|
|
23178
|
+
};
|
|
23179
|
+
return {
|
|
23180
|
+
file,
|
|
23181
|
+
type: result.target,
|
|
23182
|
+
valid: false,
|
|
23183
|
+
error: result.message,
|
|
23184
|
+
issues: result.issues
|
|
23185
|
+
};
|
|
23186
|
+
}
|
|
23187
|
+
function logValidationFailure(fileResult) {
|
|
23188
|
+
logger_logger.error(`${fileResult.file} failed validation: ${fileResult.error ?? "Validation failed"}`);
|
|
23189
|
+
for (const issue of fileResult.issues ?? []){
|
|
23190
|
+
const issuePath = issue.path.length > 0 ? issue.path : "root";
|
|
23191
|
+
logger_logger.error(` - ${issuePath}: ${issue.message}`);
|
|
23192
|
+
}
|
|
23193
|
+
}
|
|
23194
|
+
function handleValidateFile(file) {
|
|
23195
|
+
const fileResult = validateSingleFile(file);
|
|
23196
|
+
if (fileResult.valid) {
|
|
23197
|
+
if (getGlobalOutputConfig().jsonMode) logJsonResult(true, {
|
|
23198
|
+
data: {
|
|
23199
|
+
file: fileResult.file,
|
|
23200
|
+
type: fileResult.type,
|
|
23201
|
+
valid: true
|
|
23202
|
+
}
|
|
23203
|
+
});
|
|
23204
|
+
else logger_logger.success(`${fileResult.file} is valid (${fileResult.type})`);
|
|
23205
|
+
return;
|
|
23206
|
+
}
|
|
23207
|
+
if (getGlobalOutputConfig().jsonMode) logJsonResult(false, {
|
|
23208
|
+
error: fileResult.error,
|
|
23209
|
+
data: {
|
|
23210
|
+
file: fileResult.file,
|
|
23211
|
+
type: fileResult.type,
|
|
23212
|
+
issues: fileResult.issues ?? []
|
|
23213
|
+
}
|
|
23214
|
+
});
|
|
23215
|
+
else logValidationFailure(fileResult);
|
|
23216
|
+
process.exitCode = 1;
|
|
23217
|
+
}
|
|
23218
|
+
function handleValidateDirectory(dir) {
|
|
23219
|
+
const files = collectValidationFiles(dir);
|
|
23220
|
+
if (0 === files.length) {
|
|
23221
|
+
const error = `No supported files found under ${dir}`;
|
|
23222
|
+
if (getGlobalOutputConfig().jsonMode) logJsonResult(false, {
|
|
23223
|
+
error,
|
|
23224
|
+
data: {
|
|
23225
|
+
file: dir,
|
|
23226
|
+
valid: false,
|
|
23227
|
+
files: []
|
|
23228
|
+
}
|
|
23229
|
+
});
|
|
23230
|
+
else logger_logger.error(error);
|
|
23231
|
+
process.exitCode = 1;
|
|
23232
|
+
return;
|
|
23233
|
+
}
|
|
23234
|
+
const fileResults = files.map(validateSingleFile);
|
|
23235
|
+
const failedResults = fileResults.filter((result)=>!result.valid);
|
|
23236
|
+
if (getGlobalOutputConfig().jsonMode) logJsonResult(0 === failedResults.length, {
|
|
23237
|
+
error: failedResults.length > 0 ? "Validation failed" : void 0,
|
|
23238
|
+
data: {
|
|
23239
|
+
file: dir,
|
|
23240
|
+
valid: 0 === failedResults.length,
|
|
23241
|
+
count: fileResults.length,
|
|
23242
|
+
files: fileResults
|
|
23243
|
+
}
|
|
23244
|
+
});
|
|
23245
|
+
else for (const fileResult of fileResults)if (fileResult.valid) logger_logger.success(`${fileResult.file} is valid (${fileResult.type})`);
|
|
23246
|
+
else logValidationFailure(fileResult);
|
|
23247
|
+
if (failedResults.length > 0) process.exitCode = 1;
|
|
23248
|
+
}
|
|
22924
23249
|
function getUpdateInstructions(platform) {
|
|
22925
23250
|
if ("win32" === platform) return [
|
|
22926
23251
|
"try { irm https://anvil.works/install-cli.ps1 | iex } catch { npm install -g @anvil-works/anvil-cli@latest }",
|
|
@@ -23034,43 +23359,17 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
23034
23359
|
program.command("update").description("Update anvil to the latest version").alias("u").action(async ()=>{
|
|
23035
23360
|
await handleUpdateCommand();
|
|
23036
23361
|
});
|
|
23037
|
-
program.command("validate").description("Validate
|
|
23038
|
-
let
|
|
23362
|
+
program.command("validate").description("Validate Anvil app files by path or directory (anvil.yaml, client_code/**/*.yaml, client_code/**/*.html, client_code/**/*.py, or server_code/**/*.py)").argument("<path>", "Path to file or directory").action((file)=>{
|
|
23363
|
+
let pathStat;
|
|
23039
23364
|
try {
|
|
23040
|
-
|
|
23365
|
+
pathStat = (0, external_fs_.statSync)(file);
|
|
23041
23366
|
} catch (error) {
|
|
23042
|
-
logger_logger.error(`Failed to
|
|
23367
|
+
logger_logger.error(`Failed to access ${file}: ${error.message}`);
|
|
23043
23368
|
process.exitCode = 1;
|
|
23044
23369
|
return;
|
|
23045
23370
|
}
|
|
23046
|
-
|
|
23047
|
-
|
|
23048
|
-
if (getGlobalOutputConfig().jsonMode) logJsonResult(true, {
|
|
23049
|
-
data: {
|
|
23050
|
-
file,
|
|
23051
|
-
type: result.target,
|
|
23052
|
-
valid: true
|
|
23053
|
-
}
|
|
23054
|
-
});
|
|
23055
|
-
else logger_logger.success(`${file} is valid (${result.target})`);
|
|
23056
|
-
return;
|
|
23057
|
-
}
|
|
23058
|
-
if (getGlobalOutputConfig().jsonMode) logJsonResult(false, {
|
|
23059
|
-
error: result.message,
|
|
23060
|
-
data: {
|
|
23061
|
-
file,
|
|
23062
|
-
type: result.target,
|
|
23063
|
-
issues: result.issues
|
|
23064
|
-
}
|
|
23065
|
-
});
|
|
23066
|
-
else {
|
|
23067
|
-
logger_logger.error(`${file} failed validation: ${result.message}`);
|
|
23068
|
-
for (const issue of result.issues){
|
|
23069
|
-
const issuePath = issue.path.length > 0 ? issue.path : "root";
|
|
23070
|
-
logger_logger.error(` - ${issuePath}: ${issue.message}`);
|
|
23071
|
-
}
|
|
23072
|
-
}
|
|
23073
|
-
process.exitCode = 1;
|
|
23371
|
+
if (pathStat.isDirectory()) return void handleValidateDirectory(file);
|
|
23372
|
+
handleValidateFile(file);
|
|
23074
23373
|
});
|
|
23075
23374
|
if (watchCommand) {
|
|
23076
23375
|
const watchOptions = watchCommand.options.map((opt)=>{
|
package/dist/index.js
CHANGED
|
@@ -16317,6 +16317,167 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16317
16317
|
ok: true
|
|
16318
16318
|
};
|
|
16319
16319
|
}
|
|
16320
|
+
const PYTHON_VALIDATOR_SCRIPT = String.raw`
|
|
16321
|
+
import ast
|
|
16322
|
+
import json
|
|
16323
|
+
import sys
|
|
16324
|
+
|
|
16325
|
+
|
|
16326
|
+
def issue_path(error):
|
|
16327
|
+
if getattr(error, "lineno", None) is None:
|
|
16328
|
+
return "root"
|
|
16329
|
+
if getattr(error, "offset", None) is None:
|
|
16330
|
+
return f"line {error.lineno}"
|
|
16331
|
+
return f"line {error.lineno}, column {error.offset}"
|
|
16332
|
+
|
|
16333
|
+
|
|
16334
|
+
def base_name(node):
|
|
16335
|
+
if isinstance(node, ast.Name):
|
|
16336
|
+
return node.id
|
|
16337
|
+
if isinstance(node, ast.Attribute):
|
|
16338
|
+
return node.attr
|
|
16339
|
+
return None
|
|
16340
|
+
|
|
16341
|
+
|
|
16342
|
+
def imports_template_from_anvil_designer(node, template_name):
|
|
16343
|
+
if node.level < 1:
|
|
16344
|
+
return False
|
|
16345
|
+
if node.module not in ("_anvil_designer", None):
|
|
16346
|
+
return False
|
|
16347
|
+
return any(alias.name == template_name for alias in node.names)
|
|
16348
|
+
|
|
16349
|
+
|
|
16350
|
+
file_path = sys.argv[1]
|
|
16351
|
+
class_name = sys.argv[2]
|
|
16352
|
+
template_name = f"{class_name}Template" if class_name else ""
|
|
16353
|
+
source = sys.stdin.read()
|
|
16354
|
+
issues = []
|
|
16355
|
+
|
|
16356
|
+
try:
|
|
16357
|
+
compile(source, file_path, "exec")
|
|
16358
|
+
except SyntaxError as error:
|
|
16359
|
+
issues.append({"path": issue_path(error), "message": error.msg})
|
|
16360
|
+
print(json.dumps({"issues": issues}))
|
|
16361
|
+
sys.exit(0)
|
|
16362
|
+
|
|
16363
|
+
if class_name:
|
|
16364
|
+
tree = ast.parse(source, filename=file_path)
|
|
16365
|
+
has_template_import = False
|
|
16366
|
+
has_form_class = False
|
|
16367
|
+
|
|
16368
|
+
for node in ast.walk(tree):
|
|
16369
|
+
if isinstance(node, ast.ImportFrom) and imports_template_from_anvil_designer(node, template_name):
|
|
16370
|
+
has_template_import = True
|
|
16371
|
+
elif isinstance(node, ast.ClassDef) and node.name == class_name:
|
|
16372
|
+
if any(base_name(base) == template_name for base in node.bases):
|
|
16373
|
+
has_form_class = True
|
|
16374
|
+
|
|
16375
|
+
if not has_template_import:
|
|
16376
|
+
issues.append({
|
|
16377
|
+
"path": "root",
|
|
16378
|
+
"message": f"missing {chr(96)}from ._anvil_designer import {template_name}{chr(96)}",
|
|
16379
|
+
})
|
|
16380
|
+
if not has_form_class:
|
|
16381
|
+
issues.append({
|
|
16382
|
+
"path": "root",
|
|
16383
|
+
"message": f"missing class {chr(96)}{class_name}{chr(96)} inheriting from {chr(96)}{template_name}{chr(96)}",
|
|
16384
|
+
})
|
|
16385
|
+
|
|
16386
|
+
print(json.dumps({"issues": issues}))
|
|
16387
|
+
`;
|
|
16388
|
+
function findPythonInterpreter() {
|
|
16389
|
+
const candidates = [
|
|
16390
|
+
process.env.ANVIL_PYTHON,
|
|
16391
|
+
"python3",
|
|
16392
|
+
"python"
|
|
16393
|
+
].filter((candidate)=>"string" == typeof candidate && candidate.length > 0);
|
|
16394
|
+
for (const candidate of candidates){
|
|
16395
|
+
const result = (0, external_child_process_namespaceObject.spawnSync)(candidate, [
|
|
16396
|
+
"--version"
|
|
16397
|
+
], {
|
|
16398
|
+
encoding: "utf8"
|
|
16399
|
+
});
|
|
16400
|
+
if (!result.error && 0 === result.status) return candidate;
|
|
16401
|
+
}
|
|
16402
|
+
return null;
|
|
16403
|
+
}
|
|
16404
|
+
function isClientFormPython(filePath) {
|
|
16405
|
+
if ("__init__.py" === external_path_default().basename(filePath)) {
|
|
16406
|
+
const formDir = external_path_default().dirname(filePath);
|
|
16407
|
+
return external_fs_.existsSync(external_path_default().join(formDir, "form_template.html")) || external_fs_.existsSync(external_path_default().join(formDir, "form_template.yaml"));
|
|
16408
|
+
}
|
|
16409
|
+
const parsed = external_path_default().parse(filePath);
|
|
16410
|
+
return external_fs_.existsSync(external_path_default().join(parsed.dir, `${parsed.name}.html`)) || external_fs_.existsSync(external_path_default().join(parsed.dir, `${parsed.name}.yaml`));
|
|
16411
|
+
}
|
|
16412
|
+
function expectedFormClassName(filePath) {
|
|
16413
|
+
if (!isClientFormPython(filePath)) return null;
|
|
16414
|
+
if ("__init__.py" === external_path_default().basename(filePath)) return external_path_default().basename(external_path_default().dirname(filePath));
|
|
16415
|
+
return external_path_default().basename(filePath, ".py");
|
|
16416
|
+
}
|
|
16417
|
+
function isUnderDirectory(filePath, directoryPath) {
|
|
16418
|
+
const relativePath = external_path_default().relative(directoryPath, filePath);
|
|
16419
|
+
return relativePath.length > 0 && !relativePath.startsWith("..") && !external_path_default().isAbsolute(relativePath);
|
|
16420
|
+
}
|
|
16421
|
+
function validatePython(filePath, fileContent, appRoot) {
|
|
16422
|
+
const interpreter = findPythonInterpreter();
|
|
16423
|
+
if (!interpreter) return {
|
|
16424
|
+
ok: false,
|
|
16425
|
+
message: "Python file could not be validated",
|
|
16426
|
+
issues: [
|
|
16427
|
+
{
|
|
16428
|
+
path: "root",
|
|
16429
|
+
message: "Python validation requires python3 or python on PATH, or ANVIL_PYTHON to be set"
|
|
16430
|
+
}
|
|
16431
|
+
]
|
|
16432
|
+
};
|
|
16433
|
+
const absolutePath = external_path_default().resolve(filePath);
|
|
16434
|
+
const clientCodeRoot = appRoot ? external_path_default().join(appRoot, "client_code") : null;
|
|
16435
|
+
const formClassName = clientCodeRoot && isUnderDirectory(absolutePath, clientCodeRoot) ? expectedFormClassName(absolutePath) : null;
|
|
16436
|
+
const result = (0, external_child_process_namespaceObject.spawnSync)(interpreter, [
|
|
16437
|
+
"-c",
|
|
16438
|
+
PYTHON_VALIDATOR_SCRIPT,
|
|
16439
|
+
filePath,
|
|
16440
|
+
formClassName ?? ""
|
|
16441
|
+
], {
|
|
16442
|
+
encoding: "utf8",
|
|
16443
|
+
input: fileContent,
|
|
16444
|
+
maxBuffer: 1048576
|
|
16445
|
+
});
|
|
16446
|
+
if (result.error || 0 !== result.status) return {
|
|
16447
|
+
ok: false,
|
|
16448
|
+
message: "Python file could not be validated",
|
|
16449
|
+
issues: [
|
|
16450
|
+
{
|
|
16451
|
+
path: "root",
|
|
16452
|
+
message: result.error?.message ?? (result.stderr.trim() || "Python validator failed")
|
|
16453
|
+
}
|
|
16454
|
+
]
|
|
16455
|
+
};
|
|
16456
|
+
let parsed;
|
|
16457
|
+
try {
|
|
16458
|
+
parsed = JSON.parse(result.stdout);
|
|
16459
|
+
} catch (error) {
|
|
16460
|
+
return {
|
|
16461
|
+
ok: false,
|
|
16462
|
+
message: "Python file could not be validated",
|
|
16463
|
+
issues: [
|
|
16464
|
+
{
|
|
16465
|
+
path: "root",
|
|
16466
|
+
message: error instanceof Error ? error.message : String(error)
|
|
16467
|
+
}
|
|
16468
|
+
]
|
|
16469
|
+
};
|
|
16470
|
+
}
|
|
16471
|
+
const issues = parsed.issues ?? [];
|
|
16472
|
+
if (issues.length > 0) return {
|
|
16473
|
+
ok: false,
|
|
16474
|
+
message: "Python file is invalid for Anvil",
|
|
16475
|
+
issues
|
|
16476
|
+
};
|
|
16477
|
+
return {
|
|
16478
|
+
ok: true
|
|
16479
|
+
};
|
|
16480
|
+
}
|
|
16320
16481
|
const validators_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16321
16482
|
const DB_ACCESS_LEVELS = new Set([
|
|
16322
16483
|
"none",
|
|
@@ -16714,6 +16875,13 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16714
16875
|
const relativeToClientCode = normalizedPath.slice(startIndex + clientCodePrefix.length);
|
|
16715
16876
|
if (relativeToClientCode.endsWith(".yaml")) return "form_template.yaml";
|
|
16716
16877
|
if (relativeToClientCode.endsWith(".html")) return "form_template.html";
|
|
16878
|
+
if (relativeToClientCode.endsWith(".py")) return "python";
|
|
16879
|
+
}
|
|
16880
|
+
const serverCodePrefix = "server_code/";
|
|
16881
|
+
if (normalizedPath.startsWith(serverCodePrefix) || normalizedPath.includes(`/${serverCodePrefix}`)) {
|
|
16882
|
+
const startIndex = normalizedPath.lastIndexOf(serverCodePrefix);
|
|
16883
|
+
const relativeToServerCode = normalizedPath.slice(startIndex + serverCodePrefix.length);
|
|
16884
|
+
if (relativeToServerCode.endsWith(".py")) return "python";
|
|
16717
16885
|
}
|
|
16718
16886
|
return null;
|
|
16719
16887
|
}
|
|
@@ -16821,6 +16989,35 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16821
16989
|
target
|
|
16822
16990
|
};
|
|
16823
16991
|
}
|
|
16992
|
+
if ("python" === target) {
|
|
16993
|
+
if (existingPath && appRoot) {
|
|
16994
|
+
const clientCodeRoot = external_path_default().join(appRoot, "client_code");
|
|
16995
|
+
const serverCodeRoot = external_path_default().join(appRoot, "server_code");
|
|
16996
|
+
const relativeToClientCode = external_path_default().relative(clientCodeRoot, existingPath);
|
|
16997
|
+
const relativeToServerCode = external_path_default().relative(serverCodeRoot, existingPath);
|
|
16998
|
+
const isInsideClientCode = relativeToClientCode.length > 0 && !relativeToClientCode.startsWith("..") && !external_path_default().isAbsolute(relativeToClientCode);
|
|
16999
|
+
const isInsideServerCode = relativeToServerCode.length > 0 && !relativeToServerCode.startsWith("..") && !external_path_default().isAbsolute(relativeToServerCode);
|
|
17000
|
+
if (!isInsideClientCode && !isInsideServerCode || !existingPath.endsWith(".py")) return {
|
|
17001
|
+
ok: false,
|
|
17002
|
+
target,
|
|
17003
|
+
message: "File is not an Anvil app Python file",
|
|
17004
|
+
issues: [
|
|
17005
|
+
{
|
|
17006
|
+
path: "",
|
|
17007
|
+
message: `expected a .py file under ${clientCodeRoot} or ${serverCodeRoot}`
|
|
17008
|
+
}
|
|
17009
|
+
]
|
|
17010
|
+
};
|
|
17011
|
+
}
|
|
17012
|
+
const result = validatePython(filePath, fileContent, appRoot);
|
|
17013
|
+
return result.ok ? {
|
|
17014
|
+
ok: true,
|
|
17015
|
+
target
|
|
17016
|
+
} : {
|
|
17017
|
+
...result,
|
|
17018
|
+
target
|
|
17019
|
+
};
|
|
17020
|
+
}
|
|
16824
17021
|
return {
|
|
16825
17022
|
ok: false,
|
|
16826
17023
|
target: "unknown",
|
|
@@ -16828,7 +17025,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
16828
17025
|
issues: [
|
|
16829
17026
|
{
|
|
16830
17027
|
path: "",
|
|
16831
|
-
message: "supported paths are anvil.yaml, client_code/**/form_template.yaml, client_code/**/*.yaml,
|
|
17028
|
+
message: "supported paths are anvil.yaml, client_code/**/form_template.yaml, client_code/**/*.yaml, client_code/**/*.html, client_code/**/*.py, and server_code/**/*.py"
|
|
16832
17029
|
}
|
|
16833
17030
|
]
|
|
16834
17031
|
};
|
|
@@ -18775,6 +18972,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
18775
18972
|
environment;
|
|
18776
18973
|
envBranchChangeMode;
|
|
18777
18974
|
isSwitchingForEnvironment = false;
|
|
18975
|
+
environmentSwitchTargetBranch = null;
|
|
18778
18976
|
dependencyWatchEnabled;
|
|
18779
18977
|
isRefreshingDependencies = false;
|
|
18780
18978
|
hasPendingDependencyRefresh = false;
|
|
@@ -18999,6 +19197,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
18999
19197
|
async switchToEnvironmentBranch(targetBranch, source) {
|
|
19000
19198
|
if (this.isCleanedUp) return;
|
|
19001
19199
|
this.isSwitchingForEnvironment = true;
|
|
19200
|
+
this.environmentSwitchTargetBranch = targetBranch;
|
|
19002
19201
|
const oldPausedState = this.isPausedForUserInput;
|
|
19003
19202
|
try {
|
|
19004
19203
|
const status = await this.gitService.getStatus();
|
|
@@ -19060,6 +19259,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
19060
19259
|
} finally{
|
|
19061
19260
|
this.isPausedForUserInput = oldPausedState;
|
|
19062
19261
|
this.isSwitchingForEnvironment = false;
|
|
19262
|
+
this.environmentSwitchTargetBranch = null;
|
|
19063
19263
|
}
|
|
19064
19264
|
const pendingSwitch = this.pendingEnvironmentBranchSwitch;
|
|
19065
19265
|
this.pendingEnvironmentBranchSwitch = null;
|
|
@@ -19147,16 +19347,7 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
19147
19347
|
getCurrentBranch: async ()=>this.gitService.getCurrentBranch()
|
|
19148
19348
|
});
|
|
19149
19349
|
this.fileWatcher.on("branch-change", ({ newBranch })=>{
|
|
19150
|
-
|
|
19151
|
-
logger_logger.verbose(external_chalk_default().gray(" Pausing sync for 2 seconds to allow branch to settle..."));
|
|
19152
|
-
this.currentBranch = newBranch;
|
|
19153
|
-
this.lastBranchChangeTime = Date.now();
|
|
19154
|
-
this.wsClient?.updateBranch(newBranch);
|
|
19155
|
-
this.queueDependencyRefresh("branch changed");
|
|
19156
|
-
this.emit("branch-changed", {
|
|
19157
|
-
oldBranch,
|
|
19158
|
-
newBranch
|
|
19159
|
-
});
|
|
19350
|
+
this.handleFileWatcherBranchChange(newBranch);
|
|
19160
19351
|
});
|
|
19161
19352
|
this.fileWatcher.on("index-change", ()=>{
|
|
19162
19353
|
this.saveProcessor?.queueSave();
|
|
@@ -19173,6 +19364,23 @@ Promise.resolve(executeGitCredentialOperation(process.argv[2] || "get", {
|
|
|
19173
19364
|
this.startLocalChangeFallbackPolling();
|
|
19174
19365
|
await new Promise(()=>{});
|
|
19175
19366
|
}
|
|
19367
|
+
handleFileWatcherBranchChange(newBranch) {
|
|
19368
|
+
if (this.isSwitchingForEnvironment && newBranch === this.environmentSwitchTargetBranch) return void logger_logger.debug(`Ignoring FileWatcher branch-change during environment switch: ${newBranch}`);
|
|
19369
|
+
const oldBranch = this.currentBranch;
|
|
19370
|
+
logger_logger.verbose(external_chalk_default().gray(" Pausing sync for 2 seconds to allow branch to settle..."));
|
|
19371
|
+
this.currentBranch = newBranch;
|
|
19372
|
+
this.lastBranchChangeTime = Date.now();
|
|
19373
|
+
this.wsClient?.updateBranch(newBranch);
|
|
19374
|
+
this.queueDependencyRefresh("branch changed");
|
|
19375
|
+
this.emit("external-branch-changed", {
|
|
19376
|
+
oldBranch,
|
|
19377
|
+
newBranch
|
|
19378
|
+
});
|
|
19379
|
+
this.emit("branch-changed", {
|
|
19380
|
+
oldBranch,
|
|
19381
|
+
newBranch
|
|
19382
|
+
});
|
|
19383
|
+
}
|
|
19176
19384
|
startWatchingInBackground(onError) {
|
|
19177
19385
|
this.startWatching().catch((error)=>{
|
|
19178
19386
|
this.cleanup();
|
package/dist/program.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2LpC,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,EAAE,CAezE;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAwFzD;AAiBD,wBAAgB,YAAY,IAAI,OAAO,CAkGtC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ValidationIssue } from "./validators";
|
|
2
|
+
export type PythonValidationResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
message: string;
|
|
7
|
+
issues: ValidationIssue[];
|
|
8
|
+
};
|
|
9
|
+
export declare function validatePython(filePath: string, fileContent: string, appRoot: string | null): PythonValidationResult;
|
|
10
|
+
//# sourceMappingURL=validatePython.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validatePython.d.ts","sourceRoot":"","sources":["../src/validatePython.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,MAAM,sBAAsB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IACI,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAqHR,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,sBAAsB,CAiEpH"}
|
package/dist/validators.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type AnvilYamlValidationResult = {
|
|
|
16
16
|
message: string;
|
|
17
17
|
issues: ValidationIssue[];
|
|
18
18
|
};
|
|
19
|
-
export type ValidationTarget = "anvil.yaml" | "form_template.yaml" | "form_template.html";
|
|
19
|
+
export type ValidationTarget = "anvil.yaml" | "form_template.yaml" | "form_template.html" | "python";
|
|
20
20
|
export type ValidatePathResult = {
|
|
21
21
|
ok: true;
|
|
22
22
|
target: ValidationTarget;
|
|
@@ -43,6 +43,7 @@ export declare function findAnvilAppRoot(filePath: string): string | null;
|
|
|
43
43
|
* - `anvil.yaml`
|
|
44
44
|
* - any form YAML under `client_code/` with a `.yaml` suffix
|
|
45
45
|
* - any form HTML under `client_code/` with a `.html` suffix
|
|
46
|
+
* - any Python file under `client_code/` or `server_code/`
|
|
46
47
|
*/
|
|
47
48
|
export declare function validatePath(filePath: string, fileContent: string): ValidatePathResult;
|
|
48
49
|
//# sourceMappingURL=validators.d.ts.map
|
package/dist/validators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,eAAe,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAClC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IACI,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAER,MAAM,MAAM,yBAAyB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IACI,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAER,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,oBAAoB,GAAG,oBAAoB,GAAG,QAAQ,CAAC;AAErG,MAAM,MAAM,kBAAkB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GACtC;IACI,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAoER,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,UAEhD;AAgBD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,4BAA4B,CA0BtF;AAuZD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,yBAAyB,CAkFhF;AAED,iBAAS,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CA4BxE;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAC;AAOjC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBhE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAoItF"}
|