Skip to content

feat(rsc): add import.meta.viteRsc.import API#1063

Merged
hi-ogawa merged 35 commits intomainfrom
01-16-feat_rsc_add_import_._environment_._
Jan 19, 2026
Merged

feat(rsc): add import.meta.viteRsc.import API#1063
hi-ogawa merged 35 commits intomainfrom
01-16-feat_rsc_add_import_._environment_._

Conversation

@hi-ogawa
Copy link
Copy Markdown
Contributor

@hi-ogawa hi-ogawa commented Jan 16, 2026

Summary

Add import.meta.viteRsc.import() - a more ergonomic API for cross-environment module loading.

import.meta.viteRsc.import: <T>(
  specifier: string,
  options: { environment: "ssr" | "rsc" },
) => Promise<T>

Comparison

Before (loadModule):

// vite.config.ts - must configure entry manually
export default {
  environments: {
    ssr: {
      build: {
        rollupOptions: {
          input: { index: './entry.ssr.tsx' }
        }
      }
    }
  }
}

// entry.rsc.tsx
const ssrModule = await import.meta.viteRsc.loadModule<typeof import('./entry.ssr')>(
  'ssr',
  'index'
);

After (import):

// vite.config.ts - no explicit `rollupOptions.input` needed

// entry.rsc.tsx
const ssrModule = await import.meta.viteRsc.import<typeof import('./entry.ssr')>(
  './entry.ssr',
  { environment: 'ssr' }
);

How it works

  • Dev: Uses __VITE_ENVIRONMENT_RUNNER_IMPORT__ (same as loadModule)
  • Build: Auto-discovers imports, emits entries, generates manifest (__vite_rsc_env_imports_manifest.js)

Side note: Removal of internal no-ssr build path

This PR removes the internal "no-ssr" build path (4-step pipeline) that was automatically triggered when ssr.rollupOptions.input was not configured. This path was incompatible with auto-discovery—the decision to skip ssr build was made before scanning could discover import.meta.viteRsc.import calls targeting ssr.

This is not a breaking change:

  • Build output gains a small unused fallback chunk in ssr output when ssr isn't explicitly configured
  • Runtime behavior is unchanged
  • Users who want a true no-ssr build can use customBuildApp (see examples/no-ssr)

Documentation

  • README.md - API documentation with comparison
  • types/index.d.ts - TypeScript types with JSDoc

Test plan

  • Implement transform plugin
  • Test build mode with starter example
  • Verify entry auto-discovery works
  • Test dev mode

🤖 Generated with Claude Code

hi-ogawa and others added 16 commits January 16, 2026 12:25
- Add architecture.md documenting build pipeline and data flow
- Add bundler-comparison.md comparing RSC approaches across bundlers
- Add implementation plan for new ergonomic import API
- Stub import-environment.ts plugin file

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add import-environment plugin with transform/renderChunk hooks
- Dev mode: rewrite to globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__
- Build mode: emit marker, resolve in renderChunk to relative import
- Auto-discover entries and inject into target environment's input
- Handle empty SSR input case in buildApp orchestration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document the preferred implementation using static import functions
in a manifest file, which supports custom entryFileNames and enables
proper static analysis by bundlers.

Key additions:
- Manifest with static import functions (not dynamic lookups)
- Bidirectional support (RSC → SSR and SSR → RSC)
- Updated data flow showing entry injection after both scans
- Per-environment manifest generation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…leNames

Replace hardcoded output filenames with a manifest approach that supports
custom entryFileNames config. The manifest uses static import functions
for proper bundler analysis.

Changes:
- Transform emits manifest lookup instead of markers
- resolveId marks manifest as external during build
- generateBundle tracks resolvedId → outputFileName mapping
- writeEnvironmentImportsManifest generates manifest after all builds
- Split entry injection for bidirectional support (RSC↔SSR)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace direct rollupOptions.input mutation with:
- Virtual scan placeholder for scan builds without configured entries
- emitFile in buildStart for emitting discovered entries in real builds

This follows the fullstack plugin pattern and provides cleaner separation
between scan and real build phases.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use a placeholder in transform and replace with correct relative path
in renderChunk, similar to how BUILD_ASSETS_MANIFEST_NAME is handled.
This ensures manifest imports work correctly for nested chunk outputs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Let Rollup auto-name emitted chunks instead of deriving entry names
from specifiers. The actual output filename is tracked via
generateBundle anyway, so the explicit name was unnecessary.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tMetaMap

Add fileName field to EnvironmentImportMeta instead of tracking output
filenames in a separate map. This simplifies the data structure and
removes the TODO about merging the maps.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use toRelativeId() for manifest keys instead of absolute paths.
This ensures stable builds across different machines/environments.

Before: "/home/user/.../entry.ssr.tsx"
After:  "src/framework/entry.ssr.tsx"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change from `Record<resolvedId, meta>` to `Record<sourceEnv, Record<resolvedId, meta>>`.
This properly handles the case where the same module is imported from multiple
source environments, avoiding overwrites.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@hi-ogawa hi-ogawa force-pushed the 01-16-feat_rsc_add_import_._environment_._ branch from de75327 to b2b877a Compare January 16, 2026 07:25
hi-ogawa and others added 9 commits January 16, 2026 16:40
Add documentation for the new import.meta.viteRsc.import() API in both
README.md and types/index.d.ts with JSDoc, examples, and type signatures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…esolution

- Replace `rscBundle` with `bundles: Record<string, OutputBundle>` in RscPluginManager
- Lookup fileName from bundles in writeEnvironmentImportsManifest instead of mutating meta
- Remove generateBundle hook from import-environment plugin (no longer needed)
- Remove unused fileName field from EnvironmentImportMeta type

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…nv][resolvedId]

- Update type definition to add targetEnv as middle key
- Update write logic in transform to use new structure
- Update buildStart to lookup by targetEnv directly (more efficient)
- Update writeEnvironmentImportsManifest to iterate by targetEnv

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@hi-ogawa hi-ogawa marked this pull request as ready for review January 16, 2026 08:42
@hi-ogawa hi-ogawa marked this pull request as draft January 16, 2026 10:53
@hi-ogawa hi-ogawa marked this pull request as ready for review January 19, 2026 02:22
@hi-ogawa hi-ogawa merged commit b0e7a1f into main Jan 19, 2026
21 checks passed
@hi-ogawa hi-ogawa deleted the 01-16-feat_rsc_add_import_._environment_._ branch January 19, 2026 02:28
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.

1 participant