diff --git a/.changeset/dirty-years-attend.md b/.changeset/dirty-years-attend.md new file mode 100644 index 0000000000..7a1bf1a2e9 --- /dev/null +++ b/.changeset/dirty-years-attend.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-function': patch +'@aws-amplify/backend': patch +--- + +fix: Clear generated env directory before shim generation diff --git a/packages/backend-function/src/function_env_type_generator.test.ts b/packages/backend-function/src/function_env_type_generator.test.ts index 15a913479c..a5d85955a3 100644 --- a/packages/backend-function/src/function_env_type_generator.test.ts +++ b/packages/backend-function/src/function_env_type_generator.test.ts @@ -1,11 +1,15 @@ -import { describe, it, mock } from 'node:test'; +import { beforeEach, describe, it, mock } from 'node:test'; import fs from 'fs'; import fsp from 'fs/promises'; import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js'; import assert from 'assert'; import { pathToFileURL } from 'url'; +import path from 'path'; void describe('FunctionEnvironmentTypeGenerator', () => { + beforeEach(() => { + resetFactoryGlobalState(); + }); void it('generates a type definition file', () => { const fsOpenSyncMock = mock.method(fs, 'openSync'); const fsWriteFileSyncMock = mock.method(fs, 'writeFileSync', () => null); @@ -101,4 +105,54 @@ void describe('FunctionEnvironmentTypeGenerator', () => { mock.restoreAll(); }); + void it('clears the generated env directory even if there are multiple calls', async () => { + const fsExistsSyncMock = mock.method(fs, 'existsSync', () => true); + const fsRmSyncMock = mock.method(fs, 'rmSync', () => {}); + + new FunctionEnvironmentTypeGenerator('testFunction1'); + + assert.equal(fsExistsSyncMock.mock.calls.length, 1); + assert.equal(fsRmSyncMock.mock.calls.length, 1); + + new FunctionEnvironmentTypeGenerator('testFunction2'); + + assert.equal(fsExistsSyncMock.mock.calls.length, 1); + assert.equal(fsRmSyncMock.mock.calls.length, 1); + + fsExistsSyncMock.mock.restore(); + fsRmSyncMock.mock.restore(); + }); + void it("don't clear the generated env directory if it doesn't exist", async () => { + const fsExistsSyncMock = mock.method(fs, 'existsSync', () => false); + const fsRmSyncMock = mock.method(fs, 'rmSync', () => {}); + + new FunctionEnvironmentTypeGenerator('testFunction'); + + assert.equal(fsExistsSyncMock.mock.calls.length, 1); + assert.equal(fsRmSyncMock.mock.calls.length, 0); + + fsExistsSyncMock.mock.restore(); + fsRmSyncMock.mock.restore(); + }); + void it('ensure correct directory is deleted', async () => { + const pathToDelete = path.join( + process.cwd(), + '.amplify', + 'generated', + 'env' + ); + const fsExistsSyncMock = mock.method(fs, 'existsSync', () => true); + const fsRmSyncMock = mock.method(fs, 'rmSync', () => {}); + + new FunctionEnvironmentTypeGenerator('testFunction'); + + assert.equal(fsExistsSyncMock.mock.calls.length, 1); + assert.equal(fsRmSyncMock.mock.calls[0].arguments[0], pathToDelete); + + fsExistsSyncMock.mock.restore(); + fsRmSyncMock.mock.restore(); + }); + const resetFactoryGlobalState = () => { + FunctionEnvironmentTypeGenerator.isEnvDirectoryInitialized = false; + }; }); diff --git a/packages/backend-function/src/function_env_type_generator.ts b/packages/backend-function/src/function_env_type_generator.ts index d01a701a19..162c5ecd37 100644 --- a/packages/backend-function/src/function_env_type_generator.ts +++ b/packages/backend-function/src/function_env_type_generator.ts @@ -7,10 +7,11 @@ import { EOL } from 'os'; * Generates a typed process.env shim for environment variables */ export class FunctionEnvironmentTypeGenerator { + // Using this to ensure directory is cleared once per synthesis + static isEnvDirectoryInitialized = false; private readonly header = '// This file is auto-generated by Amplify. Edits will be overwritten.'; - // The variable gets updated when the fully typed file is updated. private readonly envAssignment = 'export const env = process.env'; private typeDefFilePath: string; @@ -21,9 +22,14 @@ export class FunctionEnvironmentTypeGenerator { * Initialize typed process.env shim file name and location */ constructor(private readonly functionName: string) { - this.typeDefFilePath = `${process.cwd()}/.amplify/generated/env/${ - this.functionName - }.ts`; + this.typeDefFilePath = path.join( + process.cwd(), + '.amplify', + 'generated', + 'env', + `${this.functionName}.ts` + ); + this.clearGeneratedEnvDirectory(); } /** @@ -97,4 +103,22 @@ export class FunctionEnvironmentTypeGenerator { fs.writeFileSync(this.typeDefFilePath, content); }; + /** + * Clear existing files and subdirectories in the generated env directory + */ + private clearGeneratedEnvDirectory = (): void => { + if (FunctionEnvironmentTypeGenerator.isEnvDirectoryInitialized) { + return; + } + const pathToDelete = path.join( + process.cwd(), + '.amplify', + 'generated', + 'env' + ); + if (fs.existsSync(pathToDelete)) { + fs.rmSync(pathToDelete, { recursive: true, force: true }); + FunctionEnvironmentTypeGenerator.isEnvDirectoryInitialized = true; + } + }; }