Summary
When using @analogjs/vite-plugin-angular (via @analogjs/storybook-angular), Vite's built-in ?raw import does not work for .ts files. Importing a TypeScript file with ?raw yields "default" is not exported, because the Angular plugin transforms/compiles .ts ids and shadows Vite's raw loader.
It would be great to either handle this in the plugin (skip ?raw-queried ids) or document a recommended workaround.
Environment
@analogjs/storybook-angular: 2.5.2
@analogjs/vite-plugin-angular: 2.1.3
vite: 7.1.5
@storybook/angular / storybook: 10.4.1
@angular/core: 21.2.9
Reproduction
// any .ts file imported as raw text
import source from './my-component?raw';
console.log(source); // expected: the file contents as a string
Build/serve fails with:
"default" is not exported by "….ts?raw", imported by "….ts".
Expected
import x from './file.ts?raw' returns the file contents as a string (default export), as with stock Vite.
Actual
The Angular plugin processes the .ts id regardless of the ?raw query, so Vite's raw loader never runs and there is no default export.
Use case
In a Storybook (@analogjs/storybook-angular) setup we want the "Show code" panel of a story to display the full source of a standalone story component, by feeding ?raw content into parameters.docs.source.code. The ?raw import is the natural way to do this, but it's broken by the plugin.
Workaround
A small enforce: 'pre' Vite plugin that resolves *.ts?raw to an opaque virtual module (so the Angular/esbuild transforms don't touch it) and returns the file contents:
import { readFile } from 'node:fs/promises';
import type { Plugin } from 'vite';
function rawTsPlugin(): Plugin {
const rawModules = new Map<string, string>();
let counter = 0;
return {
name: 'raw-ts',
enforce: 'pre',
async resolveId(id, importer) {
if (!id.endsWith('?raw')) return null;
const resolved = await this.resolve(id.slice(0, -'?raw'.length), importer, { skipSelf: true });
if (!resolved || !resolved.id.endsWith('.ts')) return null;
// opaque virtual id WITHOUT a `.ts` suffix, so the esbuild/Angular
// transforms don't compile it and strip the default export
const virtualId = `\0raw-${counter++}`;
rawModules.set(virtualId, resolved.id);
return virtualId;
},
async load(id) {
const file = rawModules.get(id);
if (!file) return null;
const code = await readFile(file, 'utf-8');
return `export default ${JSON.stringify(code)};`;
}
};
}
Notes from getting this to work:
- A
pre load hook on the …ts?raw id alone wasn't enough — the id still got compiled.
- Returning a virtual id that ends in
.ts also failed: the esbuild/Angular transform matched it and stripped the export default. Using an opaque id without a .ts suffix fixed it.
Proposal
- Have
@analogjs/vite-plugin-angular ignore ids with a ?raw (and probably ?url / other Vite query) suffix so Vite's built-in handling applies, or
- Document this limitation + the recommended workaround (e.g. in the AnalogJS /
storybook-angular docs).
Link to source — https://github.com/radix-ng/primitives/blob/main/apps/radix-storybook/.storybook/main.ts#L20
Summary
When using
@analogjs/vite-plugin-angular(via@analogjs/storybook-angular), Vite's built-in?rawimport does not work for.tsfiles. Importing a TypeScript file with?rawyields"default" is not exported, because the Angular plugin transforms/compiles.tsids and shadows Vite's raw loader.It would be great to either handle this in the plugin (skip
?raw-queried ids) or document a recommended workaround.Environment
@analogjs/storybook-angular: 2.5.2@analogjs/vite-plugin-angular: 2.1.3vite: 7.1.5@storybook/angular/storybook: 10.4.1@angular/core: 21.2.9Reproduction
Build/serve fails with:
Expected
import x from './file.ts?raw'returns the file contents as a string (default export), as with stock Vite.Actual
The Angular plugin processes the
.tsid regardless of the?rawquery, so Vite's raw loader never runs and there is no default export.Use case
In a Storybook (
@analogjs/storybook-angular) setup we want the "Show code" panel of a story to display the full source of a standalone story component, by feeding?rawcontent intoparameters.docs.source.code. The?rawimport is the natural way to do this, but it's broken by the plugin.Workaround
A small
enforce: 'pre'Vite plugin that resolves*.ts?rawto an opaque virtual module (so the Angular/esbuild transforms don't touch it) and returns the file contents:Notes from getting this to work:
preloadhook on the…ts?rawid alone wasn't enough — the id still got compiled..tsalso failed: the esbuild/Angular transform matched it and stripped theexport default. Using an opaque id without a.tssuffix fixed it.Proposal
@analogjs/vite-plugin-angularignore ids with a?raw(and probably?url/ other Vite query) suffix so Vite's built-in handling applies, orstorybook-angulardocs).Link to source — https://github.com/radix-ng/primitives/blob/main/apps/radix-storybook/.storybook/main.ts#L20