1+ import { existsSync } from 'node:fs'
12import 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
86import { getPaths , importStatementPath } from '@cedarjs/project-config'
97import {
@@ -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 = / \. j s x ? $ / . test ( id ) ? id . replace ( / \. j s x ? $ / , '' ) : 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+
1835export 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 ( / \. j s x ? $ / , '' )
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 ( / \. j s x ? $ / , '' )
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 : [ / \/ w e b \/ / ] ,
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
0 commit comments