Skip to content

feat(vite): decouple vite plugin from compiler - #97

Open
janis-me wants to merge 2 commits into
mainfrom
feat/improve-vite-plugin
Open

feat(vite): decouple vite plugin from compiler#97
janis-me wants to merge 2 commits into
mainfrom
feat/improve-vite-plugin

Conversation

@janis-me

Copy link
Copy Markdown
Member

This decouples vite plugin and compiler, eliminating the need for rolldown and, more importantly,
enabling surimi files to benefit from the full vite ecosystem, like importing css files,
better exports, HMR, etc.

This does make some things simpler, but now we have to manage the secondary vite server
for code execution etc. which is another thing to handle

This decouples vite plugin and compiler, eliminating the need for rolldown and, more importantly,
enabling surimi files to benefit from the full vite ecosystem, like importing css files,
better exports, HMR, etc.

This does make some things simpler, but now we have to manage the secondary vite server
for code execution etc. which is another thing to handle
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 21, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
surimi 06426ba Commit Preview URL

Branch Preview URL
Jun 22 2026, 05:17 AM

@janis-me
janis-me marked this pull request as ready for review June 21, 2026 20:05
Copilot AI review requested due to automatic review settings June 21, 2026 20:05

janis-me commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

throw new Error(`transform returned no code for ${requestId}`);
}

const absoluteId = normalizeModuleId(path.join(fixture.root, moduleId), { root: fixture.root } as never);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect parameter type passed to normalizeModuleId. The function expects root to be a string | undefined, but an object { root: fixture.root } is being passed (cast with as never to bypass type checking). This will cause runtime errors when the function attempts string operations on the root parameter.

// Fix: Pass the root string directly
const absoluteId = normalizeModuleId(path.join(fixture.root, moduleId), fixture.root);
Suggested change
const absoluteId = normalizeModuleId(path.join(fixture.root, moduleId), { root: fixture.root } as never);
const absoluteId = normalizeModuleId(path.join(fixture.root, moduleId), fixture.root);

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR decouples vite-plugin-surimi from the rolldown-based compiler by introducing a Vite-owned evaluation server/module runner, enabling Surimi style files (and Vue <surimi> blocks) to participate in the broader Vite ecosystem (plain CSS imports, asset handling, HMR, etc.).

Changes:

  • Added a SurimiEvaluator (owned Vite server + module-runner) and migrated plugin/Vue block compilation to it.
  • Introduced canonical module-id normalization utilities and updated virtual CSS resolution/emission logic.
  • Added a comprehensive Vitest suite for parity, module resolution (incl. ?raw), CSS emission, Vue blocks, exports, and errors.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
packages/vite-plugin-surimi/test/vue.spec.ts Adds Vue <surimi> block evaluation tests (virtual ids, imports, evaluator reuse).
packages/vite-plugin-surimi/test/parity.spec.ts Adds parity tests comparing plugin output vs compiler fixtures.
packages/vite-plugin-surimi/test/module-resolution.spec.ts Adds tests for CSS/TS/TSX/alias/?raw imports and dependency dedupe.
packages/vite-plugin-surimi/test/helpers/vite.ts Adds Vite test harness (fixture FS, build/serve transform helpers, evaluator helpers).
packages/vite-plugin-surimi/test/exports.spec.ts Adds tests for export serialization behavior.
packages/vite-plugin-surimi/test/errors.spec.ts Adds error-path tests (invalid Surimi file, SSR inlineCss rejection).
packages/vite-plugin-surimi/test/css-emission.spec.ts Adds tests for dev virtual CSS import, build CSS emission, SSR behavior.
packages/vite-plugin-surimi/src/vue.ts Updates Vue integration to use shared evaluator compilation + import-path helpers.
packages/vite-plugin-surimi/src/types.ts Extends shared plugin context with evaluator + compilation getter.
packages/vite-plugin-surimi/src/runner.ts Introduces owned-server evaluator and dependency collection helpers.
packages/vite-plugin-surimi/src/plugin.ts Migrates core plugin to evaluator-backed compilation + updated HMR/virtual CSS logic.
packages/vite-plugin-surimi/src/normalize-module-id.ts Adds canonical module id + import-path/virtual-id helpers.
packages/compiler/src/index.ts Re-exports compiler transform helpers and extraction utilities.
packages/compiler/src/index.node.ts Re-exports shared compiler utilities from node entry.
packages/compiler/src/index.browser.ts Re-exports shared compiler utilities from browser entry.
packages/compiler/src/extract.ts Introduces shared extraction of CSS + serializable exports from evaluated modules.
packages/compiler/src/constants.ts Adds shared compiler constants.
packages/compiler/src/compiler.ts Refactors to use shared extract/constants and exports transform plugins/utilities.
Comments suppressed due to low confidence (1)

packages/vite-plugin-surimi/src/plugin.ts:324

  • If dependencies contains query-bearing ids, passing them directly to addWatchFile() will register a non-existent path like /abs/file.css?raw. Strip queries before watching so file changes are detected reliably.
          if (ctx.isDev && !options?.ssr) {
            const addWatch = this.addWatchFile.bind(this);
            addWatchFilesForDeps(dependencies, filesWatched, addWatch, (p: string) => path.isAbsolute(p));
          }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +85 to +88
const cssTsCandidate = `${absoluteCss.slice(0, -4)}.ts`;
if (existsSync(cssTsCandidate)) return cssTsCandidate;

return null;
Comment on lines +249 to +260
const cleanId = dependencyId.split('?')[0] ?? dependencyId;
if (cleanId === ownerId) return false;
if (isSurimiStyleFile(cleanId)) return false;
if (cleanId.endsWith('.surimi.css')) return false;
if (cleanId.includes('node_modules')) return false;

return (
/\.(css|scss|sass|less)$/i.test(cleanId) ||
dependencyId.includes('?raw') ||
dependencyId.includes('?url') ||
dependencyId.includes('?inline')
);
Comment on lines +112 to +135
const normalized = normalizeModuleId(moduleId.split('?')[0] ?? moduleId, root);
if (visited.has(normalized)) return;
visited.add(normalized);

const mod = moduleGraph.getModuleById(normalized) ?? moduleGraph.getModuleById(moduleId);
if (!mod) return;

for (const imported of mod.importedModules) {
const importedId = imported.id ?? imported.url;
if (!importedId) continue;

const cleanId = importedId.split('?')[0] ?? importedId;
if (cleanId.includes('node_modules')) continue;
if (isDevelopmentSurimiFile(cleanId)) continue;
if (cleanId.startsWith('\0')) continue;

const normalizedImport = normalizeModuleId(cleanId, root);
deps.add(normalizedImport);
visit(normalizedImport);
}
};

visit(entryId);
deps.add(normalizeModuleId(entryId.split('?')[0] ?? entryId, root));
Comment on lines 45 to +49
const normalizeDependencyId = (dependencyId: string, ownerId: string): string => {
// Rolldown mixes relative, absolute, and virtual ("\0") ids. Normalizing avoids cache misses when Vite
// requests the same file under a different shape (e.g. absolute path during SSR versus relative in dev).
const cleanId = dependencyId.split('?')[0] ?? dependencyId;

if (cleanId.startsWith('\0')) return cleanId;
if (path.isAbsolute(cleanId)) return normalizePath(cleanId);
if (ctx.resolvedConfig) return getAbsoluteId(cleanId, ctx.resolvedConfig);
if (path.isAbsolute(cleanId)) {
Comment on lines +47 to +53
export function toImportPath(dependencyId: string, ownerId: string, root?: string): string {
const absoluteDependency = normalizeModuleId(dependencyId, root);
const absoluteOwner = normalizeModuleId(ownerId, root);
const relative = path.relative(path.dirname(absoluteOwner), absoluteDependency);
const posixRelative = relative.split(path.sep).join(path.posix.sep);
return posixRelative.startsWith('.') ? posixRelative : `./${posixRelative}`;
}
Comment on lines 117 to 122
const cacheEntry = ctx.compilationCache.get(cachedFile);
if (cacheEntry?.dependencies.includes(normalizedChangedFile)) {
ctx.compilationCache.delete(cachedFile);
ctx.evaluator?.invalidate(cachedFile);
modules.push(...collectModulesForInvalidation(cachedFile, moduleGraph, inlineCss));
}
Comment on lines 148 to 151
if (ctx.isDev && !options?.ssr) {
const addWatch = this.addWatchFile.bind(this);
addWatchFilesForDeps(resultToCache.dependencies, filesWatched, addWatch, (p: string) => path.isAbsolute(p));
}
Comment on lines +139 to +142
export class SurimiEvaluator {
private server: ViteDevServer | null = null;
private runner: ModuleRunner | null = null;
private readonly virtualSources = new Map<string, string>();
Comment on lines +153 to +156
private async ensureServer(): Promise<{ server: ViteDevServer; runner: ModuleRunner }> {
if (this.server && this.runner) {
return { server: this.server, runner: this.runner };
}
Comment on lines +170 to +181
this.server = await createServer({
configFile: false,
root: this.normalizedRoot,
logLevel: 'silent',
...(baseResolve ? { resolve: baseResolve } : {}),
plugins,
server: { middlewareMode: true, ws: false },
});

this.runner = createServerModuleRunner(this.server.environments.ssr, { hmr: false });

return { server: this.server, runner: this.runner };
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.

2 participants