From f2aed62d0bf1b66e870ee6b4aab80cd1702793ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Padier?= Date: Wed, 15 Jan 2025 09:33:12 +0100 Subject: [PATCH] fix: tree shake stringified JSON imports (#19189) --- .../vite/src/node/__tests__/build.spec.ts | 63 +++++++++++++++++++ .../src/node/__tests__/plugins/json.spec.ts | 6 +- packages/vite/src/node/plugins/json.ts | 4 +- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/__tests__/build.spec.ts b/packages/vite/src/node/__tests__/build.spec.ts index 3c4be301e6d528..f5dd1419f04d7c 100644 --- a/packages/vite/src/node/__tests__/build.spec.ts +++ b/packages/vite/src/node/__tests__/build.spec.ts @@ -134,6 +134,69 @@ describe('build', () => { assertOutputHashContentChange(result[0], result[1]) }) + test.for([ + [true, true], + [true, false], + [false, true], + [false, false], + ['auto', true], + ['auto', false], + ] as const)( + 'large json object files should have tree-shaking (json.stringify: %s, json.namedExports: %s)', + async ([stringify, namedExports]) => { + const esBundle = (await build({ + mode: 'development', + root: resolve(__dirname, 'packages/build-project'), + logLevel: 'silent', + json: { stringify, namedExports }, + build: { + minify: false, + modulePreload: { polyfill: false }, + write: false, + }, + plugins: [ + { + name: 'test', + resolveId(id) { + if ( + id === 'entry.js' || + id === 'object.json' || + id === 'array.json' + ) { + return '\0' + id + } + }, + load(id) { + if (id === '\0entry.js') { + return ` + import object from 'object.json'; + import array from 'array.json'; + console.log(); + ` + } + if (id === '\0object.json') { + return ` + {"value": {"${stringify}_${namedExports}":"JSON_OBJ${'_'.repeat(10_000)}"}} + ` + } + if (id === '\0array.json') { + return ` + ["${stringify}_${namedExports}","JSON_ARR${'_'.repeat(10_000)}"] + ` + } + }, + }, + ], + })) as RollupOutput + + const foo = esBundle.output.find( + (chunk) => chunk.type === 'chunk' && chunk.isEntry, + ) as OutputChunk + expect(foo.code).not.contains('JSON_ARR') + expect(foo.code).not.contains('JSON_OBJ') + }, + ) + test('external modules should not be hoisted in library build', async () => { const [esBundle] = (await build({ logLevel: 'silent', diff --git a/packages/vite/src/node/__tests__/plugins/json.spec.ts b/packages/vite/src/node/__tests__/plugins/json.spec.ts index 3e114fd3dd145b..e90bcb39c22737 100644 --- a/packages/vite/src/node/__tests__/plugins/json.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/json.spec.ts @@ -62,7 +62,7 @@ describe('transform', () => { false, ) expect(actualSmall).toMatchInlineSnapshot( - `"export default JSON.parse("[{\\"a\\":1,\\"b\\":2}]")"`, + `"export default /* #__PURE__ */ JSON.parse("[{\\"a\\":1,\\"b\\":2}]")"`, ) }) @@ -122,7 +122,7 @@ describe('transform', () => { false, ) expect(actualDev).toMatchInlineSnapshot( - `"export default JSON.parse("{\\"a\\":1,\\n\\"🫠\\": \\"\\",\\n\\"const\\": false}")"`, + `"export default /* #__PURE__ */ JSON.parse("{\\"a\\":1,\\n\\"🫠\\": \\"\\",\\n\\"const\\": false}")"`, ) const actualBuild = transform( @@ -131,7 +131,7 @@ describe('transform', () => { true, ) expect(actualBuild).toMatchInlineSnapshot( - `"export default JSON.parse("{\\"a\\":1,\\"🫠\\":\\"\\",\\"const\\":false}")"`, + `"export default /* #__PURE__ */ JSON.parse("{\\"a\\":1,\\"🫠\\":\\"\\",\\"const\\":false}")"`, ) }) diff --git a/packages/vite/src/node/plugins/json.ts b/packages/vite/src/node/plugins/json.ts index 33c1c0d27aeaac..0234d471d39243 100644 --- a/packages/vite/src/node/plugins/json.ts +++ b/packages/vite/src/node/plugins/json.ts @@ -87,7 +87,7 @@ export function jsonPlugin( } return { - code: `export default JSON.parse(${JSON.stringify(json)})`, + code: `export default /* #__PURE__ */ JSON.parse(${JSON.stringify(json)})`, map: { mappings: '' }, } } @@ -120,7 +120,7 @@ function serializeValue(value: unknown): string { value != null && valueAsString.length > 10 * 1000 ) { - return `JSON.parse(${JSON.stringify(valueAsString)})` + return `/* #__PURE__ */ JSON.parse(${JSON.stringify(valueAsString)})` } return valueAsString }