Storefront component import map does not strip trailing `index` for anonymous UX components
- Dominant language
- PHP
- Stars
- 3.4k
- Forks
- 1.2k
- Avg merge
- 3d 55m
- Merged PRs (30d)
- 436
Description
### PHP Version
8.3+
### Shopware Version
6.7.12.1
### Expected behaviour
According to the component-system docs
([Directory Structure & Component Script Loading](https://github.com/shopware/shopware/tree/trunk/src/Storefront/Resources/app/storefront/src/component-system#directory-structure--component-script-loading)),
anonymous/index component layout should resolve to the **directory name** as the component name — same as the flat layout.
Both of these should produce the JS import-map specifier / `data-component` name
`MyNamespace:MyComponent`:
```
views/components/MyNamespace/MyComponent.html.twig
views/components/MyNamespace/MyComponent.js
```
```
views/components/MyNamespace/MyComponent/index.html.twig
views/components/MyNamespace/MyComponent/index.js
```
Runtime should load the built module via the import map when the template uses:
```html
```
### Actual behaviour
With the index layout, the Vite entry / import-map tag **keeps** the trailing `index` segment:
- Vite manifest `name`: `MyNamespace/MyComponent/index`
- Import map key: `MyNamespace:MyComponent:index`
- Template / Twig component name: `MyNamespace:MyComponent`
`Shopware.getComponent('MyNamespace:MyComponent')` does not find a match in the import map, falls back to the bare specifier, and the browser tries to fetch `mynamespace:MyComponent` as a custom URL scheme, which surfaces as:
```
Access to script at 'mynamespace:MyComponent' from origin '…' has been blocked by CORS policy
Failed to import component MyNamespace:MyComponent: TypeError: Failed to fetch dynamically imported module
```
This is misleading: the real failure is a **missing import-map entry**, not CORS.
Twig anonymous components work (directory name = component name). Only the **JS/CSS build + import-map aggregation** path fails to strip `/index`.
### How to reproduce
1. Create an extension component using index naming, e.g.:
```
Resources/views/components/Demo/Search/Action/index.html.twig
Resources/views/components/Demo/Search/Action/index.js
```
2. In the template:
```twig
…
```
```js
// index.js
export default class SearchAction extends ShopwareComponent {
init() { console.log('ok'); }
}
```
3. Build components and refresh theme assets/import map
(`composer build:js:storefront` / theme compile / asset install as applicable).
4. Load a storefront page that renders the component.
5. Observe console: bare-specifier fetch + failed dynamic import for `Demo:Search:Action`.
6. Inspect Vite `build-meta.json` / manifest: entry name is `Demo/Search/Action/index` → import map key `Demo:Search:Action:index`.
### Root cause (code)
Import-map tags are derived from the Vite entry name with a simple `/` → `:` replace and **no** trailing `index` normalization:
- `src/Storefront/Resources/app/storefront/build/vite/build-components.js` — `makeJsEntryName` keeps `…/index`
- `src/Storefront/Resources/app/storefront/build/vite/component-config-factory.ts` — same
- `src/Storefront/Resources/app/storefront/build/vite/dev-import-map-plugin.ts` — `fileToTag()` does not strip `/index`
- `src/Storefront/Theme/ThemeCompiler.php` (`readBundleComponentManifest`) —
`$tag = str_replace('/', ':', $entryName);` does not strip `/index`
Docs already describe the intended behaviour; implementation does not match.
### Suggested fix
Normalize entry names / tags by stripping a trailing `/index` (and `.scss`/`.css` variants consistently) in:
1. JS entry naming (`makeJsEntryName` / style entry helpers)
2. Dev import map `fileToTag()`
3. Optionally `ThemeCompiler` as a safety net for already-built manifests
After fix, index layout should map to `Namespace:Component` (not `Namespace:Component:index`).
### Workaround
Use flat file naming instead of index directories for co-located JS/SCSS:
```
MyComponent.js + MyComponent.html.twig
```
instead of
```
MyComponent/index.js + MyComponent/index.html.twig
```
Contributor guide
Assessment
This issue has not been assessed yet.