Skip to content

Commit 7c28098

Browse files
chore(vite): replace vite-node with Vite's native RunnableDevEnvironment (#1717)
Co-authored-by: Tobbe Lundberg <tobbe@tlundberg.com>
1 parent dfca645 commit 7c28098

8 files changed

Lines changed: 624 additions & 92 deletions

File tree

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@cedarjs/structure": "workspace:*",
4343
"@cedarjs/telemetry": "workspace:*",
4444
"@cedarjs/utils": "workspace:*",
45+
"@cedarjs/vite": "workspace:*",
4546
"@cedarjs/web-server": "workspace:*",
4647
"@listr2/prompt-adapter-enquirer": "4.2.1",
4748
"@opentelemetry/api": "1.9.0",
@@ -88,7 +89,6 @@
8889
"title-case": "3.0.3",
8990
"unionfs": "4.6.0",
9091
"uuid": "11.1.0",
91-
"vite-node": "3.2.4",
9292
"yargs": "17.7.2"
9393
},
9494
"devDependencies": {

packages/cli/src/lib/exec.js

Lines changed: 43 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1+
import { existsSync } from 'node:fs'
12
import path from 'node:path'
23

3-
import { createServer, version as viteVersion } from 'vite'
4-
import { ViteNodeRunner } from 'vite-node/client'
5-
import { ViteNodeServer } from 'vite-node/server'
6-
import { installSourcemapsSupport } from 'vite-node/source-map'
4+
import { createServer, isRunnableDevEnvironment } from 'vite'
75

86
import { getPaths, importStatementPath } from '@cedarjs/project-config'
97
import {
@@ -13,8 +11,27 @@ import {
1311
cedarSwapApolloProvider,
1412
cedarImportDirPlugin,
1513
cedarAutoImportsPlugin,
14+
cedarCjsCompatPlugin,
1615
} from '@cedarjs/vite'
1716

17+
// When the customResolver returns an id, that id is final — Vite won't try
18+
// alternative extensions on it. This helper resolves the actual file on disk,
19+
// handling both bare paths (src/lib/jobs) and .js/.jsx paths that map to .ts
20+
// files in a TypeScript project (e.g. db.js → db.ts).
21+
function resolveExtension(id) {
22+
if (existsSync(id)) {
23+
return id
24+
}
25+
// Strip .js/.jsx extension if present, then try TypeScript and JS extensions
26+
const withoutExt = /\.jsx?$/.test(id) ? id.replace(/\.jsx?$/, '') : id
27+
for (const ext of ['.ts', '.tsx', '.js', '.jsx']) {
28+
if (existsSync(withoutExt + ext)) {
29+
return withoutExt + ext
30+
}
31+
}
32+
return id
33+
}
34+
1835
export async function runScriptFunction({
1936
path: scriptPath,
2037
functionName,
@@ -28,10 +45,16 @@ export async function runScriptFunction({
2845
const server = await createServer({
2946
mode: 'production',
3047
optimizeDeps: {
31-
// This is recommended in the vite-node readme
3248
noDiscovery: true,
3349
include: undefined,
3450
},
51+
server: {
52+
hmr: false,
53+
watch: null,
54+
},
55+
environments: {
56+
nodeRunnerEnv: {},
57+
},
3558
resolve: {
3659
alias: [
3760
{
@@ -63,23 +86,18 @@ export async function runScriptFunction({
6386
// from scripts/ because it doesn't know what the src/ alias is.
6487
// So we have to tell it to use the correct path based on what file
6588
// is doing the importing.
66-
// Also, to support both imports like 'src/lib/db.js' and
67-
// 'src/lib/db' in ts files we need to have special treatment for
68-
// the .js extension.
89+
// Also, to support imports like 'src/lib/db.js' in TS projects
90+
// where only a .ts file exists, we resolve the correct extension
91+
// ourselves — the customResolver result is final and Vite won't
92+
// try alternative extensions on it.
6993
if (importer.startsWith(apiImportBase)) {
7094
const apiImportSrc = importStatementPath(getPaths().api.src)
71-
let resolvedId = id.replace('src', apiImportSrc)
72-
if (importer.endsWith('.ts') || importer.endsWith('.tsx')) {
73-
resolvedId = resolvedId.replace(/\.jsx?$/, '')
74-
}
75-
return { id: resolvedId }
95+
const resolvedId = id.replace('src', apiImportSrc)
96+
return { id: resolveExtension(resolvedId) }
7697
} else if (importer.startsWith(webImportBase)) {
7798
const webImportSrc = importStatementPath(getPaths().web.src)
78-
let resolvedId = id.replace('src', webImportSrc)
79-
if (importer.endsWith('.ts') || importer.endsWith('.tsx')) {
80-
resolvedId = resolvedId.replace(/\.jsx?$/, '')
81-
}
82-
return { id: resolvedId }
99+
const resolvedId = id.replace('src', webImportSrc)
100+
return { id: resolveExtension(resolvedId) }
83101
}
84102

85103
return null
@@ -88,6 +106,7 @@ export async function runScriptFunction({
88106
],
89107
},
90108
plugins: [
109+
cedarCjsCompatPlugin(),
91110
cedarjsResolveCedarStyleImportsPlugin(),
92111
cedarCellTransform(),
93112
cedarjsJobPathInjectorPlugin(),
@@ -97,49 +116,24 @@ export async function runScriptFunction({
97116
],
98117
})
99118

100-
// For old Vite, this is needed to initialize the plugins.
101-
if (Number(viteVersion.split('.')[0]) < 6) {
102-
await server.pluginContainer.buildStart({})
119+
const env = server.environments.nodeRunnerEnv
120+
if (!env || !isRunnableDevEnvironment(env)) {
121+
await server.close()
122+
throw new Error('Vite environment is not runnable.')
103123
}
104124

105-
const node = new ViteNodeServer(server, {
106-
transformMode: {
107-
ssr: [/.*/],
108-
web: [/\/web\//],
109-
},
110-
deps: {
111-
fallbackCJS: true,
112-
},
113-
})
114-
115-
// fixes stacktraces in Errors
116-
installSourcemapsSupport({
117-
getSourceMap: (source) => node.getSourceMap(source),
118-
})
119-
120-
const runner = new ViteNodeRunner({
121-
root: server.config.root,
122-
base: server.config.base,
123-
fetchModule(id) {
124-
return node.fetchModule(id)
125-
},
126-
resolveId(id, importer) {
127-
return node.resolveId(id, importer)
128-
},
129-
})
130-
131125
let returnValue
132126
let scriptError = null
133127

134128
try {
135-
const script = await runner.executeFile(scriptPath)
129+
const script = await env.runner.import(scriptPath)
136130
returnValue = await script[functionName](args)
137131
} catch (error) {
138132
scriptError = error
139133
}
140134

141135
try {
142-
const { db } = await runner.executeFile(path.join(getPaths().api.lib, 'db'))
136+
const { db } = await env.runner.import(path.join(getPaths().api.lib, 'db'))
143137
db.$disconnect()
144138
} catch (e) {
145139
// silence

packages/prerender/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@
9191
"rollup-plugin-swc3": "0.12.1",
9292
"unimport": "5.7.0",
9393
"unplugin-auto-import": "19.3.0",
94-
"vite": "7.3.2",
95-
"vite-node": "3.2.4"
94+
"vite": "7.3.2"
9695
},
9796
"devDependencies": {
9897
"@cedarjs/framework-tools": "workspace:*",

packages/prerender/src/graphql/node-runner.ts

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { createServer, version as viteVersion, mergeConfig } from 'vite'
2-
import type { ViteDevServer, UserConfig } from 'vite'
3-
import { ViteNodeRunner } from 'vite-node/client'
4-
import { ViteNodeServer } from 'vite-node/server'
5-
import { installSourcemapsSupport } from 'vite-node/source-map'
1+
import { createServer, isRunnableDevEnvironment, mergeConfig } from 'vite'
2+
import type { ViteDevServer, RunnableDevEnvironment, UserConfig } from 'vite'
63

74
import { getPaths } from '@cedarjs/project-config'
85
import {
96
cedarCellTransform,
107
cedarjsResolveCedarStyleImportsPlugin,
118
cedarjsJobPathInjectorPlugin,
129
cedarSwapApolloProvider,
10+
cedarCjsCompatPlugin,
1311
} from '@cedarjs/vite'
1412

1513
import { cedarAutoImportsPlugin } from './vite-plugin-cedar-auto-import.js'
@@ -19,10 +17,16 @@ async function createViteServer(customConfig: UserConfig = {}) {
1917
const defaultConfig: UserConfig = {
2018
mode: 'production',
2119
optimizeDeps: {
22-
// This is recommended in the vite-node readme
2320
noDiscovery: true,
2421
include: undefined,
2522
},
23+
server: {
24+
hmr: false,
25+
watch: null,
26+
},
27+
environments: {
28+
nodeRunnerEnv: {},
29+
},
2630
resolve: {
2731
alias: [
2832
{
@@ -32,6 +36,7 @@ async function createViteServer(customConfig: UserConfig = {}) {
3236
],
3337
},
3438
plugins: [
39+
cedarCjsCompatPlugin(),
3540
cedarImportDirPlugin(),
3641
cedarAutoImportsPlugin(),
3742
cedarjsResolveCedarStyleImportsPlugin(),
@@ -45,17 +50,12 @@ async function createViteServer(customConfig: UserConfig = {}) {
4550

4651
const server = await createServer(mergedConfig)
4752

48-
// For old Vite, this is needed to initialize the plugins.
49-
if (Number(viteVersion.split('.')[0]) < 6) {
50-
await server.pluginContainer.buildStart({})
51-
}
52-
5353
return server
5454
}
5555

5656
export class NodeRunner {
5757
private viteServer?: ViteDevServer = undefined
58-
private runner?: ViteNodeRunner = undefined
58+
private env?: RunnableDevEnvironment = undefined
5959
private readonly customViteConfig: UserConfig
6060

6161
constructor(customViteConfig: UserConfig = {}) {
@@ -64,39 +64,27 @@ export class NodeRunner {
6464

6565
async init() {
6666
this.viteServer = await createViteServer(this.customViteConfig)
67-
const nodeServer = new ViteNodeServer(this.viteServer, {
68-
transformMode: {
69-
ssr: [/.*/],
70-
web: [/\/web\//],
71-
},
72-
deps: {
73-
fallbackCJS: true,
74-
},
75-
})
7667

77-
// fixes stacktraces in Errors
78-
installSourcemapsSupport({
79-
getSourceMap: (source) => nodeServer?.getSourceMap(source),
80-
})
68+
const env = this.viteServer.environments.nodeRunnerEnv
69+
if (!env || !isRunnableDevEnvironment(env)) {
70+
await this.viteServer.close()
71+
throw new Error('Vite environment is not runnable.')
72+
}
8173

82-
this.runner = new ViteNodeRunner({
83-
root: this.viteServer.config.root,
84-
base: this.viteServer.config.base,
85-
fetchModule(id) {
86-
return nodeServer.fetchModule(id)
87-
},
88-
resolveId(id, importer) {
89-
return nodeServer.resolveId(id, importer)
90-
},
91-
})
74+
this.env = env
9275
}
9376

9477
async importFile(filePath: string) {
95-
if (!this.runner) {
78+
if (!this.env) {
9679
await this.init()
9780
}
9881

99-
return this.runner?.executeFile(filePath)
82+
const env = this.env
83+
if (!env) {
84+
throw new Error('NodeRunner failed to initialize')
85+
}
86+
87+
return env.runner.import(filePath)
10088
}
10189

10290
async close() {

packages/vite/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"@vitejs/plugin-react": "4.7.0",
8888
"@whatwg-node/fetch": "0.10.13",
8989
"@whatwg-node/server": "0.10.18",
90+
"acorn": "8.16.0",
9091
"acorn-loose": "8.5.2",
9192
"ansis": "4.2.0",
9293
"buffer": "6.0.3",

packages/vite/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { cedarMergedConfig } from './plugins/vite-plugin-merged-config.js'
2121
import { cedarSwapApolloProvider } from './plugins/vite-plugin-swap-apollo-provider.js'
2222

2323
export { cedarAutoImportsPlugin } from './plugins/vite-plugin-cedar-auto-import.js'
24+
export { cedarCjsCompatPlugin } from './plugins/vite-plugin-cedar-cjs-compat.js'
2425
export { cedarCellTransform } from './plugins/vite-plugin-cedar-cell.js'
2526
export { cedarEntryInjectionPlugin } from './plugins/vite-plugin-cedar-entry-injection.js'
2627
export { cedarHtmlEnvPlugin } from './plugins/vite-plugin-cedar-html-env.js'

0 commit comments

Comments
 (0)