Skip to content

Commit

Permalink
feat: add vite-node into the core
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Jan 19, 2024
1 parent 526cf23 commit 3ba7436
Show file tree
Hide file tree
Showing 123 changed files with 3,722 additions and 20 deletions.
13 changes: 12 additions & 1 deletion packages/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,23 @@
"./client": {
"types": "./client.d.ts"
},
"./runtime": {
"types": "./dist/node/runtime.d.ts",
"import": "./dist/node/runtime.js"
},
"./dist/client/*": "./dist/client/*",
"./types/*": {
"types": "./types/*"
},
"./package.json": "./package.json"
},
"typesVersions": {
"*": {
"runtime": [
"dist/node/runtime.d.ts"
]
}
},
"files": [
"bin",
"dist",
Expand All @@ -64,7 +75,7 @@
"build": "rimraf dist && run-s build-bundle build-types",
"build-bundle": "rollup --config rollup.config.ts --configPlugin typescript",
"build-types": "run-s build-types-temp build-types-roll build-types-check",
"build-types-temp": "tsc --emitDeclarationOnly --outDir temp/node -p src/node",
"build-types-temp": "tsc --emitDeclarationOnly --outDir temp -p src/node",
"build-types-roll": "rollup --config rollup.dts.config.ts --configPlugin typescript && rimraf temp",
"build-types-check": "tsc --project tsconfig.check.json",
"typecheck": "tsc --noEmit",
Expand Down
8 changes: 7 additions & 1 deletion packages/vite/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ function createNodeConfig(isProduction: boolean) {
index: path.resolve(__dirname, 'src/node/index.ts'),
cli: path.resolve(__dirname, 'src/node/cli.ts'),
constants: path.resolve(__dirname, 'src/node/constants.ts'),
runtime: path.resolve(__dirname, 'src/node/ssr/runtime/index.ts'),
},
output: {
...sharedNodeOptions.output,
Expand Down Expand Up @@ -299,7 +300,12 @@ const __require = require;
name: 'cjs-chunk-patch',
renderChunk(code, chunk) {
if (!chunk.fileName.includes('chunks/dep-')) return

// don't patch runtime utils chunk because it should stay lightweight and we know it doesn't use require
if (
chunk.name === 'utils' &&
chunk.moduleIds.some((id) => id.endsWith('/ssr/runtime/utils.ts'))
)
return
const match = code.match(/^(?:import[\s\S]*?;\s*)+/)
const index = match ? match.index! + match[0].length : 0
const s = new MagicString(code)
Expand Down
54 changes: 40 additions & 14 deletions packages/vite/rollup.dts.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,24 @@ const pkg = JSON.parse(
readFileSync(new URL('./package.json', import.meta.url)).toString(),
)

const external = [
/^node:*/,
'rollup/parseAst',
...Object.keys(pkg.dependencies),
// lightningcss types are bundled
...Object.keys(pkg.devDependencies).filter((d) => d !== 'lightningcss'),
]

export default defineConfig({
input: './temp/node/index.d.ts',
input: {
index: './temp/node/index.d.ts',
runtime: './temp/node/ssr/runtime/index.d.ts',
},
output: {
file: './dist/node/index.d.ts',
format: 'es',
dir: './dist/node',
format: 'esm',
},
external: [
/^node:*/,
'rollup/parseAst',
...Object.keys(pkg.dependencies),
// lightningcss types are bundled
...Object.keys(pkg.devDependencies).filter((d) => d !== 'lightningcss'),
],
external,
plugins: [patchTypes(), dts({ respectExternal: true })],
})

Expand Down Expand Up @@ -84,15 +89,35 @@ function patchTypes(): Plugin {
}
},
renderChunk(code, chunk) {
validateChunkImports.call(this, chunk)
code = replaceConfusingTypeNames.call(this, code, chunk)
code = stripInternalTypes.call(this, code, chunk)
code = cleanUnnecessaryComments(code)
if (chunk.fileName.startsWith('runtime')) {
validateRuntimeChunk.call(this, chunk)
} else {
validateChunkImports.call(this, chunk)
code = replaceConfusingTypeNames.call(this, code, chunk)
code = stripInternalTypes.call(this, code, chunk)
code = cleanUnnecessaryComments(code)
}
return code
},
}
}

/**
* Runtime chunk should only import local dependencies to stay lightweight
*/
function validateRuntimeChunk(this: PluginContext, chunk: RenderedChunk) {
for (const id of chunk.imports) {
if (
!id.startsWith('./') &&
!id.startsWith('../') &&
!id.startsWith('runtime.d')
) {
this.warn(`${chunk.fileName} imports "${id}" which is not allowed`)
process.exitCode = 1
}
}
}

/**
* Validate that chunk imports do not import dev deps
*/
Expand All @@ -103,6 +128,7 @@ function validateChunkImports(this: PluginContext, chunk: RenderedChunk) {
!id.startsWith('./') &&
!id.startsWith('../') &&
!id.startsWith('node:') &&
!id.startsWith('runtime.d') &&
!deps.includes(id) &&
!deps.some((name) => id.startsWith(name + '/'))
) {
Expand Down
11 changes: 11 additions & 0 deletions packages/vite/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export { optimizeDeps } from './optimizer'
export { formatPostcssSourceMap, preprocessCSS } from './plugins/css'
export { transformWithEsbuild } from './plugins/esbuild'
export { buildErrorMessage } from './server/middlewares/error'
export { ssrFetchModule } from './ssr/ssrFetchModule'
export * from './publicUtils'

// additional types
Expand Down Expand Up @@ -119,6 +120,16 @@ export type {
} from './server/transformRequest'
export type { HmrOptions, HmrContext } from './server/hmr'

export type {
HMRBroadcaster,
HMRChannel,
ServerHMRChannel,
HMRBroadcasterClient,
} from './server/hmr'
export type { FetchFunction } from './ssr/runtime/index'
export { createViteRuntime } from './ssr/runtime/node/mainThreadRuntime'
export { ServerHMRConnector } from './ssr/runtime/node/serverHmrConnector'

export type { BindCLIShortcutsOptions, CLIShortcut } from './shortcuts'

export type {
Expand Down
76 changes: 76 additions & 0 deletions packages/vite/src/node/server/hmr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fsp from 'node:fs/promises'
import path from 'node:path'
import type { Server } from 'node:http'
import { EventEmitter } from 'node:events'
import colors from 'picocolors'
import type { CustomPayload, HMRPayload, Update } from 'types/hmrPayload'
import type { RollupError } from 'rollup'
Expand Down Expand Up @@ -252,6 +253,9 @@ export function updateModules(
? isExplicitImportRequired(acceptedVia.url)
: false,
isWithinCircularImport,
// browser modules are invalidated by changing ?t= query,
// but in ssr we control the module system, so we can directly remove them form cache
ssrInvalidates: getSSRInvalidatedImporters(acceptedVia),
}),
),
)
Expand Down Expand Up @@ -288,6 +292,32 @@ export function updateModules(
})
}

function populateSSRImporters(
module: ModuleNode,
timestamp: number,
seen: Set<ModuleNode>,
) {
module.ssrImportedModules.forEach((importer) => {
if (seen.has(importer)) {
return
}
if (
importer.lastHMRTimestamp === timestamp ||
importer.lastInvalidationTimestamp === timestamp
) {
seen.add(importer)
populateSSRImporters(importer, timestamp, seen)
}
})
return seen
}

function getSSRInvalidatedImporters(module: ModuleNode) {
return [
...populateSSRImporters(module, module.lastHMRTimestamp, new Set()),
].map((m) => m.file!)
}

export async function handleFileAddUnlink(
file: string,
server: ViteDevServer,
Expand Down Expand Up @@ -751,3 +781,49 @@ export function createHMRBroadcaster(): HMRBroadcaster {
}
return broadcaster
}

export interface ServerHMRChannel extends HMRChannel {
api: {
innerEmitter: EventEmitter
outsideEmitter: EventEmitter
}
}

export function createServerHMRChannel(): ServerHMRChannel {
const innerEmitter = new EventEmitter()
const outsideEmitter = new EventEmitter()

return {
name: 'ssr',
send(...args: any[]) {
let payload: HMRPayload
if (typeof args[0] === 'string') {
payload = {
type: 'custom',
event: args[0],
data: args[1],
}
} else {
payload = args[0]
}
outsideEmitter.emit('send', payload)
},
off(event, listener: () => void) {
innerEmitter.off(event, listener)
},
on: ((event: string, listener: () => unknown) => {
innerEmitter.on(event, listener)
}) as ServerHMRChannel['on'],
close() {
innerEmitter.removeAllListeners()
outsideEmitter.removeAllListeners()
},
listen() {
innerEmitter.emit('connection')
},
api: {
innerEmitter,
outsideEmitter,
},
}
}
14 changes: 13 additions & 1 deletion packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import { printServerUrls } from '../logger'
import { createNoopWatcher, resolveChokidarOptions } from '../watch'
import { initPublicFiles } from '../publicDir'
import { getEnvFilesForMode } from '../env'
import type { FetchResult } from '../ssr/runtime/types'
import { ssrFetchModule } from '../ssr/ssrFetchModule'
import type { PluginContainer } from './pluginContainer'
import { ERR_CLOSED_SERVER, createPluginContainer } from './pluginContainer'
import type { WebSocketServer } from './ws'
Expand All @@ -73,6 +75,7 @@ import { errorMiddleware, prepareError } from './middlewares/error'
import type { HMRBroadcaster, HmrOptions } from './hmr'
import {
createHMRBroadcaster,
createServerHMRChannel,
getShortName,
handleFileAddUnlink,
handleHMRUpdate,
Expand Down Expand Up @@ -291,6 +294,10 @@ export interface ViteDevServer {
url: string,
opts?: { fixStacktrace?: boolean },
): Promise<Record<string, any>>
/**
* Fetch information about the module
*/
ssrFetchModule(id: string, importer?: string): Promise<FetchResult>
/**
* Returns a fixed version of the given stack
*/
Expand Down Expand Up @@ -410,7 +417,9 @@ export async function _createServer(
: await resolveHttpServer(serverConfig, middlewares, httpsOptions)

const ws = createWebSocketServer(httpServer, config, httpsOptions)
const hot = createHMRBroadcaster().addChannel(ws)
const hot = createHMRBroadcaster()
.addChannel(ws)
.addChannel(createServerHMRChannel())
if (typeof config.server.hmr === 'object' && config.server.hmr.channels) {
config.server.hmr.channels.forEach((channel) => hot.addChannel(channel))
}
Expand Down Expand Up @@ -493,6 +502,9 @@ export async function _createServer(
opts?.fixStacktrace,
)
},
async ssrFetchModule(url: string, importer?: string) {
return ssrFetchModule(server, url, importer)
},
ssrFixStacktrace(e) {
ssrFixStacktrace(e, moduleGraph)
},
Expand Down
1 change: 1 addition & 0 deletions packages/vite/src/node/ssr/runtime/__tests__/fixtures/a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a = 'a'
1 change: 1 addition & 0 deletions packages/vite/src/node/ssr/runtime/__tests__/fixtures/b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const b = 'b'
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const name = 'basic'

export const meta = import.meta
7 changes: 7 additions & 0 deletions packages/vite/src/node/ssr/runtime/__tests__/fixtures/c.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-disable no-console */

export { a as c } from './a'

import.meta.hot?.accept(() => {
console.log('accept c')
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { b } from './circular-b'
export const a = 'a'
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { a } from './circular-a'
export const b = 'b'
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { a } from './circular-a'
export { b } from './circular-b'

// since there is no .accept, it does full reload
import.meta.hot.on('vite:beforeFullReload', () => {
// eslint-disable-next-line no-console
console.log('full reload')
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { hello } from '@vitejs/cjs-external'

export const result = hello()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { nonExisting } from '@vitejs/cjs-external'

// eslint-disable-next-line no-console
console.log(nonExisting)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
hello: () => 'world',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@vitejs/cjs-external",
"private": true,
"version": "0.0.0",
"type": "commonjs",
"main": "index.cjs"
}
7 changes: 7 additions & 0 deletions packages/vite/src/node/ssr/runtime/__tests__/fixtures/d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-disable no-console */

export { c as d } from './c'

import.meta.hot?.accept(() => {
console.log('accept d')
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as staticModule from './basic'

export const initialize = async () => {
const nameRelative = './basic'
const nameAbsolute = '/fixtures/basic'
const nameAbsoluteExtension = '/fixtures/basic.js'
return {
dynamicProcessed: await import('./basic'),
dynamicRelative: await import(nameRelative),
dynamicAbsolute: await import(nameAbsolute),
dynamicAbsoluteExtension: await import(nameAbsoluteExtension),
static: staticModule,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { hello } from '@vitejs/esm-external'

export const result = hello()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { nonExisting } from '@vitejs/esm-external'

// eslint-disable-next-line no-console
console.log(nonExisting)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const hello = () => 'world'
Loading

0 comments on commit 3ba7436

Please sign in to comment.