Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/vite/src/node/__tests__/build.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,36 @@ describe('build', () => {
expect(chunk?.code).toContain('from-top-level-input')
})

test('top-level input can be a virtual module for build', async () => {
const input = 'virtual:entry'
const resolvedInput = `\0${input}`
const result = (await build({
root: resolve(dirname, 'packages/build-project'),
logLevel: 'silent',
input,
build: {
write: false,
},
plugins: [
{
name: 'virtual-entry',
resolveId(id) {
if (id === input) {
return resolvedInput
}
},
load(id) {
if (id === resolvedInput) {
return `console.log('from-virtual-top-level-input')`
}
},
},
],
})) as RolldownOutput
const chunk = result.output.find((o) => o.type === 'chunk')
expect(chunk?.code).toContain('from-virtual-top-level-input')
})

test('file hash should change when pure css chunk changes', async () => {
const buildProject = async (cssColor: string) => {
return (await build({
Expand Down
68 changes: 14 additions & 54 deletions packages/vite/src/node/__tests__/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1175,16 +1175,11 @@ describe('resolveConfig', () => {
)
})

const resolveInputFromRoot = (p: string) =>
normalizePath(path.resolve(process.cwd(), p))

test('top-level input applies to the client environment only (non-inherit)', async () => {
const config = await resolveConfig({ input: 'src/main.ts' }, 'serve')

expect(config.input).toBe(resolveInputFromRoot('src/main.ts'))
expect(config.environments.client.input).toBe(
resolveInputFromRoot('src/main.ts'),
)
expect(config.input).toBe('src/main.ts')
expect(config.environments.client.input).toBe('src/main.ts')
expect(
config.environments.client.build.rolldownOptions.input,
).toBeUndefined()
Expand All @@ -1203,59 +1198,31 @@ describe('resolveConfig', () => {
'serve',
)

expect(config.environments.client.input).toBe(
resolveInputFromRoot('src/main.ts'),
)
expect(config.environments.ssr.input).toBe(
resolveInputFromRoot('src/entry-server.ts'),
)
expect(config.environments.client.input).toBe('src/main.ts')
expect(config.environments.ssr.input).toBe('src/entry-server.ts')
})

test('resolves array input to absolute paths', async () => {
test('keeps array input relative to the root', async () => {
const config = await resolveConfig(
{ input: ['src/a.ts', 'src/b.ts'] },
'serve',
)

expect(config.environments.client.input).toEqual([
resolveInputFromRoot('src/a.ts'),
resolveInputFromRoot('src/b.ts'),
])
expect(config.environments.client.input).toEqual(['src/a.ts', 'src/b.ts'])
})

test('resolves record input to absolute paths', async () => {
test('keeps record input relative to the root', async () => {
const config = await resolveConfig(
{ input: { main: 'src/a.ts', admin: 'src/b.ts' } },
'serve',
)

expect(config.environments.client.input).toEqual({
main: resolveInputFromRoot('src/a.ts'),
admin: resolveInputFromRoot('src/b.ts'),
main: 'src/a.ts',
admin: 'src/b.ts',
})
})

test('adds input to server.fs.allow by default', async () => {
const inputs = {
main: resolveInputFromRoot('src/a.ts'),
admin: resolveInputFromRoot('src/b.ts'),
}
const config = await resolveConfig(
{ input: { main: 'src/a.ts', admin: 'src/b.ts' } },
'serve',
)

expect(config.server.fs.allow).toStrictEqual(
expect.arrayContaining(Object.values(inputs)),
)
})

test('adds the default index.html input to server.fs.allow', async () => {
const config = await resolveConfig({}, 'serve')

expect(config.server.fs.allow).toContain(resolveInputFromRoot('index.html'))
})

test('reserves glob characters in input', async () => {
const cases: { name: string; input: UserConfig['input'] }[] = [
{ name: 'wildcard', input: 'src/*.ts' },
Expand Down Expand Up @@ -1286,17 +1253,17 @@ describe('resolveConfig', () => {
{
name: 'glob',
input: 'src/\\*.ts',
expected: resolveInputFromRoot('src/*.ts'),
expected: 'src/*.ts',
},
{
name: 'array element',
input: ['src/\\*.ts'],
expected: [resolveInputFromRoot('src/*.ts')],
expected: ['src/*.ts'],
},
{
name: 'record',
input: { main: 'src/\\*.ts' },
expected: { main: resolveInputFromRoot('src/*.ts') },
expected: { main: 'src/*.ts' },
},
]

Expand All @@ -1319,14 +1286,14 @@ describe('resolveConfig', () => {
{
name: 'special characters',
input: 'src/a-b_c$.ts',
expected: resolveInputFromRoot('src/a-b_c$.ts'),
expected: 'src/a-b_c$.ts',
},
...(isWindows
? [
{
name: 'windows path',
input: 'src\\foo.ts',
expected: resolveInputFromRoot('src/foo.ts'),
expected: 'src\\foo.ts',
},
]
: []),
Expand Down Expand Up @@ -2047,7 +2014,6 @@ describe('resolveServerOptions', () => {
const resolved = await resolveServerOptions(
'/root',
{ allowedHosts: [] },
undefined,
logger,
)
expect(resolved.allowedHosts).toEqual(['example.com'])
Expand All @@ -2059,7 +2025,6 @@ describe('resolveServerOptions', () => {
const resolved = await resolveServerOptions(
'/root',
{ allowedHosts: [] },
undefined,
logger,
)
expect(resolved.allowedHosts).toEqual([
Expand All @@ -2075,7 +2040,6 @@ describe('resolveServerOptions', () => {
const resolved = await resolveServerOptions(
'/root',
{ allowedHosts: [] },
undefined,
logger,
)
expect(resolved.allowedHosts).toEqual([
Expand All @@ -2091,7 +2055,6 @@ describe('resolveServerOptions', () => {
const resolved = await resolveServerOptions(
'/root',
{ allowedHosts: [] },
undefined,
logger,
)
expect(resolved.allowedHosts).toEqual(['example.com', 'test.com'])
Expand All @@ -2102,7 +2065,6 @@ describe('resolveServerOptions', () => {
const resolved = await resolveServerOptions(
'/root',
{ allowedHosts: ['existing.com'] },
undefined,
logger,
)
expect(resolved.allowedHosts).toEqual([
Expand All @@ -2117,7 +2079,6 @@ describe('resolveServerOptions', () => {
const resolved = await resolveServerOptions(
'/root',
{ allowedHosts: true },
undefined,
logger,
)
expect(resolved.allowedHosts).toBe(true)
Expand All @@ -2130,7 +2091,6 @@ describe('resolveServerOptions', () => {
const resolved = await resolveServerOptions(
'/root',
{ allowedHosts: [] },
undefined,
logger,
)
expect(resolved.allowedHosts).toEqual([])
Expand Down
128 changes: 127 additions & 1 deletion packages/vite/src/node/__tests__/dev.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { afterEach, describe, expect, test } from 'vitest'
import path from 'node:path'
import { afterEach, describe, expect, test, vi } from 'vitest'
import type { ResolvedServerUrls } from 'vite'
import { createServer, resolveConfig } from '..'
import type { ViteDevServer } from '..'
import { promiseWithResolvers } from '../../shared/utils'
import { createLogger } from '../logger'
import { normalizePath } from '../utils'

describe('resolveBuildEnvironmentOptions in dev', () => {
test('build.rolldownOptions should not have input in lib', async () => {
Expand All @@ -28,6 +31,129 @@ describe('the dev server', () => {
await server?.close()
})

test('resolves each environment input as a safe module', async () => {
const root = path.join(import.meta.dirname, 'fixtures', 'input-option')
const clientEntry = normalizePath(path.join(root, 'client-entry.js'))
const ssrEntry = normalizePath(path.join(root, 'ssr-entry.js'))

server = await createServer({
configFile: false,
root,
input: 'virtual:client-entry',
environments: {
ssr: { input: { main: 'virtual:ssr-entry' } },
},
optimizeDeps: { noDiscovery: true },
server: { fs: { allow: [] }, middlewareMode: true, ws: false },
plugins: [
{
name: 'resolve-environment-entries',
resolveId(id) {
if (id === `virtual:${this.environment.name}-entry`) {
return this.environment.name === 'client' ? clientEntry : ssrEntry
}
},
},
],
})

expect(server.config.safeModulePaths).toStrictEqual(new Set([clientEntry]))
await server.environments.ssr.pluginContainer.buildStart()
expect(server.config.safeModulePaths).toStrictEqual(
new Set([clientEntry, ssrEntry]),
)
})

test('does not mark an external environment input as safe', async () => {
const root = path.join(import.meta.dirname, 'fixtures', 'input-option')
const externalEntry = normalizePath(path.join(root, 'external-entry.js'))

server = await createServer({
configFile: false,
root,
input: 'virtual:external-entry',
optimizeDeps: { noDiscovery: true },
server: { fs: { allow: [] }, middlewareMode: true, ws: false },
plugins: [
{
name: 'external-environment-entry',
resolveId(id) {
if (id === 'virtual:external-entry') {
return { id: externalEntry, external: true }
}
},
},
],
})

expect(server.config.safeModulePaths).not.toContain(externalEntry)
})

test('silently resolves index.html as the fallback for every environment', async () => {
const root = path.join(import.meta.dirname, 'fixtures', 'input-option')
const clientEntry = normalizePath(path.join(root, 'client-index.html'))
const resolvedEnvironments = new Set<string>()
const logger = createLogger('silent')
logger.warn = vi.fn()

server = await createServer({
configFile: false,
root,
customLogger: logger,
optimizeDeps: { noDiscovery: true },
server: { fs: { allow: [] }, middlewareMode: true, ws: false },
plugins: [
{
name: 'resolve-environment-index',
resolveId(id) {
if (id !== 'index.html') return
resolvedEnvironments.add(this.environment.name)
if (this.environment.name === 'ssr') {
throw new Error('ssr does not have an HTML entry')
}
return clientEntry
},
},
],
})

expect(resolvedEnvironments).toStrictEqual(new Set(['client']))
await server.environments.ssr.pluginContainer.buildStart()
expect(resolvedEnvironments).toStrictEqual(new Set(['client', 'ssr']))
expect(server.config.safeModulePaths).toStrictEqual(new Set([clientEntry]))
expect(logger.warn).not.toHaveBeenCalled()
})

test('does not ignore buildStart errors while resolving fallback inputs', async () => {
server = await createServer({
configFile: false,
root: path.join(import.meta.dirname, 'fixtures', 'input-option'),
logLevel: 'silent',
optimizeDeps: { noDiscovery: true },
plugins: [
{
name: 'failing-build-start',
perEnvironmentStartEndDuringDev: true,
buildStart() {
if (this.environment.name === 'ssr') {
throw new Error('buildStart failed')
}
},
},
],
server: {
fs: { allow: [] },
middlewareMode: true,
watch: null,
ws: false,
},
})

await expect(
server.environments.ssr.pluginContainer.buildStart(),
).rejects.toThrow('buildStart failed')
})

test('resolves the server URLs before the httpServer listening events are called', async () => {
expect.assertions(1)

Expand Down
Loading
Loading