Skip to content

?raw imports are shadowed by the Angular Vite plugin (docs / enhancement) #2356

@pimenovoleg

Description

@pimenovoleg

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

  1. 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
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions