swallowkit 2.0.0-alpha.2 → 2.0.0-alpha.3
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/cli/commands/dev.d.ts +2 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +39 -27
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +27 -118
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +4 -1
- package/dist/core/project/agent-instructions.js.map +1 -1
- package/dist/core/project/home-page.d.ts +4 -0
- package/dist/core/project/home-page.d.ts.map +1 -0
- package/dist/core/project/home-page.js +113 -0
- package/dist/core/project/home-page.js.map +1 -0
- package/dist/core/project/react-project.d.ts +1 -1
- package/dist/core/project/react-project.d.ts.map +1 -1
- package/dist/core/project/react-project.js +45 -24
- package/dist/core/project/react-project.js.map +1 -1
- package/dist/core/scaffold/auth-generator.js +8 -8
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +6 -16
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/utils/package-manager.js +1 -1
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +5 -7
- package/dist/utils/python-uv.d.ts.map +1 -1
- package/dist/utils/python-uv.js +50 -24
- package/dist/utils/python-uv.js.map +1 -1
- package/dist/utils/react-dev.d.ts +4 -0
- package/dist/utils/react-dev.d.ts.map +1 -0
- package/dist/utils/react-dev.js +35 -0
- package/dist/utils/react-dev.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/auth.test.ts +9 -0
- package/src/__tests__/dev.test.ts +13 -6
- package/src/__tests__/home-page.test.ts +50 -0
- package/src/__tests__/init.test.ts +57 -0
- package/src/__tests__/package-manager.test.ts +2 -2
- package/src/__tests__/plan-apply.test.ts +9 -0
- package/src/__tests__/python-uv.test.ts +50 -14
- package/src/__tests__/python-venv.test.ts +48 -0
- package/src/__tests__/react-dev.test.ts +72 -0
- package/src/__tests__/react.test.ts +19 -0
- package/src/cli/commands/dev.ts +40 -31
- package/src/cli/commands/init.ts +29 -121
- package/src/core/project/agent-instructions.ts +4 -1
- package/src/core/project/home-page.ts +114 -0
- package/src/core/project/react-project.ts +49 -27
- package/src/core/scaffold/auth-generator.ts +8 -8
- package/src/core/scaffold/native-schema-generator.ts +8 -27
- package/src/utils/package-manager.ts +1 -1
- package/src/utils/python-uv.ts +53 -33
- package/src/utils/react-dev.ts +31 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { BackendLanguage } from "../../types";
|
|
4
|
+
import { buildReactDevWatchIgnoredSource } from "../../utils/react-dev";
|
|
5
|
+
import { DEFAULT_AZURE_PYTHON_VERSION } from "../../utils/python-uv";
|
|
6
|
+
import { buildHomePage } from "./home-page";
|
|
4
7
|
import {
|
|
5
8
|
PackageManager,
|
|
6
9
|
getCommands,
|
|
@@ -45,7 +48,8 @@ export function buildReactSwaConfig() {
|
|
|
45
48
|
|
|
46
49
|
export function createReactProject(
|
|
47
50
|
projectDir: string,
|
|
48
|
-
projectName: string
|
|
51
|
+
projectName: string,
|
|
52
|
+
pm: PackageManager = "pnpm"
|
|
49
53
|
): void {
|
|
50
54
|
write(
|
|
51
55
|
projectDir,
|
|
@@ -117,7 +121,11 @@ export default defineConfig({
|
|
|
117
121
|
'@': fileURLToPath(new URL('.', import.meta.url)),
|
|
118
122
|
'@${projectName}/shared': fileURLToPath(new URL('./shared/index.ts', import.meta.url)),
|
|
119
123
|
} },
|
|
120
|
-
server: {
|
|
124
|
+
server: {
|
|
125
|
+
port: 3000,
|
|
126
|
+
strictPort: true,
|
|
127
|
+
watch: { ignored: ${buildReactDevWatchIgnoredSource("fileURLToPath(new URL('.', import.meta.url))")} },
|
|
128
|
+
},
|
|
121
129
|
build: { outDir: 'dist' },
|
|
122
130
|
});
|
|
123
131
|
`
|
|
@@ -126,9 +134,9 @@ export default defineConfig({
|
|
|
126
134
|
projectDir,
|
|
127
135
|
"index.html",
|
|
128
136
|
`<!doctype html>
|
|
129
|
-
<html lang="en">
|
|
137
|
+
<html lang="en" class="h-full antialiased">
|
|
130
138
|
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>SwallowKit</title></head>
|
|
131
|
-
<body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body>
|
|
139
|
+
<body class="min-h-full flex flex-col"><div id="root" class="flex flex-1 flex-col"></div><script type="module" src="/src/main.tsx"></script></body>
|
|
132
140
|
</html>
|
|
133
141
|
`
|
|
134
142
|
);
|
|
@@ -169,31 +177,34 @@ export default function App() {
|
|
|
169
177
|
write(
|
|
170
178
|
projectDir,
|
|
171
179
|
"src\\Home.tsx",
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
buildHomePage("react", pm)
|
|
181
|
+
);
|
|
182
|
+
write(projectDir, "src\\style.css", `@import "tailwindcss";
|
|
183
|
+
|
|
184
|
+
:root {
|
|
185
|
+
--background: #ffffff;
|
|
186
|
+
--foreground: #171717;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@theme inline {
|
|
190
|
+
--color-background: var(--background);
|
|
191
|
+
--color-foreground: var(--foreground);
|
|
192
|
+
--font-sans: Arial, Helvetica, sans-serif;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@media (prefers-color-scheme: dark) {
|
|
196
|
+
:root {
|
|
197
|
+
--background: #0a0a0a;
|
|
198
|
+
--foreground: #ededed;
|
|
185
199
|
}
|
|
186
|
-
return <main className="container mx-auto p-8">
|
|
187
|
-
<h1 className="text-3xl font-bold mb-4">SwallowKit + React</h1>
|
|
188
|
-
<p>React SPA → SWA managed Functions BFF → standalone Functions → Cosmos DB</p>
|
|
189
|
-
<button className="my-4 bg-blue-600 text-white rounded px-4 py-2" onClick={testConnection}>Test backend connection</button>
|
|
190
|
-
<p role="status">{message}</p>
|
|
191
|
-
<nav>{scaffoldConfig.models.map(model => <p key={model.name}><Link to={model.path}>{model.label}</Link></p>)}</nav>
|
|
192
|
-
</main>;
|
|
193
200
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
|
|
202
|
+
body {
|
|
203
|
+
background: var(--background);
|
|
204
|
+
color: var(--foreground);
|
|
205
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
206
|
+
}
|
|
207
|
+
`);
|
|
197
208
|
write(
|
|
198
209
|
projectDir,
|
|
199
210
|
"lib\\scaffold-config.ts",
|
|
@@ -375,6 +386,17 @@ The BFF stays separate even with \`--no-functions\`; \`--no-swa\` starts only th
|
|
|
375
386
|
direct Vite/BFF servers (no same-origin API routing or SWA authentication).
|
|
376
387
|
Core Tools v4 must already be available. Start Cosmos DB Emulator manually if
|
|
377
388
|
this application needs Cosmos; SwallowKit never starts it.
|
|
389
|
+
${backend === "python" ? `
|
|
390
|
+
Python environments use normal uv discovery and user settings, including installed
|
|
391
|
+
Python, \`.python-version\`, and \`UV_PYTHON\`. Local Python is not pinned to the Azure
|
|
392
|
+
deployment runtime (${DEFAULT_AZURE_PYTHON_VERSION}). Functions uses \`functions/.venv\`; scaffold uses
|
|
393
|
+
\`functions/.codegen-venv\`. Existing environments are never cleared automatically.
|
|
394
|
+
When switching interpreters, explicitly recreate them with uv. Do not disable
|
|
395
|
+
Windows application-control policy to run an unsigned interpreter.
|
|
396
|
+
` : backend === "csharp" ? `
|
|
397
|
+
The .NET isolated backend starts with \`dotnet run\` so extensions are loaded from
|
|
398
|
+
the build output. Vite does not watch \`functions/\` or \`bff/\` build artifacts.
|
|
399
|
+
` : ""}
|
|
378
400
|
|
|
379
401
|
Local BFF settings live in \`bff/local.settings.json\` (gitignored). Backend settings
|
|
380
402
|
live in \`functions/local.settings.json\`. Keep their secrets out of source control.
|
|
@@ -1161,17 +1161,17 @@ export default function LoginPage() {
|
|
|
1161
1161
|
};
|
|
1162
1162
|
|
|
1163
1163
|
return (
|
|
1164
|
-
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
1165
|
-
<div className="w-full max-w-md p-8 bg-white rounded-lg shadow-md">
|
|
1166
|
-
<h1 className="text-2xl font-bold text-center mb-6">ログイン</h1>
|
|
1164
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
|
|
1165
|
+
<div className="w-full max-w-md p-8 bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
|
1166
|
+
<h1 className="text-2xl font-bold text-center mb-6 text-gray-900 dark:text-gray-100">ログイン</h1>
|
|
1167
1167
|
{error && (
|
|
1168
|
-
<div className="mb-4 p-3 bg-red-50 border border-red-200 text-red-700 rounded text-sm">
|
|
1168
|
+
<div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-300 rounded text-sm">
|
|
1169
1169
|
{error}
|
|
1170
1170
|
</div>
|
|
1171
1171
|
)}
|
|
1172
1172
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
1173
1173
|
<div>
|
|
1174
|
-
<label htmlFor="loginId" className="block text-sm font-medium text-gray-700 mb-1">
|
|
1174
|
+
<label htmlFor="loginId" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
1175
1175
|
ログインID
|
|
1176
1176
|
</label>
|
|
1177
1177
|
<input
|
|
@@ -1179,13 +1179,13 @@ export default function LoginPage() {
|
|
|
1179
1179
|
type="text"
|
|
1180
1180
|
value={loginId}
|
|
1181
1181
|
onChange={(e) => setLoginId(e.target.value)}
|
|
1182
|
-
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1182
|
+
className="w-full px-3 py-2 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1183
1183
|
required
|
|
1184
1184
|
autoFocus
|
|
1185
1185
|
/>
|
|
1186
1186
|
</div>
|
|
1187
1187
|
<div>
|
|
1188
|
-
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
|
|
1188
|
+
<label htmlFor="password" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
1189
1189
|
パスワード
|
|
1190
1190
|
</label>
|
|
1191
1191
|
<input
|
|
@@ -1193,7 +1193,7 @@ export default function LoginPage() {
|
|
|
1193
1193
|
type="password"
|
|
1194
1194
|
value={password}
|
|
1195
1195
|
onChange={(e) => setPassword(e.target.value)}
|
|
1196
|
-
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1196
|
+
className="w-full px-3 py-2 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1197
1197
|
required
|
|
1198
1198
|
/>
|
|
1199
1199
|
</div>
|
|
@@ -7,10 +7,10 @@ import { FileOperationSession } from "../operations/file-session";
|
|
|
7
7
|
import { ModelInfo, toKebabCase } from "./model-parser";
|
|
8
8
|
import { generateOpenApiDocument } from "./openapi-generator";
|
|
9
9
|
import {
|
|
10
|
-
buildProjectLocalUvEnv,
|
|
11
10
|
buildProjectLocalUvInstallerEnv,
|
|
12
11
|
buildUvPipInstallArgs,
|
|
13
|
-
|
|
12
|
+
buildPythonCodegenInstallEnv,
|
|
13
|
+
ensureUvVirtualEnvironment,
|
|
14
14
|
getProjectLocalUvInstallerCommand,
|
|
15
15
|
getProjectLocalUvPaths,
|
|
16
16
|
getPythonProjectRoot,
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
export const NSWAG_CONSOLECORE_VERSION = "14.7.1";
|
|
20
20
|
export const PYTHON_SCHEMA_CODEGEN_REQUIREMENT = "datamodel-code-generator>=0.44.0,<1.0.0";
|
|
21
21
|
const PYTHON_OUTPUT_MODEL_TYPE = "pydantic_v2.BaseModel";
|
|
22
|
+
// Preserve model syntax compatibility with existing deployments, not the tool's interpreter.
|
|
23
|
+
const PYTHON_MODEL_SYNTAX_VERSION = "3.11";
|
|
22
24
|
|
|
23
25
|
function getMachineAwareStdio(): "inherit" | "pipe" {
|
|
24
26
|
return process.env.SWALLOWKIT_MACHINE_OUTPUT === "1" ? "pipe" : "inherit";
|
|
@@ -201,7 +203,7 @@ export function getPythonNativeGeneratorArgs(specPath: string, outputPath: strin
|
|
|
201
203
|
"--output-model-type",
|
|
202
204
|
PYTHON_OUTPUT_MODEL_TYPE,
|
|
203
205
|
"--target-python-version",
|
|
204
|
-
|
|
206
|
+
PYTHON_MODEL_SYNTAX_VERSION,
|
|
205
207
|
"--disable-timestamp",
|
|
206
208
|
"--use-union-operator",
|
|
207
209
|
"--collapse-root-models",
|
|
@@ -724,14 +726,8 @@ function ensurePythonCodegenProjectFiles(
|
|
|
724
726
|
return requirementsPath;
|
|
725
727
|
}
|
|
726
728
|
|
|
727
|
-
function getVirtualEnvPythonPath(venvDir: string): string {
|
|
728
|
-
return process.platform === "win32"
|
|
729
|
-
? path.join(venvDir, "Scripts", "python.exe")
|
|
730
|
-
: path.join(venvDir, "bin", "python");
|
|
731
|
-
}
|
|
732
|
-
|
|
733
729
|
async function ensureProjectLocalUvCommand(projectRoot: string): Promise<{ command: string; env: NodeJS.ProcessEnv }> {
|
|
734
|
-
const uvEnv =
|
|
730
|
+
const uvEnv = { ...process.env };
|
|
735
731
|
const { localUvExecutable } = getProjectLocalUvPaths(projectRoot);
|
|
736
732
|
|
|
737
733
|
if (canRun("uv", ["--version"], projectRoot)) {
|
|
@@ -762,22 +758,7 @@ async function ensurePythonCodegenEnvironment(functionsRoot: string, requirement
|
|
|
762
758
|
const projectRoot = getPythonProjectRoot(functionsRoot);
|
|
763
759
|
const { command: uvCommand, env: uvEnv } = await ensureProjectLocalUvCommand(projectRoot);
|
|
764
760
|
const venvDir = path.join(functionsRoot, ".codegen-venv");
|
|
765
|
-
const venvPython =
|
|
766
|
-
|
|
767
|
-
if (!canRun(venvPython, ["--version"], functionsRoot, uvEnv)) {
|
|
768
|
-
const venvArgs = buildUvVenvArgs(venvDir);
|
|
769
|
-
if (fs.existsSync(venvDir)) {
|
|
770
|
-
venvArgs.push("--clear");
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
await runCommand(
|
|
774
|
-
uvCommand,
|
|
775
|
-
venvArgs,
|
|
776
|
-
functionsRoot,
|
|
777
|
-
"Failed to create the Python schema code generation virtual environment.",
|
|
778
|
-
uvEnv
|
|
779
|
-
);
|
|
780
|
-
}
|
|
761
|
+
const venvPython = await ensureUvVirtualEnvironment(uvCommand, venvDir, functionsRoot, uvEnv);
|
|
781
762
|
|
|
782
763
|
if (!canRun(venvPython, ["-c", "import datamodel_code_generator"], functionsRoot, uvEnv)) {
|
|
783
764
|
await runCommand(
|
|
@@ -785,7 +766,7 @@ async function ensurePythonCodegenEnvironment(functionsRoot: string, requirement
|
|
|
785
766
|
buildUvPipInstallArgs(venvPython, requirementsPath),
|
|
786
767
|
functionsRoot,
|
|
787
768
|
"Failed to install Python schema generation dependencies.",
|
|
788
|
-
uvEnv
|
|
769
|
+
buildPythonCodegenInstallEnv(uvEnv)
|
|
789
770
|
);
|
|
790
771
|
}
|
|
791
772
|
|
package/src/utils/python-uv.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import { execFile } from "child_process";
|
|
4
|
+
import { promisify } from "util";
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export const DEFAULT_AZURE_PYTHON_VERSION = "3.14";
|
|
4
8
|
|
|
5
9
|
export interface ProjectLocalUvPaths {
|
|
6
10
|
stateDir: string;
|
|
7
|
-
cacheDir: string;
|
|
8
|
-
pythonInstallDir: string;
|
|
9
|
-
toolDir: string;
|
|
10
|
-
toolBinDir: string;
|
|
11
11
|
localUvInstallDir: string;
|
|
12
12
|
localUvExecutable: string;
|
|
13
13
|
}
|
|
@@ -22,10 +22,6 @@ export function getProjectLocalUvPaths(projectRoot: string): ProjectLocalUvPaths
|
|
|
22
22
|
|
|
23
23
|
return {
|
|
24
24
|
stateDir,
|
|
25
|
-
cacheDir: path.join(stateDir, "cache"),
|
|
26
|
-
pythonInstallDir: path.join(stateDir, "python"),
|
|
27
|
-
toolDir: path.join(stateDir, "tools"),
|
|
28
|
-
toolBinDir: path.join(stateDir, "tools", "bin"),
|
|
29
25
|
localUvInstallDir,
|
|
30
26
|
localUvExecutable: process.platform === "win32"
|
|
31
27
|
? path.join(localUvInstallDir, "uv.exe")
|
|
@@ -33,40 +29,26 @@ export function getProjectLocalUvPaths(projectRoot: string): ProjectLocalUvPaths
|
|
|
33
29
|
};
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
export function
|
|
32
|
+
export function buildProjectLocalUvInstallerEnv(
|
|
37
33
|
baseEnv: NodeJS.ProcessEnv,
|
|
38
34
|
projectRoot: string
|
|
39
35
|
): NodeJS.ProcessEnv {
|
|
40
36
|
const uvPaths = getProjectLocalUvPaths(projectRoot);
|
|
37
|
+
|
|
41
38
|
const env: NodeJS.ProcessEnv = {
|
|
42
39
|
...baseEnv,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
UV_TOOL_DIR: uvPaths.toolDir,
|
|
46
|
-
UV_TOOL_BIN_DIR: uvPaths.toolBinDir,
|
|
47
|
-
UV_PYTHON_PREFERENCE: "only-managed",
|
|
40
|
+
UV_UNMANAGED_INSTALL: uvPaths.localUvInstallDir,
|
|
41
|
+
UV_NO_MODIFY_PATH: "1",
|
|
48
42
|
};
|
|
49
|
-
|
|
43
|
+
// Windows PowerShell must discover its own modules, not inherit PowerShell 7's.
|
|
50
44
|
if (process.platform === "win32") {
|
|
51
|
-
env
|
|
45
|
+
for (const key of Object.keys(env)) {
|
|
46
|
+
if (key.toLowerCase() === "psmodulepath") delete env[key];
|
|
47
|
+
}
|
|
52
48
|
}
|
|
53
|
-
|
|
54
49
|
return env;
|
|
55
50
|
}
|
|
56
51
|
|
|
57
|
-
export function buildProjectLocalUvInstallerEnv(
|
|
58
|
-
baseEnv: NodeJS.ProcessEnv,
|
|
59
|
-
projectRoot: string
|
|
60
|
-
): NodeJS.ProcessEnv {
|
|
61
|
-
const uvPaths = getProjectLocalUvPaths(projectRoot);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
...buildProjectLocalUvEnv(baseEnv, projectRoot),
|
|
65
|
-
UV_UNMANAGED_INSTALL: uvPaths.localUvInstallDir,
|
|
66
|
-
UV_NO_MODIFY_PATH: "1",
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
52
|
export function getProjectLocalUvInstallerCommand(): { command: string; args: string[] } {
|
|
71
53
|
if (process.platform === "win32") {
|
|
72
54
|
return {
|
|
@@ -87,10 +69,48 @@ export function getProjectLocalUvInstallerCommand(): { command: string; args: st
|
|
|
87
69
|
};
|
|
88
70
|
}
|
|
89
71
|
|
|
90
|
-
export function buildUvVenvArgs(venvDir: string
|
|
91
|
-
return ["venv", venvDir
|
|
72
|
+
export function buildUvVenvArgs(venvDir: string): string[] {
|
|
73
|
+
return ["venv", venvDir];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getVirtualEnvPythonPath(venvDir: string): string {
|
|
77
|
+
return process.platform === "win32"
|
|
78
|
+
? path.join(venvDir, "Scripts", "python.exe")
|
|
79
|
+
: path.join(venvDir, "bin", "python");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function ensureUvVirtualEnvironment(
|
|
83
|
+
uvCommand: string,
|
|
84
|
+
venvDir: string,
|
|
85
|
+
cwd: string,
|
|
86
|
+
env: NodeJS.ProcessEnv
|
|
87
|
+
): Promise<string> {
|
|
88
|
+
const pythonExecutable = getVirtualEnvPythonPath(venvDir);
|
|
89
|
+
if (!fs.existsSync(venvDir)) {
|
|
90
|
+
const { stdout, stderr } = await execFileAsync(uvCommand, buildUvVenvArgs(venvDir), { cwd, env });
|
|
91
|
+
if (env.SWALLOWKIT_MACHINE_OUTPUT !== "1") {
|
|
92
|
+
if (stdout) process.stdout.write(stdout);
|
|
93
|
+
if (stderr) process.stderr.write(stderr);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
await execFileAsync(pythonExecutable, ["--version"], { cwd, env });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Cannot execute Python at ${pythonExecutable}: ${reason}\n` +
|
|
102
|
+
"The virtual environment was preserved. Check the interpreter and OS application-control policy. " +
|
|
103
|
+
"To switch Python, explicitly recreate this environment with uv and a trusted interpreter; SwallowKit will not clear it automatically."
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return pythonExecutable;
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
export function buildUvPipInstallArgs(pythonExecutable: string, requirementsPath: string): string[] {
|
|
95
110
|
return ["pip", "install", "--python", pythonExecutable, "-r", requirementsPath];
|
|
96
111
|
}
|
|
112
|
+
|
|
113
|
+
export function buildPythonCodegenInstallEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
114
|
+
// Codegen reads JSON; PyYAML's optional accelerator must not require a C compiler.
|
|
115
|
+
return { ...env, PYYAML_FORCE_LIBYAML: env.PYYAML_FORCE_LIBYAML ?? "0" };
|
|
116
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const REACT_DEV_WATCH_DIRECTORIES = [
|
|
2
|
+
"functions",
|
|
3
|
+
"bff",
|
|
4
|
+
".uv",
|
|
5
|
+
".swallowkit",
|
|
6
|
+
"shared/dist",
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export function buildReactDevWatchIgnoredSource(rootExpression: string): string {
|
|
10
|
+
return `${JSON.stringify(REACT_DEV_WATCH_DIRECTORIES)}.map(directory => \`\${${rootExpression}.replace(/\\\\/g, '/').replace(/\\/$/, '')}/\${directory}/**\`)`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function buildReactDevConfigSource(): string {
|
|
14
|
+
return `import { defineConfig, loadConfigFromFile, mergeConfig } from 'vite';
|
|
15
|
+
|
|
16
|
+
export default defineConfig(async env => {
|
|
17
|
+
const loaded = await loadConfigFromFile(env, undefined, process.cwd());
|
|
18
|
+
const config = loaded?.config ?? {};
|
|
19
|
+
if (config.server?.watch === null) return config;
|
|
20
|
+
return mergeConfig(config, {
|
|
21
|
+
plugins: [{
|
|
22
|
+
name: 'swallowkit-dev-watch-boundaries',
|
|
23
|
+
configResolved(resolved) {
|
|
24
|
+
resolved.configFileDependencies.push(...(loaded?.dependencies ?? []));
|
|
25
|
+
},
|
|
26
|
+
}],
|
|
27
|
+
server: { watch: { ignored: ${buildReactDevWatchIgnoredSource("process.cwd()")} } },
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
`;
|
|
31
|
+
}
|