From 998ccf6620f35cd9a576350fb004bf7884e7ef19 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Wed, 31 Jan 2024 10:47:22 +0000 Subject: [PATCH] app-backend: replace all instances when injecting config Signed-off-by: MT Lewis --- .changeset/red-eggs-serve.md | 5 ++ plugins/app-backend/src/lib/config.test.ts | 73 ++++++++++++++++++++++ plugins/app-backend/src/lib/config.ts | 6 +- 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 .changeset/red-eggs-serve.md diff --git a/.changeset/red-eggs-serve.md b/.changeset/red-eggs-serve.md new file mode 100644 index 0000000000..1b2c9b3b07 --- /dev/null +++ b/.changeset/red-eggs-serve.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app-backend': patch +--- + +Support injecting config multiple times in a single bundle diff --git a/plugins/app-backend/src/lib/config.test.ts b/plugins/app-backend/src/lib/config.test.ts index f36052116a..ed2b8b8777 100644 --- a/plugins/app-backend/src/lib/config.test.ts +++ b/plugins/app-backend/src/lib/config.test.ts @@ -62,6 +62,28 @@ describe('injectConfig', () => { expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]))).toEqual([]); }); + it('should inject config repeatedly if marker appears multiple times', async () => { + fsMock.readdir.mockResolvedValue(['main.js'] as any); + readFileMock.mockImplementation( + async () => + '({a:"__APP_INJECTED_RUNTIME_CONFIG__",b:"__APP_INJECTED_RUNTIME_CONFIG__"})', + ); + await injectConfig(baseOptions); + expect(fsMock.readdir).toHaveBeenCalledTimes(1); + expect(fsMock.readFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledWith( + resolvePath(MOCK_DIR, 'main.js'), + '({a:/*__APP_INJECTED_CONFIG_MARKER__*/"[]"/*__INJECTED_END__*/,b:/*__APP_INJECTED_CONFIG_MARKER__*/"[]"/*__INJECTED_END__*/})', + 'utf8', + ); + + // eslint-disable-next-line no-eval + expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]).a)).toEqual([]); + // eslint-disable-next-line no-eval + expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]).b)).toEqual([]); + }); + it('should find the correct file to inject', async () => { fsMock.readdir.mockResolvedValue([ 'before.js', @@ -152,4 +174,55 @@ describe('injectConfig', () => { { data: { x: 1, y: 2 }, context: 'test' }, ]); }); + + it('should re-inject config repeatedly if needed', async () => { + fsMock.readdir.mockResolvedValue(['main.js'] as any); + readFileMock.mockResolvedValue( + '({ a: JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__"), b: JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__") })', + ); + + await injectConfig({ + ...baseOptions, + appConfigs: [{ data: { x: 0 }, context: 'test' }], + }); + + expect(fsMock.writeFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledWith( + resolvePath(MOCK_DIR, 'main.js'), + '({ a: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/), b: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/) })', + 'utf8', + ); + + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[0][1]).a).toEqual([ + { data: { x: 0 }, context: 'test' }, + ]); + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[0][1]).b).toEqual([ + { data: { x: 0 }, context: 'test' }, + ]); + + readFileMock.mockResolvedValue(fsMock.writeFile.mock.calls[0][1]); + + await injectConfig({ + ...baseOptions, + appConfigs: [{ data: { x: 1, y: 2 }, context: 'test' }], + }); + + expect(fsMock.writeFile).toHaveBeenCalledTimes(2); + expect(fsMock.writeFile).toHaveBeenLastCalledWith( + resolvePath(MOCK_DIR, 'main.js'), + '({ a: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":1,\\"y\\":2},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/), b: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":1,\\"y\\":2},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/) })', + 'utf8', + ); + + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[1][1]).a).toEqual([ + { data: { x: 1, y: 2 }, context: 'test' }, + ]); + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[1][1]).b).toEqual([ + { data: { x: 1, y: 2 }, context: 'test' }, + ]); + }); }); diff --git a/plugins/app-backend/src/lib/config.ts b/plugins/app-backend/src/lib/config.ts index 05b304075e..63912df5c4 100644 --- a/plugins/app-backend/src/lib/config.ts +++ b/plugins/app-backend/src/lib/config.ts @@ -49,7 +49,7 @@ export async function injectConfig( if (content.includes('__APP_INJECTED_RUNTIME_CONFIG__')) { logger.info(`Injecting env config into ${jsFile}`); - const newContent = content.replace( + const newContent = content.replaceAll( '"__APP_INJECTED_RUNTIME_CONFIG__"', injected, ); @@ -58,8 +58,8 @@ export async function injectConfig( } else if (content.includes('__APP_INJECTED_CONFIG_MARKER__')) { logger.info(`Replacing injected env config in ${jsFile}`); - const newContent = content.replace( - /\/\*__APP_INJECTED_CONFIG_MARKER__\*\/.*\/\*__INJECTED_END__\*\//, + const newContent = content.replaceAll( + /\/\*__APP_INJECTED_CONFIG_MARKER__\*\/.*?\/\*__INJECTED_END__\*\//g, injected, ); await fs.writeFile(path, newContent, 'utf8');