From 10b14b36decf6de96ed93745c9aa6e6b3b1ac4ef Mon Sep 17 00:00:00 2001 From: D N <4661784+retyui@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:08:19 +0200 Subject: [PATCH] feat: No json as string in JS bundle issue: https://github.com/callstack/repack/issues/1461 --- .changeset/raw-json-assets.md | 5 ++ .../getAssetTransformRules.test.ts.snap | 72 +++++++++++++++++++ .../__tests__/getAssetTransformRules.test.ts | 42 +++++++---- .../src/utils/getAssetTransformRules.ts | 18 +++++ .../api/utils/get-asset-transform-rules.md | 12 ++++ 5 files changed, 137 insertions(+), 12 deletions(-) create mode 100644 .changeset/raw-json-assets.md diff --git a/.changeset/raw-json-assets.md b/.changeset/raw-json-assets.md new file mode 100644 index 000000000..bba5b589e --- /dev/null +++ b/.changeset/raw-json-assets.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack": minor +--- + +Add `enableRawJson` option to `getAssetTransformRules`. It is enabled by default and adds a `type: "json"` rule with `generator: { JSONParse: false }`, so `.json` files are bundled as raw text and parsed at runtime with `JSON.parse('...')` instead of being parsed into an object at build time. Pass `enableRawJson: false` to keep the previous behavior. diff --git a/packages/repack/src/utils/__tests__/__snapshots__/getAssetTransformRules.test.ts.snap b/packages/repack/src/utils/__tests__/__snapshots__/getAssetTransformRules.test.ts.snap index c99a6420e..efa357d7c 100644 --- a/packages/repack/src/utils/__tests__/__snapshots__/getAssetTransformRules.test.ts.snap +++ b/packages/repack/src/utils/__tests__/__snapshots__/getAssetTransformRules.test.ts.snap @@ -2,6 +2,13 @@ exports[`getAssetTransformRules should add SVGR rule when svg="svgr" 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { @@ -27,6 +34,13 @@ exports[`getAssetTransformRules should add SVGR rule when svg="svgr" 1`] = ` exports[`getAssetTransformRules should add URI rule when svg="uri" 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { @@ -47,6 +61,13 @@ exports[`getAssetTransformRules should add URI rule when svg="uri" 1`] = ` exports[`getAssetTransformRules should add XML rule when svg="xml" 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { @@ -67,6 +88,13 @@ exports[`getAssetTransformRules should add XML rule when svg="xml" 1`] = ` exports[`getAssetTransformRules should include additional options for SVGR 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { @@ -91,8 +119,31 @@ exports[`getAssetTransformRules should include additional options for SVGR 1`] = ] `; +exports[`getAssetTransformRules should omit the raw JSON rule when enableRawJson is false 1`] = ` +[ + { + "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|svg\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, + "use": { + "loader": "@callstack/repack/assets-loader", + "options": { + "inline": undefined, + "maxInlineSize": undefined, + "remote": undefined, + }, + }, + }, +] +`; + exports[`getAssetTransformRules should return default asset transform rules when no options provided 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|svg\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { @@ -109,6 +160,13 @@ exports[`getAssetTransformRules should return default asset transform rules when exports[`getAssetTransformRules should return rules with inline option when provided 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|svg\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { @@ -125,6 +183,13 @@ exports[`getAssetTransformRules should return rules with inline option when prov exports[`getAssetTransformRules should return rules with maxInlineSize option when provided 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|svg\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { @@ -141,6 +206,13 @@ exports[`getAssetTransformRules should return rules with maxInlineSize option wh exports[`getAssetTransformRules should return rules with remote options when provided 1`] = ` [ + { + "generator": { + "JSONParse": false, + }, + "test": /\\\\\\.json\\$/, + "type": "json", + }, { "test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|svg\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/, "use": { diff --git a/packages/repack/src/utils/__tests__/getAssetTransformRules.test.ts b/packages/repack/src/utils/__tests__/getAssetTransformRules.test.ts index fe5fa96e9..534e5a8d0 100644 --- a/packages/repack/src/utils/__tests__/getAssetTransformRules.test.ts +++ b/packages/repack/src/utils/__tests__/getAssetTransformRules.test.ts @@ -10,7 +10,7 @@ describe('getAssetTransformRules', () => { const rules = getAssetTransformRules({ inline: true }); // @ts-expect-error - expect(rules[0]?.use?.options?.inline).toEqual(true); + expect(rules[1]?.use?.options?.inline).toEqual(true); expect(rules).toMatchSnapshot(); }); @@ -18,7 +18,7 @@ describe('getAssetTransformRules', () => { const rules = getAssetTransformRules({ maxInlineSize: 1024 }); // @ts-expect-error - expect(rules[0]?.use?.options?.maxInlineSize).toEqual(1024); + expect(rules[1]?.use?.options?.maxInlineSize).toEqual(1024); expect(rules).toMatchSnapshot(); }); @@ -27,39 +27,39 @@ describe('getAssetTransformRules', () => { const rules = getAssetTransformRules({ remote: remoteOptions }); // @ts-expect-error - expect(rules[0]?.use?.options?.remote).toHaveProperty('enabled', true); + expect(rules[1]?.use?.options?.remote).toHaveProperty('enabled', true); expect(rules).toMatchSnapshot(); }); it('should add SVGR rule when svg="svgr"', () => { const rules = getAssetTransformRules({ svg: 'svgr' }); - expect(rules).toHaveLength(2); - expect(rules[1]?.use?.loader).toEqual('@svgr/webpack'); + expect(rules).toHaveLength(3); + expect(rules[2]?.use?.loader).toEqual('@svgr/webpack'); expect(rules).toMatchSnapshot(); }); it('should add XML rule when svg="xml"', () => { const rules = getAssetTransformRules({ svg: 'xml' }); - expect(rules).toHaveLength(2); + expect(rules).toHaveLength(3); // @ts-expect-error - expect(rules[1]?.type).toEqual('asset/source'); + expect(rules[2]?.type).toEqual('asset/source'); expect(rules).toMatchSnapshot(); }); it('should add URI rule when svg="uri"', () => { const rules = getAssetTransformRules({ svg: 'uri' }); - expect(rules).toHaveLength(2); + expect(rules).toHaveLength(3); // @ts-expect-error - expect(rules[1]?.type).toEqual('asset/inline'); + expect(rules[2]?.type).toEqual('asset/inline'); expect(rules).toMatchSnapshot(); }); it('should exclude .svg from main asset extensions when svg option is provided', () => { const rules = getAssetTransformRules({ svg: 'uri' }); - const ruleTest = rules[0]?.test; + const ruleTest = rules[1]?.test; expect(ruleTest.test('test.svg')).toEqual(false); }); @@ -68,8 +68,26 @@ describe('getAssetTransformRules', () => { svg: { type: 'svgr', options: { dimensions: false } }, }); - expect(rules).toHaveLength(2); - expect(rules[1]?.use?.options).toHaveProperty('dimensions', false); + expect(rules).toHaveLength(3); + expect(rules[2]?.use?.options).toHaveProperty('dimensions', false); + expect(rules).toMatchSnapshot(); + }); + + it('should add raw JSON rule by default', () => { + const rules = getAssetTransformRules(); + + expect(rules[0]).toEqual({ + test: /\.json$/, + type: 'json', + generator: { JSONParse: false }, + }); + }); + + it('should omit the raw JSON rule when enableRawJson is false', () => { + const rules = getAssetTransformRules({ enableRawJson: false }); + + expect(rules).toHaveLength(1); + expect(rules[0]?.test.source).not.toContain('json'); expect(rules).toMatchSnapshot(); }); }); diff --git a/packages/repack/src/utils/getAssetTransformRules.ts b/packages/repack/src/utils/getAssetTransformRules.ts index c3040121d..203d0af96 100644 --- a/packages/repack/src/utils/getAssetTransformRules.ts +++ b/packages/repack/src/utils/getAssetTransformRules.ts @@ -58,6 +58,14 @@ interface GetAssetTransformRulesOptions { * - 'uri': Loads SVGs as inline URIs to be used with SvgUri from react-native-svg */ svg?: SvgType; + + /** + * Whether to load `.json` files as raw text instead of parsing them at build time. + * When enabled, JSON files are emitted as a string and parsed at runtime with `JSON.parse('...')`. + * + * @defaultValue true + */ + enableRawJson?: boolean; } /** @@ -68,6 +76,7 @@ interface GetAssetTransformRulesOptions { * @param options.maxInlineSize Maximum asset file size in bytes to inline as base64 URIs (requires inline: true); larger assets are extracted as separate files * @param options.remote Configuration for remote asset loading with publicPath and optional assetPath function * @param options.svg Determines how SVG files should be processed ('svgr', 'xml', or 'uri') + * @param options.enableRawJson Whether to load `.json` files as raw text parsed at runtime with `JSON.parse` (defaults to true) * * @returns Array of webpack/rspack rules for transforming assets */ @@ -76,6 +85,7 @@ export function getAssetTransformRules({ maxInlineSize, remote, svg, + enableRawJson = true, }: GetAssetTransformRulesOptions = {}) { const extensions = svg ? ASSET_EXTENSIONS.filter((ext) => ext !== 'svg') @@ -90,6 +100,14 @@ export function getAssetTransformRules({ const rules = []; + if (enableRawJson) { + rules.push({ + test: /\.json$/, + type: 'json', + generator: { JSONParse: false }, + }); + } + rules.push({ test: getAssetExtensionsRegExp(extensions), use: { diff --git a/website/src/latest/api/utils/get-asset-transform-rules.md b/website/src/latest/api/utils/get-asset-transform-rules.md index 5210df214..17d164688 100644 --- a/website/src/latest/api/utils/get-asset-transform-rules.md +++ b/website/src/latest/api/utils/get-asset-transform-rules.md @@ -22,6 +22,7 @@ interface GetAssetTransformRulesOptions { }) => string; }; svg?: "svgr" | "xml" | "uri" | { type: "svgr", options: Record }; + enableRawJson?: boolean; } ``` @@ -97,6 +98,17 @@ Allows to configure additional options to `@svgr/webpack`. The full list of avai Learn more about using SVG in the [SVG guide](/docs/guides/svg). ::: +### options.enableRawJson + +- Type: `boolean` +- Default: `true` + +Whether to load `.json` files as raw text instead of parsing them at build time. + +When enabled (the default), a rule with `type: "json"` and `generator: { JSONParse: false }` is added, so the bundled JSON is emitted as a string and parsed at runtime with `JSON.parse('...')`. This keeps the bundle smaller for large JSON files, since the parsed object is not inlined into the bundle. + +Set it to `false` to disable the rule and let the bundler parse `.json` files at build time. + ## Example ```js title=rspack.config.cjs