Skip to content

Commit 68e15c8

Browse files
authored
Create directories before doing rename (#95)
* Fix renaming into dir * Add % ()s * Tick version
1 parent b7601c8 commit 68e15c8

File tree

5 files changed

+202
-97
lines changed

5 files changed

+202
-97
lines changed

deno.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "https://raw.githubusercontent.com/denoland/deno/348900b8b79f4a434cab4c74b3bc8d4d2fa8ee74/cli/schemas/config-file.v1.json",
33
"name": "@valtown/vt",
44
"description": "The Val Town CLI",
5-
"version": "0.1.17",
5+
"version": "0.1.18",
66
"exports": "./vt.ts",
77
"license": "MIT",
88
"tasks": {

src/cmd/lib/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function formatStatus(
7575
dirname(file.path),
7676
styleConfig.color(basename(file.path)),
7777
) +
78-
` ${colors.gray((file.similarity * 100).toFixed(2) + "%")}`;
78+
` ${colors.gray("(" + (file.similarity * 100).toFixed(2) + "%)")}`;
7979
} else {
8080
// Normal path display for other statuses
8181
pathDisplay = join(

src/sdk.ts

+28-8
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,31 @@ export async function branchNameToBranch(
4141
throw new Deno.errors.NotFound(`Branch "${branchName}" not found in project`);
4242
}
4343

44+
/**
45+
* Checks if a file exists at the specified path in a project
46+
*
47+
* @param {string} projectId - The ID of the project containing the file
48+
* @param {string} filePath - The file path to check
49+
* @param {string} branchId - The ID of the project branch to reference
50+
* @param {number} version - The version of the project to check
51+
* @returns {Promise<boolean>} Promise resolving to true if the file exists, false otherwise
52+
*/
53+
export async function projectItemExists(
54+
projectId: string,
55+
branchId: string,
56+
filePath: string,
57+
version: number,
58+
): Promise<boolean> {
59+
try {
60+
const item = await getProjectItem(projectId, branchId, version, filePath);
61+
return item !== undefined;
62+
} catch (e) {
63+
if (e instanceof ValTown.APIError && e.status === 404) {
64+
return false;
65+
} else throw e;
66+
}
67+
}
68+
4469
/**
4570
* Converts a file path to its corresponding project item for a given project.
4671
*
@@ -57,18 +82,13 @@ export const getProjectItem = memoize(async (
5782
version: number,
5883
filePath: string,
5984
): Promise<ValTown.Projects.FileRetrieveResponse | undefined> => {
60-
branchId = (branchId ||
61-
await branchNameToBranch(projectId, DEFAULT_BRANCH_NAME).then((resp) =>
62-
resp.id
63-
))!;
64-
6585
const projectItems = await listProjectItems(projectId, branchId, version);
66-
const results = [];
86+
6787
for (const filepath of projectItems) {
68-
if (filepath.path === filePath) results.push(filepath);
88+
if (filepath.path === filePath) return filepath;
6989
}
7090

71-
return results.length === 1 ? results[0] : undefined;
91+
return undefined;
7292
});
7393

7494
/**

src/vt/lib/push.ts

+51-47
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ export interface PushParams {
1818
projectId: string;
1919
/** The branch ID to upload to. */
2020
branchId: string;
21-
/** The version to compute the file state changes against. Defaults to latest version. */
22-
latestVersion?: number;
2321
/** The current file state. If not provided, it will be computed. */
2422
fileState?: ItemStatusManager;
2523
/** A list of gitignore rules. */
@@ -40,20 +38,19 @@ export async function push(params: PushParams): Promise<ItemStatusManager> {
4038
targetDir,
4139
projectId,
4240
branchId,
43-
latestVersion,
4441
fileState,
4542
gitignoreRules,
4643
dryRun = false,
4744
} = params;
48-
latestVersion = latestVersion ?? await getLatestVersion(projectId, branchId);
45+
const initialVersion = await getLatestVersion(projectId, branchId);
4946

5047
// Use provided status, or retrieve the status
5148
if (!fileState || fileState.isEmpty()) {
5249
fileState = await status({
5350
targetDir,
5451
projectId,
5552
branchId,
56-
version: latestVersion,
53+
version: initialVersion,
5754
gitignoreRules,
5855
});
5956
}
@@ -64,7 +61,7 @@ export async function push(params: PushParams): Promise<ItemStatusManager> {
6461
const existingItems = await listProjectItems(
6562
projectId,
6663
branchId,
67-
latestVersion,
64+
initialVersion,
6865
);
6966

7067
// Create a set of existing paths that already exist
@@ -76,30 +73,52 @@ export async function push(params: PushParams): Promise<ItemStatusManager> {
7673
]);
7774

7875
// Create all necessary directories first
79-
const createFilesPromise = createRequiredDirectories(
76+
const versionAfterDirectories = await createRequiredDirectories(
8077
projectId,
8178
branchId,
8279
fileState,
8380
existingDirs,
84-
)
85-
.then(() =>
86-
Promise.all(
87-
fileState.created
88-
.filter((f) => f.type !== "directory") // Already created directories
89-
.map(async (file) => {
90-
// Upload the file
91-
await sdk.projects.files.create(
92-
projectId,
93-
{
94-
path: file.path,
95-
content: await Deno.readTextFile(join(targetDir, file.path)),
96-
branch_id: branchId,
97-
type: file.type as Exclude<ProjectItemType, "directory">,
98-
},
99-
);
100-
}),
101-
)
102-
);
81+
) + initialVersion;
82+
83+
// Rename files that were renamed locally
84+
const renamePromises = fileState.renamed
85+
.filter((file) => file.type !== "directory")
86+
.map(async (file) => {
87+
// We created the parent directory already, but not the file, so we must
88+
// query the ID of the parent directory to set it as the new parent of the
89+
// item
90+
const parent = await getProjectItem(
91+
projectId,
92+
branchId,
93+
versionAfterDirectories,
94+
dirname(file.path),
95+
);
96+
97+
await sdk.projects.files.update(projectId, {
98+
branch_id: branchId,
99+
name: basename(file.path),
100+
type: file.type as ProjectFileType,
101+
content: file.content,
102+
parent_id: parent?.id,
103+
path: file.oldPath,
104+
});
105+
});
106+
107+
// Create all new files that were created (we already handled directories)
108+
const createdPromises = fileState.created
109+
.filter((f) => f.type !== "directory") // Already created directories
110+
.map(async (file) => {
111+
// Upload the file
112+
await sdk.projects.files.create(
113+
projectId,
114+
{
115+
path: file.path,
116+
content: await Deno.readTextFile(join(targetDir, file.path)),
117+
branch_id: branchId,
118+
type: file.type as Exclude<ProjectItemType, "directory">,
119+
},
120+
);
121+
});
103122

104123
// Upload files that were modified locally
105124
const modifiedPromises = fileState.modified
@@ -117,25 +136,6 @@ export async function push(params: PushParams): Promise<ItemStatusManager> {
117136
);
118137
});
119138

120-
// Rename files that were renamed locally
121-
const renamePromises = fileState.renamed
122-
.filter((file) => file.type !== "directory")
123-
.map(async (file) => {
124-
await sdk.projects.files.update(projectId, {
125-
branch_id: branchId,
126-
name: basename(file.path),
127-
type: file.type as ProjectFileType,
128-
content: file.content,
129-
parent_id: (await getProjectItem(
130-
projectId,
131-
branchId,
132-
latestVersion,
133-
dirname(file.path),
134-
))?.id,
135-
path: file.oldPath,
136-
});
137-
});
138-
139139
// Delete files that exist on the server but not locally
140140
const deletedPromises = fileState.deleted.map(async (file) => {
141141
await sdk.projects.files.delete(projectId, {
@@ -150,7 +150,7 @@ export async function push(params: PushParams): Promise<ItemStatusManager> {
150150
...modifiedPromises,
151151
...deletedPromises,
152152
...renamePromises,
153-
createFilesPromise,
153+
...createdPromises,
154154
]);
155155

156156
return fileState;
@@ -161,7 +161,7 @@ async function createRequiredDirectories(
161161
branchId: string,
162162
fileState: ItemStatusManager,
163163
existingDirs: Set<string>,
164-
): Promise<void> {
164+
): Promise<number> {
165165
// Get directories that need to be created
166166
const dirsToCreate = fileState.created
167167
.filter((f) => f.type === "directory")
@@ -193,12 +193,16 @@ async function createRequiredDirectories(
193193
});
194194

195195
// Create all necessary directories
196+
let createdCount = 0;
196197
for (const path of sortedDirsToCreate) {
197198
await sdk.projects.files.create(
198199
projectId,
199200
{ path, type: "directory", branch_id: branchId },
200201
);
201202
// Add to existing dirs set after creation
202203
existingDirs.add(path);
204+
createdCount++;
203205
}
206+
207+
return createdCount;
204208
}

0 commit comments

Comments
 (0)