Skip to content

[13.x] Add support for Vite's chunk import map - #60874

Draft
julesjanssen wants to merge 2 commits into
laravel:13.xfrom
julesjanssen:feature/vite-chunk-import-map
Draft

[13.x] Add support for Vite's chunk import map#60874
julesjanssen wants to merge 2 commits into
laravel:13.xfrom
julesjanssen:feature/vite-chunk-import-map

Conversation

@julesjanssen

Copy link
Copy Markdown
Contributor

Adds support for Vite's build.chunkImportMap option.

With chunkImportMap enabled, Vite makes generated chunks import one another through stable identifiers instead of hashed filenames, and emits an importmap.json mapping those identifiers to the real files. This improves long-term caching: changing one chunk no longer cascades new hashes onto every chunk that imports it.

For it to work, a <script type="importmap"> must appear before the module scripts. Vite injects it when it controls the HTML, but Laravel renders its own tags from the manifest. Currently, enabling chunkImportMap silently breaks module resolution.

This PR adds import-map injection to the Vite class:

  • When public/build/importmap.json exists, @vite emits <script type="importmap">…</script> ahead of the modulepreload links and module scripts.
  • Fully opt-in and backward compatible. Nothing changes unless that file is present (i.e. you turned on chunkImportMap).
  • The tag honors the configured CSP nonce.
  • Adds Vite::useImportMapFilename() for parity with useManifestFilename(), in case the file is renamed in the Vite config.

Usage:

// vite.config.js
export default defineConfig({
  build: {
    chunkImportMap: true,
  },
});

Tests cover injection, ordering (import map before the module scripts), the CSP nonce, and the custom filename.

Note: chunkImportMap is still marked experimental in Vite.

@pascalbaljet

Copy link
Copy Markdown
Member

@julesjanssen Thanks for this! Any reason behind json_decode + json_encode rather than passing the file contents straight through?

@julesjanssen

Copy link
Copy Markdown
Contributor Author

The JSON_HEX_TAG option (which is used in all cases where the framework outputs JSON), can help to avoid any HTML injection, although this file shouldn't contain any user-input. This mechanism still serves as a sort of validation though. If the file is corrupted in any way, it will not output a broken import-map.
It also matches the way this class handles manifest.json. But we're not doing anything with the content of this file, so it's not strictly necessary.

@taylorotwell

Copy link
Copy Markdown
Member

Agent review surfaced a situation for CDN based deployments that may warrant consideration.

Transform each Vite-generated import-map target through the same resolver used for module tags.
For the normal Laravel Vite output:

{
  "imports": {
    "/build/assets/chunk-stable.js": "/build/assets/chunk-abc123.js"
  }
}

the inline map should become:

{
  "imports": {
    "/build/assets/chunk-stable.js": "https://cdn.example.com/build/assets/chunk-abc123.js"
  }
}

while leaving the stable specifier key unchanged, because that is what Vite writes into import statements.
A minimal implementation would normalize only map values that point into the configured build directory, then use assetPath():

protected function makeImportMapTag($importMap)
{
    foreach ($importMap['imports'] ?? [] as $specifier => $url) {
        if (str_starts_with($url, '/'.$this->buildDirectory.'/')) {
            $importMap['imports'][$specifier] = $this->assetPath(ltrim($url, '/'));
        }
    }

    return '<script type="importmap"'.$this->nonceAttribute().'>'
        .json_encode($importMap, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG)
        .'</script>';
}

The actual version should pass the effective $buildDirectory into the helper, since __invoke() can override it. It should also add a test asserting that an https://cdn.example.com asset URL is present in both the entry <script> and import-map target.

@taylorotwell
taylorotwell marked this pull request as draft August 2, 2026 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants