Skip to content

Commit ee917c0

Browse files
authored
refactor(fs): prefer our fs module, eliminate fs-extra #5689
## Problem We want to avoid fs-extra, and eventually move it to the banned import list. As a first step, we remove fs-extra with as much of our own `fs.ts` work, and `fs` when not possible. ## Solution - Replace `ensureDir` with `mkDir` as suggested here: https://github.com/jprichardson/node-fs-extra/blob/HEAD/docs/ensureDir.md - Also `ensureDir` with permissions arg can be `mkDir` with `chmod` following. - Replace `move` with `rename` since we aren't using virtual file system, or crossing disk partitions: https://stackoverflow.com/questions/8579055/how-do-i-move-files-in-node-js
1 parent a4bc012 commit ee917c0

File tree

27 files changed

+145
-134
lines changed

27 files changed

+145
-134
lines changed

packages/core/src/amazonq/commons/diff.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,26 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { existsSync } from 'fs'
76
import * as vscode from 'vscode'
87
import { featureDevScheme } from '../../amazonqFeatureDev/constants'
8+
import { fs } from '../../shared'
99

1010
export async function openDiff(leftPath: string, rightPath: string, tabId: string) {
11-
const { left, right } = getFileDiffUris(leftPath, rightPath, tabId)
11+
const { left, right } = await getFileDiffUris(leftPath, rightPath, tabId)
1212
await vscode.commands.executeCommand('vscode.diff', left, right)
1313
}
1414

1515
export async function openDeletedDiff(filePath: string, name: string, tabId: string) {
16-
const fileUri = getOriginalFileUri(filePath, tabId)
16+
const fileUri = await getOriginalFileUri(filePath, tabId)
1717
await vscode.commands.executeCommand('vscode.open', fileUri, {}, `${name} (Deleted)`)
1818
}
1919

20-
export function getOriginalFileUri(fullPath: string, tabId: string) {
21-
return existsSync(fullPath) ? vscode.Uri.file(fullPath) : createAmazonQUri('empty', tabId)
20+
export async function getOriginalFileUri(fullPath: string, tabId: string) {
21+
return (await fs.exists(fullPath)) ? vscode.Uri.file(fullPath) : createAmazonQUri('empty', tabId)
2222
}
2323

24-
export function getFileDiffUris(leftPath: string, rightPath: string, tabId: string) {
25-
const left = getOriginalFileUri(leftPath, tabId)
24+
export async function getFileDiffUris(leftPath: string, rightPath: string, tabId: string) {
25+
const left = await getOriginalFileUri(leftPath, tabId)
2626
const right = createAmazonQUri(rightPath, tabId)
2727

2828
return { left, right }

packages/core/src/amazonq/lsp/lspController.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import * as vscode from 'vscode'
77
import * as path from 'path'
8-
import * as fs from 'fs-extra'
8+
import { createWriteStream } from 'fs'
99
import * as crypto from 'crypto'
1010
import { getLogger } from '../../shared/logger/logger'
1111
import { CurrentWsFolders, collectFilesForIndex } from '../../shared/utilities/workspaceUtils'
@@ -19,12 +19,11 @@ import { CodeWhispererSettings } from '../../codewhisperer/util/codewhispererSet
1919
import { activate as activateLsp } from './lspClient'
2020
import { telemetry } from '../../shared/telemetry'
2121
import { isCloud9 } from '../../shared/extensionUtilities'
22-
import { globals, ToolkitError } from '../../shared'
22+
import { fs, globals, ToolkitError } from '../../shared'
2323
import { AuthUtil } from '../../codewhisperer'
2424
import { isWeb } from '../../shared/extensionGlobals'
2525
import { getUserAgent } from '../../shared/telemetry/util'
2626
import { isAmazonInternalOs } from '../../shared/vscode/env'
27-
import { fs as fs2 } from '../../shared/fs/fs'
2827

2928
function getProjectPaths() {
3029
const workspaceFolders = vscode.workspace.workspaceFolders
@@ -106,7 +105,7 @@ export class LspController {
106105
throw new ToolkitError(`Failed to download. Error: ${JSON.stringify(res)}`)
107106
}
108107
return new Promise((resolve, reject) => {
109-
const file = fs.createWriteStream(localFile)
108+
const file = createWriteStream(localFile)
110109
res.body.pipe(file)
111110
res.body.on('error', (err) => {
112111
reject(err)
@@ -134,16 +133,16 @@ export class LspController {
134133
}
135134

136135
async getFileSha384(filePath: string): Promise<string> {
137-
const fileBuffer = await fs.promises.readFile(filePath)
136+
const fileBuffer = await fs.readFile(filePath)
138137
const hash = crypto.createHash('sha384')
139138
hash.update(fileBuffer)
140139
return hash.digest('hex')
141140
}
142141

143-
isLspInstalled(context: vscode.ExtensionContext) {
142+
async isLspInstalled(context: vscode.ExtensionContext) {
144143
const localQServer = context.asAbsolutePath(path.join('resources', 'qserver'))
145144
const localNodeRuntime = context.asAbsolutePath(path.join('resources', nodeBinName))
146-
return fs.existsSync(localQServer) && fs.existsSync(localNodeRuntime)
145+
return (await fs.exists(localQServer)) && (await fs.exists(localNodeRuntime))
147146
}
148147

149148
getQserverFromManifest(manifest: Manifest): Content | undefined {
@@ -208,7 +207,7 @@ export class LspController {
208207
getLogger().error(
209208
`LspController: Downloaded file sha ${sha384} does not match manifest ${content.hashes[0]}.`
210209
)
211-
fs.removeSync(filePath)
210+
await fs.delete(filePath)
212211
return false
213212
}
214213
return true
@@ -226,19 +225,19 @@ export class LspController {
226225
async tryInstallLsp(context: vscode.ExtensionContext): Promise<boolean> {
227226
let tempFolder = undefined
228227
try {
229-
if (this.isLspInstalled(context)) {
228+
if (await this.isLspInstalled(context)) {
230229
getLogger().info(`LspController: LSP already installed`)
231230
return true
232231
}
233232
// clean up previous downloaded LSP
234233
const qserverPath = context.asAbsolutePath(path.join('resources', 'qserver'))
235-
if (fs.existsSync(qserverPath)) {
234+
if (await fs.exists(qserverPath)) {
236235
await tryRemoveFolder(qserverPath)
237236
}
238237
// clean up previous downloaded node runtime
239238
const nodeRuntimePath = context.asAbsolutePath(path.join('resources', nodeBinName))
240-
if (fs.existsSync(nodeRuntimePath)) {
241-
fs.rmSync(nodeRuntimePath)
239+
if (await fs.exists(nodeRuntimePath)) {
240+
await fs.delete(nodeRuntimePath)
242241
}
243242
// fetch download url for qserver and node runtime
244243
const manifest: Manifest = (await this.fetchManifest()) as Manifest
@@ -259,16 +258,16 @@ export class LspController {
259258
}
260259
const zip = new AdmZip(qserverZipTempPath)
261260
zip.extractAllTo(tempFolder)
262-
fs.moveSync(path.join(tempFolder, 'qserver'), qserverPath)
261+
await fs.rename(path.join(tempFolder, 'qserver'), qserverPath)
263262

264263
// download node runtime to temp folder
265264
const nodeRuntimeTempPath = path.join(tempFolder, nodeBinName)
266265
const downloadNodeOk = await this.downloadAndCheckHash(nodeRuntimeTempPath, nodeRuntimeContent)
267266
if (!downloadNodeOk) {
268267
return false
269268
}
270-
await fs2.chmod(nodeRuntimeTempPath, 0o755)
271-
fs.moveSync(nodeRuntimeTempPath, nodeRuntimePath)
269+
await fs.chmod(nodeRuntimeTempPath, 0o755)
270+
await fs.rename(nodeRuntimeTempPath, nodeRuntimePath)
272271
return true
273272
} catch (e) {
274273
getLogger().error(`LspController: Failed to setup LSP server ${e}`)

packages/core/src/awsService/iot/commands/createCert.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
*/
55

66
import * as vscode from 'vscode'
7-
import * as fs from 'fs-extra'
87
import * as path from 'path'
98
import { getLogger } from '../../../shared/logger'
109
import { localize } from '../../../shared/utilities/vsCodeUtils'
1110
import { showViewLogsMessage } from '../../../shared/utilities/messages'
1211
import { IotCertsFolderNode } from '../explorer/iotCertFolderNode'
1312
import { fileExists } from '../../../shared/filesystemUtilities'
1413
import { Iot } from 'aws-sdk'
14+
import { fs } from '../../../shared'
1515

1616
// eslint-disable-next-line @typescript-eslint/naming-convention
1717
const MODE_RW_R_R = 0o644 //File permission 0644 rw-r--r-- for PEM files.

packages/core/src/awsService/iot/commands/createPolicy.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
*/
55

66
import * as vscode from 'vscode'
7-
import * as fs from 'fs-extra'
87
import { getLogger } from '../../../shared/logger'
98
import { localize } from '../../../shared/utilities/vsCodeUtils'
109
import { showViewLogsMessage } from '../../../shared/utilities/messages'
1110
import { IotPolicyFolderNode } from '../explorer/iotPolicyFolderNode'
11+
import { fs } from '../../../shared'
1212

1313
/**
1414
* Creates a policy from a policy document.
@@ -49,7 +49,7 @@ export async function createPolicyCommand(node: IotPolicyFolderNode, getPolicyDo
4949
await node.refreshNode()
5050
}
5151

52-
export async function getPolicyDocument(): Promise<Buffer | undefined> {
52+
export async function getPolicyDocument(): Promise<Uint8Array | undefined> {
5353
const fileLocation = await vscode.window.showOpenDialog({
5454
canSelectFolders: false,
5555
canSelectFiles: true,
@@ -64,7 +64,7 @@ export async function getPolicyDocument(): Promise<Buffer | undefined> {
6464

6565
const policyLocation = fileLocation[0]
6666

67-
let data: Buffer
67+
let data: Uint8Array
6868
try {
6969
data = await fs.readFile(policyLocation.fsPath)
7070
} catch (e) {

packages/core/src/codecatalyst/model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { DevEnvClient } from '../shared/clients/devenvClient'
1818
import { getLogger } from '../shared/logger'
1919
import { AsyncCollection, toCollection } from '../shared/utilities/asyncCollection'
2020
import { getCodeCatalystSpaceName, getCodeCatalystProjectName, getCodeCatalystDevEnvId } from '../shared/vscode/env'
21-
import { writeFile } from 'fs-extra'
2221
import { sshAgentSocketVariable, startSshAgent, startVscodeRemote } from '../shared/extensions/ssh'
2322
import { ChildProcess } from '../shared/utilities/processUtils'
2423
import { isDevenvVscode } from './utils'
@@ -31,6 +30,7 @@ import { ToolkitError } from '../shared/errors'
3130
import { Result } from '../shared/utilities/result'
3231
import { VscodeRemoteConnection, ensureDependencies } from '../shared/remoteSession'
3332
import { SshConfig, sshLogFileLocation } from '../shared/sshConfig'
33+
import { fs } from '../shared'
3434

3535
export type DevEnvironmentId = Pick<DevEnvironment, 'id' | 'org' | 'project'>
3636
export const connectScriptPrefix = 'codecatalyst_connect'
@@ -134,7 +134,7 @@ export function createBoundProcess(envProvider: EnvProvider): typeof ChildProces
134134
}
135135

136136
export async function cacheBearerToken(bearerToken: string, devenvId: string): Promise<void> {
137-
await writeFile(bearerTokenCacheLocation(devenvId), `${bearerToken}`, 'utf8')
137+
await fs.writeFile(bearerTokenCacheLocation(devenvId), `${bearerToken}`, 'utf8')
138138
}
139139

140140
export function bearerTokenCacheLocation(devenvId: string): string {

packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
import * as vscode from 'vscode'
6-
import * as fs from 'fs'
6+
import * as nodefs from 'fs'
77
import * as path from 'path'
88
import * as os from 'os'
99
import * as codeWhisperer from '../../client/codewhisperer'
@@ -44,7 +44,7 @@ import { AuthUtil } from '../../util/authUtil'
4444
import { createCodeWhispererChatStreamingClient } from '../../../shared/clients/codewhispererChatClient'
4545
import { downloadExportResultArchive } from '../../../shared/utilities/download'
4646
import { ExportIntent, TransformationDownloadArtifactType } from '@amzn/codewhisperer-streaming'
47-
import fs2 from '../../../shared/fs/fs'
47+
import fs from '../../../shared/fs/fs'
4848
import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession'
4949
import { convertToTimeString, encodeHTML } from '../../../shared/utilities/textUtilities'
5050

@@ -109,7 +109,7 @@ export async function uploadArtifactToS3(
109109
) {
110110
throwIfCancelled()
111111
try {
112-
const uploadFileByteSize = (await fs.promises.stat(fileName)).size
112+
const uploadFileByteSize = (await nodefs.promises.stat(fileName)).size
113113
getLogger().info(
114114
`Uploading project artifact at %s with checksum %s using uploadId: %s and size %s kB`,
115115
fileName,
@@ -177,7 +177,7 @@ export async function stopJob(jobId: string) {
177177
}
178178

179179
export async function uploadPayload(payloadFileName: string, uploadContext?: UploadContext) {
180-
const buffer = fs.readFileSync(payloadFileName)
180+
const buffer = Buffer.from(await fs.readFile(payloadFileName))
181181
const sha256 = getSha256(buffer)
182182

183183
throwIfCancelled()
@@ -249,7 +249,7 @@ function isExcludedDependencyFile(path: string): boolean {
249249
* getFilesRecursively on the source code folder.
250250
*/
251251
function getFilesRecursively(dir: string, isDependenciesFolder: boolean): string[] {
252-
const entries = fs.readdirSync(dir, { withFileTypes: true })
252+
const entries = nodefs.readdirSync(dir, { withFileTypes: true })
253253
const files = entries.flatMap((entry) => {
254254
const res = path.resolve(dir, entry.name)
255255
// exclude 'target' directory from ZIP (except if zipping dependencies) due to issues in backend
@@ -301,22 +301,22 @@ export async function zipCode({ dependenciesFolder, humanInTheLoopFlag, modulePa
301301
const sourceFiles = getFilesRecursively(modulePath, false)
302302
let sourceFilesSize = 0
303303
for (const file of sourceFiles) {
304-
if (fs.statSync(file).isDirectory()) {
304+
if (nodefs.statSync(file).isDirectory()) {
305305
getLogger().info('CodeTransformation: Skipping directory, likely a symlink')
306306
continue
307307
}
308308
const relativePath = path.relative(modulePath, file)
309309
const paddedPath = path.join('sources', relativePath)
310310
zip.addLocalFile(file, path.dirname(paddedPath))
311-
sourceFilesSize += (await fs.promises.stat(file)).size
311+
sourceFilesSize += (await nodefs.promises.stat(file)).size
312312
}
313313
getLogger().info(`CodeTransformation: source code files size = ${sourceFilesSize}`)
314314
}
315315

316316
throwIfCancelled()
317317

318318
let dependencyFiles: string[] = []
319-
if (fs.existsSync(dependenciesFolder.path)) {
319+
if (await fs.exists(dependenciesFolder.path)) {
320320
dependencyFiles = getFilesRecursively(dependenciesFolder.path, true)
321321
}
322322

@@ -330,7 +330,7 @@ export async function zipCode({ dependenciesFolder, humanInTheLoopFlag, modulePa
330330
// const paddedPath = path.join(`dependencies/${dependenciesFolder.name}`, relativePath)
331331
const paddedPath = path.join(`dependencies/`, relativePath)
332332
zip.addLocalFile(file, path.dirname(paddedPath))
333-
dependencyFilesSize += (await fs.promises.stat(file)).size
333+
dependencyFilesSize += (await nodefs.promises.stat(file)).size
334334
}
335335
getLogger().info(`CodeTransformation: dependency files size = ${dependencyFilesSize}`)
336336
dependenciesCopied = true
@@ -353,19 +353,19 @@ export async function zipCode({ dependenciesFolder, humanInTheLoopFlag, modulePa
353353
}
354354

355355
tempFilePath = path.join(os.tmpdir(), 'zipped-code.zip')
356-
fs.writeFileSync(tempFilePath, zip.toBuffer())
357-
if (fs.existsSync(dependenciesFolder.path)) {
358-
fs.rmSync(dependenciesFolder.path, { recursive: true, force: true })
356+
await fs.writeFile(tempFilePath, zip.toBuffer())
357+
if (await fs.exists(dependenciesFolder.path)) {
358+
await fs.delete(dependenciesFolder.path, { recursive: true, force: true })
359359
}
360360
} catch (e: any) {
361361
throw Error('Failed to zip project')
362362
} finally {
363363
if (logFilePath) {
364-
fs.rmSync(logFilePath)
364+
await fs.delete(logFilePath)
365365
}
366366
}
367367

368-
const zipSize = (await fs.promises.stat(tempFilePath)).size
368+
const zipSize = (await nodefs.promises.stat(tempFilePath)).size
369369

370370
const exceedsLimit = zipSize > CodeWhispererConstants.uploadZipSizeLimitInBytes
371371

@@ -409,7 +409,7 @@ export async function startJob(uploadId: string) {
409409
}
410410

411411
export function getImageAsBase64(filePath: string) {
412-
const fileContents = fs.readFileSync(filePath, { encoding: 'base64' })
412+
const fileContents = nodefs.readFileSync(filePath, { encoding: 'base64' })
413413
return `data:image/svg+xml;base64,${fileContents}`
414414
}
415415

@@ -725,9 +725,9 @@ export async function downloadAndExtractResultArchive(
725725
pathToArchiveDir: string,
726726
downloadArtifactType: TransformationDownloadArtifactType
727727
) {
728-
const archivePathExists = await fs2.existsDir(pathToArchiveDir)
728+
const archivePathExists = await fs.existsDir(pathToArchiveDir)
729729
if (!archivePathExists) {
730-
await fs2.mkdir(pathToArchiveDir)
730+
await fs.mkdir(pathToArchiveDir)
731731
}
732732

733733
const pathToArchive = path.join(pathToArchiveDir, 'ExportResultsArchive.zip')

packages/core/src/dev/codecatalyst.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
import { glob } from 'glob'
7-
import * as fs from 'fs-extra'
87
import * as path from 'path'
98
import * as vscode from 'vscode'
109
import * as manifest from '../../package.json'
@@ -21,6 +20,7 @@ import { startVscodeRemote } from '../shared/extensions/ssh'
2120
import { isValidResponse } from '../shared/wizards/wizard'
2221
import { createQuickPick } from '../shared/ui/pickerPrompter'
2322
import { createCommonButtons } from '../shared/ui/buttons'
23+
import { fs } from '../shared'
2424

2525
type LazyProgress<T> = vscode.Progress<T> & vscode.Disposable & { getToken(): Timeout }
2626

@@ -217,7 +217,7 @@ async function installVsix(
217217
if (path.extname(resp) !== '.vsix') {
218218
progress.report({ message: 'Copying extension...' })
219219

220-
const packageData = await fs.readFile(path.join(resp, 'package.json'), 'utf-8')
220+
const packageData = await fs.readFileAsString(path.join(resp, 'package.json'))
221221
const targetManfiest: typeof manifest = JSON.parse(packageData)
222222
const destName = `${extPath}/${extId}-${targetManfiest.version}`
223223
const source = `${resp}${path.sep}`

packages/core/src/dynamicResources/awsResourceManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
import { writeFileSync } from 'fs'
7-
import { remove } from 'fs-extra'
87
import * as path from 'path'
98
import * as vscode from 'vscode'
109
import { CloudFormationClient } from '../shared/clients/cloudFormationClient'
@@ -20,6 +19,7 @@ import { ResourceNode } from './explorer/nodes/resourceNode'
2019
import { ResourceTypeNode } from './explorer/nodes/resourceTypeNode'
2120
import { isCloud9 } from '../shared/extensionUtilities'
2221
import globals from '../shared/extensionGlobals'
22+
import { fs } from '../shared'
2323

2424
export const resourceFileGlobPattern = '**/*.awsResource.json'
2525

@@ -97,7 +97,7 @@ export class AwsResourceManager {
9797
}
9898

9999
if (uri.scheme === 'file') {
100-
await remove(uri.fsPath)
100+
await fs.delete(uri.fsPath)
101101

102102
globals.schemaService.registerMapping({
103103
uri,

0 commit comments

Comments
 (0)