Skip to content

Commit

Permalink
refactor: expose more types, fix caching with the query issue, requir…
Browse files Browse the repository at this point in the history
…e onUpdate
  • Loading branch information
sheremet-va committed Jan 21, 2024
1 parent c0de2ff commit 2423810
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as mov } from './assets/placeholder.mov'
export { default as txt } from './assets/placeholder.txt'
export { default as png } from './assets/placeholder.png'
export { default as webp } from './assets/placeholder.webp'
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.test {
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.test {
color: red;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,61 @@ describe('vite-runtime initialization', async () => {
expect(mod.msg).toBe('virtual')
})

it('css is loaded correctly', async ({ runtime }) => {
const css = await runtime.executeUrl('/fixtures/test.css')
expect(css.default).toMatchInlineSnapshot(`
".test {
color: red;
}
"
`)
const module = await runtime.executeUrl('/fixtures/test.module.css')
expect(module).toMatchObject({
default: {
test: expect.stringMatching(/^_test_/),
},
test: expect.stringMatching(/^_test_/),
})
})

it('assets are loaded correctly', async ({ runtime }) => {
const assets = await runtime.executeUrl('/fixtures/assets.js')
expect(assets).toMatchObject({
mov: '/fixtures/assets/placeholder.mov',
txt: '/fixtures/assets/placeholder.txt',
png: '/fixtures/assets/placeholder.png',
webp: '/fixtures/assets/placeholder.webp',
})
})

it('ids with Vite queries are loaded correctly', async ({ runtime }) => {
const raw = await runtime.executeUrl('/fixtures/simple.js?raw')
expect(raw.default).toMatchInlineSnapshot(`
"export const test = 'I am initialized'
import.meta.hot?.accept()
"
`)
const url = await runtime.executeUrl('/fixtures/simple.js?url')
expect(url.default).toMatchInlineSnapshot(`"/fixtures/simple.js"`)
const inline = await runtime.executeUrl('/fixtures/test.css?inline')
expect(inline.default).toMatchInlineSnapshot(`
".test {
color: red;
}
"
`)
})

it('modules with query strings are treated as different modules', async ({
runtime,
}) => {
const modSimple = await runtime.executeUrl('/fixtures/simple.js')
const modUrl = await runtime.executeUrl('/fixtures/simple.js?url')
expect(modSimple).not.toBe(modUrl)
expect(modUrl.default).toBe('/fixtures/simple.js')
})

it('exports is not modifiable', async ({ runtime }) => {
const mod = await runtime.executeUrl('/fixtures/simple.js')
expect(() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/ssr/runtime/esmRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const AsyncFunction = async function () {}.constructor as typeof Function
export class ESModulesRunner implements ViteModuleRunner {
async runViteModule(
context: ViteRuntimeModuleContext,
transformed: string,
code: string,
): Promise<any> {
// use AsyncFunction instead of vm module to support broader array of environments out of the box
const initModule = new AsyncFunction(
Expand All @@ -28,7 +28,7 @@ export class ESModulesRunner implements ViteModuleRunner {
ssrDynamicImportKey,
ssrExportAllKey,
// source map should already be inlined by Vite
'"use strict";' + transformed,
'"use strict";' + code,
)

await initModule(
Expand Down
5 changes: 4 additions & 1 deletion packages/vite/src/node/ssr/runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ export { ESModulesRunner } from './esmRunner'

export { handleHMRUpdate, createHMRHandler } from './hmrHandler'

export type { HMRLogger } from '../../../shared/hmr'
export type { HMRLogger, HMRConnection } from '../../../shared/hmr'
export type {
ViteModuleRunner,
ViteRuntimeModuleContext,
ModuleCache,
FetchResult,
FetchFunction,
ResolvedResult,
SSRImportMetadata,
ViteRuntimeImportMeta,
ViteServerClientOptions,
} from './types'
export {
Expand Down
9 changes: 1 addition & 8 deletions packages/vite/src/node/ssr/runtime/node/mainThreadRuntime.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ViteDevServer } from '../../../index'
import { ViteRuntime } from '../runtime'
import { ESModulesRunner } from '../esmRunner'
import { createHMRHandler } from '../hmrHandler'
import type { ViteModuleRunner, ViteServerClientOptions } from '../types'
import type { HMRLogger } from '../../../../shared/hmr'
import { ServerHMRConnector } from './serverHmrConnector'
Expand Down Expand Up @@ -35,7 +34,7 @@ export async function createViteRuntime(
options: MainThreadRuntimeOptions = {},
): Promise<ViteRuntime> {
const hmr = createHMROptions(server, options)
const runtime = new ViteRuntime(
return new ViteRuntime(
{
...options,
root: server.config.root,
Expand All @@ -44,10 +43,4 @@ export async function createViteRuntime(
},
options.runner || new ESModulesRunner(),
)

if (hmr) {
hmr.connection.onUpdate(createHMRHandler(runtime))
}

return runtime
}
23 changes: 14 additions & 9 deletions packages/vite/src/node/ssr/runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
ssrModuleExportsKey,
} from './constants'
import { silentConsole } from './hmrLogger'
import { createHMRHandler } from './hmrHandler'

interface ViteRuntimeDebugger {
(formatter: unknown, ...args: unknown[]): void
Expand All @@ -46,7 +47,7 @@ export class ViteRuntime {
public hmrClient?: HMRClient
public entrypoints = new Set<string>()

private idToFileMap = new Map<string, string>()
private idToUrlMap = new Map<string, string>()
private envProxy: ImportMetaEnv

constructor(
Expand All @@ -68,6 +69,7 @@ export class ViteRuntime {
return this.executeUrl(acceptedPath)
},
)
options.hmr.connection.onUpdate(createHMRHandler(this))
}
}

Expand All @@ -94,7 +96,7 @@ export class ViteRuntime {

public clearCache(): void {
this.moduleCache.clear()
this.idToFileMap.clear()
this.idToUrlMap.clear()
this.entrypoints.clear()
this.hmrClient?.clear()
}
Expand Down Expand Up @@ -204,7 +206,7 @@ export class ViteRuntime {
id: string,
importer?: string,
): Promise<ResolvedResult> {
const normalized = this.idToFileMap.get(id)
const normalized = this.idToUrlMap.get(id)
if (normalized) {
const mod = this.moduleCache.getByModuleId(normalized)
if (mod.meta) {
Expand All @@ -219,19 +221,22 @@ export class ViteRuntime {
// base moduleId on "file" and not on id
// if `import(variable)` is called it's possible that it doesn't have an extension for example
// if we used id for that, it's possible to have a duplicated module
const moduleId = this.moduleCache.normalize(fetchedModule.file || id)
const idQuery = id.split('?')[1]
const query = idQuery ? `?${idQuery}` : ''
const fullFile = fetchedModule.file ? `${fetchedModule.file}${query}` : id
const moduleId = this.moduleCache.normalize(fullFile)
const mod = this.moduleCache.getByModuleId(moduleId)
fetchedModule.id = moduleId
mod.meta = fetchedModule
this.idToFileMap.set(id, moduleId)
this.idToFileMap.set(unwrapId(id), moduleId)
this.idToUrlMap.set(id, moduleId)
this.idToUrlMap.set(unwrapId(id), moduleId)
return fetchedModule as ResolvedResult
}

// override is allowed, consider this a public API
protected async directRequest(
id: string,
{ file, externalize, code: transformed, id: moduleId }: ResolvedResult,
{ file, externalize, code, id: moduleId }: ResolvedResult,
_callstack: string[],
metadata?: SSRImportMetadata,
): Promise<any> {
Expand Down Expand Up @@ -267,7 +272,7 @@ export class ViteRuntime {
return exports
}

if (transformed == null) {
if (code == null) {
const importer = callstack[callstack.length - 2]
throw new Error(
`[vite-runtime] Failed to load "${id}"${
Expand Down Expand Up @@ -330,7 +335,7 @@ export class ViteRuntime {

this.debug?.('[vite-runtime] executing', href)

await this.runner.runViteModule(context, transformed, metadata)
await this.runner.runViteModule(context, code, id, metadata)

return exports
}
Expand Down
13 changes: 8 additions & 5 deletions packages/vite/src/node/ssr/runtime/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ViteHotContext } from 'types/hot'
import type { HMRPayload } from 'types/hmrPayload'
import type { HMRConnection, HMRLogger } from '../../../shared/hmr'
import type { ModuleCacheMap } from './moduleCache'
import type {
Expand All @@ -22,6 +23,10 @@ export interface DefineImportMetadata {
importedNames?: string[]
}

export interface HMRRuntimeConnection extends HMRConnection {
onUpdate(callback: (payload: HMRPayload) => void): void
}

export interface SSRImportMetadata extends DefineImportMetadata {
isDynamicImport?: boolean
entrypoint?: boolean
Expand Down Expand Up @@ -49,12 +54,10 @@ export interface ViteModuleRunner {
runViteModule(
context: ViteRuntimeModuleContext,
code: string,
id: string,
metadata?: SSRImportMetadata,
): Promise<any>
runExternalModule(
filepath: string,
metadata?: SSRImportMetadata,
): Promise<any>
runExternalModule(file: string, metadata?: SSRImportMetadata): Promise<any>
/**
* This is called for every "import" (dynamic and static) statement and is not cached
*/
Expand Down Expand Up @@ -102,7 +105,7 @@ export interface ViteServerClientOptions {
hmr?:
| false
| {
connection: HMRConnection
connection: HMRRuntimeConnection
logger?: false | HMRLogger
}
moduleCache?: ModuleCacheMap
Expand Down
69 changes: 36 additions & 33 deletions packages/vite/src/node/ssr/runtime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,41 +46,44 @@ export function createImportMetaEnvProxy(
environmentVariables?: Record<string, any>,
): ImportMetaEnv {
const booleanKeys = ['DEV', 'PROD', 'SSR']
return new Proxy(process.env, {
get(_, key) {
if (typeof key !== 'string') return undefined
const env = _getEnv(environmentVariables)
if (booleanKeys.includes(key)) return !!env[key]
return env[key] ?? _envShim[key]
},
has(_, key) {
const env = _getEnv(environmentVariables)
return key in env || key in _envShim
},
set(_, key, value) {
if (typeof key !== 'string') return true

if (booleanKeys.includes(key)) {
value = value ? '1' : ''
}
return new Proxy(
{},
{
get(_, key) {
if (typeof key !== 'string') return undefined
const env = _getEnv(environmentVariables)
if (booleanKeys.includes(key)) return !!env[key]
return env[key] ?? _envShim[key]
},
has(_, key) {
const env = _getEnv(environmentVariables)
return key in env || key in _envShim
},
set(_, key, value) {
if (typeof key !== 'string') return true

if (booleanKeys.includes(key)) {
value = value ? '1' : ''
}

const env = _getEnv(environmentVariables) || _envShim
env[key] = value
return true
},
deleteProperty(_, prop) {
if (!prop) {
return false
}
const env = _getEnv(environmentVariables) || _envShim
delete env[prop as any]
return true
},
ownKeys() {
const env = _getEnv(environmentVariables) || _envShim
return Object.keys(env)
const env = _getEnv(environmentVariables) || _envShim
env[key] = value
return true
},
deleteProperty(_, prop) {
if (!prop) {
return false
}
const env = _getEnv(environmentVariables) || _envShim
delete env[prop as any]
return true
},
ownKeys() {
const env = _getEnv(environmentVariables) || _envShim
return Object.keys(env)
},
},
}) as ImportMetaEnv
) as ImportMetaEnv
}

export function isPrimitive(value: unknown): boolean {
Expand Down

0 comments on commit 2423810

Please sign in to comment.