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.
Files changed (54) hide show
  1. package/dist/cli/commands/dev.d.ts +2 -2
  2. package/dist/cli/commands/dev.d.ts.map +1 -1
  3. package/dist/cli/commands/dev.js +39 -27
  4. package/dist/cli/commands/dev.js.map +1 -1
  5. package/dist/cli/commands/init.d.ts +2 -1
  6. package/dist/cli/commands/init.d.ts.map +1 -1
  7. package/dist/cli/commands/init.js +27 -118
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/dist/core/project/agent-instructions.d.ts.map +1 -1
  10. package/dist/core/project/agent-instructions.js +4 -1
  11. package/dist/core/project/agent-instructions.js.map +1 -1
  12. package/dist/core/project/home-page.d.ts +4 -0
  13. package/dist/core/project/home-page.d.ts.map +1 -0
  14. package/dist/core/project/home-page.js +113 -0
  15. package/dist/core/project/home-page.js.map +1 -0
  16. package/dist/core/project/react-project.d.ts +1 -1
  17. package/dist/core/project/react-project.d.ts.map +1 -1
  18. package/dist/core/project/react-project.js +45 -24
  19. package/dist/core/project/react-project.js.map +1 -1
  20. package/dist/core/scaffold/auth-generator.js +8 -8
  21. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
  22. package/dist/core/scaffold/native-schema-generator.js +6 -16
  23. package/dist/core/scaffold/native-schema-generator.js.map +1 -1
  24. package/dist/utils/package-manager.js +1 -1
  25. package/dist/utils/package-manager.js.map +1 -1
  26. package/dist/utils/python-uv.d.ts +5 -7
  27. package/dist/utils/python-uv.d.ts.map +1 -1
  28. package/dist/utils/python-uv.js +50 -24
  29. package/dist/utils/python-uv.js.map +1 -1
  30. package/dist/utils/react-dev.d.ts +4 -0
  31. package/dist/utils/react-dev.d.ts.map +1 -0
  32. package/dist/utils/react-dev.js +35 -0
  33. package/dist/utils/react-dev.js.map +1 -0
  34. package/package.json +1 -1
  35. package/src/__tests__/auth.test.ts +9 -0
  36. package/src/__tests__/dev.test.ts +13 -6
  37. package/src/__tests__/home-page.test.ts +50 -0
  38. package/src/__tests__/init.test.ts +57 -0
  39. package/src/__tests__/package-manager.test.ts +2 -2
  40. package/src/__tests__/plan-apply.test.ts +9 -0
  41. package/src/__tests__/python-uv.test.ts +50 -14
  42. package/src/__tests__/python-venv.test.ts +48 -0
  43. package/src/__tests__/react-dev.test.ts +72 -0
  44. package/src/__tests__/react.test.ts +19 -0
  45. package/src/cli/commands/dev.ts +40 -31
  46. package/src/cli/commands/init.ts +29 -121
  47. package/src/core/project/agent-instructions.ts +4 -1
  48. package/src/core/project/home-page.ts +114 -0
  49. package/src/core/project/react-project.ts +49 -27
  50. package/src/core/scaffold/auth-generator.ts +8 -8
  51. package/src/core/scaffold/native-schema-generator.ts +8 -27
  52. package/src/utils/package-manager.ts +1 -1
  53. package/src/utils/python-uv.ts +53 -33
  54. 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: { port: 3000, strictPort: true },
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
- `import { useState } from 'react';
173
- import { Link } from 'react-router-dom';
174
- import { scaffoldConfig } from '@/lib/scaffold-config';
175
-
176
- export default function Home() {
177
- const [message, setMessage] = useState('');
178
- async function testConnection() {
179
- try {
180
- const response = await fetch('/api/greet?name=SwallowKit');
181
- if (!response.ok) throw new Error('BFF request failed (' + response.status + ')');
182
- const data = await response.json();
183
- setMessage(data.message);
184
- } catch (error) { setMessage(error instanceof Error ? error.message : String(error)); }
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
- write(projectDir, "src\\style.css", '@import "tailwindcss";\n');
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
- buildUvVenvArgs,
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
- "3.11",
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 = buildProjectLocalUvEnv(process.env, projectRoot);
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 = getVirtualEnvPythonPath(venvDir);
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
 
@@ -250,5 +250,5 @@ export function getFunctionsStartScript(
250
250
  return "cd functions && npm start";
251
251
  }
252
252
 
253
- return "cd functions && func start";
253
+ return backendLanguage === "csharp" ? "cd functions && dotnet run" : "cd functions && func start";
254
254
  }
@@ -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
- export const SWALLOWKIT_PYTHON_VERSION = "3.11";
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 buildProjectLocalUvEnv(
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
- UV_CACHE_DIR: uvPaths.cacheDir,
44
- UV_PYTHON_INSTALL_DIR: uvPaths.pythonInstallDir,
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.UV_PYTHON_NO_REGISTRY = "true";
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, pythonVersion = SWALLOWKIT_PYTHON_VERSION): string[] {
91
- return ["venv", venvDir, "--python", pythonVersion];
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
+ }