6
e2e-tests/fixtures/rename-edit.md
Normal file
6
e2e-tests/fixtures/rename-edit.md
Normal file
@@ -0,0 +1,6 @@
|
||||
<dyad-rename from="src/App.tsx" to="src/Renamed.tsx">
|
||||
</dyad-rename>
|
||||
|
||||
<dyad-write path="src/Renamed.tsx">
|
||||
// newly added content to renamed file should exist
|
||||
</dyad-write>
|
||||
9
e2e-tests/rename_edit.spec.ts
Normal file
9
e2e-tests/rename_edit.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { test } from "./helpers/test_helper";
|
||||
|
||||
test("rename then edit works", async ({ po }) => {
|
||||
await po.setUp({ autoApprove: true });
|
||||
await po.importApp("minimal");
|
||||
|
||||
await po.sendPrompt("tc=rename-edit");
|
||||
await po.snapshotAppFiles({ name: "rename-edit" });
|
||||
});
|
||||
185
e2e-tests/snapshots/rename_edit.spec.ts_rename-edit.txt
Normal file
185
e2e-tests/snapshots/rename_edit.spec.ts_rename-edit.txt
Normal file
@@ -0,0 +1,185 @@
|
||||
=== .gitignore ===
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
|
||||
=== file1.txt ===
|
||||
A file (2)
|
||||
|
||||
=== index.html ===
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>dyad-generated-app</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
=== package.json ===
|
||||
{
|
||||
"name": "vite_react_shadcn_ts",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build:dev": "vite build --mode development",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.5.5",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react-swc": "^3.9.0",
|
||||
"typescript": "^5.5.3",
|
||||
"vite": "^6.3.4"
|
||||
},
|
||||
"packageManager": "<scrubbed>"
|
||||
}
|
||||
|
||||
=== src/main.tsx ===
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App.tsx";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
|
||||
|
||||
=== src/Renamed.tsx ===
|
||||
// newly added content to renamed file should exist
|
||||
|
||||
=== src/vite-env.d.ts ===
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
|
||||
=== tsconfig.app.json ===
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitAny": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
|
||||
=== tsconfig.json ===
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"noImplicitAny": false,
|
||||
"noUnusedParameters": false,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"noUnusedLocals": false,
|
||||
"strictNullChecks": false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
=== tsconfig.node.json ===
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
|
||||
=== vite.config.ts ===
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig(() => ({
|
||||
server: {
|
||||
host: "::",
|
||||
port: 8080,
|
||||
},
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -3,8 +3,7 @@ import { PlaywrightTestConfig } from "@playwright/test";
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: "./e2e-tests",
|
||||
workers: 1,
|
||||
retries: process.env.CI ? 2 : 1,
|
||||
// maxFailures: 1,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
timeout: process.env.CI ? 180_000 : 30_000,
|
||||
// Use a custom snapshot path template because Playwright's default
|
||||
// is platform-specific which isn't necessary for Dyad e2e tests
|
||||
|
||||
@@ -305,30 +305,55 @@ export async function processFullResponseActions(
|
||||
}
|
||||
}
|
||||
|
||||
// Process all file writes
|
||||
for (const tag of dyadWriteTags) {
|
||||
const filePath = tag.path;
|
||||
const content = tag.content;
|
||||
//////////////////////
|
||||
// File operations //
|
||||
// Do it in this order:
|
||||
// 1. Deletes
|
||||
// 2. Renames
|
||||
// 3. Writes
|
||||
//
|
||||
// Why?
|
||||
// - Deleting first avoids path conflicts before the other operations.
|
||||
// - LLMs like to rename and then edit the same file.
|
||||
//////////////////////
|
||||
|
||||
// Process all file deletions
|
||||
for (const filePath of dyadDeletePaths) {
|
||||
const fullFilePath = path.join(appPath, filePath);
|
||||
|
||||
// Ensure directory exists
|
||||
const dirPath = path.dirname(fullFilePath);
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
// Delete the file if it exists
|
||||
if (fs.existsSync(fullFilePath)) {
|
||||
if (fs.lstatSync(fullFilePath).isDirectory()) {
|
||||
fs.rmdirSync(fullFilePath, { recursive: true });
|
||||
} else {
|
||||
fs.unlinkSync(fullFilePath);
|
||||
}
|
||||
logger.log(`Successfully deleted file: ${fullFilePath}`);
|
||||
deletedFiles.push(filePath);
|
||||
|
||||
// Write file content
|
||||
fs.writeFileSync(fullFilePath, content);
|
||||
logger.log(`Successfully wrote file: ${fullFilePath}`);
|
||||
writtenFiles.push(filePath);
|
||||
// Remove the file from git
|
||||
try {
|
||||
await git.remove({
|
||||
fs,
|
||||
dir: appPath,
|
||||
filepath: filePath,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to git remove deleted file ${filePath}:`, error);
|
||||
// Continue even if remove fails as the file was still deleted
|
||||
}
|
||||
} else {
|
||||
logger.warn(`File to delete does not exist: ${fullFilePath}`);
|
||||
}
|
||||
if (isServerFunction(filePath)) {
|
||||
try {
|
||||
await deploySupabaseFunctions({
|
||||
await deleteSupabaseFunction({
|
||||
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||
functionName: path.basename(path.dirname(filePath)),
|
||||
content: content,
|
||||
functionName: getFunctionNameFromPath(filePath),
|
||||
});
|
||||
} catch (error) {
|
||||
errors.push({
|
||||
message: `Failed to deploy Supabase function: ${filePath}`,
|
||||
message: `Failed to delete Supabase function: ${filePath}`,
|
||||
error: error,
|
||||
});
|
||||
}
|
||||
@@ -398,43 +423,30 @@ export async function processFullResponseActions(
|
||||
}
|
||||
}
|
||||
|
||||
// Process all file deletions
|
||||
for (const filePath of dyadDeletePaths) {
|
||||
// Process all file writes
|
||||
for (const tag of dyadWriteTags) {
|
||||
const filePath = tag.path;
|
||||
const content = tag.content;
|
||||
const fullFilePath = path.join(appPath, filePath);
|
||||
|
||||
// Delete the file if it exists
|
||||
if (fs.existsSync(fullFilePath)) {
|
||||
if (fs.lstatSync(fullFilePath).isDirectory()) {
|
||||
fs.rmdirSync(fullFilePath, { recursive: true });
|
||||
} else {
|
||||
fs.unlinkSync(fullFilePath);
|
||||
}
|
||||
logger.log(`Successfully deleted file: ${fullFilePath}`);
|
||||
deletedFiles.push(filePath);
|
||||
// Ensure directory exists
|
||||
const dirPath = path.dirname(fullFilePath);
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
|
||||
// Remove the file from git
|
||||
try {
|
||||
await git.remove({
|
||||
fs,
|
||||
dir: appPath,
|
||||
filepath: filePath,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to git remove deleted file ${filePath}:`, error);
|
||||
// Continue even if remove fails as the file was still deleted
|
||||
}
|
||||
} else {
|
||||
logger.warn(`File to delete does not exist: ${fullFilePath}`);
|
||||
}
|
||||
// Write file content
|
||||
fs.writeFileSync(fullFilePath, content);
|
||||
logger.log(`Successfully wrote file: ${fullFilePath}`);
|
||||
writtenFiles.push(filePath);
|
||||
if (isServerFunction(filePath)) {
|
||||
try {
|
||||
await deleteSupabaseFunction({
|
||||
await deploySupabaseFunctions({
|
||||
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||
functionName: getFunctionNameFromPath(filePath),
|
||||
functionName: path.basename(path.dirname(filePath)),
|
||||
content: content,
|
||||
});
|
||||
} catch (error) {
|
||||
errors.push({
|
||||
message: `Failed to delete Supabase function: ${filePath}`,
|
||||
message: `Failed to deploy Supabase function: ${filePath}`,
|
||||
error: error,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user