Skip to content

Commit 1845ac8

Browse files
committed
refactor: split into more components
1 parent 0841c89 commit 1845ac8

File tree

3 files changed

+109
-78
lines changed

3 files changed

+109
-78
lines changed

packages/core/src/amazonq/util/zipProjectUtil.ts

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ export interface ZippedWorkspaceResult {
1313
totalFileBytes: number
1414
}
1515

16+
interface ZipFileAddedResult {
17+
result: 'added'
18+
addedBytes: number
19+
}
20+
21+
interface ZipFileSkippedResult {
22+
result: 'skipped'
23+
reason: 'excluded' | 'missing'
24+
}
25+
1626
interface ZipProjectOptions {
1727
includeProjectName?: boolean
1828
nonPosixPath?: boolean
@@ -24,7 +34,51 @@ interface ZipProjectCustomizations {
2434
computeSideEffects?: (file: CollectFilesResultItem) => Promise<void> | void
2535
}
2636

27-
export async function addToZip(
37+
export async function addFileToZip(
38+
file: CollectFilesResultItem,
39+
zip: ZipStream,
40+
customizations?: ZipProjectCustomizations,
41+
options?: ZipProjectOptions
42+
): Promise<ZipFileAddedResult | ZipFileSkippedResult> {
43+
if (customizations?.isExcluded && customizations.isExcluded(file)) {
44+
return { result: 'skipped', reason: 'excluded' }
45+
}
46+
const errorToThrow = customizations?.checkForError ? customizations.checkForError(file) : undefined
47+
if (errorToThrow) {
48+
throw errorToThrow
49+
}
50+
51+
// Paths in zip should be POSIX compliant regardless of OS
52+
// Reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
53+
const zipFilePath = options?.includeProjectName
54+
? path.join(path.basename(file.workspaceFolder.uri.fsPath), file.zipFilePath)
55+
: file.zipFilePath
56+
const posixPath = options?.nonPosixPath ? zipFilePath : zipFilePath.split(path.sep).join(path.posix.sep)
57+
58+
try {
59+
// filepath will be out-of-sync for files with unsaved changes.
60+
if (file.isText) {
61+
zip.writeString(file.fileContent, posixPath)
62+
} else {
63+
zip.writeFile(file.fileUri.fsPath, path.dirname(posixPath))
64+
}
65+
} catch (error) {
66+
if (error instanceof Error && error.message.includes('File not found')) {
67+
// No-op: Skip if file was deleted or does not exist
68+
// Reference: https://github.com/cthackers/adm-zip/blob/1cd32f7e0ad3c540142a76609bb538a5cda2292f/adm-zip.js#L296-L321
69+
return { result: 'skipped', reason: 'missing' }
70+
}
71+
throw error
72+
}
73+
74+
if (customizations?.computeSideEffects) {
75+
await customizations.computeSideEffects(file)
76+
}
77+
78+
return { result: 'added', addedBytes: file.fileSizeBytes }
79+
}
80+
81+
export async function addProjectToZip(
2882
repoRootPaths: string[],
2983
workspaceFolders: CurrentWsFolders,
3084
collectFilesOptions: CollectFilesOptions,
@@ -41,40 +95,9 @@ export async function addToZip(
4195
}
4296
zippedFiles.add(file.zipFilePath)
4397

44-
if (customizations?.isExcluded && customizations.isExcluded(file)) {
45-
continue
46-
}
47-
const errorToThrow = customizations?.checkForError ? customizations.checkForError(file) : undefined
48-
if (errorToThrow) {
49-
throw errorToThrow
50-
}
51-
52-
totalBytes += file.fileSizeBytes
53-
// Paths in zip should be POSIX compliant regardless of OS
54-
// Reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
55-
const zipFilePath = options?.includeProjectName
56-
? path.join(path.basename(file.workspaceFolder.uri.fsPath), file.zipFilePath)
57-
: file.zipFilePath
58-
const posixPath = options?.nonPosixPath ? zipFilePath : zipFilePath.split(path.sep).join(path.posix.sep)
59-
60-
try {
61-
// filepath will be out-of-sync for files with unsaved changes.
62-
if (file.isText) {
63-
zip.writeString(file.fileContent, posixPath)
64-
} else {
65-
zip.writeFile(file.fileUri.fsPath, path.dirname(posixPath))
66-
}
67-
} catch (error) {
68-
if (error instanceof Error && error.message.includes('File not found')) {
69-
// No-op: Skip if file was deleted or does not exist
70-
// Reference: https://github.com/cthackers/adm-zip/blob/1cd32f7e0ad3c540142a76609bb538a5cda2292f/adm-zip.js#L296-L321
71-
continue
72-
}
73-
throw error
74-
}
75-
76-
if (customizations?.computeSideEffects) {
77-
await customizations.computeSideEffects(file)
98+
const addFileResult = await addFileToZip(file, zip, customizations, options)
99+
if (addFileResult.result === 'added') {
100+
totalBytes += addFileResult.addedBytes
78101
}
79102
}
80103

@@ -88,7 +111,7 @@ export async function zipProject(
88111
customizations?: ZipProjectCustomizations,
89112
options?: ZipProjectOptions & { zip?: ZipStream }
90113
): Promise<ZippedWorkspaceResult> {
91-
const { zip, totalBytesAdded } = await addToZip(
114+
const { zip, totalBytesAdded } = await addProjectToZip(
92115
repoRootPaths,
93116
workspaceFolders,
94117
collectFilesOptions,

packages/core/src/codewhisperer/util/zipUtil.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { ProjectZipError } from '../../amazonqTest/error'
3030
import { removeAnsi } from '../../shared/utilities/textUtilities'
3131
import { normalize } from '../../shared/utilities/pathUtils'
3232
import { ZipStream } from '../../shared/utilities/zipStream'
33-
import { addToZip } from '../../amazonq/util/zipProjectUtil'
33+
import { addProjectToZip } from '../../amazonq/util/zipProjectUtil'
3434

3535
export interface ZipMetadata {
3636
rootDir: string
@@ -213,12 +213,9 @@ export class ZipUtil {
213213

214214
protected async zipProject(useCase: FeatureUseCase, projectPath?: string, metadataDir?: string) {
215215
const zip = new ZipStream()
216-
let projectPaths = []
217-
if (useCase === FeatureUseCase.TEST_GENERATION && projectPath) {
218-
projectPaths.push(projectPath)
219-
} else {
220-
projectPaths = this.getProjectPaths()
221-
}
216+
const projectPaths =
217+
useCase === FeatureUseCase.TEST_GENERATION && projectPath ? [projectPath] : this.getProjectPaths()
218+
222219
if (useCase === FeatureUseCase.CODE_SCAN) {
223220
await this.processCombinedGitDiff(zip, projectPaths, '', CodeWhispererConstants.CodeAnalysisScope.PROJECT)
224221
}
@@ -453,7 +450,7 @@ export class ZipUtil {
453450
this._totalSize += file.fileSizeBytes
454451
}
455452

456-
await addToZip(
453+
await addProjectToZip(
457454
projectPaths,
458455
workspaceFolders,
459456
collectFilesOptions,

packages/core/src/shared/utilities/workspaceUtils.ts

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -330,13 +330,16 @@ async function filterOutGitignoredFiles(
330330
return gitIgnoreFilter.filterFiles(files)
331331
}
332332

333-
export type CollectFilesResultItem = {
334-
workspaceFolder: vscode.WorkspaceFolder
335-
relativeFilePath: string
333+
export type FileInformation = {
336334
fileUri: vscode.Uri
337-
fileContent: string
338335
fileSizeBytes: number
339336
isText: boolean
337+
}
338+
339+
export interface CollectFilesResultItem extends FileInformation {
340+
workspaceFolder: vscode.WorkspaceFolder
341+
relativeFilePath: string
342+
fileContent: string
340343
zipFilePath: string
341344
}
342345
export type CollectFilesFilter = (relativePath: string) => boolean // returns true if file should be filtered out
@@ -423,28 +426,14 @@ export async function collectFiles(
423426
continue
424427
}
425428

426-
const result: Omit<CollectFilesResultItem, 'fileContent'> = {
427-
workspaceFolder: relativePath.workspaceFolder,
428-
relativeFilePath: relativePath.relativePath,
429-
fileUri: file,
430-
fileSizeBytes: fileStat.size,
431-
zipFilePath: prefixWithFolderPrefix(relativePath.workspaceFolder, relativePath.relativePath),
432-
isText: !ZipConstants.knownBinaryFileExts.includes(path.extname(file.fsPath)),
433-
}
434-
if (includeContent) {
435-
const hasUnsavedChanges = isFileOpenAndDirty(file)
436-
const content =
437-
result.isText && hasUnsavedChanges ? await getCurrentTextContent(file) : await readFile(file)
438-
if (content === undefined) {
439-
continue
440-
}
441-
429+
const fileStats = await getFileStats(file, includeContent, fileStat.size)
430+
if (fileStats) {
442431
storage.push({
443-
...result,
444-
fileContent: content,
432+
...fileStats,
433+
workspaceFolder: relativePath.workspaceFolder,
434+
relativeFilePath: relativePath.relativePath,
435+
zipFilePath: prefixWithFolderPrefix(relativePath.workspaceFolder, relativePath.relativePath),
445436
})
446-
} else {
447-
storage.push(result)
448437
}
449438
totalSizeBytes += fileStat.size
450439
}
@@ -465,16 +454,6 @@ export async function collectFiles(
465454
}
466455
return prefix === '' ? path : `${prefix}/${path}`
467456
}
468-
469-
async function getCurrentTextContent(uri: vscode.Uri) {
470-
const document = await vscode.workspace.openTextDocument(uri)
471-
const content = document.getText()
472-
return content
473-
}
474-
475-
function isFileOpenAndDirty(uri: vscode.Uri) {
476-
return vscode.workspace.textDocuments.some((document) => document.uri.fsPath === uri.fsPath && document.isDirty)
477-
}
478457
}
479458

480459
const readFile = async (file: vscode.Uri) => {
@@ -501,6 +480,38 @@ const workspaceFolderPrefixGuards = {
501480
maximumFoldersWithMatchingSubfolders: 10_000,
502481
}
503482

483+
export async function getFileStats(file: vscode.Uri, includeContent: boolean, fileSize?: number) {
484+
const fileWithoutContent = {
485+
fileUri: file,
486+
fileSizeBytes: fileSize ?? (await fs.stat(file)).size,
487+
isText: !ZipConstants.knownBinaryFileExts.includes(path.extname(file.fsPath)),
488+
}
489+
if (includeContent) {
490+
const hasUnsavedChanges = isFileOpenAndDirty(file)
491+
const fileContent =
492+
fileWithoutContent.isText && hasUnsavedChanges ? await getCurrentTextContent(file) : await readFile(file)
493+
if (fileContent === undefined) {
494+
return
495+
}
496+
return {
497+
...fileWithoutContent,
498+
fileContent,
499+
}
500+
} else {
501+
return fileWithoutContent
502+
}
503+
504+
function isFileOpenAndDirty(uri: vscode.Uri) {
505+
return vscode.workspace.textDocuments.some((document) => document.uri.fsPath === uri.fsPath && document.isDirty)
506+
}
507+
508+
async function getCurrentTextContent(uri: vscode.Uri) {
509+
const document = await vscode.workspace.openTextDocument(uri)
510+
const content = document.getText()
511+
return content
512+
}
513+
}
514+
504515
/**
505516
* tries to determine the possible prefixes we will use for a given workspace folder in the zip file
506517
* We want to keep the folder names in the prefix, since they might convey useful information, for example

0 commit comments

Comments
 (0)