From bbbd0eca6bc94750d2301a25b4e37ae029a80633 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:20:40 +0900 Subject: [PATCH 1/9] refactor: enable `noUncheckedIndexedAccess` --- packages/create-vite/src/index.ts | 8 +-- packages/create-vite/tsconfig.json | 3 +- packages/plugin-legacy/src/index.ts | 6 +- packages/plugin-legacy/tsconfig.json | 1 + packages/vite/rollup.config.ts | 2 +- packages/vite/rollup.dts.config.ts | 2 +- packages/vite/rollupLicensePlugin.ts | 14 ++-- packages/vite/src/client/client.ts | 5 +- packages/vite/src/client/env.ts | 2 +- packages/vite/src/client/overlay.ts | 5 +- .../module-runner/sourcemap/interceptor.ts | 16 +++-- packages/vite/src/node/assetSource.ts | 6 +- packages/vite/src/node/baseEnvironment.ts | 13 +++- packages/vite/src/node/build.ts | 13 ++-- packages/vite/src/node/config.ts | 12 ++-- .../src/node/optimizer/esbuildDepPlugin.ts | 6 +- packages/vite/src/node/optimizer/index.ts | 26 ++++--- packages/vite/src/node/optimizer/optimizer.ts | 24 +++---- packages/vite/src/node/optimizer/resolve.ts | 5 +- packages/vite/src/node/optimizer/scan.ts | 5 +- packages/vite/src/node/plugins/asset.ts | 18 +++-- .../src/node/plugins/assetImportMetaUrl.ts | 4 +- packages/vite/src/node/plugins/css.ts | 27 +++---- packages/vite/src/node/plugins/dataUri.ts | 5 +- packages/vite/src/node/plugins/define.ts | 2 +- .../src/node/plugins/dynamicImportVars.ts | 9 ++- packages/vite/src/node/plugins/html.ts | 9 ++- .../vite/src/node/plugins/importAnalysis.ts | 11 +-- .../src/node/plugins/importAnalysisBuild.ts | 31 +++++--- .../vite/src/node/plugins/importMetaGlob.ts | 10 +-- packages/vite/src/node/plugins/json.ts | 5 +- packages/vite/src/node/plugins/manifest.ts | 4 +- .../vite/src/node/plugins/optimizedDeps.ts | 5 +- packages/vite/src/node/plugins/resolve.ts | 7 +- packages/vite/src/node/plugins/worker.ts | 3 +- .../src/node/plugins/workerImportMetaUrl.ts | 3 +- packages/vite/src/node/preview.ts | 2 +- packages/vite/src/node/server/index.ts | 8 +-- .../src/node/server/middlewares/indexHtml.ts | 2 +- .../vite/src/node/server/middlewares/proxy.ts | 4 +- .../vite/src/node/ssr/ssrManifestPlugin.ts | 4 +- packages/vite/src/node/ssr/ssrStacktrace.ts | 5 +- packages/vite/src/node/ssr/ssrTransform.ts | 6 +- packages/vite/src/node/utils.ts | 23 +++--- .../src/shared/__tests_dts__/typeUtils.ts | 70 +++++++++++++++++++ packages/vite/src/shared/tsconfig.json | 2 +- packages/vite/src/shared/typeUtils.ts | 26 +++++++ packages/vite/tsconfig.base.json | 3 +- 48 files changed, 330 insertions(+), 152 deletions(-) create mode 100644 packages/vite/src/shared/__tests_dts__/typeUtils.ts create mode 100644 packages/vite/src/shared/typeUtils.ts diff --git a/packages/create-vite/src/index.ts b/packages/create-vite/src/index.ts index 6090d1e37b1744..853f480fc4c1de 100755 --- a/packages/create-vite/src/index.ts +++ b/packages/create-vite/src/index.ts @@ -478,7 +478,7 @@ async function init() { const replacedArgs = args.map((arg) => arg.replace('TARGET_DIR', () => targetDir), ) - const { status } = spawn.sync(command, replacedArgs, { + const { status } = spawn.sync(command!, replacedArgs, { stdio: 'inherit', }) process.exit(status ?? 0) @@ -596,11 +596,11 @@ function emptyDir(dir: string) { function pkgFromUserAgent(userAgent: string | undefined) { if (!userAgent) return undefined - const pkgSpec = userAgent.split(' ')[0] + const pkgSpec = userAgent.split(' ')[0]! const pkgSpecArr = pkgSpec.split('/') return { - name: pkgSpecArr[0], - version: pkgSpecArr[1], + name: pkgSpecArr[0]!, + version: pkgSpecArr[1] ?? '', } } diff --git a/packages/create-vite/tsconfig.json b/packages/create-vite/tsconfig.json index 9fc23ebe675e0f..ac75b047d029b9 100644 --- a/packages/create-vite/tsconfig.json +++ b/packages/create-vite/tsconfig.json @@ -10,6 +10,7 @@ "declaration": false, "sourceMap": false, "noUnusedLocals": true, - "esModuleInterop": true + "esModuleInterop": true, + "noUncheckedIndexedAccess": true } } diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index d356793b231c72..12a0838c3b75fa 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -717,7 +717,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { if (isLegacyBundle(bundle, opts) && genModern) { // avoid emitting duplicate assets for (const name in bundle) { - if (bundle[name].type === 'asset' && !/.+\.map$/.test(name)) { + if (bundle[name]!.type === 'asset' && !/.+\.map$/.test(name)) { delete bundle[name] } } @@ -831,7 +831,7 @@ async function buildPolyfillChunk( }, }, }) - const _polyfillChunk = Array.isArray(res) ? res[0] : res + const _polyfillChunk = Array.isArray(res) ? res[0]! : res if (!('output' in _polyfillChunk)) return const polyfillChunk = _polyfillChunk.output.find( (chunk) => chunk.type === 'chunk' && chunk.isEntry, @@ -840,7 +840,7 @@ async function buildPolyfillChunk( // associate the polyfill chunk to every entry chunk so that we can retrieve // the polyfill filename in index html transform for (const key in bundle) { - const chunk = bundle[key] + const chunk = bundle[key]! if (chunk.type === 'chunk' && chunk.facadeModuleId) { facadeToChunkMap.set(chunk.facadeModuleId, polyfillChunk.fileName) } diff --git a/packages/plugin-legacy/tsconfig.json b/packages/plugin-legacy/tsconfig.json index e4b331d3c7d139..69d4f38cc9662b 100644 --- a/packages/plugin-legacy/tsconfig.json +++ b/packages/plugin-legacy/tsconfig.json @@ -11,6 +11,7 @@ "sourceMap": true, "noUnusedLocals": true, "esModuleInterop": true, + "noUncheckedIndexedAccess": true, "paths": { "vite": ["../vite/src/node/index.js"] } diff --git a/packages/vite/rollup.config.ts b/packages/vite/rollup.config.ts index bec0eabdd65d38..6d349404da57dc 100644 --- a/packages/vite/rollup.config.ts +++ b/packages/vite/rollup.config.ts @@ -242,7 +242,7 @@ function shimDepsPlugin(deps: Record): Plugin { transform(code, id) { for (const file in deps) { if (id.replace(/\\/g, '/').endsWith(file)) { - for (const { src, replacement, pattern } of deps[file]) { + for (const { src, replacement, pattern } of deps[file]!) { const magicString = new MagicString(code) if (src) { diff --git a/packages/vite/rollup.dts.config.ts b/packages/vite/rollup.dts.config.ts index e622053ba156ba..83804a3f23a57c 100644 --- a/packages/vite/rollup.dts.config.ts +++ b/packages/vite/rollup.dts.config.ts @@ -182,7 +182,7 @@ function replaceConfusingTypeNames( continue } - const betterId = replacements[id] + const betterId = replacements[id]! const regexEscapedId = escapeRegex(id) // If the better id accesses a namespace, the existing `Foo as Foo$1` // named import cannot be replaced with `Foo as Namespace.Foo`, so we diff --git a/packages/vite/rollupLicensePlugin.ts b/packages/vite/rollupLicensePlugin.ts index 831883988713fa..52629054b7da75 100644 --- a/packages/vite/rollupLicensePlugin.ts +++ b/packages/vite/rollupLicensePlugin.ts @@ -28,11 +28,11 @@ export default function licensePlugin( let dependencyLicenseTexts = '' for (let i = 0; i < deps.length; i++) { // Find dependencies with the same license text so it can be shared - const licenseText = deps[i].licenseText - const sameDeps = [deps[i]] + const licenseText = deps[i]!.licenseText + const sameDeps = [deps[i]!] if (licenseText) { for (let j = i + 1; j < deps.length; j++) { - if (licenseText === deps[j].licenseText) { + if (licenseText === deps[j]!.licenseText) { sameDeps.push(...deps.splice(j, 1)) j-- } @@ -47,11 +47,11 @@ export default function licensePlugin( depInfos.length > 1 && depInfos.every( (info) => - info.license === depInfos[0].license && - info.names === depInfos[0].names, + info.license === depInfos[0]!.license && + info.names === depInfos[0]!.names, ) ) { - const { license, names } = depInfos[0] + const { license, names } = depInfos[0]! const repositoryText = depInfos .map((info) => info.repository) .filter(Boolean) @@ -64,7 +64,7 @@ export default function licensePlugin( // Else show each dependency separately else { for (let j = 0; j < depInfos.length; j++) { - const { license, names, repository } = depInfos[j] + const { license, names, repository } = depInfos[j]! if (license) text += `License: ${license}\n` if (names) text += `By: ${names}\n` diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index fdf13ded820b28..0a02b76b670af7 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -140,7 +140,10 @@ const hmrClient = new HMRClient( explicitImportRequired, isWithinCircularImport, }) { - const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`) + const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`) as [ + string, + ...string[], + ] const importPromise = import( /* @vite-ignore */ base + diff --git a/packages/vite/src/client/env.ts b/packages/vite/src/client/env.ts index 158ce43f7bf36f..2eaf1b9120c7d7 100644 --- a/packages/vite/src/client/env.ts +++ b/packages/vite/src/client/env.ts @@ -18,7 +18,7 @@ Object.keys(defines).forEach((key) => { const segments = key.split('.') let target = context for (let i = 0; i < segments.length; i++) { - const segment = segments[i] + const segment = segments[i]! if (i === segments.length - 1) { target[segment] = defines[key] } else { diff --git a/packages/vite/src/client/overlay.ts b/packages/vite/src/client/overlay.ts index 63f570be488efb..8bc3ed07edb84e 100644 --- a/packages/vite/src/client/overlay.ts +++ b/packages/vite/src/client/overlay.ts @@ -225,7 +225,10 @@ export class ErrorOverlay extends HTMLElement { } this.text('.message-body', message.trim()) - const [file] = (err.loc?.file || err.id || 'unknown file').split(`?`) + const [file] = (err.loc?.file || err.id || 'unknown file').split(`?`) as [ + string, + ...string[], + ] if (err.loc) { this.text('.file', `${file}:${err.loc.line}:${err.loc.column}`, links) } else if (err.id) { diff --git a/packages/vite/src/module-runner/sourcemap/interceptor.ts b/packages/vite/src/module-runner/sourcemap/interceptor.ts index eb4f41f83e68a2..388a59b24e7a1b 100644 --- a/packages/vite/src/module-runner/sourcemap/interceptor.ts +++ b/packages/vite/src/module-runner/sourcemap/interceptor.ts @@ -3,6 +3,7 @@ import type { ModuleRunner } from '../runner' import { posixDirname, posixResolve } from '../utils' import type { EvaluatedModules } from '../evaluatedModules' import { slash } from '../../shared/utils' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' import { DecodedMap, getOriginalPosition } from './decoder' interface RetrieveFileHandler { @@ -172,7 +173,8 @@ function retrieveSourceMap(source: string) { function mapSourcePosition(position: OriginalMapping) { if (!position.source) return position - let sourceMap = getRunnerSourceMap(position) + let sourceMap: CachedMapEntry | null | undefined = + getRunnerSourceMap(position) if (!sourceMap) sourceMap = sourceMapCache[position.source] if (!sourceMap) { // Call the (overrideable) retrieveSourceMap function to get the source map. @@ -238,7 +240,9 @@ function mapSourcePosition(position: OriginalMapping) { // https://code.google.com/p/v8/source/browse/trunk/src/messages.js function mapEvalOrigin(origin: string): string { // Most eval() calls are in this format - let match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin) + const match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec( + origin, + ) as StrictRegExpExecArrayFromLen<4> | null if (match) { const position = mapSourcePosition({ name: null, @@ -250,8 +254,10 @@ function mapEvalOrigin(origin: string): string { } // Parse nested eval() calls using recursion - match = /^eval at ([^(]+) \((.+)\)$/.exec(origin) - if (match) return `eval at ${match[1]} (${mapEvalOrigin(match[2])})` + const match2 = /^eval at ([^(]+) \((.+)\)$/.exec( + origin, + ) as StrictRegExpExecArrayFromLen<2> | null + if (match2) return `eval at ${match2[1]} (${mapEvalOrigin(match2[2])})` // Make sure we still return useful information if we didn't find anything return origin @@ -425,7 +431,7 @@ function prepareStackTrace(error: Error, stack: CallSite[]) { const state = { nextPosition: null, curPosition: null } const processedStack = [] for (let i = stack.length - 1; i >= 0; i--) { - processedStack.push(`\n at ${wrapCallSite(stack[i], state)}`) + processedStack.push(`\n at ${wrapCallSite(stack[i]!, state)}`) state.nextPosition = state.curPosition } state.curPosition = state.nextPosition = null diff --git a/packages/vite/src/node/assetSource.ts b/packages/vite/src/node/assetSource.ts index b733258896678f..2acc2a71b27c2c 100644 --- a/packages/vite/src/node/assetSource.ts +++ b/packages/vite/src/node/assetSource.ts @@ -128,7 +128,7 @@ export function getNodeAssetAttributes( key: 'vite-ignore', value: '', attributes, - location: node.sourceCodeLocation!.attrs!['vite-ignore'], + location: node.sourceCodeLocation!.attrs!['vite-ignore']!, }, ] } @@ -137,8 +137,8 @@ export function getNodeAssetAttributes( function handleAttributeKey(key: string, type: 'src' | 'srcset') { const value = attributes[key] if (!value) return - if (matched.filter && !matched.filter({ key, value, attributes })) return - const location = node.sourceCodeLocation!.attrs![key] + if (matched!.filter && !matched!.filter({ key, value, attributes })) return + const location = node.sourceCodeLocation!.attrs![key]! actions.push({ type, key, value, attributes, location }) } matched.srcAttributes?.forEach((key) => handleAttributeKey(key, 'src')) diff --git a/packages/vite/src/node/baseEnvironment.ts b/packages/vite/src/node/baseEnvironment.ts index c3202ccf06d28e..8ab2a6b24feecc 100644 --- a/packages/vite/src/node/baseEnvironment.ts +++ b/packages/vite/src/node/baseEnvironment.ts @@ -52,7 +52,7 @@ export class PartialEnvironment { constructor( name: string, topLevelConfig: ResolvedConfig, - options: ResolvedEnvironmentOptions = topLevelConfig.environments[name], + options?: ResolvedEnvironmentOptions, ) { // only allow some characters so that we can use name without escaping for directory names // and make users easier to access with `environments.*` @@ -61,6 +61,13 @@ export class PartialEnvironment { `Invalid environment name "${name}". Environment names must only contain alphanumeric characters and "$", "_".`, ) } + if (!options) { + const resolvedOptions = topLevelConfig.environments[name] + if (!resolvedOptions) { + throw new Error(`Environment "${name}" is not defined in config.`) + } + options = resolvedOptions + } this.name = name this._topLevelConfig = topLevelConfig this._options = options @@ -82,7 +89,7 @@ export class PartialEnvironment { const colorIndex = [...this.name].reduce((acc, c) => acc + c.charCodeAt(0), 0) % environmentColors.length - const infoColor = environmentColors[colorIndex || 0] + const infoColor = environmentColors[colorIndex || 0]! this.logger = { get hasWarned() { return topLevelConfig.logger.hasWarned @@ -142,7 +149,7 @@ export class BaseEnvironment extends PartialEnvironment { constructor( name: string, config: ResolvedConfig, - options: ResolvedEnvironmentOptions = config.environments[name], + options?: ResolvedEnvironmentOptions, ) { super(name, config, options) } diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index e5d80841ad4289..68d2240b705009 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -831,7 +831,7 @@ async function buildEnvironment( logger.info( `${colors.green(`✓ built in ${displayTime(Date.now() - startTime)}`)}`, ) - return Array.isArray(outputs) ? res : res[0] + return Array.isArray(outputs) ? res : res[0]! } catch (e) { enhanceRollupError(e) clearLine() @@ -1532,7 +1532,8 @@ export async function createBuilder( // remove the default values that shouldn't be used at all once the config is resolved const environmentName = resolved.build.ssr ? 'ssr' : 'client' ;(resolved.build as ResolvedBuildOptions) = { - ...resolved.environments[environmentName].build, + // both resolved.environments.client and resolved.environments.ssr exists + ...resolved.environments[environmentName]!.build, } } const config = await resolveConfigToBuild(inlineConfig, patchConfig) @@ -1575,21 +1576,21 @@ export async function createBuilder( // We can deprecate `config.build` in ResolvedConfig and push everyone to upgrade, and later // remove the default values that shouldn't be used at all once the config is resolved ;(resolved.build as ResolvedBuildOptions) = { - ...resolved.environments[environmentName].build, + ...resolved.environments[environmentName]!.build, } } const patchPlugins = (resolvedPlugins: Plugin[]) => { // Force opt-in shared plugins let j = 0 for (let i = 0; i < resolvedPlugins.length; i++) { - const environmentPlugin = resolvedPlugins[i] + const environmentPlugin = resolvedPlugins[i]! if ( configBuilder.sharedPlugins || environmentPlugin.sharedDuringBuild ) { for (let k = j; k < config.plugins.length; k++) { - if (environmentPlugin.name === config.plugins[k].name) { - resolvedPlugins[i] = config.plugins[k] + if (environmentPlugin.name === config.plugins[k]!.name) { + resolvedPlugins[i] = config.plugins[k]! j = k + 1 break } diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 6c65a2aaaa9012..319a91dbccdb22 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -1139,7 +1139,7 @@ export async function resolveConfig( name === 'client' ? defaultClientEnvironmentOptions : defaultNonClientEnvironmentOptions, - config.environments[name], + config.environments[name]!, ) } @@ -1155,7 +1155,7 @@ export async function resolveConfig( const resolvedEnvironments: Record = {} for (const environmentName of Object.keys(config.environments)) { resolvedEnvironments[environmentName] = resolveEnvironmentOptions( - config.environments[environmentName], + config.environments[environmentName]!, resolvedDefaultResolve.alias, resolvedDefaultResolve.preserveSymlinks, logger, @@ -1170,7 +1170,7 @@ export async function resolveConfig( // optimizeDeps in the ResolvedConfig hook, so these changes will be reflected on the // client environment. const backwardCompatibleOptimizeDeps = - resolvedEnvironments.client.optimizeDeps + resolvedEnvironments.client!.optimizeDeps const resolvedDevEnvironmentOptions = resolveDevEnvironmentOptions( config.dev, @@ -1837,7 +1837,7 @@ async function bundleConfigFile( }, ], }) - const { text } = result.outputFiles[0] + const { text } = result.outputFiles[0]! return { code: text, dependencies: result.metafile ? Object.keys(result.metafile.inputs) : [], @@ -1936,12 +1936,12 @@ async function runConfigEnvironmentHook( const handler = getHookHandler(hook) if (handler) { for (const name of environmentNames) { - const res = await handler(name, environments[name], { + const res = await handler(name, environments[name]!, { ...configEnv, isSsrTargetWebworker: isSsrTargetWebworkerSet && name === 'ssr', }) if (res) { - environments[name] = mergeConfig(environments[name], res) + environments[name] = mergeConfig(environments[name]!, res) } } } diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 7afbb7fbdea82f..316a43ffb3eae8 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -96,7 +96,7 @@ export function esbuildDepPlugin( _importer = normalizePath(path.join(resolveDir, '*')) } else { // map importer ids to file paths for correct resolution - _importer = importer in qualified ? qualified[importer] : importer + _importer = importer in qualified ? qualified[importer]! : importer } const resolver = kind.startsWith('require') ? _resolveRequire : _resolve return resolver(environment, id, _importer) @@ -192,7 +192,7 @@ export function esbuildDepPlugin( ? `import ${modulePath};` : `export { default } from ${modulePath};` + `export * from ${modulePath};`, - loader: 'js', + loader: 'js' as const, } }, ) @@ -201,7 +201,7 @@ export function esbuildDepPlugin( const flatId = flattenId(id) if (flatId in qualified) { return { - path: qualified[flatId], + path: qualified[flatId]!, } } } diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index f52bd879149a8f..455c49e4f6a118 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -438,7 +438,7 @@ export function toDiscoveredDependencies( ) const discovered: Record = {} for (const id in deps) { - const src = deps[id] + const src = deps[id]! discovered[id] = { id, file: getOptimizedDepPath(environment, id), @@ -632,15 +632,13 @@ export function runOptimizeDeps( processingCacheDir, ) - const { exportsData, ...info } = depsInfo[id] + const { exportsData, ...info } = depsInfo[id]! addOptimizedDepInfo(metadata, 'optimized', { ...info, // We only need to hash the output.imports in to check for stability, but adding the hash // and file path gives us a unique hash that may be useful for other things in the future fileHash: getHash( - metadata.hash + - depsInfo[id].file + - JSON.stringify(output.imports), + metadata.hash + info.file + JSON.stringify(output.imports), ), browserHash: metadata.browserHash, // After bundling we have more information and can warn the user about legacy packages @@ -648,7 +646,7 @@ export function runOptimizeDeps( needsInterop: needsInterop( environment, id, - idToExports[id], + idToExports[id]!, output, ), }) @@ -676,7 +674,7 @@ export function runOptimizeDeps( } else { // workaround Firefox warning by removing blank source map reference // https://github.com/evanw/esbuild/issues/3945 - const output = meta.outputs[o] + const output = meta.outputs[o]! // filter by exact bytes of an empty source map if (output.bytes === 93) { const jsMapPath = path.resolve(o) @@ -754,9 +752,9 @@ async function prepareEsbuildOptimizerRun( optimizeDeps?.esbuildOptions ?? {} await Promise.all( - Object.keys(depsInfo).map(async (id) => { - const src = depsInfo[id].src! - const exportsData = await (depsInfo[id].exportsData ?? + Object.entries(depsInfo).map(async ([id, info]) => { + const src = info.src! + const exportsData = await (info.exportsData ?? extractExportsData(environment, src)) if (exportsData.jsxLoader && !esbuildOptions.loader?.['.js']) { // Ensure that optimization won't fail by defaulting '.js' to the JSX parser. @@ -849,7 +847,7 @@ export async function addManuallyIncludedOptimizeDeps( const includes = [...optimizeDepsInclude] for (let i = 0; i < includes.length; i++) { - const id = includes[i] + const id = includes[i]! if (isDynamicPattern(id)) { const globIds = expandGlobIds(id, environment.getTopLevelConfig()) includes.splice(i, 1, ...globIds) @@ -884,7 +882,7 @@ export function depsFromOptimizedDepInfo( ): Record { const obj: Record = {} for (const key in depsInfo) { - obj[key] = depsInfo[key].src! + obj[key] = depsInfo[key]!.src! } return obj } @@ -1094,7 +1092,7 @@ export async function extractExportsData( write: false, format: 'esm', }) - const [, exports, , hasModuleSyntax] = parse(result.outputFiles[0].text) + const [, exports, , hasModuleSyntax] = parse(result.outputFiles[0]!.text) return { hasModuleSyntax, exports: exports.map((e) => e.n), @@ -1304,7 +1302,7 @@ function findOptimizedDepInfoInRecord( callbackFn: (depInfo: OptimizedDepInfo, id: string) => any, ): OptimizedDepInfo | undefined { for (const o of Object.keys(dependenciesInfo)) { - const info = dependenciesInfo[o] + const info = dependenciesInfo[o]! if (callbackFn(info, o)) { return info } diff --git a/packages/vite/src/node/optimizer/optimizer.ts b/packages/vite/src/node/optimizer/optimizer.ts index 8349e914dfe6c9..dba22cacbe8937 100644 --- a/packages/vite/src/node/optimizer/optimizer.ts +++ b/packages/vite/src/node/optimizer/optimizer.ts @@ -218,7 +218,7 @@ export function createDepsOptimizer( // This is also used by the CJS externalization heuristics in legacy mode for (const id of Object.keys(deps)) { if (!metadata.discovered[id]) { - addMissingDep(id, deps[id]) + addMissingDep(id, deps[id]!) } } @@ -277,11 +277,11 @@ export function createDepsOptimizer( // Clone optimized info objects, fileHash, browserHash may be changed for them const metadata = depsOptimizer.metadata! for (const dep of Object.keys(metadata.optimized)) { - knownDeps[dep] = { ...metadata.optimized[dep] } + knownDeps[dep] = { ...metadata.optimized[dep]! } } for (const dep of Object.keys(metadata.discovered)) { // Clone the discovered info discarding its processing promise - const { processing, ...info } = metadata.discovered[dep] + const { processing, ...info } = metadata.discovered[dep]! knownDeps[dep] = info } return knownDeps @@ -352,7 +352,8 @@ export function createDepsOptimizer( metadata.hash !== newData.hash || Object.keys(metadata.optimized).some((dep) => { return ( - metadata.optimized[dep].fileHash !== newData.optimized[dep].fileHash + metadata.optimized[dep]!.fileHash !== + newData.optimized[dep]?.fileHash ) }) @@ -363,7 +364,7 @@ export function createDepsOptimizer( // in which case they will keep being added to metadata.discovered for (const id in metadata.discovered) { if (!newData.optimized[id]) { - addOptimizedDepInfo(newData, 'discovered', metadata.discovered[id]) + addOptimizedDepInfo(newData, 'discovered', metadata.discovered[id]!) } } @@ -371,12 +372,11 @@ export function createDepsOptimizer( if (!needsReload) { newData.browserHash = metadata.browserHash for (const dep in newData.chunks) { - newData.chunks[dep].browserHash = metadata.browserHash + newData.chunks[dep]!.browserHash = metadata.browserHash } for (const dep in newData.optimized) { - newData.optimized[dep].browserHash = ( - metadata.optimized[dep] || metadata.discovered[dep] - ).browserHash + newData.optimized[dep]!.browserHash = (metadata.optimized[dep] || + metadata.discovered[dep])!.browserHash } } @@ -386,7 +386,7 @@ export function createDepsOptimizer( for (const o in newData.optimized) { const discovered = metadata.discovered[o] if (discovered) { - const optimized = newData.optimized[o] + const optimized = newData.optimized[o]! discovered.browserHash = optimized.browserHash discovered.fileHash = optimized.fileHash discovered.needsInterop = optimized.needsInterop @@ -675,7 +675,7 @@ export function createDepsOptimizer( // Add deps found by the scanner to the discovered deps while crawling for (const dep of scanDeps) { if (!crawlDeps.includes(dep)) { - addMissingDep(dep, result.metadata.optimized[dep].src!) + addMissingDep(dep, result.metadata.optimized[dep]!.src!) } } if (scannerMissedDeps) { @@ -772,7 +772,7 @@ function findInteropMismatches( ) { const needsInteropMismatch = [] for (const dep in discovered) { - const discoveredDepInfo = discovered[dep] + const discoveredDepInfo = discovered[dep]! if (discoveredDepInfo.needsInterop === undefined) continue const depInfo = optimized[dep] diff --git a/packages/vite/src/node/optimizer/resolve.ts b/packages/vite/src/node/optimizer/resolve.ts index 6da566b3c6e011..e07fd4a2223742 100644 --- a/packages/vite/src/node/optimizer/resolve.ts +++ b/packages/vite/src/node/optimizer/resolve.ts @@ -7,6 +7,7 @@ import { resolvePackageData } from '../packages' import { slash } from '../../shared/utils' import type { Environment } from '../environment' import { createBackCompatIdResolver } from '../idResolver' +import type { StrictRegExpExecArray } from '../../shared/typeUtils' export function createOptimizeDepsIncludeResolver( environment: Environment, @@ -106,7 +107,9 @@ export function expandGlobIds(id: string, config: ResolvedConfig): string[] { // `filePath`: "./dist/glob/foo-browser/foo.js" // we need to revert the file path back to the export key by // matching value regex and replacing the capture groups to the key - const matched = exportsValueGlobRe.exec(slash(filePath)) + const matched = exportsValueGlobRe.exec( + slash(filePath), + ) as StrictRegExpExecArray<[true, ...true[]]> | null // `matched`: [..., 'foo', 'foo'] if (matched) { let allGlobSame = matched.length === 2 diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 10bb9960cb759e..b64c56bd795c2f 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -42,6 +42,7 @@ import type { DevEnvironment } from '../server/environment' import { transformGlobImport } from '../plugins/importMetaGlob' import { cleanUrl } from '../../shared/utils' import { loadTsconfigJsonForFile } from '../plugins/esbuild' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' export class ScanEnvironment extends BaseEnvironment { mode = 'scan' as const @@ -504,7 +505,9 @@ function esbuildScanPlugin( const isHtml = p.endsWith('.html') let js = '' let scriptId = 0 - const matches = raw.matchAll(scriptRE) + const matches = raw.matchAll(scriptRE) as RegExpStringIterator< + StrictRegExpExecArrayFromLen<2> + > for (const [, openTag, content] of matches) { const typeMatch = typeRE.exec(openTag) const type = diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index d6f84ebc42966a..9e027aee2a4a79 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -30,6 +30,10 @@ import { withTrailingSlash, } from '../../shared/utils' import type { Environment } from '../environment' +import type { + StrictRegExpExecArray, + StrictRegExpExecArrayFromLen, +} from '../../shared/typeUtils' // referenceId is base64url but replaces - with $ export const assetUrlRE = /__VITE_ASSET__([\w$]+)__(?:\$_(.*?)__)?/g @@ -85,7 +89,9 @@ export function renderAssetUrlInJS( assetUrlRE.lastIndex = 0 while ((match = assetUrlRE.exec(code))) { s ||= new MagicString(code) - const [full, referenceId, postfix = ''] = match + const [full, referenceId, postfix = ''] = match as StrictRegExpExecArray< + [true, boolean] + > const file = pluginContext.getFileName(referenceId) chunk.viteMetadata!.importedAssets.add(cleanUrl(file)) const filename = file + postfix @@ -112,7 +118,7 @@ export function renderAssetUrlInJS( publicAssetUrlRE.lastIndex = 0 while ((match = publicAssetUrlRE.exec(code))) { s ||= new MagicString(code) - const [full, hash] = match + const [full, hash] = match as StrictRegExpExecArrayFromLen<1> const publicUrl = publicAssetUrlMap.get(hash)!.slice(1) const replacement = toOutputFilePathInJS( environment, @@ -222,13 +228,13 @@ export function assetPlugin(config: ResolvedConfig): Plugin { generateBundle(_, bundle) { // Remove empty entry point file for (const file in bundle) { - const chunk = bundle[file] + const chunk = bundle[file]! if ( chunk.type === 'chunk' && chunk.isEntry && chunk.moduleIds.length === 1 && - config.assetsInclude(chunk.moduleIds[0]) && - this.getModuleInfo(chunk.moduleIds[0])?.meta['vite:asset'] + config.assetsInclude(chunk.moduleIds[0]!) && + this.getModuleInfo(chunk.moduleIds[0]!)?.meta['vite:asset'] ) { delete bundle[file] } @@ -241,7 +247,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { ) { for (const file in bundle) { if ( - bundle[file].type === 'asset' && + bundle[file]!.type === 'asset' && !file.endsWith('ssr-manifest.json') && !jsSourceMapRE.test(file) ) { diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index ba3e434d44d2a0..a06015002dc1e9 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -13,6 +13,7 @@ import { CLIENT_ENTRY } from '../constants' import { slash } from '../../shared/utils' import { createBackCompatIdResolver } from '../idResolver' import type { ResolveIdFn } from '../idResolver' +import type { StrictRegExpIndicesArrayFromLen } from '../../shared/typeUtils' import { fileToUrl } from './asset' import { preloadHelperId } from './importAnalysisBuild' import type { InternalResolveOptions } from './resolve' @@ -60,7 +61,8 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { let match: RegExpExecArray | null while ((match = assetImportMetaUrlRE.exec(cleanString))) { - const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices! + const [[startIndex, endIndex], [urlStart, urlEnd]] = + match.indices! as StrictRegExpIndicesArrayFromLen<1> if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue const rawUrl = code.slice(urlStart, urlEnd) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 3f3e35fb962f49..efcaa84ee32495 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -87,6 +87,7 @@ import { searchForWorkspaceRoot } from '../server/searchRoot' import { type DevEnvironment } from '..' import type { PackageCache } from '../packages' import { findNearestPackageData } from '../packages' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' import { addToHTMLProxyTransformResult } from './html' import { assetUrlRE, @@ -376,7 +377,7 @@ export function cssPlugin(config: ResolvedConfig): Plugin { return joinUrlSegments(config.base, decodedUrl) } } - const [id, fragment] = decodedUrl.split('#') + const [id, fragment] = decodedUrl.split('#') as [string, ...string[]] let resolved = await resolveUrl(id, importer) if (resolved) { if (fragment) resolved += '#' + fragment @@ -712,7 +713,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { let match: RegExpExecArray | null cssUrlAssetRE.lastIndex = 0 while ((match = cssUrlAssetRE.exec(code))) { - const [full, idHex] = match + const [full, idHex] = match as StrictRegExpExecArrayFromLen<1> const id = Buffer.from(idHex, 'hex').toString() const originalFileName = cleanUrl(id) const cssAssetName = ensureFileExt( @@ -910,7 +911,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { collected.add(chunk) // First collect all styles from the synchronous imports (lowest priority) - chunk.imports.forEach((importName) => collect(bundle[importName])) + chunk.imports.forEach((importName) => collect(bundle[importName]!)) // Save dynamic imports in deterministic order to add the styles later (to have the highest priority) chunk.dynamicImports.forEach((importName) => dynamicImports.add(importName), @@ -928,7 +929,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } // Now collect the dynamic chunks, this is done last to have the styles overwrite the previous ones for (const chunkName of dynamicImports) { - collect(bundle[chunkName]) + collect(bundle[chunkName]!) } // Finally, if there's any extracted CSS, we emit the asset @@ -959,7 +960,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // but they are still in `pureCssChunks`. // So we need to filter the names and only use those who are defined const pureCssChunkNames = [...pureCssChunks] - .map((pureCssChunk) => prelimaryNameToChunkMap[pureCssChunk.fileName]) + .map( + (pureCssChunk) => prelimaryNameToChunkMap[pureCssChunk.fileName]!, + ) .filter(Boolean) const replaceEmptyChunk = getEmptyChunkReplacer( @@ -968,7 +971,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { ) for (const file in bundle) { - const chunk = bundle[file] + const chunk = bundle[file]! if (chunk.type === 'chunk') { let chunkImportsPureCssChunk = false // remove pure css chunk from other chunk's imports, @@ -1455,7 +1458,7 @@ async function compileCSS( ignore: ['**/node_modules/**'], }) for (let i = 0; i < files.length; i++) { - deps.add(files[i]) + deps.add(files[i]!) } } else if (message.type === 'warning') { const warning = message as PostCSS.Warning @@ -1766,7 +1769,7 @@ function rewriteCssUrls( replacer: CssUrlReplacer, ): Promise { return asyncReplace(css, cssUrlRE, async (match) => { - const [matched, rawUrl] = match + const [matched, rawUrl] = match as StrictRegExpExecArrayFromLen<2> return await doUrlReplace(rawUrl.trim(), matched, replacer) }) } @@ -1776,7 +1779,7 @@ function rewriteCssDataUris( replacer: CssUrlReplacer, ): Promise { return asyncReplace(css, cssDataUriRE, async (match) => { - const [matched, rawUrl] = match + const [matched, rawUrl] = match as StrictRegExpExecArrayFromLen<2> return await doUrlReplace(rawUrl.trim(), matched, replacer, 'data-uri') }) } @@ -1786,7 +1789,7 @@ function rewriteImportCss( replacer: CssUrlReplacer, ): Promise { return asyncReplace(css, importCssRE, async (match) => { - const [matched, rawUrl] = match + const [matched, rawUrl] = match as StrictRegExpExecArrayFromLen<1> return await doImportCSSReplace(rawUrl, matched, replacer) }) } @@ -1801,7 +1804,7 @@ async function rewriteCssImageSet( replacer: CssUrlReplacer, ): Promise { return await asyncReplace(css, cssImageSetRE, async (match) => { - const [, rawUrl] = match + const [, rawUrl] = match as StrictRegExpExecArrayFromLen<1> const url = await processSrcSet(rawUrl, async ({ url }) => { // the url maybe url(...) if (cssUrlRE.test(url)) { @@ -3290,7 +3293,7 @@ export const convertTargets = ( const [major, minor = 0] = entry .slice(index) .split('.') - .map((v) => parseInt(v, 10)) + .map((v) => parseInt(v, 10)) as [number, ...number[]] if (!isNaN(major) && !isNaN(minor)) { const version = (major << 16) | (minor << 8) if (!targets[browser] || version < targets[browser]!) { diff --git a/packages/vite/src/node/plugins/dataUri.ts b/packages/vite/src/node/plugins/dataUri.ts index 9560700a5fd80e..21b39eda4570fc 100644 --- a/packages/vite/src/node/plugins/dataUri.ts +++ b/packages/vite/src/node/plugins/dataUri.ts @@ -3,6 +3,7 @@ // ref https://github.com/vitejs/vite/issues/1428#issuecomment-757033808 import { URL } from 'node:url' import type { Plugin } from '../plugin' +import type { StrictRegExpExecArray } from '../../shared/typeUtils' const dataUriRE = /^([^/]+\/[^;,]+)(;base64)?,([\s\S]*)$/ const base64RE = /base64/i @@ -36,7 +37,9 @@ export function dataURIPlugin(): Plugin { return } - const [, mime, format, data] = match + const [, mime, format, data] = match as StrictRegExpExecArray< + [true, boolean, true] + > if (mime !== 'text/javascript') { throw new Error( `data URI with non-JavaScript mime type is not supported. If you're using legacy JavaScript MIME types (such as 'application/javascript'), please use 'text/javascript' instead.`, diff --git a/packages/vite/src/node/plugins/define.ts b/packages/vite/src/node/plugins/define.ts index 36c9b57dbfeb6c..03129556f11a29 100644 --- a/packages/vite/src/node/plugins/define.ts +++ b/packages/vite/src/node/plugins/define.ts @@ -236,7 +236,7 @@ export function serializeDefine(define: Record): string { let res = `{` const keys = Object.keys(define).sort() for (let i = 0; i < keys.length; i++) { - const key = keys[i] + const key = keys[i]! const val = define[key] res += `${JSON.stringify(key)}: ${handleDefineValue(val)}` if (i !== keys.length - 1) { diff --git a/packages/vite/src/node/plugins/dynamicImportVars.ts b/packages/vite/src/node/plugins/dynamicImportVars.ts index 589b67a0aee864..cc36384d468bfb 100644 --- a/packages/vite/src/node/plugins/dynamicImportVars.ts +++ b/packages/vite/src/node/plugins/dynamicImportVars.ts @@ -81,8 +81,11 @@ function parseDynamicImportPattern( // ? is escaped on posix OS requestQueryMaybeEscapedSplitRE, 2, - ) - let [rawPattern, search] = filename.split(requestQuerySplitRE, 2) + ) as [string, string | undefined] + let [rawPattern, search] = filename.split(requestQuerySplitRE, 2) as [ + string, + string | undefined, + ] let globParams: DynamicImportRequest | null = null if (search) { search = '?' + search @@ -226,7 +229,7 @@ export function dynamicImportVarsPlugin(config: ResolvedConfig): Plugin { ss: expStart, se: expEnd, d: dynamicIndex, - } = imports[index] + } = imports[index]! if (dynamicIndex === -1 || source[start] !== '`') { continue diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 8e34cdb15ac93b..c893bb7495a3cb 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -34,6 +34,10 @@ import type { Logger } from '../logger' import { cleanUrl } from '../../shared/utils' import { perEnvironmentState } from '../environment' import { getNodeAssetAttributes } from '../assetSource' +import type { + StrictRegExpExecArrayFromLen, + StrictRegExpIndicesArrayFromLen, +} from '../../shared/typeUtils' import { assetUrlRE, getPublicAssetFilename, @@ -926,7 +930,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { inlineCSSRE.lastIndex = 0 while ((match = inlineCSSRE.exec(result))) { s ||= new MagicString(result) - const { 0: full, 1: scopedName } = match + const [full, scopedName] = match as StrictRegExpExecArrayFromLen<1> const cssTransformedCode = htmlProxyResult.get(scopedName)! s.update(match.index, match.index + full.length, cssTransformedCode) } @@ -1018,7 +1022,8 @@ export function extractImportExpressionFromClassicScript( let match: RegExpExecArray | null inlineImportRE.lastIndex = 0 while ((match = inlineImportRE.exec(cleanCode))) { - const [, [urlStart, urlEnd]] = match.indices! + const [, [urlStart, urlEnd]] = + match.indices as StrictRegExpIndicesArrayFromLen<1> const start = urlStart + 1 const end = urlEnd - 1 scriptUrls.push({ diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index f13d920fb83647..2e664b77c13a10 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -66,6 +66,7 @@ import { wrapId, } from '../../shared/utils' import type { TransformPluginContext } from '../server/pluginContainer' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' import { throwOutdatedRequest } from './optimizedDeps' import { isCSSRequest, isDirectCSSRequest } from './css' import { browserExternalId } from './resolve' @@ -171,8 +172,8 @@ function extractImportedBindings( code: match[0], start: match.index, end: match.index + match[0].length, - imports: match.groups!.imports, - specifier: match.groups!.specifier, + imports: match.groups!.imports ?? '', + specifier: match.groups!.specifier!, } const parsed = parseStaticImport(staticImport) if (!parsed) { @@ -393,7 +394,9 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { // (e.g. vue blocks), inherit importer's version query // do not do this for unknown type imports, otherwise the appended // query can break 3rd party plugin's extension checks. - const versionMatch = DEP_VERSION_RE.exec(importer) + const versionMatch = DEP_VERSION_RE.exec( + importer, + ) as StrictRegExpExecArrayFromLen<1> | null if (versionMatch) { url = injectQuery(url, versionMatch[1]) } @@ -948,7 +951,7 @@ export function transformCjsImport( importer: string, config: ResolvedConfig, ): string | undefined { - const node = parseAst(importExp).body[0] + const node = parseAst(importExp).body[0]! // `export * from '...'` may cause unexpected problem, so give it a warning if ( diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index d883e0c4cd222e..030a0cc71d3ba2 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -19,6 +19,7 @@ import type { ResolvedConfig } from '../config' import { toOutputFilePathInJS } from '../build' import { genSourceMapUrl } from '../server/sourcemap' import type { Environment } from '../environment' +import type { StrictRegExpExecArray } from '../../shared/typeUtils' import { removedPureCssFilesCache } from './css' import { createParseErrorInfo } from './importAnalysis' @@ -47,6 +48,12 @@ const dynamicImportPrefixRE = /import\s*\(/ const dynamicImportTreeshakenRE = /((?:\bconst\s+|\blet\s+|\bvar\s+|,\s*)(\{[^{}.=]+\})\s*=\s*await\s+import\([^)]+\))|(\(\s*await\s+import\([^)]+\)\s*\)(\??\.[\w$]+))|\bimport\([^)]+\)(\s*\.then\(\s*(?:function\s*)?\(\s*\{([^{}.=]+)\}\))/g +type DynamicImportTreeshakenMatch = StrictRegExpExecArray< + | [true, true, false, false, false, false] // const { foo } = await import() + | [false, false, true, true, false, false] // (await import()).foo + | [false, false, false, false, true, true] // import().then(({ foo }) => {}) +> + function toRelativePath(filename: string, importer: string) { const relPath = path.posix.relative(path.posix.dirname(importer), filename) return relPath[0] === '.' ? relPath : `./${relPath}` @@ -110,7 +117,7 @@ function preload( // When isBaseRelative is true then we have `importerUrl` and `dep` is // already converted to an absolute URL by the `assetsURL` function for (let i = links.length - 1; i >= 0; i--) { - const link = links[i] + const link = links[i]! // The `links[i].href` is an absolute URL thanks to browser doing the work // for us. See https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:idl-domstring-5 if (link.href === dep && (!isCss || link.rel === 'stylesheet')) { @@ -250,7 +257,11 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { if (insertPreload) { let match - while ((match = dynamicImportTreeshakenRE.exec(source))) { + while ( + (match = dynamicImportTreeshakenRE.exec( + source, + ) as DynamicImportTreeshakenMatch | null) + ) { /* handle `const {foo} = await import('foo')` * * match[1]: `const {foo} = await import('foo')` @@ -261,7 +272,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { if (match[1]) { dynamicImports[dynamicImportTreeshakenRE.lastIndex] = { declaration: `const ${match[2]}`, - names: match[2]?.trim(), + names: match[2].trim(), } continue } @@ -280,7 +291,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { names = 'default: __vite_default__' } dynamicImports[ - dynamicImportTreeshakenRE.lastIndex - match[4]?.length - 1 + dynamicImportTreeshakenRE.lastIndex - match[4].length - 1 ] = { declaration: `const {${names}}`, names: `{ ${names} }` } continue } @@ -292,9 +303,9 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { * import end: `import('foo').` * ^ */ - const names = match[6]?.trim() + const names = match[6]!.trim() dynamicImports[ - dynamicImportTreeshakenRE.lastIndex - match[5]?.length + dynamicImportTreeshakenRE.lastIndex - match[5]!.length ] = { declaration: `const {${names}}`, names: `{ ${names} }` } } } @@ -311,7 +322,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { se: expEnd, d: dynamicIndex, a: attributeIndex, - } = imports[index] + } = imports[index]! const isDynamicImport = dynamicIndex > -1 @@ -409,7 +420,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { const removedPureCssFiles = removedPureCssFilesCache.get(config) if (removedPureCssFiles && removedPureCssFiles.size > 0) { for (const file in bundle) { - const chunk = bundle[file] + const chunk = bundle[file]! if (chunk.type === 'chunk' && chunk.code.includes('import')) { const code = chunk.code let imports!: ImportSpecifier[] @@ -465,7 +476,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { const { modulePreload } = this.environment.config.build for (const file in bundle) { - const chunk = bundle[file] + const chunk = bundle[file]! // can't use chunk.dynamicImports.length here since some modules e.g. // dynamic import to constant json may get inlined. if (chunk.type === 'chunk' && chunk.code.indexOf(preloadMarker) > -1) { @@ -511,7 +522,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { e: end, ss: expStart, se: expEnd, - } = imports[index] + } = imports[index]! // check the chunk being imported let url = name if (!url) { diff --git a/packages/vite/src/node/plugins/importMetaGlob.ts b/packages/vite/src/node/plugins/importMetaGlob.ts index 43534d7d82bca5..92daf00f1f9d64 100644 --- a/packages/vite/src/node/plugins/importMetaGlob.ts +++ b/packages/vite/src/node/plugins/importMetaGlob.ts @@ -247,7 +247,7 @@ export async function parseImportGlob( const statementCode = code.slice(start, end) - const rootAst = (await parseAstAsync(statementCode)).body[0] + const rootAst = (await parseAstAsync(statementCode)).body[0]! if (rootAst.type !== 'ExpressionStatement') { throw err(`Expect CallExpression, got ${rootAst.type}`) } @@ -277,7 +277,7 @@ export async function parseImportGlob( `Expected glob to be a string, but got dynamic template literal`, ) } - globs.push(element.quasis[0].value.raw) + globs.push(element.quasis[0]!.value.raw) } else { throw err('Could only use literals') } @@ -309,7 +309,9 @@ export async function parseImportGlob( const globsResolved = await Promise.all( globs.map((glob) => toAbsoluteGlob(glob, root, importer, resolveId)), ) - const isRelative = globs.every((i) => '.!'.includes(i[0])) + const isRelative = globs.every((i) => + '.!'.includes(/* glob is not empty */ i[0]!), + ) const sliceCode = cleanCode.slice(0, start) const onlyKeys = objectKeysRE.test(sliceCode) let onlyValues = false @@ -623,7 +625,7 @@ export function getCommonBase(globsResolved: string[]): null | string { if (!bases.length) return null let commonAncestor = '' - const dirS = bases[0].split('/') + const dirS = bases[0]!.split('/') for (let i = 0; i < dirS.length; i++) { const candidate = dirS.slice(0, i + 1).join('/') if (bases.every((base) => base.startsWith(candidate))) diff --git a/packages/vite/src/node/plugins/json.ts b/packages/vite/src/node/plugins/json.ts index d531ee80efac7f..4d5af46324b575 100644 --- a/packages/vite/src/node/plugins/json.ts +++ b/packages/vite/src/node/plugins/json.ts @@ -10,6 +10,7 @@ import { dataToEsm, makeLegalIdentifier } from '@rollup/pluginutils' import { SPECIAL_QUERY_RE } from '../constants' import type { Plugin } from '../plugin' import { stripBomTag } from '../utils' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' export interface JsonOptions { /** @@ -133,7 +134,9 @@ export function extractJsonErrorPosition( return inputLength - 1 } - const errorMessageList = /at position (\d+)/.exec(errorMessage) + const errorMessageList = /at position (\d+)/.exec( + errorMessage, + ) as StrictRegExpExecArrayFromLen<1> | null return errorMessageList ? Math.max(parseInt(errorMessageList[1], 10) - 1, 0) : undefined diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index 0263902d4c2836..a9d8665b789205 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -145,14 +145,14 @@ export function manifestPlugin(): Plugin { const fileNameToAsset = new Map() for (const file in bundle) { - const chunk = bundle[file] + const chunk = bundle[file]! if (chunk.type === 'chunk') { manifest[getChunkName(chunk)] = createChunk(chunk) } else if (chunk.type === 'asset' && chunk.names.length > 0) { // Add every unique asset to the manifest, keyed by its original name const src = chunk.originalFileNames.length > 0 - ? chunk.originalFileNames[0] + ? chunk.originalFileNames[0]! : '_' + path.basename(chunk.fileName) const isEntry = entryCssAssetFileNames.has(chunk.fileName) const asset = createAsset(chunk, src, isEntry) diff --git a/packages/vite/src/node/plugins/optimizedDeps.ts b/packages/vite/src/node/plugins/optimizedDeps.ts index 544dcc329291d1..0583b878307db2 100644 --- a/packages/vite/src/node/plugins/optimizedDeps.ts +++ b/packages/vite/src/node/plugins/optimizedDeps.ts @@ -11,6 +11,7 @@ import { createDebugger } from '../utils' import { optimizedDepInfoFromFile } from '../optimizer' import { cleanUrl } from '../../shared/utils' import { ERR_OUTDATED_OPTIMIZED_DEP } from '../../shared/constants' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' const debug = createDebugger('vite:optimize-deps') @@ -35,7 +36,9 @@ export function optimizedDepsPlugin(): Plugin { if (depsOptimizer?.isOptimizedDepFile(id)) { const metadata = depsOptimizer.metadata const file = cleanUrl(id) - const versionMatch = DEP_VERSION_RE.exec(file) + const versionMatch = DEP_VERSION_RE.exec( + file, + ) as StrictRegExpExecArrayFromLen<1> | null const browserHash = versionMatch ? versionMatch[1].split('=')[1] : undefined diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index c7a113a2b31727..324cbd0e7465dc 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -51,6 +51,7 @@ import { splitFileAndPostfix, withTrailingSlash, } from '../../shared/utils' +import type { StrictRegExpExecArray } from '../../shared/typeUtils' const normalizedClientEntry = normalizePath(CLIENT_ENTRY) const normalizedEnvEntry = normalizePath(ENV_ENTRY) @@ -693,10 +694,12 @@ export function tryNodeResolve( const { root, dedupe, isBuild, preserveSymlinks, packageCache } = options // check for deep import, e.g. "my-lib/foo" - const deepMatch = deepImportRE.exec(id) + const deepMatch = deepImportRE.exec(id) as StrictRegExpExecArray< + [true, false] | [false, true] + > | null // package name doesn't include postfixes // trim them to support importing package with queries (e.g. `import css from 'normalize.css?inline'`) - const pkgId = deepMatch ? deepMatch[1] || deepMatch[2] : cleanUrl(id) + const pkgId = deepMatch ? (deepMatch[1] || deepMatch[2])! : cleanUrl(id) let basedir: string if (dedupe?.includes(pkgId)) { diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index b7eea41aac0a6a..35bcaf011adb28 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -19,6 +19,7 @@ import { toOutputFilePathInJS, } from '../build' import { cleanUrl } from '../../shared/utils' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' import { fileToUrl } from './asset' type WorkerBundleAsset = { @@ -429,7 +430,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { const { fileNameHash } = workerMap while ((match = workerAssetUrlRE.exec(code))) { - const [full, hash] = match + const [full, hash] = match as StrictRegExpExecArrayFromLen<1> const filename = fileNameHash.get(hash)! const replacement = toOutputFilePathInJS( this.environment, diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 8d8d316a4ec214..30a398b3750036 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -8,6 +8,7 @@ import { evalValue, injectQuery, transformStableResult } from '../utils' import { createBackCompatIdResolver } from '../idResolver' import type { ResolveIdFn } from '../idResolver' import { cleanUrl, slash } from '../../shared/utils' +import type { StrictRegExpIndicesArrayFromLen } from '../../shared/typeUtils' import type { WorkerType } from './worker' import { WORKER_FILE_ID, workerFileToUrl } from './worker' import { fileToUrl } from './asset' @@ -139,7 +140,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { let match: RegExpExecArray | null while ((match = workerImportMetaUrlRE.exec(cleanString))) { const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] = - match.indices! + match.indices! as StrictRegExpIndicesArrayFromLen<2> const rawUrl = code.slice(urlStart, urlEnd) diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts index 2ff3984515a292..f788e3cd640177 100644 --- a/packages/vite/src/node/preview.ts +++ b/packages/vite/src/node/preview.ts @@ -117,7 +117,7 @@ export async function preview( ) const clientOutDir = - config.environments.client.build.outDir ?? config.build.outDir + config.environments.client!.build.outDir ?? config.build.outDir const distDir = path.resolve(config.root, clientOutDir) if ( !fs.existsSync(distDir) && diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 772b163a745eb7..097d0d6d2159e2 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -500,8 +500,8 @@ export async function _createServer( // Backward compatibility let moduleGraph = new ModuleGraph({ - client: () => environments.client.moduleGraph, - ssr: () => environments.ssr.moduleGraph, + client: () => environments.client!.moduleGraph, + ssr: () => environments.ssr!.moduleGraph, }) const pluginContainer = createPluginContainer(environments) @@ -708,7 +708,7 @@ export async function _createServer( }, waitForRequestsIdle(ignoredId?: string): Promise { - return environments.client.waitForRequestsIdle(ignoredId) + return environments.client!.waitForRequestsIdle(ignoredId) }, _setInternalServer(_server: ViteDevServer) { @@ -910,7 +910,7 @@ export async function _createServer( // For backward compatibility, we call buildStart for the client // environment when initing the server. For other environments // buildStart will be called when the first request is transformed - await environments.client.pluginContainer.buildStart() + await environments.client!.pluginContainer.buildStart() // ensure ws server started if (onListen || options.listen) { diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index ae432900d46756..a9a61ff0b942ce 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -114,7 +114,7 @@ function shouldPreTransform(url: string, config: ResolvedConfig) { const wordCharRE = /\w/ function isBareRelative(url: string) { - return wordCharRE.test(url[0]) && !url.includes(':') + return url[0] && wordCharRE.test(url[0]) && !url.includes(':') } const processNodeUrl = ( diff --git a/packages/vite/src/node/server/middlewares/proxy.ts b/packages/vite/src/node/server/middlewares/proxy.ts index 5f7c5b04c975fa..a0588171ceb770 100644 --- a/packages/vite/src/node/server/middlewares/proxy.ts +++ b/packages/vite/src/node/server/middlewares/proxy.ts @@ -162,7 +162,7 @@ export function proxyMiddleware( const url = req.url! for (const context in proxies) { if (doesProxyContextMatchUrl(context, url)) { - const [proxy, opts] = proxies[context] + const [proxy, opts] = proxies[context]! if ( opts.ws || opts.target?.toString().startsWith('ws:') || @@ -198,7 +198,7 @@ export function proxyMiddleware( const url = req.url! for (const context in proxies) { if (doesProxyContextMatchUrl(context, url)) { - const [proxy, opts] = proxies[context] + const [proxy, opts] = proxies[context]! const options: HttpProxy.ServerOptions = {} if (opts.bypass) { diff --git a/packages/vite/src/node/ssr/ssrManifestPlugin.ts b/packages/vite/src/node/ssr/ssrManifestPlugin.ts index 56f87400c360c4..2868f624a426d8 100644 --- a/packages/vite/src/node/ssr/ssrManifestPlugin.ts +++ b/packages/vite/src/node/ssr/ssrManifestPlugin.ts @@ -34,7 +34,7 @@ export function ssrManifestPlugin(): Plugin { const ssrManifest = getSsrManifest(this) const { base } = config for (const file in bundle) { - const chunk = bundle[file] + const chunk = bundle[file]! if (chunk.type === 'chunk') { for (const id in chunk.modules) { const normalizedId = normalizePath(relative(config.root, id)) @@ -73,7 +73,7 @@ export function ssrManifestPlugin(): Plugin { } if (imports.length) { for (let index = 0; index < imports.length; index++) { - const { s: start, e: end, n: name } = imports[index] + const { s: start, e: end, n: name } = imports[index]! // check the chunk being imported const url = code.slice(start, end) const deps: string[] = [] diff --git a/packages/vite/src/node/ssr/ssrStacktrace.ts b/packages/vite/src/node/ssr/ssrStacktrace.ts index 18489224ea4af6..1b188d8b43a8a5 100644 --- a/packages/vite/src/node/ssr/ssrStacktrace.ts +++ b/packages/vite/src/node/ssr/ssrStacktrace.ts @@ -1,6 +1,7 @@ import path from 'node:path' import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping' import type { EnvironmentModuleGraph } from '..' +import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' let offset: number @@ -15,7 +16,9 @@ function calculateOffsetOnce() { // in Node 12, stack traces account for the function wrapper. // in Node 13 and later, the function wrapper adds two lines, // which must be subtracted to generate a valid mapping - const match = /:(\d+):\d+\)$/.exec(e.stack.split('\n')[1]) + const match = /:(\d+):\d+\)$/.exec( + e.stack.split('\n')[1], + ) as StrictRegExpExecArrayFromLen<1> | null offset = match ? +match[1] - 1 : 0 } } diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 3a8aa9a33eeda8..bc15aaf8f3979c 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -335,7 +335,7 @@ async function ssrTransformScript( onStatements(statements) { // ensure ";" between statements for (let i = 0; i < statements.length - 1; i++) { - const stmt = statements[i] + const stmt = statements[i]! if ( code[stmt.end - 1] !== ';' && stmt.type !== 'FunctionDeclaration' && @@ -371,7 +371,7 @@ async function ssrTransformScript( if (!declaredConst.has(id.name)) { declaredConst.add(id.name) // locate the top-most node containing the class declaration - const topNode = parentStack[parentStack.length - 2] + const topNode = parentStack[parentStack.length - 2]! s.prependRight(topNode.start, `const ${id.name} = ${binding};\n`) } } else if (parent.type === 'CallExpression') { @@ -635,7 +635,7 @@ function walk( // emit the identifier events in BFS so the hoisted declarations // can be captured correctly identifiers.forEach(([node, stack]) => { - if (!isInScope(node.name, stack)) onIdentifier(node, stack[0], stack) + if (!isInScope(node.name, stack)) onIdentifier(node, stack[0]!, stack) }) } diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 794a333276782f..957ec1de18e8e2 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -28,6 +28,7 @@ import { withTrailingSlash, } from '../shared/utils' import { VALID_ID_PREFIX } from '../shared/constants' +import type { StrictRegExpExecArray } from '../shared/typeUtils' import { CLIENT_ENTRY, CLIENT_PUBLIC_PATH, @@ -459,7 +460,7 @@ export function posToNumber(source: string, pos: number | Pos): number { const { line, column } = pos let start = 0 for (let i = 0; i < line - 1 && i < lines.length; i++) { - start += lines[i].length + 1 + start += lines[i]!.length + 1 } return start + column } @@ -476,7 +477,7 @@ export function numberToPos(source: string, offset: number | Pos): Pos { let line = 0 let column = 0 for (; line < lines.length; line++) { - const lineLength = lines[line].length + 1 + const lineLength = lines[line]!.length + 1 if (counted + lineLength >= offset) { column = offset - counted + 1 break @@ -500,7 +501,7 @@ export function generateCodeFrame( let count = 0 const res: string[] = [] for (let i = 0; i < lines.length; i++) { - count += lines[i].length + count += lines[i]!.length if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue @@ -510,7 +511,7 @@ export function generateCodeFrame( lines[j] }`, ) - const lineLength = lines[j].length + const lineLength = lines[j]!.length if (j === i) { // push underline const pad = Math.max(start - (count - lineLength), 0) @@ -564,7 +565,9 @@ export function emptyDir(dir: string, skip?: string[]): void { const matched = splitFirstDirRE.exec(file) if (matched) { nested ??= new Map() - const [, nestedDir, skipPath] = matched + const [, nestedDir, skipPath] = matched as StrictRegExpExecArray< + [true, true] + > let nestedSkip = nested.get(nestedDir) if (!nestedSkip) { nestedSkip = [] @@ -673,7 +676,7 @@ function windowsSafeRealPathSync(path: string): string { function optimizeSafeRealPathSync() { // Skip if using Node <18.10 due to MAX_PATH issue: https://github.com/vitejs/vite/issues/12931 const nodeVersion = process.versions.node.split('.').map(Number) - if (nodeVersion[0] < 18 || (nodeVersion[0] === 18 && nodeVersion[1] < 10)) { + if (nodeVersion[0]! < 18 || (nodeVersion[0] === 18 && nodeVersion[1]! < 10)) { safeRealpathSync = fs.realpathSync return } @@ -853,7 +856,7 @@ export function combineSourcemaps( if (useArrayInterface) { map = remapping(sourcemapList, () => null) } else { - map = remapping(sourcemapList[0], function loader(sourcefile) { + map = remapping(sourcemapList[0]!, function loader(sourcefile) { const mapForSources = sourcemapList .slice(mapIndex) .find((s) => s.sources.includes(sourcefile)) @@ -1405,11 +1408,11 @@ export function evalValue(rawValue: string): T { export function getNpmPackageName(importPath: string): string | null { const parts = importPath.split('/') - if (parts[0][0] === '@') { + if (parts[0]![0] === '@') { if (!parts[1]) return null return `${parts[0]}/${parts[1]}` } else { - return parts[0] + return parts[0]! } } @@ -1427,7 +1430,7 @@ export function getPackageManagerCommand( type: CommandType = 'install', ): string { const packageManager = - process.env.npm_config_user_agent?.split(' ')[0].split('/')[0] || 'npm' + process.env.npm_config_user_agent?.split(' ')[0]!.split('/')[0] || 'npm' switch (type) { case 'install': return packageManager === 'npm' ? 'npm install' : `${packageManager} add` diff --git a/packages/vite/src/shared/__tests_dts__/typeUtils.ts b/packages/vite/src/shared/__tests_dts__/typeUtils.ts new file mode 100644 index 00000000000000..5171b4e780ba99 --- /dev/null +++ b/packages/vite/src/shared/__tests_dts__/typeUtils.ts @@ -0,0 +1,70 @@ +import type { Equal, ExpectTrue } from '@type-challenges/utils' +import type { + BooleansToValues, + StrictRegExpExecArray, + StrictRegExpExecArrayFromLen, + StrictRegExpIndicesArray, + StrictRegExpIndicesArrayFromLen, +} from '../typeUtils' + +export type booleansToValuesCases = [ + ExpectTrue, [string]>>, + ExpectTrue, [undefined]>>, + ExpectTrue, [string | undefined]>>, +] + +export type strictRegExpExecArrayCases = [ + ExpectTrue< + Equal< + StrictRegExpExecArray<[true, false, boolean]>, + RegExpExecArray & [string, string, undefined, string | undefined] + > + >, + ExpectTrue< + Equal< + StrictRegExpExecArray<[true, false] | [false, true]>, + RegExpExecArray & + ([string, string, undefined] | [string, undefined, string]) + > + >, +] + +export type strictRegExpExecArrayFromLenCases = [ + ExpectTrue< + Equal, RegExpExecArray & [string, string]> + >, +] + +export type strictRegExpIndicesArrayCases = [ + ExpectTrue< + Equal< + StrictRegExpIndicesArray<[true, false, boolean]>, + RegExpIndicesArray & + [ + [number, number], + [number, number], + undefined, + [number, number] | undefined, + ] + > + >, + ExpectTrue< + Equal< + StrictRegExpIndicesArray<[true, false] | [false, true]>, + RegExpIndicesArray & + ( + | [[number, number], [number, number], undefined] + | [[number, number], undefined, [number, number]] + ) + > + >, +] + +export type strictRegExpIndicesArrayFromLenCases = [ + ExpectTrue< + Equal< + StrictRegExpIndicesArrayFromLen<1>, + RegExpIndicesArray & [[number, number], [number, number]] + > + >, +] diff --git a/packages/vite/src/shared/tsconfig.json b/packages/vite/src/shared/tsconfig.json index 96451a95759b42..a7f7890f1d0e7b 100644 --- a/packages/vite/src/shared/tsconfig.json +++ b/packages/vite/src/shared/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.base.json", "include": ["./", "../dep-types", "../types"], - "exclude": ["**/__tests__", "**/__tests_dts__"], + "exclude": ["**/__tests__"], "compilerOptions": { "lib": ["ESNext", "DOM"], "stripInternal": true diff --git a/packages/vite/src/shared/typeUtils.ts b/packages/vite/src/shared/typeUtils.ts new file mode 100644 index 00000000000000..2a05788ce8edde --- /dev/null +++ b/packages/vite/src/shared/typeUtils.ts @@ -0,0 +1,26 @@ +type ConstructTuple< + L extends number, + V = unknown, + R extends V[] = [], +> = R['length'] extends L ? R : ConstructTuple + +export type BooleansToValues = { + [K in keyof Booleans]: true extends Booleans[K] + ? false extends Booleans[K] + ? Value | undefined + : Value + : undefined +} + +export type StrictRegExpExecArray = + RegExpExecArray & [string, ...BooleansToValues] + +export type StrictRegExpExecArrayFromLen = + StrictRegExpExecArray> + +export type StrictRegExpIndicesArray = + RegExpIndicesArray & + [[number, number], ...BooleansToValues] + +export type StrictRegExpIndicesArrayFromLen = + StrictRegExpIndicesArray> diff --git a/packages/vite/tsconfig.base.json b/packages/vite/tsconfig.base.json index 71dfbdf5495748..141cbf4373aa40 100644 --- a/packages/vite/tsconfig.base.json +++ b/packages/vite/tsconfig.base.json @@ -9,6 +9,7 @@ "noImplicitOverride": true, "noUnusedLocals": true, "esModuleInterop": true, - "useUnknownInCatchVariables": false + "useUnknownInCatchVariables": false, + "noUncheckedIndexedAccess": true } } From 26cc678ed1efd17853e1434ecb1ec46ab866cbf8 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:18:56 +0900 Subject: [PATCH 2/9] chore: disable it inside tests --- packages/vite/src/node/__tests__/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/__tests__/tsconfig.json b/packages/vite/src/node/__tests__/tsconfig.json index ebfc2d30cf7493..17bc88c0a1f0d3 100644 --- a/packages/vite/src/node/__tests__/tsconfig.json +++ b/packages/vite/src/node/__tests__/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "esModuleInterop": true, "declaration": false, - "resolveJsonModule": true + "resolveJsonModule": true, + "noUncheckedIndexedAccess": false } } From 16ecfc90a903ba8df23db18d99012083114aa5d3 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:31:10 +0900 Subject: [PATCH 3/9] refactor: enable no-unnecessary-condition --- eslint.config.js | 13 +++++ packages/create-vite/src/index.ts | 10 ++-- packages/plugin-legacy/src/index.ts | 2 +- packages/vite/src/client/client.ts | 1 + packages/vite/src/client/overlay.ts | 31 ++++++----- packages/vite/src/module-runner/hmrHandler.ts | 6 +-- packages/vite/src/module-runner/runner.ts | 1 + .../vite/src/module-runner/sourcemap/index.ts | 1 + .../module-runner/sourcemap/interceptor.ts | 4 +- packages/vite/src/node/build.ts | 18 +++---- packages/vite/src/node/config.ts | 52 ++++++++----------- packages/vite/src/node/external.ts | 2 +- packages/vite/src/node/idResolver.ts | 2 +- packages/vite/src/node/logger.ts | 2 +- .../src/node/optimizer/esbuildDepPlugin.ts | 2 +- packages/vite/src/node/optimizer/index.ts | 27 +++++----- packages/vite/src/node/optimizer/optimizer.ts | 3 +- packages/vite/src/node/optimizer/scan.ts | 4 +- packages/vite/src/node/plugin.ts | 2 +- packages/vite/src/node/plugins/asset.ts | 2 +- .../src/node/plugins/assetImportMetaUrl.ts | 2 +- packages/vite/src/node/plugins/css.ts | 51 +++++++++--------- packages/vite/src/node/plugins/esbuild.ts | 2 +- packages/vite/src/node/plugins/html.ts | 15 +++--- .../vite/src/node/plugins/importAnalysis.ts | 11 ++-- .../src/node/plugins/importAnalysisBuild.ts | 6 ++- .../vite/src/node/plugins/importMetaGlob.ts | 7 +-- packages/vite/src/node/plugins/json.ts | 2 +- packages/vite/src/node/plugins/manifest.ts | 4 +- packages/vite/src/node/plugins/preAlias.ts | 5 +- packages/vite/src/node/plugins/resolve.ts | 12 ++--- .../vite/src/node/plugins/splitVendorChunk.ts | 4 +- packages/vite/src/node/plugins/worker.ts | 6 +-- .../src/node/plugins/workerImportMetaUrl.ts | 8 +-- packages/vite/src/node/preview.ts | 7 ++- packages/vite/src/node/server/environment.ts | 4 +- packages/vite/src/node/server/hmr.ts | 14 +++-- packages/vite/src/node/server/index.ts | 5 +- .../src/node/server/middlewares/indexHtml.ts | 18 +++---- .../src/node/server/middlewares/transform.ts | 2 +- .../vite/src/node/server/mixedModuleGraph.ts | 2 +- .../vite/src/node/server/pluginContainer.ts | 30 +++++------ packages/vite/src/node/server/sourcemap.ts | 4 +- .../vite/src/node/server/transformRequest.ts | 19 ++++--- packages/vite/src/node/server/ws.ts | 12 +++-- packages/vite/src/node/ssr/ssrStacktrace.ts | 16 +++--- packages/vite/src/node/ssr/ssrTransform.ts | 19 +++---- packages/vite/src/node/utils.ts | 8 +-- packages/vite/src/shared/hmr.ts | 2 +- .../vite/src/shared/moduleRunnerTransport.ts | 1 + 50 files changed, 238 insertions(+), 245 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 968a7a89b5e1a4..3cc30c15828382 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -145,6 +145,16 @@ export default tseslint.config( '@typescript-eslint/consistent-type-definitions': 'off', '@typescript-eslint/prefer-for-of': 'off', '@typescript-eslint/prefer-function-type': 'off', + ...(shouldTypeCheck + ? { + '@typescript-eslint/no-unnecessary-condition': [ + 'error', + { + allowConstantLoopConditions: true, + }, + ], + } + : {}), 'import-x/no-nodejs-modules': [ 'error', @@ -346,5 +356,8 @@ export default tseslint.config( project: false, }, }, + rules: { + '@typescript-eslint/no-unnecessary-condition': 'off', + }, }, ) diff --git a/packages/create-vite/src/index.ts b/packages/create-vite/src/index.ts index 853f480fc4c1de..75748bfb1e7a53 100755 --- a/packages/create-vite/src/index.ts +++ b/packages/create-vite/src/index.ts @@ -289,9 +289,10 @@ const FRAMEWORKS: Framework[] = [ }, ] -const TEMPLATES = FRAMEWORKS.map( - (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name], -).reduce((a, b) => a.concat(b), []) +const TEMPLATES = FRAMEWORKS.map((f) => f.variants.map((v) => v.name)).reduce( + (a, b) => a.concat(b), + [], +) const renameFiles: Record = { _gitignore: '.gitignore', @@ -394,8 +395,7 @@ async function init() { }), }, { - type: (framework: Framework) => - framework && framework.variants ? 'select' : null, + type: 'select', name: 'variant', message: reset('Select a variant:'), choices: (framework: Framework) => diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index 12a0838c3b75fa..3fdba02efefafb 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -971,7 +971,7 @@ function wrapIIFEBabelPlugin(): BabelPlugin { } const hash = - // eslint-disable-next-line n/no-unsupported-features/node-builtins -- crypto.hash is supported in Node 21.7.0+, 20.12.0+ + // eslint-disable-next-line n/no-unsupported-features/node-builtins, @typescript-eslint/no-unnecessary-condition -- crypto.hash is supported in Node 21.7.0+, 20.12.0+ crypto.hash ?? (( algorithm: string, diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 0a02b76b670af7..5dee5b4c1a8838 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -312,6 +312,7 @@ const hasDocument = 'document' in globalThis function createErrorOverlay(err: ErrorPayload['err']) { clearErrorOverlay() const { customElements } = globalThis + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (customElements) { const ErrorOverlayConstructor = customElements.get(overlayId)! document.body.appendChild(new ErrorOverlayConstructor(err)) diff --git a/packages/vite/src/client/overlay.ts b/packages/vite/src/client/overlay.ts index 8bc3ed07edb84e..937be482db2303 100644 --- a/packages/vite/src/client/overlay.ts +++ b/packages/vite/src/client/overlay.ts @@ -267,23 +267,21 @@ export class ErrorOverlay extends HTMLElement { fileRE.lastIndex = 0 while ((match = fileRE.exec(text))) { const { 0: file, index } = match - if (index != null) { - const frag = text.slice(curIndex, index) - el.appendChild(document.createTextNode(frag)) - const link = document.createElement('a') - link.textContent = file - link.className = 'file-link' - link.onclick = () => { - fetch( - new URL( - `${base}__open-in-editor?file=${encodeURIComponent(file)}`, - import.meta.url, - ), - ) - } - el.appendChild(link) - curIndex += frag.length + file.length + const frag = text.slice(curIndex, index) + el.appendChild(document.createTextNode(frag)) + const link = document.createElement('a') + link.textContent = file + link.className = 'file-link' + link.onclick = () => { + fetch( + new URL( + `${base}__open-in-editor?file=${encodeURIComponent(file)}`, + import.meta.url, + ), + ) } + el.appendChild(link) + curIndex += frag.length + file.length } } } @@ -295,6 +293,7 @@ export class ErrorOverlay extends HTMLElement { export const overlayId = 'vite-error-overlay' const { customElements } = globalThis // Ensure `customElements` is defined before the next line. +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (customElements && !customElements.get(overlayId)) { customElements.define(overlayId, ErrorOverlay) } diff --git a/packages/vite/src/module-runner/hmrHandler.ts b/packages/vite/src/module-runner/hmrHandler.ts index 5844c0de844ef2..9615f4485eb57b 100644 --- a/packages/vite/src/module-runner/hmrHandler.ts +++ b/packages/vite/src/module-runner/hmrHandler.ts @@ -151,11 +151,11 @@ function getModulesEntrypoints( if (!module) { continue } - if (module.importers && !module.importers.size) { + if (!module.importers.size) { entrypoints.add(module.url) continue } - for (const importer of module.importers || []) { + for (const importer of module.importers) { getModulesEntrypoints(runner, [importer], visited, entrypoints) } } @@ -167,7 +167,7 @@ function findAllEntrypoints( entrypoints = new Set(), ): Set { for (const mod of runner.evaluatedModules.idToModuleMap.values()) { - if (mod.importers && !mod.importers.size) { + if (!mod.importers.size) { entrypoints.add(mod.url) } } diff --git a/packages/vite/src/module-runner/runner.ts b/packages/vite/src/module-runner/runner.ts index 48942fae5c9964..5d3082254dbbd6 100644 --- a/packages/vite/src/module-runner/runner.ts +++ b/packages/vite/src/module-runner/runner.ts @@ -347,6 +347,7 @@ export class ModuleRunner { const { code, file } = fetchResult + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- is this needed? if (code == null) { const importer = callstack[callstack.length - 2] throw new Error( diff --git a/packages/vite/src/module-runner/sourcemap/index.ts b/packages/vite/src/module-runner/sourcemap/index.ts index 0efc5ca2db97b5..5ff046e4b5f995 100644 --- a/packages/vite/src/module-runner/sourcemap/index.ts +++ b/packages/vite/src/module-runner/sourcemap/index.ts @@ -14,6 +14,7 @@ export function enableSourceMapSupport(runner: ModuleRunner): () => void { `Cannot use "sourcemapInterceptor: 'node'" because "process.setSourceMapsEnabled" function is not available. Please use Node >= 16.6.0.`, ) } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- process.sourceMapsEnabled is not available in Node < 20.7.0 const isEnabledAlready = process.sourceMapsEnabled ?? false process.setSourceMapsEnabled(true) return () => !isEnabledAlready && process.setSourceMapsEnabled(false) diff --git a/packages/vite/src/module-runner/sourcemap/interceptor.ts b/packages/vite/src/module-runner/sourcemap/interceptor.ts index 388a59b24e7a1b..aa76a20f28a032 100644 --- a/packages/vite/src/module-runner/sourcemap/interceptor.ts +++ b/packages/vite/src/module-runner/sourcemap/interceptor.ts @@ -212,7 +212,7 @@ function mapSourcePosition(position: OriginalMapping) { } // Resolve the source URL relative to the URL of the source map - if (sourceMap && sourceMap.map && sourceMap.url) { + if (sourceMap.map && sourceMap.url) { const originalPosition = getOriginalPosition(sourceMap.map, position) // Only return the original position if a matching line was found. If no @@ -351,7 +351,7 @@ function cloneCallSite(frame: CallSite) { } function wrapCallSite(frame: CallSite, state: State) { - // provides interface backward compatibility + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- provides interface backward compatibility if (state === undefined) state = { nextPosition: null, curPosition: null } if (frame.isNative()) { diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 68d2240b705009..fd6a7baf241164 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -393,7 +393,7 @@ export function resolveBuildEnvironmentOptions( // Backward compatibility isSsrTargetWebworkerEnvironment?: boolean, ): ResolvedBuildEnvironmentOptions { - const deprecatedPolyfillModulePreload = raw?.polyfillModulePreload + const deprecatedPolyfillModulePreload = raw.polyfillModulePreload const { polyfillModulePreload, ...rest } = raw raw = rest if (deprecatedPolyfillModulePreload !== undefined) { @@ -454,7 +454,6 @@ export function resolveBuildEnvironmentOptions( } if (isSsrTargetWebworkerEnvironment) { - resolved.rollupOptions ??= {} resolved.rollupOptions.output ??= {} const output = resolved.rollupOptions.output for (const out of arraify(output)) { @@ -558,7 +557,7 @@ async function buildEnvironment( const resolve = (p: string) => path.resolve(root, p) const input = libOptions - ? options.rollupOptions?.input || + ? options.rollupOptions.input || (typeof libOptions.entry === 'string' ? resolve(libOptions.entry) : Array.isArray(libOptions.entry) @@ -571,7 +570,7 @@ async function buildEnvironment( )) : typeof options.ssr === 'string' ? resolve(options.ssr) - : options.rollupOptions?.input || resolve('index.html') + : options.rollupOptions.input || resolve('index.html') if (ssr && typeof input === 'string' && input.endsWith('.html')) { throw new Error( @@ -611,7 +610,7 @@ async function buildEnvironment( output: options.rollupOptions.output, input, plugins, - external: options.rollupOptions?.external, + external: options.rollupOptions.external, onwarn(warning, warn) { onRollupWarning(warning, warn, environment) }, @@ -751,7 +750,7 @@ async function buildEnvironment( // resolve lib mode outputs const outputs = resolveBuildOutputs( - options.rollupOptions?.output, + options.rollupOptions.output, libOptions, logger, ) @@ -768,7 +767,7 @@ async function buildEnvironment( const resolvedOutDirs = getResolvedOutDirs( root, options.outDir, - options.rollupOptions?.output, + options.rollupOptions.output, ) const emptyOutDir = resolveEmptyOutDir( options.emptyOutDir, @@ -1066,7 +1065,7 @@ export function onRollupWarning( } clearLine() - const userOnWarn = environment.config.build.rollupOptions?.onwarn + const userOnWarn = environment.config.build.rollupOptions.onwarn if (userOnWarn) { userOnWarn(warning, viteWarn) } else { @@ -1234,6 +1233,7 @@ function injectEnvironmentInContext( context: Context, environment: BuildEnvironment, ) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- context.environment is undefined here context.environment ??= environment return context } @@ -1449,7 +1449,7 @@ export class BuildEnvironment extends BaseEnvironment { if (setup?.options) { options = mergeConfig( options, - setup?.options, + setup.options, ) as ResolvedEnvironmentOptions } super(name, config, options) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 319a91dbccdb22..d3c6d8b5775e3b 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -25,6 +25,7 @@ import { FS_PREFIX, } from './constants' import type { + FalsyPlugin, HookHandler, Plugin, PluginOption, @@ -1006,7 +1007,7 @@ export async function resolveConfig( mode = inlineConfig.mode || config.mode || mode configEnv.mode = mode - const filterPlugin = (p: Plugin) => { + const filterPlugin = (p: Plugin | FalsyPlugin): p is Plugin => { if (!p) { return false } else if (!p.apply) { @@ -1020,7 +1021,7 @@ export async function resolveConfig( // resolve plugins const rawPlugins = ( - (await asyncFlatten(config.plugins || [])) as Plugin[] + (await asyncFlatten(config.plugins || [])) as Array ).filter(filterPlugin) const [prePlugins, normalPlugins, postPlugins] = sortUserPlugins(rawPlugins) @@ -1072,12 +1073,12 @@ export async function resolveConfig( // Backward compatibility: server.warmup.clientFiles/ssrFiles -> environment.dev.warmup const warmupOptions = config.server?.warmup if (warmupOptions?.clientFiles) { - configEnvironmentsClient.dev.warmup = warmupOptions?.clientFiles + configEnvironmentsClient.dev.warmup = warmupOptions.clientFiles } if (warmupOptions?.ssrFiles) { configEnvironmentsSsr ??= {} configEnvironmentsSsr.dev ??= {} - configEnvironmentsSsr.dev.warmup = warmupOptions?.ssrFiles + configEnvironmentsSsr.dev.warmup = warmupOptions.ssrFiles } // Backward compatibility: merge ssr into environments.ssr.config as defaults @@ -1103,11 +1104,7 @@ export async function resolveConfig( } // The client and ssr environment configs can't be removed by the user in the config hook - if ( - !config.environments || - !config.environments.client || - (!config.environments.ssr && !isBuild) - ) { + if (!config.environments.client || (!config.environments.ssr && !isBuild)) { throw new Error( 'Required environments configuration were stripped out in the config hook', ) @@ -1240,7 +1237,7 @@ export async function resolveConfig( ? !isBuild || config.build?.ssr ? '/' : './' - : (resolveBaseUrl(config.base, isBuild, logger) ?? configDefaults.base) + : resolveBaseUrl(config.base, isBuild, logger) // resolve cache directory const pkgDir = findNearestPackageData(resolvedRoot, packageCache)?.dir @@ -1573,7 +1570,7 @@ assetFileNames isn't equal for every build.rollupOptions.output. A single patter * electron or expects to deploy */ export function resolveBaseUrl( - base: UserConfig['base'] = '/', + base: UserConfig['base'] = configDefaults.base, isBuild: boolean, logger: Logger, ): string { @@ -1840,7 +1837,7 @@ async function bundleConfigFile( const { text } = result.outputFiles[0]! return { code: text, - dependencies: result.metafile ? Object.keys(result.metafile.inputs) : [], + dependencies: Object.keys(result.metafile.inputs), } } @@ -1913,11 +1910,9 @@ async function runConfigHook( for (const p of getSortedPluginsByHook('config', plugins)) { const hook = p.config const handler = getHookHandler(hook) - if (handler) { - const res = await handler(conf, configEnv) - if (res && res !== conf) { - conf = mergeConfig(conf, res) - } + const res = await handler(conf, configEnv) + if (res && res !== conf) { + conf = mergeConfig(conf, res) } } @@ -1934,15 +1929,13 @@ async function runConfigEnvironmentHook( for (const p of getSortedPluginsByHook('configEnvironment', plugins)) { const hook = p.configEnvironment const handler = getHookHandler(hook) - if (handler) { - for (const name of environmentNames) { - const res = await handler(name, environments[name]!, { - ...configEnv, - isSsrTargetWebworker: isSsrTargetWebworkerSet && name === 'ssr', - }) - if (res) { - environments[name] = mergeConfig(environments[name]!, res) - } + for (const name of environmentNames) { + const res = await handler(name, environments[name]!, { + ...configEnv, + isSsrTargetWebworker: isSsrTargetWebworkerSet && name === 'ssr', + }) + if (res) { + environments[name] = mergeConfig(environments[name]!, res) } } } @@ -1956,7 +1949,7 @@ function optimizeDepsDisabledBackwardCompatibility( const optimizeDepsDisabled = optimizeDeps.disabled if (optimizeDepsDisabled !== undefined) { if (optimizeDepsDisabled === true || optimizeDepsDisabled === 'dev') { - const commonjsOptionsInclude = resolved.build?.commonjsOptions?.include + const commonjsOptionsInclude = resolved.build.commonjsOptions.include const commonjsPluginDisabled = Array.isArray(commonjsOptionsInclude) && commonjsOptionsInclude.length === 0 @@ -1976,10 +1969,7 @@ function optimizeDepsDisabledBackwardCompatibility( } `), ) - } else if ( - optimizeDepsDisabled === false || - optimizeDepsDisabled === 'build' - ) { + } else { resolved.logger.warn( colors.yellow(`(!) Experimental ${optimizeDepsPath}optimizeDeps.disabled and deps pre-bundling during build were removed in Vite 5.1. Setting it to ${optimizeDepsDisabled} now has no effect. diff --git a/packages/vite/src/node/external.ts b/packages/vite/src/node/external.ts index 58d30cfc096371..7b95d6aa92b959 100644 --- a/packages/vite/src/node/external.ts +++ b/packages/vite/src/node/external.ts @@ -61,7 +61,7 @@ export function createIsConfiguredAsExternal( !(Array.isArray(noExternal) && noExternal.length === 0) && createFilter(undefined, noExternal, { resolve: false }) - const targetConditions = resolve.externalConditions || [] + const targetConditions = resolve.externalConditions const resolveOptions: InternalResolveOptions = { ...resolve, diff --git a/packages/vite/src/node/idResolver.ts b/packages/vite/src/node/idResolver.ts index 24b93bca999468..78ffb2dd2eb29a 100644 --- a/packages/vite/src/node/idResolver.ts +++ b/packages/vite/src/node/idResolver.ts @@ -25,7 +25,7 @@ export function createBackCompatIdResolver( options?: Partial, ): ResolveIdFn { const compatResolve = config.createResolver(options) - let resolve: ResolveIdFn + let resolve: ResolveIdFn | undefined return async (environment, id, importer, aliasOnly) => { if (environment.name === 'client' || environment.name === 'ssr') { return compatResolve(id, importer, aliasOnly, environment.name === 'ssr') diff --git a/packages/vite/src/node/logger.ts b/packages/vite/src/node/logger.ts index 8bfa027c61fcd2..ad69cea273ba99 100644 --- a/packages/vite/src/node/logger.ts +++ b/packages/vite/src/node/logger.ts @@ -55,7 +55,7 @@ export interface LoggerOptions { // Only initialize the timeFormatter when the timestamp option is used, and // reuse it across all loggers -let timeFormatter: Intl.DateTimeFormat +let timeFormatter: Intl.DateTimeFormat | undefined function getTimeFormatter() { timeFormatter ??= new Intl.DateTimeFormat(undefined, { hour: 'numeric', diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 316a43ffb3eae8..c98245bc5f4ac6 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -57,7 +57,7 @@ export function esbuildDepPlugin( // remove optimizable extensions from `externalTypes` list const allExternalTypes = extensions - ? externalTypes.filter((type) => !extensions?.includes('.' + type)) + ? externalTypes.filter((type) => !extensions.includes('.' + type)) : externalTypes // use separate package cache for optimizer as it caches paths around node_modules diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 455c49e4f6a118..429c6b866df458 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -343,7 +343,7 @@ let firstLoadCachedDepOptimizationMetadata = true */ export async function loadCachedDepOptimizationMetadata( environment: Environment, - force = environment.config.optimizeDeps?.force ?? false, + force = environment.config.optimizeDeps.force ?? false, asCommand = false, ): Promise { const log = asCommand ? environment.logger.info : debug @@ -749,7 +749,7 @@ async function prepareEsbuildOptimizerRun( const { optimizeDeps } = environment.config const { plugins: pluginsFromConfig = [], ...esbuildOptions } = - optimizeDeps?.esbuildOptions ?? {} + optimizeDeps.esbuildOptions ?? {} await Promise.all( Object.entries(depsInfo).map(async ([id, info]) => { @@ -788,7 +788,7 @@ async function prepareEsbuildOptimizerRun( ? 'browser' : 'node') - const external = [...(optimizeDeps?.exclude ?? [])] + const external = [...(optimizeDeps.exclude ?? [])] const plugins = [...pluginsFromConfig] if (external.length) { @@ -835,7 +835,7 @@ export async function addManuallyIncludedOptimizeDeps( ): Promise { const { logger } = environment const { optimizeDeps } = environment.config - const optimizeDepsInclude = optimizeDeps?.include ?? [] + const optimizeDepsInclude = optimizeDeps.include ?? [] if (optimizeDepsInclude.length) { const unableToOptimize = (id: string, msg: string) => { if (optimizeDepsInclude.includes(id)) { @@ -1081,7 +1081,7 @@ export async function extractExportsData( const { optimizeDeps } = environment.config - const esbuildOptions = optimizeDeps?.esbuildOptions ?? {} + const esbuildOptions = optimizeDeps.esbuildOptions ?? {} if (optimizeDeps.extensions?.some((ext) => filePath.endsWith(ext))) { // For custom supported extensions, build the entry file to transform it into JS, // and then parse with es-module-lexer. Note that the `bundle` option is not `true`, @@ -1136,7 +1136,7 @@ function needsInterop( exportsData: ExportsData, output?: { exports: string[] }, ): boolean { - if (environment.config.optimizeDeps?.needsInterop?.includes(id)) { + if (environment.config.optimizeDeps.needsInterop?.includes(id)) { return true } const { hasModuleSyntax, exports } = exportsData @@ -1152,9 +1152,8 @@ function needsInterop( const generatedExports: string[] = output.exports if ( - !generatedExports || - (isSingleDefaultExport(generatedExports) && - !isSingleDefaultExport(exports)) + isSingleDefaultExport(generatedExports) && + !isSingleDefaultExport(exports) ) { return true } @@ -1215,15 +1214,15 @@ function getConfigHash(environment: Environment): string { assetsInclude: config.assetsInclude, plugins: config.plugins.map((p) => p.name), optimizeDeps: { - include: optimizeDeps?.include + include: optimizeDeps.include ? unique(optimizeDeps.include).sort() : undefined, - exclude: optimizeDeps?.exclude + exclude: optimizeDeps.exclude ? unique(optimizeDeps.exclude).sort() : undefined, esbuildOptions: { - ...optimizeDeps?.esbuildOptions, - plugins: optimizeDeps?.esbuildOptions?.plugins?.map((p) => p.name), + ...optimizeDeps.esbuildOptions, + plugins: optimizeDeps.esbuildOptions?.plugins?.map((p) => p.name), }, }, }, @@ -1390,6 +1389,6 @@ const safeRename = promisify(function gracefulRename( if (backoff < 100) backoff += 10 return } - if (cb) cb(er) + cb(er) }) }) diff --git a/packages/vite/src/node/optimizer/optimizer.ts b/packages/vite/src/node/optimizer/optimizer.ts index dba22cacbe8937..38a5e7acecf1fe 100644 --- a/packages/vite/src/node/optimizer/optimizer.ts +++ b/packages/vite/src/node/optimizer/optimizer.ts @@ -329,7 +329,8 @@ export function createDepsOptimizer( optimizationResult = undefined } - if (closed) { + // `closed` can be true because of the await optimizationResult.result above + if (closed as boolean) { currentlyProcessing = false processingResult.cancel() resolveEnqueuedProcessingPromises() diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index b64c56bd795c2f..5866db4aa19281 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -184,7 +184,7 @@ export function scanImports(environment: ScanEnvironment): { }) }) } - if (!context || scanContext?.cancelled) { + if (!context || scanContext.cancelled) { disposeContext() return { deps: {}, missing: {} } } @@ -249,7 +249,7 @@ async function computeEntries(environment: ScanEnvironment) { let entries: string[] = [] const explicitEntryPatterns = environment.config.optimizeDeps.entries - const buildInput = environment.config.build.rollupOptions?.input + const buildInput = environment.config.build.rollupOptions.input if (explicitEntryPatterns) { entries = await globEntries(explicitEntryPatterns, environment) diff --git a/packages/vite/src/node/plugin.ts b/packages/vite/src/node/plugin.ts index 1f76300037a2f9..033b907d98706b 100644 --- a/packages/vite/src/node/plugin.ts +++ b/packages/vite/src/node/plugin.ts @@ -331,7 +331,7 @@ export type PluginWithRequiredHook = Plugin & { type Thenable = T | Promise -type FalsyPlugin = false | null | undefined +export type FalsyPlugin = false | null | undefined export type PluginOption = Thenable diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 9e027aee2a4a79..859fc60a21f8b2 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -311,7 +311,7 @@ export async function fileToDevUrl( if (skipBase) { return rtn } - const base = joinUrlSegments(config.server?.origin ?? '', config.decodedBase) + const base = joinUrlSegments(config.server.origin ?? '', config.decodedBase) return joinUrlSegments(base, removeLeadingSlash(rtn)) } diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index a06015002dc1e9..3e629568578a9d 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -32,7 +32,7 @@ import { hasViteIgnoreRE } from './importAnalysis' */ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const { publicDir } = config - let assetResolver: ResolveIdFn + let assetResolver: ResolveIdFn | undefined const fsResolveOptions: InternalResolveOptions = { ...config.resolve, diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index efcaa84ee32495..50dcc5da2fbe91 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -303,7 +303,7 @@ export function cssPlugin(config: ResolvedConfig): Plugin { let preprocessorWorkerController: PreprocessorWorkerController | undefined // warm up cache for resolved postcss config - if (config.css?.transformer !== 'lightningcss') { + if (config.css.transformer !== 'lightningcss') { resolvePostcssConfig(config) } @@ -545,7 +545,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { if (config.command === 'serve') { const getContentWithSourcemap = async (content: string) => { - if (config.css?.devSourcemap) { + if (config.css.devSourcemap) { const sourcemap = this.getCombinedSourcemap() if (sourcemap.mappings) { await injectSourcesContent(sourcemap, cleanUrl(id), config.logger) @@ -907,7 +907,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { const dynamicImports = new Set() function collect(chunk: OutputChunk | OutputAsset) { - if (!chunk || chunk.type !== 'chunk' || collected.has(chunk)) return + if (chunk.type !== 'chunk' || collected.has(chunk)) return collected.add(chunk) // First collect all styles from the synchronous imports (lowest priority) @@ -993,7 +993,8 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } return true }) - if (chunkImportsPureCssChunk) { + // `chunkImportsPureCssChunk` might be set om the filter above + if (chunkImportsPureCssChunk as boolean) { chunk.code = replaceEmptyChunk(chunk.code) } } @@ -1184,7 +1185,7 @@ async function compileCSSPreprocessors( workerController: PreprocessorWorkerController, ): Promise<{ code: string; map?: ExistingRawSourceMap; deps?: Set }> { const { config } = environment - const { preprocessorOptions, devSourcemap } = config.css ?? {} + const { preprocessorOptions, devSourcemap } = config.css const atImportResolvers = getAtImportResolvers( environment.getTopLevelConfig(), ) @@ -1209,7 +1210,7 @@ async function compileCSSPreprocessors( } let deps: Set | undefined - if (preprocessResult.deps) { + if (preprocessResult.deps.length > 0) { const normalizedFilename = normalizePath(opts.filename) // sometimes sass registers the file itself as a dep deps = new Set( @@ -1257,11 +1258,11 @@ async function compileCSS( deps?: Set }> { const { config } = environment - if (config.css?.transformer === 'lightningcss') { + if (config.css.transformer === 'lightningcss') { return compileLightningCSS(id, code, environment, urlReplacer) } - const { modules: modulesOptions, devSourcemap } = config.css || {} + const { modules: modulesOptions, devSourcemap } = config.css const isModule = modulesOptions !== false && cssModuleRE.test(id) // although at serve time it can work without processing, we do need to // crawl them in order to register watch dependencies. @@ -1305,10 +1306,8 @@ async function compileCSS( const atImportResolvers = getAtImportResolvers( environment.getTopLevelConfig(), ) - const postcssOptions = (postcssConfig && postcssConfig.options) || {} - - const postcssPlugins = - postcssConfig && postcssConfig.plugins ? postcssConfig.plugins.slice() : [] + const postcssOptions = postcssConfig?.options ?? {} + const postcssPlugins = postcssConfig?.plugins.slice() ?? [] if (needInlineImport) { postcssPlugins.unshift( @@ -1664,7 +1663,7 @@ async function resolvePostcssConfig( } // inline postcss config via vite config - const inlineOptions = config.css?.postcss + const inlineOptions = config.css.postcss if (isObject(inlineOptions)) { const options = { ...inlineOptions } @@ -1890,7 +1889,7 @@ async function minifyCSS( if (config.build.cssMinify === 'lightningcss') { const { code, warnings } = (await importLightningCSS()).transform({ - ...config.css?.lightningcss, + ...config.css.lightningcss, targets: convertTargets(config.build.cssTarget), cssModules: undefined, // TODO: Pass actual filename here, which can also be passed to esbuild's @@ -2136,7 +2135,7 @@ function cleanScssBugUrl(url: string) { // check bug via `window` and `location` global typeof window !== 'undefined' && typeof location !== 'undefined' && - typeof location?.href === 'string' + typeof location.href === 'string' ) { const prefix = location.href.replace(/\/$/, '') return url.replace(prefix, '') @@ -2226,7 +2225,7 @@ const makeScssWorker = ( done, ) => { internalImporter(url, importer, options.filename).then((data) => - done?.(data), + done(data), ) } const importer = [_internalImporter] @@ -2698,13 +2697,10 @@ const makeLessWorker = ( '@', resolvers.less, ) - if (result) { - return { - resolved, - contents: 'contents' in result ? result.contents : undefined, - } + return { + resolved, + contents: 'contents' in result ? result.contents : undefined, } - return result } const worker = new WorkerWithFallback( @@ -2858,7 +2854,8 @@ const lessProcessor = ( return { code: '', error: normalizedError, deps: [] } } - const map: ExistingRawSourceMap = result.map && JSON.parse(result.map) + const map: ExistingRawSourceMap | undefined = + result.map && JSON.parse(result.map) if (map) { delete map.sourcesContent } @@ -3118,14 +3115,14 @@ async function compileLightningCSS( ? (await importLightningCSS()).transformStyleAttribute({ filename, code: Buffer.from(src), - targets: config.css?.lightningcss?.targets, + targets: config.css.lightningcss?.targets, minify: config.isProduction && !!config.build.cssMinify, analyzeDependencies: true, }) : await ( await importLightningCSS() ).bundleAsync({ - ...config.css?.lightningcss, + ...config.css.lightningcss, filename, resolver: { read(filePath) { @@ -3162,10 +3159,10 @@ async function compileLightningCSS( sourceMap: config.command === 'build' ? !!config.build.sourcemap - : config.css?.devSourcemap, + : config.css.devSourcemap, analyzeDependencies: true, cssModules: cssModuleRE.test(id) - ? (config.css?.lightningcss?.cssModules ?? true) + ? (config.css.lightningcss?.cssModules ?? true) : undefined, }) diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index 6d3ffc86f33b06..a495362b66b7eb 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -280,7 +280,7 @@ export function esbuildPlugin(config: ResolvedConfig): Plugin { transformOptions, undefined, config, - server?.watcher, + server.watcher, ) if (result.warnings.length) { result.warnings.forEach((m) => { diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index c893bb7495a3cb..f107ac662923e5 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -419,9 +419,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { // for each encountered asset url, rewrite original html so that it // references the post-build location, ignoring empty attributes and // attributes that directly reference named output. - const namedOutput = Object.keys( - config?.build?.rollupOptions?.input || {}, - ) + const namedOutput = Object.keys(config.build.rollupOptions.input || {}) const processAssetUrl = async (url: string, shouldInline?: boolean) => { if ( url !== '' && // Empty attribute @@ -551,7 +549,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { } })(), ) - } else if (attr.type === 'src') { + } else { const url = decodeURI(attr.value) if (checkPublicFile(url, config)) { overwriteAttrValue( @@ -654,7 +652,11 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { isAsyncScriptMap.get(config)!.set(id, everyScriptIsAsync) - if (someScriptsAreAsync && someScriptsAreDefer) { + // `someScriptsAreAsync` and `someScriptsAreDefer` can be true + if ( + (someScriptsAreAsync as boolean) && + (someScriptsAreDefer as boolean) + ) { config.logger.warn( `\nMixed async and defer script modules in ${id}, output script will fallback to defer. Every script, including inline ones, need to be marked as async for your output script to be async.`, ) @@ -705,7 +707,8 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { if ( modulePreload !== false && modulePreload.polyfill && - (someScriptsAreAsync || someScriptsAreDefer) + // `someScriptsAreAsync` and `someScriptsAreDefer` can be true + ((someScriptsAreAsync as boolean) || (someScriptsAreDefer as boolean)) ) { js = `import "${modulePreloadPolyfillId}";\n${js}` } diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 2e664b77c13a10..7d01ac65f32810 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -176,9 +176,6 @@ function extractImportedBindings( specifier: match.groups!.specifier!, } const parsed = parseStaticImport(staticImport) - if (!parsed) { - return - } if (parsed.namespacedImport) { bindings.add('*') } @@ -224,7 +221,7 @@ function extractImportedBindings( export function importAnalysisPlugin(config: ResolvedConfig): Plugin { const { root, base } = config const clientPublicPath = path.posix.join(base, CLIENT_PUBLIC_PATH) - const enablePartialAccept = config.experimental?.hmrPartialAccept + const enablePartialAccept = config.experimental.hmrPartialAccept const matchAlias = getAliasPatternMatcher(config.resolve.alias) let _env: string | undefined @@ -308,6 +305,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { return source } + /* eslint-disable @typescript-eslint/no-unnecessary-condition -- there's many false positives */ let hasHMR = false let isSelfAccepting = false let hasEnv = false @@ -355,7 +353,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { throw e }) - if (!resolved || resolved.meta?.['vite:alias']?.noResolved) { + if (!resolved || resolved.meta['vite:alias']?.noResolved) { // in ssr, we should let node handle the missing modules if (ssr) { return [url, url] @@ -817,6 +815,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { )}`, ) + /* eslint-enable @typescript-eslint/no-unnecessary-condition */ if (s) { return transformStableResult(s, importer, config) } else { @@ -989,7 +988,7 @@ export function transformCjsImport( }) } else if (spec.type === 'ImportNamespaceSpecifier') { importNames.push({ importedName: '*', localName: spec.local.name }) - } else if (spec.type === 'ExportSpecifier') { + } else { // for ExportSpecifier, local name is same as imported name // prefix the variable name to avoid clashing with other local variables const importedName = getIdentifierNameOrLiteralValue( diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index 030a0cc71d3ba2..e06f2f7748be36 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -77,6 +77,7 @@ function indexOfMatchInSlice( function detectScriptRel() { const relList = typeof document !== 'undefined' && document.createElement('link').relList + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `relList` can be not supported return relList && relList.supports && relList.supports('modulepreload') ? 'modulepreload' : 'preload' @@ -593,7 +594,8 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { let depsArray = deps.size > 1 || // main chunk is removed - (hasRemovedPureCssChunk && deps.size > 0) + // `hasRemovedPureCssChunk` can be true + ((hasRemovedPureCssChunk as boolean) && deps.size > 0) ? modulePreload === false ? // CSS deps use the same mechanism as module preloads, so even if disabled, // we still need to pass these deps to the preload helper in dynamic imports. @@ -716,7 +718,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { '', ) chunk.code += `\n//# sourceMappingURL=${genSourceMapUrl(map)}` - } else if (buildSourcemap) { + } else { const mapAsset = bundle[chunk.fileName + '.map'] if (mapAsset && mapAsset.type === 'asset') { mapAsset.source = map.toString() diff --git a/packages/vite/src/node/plugins/importMetaGlob.ts b/packages/vite/src/node/plugins/importMetaGlob.ts index 92daf00f1f9d64..16f31d2ec4eb08 100644 --- a/packages/vite/src/node/plugins/importMetaGlob.ts +++ b/packages/vite/src/node/plugins/importMetaGlob.ts @@ -133,9 +133,9 @@ function parseGlobOptions( optsStartIndex: number, logger?: Logger, ): ParsedGeneralImportGlobOptions { - let opts: GeneralImportGlobOptions = {} + let evaledOpts: unknown try { - opts = evalValue(rawOpts) + evaledOpts = evalValue(rawOpts) } catch { throw err( 'Vite is unable to parse the glob options as the value is not static', @@ -143,9 +143,10 @@ function parseGlobOptions( ) } - if (opts == null) { + if (evaledOpts == null) { return {} } + const opts = evaledOpts as GeneralImportGlobOptions for (const key in opts) { if (!(key in knownOptions)) { diff --git a/packages/vite/src/node/plugins/json.ts b/packages/vite/src/node/plugins/json.ts index 4d5af46324b575..c383827324a551 100644 --- a/packages/vite/src/node/plugins/json.ts +++ b/packages/vite/src/node/plugins/json.ts @@ -79,7 +79,7 @@ export function jsonPlugin( options.stringify === true || // use 10kB as a threshold // https://v8.dev/blog/cost-of-javascript-2019#:~:text=A%20good%20rule%20of%20thumb%20is%20to%20apply%20this%20technique%20for%20objects%20of%2010%20kB%20or%20larger - (options.stringify === 'auto' && json.length > 10 * 1000) + json.length > 10 * 1000 ) { // during build, parse then double-stringify to remove all // unnecessary whitespaces to reduce bundle size. diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index a9d8665b789205..c0ee8a8bb7270a 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -148,7 +148,7 @@ export function manifestPlugin(): Plugin { const chunk = bundle[file]! if (chunk.type === 'chunk') { manifest[getChunkName(chunk)] = createChunk(chunk) - } else if (chunk.type === 'asset' && chunk.names.length > 0) { + } else if (chunk.names.length > 0) { // Add every unique asset to the manifest, keyed by its original name const src = chunk.originalFileNames.length > 0 @@ -175,7 +175,7 @@ export function manifestPlugin(): Plugin { } state.outputCount++ - const output = buildOptions.rollupOptions?.output + const output = buildOptions.rollupOptions.output const outputLength = Array.isArray(output) ? output.length : 1 if (state.outputCount >= outputLength) { this.emitFile({ diff --git a/packages/vite/src/node/plugins/preAlias.ts b/packages/vite/src/node/plugins/preAlias.ts index 7422544bb98d79..b75f59542902d2 100644 --- a/packages/vite/src/node/plugins/preAlias.ts +++ b/packages/vite/src/node/plugins/preAlias.ts @@ -34,7 +34,7 @@ export function preAliasPlugin(config: ResolvedConfig): Plugin { importer && depsOptimizer && bareImportRE.test(id) && - !options?.scan && + !options.scan && id !== '@vite/client' && id !== '@vite/env' ) { @@ -120,9 +120,6 @@ function matches(pattern: string | RegExp, importee: string) { function getAliasPatterns( entries: (AliasOptions | undefined) & Alias[], ): (string | RegExp)[] { - if (!entries) { - return [] - } if (Array.isArray(entries)) { return entries.map((entry) => entry.find) } diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 324cbd0e7465dc..a9afdd2b52a5cc 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -217,7 +217,7 @@ export function resolvePlugin( // this is passed by @rollup/plugin-commonjs const isRequire: boolean = - resolveOpts?.custom?.['node-resolve']?.isRequire ?? false + resolveOpts.custom?.['node-resolve']?.isRequire ?? false const currentEnvironmentOptions = this.environment.config @@ -225,7 +225,7 @@ export function resolvePlugin( isRequire, ...currentEnvironmentOptions.resolve, ...resolveOptions, // plugin options + resolve options overrides - scan: resolveOpts?.scan ?? resolveOptions.scan, + scan: resolveOpts.scan ?? resolveOptions.scan, } const resolvedImports = resolveSubpathImports(id, importer, options) @@ -244,7 +244,7 @@ export function resolvePlugin( ) { options.isFromTsImporter = true } else { - const moduleLang = this.getModuleInfo(importer)?.meta?.vite?.lang + const moduleLang = this.getModuleInfo(importer)?.meta.vite?.lang options.isFromTsImporter = moduleLang && isTsRequest(`.${moduleLang}`) } } @@ -702,7 +702,7 @@ export function tryNodeResolve( const pkgId = deepMatch ? (deepMatch[1] || deepMatch[2])! : cleanUrl(id) let basedir: string - if (dedupe?.includes(pkgId)) { + if (dedupe.includes(pkgId)) { basedir = root } else if ( importer && @@ -720,7 +720,7 @@ export function tryNodeResolve( // check if it's a self reference dep. const selfPackageData = findNearestPackageData(basedir, packageCache) selfPkg = - selfPackageData?.data.exports && selfPackageData?.data.name === pkgId + selfPackageData?.data.exports && selfPackageData.data.name === pkgId ? selfPackageData : null } @@ -773,7 +773,7 @@ export function tryNodeResolve( let resolvedId = id if ( deepMatch && - !pkg?.data.exports && + !pkg.data.exports && path.extname(id) !== path.extname(resolved.id) ) { // id date-fns/locale diff --git a/packages/vite/src/node/plugins/splitVendorChunk.ts b/packages/vite/src/node/plugins/splitVendorChunk.ts index 39a50a02dbf28f..ce2a5eed98754c 100644 --- a/packages/vite/src/node/plugins/splitVendorChunk.ts +++ b/packages/vite/src/node/plugins/splitVendorChunk.ts @@ -102,7 +102,7 @@ export function splitVendorChunkPlugin(): Plugin { const cache = new SplitVendorChunkCache() caches.push(cache) const build = config.build ?? {} - const format = output?.format + const format = output.format if (!build.ssr && !build.lib && format !== 'umd' && format !== 'iife') { return splitVendorChunk({ cache }) } @@ -110,7 +110,7 @@ export function splitVendorChunkPlugin(): Plugin { return { name: 'vite:split-vendor-chunk', config(config) { - let outputs = config?.build?.rollupOptions?.output + let outputs = config.build?.rollupOptions?.output if (outputs) { outputs = arraify(outputs) for (const output of outputs) { diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 35bcaf011adb28..c678f771fa8ba1 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -122,7 +122,7 @@ async function bundleWorkerEntry( outputChunks.forEach((outputChunk) => { if (outputChunk.type === 'asset') { saveEmitWorkerAsset(config, outputChunk) - } else if (outputChunk.type === 'chunk') { + } else { saveEmitWorkerAsset(config, { fileName: outputChunk.fileName, originalFileName: null, @@ -278,7 +278,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { injectEnv = `importScripts(${scriptPath})\n` } else if (workerType === 'module') { injectEnv = `import ${scriptPath}\n` - } else if (workerType === 'ignore') { + } else { if (isBuild) { injectEnv = '' } else { @@ -403,7 +403,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { }, renderChunk(code, chunk, outputOptions) { - let s: MagicString + let s: MagicString | undefined const result = () => { return ( s && { diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 30a398b3750036..174d97fa9a5a63 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -30,9 +30,9 @@ function parseWorkerOptions( rawOpts: string, optsStartIndex: number, ): WorkerOptions { - let opts: WorkerOptions = {} + let opts: unknown try { - opts = evalValue(rawOpts) + opts = evalValue(rawOpts) } catch { throw err( 'Vite is unable to parse the worker options as the value is not static.' + @@ -52,7 +52,7 @@ function parseWorkerOptions( ) } - return opts + return opts as WorkerOptions } function getWorkerType(raw: string, clean: string, i: number): WorkerType { @@ -107,7 +107,7 @@ function isIncludeWorkerImportMetaUrl(code: string): boolean { export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const isBuild = config.command === 'build' - let workerResolver: ResolveIdFn + let workerResolver: ResolveIdFn | undefined const fsResolveOptions: InternalResolveOptions = { ...config.resolve, diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts index f788e3cd640177..d6c1beece175ce 100644 --- a/packages/vite/src/node/preview.ts +++ b/packages/vite/src/node/preview.ts @@ -116,8 +116,7 @@ export async function preview( true, ) - const clientOutDir = - config.environments.client!.build.outDir ?? config.build.outDir + const clientOutDir = config.environments.client!.build.outDir const distDir = path.resolve(config.root, clientOutDir) if ( !fs.existsSync(distDir) && @@ -137,7 +136,7 @@ export async function preview( const httpServer = await resolveHttpServer( config.preview, app, - await resolveHttpsConfig(config.preview?.https), + await resolveHttpsConfig(config.preview.https), ) setClientErrorHandler(httpServer, config.logger) @@ -259,7 +258,7 @@ export async function preview( ) if (options.open) { - const url = server.resolvedUrls?.local[0] ?? server.resolvedUrls?.network[0] + const url = server.resolvedUrls.local[0] ?? server.resolvedUrls.network[0] if (url) { const path = typeof options.open === 'string' ? new URL(options.open, url).href : url diff --git a/packages/vite/src/node/server/environment.ts b/packages/vite/src/node/server/environment.ts index 6b126122ee65dc..8268419e1f4a8f 100644 --- a/packages/vite/src/node/server/environment.ts +++ b/packages/vite/src/node/server/environment.ts @@ -234,12 +234,12 @@ export class DevEnvironment extends BaseEnvironment { async close(): Promise { this._closing = true - this._crawlEndFinder?.cancel() + this._crawlEndFinder.cancel() await Promise.allSettled([ this.pluginContainer.close(), this.depsOptimizer?.close(), // WebSocketServer is independent of HotChannel and should not be closed on environment close - isWebSocketServer in this.hot ? Promise.resolve() : this.hot.close?.(), + isWebSocketServer in this.hot ? Promise.resolve() : this.hot.close(), (async () => { while (this._pendingRequests.size > 0) { await Promise.allSettled( diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index fa15d0895803bf..8556919418a808 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -364,7 +364,7 @@ export function getSortedPluginsByHotUpdateHook( const sortedHotUpdatePluginsCache = new WeakMap() function getSortedHotUpdatePlugins(environment: Environment): Plugin[] { - let sortedPlugins = sortedHotUpdatePluginsCache.get(environment) as Plugin[] + let sortedPlugins = sortedHotUpdatePluginsCache.get(environment) if (!sortedPlugins) { sortedPlugins = getSortedPluginsByHotUpdateHook(environment.plugins) sortedHotUpdatePluginsCache.set(environment, sortedPlugins) @@ -989,7 +989,7 @@ export function lexAcceptedHmrDeps( // in both case this indicates a self-accepting module return true // done } - } else if (state === LexerState.inArray) { + } else { if (char === `]`) { return false // done } else if (char === ',') { @@ -1003,7 +1003,7 @@ export function lexAcceptedHmrDeps( case LexerState.inSingleQuoteString: if (char === `'`) { addDep(i) - if (prevState === LexerState.inCall) { + if ((prevState as LexerState) === LexerState.inCall) { // accept('foo', ...) return false } else { @@ -1016,7 +1016,7 @@ export function lexAcceptedHmrDeps( case LexerState.inDoubleQuoteString: if (char === `"`) { addDep(i) - if (prevState === LexerState.inCall) { + if ((prevState as LexerState) === LexerState.inCall) { // accept('foo', ...) return false } else { @@ -1029,7 +1029,7 @@ export function lexAcceptedHmrDeps( case LexerState.inTemplateString: if (char === '`') { addDep(i) - if (prevState === LexerState.inCall) { + if ((prevState as LexerState) === LexerState.inCall) { // accept('foo', ...) return false } else { @@ -1174,9 +1174,7 @@ export function createDeprecatedHotBroadcaster( return broadcaster }, close() { - return Promise.all( - broadcaster.channels.map((channel) => channel.close?.()), - ) + return Promise.all(broadcaster.channels.map((channel) => channel.close())) }, } return broadcaster diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 097d0d6d2159e2..6fec3496cc9e57 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -429,7 +429,7 @@ export async function _createServer( const resolvedOutDirs = getResolvedOutDirs( config.root, config.build.outDir, - config.build.rollupOptions?.output, + config.build.rollupOptions.output, ) const emptyOutDir = resolveEmptyOutDir( config.build.emptyOutDir, @@ -537,8 +537,7 @@ export async function _createServer( return ssrTransform(code, inMap, url, originalCode, { json: { stringify: - config.json?.stringify === true && - config.json.namedExports !== true, + config.json.stringify === true && config.json.namedExports !== true, }, }) }, diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index a9a61ff0b942ce..779ad1256e9dd6 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -375,19 +375,13 @@ const devHtmlHook: IndexHtmlTransformHook = async ( environment: server!.environments.client, }) let content = '' - if (result) { - if (result.map && 'version' in result.map) { - if (result.map.mappings) { - await injectSourcesContent( - result.map, - proxyModulePath, - config.logger, - ) - } - content = getCodeWithSourcemap('css', result.code, result.map) - } else { - content = result.code + if (result.map && 'version' in result.map) { + if (result.map.mappings) { + await injectSourcesContent(result.map, proxyModulePath, config.logger) } + content = getCodeWithSourcemap('css', result.code, result.map) + } else { + content = result.code } s.overwrite(start, end, content) }), diff --git a/packages/vite/src/node/server/middlewares/transform.ts b/packages/vite/src/node/server/middlewares/transform.ts index abd790b1ac4d3f..22f06cfbacfc5e 100644 --- a/packages/vite/src/node/server/middlewares/transform.ts +++ b/packages/vite/src/node/server/middlewares/transform.ts @@ -60,7 +60,7 @@ export function cachedTransformMiddleware( const moduleByEtag = environment.moduleGraph.getModuleByEtag(ifNoneMatch) if ( moduleByEtag?.transformResult?.etag === ifNoneMatch && - moduleByEtag?.url === req.url + moduleByEtag.url === req.url ) { // For CSS requests, if the same CSS file is imported in a module, // the browser sends the request for the direct CSS request with the etag diff --git a/packages/vite/src/node/server/mixedModuleGraph.ts b/packages/vite/src/node/server/mixedModuleGraph.ts index 28fad52e5de670..51fdc3ad1d3809 100644 --- a/packages/vite/src/node/server/mixedModuleGraph.ts +++ b/packages/vite/src/node/server/mixedModuleGraph.ts @@ -659,7 +659,7 @@ function createBackwardCompatibleFileToModulesMap( } } if (!found) { - modules?.add(ssrModule) + modules.add(ssrModule) } } } diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 06389988114daa..5d403a9409abad 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -288,10 +288,9 @@ class EnvironmentPluginContainer { const parallelPromises: Promise[] = [] for (const plugin of this.getSortedPlugins(hookName)) { // Don't throw here if closed, so buildEnd and closeBundle hooks can finish running - const hook = plugin[hookName] - if (!hook) continue if (condition && !condition(plugin)) continue + const hook = plugin[hookName] const handler: Function = getHookHandler(hook) if ((hook as { sequential?: boolean }).sequential) { await Promise.all(parallelPromises) @@ -360,7 +359,6 @@ class EnvironmentPluginContainer { for (const plugin of this.getSortedPlugins('resolveId')) { if (this._closed && this.environment.config.dev.recoverable) throwClosedServerError() - if (!plugin.resolveId) continue if (skip?.has(plugin)) continue ctx._plugin = plugin @@ -423,7 +421,6 @@ class EnvironmentPluginContainer { for (const plugin of this.getSortedPlugins('load')) { if (this._closed && this.environment.config.dev.recoverable) throwClosedServerError() - if (!plugin.load) continue ctx._plugin = plugin const handler = getHookHandler(plugin.load) const result = await this.handleHookPromise( @@ -458,7 +455,6 @@ class EnvironmentPluginContainer { for (const plugin of this.getSortedPlugins('transform')) { if (this._closed && this.environment.config.dev.recoverable) throwClosedServerError() - if (!plugin.transform) continue ctx._updateActiveInfo(plugin, id, code) const start = debugPluginTransform ? performance.now() : 0 @@ -564,7 +560,7 @@ class PluginContext implements Omit { }, ) { let skip: Set | undefined - if (options?.skipSelf !== false && this._plugin) { + if (options?.skipSelf !== false) { skip = new Set(this._resolveSkips) skip.add(this._plugin) } @@ -688,7 +684,7 @@ class PluginContext implements Omit { if (err.pluginCode) { return err // The plugin likely called `this.error` } - if (this._plugin) err.plugin = this._plugin.name + err.plugin = this._plugin.name if (this._activeId && !err.id) err.id = this._activeId if (this._activeCode) { err.pluginCode = this._activeCode @@ -740,16 +736,16 @@ class PluginContext implements Omit { if ( this instanceof TransformPluginContext && typeof err.loc?.line === 'number' && - typeof err.loc?.column === 'number' + typeof err.loc.column === 'number' ) { const rawSourceMap = this._getCombinedSourcemap() - if (rawSourceMap && 'version' in rawSourceMap) { + if ('version' in rawSourceMap) { const traced = new TraceMap(rawSourceMap as any) const { source, line, column } = originalPositionFor(traced, { line: Number(err.loc.line), column: Number(err.loc.column), }) - if (source && line != null && column != null) { + if (source) { err.loc = { file: source, line, column } } } @@ -851,7 +847,7 @@ class TransformPluginContext } } - _getCombinedSourcemap(): SourceMap { + _getCombinedSourcemap(): SourceMap | { mappings: '' } { if ( debugSourcemapCombine && debugSourcemapCombineFilter && @@ -868,10 +864,11 @@ class TransformPluginContext if ( combinedMap && !('version' in combinedMap) && + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- extra check for safety combinedMap.mappings === '' ) { this.sourcemapChain.length = 0 - return combinedMap as SourceMap + return combinedMap } for (let m of this.sourcemapChain) { @@ -912,12 +909,13 @@ class TransformPluginContext this.combinedMap = combinedMap this.sourcemapChain.length = 0 } - return this.combinedMap as SourceMap + return this.combinedMap! } getCombinedSourcemap(): SourceMap { - const map = this._getCombinedSourcemap() as SourceMap | { mappings: '' } - if (!map || (!('version' in map) && map.mappings === '')) { + const map = this._getCombinedSourcemap() + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- extra check for safety + if (!('version' in map) && map.mappings === '') { return new MagicString(this.originalCode).generateMap({ includeContent: true, hires: 'boundary', @@ -953,7 +951,7 @@ class PluginContainer { }) { return options?.environment ? options.environment - : this.environments?.[options?.ssr ? 'ssr' : 'client'] + : this.environments[options?.ssr ? 'ssr' : 'client'] } private _getPluginContainer(options?: { diff --git a/packages/vite/src/node/server/sourcemap.ts b/packages/vite/src/node/server/sourcemap.ts index 684dff128e597d..8811515e58aa4a 100644 --- a/packages/vite/src/node/server/sourcemap.ts +++ b/packages/vite/src/node/server/sourcemap.ts @@ -37,7 +37,7 @@ export async function injectSourcesContent( file: string, logger: Logger, ): Promise { - let sourceRootPromise: Promise + let sourceRootPromise: Promise | undefined const missingSources: string[] = [] const sourcesContent = map.sourcesContent || [] @@ -101,7 +101,7 @@ export function getCodeWithSourcemap( if (type === 'js') { code += `\n//# sourceMappingURL=${genSourceMapUrl(map)}` - } else if (type === 'css') { + } else { code += `\n/*# sourceMappingURL=${genSourceMapUrl(map)} */` } diff --git a/packages/vite/src/node/server/transformRequest.ts b/packages/vite/src/node/server/transformRequest.ts index 66e81559bf4aca..a7104753b976a4 100644 --- a/packages/vite/src/node/server/transformRequest.ts +++ b/packages/vite/src/node/server/transformRequest.ts @@ -70,7 +70,7 @@ export function transformRequest( options: TransformOptions = {}, ): Promise { // Backward compatibility when only `ssr` is passed - if (!options?.ssr) { + if (!options.ssr) { // Backward compatibility options = { ...options, ssr: environment.config.consumer === 'server' } } @@ -215,16 +215,18 @@ async function getCachedTransformResult( // tries to handle soft invalidation of the module if available, // returns a boolean true is successful, or false if no handling is needed - const softInvalidatedTransformResult = - module && - (await handleModuleSoftInvalidation(environment, module, timestamp)) + const softInvalidatedTransformResult = await handleModuleSoftInvalidation( + environment, + module, + timestamp, + ) if (softInvalidatedTransformResult) { debugCache?.(`[memory-hmr] ${prettyUrl}`) return softInvalidatedTransformResult } // check if we have a fresh cache - const cached = module?.transformResult + const cached = module.transformResult if (cached) { debugCache?.(`[memory] ${prettyUrl}`) return cached @@ -345,10 +347,7 @@ async function loadAndTransform( inMap: map, }) const originalCode = code - if ( - transformResult == null || - (isObject(transformResult) && transformResult.code == null) - ) { + if (transformResult.code === originalCode) { // no transform applied, keep code as-is debugTransform?.( timeFrom(transformStart) + colors.dim(` [skipped] ${prettyUrl}`), @@ -413,7 +412,7 @@ async function loadAndTransform( ? await ssrTransform(code, normalizedMap, url, originalCode, { json: { stringify: - topLevelConfig.json?.stringify === true && + topLevelConfig.json.stringify === true && topLevelConfig.json.namedExports !== true, }, }) diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts index 8b8081f43bfbb7..0dfdf5f420fc7c 100644 --- a/packages/vite/src/node/server/ws.ts +++ b/packages/vite/src/node/server/ws.ts @@ -125,11 +125,13 @@ export function createWebSocketServer( // TODO: the main server port may not have been chosen yet as it may use the next available const portsAreCompatible = !hmrPort || hmrPort === config.server.port const wsServer = hmrServer || (portsAreCompatible && server) - let hmrServerWsListener: ( - req: InstanceType, - socket: Duplex, - head: Buffer, - ) => void + let hmrServerWsListener: + | (( + req: InstanceType, + socket: Duplex, + head: Buffer, + ) => void) + | undefined const customListeners = new Map>>() const clientsMap = new WeakMap() const port = hmrPort || 24678 diff --git a/packages/vite/src/node/ssr/ssrStacktrace.ts b/packages/vite/src/node/ssr/ssrStacktrace.ts index 1b188d8b43a8a5..6224fc6c42ab92 100644 --- a/packages/vite/src/node/ssr/ssrStacktrace.ts +++ b/packages/vite/src/node/ssr/ssrStacktrace.ts @@ -3,11 +3,11 @@ import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping' import type { EnvironmentModuleGraph } from '..' import type { StrictRegExpExecArrayFromLen } from '../../shared/typeUtils' -let offset: number +let offsetCache: number | undefined -function calculateOffsetOnce() { - if (offset !== undefined) { - return +function calculateOffsetOnce(): number { + if (offsetCache !== undefined) { + return offsetCache } try { @@ -19,15 +19,17 @@ function calculateOffsetOnce() { const match = /:(\d+):\d+\)$/.exec( e.stack.split('\n')[1], ) as StrictRegExpExecArrayFromLen<1> | null - offset = match ? +match[1] - 1 : 0 + offsetCache = match ? +match[1] - 1 : 0 + return offsetCache } + throw new Error('catch above always happens') } export function ssrRewriteStacktrace( stack: string, moduleGraph: EnvironmentModuleGraph, ): string { - calculateOffsetOnce() + const offset = calculateOffsetOnce() return stack .split('\n') .map((line) => { @@ -51,7 +53,7 @@ export function ssrRewriteStacktrace( column: Number(column) - 1, }) - if (!pos.source || pos.line == null || pos.column == null) { + if (!pos.source) { return input } diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index bc15aaf8f3979c..6bf2c525324261 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -473,7 +473,7 @@ function walk( } function isInScope(name: string, parents: Node[]) { - return parents.some((node) => node && scopeMap.get(node)?.has(name)) + return parents.some((node) => scopeMap.get(node)?.has(name)) } function handlePattern(p: Pattern, parentScope: _Node) { if (p.type === 'Identifier') { @@ -571,8 +571,8 @@ function walk( enter(child: Node, parent: Node) { // skip params default value of destructure if ( - parent?.type === 'AssignmentPattern' && - parent?.right === child + parent.type === 'AssignmentPattern' && + parent.right === child ) { return this.skip() } @@ -582,9 +582,9 @@ function walk( // do not record if this is a default value // assignment of a destructuring variable if ( - (parent?.type === 'TemplateLiteral' && - parent?.expressions.includes(child)) || - (parent?.type === 'CallExpression' && parent?.callee === child) + (parent.type === 'TemplateLiteral' && + parent.expressions.includes(child)) || + (parent.type === 'CallExpression' && parent.callee === child) ) { return } @@ -706,7 +706,7 @@ function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) { } const isStaticProperty = (node: _Node): node is Property => - node && node.type === 'Property' && !node.computed + node.type === 'Property' && !node.computed const isStaticPropertyKey = (node: _Node, parent: _Node) => isStaticProperty(parent) && parent.key === node @@ -732,10 +732,7 @@ function isInDestructuringAssignment( parent: _Node, parentStack: _Node[], ): boolean { - if ( - parent && - (parent.type === 'Property' || parent.type === 'ArrayPattern') - ) { + if (parent.type === 'Property' || parent.type === 'ArrayPattern') { return parentStack.some((i) => i.type === 'AssignmentExpression') } return false diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 957ec1de18e8e2..e09dfc0e66835f 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -207,7 +207,7 @@ function testCaseInsensitiveFS() { } export const urlCanParse = - // eslint-disable-next-line n/no-unsupported-features/node-builtins + // eslint-disable-next-line n/no-unsupported-features/node-builtins, @typescript-eslint/no-unnecessary-condition URL.canParse ?? // URL.canParse is supported from Node.js 18.17.0+, 20.0.0+ ((path: string, base?: string | undefined): boolean => { @@ -990,10 +990,10 @@ export async function resolveServerUrls( .flatMap((nInterface) => nInterface ?? []) .filter( (detail) => - detail && detail.address && (detail.family === 'IPv4' || // @ts-expect-error Node 18.0 - 18.3 returns number + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition detail.family === 4), ) .forEach((detail) => { @@ -1026,7 +1026,7 @@ export const requestQueryMaybeEscapedSplitRE = /\\?\?(?!.*[/|}])/ export const blankReplacer = (match: string): string => ' '.repeat(match.length) const hash = - // eslint-disable-next-line n/no-unsupported-features/node-builtins -- crypto.hash is supported in Node 21.7.0+, 20.12.0+ + // eslint-disable-next-line n/no-unsupported-features/node-builtins, @typescript-eslint/no-unnecessary-condition -- crypto.hash is supported in Node 21.7.0+, 20.12.0+ crypto.hash ?? (( algorithm: string, @@ -1417,7 +1417,7 @@ export function getNpmPackageName(importPath: string): string | null { } export function getPkgName(name: string): string | undefined { - return name?.[0] === '@' ? name.split('/')[1] : name + return name[0] === '@' ? name.split('/')[1] : name } const escapeRegexRE = /[-/\\^$*+?.()|[\]{}]/g diff --git a/packages/vite/src/shared/hmr.ts b/packages/vite/src/shared/hmr.ts index 342e0894bef971..9951b2bd41c792 100644 --- a/packages/vite/src/shared/hmr.ts +++ b/packages/vite/src/shared/hmr.ts @@ -81,7 +81,7 @@ export class HMRContext implements ViteHotContext { _: string | readonly string[], callback: (data: any) => void, ): void { - this.acceptDeps([this.ownerPath], ([mod]) => callback?.(mod)) + this.acceptDeps([this.ownerPath], ([mod]) => callback(mod)) } dispose(cb: (data: any) => void): void { diff --git a/packages/vite/src/shared/moduleRunnerTransport.ts b/packages/vite/src/shared/moduleRunnerTransport.ts index 8fad9b0d2f5fe0..6168fdbdd0142c 100644 --- a/packages/vite/src/shared/moduleRunnerTransport.ts +++ b/packages/vite/src/shared/moduleRunnerTransport.ts @@ -155,6 +155,7 @@ const createInvokeableTransport = ( ), ) }, timeout) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- we cannot assume that timeoutId is NodeJS.Timeout timeoutId?.unref?.() } rpcPromises.set(promiseId, { resolve, reject, name, timeoutId }) From 803302770234712f2e3f0ec42e35a78eae699147 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:41:26 +0900 Subject: [PATCH 4/9] chore: disable reportUnusedDisableDirectives --- eslint.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eslint.config.js b/eslint.config.js index 3cc30c15828382..a571421eb56219 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -18,6 +18,9 @@ const shouldTypeCheck = typeof process.env.VSCODE_PID === 'string' export default tseslint.config( { + linterOptions: { + reportUnusedDisableDirectives: !shouldTypeCheck, + }, ignores: [ 'packages/create-vite/template-*', '**/dist/**', From e2acffdeddd3c6c735f936248190df93a764570f Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:43:19 +0900 Subject: [PATCH 5/9] chore: disable reportUnusedDisableDirectives --- eslint.config.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index a571421eb56219..47ad6ac806edd8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -18,9 +18,6 @@ const shouldTypeCheck = typeof process.env.VSCODE_PID === 'string' export default tseslint.config( { - linterOptions: { - reportUnusedDisableDirectives: !shouldTypeCheck, - }, ignores: [ 'packages/create-vite/template-*', '**/dist/**', @@ -31,6 +28,11 @@ export default tseslint.config( '**/*.snap', ], }, + { + linterOptions: { + reportUnusedDisableDirectives: !shouldTypeCheck, + }, + }, eslint.configs.recommended, ...tseslint.configs.recommended, ...tseslint.configs.stylistic, From d55a5967d7e2e8bf0abc3d91abcb8284b1ab77cf Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:45:29 +0900 Subject: [PATCH 6/9] chore: disable reportUnusedDisableDirectives --- eslint.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index 47ad6ac806edd8..8b6b7c734d8b5c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -30,7 +30,7 @@ export default tseslint.config( }, { linterOptions: { - reportUnusedDisableDirectives: !shouldTypeCheck, + reportUnusedDisableDirectives: shouldTypeCheck ? 'warn' : 'off', }, }, eslint.configs.recommended, From be1ddbd88a8563f7201962abf057de5b02403e42 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:58:52 +0900 Subject: [PATCH 7/9] chore: fix code --- packages/vite/src/node/plugins/css.ts | 8 ++++---- packages/vite/src/node/plugins/esbuild.ts | 4 ++-- packages/vite/src/node/plugins/importAnalysis.ts | 3 ++- packages/vite/src/node/server/pluginContainer.ts | 10 +++++----- packages/vite/src/node/ssr/ssrTransform.ts | 12 ++++++------ 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 50dcc5da2fbe91..3c186e29f6ac5a 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -906,12 +906,12 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // will be populated in order they are used by entry points const dynamicImports = new Set() - function collect(chunk: OutputChunk | OutputAsset) { - if (chunk.type !== 'chunk' || collected.has(chunk)) return + function collect(chunk: OutputChunk | OutputAsset | undefined) { + if (!chunk || chunk.type !== 'chunk' || collected.has(chunk)) return collected.add(chunk) // First collect all styles from the synchronous imports (lowest priority) - chunk.imports.forEach((importName) => collect(bundle[importName]!)) + chunk.imports.forEach((importName) => collect(bundle[importName])) // Save dynamic imports in deterministic order to add the styles later (to have the highest priority) chunk.dynamicImports.forEach((importName) => dynamicImports.add(importName), @@ -929,7 +929,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } // Now collect the dynamic chunks, this is done last to have the styles overwrite the previous ones for (const chunkName of dynamicImports) { - collect(bundle[chunkName]!) + collect(bundle[chunkName]) } // Finally, if there's any extracted CSS, we emit the asset diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index a495362b66b7eb..54e0f5df4963a5 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -265,7 +265,7 @@ export function esbuildPlugin(config: ResolvedConfig): Plugin { }, } - let server: ViteDevServer + let server: ViteDevServer | undefined return { name: 'vite:esbuild', @@ -280,7 +280,7 @@ export function esbuildPlugin(config: ResolvedConfig): Plugin { transformOptions, undefined, config, - server.watcher, + server?.watcher, ) if (result.warnings.length) { result.warnings.forEach((m) => { diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 7d01ac65f32810..8b319989a8c3b8 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -353,7 +353,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { throw e }) - if (!resolved || resolved.meta['vite:alias']?.noResolved) { + // resolved.meta does not exist in dev + if (!resolved || resolved.meta?.['vite:alias']?.noResolved) { // in ssr, we should let node handle the missing modules if (ssr) { return [url, url] diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 5d403a9409abad..a641a6c8f8c750 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -739,7 +739,7 @@ class PluginContext implements Omit { typeof err.loc.column === 'number' ) { const rawSourceMap = this._getCombinedSourcemap() - if ('version' in rawSourceMap) { + if (rawSourceMap && 'version' in rawSourceMap) { const traced = new TraceMap(rawSourceMap as any) const { source, line, column } = originalPositionFor(traced, { line: Number(err.loc.line), @@ -847,7 +847,7 @@ class TransformPluginContext } } - _getCombinedSourcemap(): SourceMap | { mappings: '' } { + _getCombinedSourcemap(): SourceMap | { mappings: '' } | null { if ( debugSourcemapCombine && debugSourcemapCombineFilter && @@ -909,20 +909,20 @@ class TransformPluginContext this.combinedMap = combinedMap this.sourcemapChain.length = 0 } - return this.combinedMap! + return this.combinedMap } getCombinedSourcemap(): SourceMap { const map = this._getCombinedSourcemap() // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- extra check for safety - if (!('version' in map) && map.mappings === '') { + if (!map || (!('version' in map) && map.mappings === '')) { return new MagicString(this.originalCode).generateMap({ includeContent: true, hires: 'boundary', source: cleanUrl(this.filename), }) } - return map + return map as SourceMap } _updateActiveInfo(plugin: Plugin, id: string, code: string): void { diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 6bf2c525324261..e8075a76d64303 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -568,10 +568,10 @@ function walk( return } ;(eswalk as any)(p.type === 'AssignmentPattern' ? p.left : p, { - enter(child: Node, parent: Node) { + enter(child: Node, parent: Node | undefined) { // skip params default value of destructure if ( - parent.type === 'AssignmentPattern' && + parent?.type === 'AssignmentPattern' && parent.right === child ) { return this.skip() @@ -582,9 +582,9 @@ function walk( // do not record if this is a default value // assignment of a destructuring variable if ( - (parent.type === 'TemplateLiteral' && + (parent?.type === 'TemplateLiteral' && parent.expressions.includes(child)) || - (parent.type === 'CallExpression' && parent.callee === child) + (parent?.type === 'CallExpression' && parent.callee === child) ) { return } @@ -708,8 +708,8 @@ function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) { const isStaticProperty = (node: _Node): node is Property => node.type === 'Property' && !node.computed -const isStaticPropertyKey = (node: _Node, parent: _Node) => - isStaticProperty(parent) && parent.key === node +const isStaticPropertyKey = (node: _Node, parent: _Node | undefined) => + parent && isStaticProperty(parent) && parent.key === node const functionNodeTypeRE = /Function(?:Expression|Declaration)$|Method$/ function isFunction(node: _Node): node is FunctionNode { From fa50e8226ca113e39b4a4ba2148f9663cc422f87 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:34:13 +0900 Subject: [PATCH 8/9] chore: fix code --- packages/create-vite/src/index.ts | 3 ++- packages/vite/src/shared/hmr.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/create-vite/src/index.ts b/packages/create-vite/src/index.ts index 75748bfb1e7a53..a86fd1958709a8 100755 --- a/packages/create-vite/src/index.ts +++ b/packages/create-vite/src/index.ts @@ -395,7 +395,8 @@ async function init() { }), }, { - type: 'select', + type: (framework: Framework | /* package name */ string) => + typeof framework === 'object' ? 'select' : null, name: 'variant', message: reset('Select a variant:'), choices: (framework: Framework) => diff --git a/packages/vite/src/shared/hmr.ts b/packages/vite/src/shared/hmr.ts index 9951b2bd41c792..a5d7d811566f8c 100644 --- a/packages/vite/src/shared/hmr.ts +++ b/packages/vite/src/shared/hmr.ts @@ -79,9 +79,9 @@ export class HMRContext implements ViteHotContext { // extracted in the server for propagation acceptExports( _: string | readonly string[], - callback: (data: any) => void, + callback?: (data: any) => void, ): void { - this.acceptDeps([this.ownerPath], ([mod]) => callback(mod)) + this.acceptDeps([this.ownerPath], ([mod]) => callback?.(mod)) } dispose(cb: (data: any) => void): void { From c1befeeddbf3eb07cf24e9ccb934ed4470a9c09f Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:33:15 +0900 Subject: [PATCH 9/9] chore: add exhaustiveness checks --- packages/vite/src/node/config.ts | 2 ++ packages/vite/src/node/plugins/html.ts | 2 ++ .../vite/src/node/plugins/importAnalysis.ts | 20 ++++++++++++++----- packages/vite/src/node/plugins/manifest.ts | 2 ++ packages/vite/src/node/plugins/worker.ts | 4 ++++ packages/vite/src/node/server/sourcemap.ts | 1 + 6 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index d3c6d8b5775e3b..8fe3c9ad109d45 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -1970,6 +1970,8 @@ function optimizeDepsDisabledBackwardCompatibility( `), ) } else { + optimizeDepsDisabled satisfies false | 'build' // exhaustiveness check + resolved.logger.warn( colors.yellow(`(!) Experimental ${optimizeDepsPath}optimizeDeps.disabled and deps pre-bundling during build were removed in Vite 5.1. Setting it to ${optimizeDepsDisabled} now has no effect. diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index f107ac662923e5..421b532988d792 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -550,6 +550,8 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { })(), ) } else { + attr.type satisfies 'src' // exhaustiveness check + const url = decodeURI(attr.value) if (checkPublicFile(url, config)) { overwriteAttrValue( diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 8b319989a8c3b8..93c37912f06c53 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -13,7 +13,11 @@ import { parseAst } from 'rollup/parseAst' import type { StaticImport } from 'mlly' import { ESM_STATIC_IMPORT_RE, parseStaticImport } from 'mlly' import { makeLegalIdentifier } from '@rollup/pluginutils' -import type { PartialResolvedId, RollupError } from 'rollup' +import type { + CustomPluginOptions, + PartialResolvedId, + RollupError, +} from 'rollup' import type { Identifier, Literal } from 'estree' import { CLIENT_DIR, @@ -305,7 +309,6 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { return source } - /* eslint-disable @typescript-eslint/no-unnecessary-condition -- there's many false positives */ let hasHMR = false let isSelfAccepting = false let hasEnv = false @@ -353,8 +356,12 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { throw e }) - // resolved.meta does not exist in dev - if (!resolved || resolved.meta?.['vite:alias']?.noResolved) { + if ( + !resolved || + // resolved.meta does not exist in dev + (resolved.meta as CustomPluginOptions | undefined)?.['vite:alias'] + ?.noResolved + ) { // in ssr, we should let node handle the missing modules if (ssr) { return [url, url] @@ -716,6 +723,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { const isClassicWorker = importer.includes(WORKER_FILE_ID) && importer.includes('type=classic') + /* eslint-disable @typescript-eslint/no-unnecessary-condition -- there's many false positives */ if (hasEnv && !isClassicWorker) { // inject import.meta.env str().prepend(getEnv(ssr)) @@ -806,6 +814,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { handlePrunedModules(prunedImports, environment) } } + /* eslint-enable @typescript-eslint/no-unnecessary-condition -- there's many false positives */ debug?.( `${timeFrom(msAtStart)} ${colors.dim( @@ -816,7 +825,6 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { )}`, ) - /* eslint-enable @typescript-eslint/no-unnecessary-condition */ if (s) { return transformStableResult(s, importer, config) } else { @@ -990,6 +998,8 @@ export function transformCjsImport( } else if (spec.type === 'ImportNamespaceSpecifier') { importNames.push({ importedName: '*', localName: spec.local.name }) } else { + spec.type satisfies 'ExportSpecifier' // exhaustiveness check + // for ExportSpecifier, local name is same as imported name // prefix the variable name to avoid clashing with other local variables const importedName = getIdentifierNameOrLiteralValue( diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index c0ee8a8bb7270a..481af0c2d34c59 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -149,6 +149,8 @@ export function manifestPlugin(): Plugin { if (chunk.type === 'chunk') { manifest[getChunkName(chunk)] = createChunk(chunk) } else if (chunk.names.length > 0) { + chunk.type satisfies 'asset' // exhaustiveness check + // Add every unique asset to the manifest, keyed by its original name const src = chunk.originalFileNames.length > 0 diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index c678f771fa8ba1..632e570ec50906 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -123,6 +123,8 @@ async function bundleWorkerEntry( if (outputChunk.type === 'asset') { saveEmitWorkerAsset(config, outputChunk) } else { + outputChunk.type satisfies 'chunk' // exhaustiveness check + saveEmitWorkerAsset(config, { fileName: outputChunk.fileName, originalFileName: null, @@ -279,6 +281,8 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } else if (workerType === 'module') { injectEnv = `import ${scriptPath}\n` } else { + workerType satisfies 'ignore' // exhaustiveness check + if (isBuild) { injectEnv = '' } else { diff --git a/packages/vite/src/node/server/sourcemap.ts b/packages/vite/src/node/server/sourcemap.ts index 8811515e58aa4a..d5d9ec018a5449 100644 --- a/packages/vite/src/node/server/sourcemap.ts +++ b/packages/vite/src/node/server/sourcemap.ts @@ -102,6 +102,7 @@ export function getCodeWithSourcemap( if (type === 'js') { code += `\n//# sourceMappingURL=${genSourceMapUrl(map)}` } else { + ;(type) satisfies 'css' // exhaustiveness check code += `\n/*# sourceMappingURL=${genSourceMapUrl(map)} */` }