diff --git a/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.test.ts b/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.test.ts index 70a12187d2..1d6e9fff20 100644 --- a/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.test.ts +++ b/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.test.ts @@ -316,4 +316,44 @@ describe('copyConfigKeyEntry', () => { await expect(fileExists(joinPath(outDir, 'tools.json'))).resolves.toBe(true) }) }) + + describe('value guard', () => { + test('throws when value is an empty string', async () => { + await inTemporaryDirectory(async (tmpDir) => { + const outDir = joinPath(tmpDir, 'out') + await mkdir(outDir) + const context = makeContext({assets: ''}) + const promise = copyConfigKeyEntry({key: 'assets', baseDir: tmpDir, outputDir: outDir, context}) + await expect(promise).rejects.toThrow(AbortError) + await expect(promise).rejects.toThrow(`'assets' can't be empty.`) + }) + }) + + test('throws when value is whitespace-only', async () => { + await inTemporaryDirectory(async (tmpDir) => { + const outDir = joinPath(tmpDir, 'out') + await mkdir(outDir) + const context = makeContext({assets: ' '}) + const promise = copyConfigKeyEntry({key: 'assets', baseDir: tmpDir, outputDir: outDir, context}) + await expect(promise).rejects.toThrow(AbortError) + await expect(promise).rejects.toThrow(`'assets' can't be empty.`) + }) + }) + + test('throws with the field name only, not the full configKey, when the key is nested', async () => { + await inTemporaryDirectory(async (tmpDir) => { + const outDir = joinPath(tmpDir, 'out') + await mkdir(outDir) + const context = makeContext({extension_points: [{assets: ''}]}) + const promise = copyConfigKeyEntry({ + key: 'extension_points[].assets', + baseDir: tmpDir, + outputDir: outDir, + context, + }) + await expect(promise).rejects.toThrow(AbortError) + await expect(promise).rejects.toThrow(`'assets' can't be empty.`) + }) + }) + }) }) diff --git a/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.ts b/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.ts index edb9b9e6b3..96e7098efe 100644 --- a/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.ts +++ b/packages/app/src/cli/services/build/steps/include-assets/copy-config-key-entry.ts @@ -48,6 +48,13 @@ export async function copyConfigKeyEntry(config: { paths = [] } + for (const sourcePath of paths) { + if (sourcePath.trim() === '') { + const fieldName = key.split('.').pop()?.replace(/\[\]$/, '') ?? key + throw new AbortError(`'${fieldName}' can't be empty.`) + } + } + if (paths.length === 0) { outputDebug(`No value for configKey '${key}', skipping\n`, stdout) return {filesCopied: 0, pathMap: new Map()}