Skip to content
Open
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
58 changes: 58 additions & 0 deletions packages/vite/src/node/__tests__/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1280,7 +1280,7 @@
]

for (const { name, input } of cases) {
await expect(resolveConfig({ input }, 'serve'), name).rejects.toThrow(

Check failure on line 1283 in packages/vite/src/node/__tests__/config.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24.15.0, windows-latest

packages/vite/src/node/__tests__/config.spec.ts > resolveConfig > reserves glob characters in input

AssertionError: promise resolved "{ input: 'src/*.ts', …(46), …(1) }" instead of rejecting - Expected + Received - Error { - "message": "rejected promise", + { + "appType": "spa", + "assetsInclude": [Function assetsInclude], + "base": "/", + "build": { + "assetsDir": "assets", + "assetsInlineLimit": 4096, + "chunkImportMap": false, + "chunkSizeWarningLimit": 500, + "commonjsOptions": { + "extensions": [ + ".js", + ".cjs", + ], + "include": [ + /node_modules/, + ], + }, + "copyPublicDir": true, + "createEnvironment": [Function createEnvironment], + "cssCodeSplit": true, + "cssMinify": true, + "cssTarget": [ + "chrome111", + "edge111", + "firefox114", + "safari16.4", + "ios16.4", + ], + "dynamicImportVarsOptions": { + "exclude": [ + /node_modules/, + ], + }, + "emitAssets": false, + "emptyOutDir": null, + "lib": false, + "license": false, + "manifest": false, + "minify": "oxc", + "modulePreload": { + "polyfill": true, + }, + "outDir": "dist", + "polyfillModulePreload": true, + "reportCompressedSize": true, + "rolldownOptions": { + "platform": "node", + }, + "rollupOptions": { + "platform": "node", + }, + "sourcemap": false, + "ssr": false, + "ssrEmitAssets": false, + "ssrManifest": false, + "target": [ + "chrome111", + "edge111", + "firefox114", + "safari16.4", + "ios16.4", + ], + "terserOptions": {}, + "watch": null, + "write": true, + }, + "builder": undefined, + "bundleChain": [], + "cacheDir": "D:/a/vite/vite/node_modules/.vite", + "command": "serve", + "configFile": undefined, + "configFileDependencies": [], + "createResolver": [Function createResolver], + "css": { + "devSourcemap": false, + "preprocessorMaxWorkers": true, + "transformer": "postcss", + }, + "decodedBase": "/", + "dev": { + "createEnvironment": [Function defaultCreateDevEnvironment], + "moduleRunnerTransform": false, + "preTransformRequests": false, + "recoverable": false, + "sourcemap": { + "js": true, + }, + "sourcemapIgnoreList": [Function isInNodeModules], + "warmup": [], + }, + "devtools": { + "config": { + "host": "localhost", + }, + "enabled": false, + }, + "env": { + "BASE_URL": "/", + "DEV": true, + "MODE": "development", + "PROD": false, + }, + "envDir": "D:/a/vite/vite", + "environments": { + "client": { + "build": { + "assetsDir": "assets", + "assetsInlineLimit": 4096, + "chunkImportMap": false, + "chunkSizeWarningLimit": 500, + "commonjsOptions": { + "extensions": [ + ".js", + ".cjs", + ], + "include": [ + /node_modules/, + ], + }, + "copyPublicDir": true, + "createEnvironment": [Function createEnvironment], + "cssCodeSplit": true, + "cssMinify": true, + "cssTarget": [ + "chrome111", + "edge111", + "firefox114", + "safari16.4", + "ios16.4", + ], + "dynamicImportVarsOptions": { + "exclude": [ + /node_modules/, + ], + }, + "emitAssets": true, + "emptyOutDir": null, + "lib": false, + "license": false, + "manifest": false, + "minify": "oxc", + "modulePreload": { + "polyfill": true, + }, + "outDir": "dist", + "polyfillModulePreload": true, + "reportCompressedSize": true, + "rolldownOptions": { + "platform": "browser", + }, + "rollupOptions": { + "platform": "browser", + }, + "sourcemap": false, + "ssr": false, + "ssrEmitAssets": false, + "ssrManifest": false, +
/`input` cannot contain glob characters/,
)
}
Expand Down Expand Up @@ -1313,12 +1313,70 @@
await expect(
resolveConfig({ input }, 'serve'),
name,
).resolves.toMatchObject({

Check failure on line 1316 in packages/vite/src/node/__tests__/config.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24.15.0, windows-latest

packages/vite/src/node/__tests__/config.spec.ts > resolveConfig > support escaped input

AssertionError: glob: expected { input: 'src/\*.ts', …(46), …(1) } to match object { input: 'src/*.ts' } (180 matching properties omitted from actual) - Expected + Received { - "input": "src/*.ts", + "input": "src/\\*.ts", } ❯ packages/vite/src/node/__tests__/config.spec.ts:1316:8
input: expected,
})
}
})

// https://github.com/vitejs/vite/issues/23383
test('preserves Windows input paths with glob-special segment names', async () => {
const cases: {
name: string
input: UserConfig['input']
expected: UserConfig['input']
}[] = [
...(isWindows
? [
{
name: 'segment starting with @',
input: 'D:\\desk\\test\\@src\\whatever.html',
expected: 'D:\\desk\\test\\@src\\whatever.html',
},
{
name: 'segment starting with !',
input: 'D:\\desk\\test\\!notes\\page.html',
expected: 'D:\\desk\\test\\!notes\\page.html',
},
{
name: 'segment starting with +',
input: 'D:\\desk\\test\\+draft\\page.html',
expected: 'D:\\desk\\test\\+draft\\page.html',
},
{
name: 'segment starting with (',
input: 'D:\\desk\\test\\(draft)\\page.html',
expected: 'D:\\desk\\test\\(draft)\\page.html',
},
{
name: 'segment starting with [',
input: 'D:\\desk\\test\\[id]\\page.html',
expected: 'D:\\desk\\test\\[id]\\page.html',
},
{
name: 'relative path',
input: 'test\\@src\\whatever.html',
expected: 'test\\@src\\whatever.html',
},
{
name: 'escaped glob on windows',
input: 'D:/desk/test/\\[id]\\page.html',
expected: 'D:/desk/test/[id]\\page.html',
},
]
: []),
]

for (const { name, input, expected } of cases) {
await expect(
resolveConfig({ input }, 'serve'),
name,
).resolves.toMatchObject({

Check failure on line 1374 in packages/vite/src/node/__tests__/config.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24.15.0, windows-latest

packages/vite/src/node/__tests__/config.spec.ts > resolveConfig > preserves Windows input paths with glob-special segment names

AssertionError: escaped glob on windows: expected { …(47), …(1) } to match object { Object (input) } (180 matching properties omitted from actual) - Expected + Received { - "input": "D:/desk/test/[id]\\page.html", + "input": "D:/desk/test/\\[id]\\page.html", } ❯ packages/vite/src/node/__tests__/config.spec.ts:1374:8
input: expected,
})
}
})

test('allows non-glob special characters in input', async () => {
const cases: {
name: string
Expand Down
10 changes: 7 additions & 3 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
createImportMetaResolver,
importMetaResolveWithCustomHookString,
} from '../module-runner/importMetaResolver'
import { withTrailingSlash } from '../shared/utils'
import { withTrailingSlash, isWindows } from '../shared/utils'
import type { AnymatchFn } from '../types/anymatch'
import type { HtmlAssetSource } from './assetSource'
import { PartialEnvironment } from './baseEnvironment'
Expand Down Expand Up @@ -945,10 +945,14 @@ function normalizeInput(
return resolved
}

const escapedGlobCharactersRE = /\\([*?[\]{}()!+@|])/g
const escapedGlobCharactersRE = /\\([*?[\]{}()|]|[@+!](?=\())/g
function hasUnescapedGlob(value: string): boolean {
return isDynamicPattern(value.replace(escapedGlobCharactersRE, ''))
}

function unescapeGlobCharacters(value: string): string {
if (isDynamicPattern(value)) {
if (isWindows) return value
if (hasUnescapedGlob(value)) {
// so that it could later be changed to accept globs without a breaking change
throw new Error(
`\`input\` cannot contain glob characters. They are reserved, ` +
Expand Down
Loading