From aef04b9bb3af9181cd4a6355ce4497f7ec0cec45 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 18 Feb 2026 08:25:55 +0000 Subject: [PATCH] chore: add more lint rules (#15553) * chore: add more lint rules * suppress pattern --- .github/scripts/bundle-size.mjs | 2 +- biome.jsonc | 31 +++++++++++++++++-- eslint.config.js | 30 +++++------------- .../hackernews/src/pages/[...stories].astro | 2 +- packages/astro/bin/astro.mjs | 2 +- packages/astro/components/Image.astro | 4 +-- packages/astro/e2e/nested-recursive.test.js | 1 + packages/astro/e2e/test-utils.js | 1 + packages/astro/src/actions/runtime/client.ts | 9 +++--- .../infra/capsize-font-metrics-resolver.ts | 2 +- .../fonts/infra/levenshtein-string-matcher.ts | 2 +- packages/astro/src/assets/index.ts | 2 +- packages/astro/src/assets/services/service.ts | 10 +++--- .../astro/src/assets/utils/queryParams.ts | 4 +-- packages/astro/src/cli/add/index.ts | 2 +- packages/astro/src/content/utils.ts | 4 +-- packages/astro/src/core/build/generate.ts | 2 +- .../astro/src/core/config/schemas/base.ts | 2 +- packages/astro/src/core/create-vite.ts | 2 +- packages/astro/src/core/dev/container.ts | 2 +- .../core/preview/vite-plugin-astro-preview.ts | 4 +-- packages/astro/src/core/render/paginate.ts | 2 +- packages/astro/src/core/routing/rewrite.ts | 2 +- packages/astro/src/env/validators.ts | 2 +- packages/astro/src/events/session.ts | 2 +- .../src/integrations/features-validation.ts | 2 +- packages/astro/src/integrations/hooks.ts | 2 +- packages/astro/src/manifest/virtual-module.ts | 2 +- .../dev-toolbar/apps/audit/rules/perf.ts | 6 ++-- .../client/dev-toolbar/apps/utils/icons.ts | 3 +- .../astro/src/runtime/server/astro-island.ts | 2 +- packages/astro/src/runtime/server/jsx.ts | 2 +- .../astro/src/runtime/server/render/head.ts | 2 +- .../astro/src/runtime/server/render/util.ts | 2 +- .../astro/src/runtime/server/serialize.ts | 4 +-- packages/astro/test/core-image-layout.test.js | 8 ++--- packages/astro/test/serialize.test.js | 2 +- packages/astro/test/streaming.test.js | 2 +- packages/create-astro/create-astro.mjs | 2 +- packages/create-astro/src/actions/context.ts | 4 +-- packages/db/src/core/integration/index.ts | 2 +- .../integrations/alpinejs/test/test-utils.js | 1 + .../src/utils/image-binding-transform.ts | 4 +-- .../src/html/css/parse-inline-styles.ts | 6 ++-- .../html/transform/html-token-transform.ts | 2 +- packages/integrations/partytown/src/sirv.ts | 4 +-- packages/integrations/preact/src/server.ts | 2 +- packages/integrations/react/src/version.ts | 2 +- .../vercel/src/image/build-service.ts | 2 +- packages/integrations/vercel/src/index.ts | 15 ++++----- .../integrations/vercel/test/image.test.js | 2 +- .../test/content-intellisense/caching.test.ts | 2 +- .../content-intellisense/completions.test.ts | 2 +- .../content-intellisense/definitions.test.ts | 2 +- .../content-intellisense/diagnostics.test.ts | 2 +- .../test/content-intellisense/hover.test.ts | 2 +- .../language-server/test/setup.js | 2 +- .../vscode/test/grammar/test.mjs | 2 +- packages/upgrade/upgrade.mjs | 2 +- 59 files changed, 120 insertions(+), 111 deletions(-) diff --git a/.github/scripts/bundle-size.mjs b/.github/scripts/bundle-size.mjs index 76d6b3f29724..ff36f3cbd778 100644 --- a/.github/scripts/bundle-size.mjs +++ b/.github/scripts/bundle-size.mjs @@ -12,7 +12,7 @@ function formatBytes(bytes, decimals = 2) { const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; + return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } export default async function checkBundleSize({ github, context }) { diff --git a/biome.jsonc b/biome.jsonc index 1c11e15b2b7b..2251f5e4db13 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -44,7 +44,9 @@ "style": { "useNodejsImportProtocol": "error", // Enforce separate type imports for type-only imports to avoid bundling unneeded code - "useImportType": "error" + "useImportType": "error", + "useExportType": "error", + "useNumberNamespace": "warn" }, "suspicious": { // This one is specific to catch `console.log`. The rest of logs are permitted @@ -53,7 +55,15 @@ "options": { "allow": ["error", "warn", "info", "debug"] } - } + }, + "noVar": "error", + "noDoubleEquals": "error", + "noDebugger": "error", + "noDuplicateCase": "error", + "noExplicitAny": "off", + // To enable later + "noImplicitAnyLet": "off", + "noSparseArray": "error" }, "correctness": { "noUnusedVariables": { @@ -63,7 +73,22 @@ } }, "noUnusedFunctionParameters": "error", - "noUnusedImports": "error" + "noUnusedImports": "error", + "noUnreachable": "error", + "noConstantCondition": "warn", + "noConstructorReturn": "error", + "noEmptyPattern": "error" + }, + "complexity": { + // There are many violations which require manual check + "noForEach": "off", + "noUselessFragments": "warn", + "useFlatMap": "warn", + // To enable later, as there are many warnings + "useOptionalChain": "off" + }, + "performance": { + "noAccumulatingSpread": "warn" } } }, diff --git a/eslint.config.js b/eslint.config.js index 86c5c448dee2..5b5f2ca4abb4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -50,24 +50,16 @@ export default [ regexp: regexpEslint, }, rules: { - // These off/configured-differently-by-default rules fit well for us + // Type-aware rules that Biome cannot replace '@typescript-eslint/switch-exhaustiveness-check': 'error', '@typescript-eslint/no-shadow': 'error', - 'no-console': 'off', - // Todo: do we want these? - '@typescript-eslint/no-unused-vars': [ - 'error', - { - args: 'all', - argsIgnorePattern: '^_', - caughtErrors: 'all', - caughtErrorsIgnorePattern: '^_', - destructuredArrayIgnorePattern: '^_', - varsIgnorePattern: '^_', - ignoreRestSiblings: true, - }, - ], + // Disabled - now handled by Biome + 'no-console': 'off', // Biome: suspicious.noConsole + '@typescript-eslint/no-unused-vars': 'off', // Biome: correctness.noUnusedVariables + 'prefer-const': 'off', // Biome: style.useConst + '@typescript-eslint/consistent-type-imports': 'off', // Biome: style.useImportType + '@typescript-eslint/await-thenable': 'off', '@typescript-eslint/array-type': 'off', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/class-literal-property-style': 'off', @@ -99,13 +91,7 @@ export default [ '@typescript-eslint/unbound-method': 'off', '@typescript-eslint/no-explicit-any': 'off', - // Used by Biome - '@typescript-eslint/consistent-type-imports': 'off', - // These rules enabled by the preset configs don't work well for us - '@typescript-eslint/await-thenable': 'off', - 'prefer-const': 'off', - - // In some cases, using explicit letter-casing is more performant than the `i` flag + // Regex-specific rules (no Biome equivalent) 'regexp/use-ignore-case': 'off', 'regexp/prefer-regexp-exec': 'warn', 'regexp/prefer-regexp-test': 'warn', diff --git a/examples/hackernews/src/pages/[...stories].astro b/examples/hackernews/src/pages/[...stories].astro index fa227e0c12ca..3f3e007c0562 100644 --- a/examples/hackernews/src/pages/[...stories].astro +++ b/examples/hackernews/src/pages/[...stories].astro @@ -16,7 +16,7 @@ const mapStories = { function safeParseInt(value: any, fallback: number) { try { - return parseInt(value) || fallback; + return Number.parseInt(value) || fallback; } catch { return fallback; } diff --git a/packages/astro/bin/astro.mjs b/packages/astro/bin/astro.mjs index 73e79291543c..4e7f59abb9b4 100755 --- a/packages/astro/bin/astro.mjs +++ b/packages/astro/bin/astro.mjs @@ -19,7 +19,7 @@ const skipSemverCheckIfAbove = IS_STACKBLITZ ? 21 : 23; async function main() { const version = process.versions.node; // Fast-path for higher Node.js versions - if ((parseInt(version) || 0) <= skipSemverCheckIfAbove) { + if ((Number.parseInt(version) || 0) <= skipSemverCheckIfAbove) { const semver = await import('semver'); try { if (!semver.satisfies(version, engines)) { diff --git a/packages/astro/components/Image.astro b/packages/astro/components/Image.astro index 96a2c1a848f6..07ce17b13f00 100644 --- a/packages/astro/components/Image.astro +++ b/packages/astro/components/Image.astro @@ -17,11 +17,11 @@ if (props.alt === undefined || props.alt === null) { // As a convenience, allow width and height to be string with a number in them, to match HTML's native `img`. if (typeof props.width === 'string') { - props.width = parseInt(props.width); + props.width = Number.parseInt(props.width); } if (typeof props.height === 'string') { - props.height = parseInt(props.height); + props.height = Number.parseInt(props.height); } const layout = props.layout ?? imageConfig.layout ?? 'none'; diff --git a/packages/astro/e2e/nested-recursive.test.js b/packages/astro/e2e/nested-recursive.test.js index d9f612642f63..a0f971db305c 100644 --- a/packages/astro/e2e/nested-recursive.test.js +++ b/packages/astro/e2e/nested-recursive.test.js @@ -2,6 +2,7 @@ import { test as base, expect } from '@playwright/test'; import { loadFixture, waitForHydrate } from './test-utils.js'; const test = base.extend({ + // biome-ignore lint/correctness/noEmptyPattern: playwright needs this astro: async ({}, use) => { const fixture = await loadFixture(import.meta.url, { root: './fixtures/nested-recursive/' }); await use(fixture); diff --git a/packages/astro/e2e/test-utils.js b/packages/astro/e2e/test-utils.js index 0263ffb2fd05..4ca3d17825dd 100644 --- a/packages/astro/e2e/test-utils.js +++ b/packages/astro/e2e/test-utils.js @@ -46,6 +46,7 @@ export function testFactory(testFile, inlineConfig) { let fixture; const test = testBase.extend({ + // biome-ignore lint/correctness/noEmptyPattern: playwright needs this astro: async ({}, use) => { fixture = fixture || (await loadFixture(testFile, inlineConfig)); await use(fixture); diff --git a/packages/astro/src/actions/runtime/client.ts b/packages/astro/src/actions/runtime/client.ts index 2a6591e4d59c..af23ffefb299 100644 --- a/packages/astro/src/actions/runtime/client.ts +++ b/packages/astro/src/actions/runtime/client.ts @@ -56,11 +56,10 @@ export const codeToStatusMap = { NETWORK_AUTHENTICATION_REQUIRED: 511, } satisfies Record; -const statusToCodeMap: Record = Object.entries(codeToStatusMap).reduce( - // reverse the key-value pairs - (acc, [key, value]) => ({ ...acc, [value]: key }), - {}, -); +// reverse the key-value pairs +const statusToCodeMap: Record = Object.fromEntries( + Object.entries(codeToStatusMap).map(([key, value]) => [value, key]), +) as Record; // T is used for error inference with SafeInput -> isInputError. // See: https://github.com/withastro/astro/pull/11173/files#r1622767246 diff --git a/packages/astro/src/assets/fonts/infra/capsize-font-metrics-resolver.ts b/packages/astro/src/assets/fonts/infra/capsize-font-metrics-resolver.ts index f3f5eae49019..19dd75a80c9b 100644 --- a/packages/astro/src/assets/fonts/infra/capsize-font-metrics-resolver.ts +++ b/packages/astro/src/assets/fonts/infra/capsize-font-metrics-resolver.ts @@ -23,7 +23,7 @@ function filterRequiredMetrics({ // Source: https://github.com/seek-oss/capsize/blob/b752693428b45994442433f7e3476ca4e3e3c507/packages/core/src/round.ts function round(value: number) { - return parseFloat(value.toFixed(4)); + return Number.parseFloat(value.toFixed(4)); } // Source: https://github.com/seek-oss/capsize/blob/b752693428b45994442433f7e3476ca4e3e3c507/packages/core/src/createFontStack.ts#L5 diff --git a/packages/astro/src/assets/fonts/infra/levenshtein-string-matcher.ts b/packages/astro/src/assets/fonts/infra/levenshtein-string-matcher.ts index 6f3d1f7e7634..27fc0413e495 100644 --- a/packages/astro/src/assets/fonts/infra/levenshtein-string-matcher.ts +++ b/packages/astro/src/assets/fonts/infra/levenshtein-string-matcher.ts @@ -133,7 +133,7 @@ export class LevenshteinStringMatcher implements StringMatcher { } #closest(str: string, arr: readonly string[]): string { - let min_distance = Infinity; + let min_distance = Number.POSITIVE_INFINITY; let min_index = 0; for (let i = 0; i < arr.length; i++) { const dist = this.#distance(str, arr[i]); diff --git a/packages/astro/src/assets/index.ts b/packages/astro/src/assets/index.ts index 9eeccf250444..6273c9cdc033 100644 --- a/packages/astro/src/assets/index.ts +++ b/packages/astro/src/assets/index.ts @@ -1,3 +1,3 @@ export { getConfiguredImageService, getImage } from './internal.js'; export { baseService, isLocalService } from './services/service.js'; -export { type LocalImageProps, type RemoteImageProps } from './types.js'; +export type { LocalImageProps, RemoteImageProps } from './types.js'; diff --git a/packages/astro/src/assets/services/service.ts b/packages/astro/src/assets/services/service.ts index f138d123d1c8..74a65fd59dc2 100644 --- a/packages/astro/src/assets/services/service.ts +++ b/packages/astro/src/assets/services/service.ts @@ -24,7 +24,7 @@ export function isLocalService(service: ImageService | undefined): service is Lo } export function parseQuality(quality: string): string | number { - let result = parseInt(quality); + let result = Number.parseInt(quality); if (Number.isNaN(result)) { return quality; } @@ -285,7 +285,7 @@ export const baseService: Omit = { // For remote images, we don't know the original image's dimensions, so we cannot know the maximum width // It is ultimately the user's responsibility to make sure they don't request images larger than the original let imageWidth = options.width; - let maxWidth = Infinity; + let maxWidth = Number.POSITIVE_INFINITY; // However, if it's an imported image, we can use the original image's width as a maximum width if (isESMImportedImage(options.src)) { @@ -322,7 +322,7 @@ export const baseService: Omit = { if (typeof density === 'number') { return density; } else { - return parseFloat(density); + return Number.parseFloat(density); } }); @@ -402,8 +402,8 @@ export const baseService: Omit = { const transform: BaseServiceTransform = { src: params.get('href')!, - width: params.has('w') ? parseInt(params.get('w')!) : undefined, - height: params.has('h') ? parseInt(params.get('h')!) : undefined, + width: params.has('w') ? Number.parseInt(params.get('w')!) : undefined, + height: params.has('h') ? Number.parseInt(params.get('h')!) : undefined, format: params.get('f') as ImageOutputFormat, quality: params.get('q'), fit: params.get('fit') as ImageFit, diff --git a/packages/astro/src/assets/utils/queryParams.ts b/packages/astro/src/assets/utils/queryParams.ts index f2c0251e4413..a11a7fcda666 100644 --- a/packages/astro/src/assets/utils/queryParams.ts +++ b/packages/astro/src/assets/utils/queryParams.ts @@ -21,8 +21,8 @@ export function getOrigQueryParams( } return { - width: parseInt(width), - height: parseInt(height), + width: Number.parseInt(width), + height: Number.parseInt(height), format: format as ImageInputFormat, }; } diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index d00c226df0c8..8fa02854031d 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -839,7 +839,7 @@ async function tryToInstallIntegrations({ const inheritedFlags = Object.entries(flags) .map(([flag]) => { - if (flag == '_') return; + if (flag === '_') return; if (INHERITED_FLAGS.has(flag)) { if (flag.length === 1) return `-${flag}`; return `--${flag}`; diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 107632c427fa..3bfa6646af3a 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -245,11 +245,11 @@ export async function getEntryData< } export function getContentEntryExts(settings: Pick) { - return settings.contentEntryTypes.map((t) => t.extensions).flat(); + return settings.contentEntryTypes.flatMap((t) => t.extensions); } export function getDataEntryExts(settings: Pick) { - return settings.dataEntryTypes.map((t) => t.extensions).flat(); + return settings.dataEntryTypes.flatMap((t) => t.extensions); } export function getEntryConfigByExtMap( diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 02fa888f7f0e..aee969cf4c5d 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -326,7 +326,7 @@ async function generatePathWithPrerenderer( (url) => url.href .replace(config.outDir.toString(), '') - .replace(/(?:\/index\.html|\.html)$/, '') == trimSlashes(pathname), + .replace(/(?:\/index\.html|\.html)$/, '') === trimSlashes(pathname), ) ) { return false; diff --git a/packages/astro/src/core/config/schemas/base.ts b/packages/astro/src/core/config/schemas/base.ts index fa9863a6539a..0b46bf1ab2c3 100644 --- a/packages/astro/src/core/config/schemas/base.ts +++ b/packages/astro/src/core/config/schemas/base.ts @@ -159,7 +159,7 @@ export const AstroConfigSchema = z.object({ adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(), integrations: z.preprocess( // preprocess - (val) => (Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val), + (val) => (Array.isArray(val) ? val.flat(Number.POSITIVE_INFINITY).filter(Boolean) : val), // validate z .array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) })) diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 3a86c66b06e8..369027e35cdf 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -261,7 +261,7 @@ export async function createVite( { command: command === 'dev' ? 'serve' : command, mode }, ]; // @ts-expect-error ignore TS2589: Type instantiation is excessively deep and possibly infinite. - plugins = plugins.flat(Infinity).filter((p) => { + plugins = plugins.flat(Number.POSITIVE_INFINITY).filter((p) => { if (!p || p?.apply === applyToFilter) { return false; } diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index 13123a463e79..c92945571876 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -64,7 +64,7 @@ export async function createContainer({ // expected behavior: do not spawn a new tab // ------------------------------------------------------ // Non-config files don't reach this point - const isServerOpenURL = typeof serverOpen == 'string' && !isRestart; + const isServerOpenURL = typeof serverOpen === 'string' && !isRestart; const isServerOpenBoolean = serverOpen && !isRestart; // Open server to the correct path. We pass the `base` here as we didn't pass the diff --git a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts index a8043192dfc9..20ce97e6408d 100644 --- a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts +++ b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts @@ -43,7 +43,7 @@ export function vitePluginAstroPreview(settings: AstroSettings): Plugin { if (!isRoot) { const hasTrailingSlash = pathname.endsWith('/'); - if (hasTrailingSlash && trailingSlash == 'never') { + if (hasTrailingSlash && trailingSlash === 'never') { res.statusCode = 404; res.end(notFoundTemplate(pathname, 'Not Found (trailingSlash is set to "never")')); return; @@ -51,7 +51,7 @@ export function vitePluginAstroPreview(settings: AstroSettings): Plugin { if ( !hasTrailingSlash && - trailingSlash == 'always' && + trailingSlash === 'always' && !HAS_FILE_EXTENSION_REGEXP.test(pathname) ) { res.statusCode = 404; diff --git a/packages/astro/src/core/render/paginate.ts b/packages/astro/src/core/render/paginate.ts index abe917f1c2ec..57f1718903cd 100644 --- a/packages/astro/src/core/render/paginate.ts +++ b/packages/astro/src/core/render/paginate.ts @@ -41,7 +41,7 @@ export function generatePaginateFunction( const result = [...Array(lastPage).keys()].map((num) => { const pageNum = num + 1; - const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize; // currentPage is 1-indexed + const start = pageSize === Number.POSITIVE_INFINITY ? 0 : (pageNum - 1) * pageSize; // currentPage is 1-indexed const end = Math.min(start + pageSize, data.length); const params = { ...additionalParams, diff --git a/packages/astro/src/core/routing/rewrite.ts b/packages/astro/src/core/routing/rewrite.ts index b43dc46f7cce..d3b0aac04383 100644 --- a/packages/astro/src/core/routing/rewrite.ts +++ b/packages/astro/src/core/routing/rewrite.ts @@ -116,7 +116,7 @@ export function findRouteToRewrite({ if ( !route.distURL.find( (url) => - url.href.replace(outDir.toString(), '').replace(/(?:\/index\.html|\.html)$/, '') == + url.href.replace(outDir.toString(), '').replace(/(?:\/index\.html|\.html)$/, '') === trimSlashes(pathname), ) ) { diff --git a/packages/astro/src/env/validators.ts b/packages/astro/src/env/validators.ts index f506bb8e7e57..c0d4f4bd8a87 100644 --- a/packages/astro/src/env/validators.ts +++ b/packages/astro/src/env/validators.ts @@ -75,7 +75,7 @@ const stringValidator = const numberValidator = ({ gt, min, lt, max, int }: NumberSchema): ValueValidator => (input) => { - const num = parseFloat(input ?? ''); + const num = Number.parseFloat(input ?? ''); if (isNaN(num)) { return { ok: false, diff --git a/packages/astro/src/events/session.ts b/packages/astro/src/events/session.ts index 8bc400d7401b..138c027536ad 100644 --- a/packages/astro/src/events/session.ts +++ b/packages/astro/src/events/session.ts @@ -130,7 +130,7 @@ export function eventCliSession( flags?: Record, ): { eventName: string; payload: EventPayload }[] { // Filter out yargs default `_` flag which is the cli command - const cliFlags = flags ? Object.keys(flags).filter((name) => name != '_') : undefined; + const cliFlags = flags ? Object.keys(flags).filter((name) => name !== '_') : undefined; const payload: EventPayload = { cliCommand, diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index 058758627311..af52a673dd16 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -54,7 +54,7 @@ export function validateSupportedFeatures( adapterName, logger, 'hybridOutput', - () => settings.config.output == 'static' && settings.buildOutput === 'server', + () => settings.config.output === 'static' && settings.buildOutput === 'server', ); validationResult.serverOutput = validateSupportKind( diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 306d147a46c1..294a51caef37 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -303,7 +303,7 @@ export async function runHookConfigSetup({ // though accessible to integration authors if discovered. function addPageExtension(...input: (string | string[])[]) { - const exts = (input.flat(Infinity) as string[]).map( + const exts = (input.flat(Number.POSITIVE_INFINITY) as string[]).map( (ext) => `.${ext.replace(/^\./, '')}`, ); updatedSettings.pageExtensions.push(...exts); diff --git a/packages/astro/src/manifest/virtual-module.ts b/packages/astro/src/manifest/virtual-module.ts index 411bce3e0a4c..627008a1082f 100644 --- a/packages/astro/src/manifest/virtual-module.ts +++ b/packages/astro/src/manifest/virtual-module.ts @@ -66,7 +66,7 @@ export { base, i18n, trailingSlash, site, compressHTML, build, image }; `; return { code }; } - if (id == RESOLVED_VIRTUAL_SERVER_ID) { + if (id === RESOLVED_VIRTUAL_SERVER_ID) { if (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { throw new AstroError({ ...AstroErrorData.ServerOnlyModule, diff --git a/packages/astro/src/runtime/client/dev-toolbar/apps/audit/rules/perf.ts b/packages/astro/src/runtime/client/dev-toolbar/apps/audit/rules/perf.ts index e8b822428fed..ca6c2f922bb7 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/apps/audit/rules/perf.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/apps/audit/rules/perf.ts @@ -113,7 +113,7 @@ export const perf: AuditRuleWithSelector[] = [ const serverRenderTime = element.getAttribute('server-render-time'); if (!serverRenderTime) return false; - const renderingTime = parseFloat(serverRenderTime); + const renderingTime = Number.parseFloat(serverRenderTime); if (Number.isNaN(renderingTime)) return false; return renderingTime > 500; @@ -131,7 +131,7 @@ export const perf: AuditRuleWithSelector[] = [ const clientRenderTime = element.getAttribute('client-render-time'); if (!clientRenderTime) return false; - const renderingTime = parseFloat(clientRenderTime); + const renderingTime = Number.parseFloat(clientRenderTime); if (Number.isNaN(renderingTime)) return false; return renderingTime > 500; @@ -141,7 +141,7 @@ export const perf: AuditRuleWithSelector[] = [ function getCleanRenderingTime(time: string | null) { if (!time) return 'unknown'; - const renderingTime = parseFloat(time); + const renderingTime = Number.parseFloat(time); if (Number.isNaN(renderingTime)) return 'unknown'; return renderingTime.toFixed(2) + 's'; diff --git a/packages/astro/src/runtime/client/dev-toolbar/apps/utils/icons.ts b/packages/astro/src/runtime/client/dev-toolbar/apps/utils/icons.ts index cddee0c263be..c8f5a79dfcd3 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/apps/utils/icons.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/apps/utils/icons.ts @@ -19,8 +19,7 @@ const categoryIcons = new Map( export function iconForIntegration(integration: Integration) { const icons = integration.categories .filter((category: string) => categoryIcons.has(category)) - .map((category: string) => categoryIcons.get(category)!) - .flat(); + .flatMap((category: string) => categoryIcons.get(category)!); return randomFromArray(icons); } diff --git a/packages/astro/src/runtime/server/astro-island.ts b/packages/astro/src/runtime/server/astro-island.ts index d9a13146e510..9adfb94ed53f 100644 --- a/packages/astro/src/runtime/server/astro-island.ts +++ b/packages/astro/src/runtime/server/astro-island.ts @@ -29,7 +29,7 @@ declare const Astro: { 8: (value) => new Uint8Array(value), 9: (value) => new Uint16Array(value), 10: (value) => new Uint32Array(value), - 11: (value) => Infinity * value, + 11: (value) => Number.POSITIVE_INFINITY * value, }; // Not using JSON.parse reviver because it's bottom-up but we want top-down diff --git a/packages/astro/src/runtime/server/jsx.ts b/packages/astro/src/runtime/server/jsx.ts index 664fb916a4c3..147eee59ef08 100644 --- a/packages/astro/src/runtime/server/jsx.ts +++ b/packages/astro/src/runtime/server/jsx.ts @@ -187,7 +187,7 @@ async function renderElement( ) { return markHTMLString( `<${tag}${spreadAttributes(props)}${markHTMLString( - (children == null || children == '') && voidElementNames.test(tag) + (children == null || children === '') && voidElementNames.test(tag) ? `/>` : `>${ children == null ? '' : await renderJSX(result, prerenderElementChildren(tag, children)) diff --git a/packages/astro/src/runtime/server/render/head.ts b/packages/astro/src/runtime/server/render/head.ts index 05003966c495..48ce553dea98 100644 --- a/packages/astro/src/runtime/server/render/head.ts +++ b/packages/astro/src/runtime/server/render/head.ts @@ -10,7 +10,7 @@ const uniqueElements = (item: any, index: number, all: any[]) => { const props = JSON.stringify(item.props); const children = item.children; return ( - index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children == children) + index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children === children) ); }; diff --git a/packages/astro/src/runtime/server/render/util.ts b/packages/astro/src/runtime/server/render/util.ts index 7123b47a87e9..d77db44508ab 100644 --- a/packages/astro/src/runtime/server/render/util.ts +++ b/packages/astro/src/runtime/server/render/util.ts @@ -179,7 +179,7 @@ export function renderElement( children = defineScriptVars(defineVars) + '\n' + children; } } - if ((children == null || children == '') && voidElementNames.test(name)) { + if ((children == null || children === '') && voidElementNames.test(name)) { return `<${name}${internalSpreadAttributes(props, shouldEscape, name)}>`; } return `<${name}${internalSpreadAttributes(props, shouldEscape, name)}>${children}`; diff --git a/packages/astro/src/runtime/server/serialize.ts b/packages/astro/src/runtime/server/serialize.ts index b7cc96666853..5faa861f50f8 100644 --- a/packages/astro/src/runtime/server/serialize.ts +++ b/packages/astro/src/runtime/server/serialize.ts @@ -95,10 +95,10 @@ function convertToSerializedForm( if (value !== null && typeof value === 'object') { return [PROP_TYPE.Value, serializeObject(value, metadata, parents)]; } - if (value === Infinity) { + if (value === Number.POSITIVE_INFINITY) { return [PROP_TYPE.Infinity, 1]; } - if (value === -Infinity) { + if (value === Number.NEGATIVE_INFINITY) { return [PROP_TYPE.Infinity, -1]; } if (value === undefined) { diff --git a/packages/astro/test/core-image-layout.test.js b/packages/astro/test/core-image-layout.test.js index c793e5e42e58..018e8ee836f2 100755 --- a/packages/astro/test/core-image-layout.test.js +++ b/packages/astro/test/core-image-layout.test.js @@ -183,8 +183,8 @@ describe('astro:image:layout', () => { const srcset = parseSrcset($img.attr('srcset')); for (const { url } of srcset) { const params = new URL(url, 'https://example.com').searchParams; - const width = parseInt(params.get('w')); - const height = parseInt(params.get('h')); + const width = Number.parseInt(params.get('w')); + const height = Number.parseInt(params.get('h')); assert.equal(width / height, aspectRatio); } }); @@ -382,8 +382,8 @@ describe('astro:image:layout', () => { it('maintains original aspect ratio', () => { let $img = $('#picture-fallback img'); - const width = parseInt($img.attr('width')); - const height = parseInt($img.attr('height')); + const width = Number.parseInt($img.attr('width')); + const height = Number.parseInt($img.attr('height')); const imageAspectRatio = width / height; const originalAspectRatio = originalWidth / originalHeight; diff --git a/packages/astro/test/serialize.test.js b/packages/astro/test/serialize.test.js index ced02ee3177c..cd734cca4a31 100644 --- a/packages/astro/test/serialize.test.js +++ b/packages/astro/test/serialize.test.js @@ -90,7 +90,7 @@ describe('serialize', () => { assert.equal(serializeProps(input), output); }); it('serializes Infinity and -Infinity', () => { - const input = { a: Infinity, b: -Infinity }; + const input = { a: Number.POSITIVE_INFINITY, b: Number.NEGATIVE_INFINITY }; const output = `{"a":[11,1],"b":[11,-1]}`; assert.equal(serializeProps(input), output); }); diff --git a/packages/astro/test/streaming.test.js b/packages/astro/test/streaming.test.js index cbd4fa4f4b46..ad71119d1e61 100644 --- a/packages/astro/test/streaming.test.js +++ b/packages/astro/test/streaming.test.js @@ -144,7 +144,7 @@ describe('Streaming disabled', () => { assert.equal(response.status, 200); assert.equal(response.headers.get('content-type'), 'text/html'); assert.equal(response.headers.has('content-length'), true); - assert.equal(parseInt(response.headers.get('content-length')) > 0, true); + assert.equal(Number.parseInt(response.headers.get('content-length')) > 0, true); const html = await response.text(); const $ = cheerio.load(html); diff --git a/packages/create-astro/create-astro.mjs b/packages/create-astro/create-astro.mjs index 6a7359eb4f0c..271abafcd20f 100755 --- a/packages/create-astro/create-astro.mjs +++ b/packages/create-astro/create-astro.mjs @@ -3,7 +3,7 @@ 'use strict'; const currentVersion = process.versions.node; -const requiredMajorVersion = parseInt(currentVersion.split('.')[0], 10); +const requiredMajorVersion = Number.parseInt(currentVersion.split('.')[0], 10); // TODO: remove once Stackblitz supports Node 22 const IS_STACKBLITZ = !!process.versions.webcontainer; const minimumMajorVersion = IS_STACKBLITZ ? 20 : 22; diff --git a/packages/create-astro/src/actions/context.ts b/packages/create-astro/src/actions/context.ts index 6a4867e4f184..44a6fbd27e7b 100644 --- a/packages/create-astro/src/actions/context.ts +++ b/packages/create-astro/src/actions/context.ts @@ -103,8 +103,8 @@ export async function getContext(argv: string[]): Promise { if (no) { yes = false; - if (install == undefined) install = false; - if (git == undefined) git = false; + if (install === undefined) install = false; + if (git === undefined) git = false; } skipHouston = diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index da535c5fed66..3e4cca65d990 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -201,7 +201,7 @@ function astroDBIntegration(options?: AstroDBConfig): AstroIntegration { await executeSeedFile({ fileUrl, environment }); }; }, - 'astro:build:done': async ({}) => { + 'astro:build:done': async () => { await tempViteServer?.close(); }, }, diff --git a/packages/integrations/alpinejs/test/test-utils.js b/packages/integrations/alpinejs/test/test-utils.js index dbc133ec3471..f18bf6add250 100644 --- a/packages/integrations/alpinejs/test/test-utils.js +++ b/packages/integrations/alpinejs/test/test-utils.js @@ -32,6 +32,7 @@ function testFactory(inlineConfig) { let fixture; const test = testBase.extend({ + // biome-ignore lint/correctness/noEmptyPattern: playwright needs this astro: async ({}, use) => { fixture = fixture || (await loadFixture(inlineConfig)); await use(fixture); diff --git a/packages/integrations/cloudflare/src/utils/image-binding-transform.ts b/packages/integrations/cloudflare/src/utils/image-binding-transform.ts index bcfa74b74f0c..df28e429e0c3 100644 --- a/packages/integrations/cloudflare/src/utils/image-binding-transform.ts +++ b/packages/integrations/cloudflare/src/utils/image-binding-transform.ts @@ -32,8 +32,8 @@ export async function transform( return ( await input .transform({ - width: url.searchParams.has('w') ? parseInt(url.searchParams.get('w')!) : undefined, - height: url.searchParams.has('h') ? parseInt(url.searchParams.get('h')!) : undefined, + width: url.searchParams.has('w') ? Number.parseInt(url.searchParams.get('w')!) : undefined, + height: url.searchParams.has('h') ? Number.parseInt(url.searchParams.get('h')!) : undefined, // `quality` is documented, but doesn't appear to work in manual testing... // quality: url.searchParams.get('q'), fit: url.searchParams.get('fit') as ImageTransform['fit'], diff --git a/packages/integrations/markdoc/src/html/css/parse-inline-styles.ts b/packages/integrations/markdoc/src/html/css/parse-inline-styles.ts index fa3217c89cc1..f5f01ff35263 100644 --- a/packages/integrations/markdoc/src/html/css/parse-inline-styles.ts +++ b/packages/integrations/markdoc/src/html/css/parse-inline-styles.ts @@ -180,12 +180,12 @@ export function parseInlineStyles(style, options) { */ function comment() { const pos = position(); - if (FORWARD_SLASH != style.charAt(0) || ASTERISK != style.charAt(1)) return; + if (FORWARD_SLASH !== style.charAt(0) || ASTERISK !== style.charAt(1)) return; let i = 2; while ( - EMPTY_STRING != style.charAt(i) && - (ASTERISK != style.charAt(i) || FORWARD_SLASH != style.charAt(i + 1)) + EMPTY_STRING !== style.charAt(i) && + (ASTERISK !== style.charAt(i) || FORWARD_SLASH !== style.charAt(i + 1)) ) { ++i; } diff --git a/packages/integrations/markdoc/src/html/transform/html-token-transform.ts b/packages/integrations/markdoc/src/html/transform/html-token-transform.ts index 10b4c030c697..d02ae0fce31b 100644 --- a/packages/integrations/markdoc/src/html/transform/html-token-transform.ts +++ b/packages/integrations/markdoc/src/html/transform/html-token-transform.ts @@ -41,7 +41,7 @@ export function htmlTokenTransform(tokenizer: Tokenizer, tokens: Token[]): Token // if the given token is a 'text' token and its trimmed content is the same as the pre-tokenized text buffer, use the original // text buffer instead to preserve leading/trailing whitespace that is lost during tokenization of pure text content if (tok.type === 'text') { - if (tok.content.trim() == textBuffer.trim()) { + if (tok.content.trim() === textBuffer.trim()) { tok.content = textBuffer; } } diff --git a/packages/integrations/partytown/src/sirv.ts b/packages/integrations/partytown/src/sirv.ts index f0ae94026c22..bf5268f2c644 100644 --- a/packages/integrations/partytown/src/sirv.ts +++ b/packages/integrations/partytown/src/sirv.ts @@ -111,8 +111,8 @@ function send(req, res, file, stats, headers) { if (req.headers.range) { code = 206; let [x, y] = req.headers.range.replace('bytes=', '').split('-'); - let end = (opts.end = parseInt(y, 10) || stats.size - 1); - let start = (opts.start = parseInt(x, 10) || 0); + let end = (opts.end = Number.parseInt(y, 10) || stats.size - 1); + let start = (opts.start = Number.parseInt(x, 10) || 0); if (start >= stats.size || end >= stats.size) { res.setHeader('Content-Range', `bytes */${stats.size}`); diff --git a/packages/integrations/preact/src/server.ts b/packages/integrations/preact/src/server.ts index 908f0125b1c8..fde181343c75 100644 --- a/packages/integrations/preact/src/server.ts +++ b/packages/integrations/preact/src/server.ts @@ -35,7 +35,7 @@ async function check( // There are edge cases (SolidJS) where Preact *might* render a string, // but components would be // It also might render an empty string. - return html == '' ? false : !html.includes(''); + return html === '' ? false : !html.includes(''); } catch { return false; } finally { diff --git a/packages/integrations/react/src/version.ts b/packages/integrations/react/src/version.ts index 164b065b5e90..beecabc7832b 100644 --- a/packages/integrations/react/src/version.ts +++ b/packages/integrations/react/src/version.ts @@ -6,7 +6,7 @@ export type ReactVersionConfig = (typeof versionsConfig)[SupportedReactVersion]; export function getReactMajorVersion(): number { const matches = /\d+\./.exec(ReactVersion); if (!matches) { - return NaN; + return Number.NaN; } return Number(matches[0]); } diff --git a/packages/integrations/vercel/src/image/build-service.ts b/packages/integrations/vercel/src/image/build-service.ts index 7e40b85be172..88f6a2d0aae8 100644 --- a/packages/integrations/vercel/src/image/build-service.ts +++ b/packages/integrations/vercel/src/image/build-service.ts @@ -113,7 +113,7 @@ const service: ExternalImageService = { if (typeof density === 'number') { return density; } else { - return parseFloat(density); + return Number.parseFloat(density); } }); diff --git a/packages/integrations/vercel/src/index.ts b/packages/integrations/vercel/src/index.ts index e6db88a9c907..a85c7dbedf4d 100644 --- a/packages/integrations/vercel/src/index.ts +++ b/packages/integrations/vercel/src/index.ts @@ -438,18 +438,15 @@ export default function vercelAdapter({ const isrConfig = typeof isr === 'object' ? isr : {}; await builder.buildServerlessFolder(entryFile, NODE_PATH, _config.root); if (isrConfig.exclude?.length) { - const expandedExclusions = isrConfig.exclude.reduce((acc, exclusion) => { + const expandedExclusions = isrConfig.exclude.flatMap((exclusion) => { if (exclusion instanceof RegExp) { - return [ - ...acc, - ...routes - .filter((route) => exclusion.test(route.pattern)) - .map((route) => route.pattern), - ]; + return routes + .filter((route) => exclusion.test(route.pattern)) + .map((route) => route.pattern); } - return [...acc, exclusion]; - }, []); + return [exclusion]; + }); const dest = _middlewareEntryPoint ? MIDDLEWARE_PATH : NODE_PATH; for (const route of expandedExclusions) { diff --git a/packages/integrations/vercel/test/image.test.js b/packages/integrations/vercel/test/image.test.js index 32ed87abed0f..c3ae7b60ff01 100644 --- a/packages/integrations/vercel/test/image.test.js +++ b/packages/integrations/vercel/test/image.test.js @@ -57,7 +57,7 @@ describe('Image', () => { const urls = srcset.split(', ').map((entry) => entry.split(' ')[0]); const widthsFromUrls = urls.map((url) => { const urlObj = new URL(url, 'http://localhost'); - return parseInt(urlObj.searchParams.get('w'), 10); + return Number.parseInt(urlObj.searchParams.get('w'), 10); }); // The configured sizes are [640, 750, 828, 1080, 1200, 1920, 2048, 3840] diff --git a/packages/language-tools/language-server/test/content-intellisense/caching.test.ts b/packages/language-tools/language-server/test/content-intellisense/caching.test.ts index 0f455b4f8b34..92c9cdd41751 100644 --- a/packages/language-tools/language-server/test/content-intellisense/caching.test.ts +++ b/packages/language-tools/language-server/test/content-intellisense/caching.test.ts @@ -11,7 +11,7 @@ const contentSchemaPath = path.resolve(fixtureDir, '.astro', 'collections', 'cac describe( 'Content Intellisense - Caching', - { skip: parseInt(process.versions.node) === 20 }, + { skip: Number.parseInt(process.versions.node) === 20 }, async () => { let languageServer: LanguageServer; diff --git a/packages/language-tools/language-server/test/content-intellisense/completions.test.ts b/packages/language-tools/language-server/test/content-intellisense/completions.test.ts index e69234aac17b..e6bbf0d60d94 100644 --- a/packages/language-tools/language-server/test/content-intellisense/completions.test.ts +++ b/packages/language-tools/language-server/test/content-intellisense/completions.test.ts @@ -7,7 +7,7 @@ import { fixtureDir } from '../utils.ts'; describe( 'Content Intellisense - Completions', - { skip: parseInt(process.versions.node) === 20 }, + { skip: Number.parseInt(process.versions.node) === 20 }, async () => { let languageServer: LanguageServer; diff --git a/packages/language-tools/language-server/test/content-intellisense/definitions.test.ts b/packages/language-tools/language-server/test/content-intellisense/definitions.test.ts index 9d31ffdf630e..2772d67573a4 100644 --- a/packages/language-tools/language-server/test/content-intellisense/definitions.test.ts +++ b/packages/language-tools/language-server/test/content-intellisense/definitions.test.ts @@ -8,7 +8,7 @@ import { fixtureDir } from '../utils.ts'; describe( 'Content Intellisense - Go To Everywhere', - { skip: parseInt(process.versions.node) === 20 }, + { skip: Number.parseInt(process.versions.node) === 20 }, async () => { let languageServer: LanguageServer; diff --git a/packages/language-tools/language-server/test/content-intellisense/diagnostics.test.ts b/packages/language-tools/language-server/test/content-intellisense/diagnostics.test.ts index 0d942408b56e..bea456c12b4b 100644 --- a/packages/language-tools/language-server/test/content-intellisense/diagnostics.test.ts +++ b/packages/language-tools/language-server/test/content-intellisense/diagnostics.test.ts @@ -9,7 +9,7 @@ import { fixtureDir } from '../utils.ts'; // TODO: We can't sync the fixture with these mistakes at all, as such we can't run these tests. describe.skip( 'Content Intellisense - Diagnostics', - { skip: parseInt(process.versions.node) === 20 }, + { skip: Number.parseInt(process.versions.node) === 20 }, async () => { let languageServer: LanguageServer; diff --git a/packages/language-tools/language-server/test/content-intellisense/hover.test.ts b/packages/language-tools/language-server/test/content-intellisense/hover.test.ts index 077c968f35d2..30d91b18247d 100644 --- a/packages/language-tools/language-server/test/content-intellisense/hover.test.ts +++ b/packages/language-tools/language-server/test/content-intellisense/hover.test.ts @@ -7,7 +7,7 @@ import { fixtureDir } from '../utils.ts'; describe( 'Content Intellisense - Hover', - { skip: parseInt(process.versions.node) === 20 }, + { skip: Number.parseInt(process.versions.node) === 20 }, async () => { let languageServer: LanguageServer; diff --git a/packages/language-tools/language-server/test/setup.js b/packages/language-tools/language-server/test/setup.js index 7227245308c1..9e4783ec3185 100644 --- a/packages/language-tools/language-server/test/setup.js +++ b/packages/language-tools/language-server/test/setup.js @@ -12,7 +12,7 @@ const fixtureDir = path.join(__dirname, './fixture'); export default async function setup() { // We only run the tests that require sync on Node.js versions other than 20 because the language server supports // a lower minimum version than Astro itself due to our lowest supported VS Code version, which mean we can't run Astro - if (parseInt(process.versions.node) !== 20) { + if (Number.parseInt(process.versions.node) !== 20) { const res = await cli('sync', '--root', fixtureDir).getResult(); if (res.exitCode !== 0) { throw new Error(res.stderr); diff --git a/packages/language-tools/vscode/test/grammar/test.mjs b/packages/language-tools/vscode/test/grammar/test.mjs index 9f3b46441914..239003e50efe 100644 --- a/packages/language-tools/vscode/test/grammar/test.mjs +++ b/packages/language-tools/vscode/test/grammar/test.mjs @@ -42,7 +42,7 @@ async function snapShotTest() { '-s', 'source.astro', './test/grammar/fixtures/**/*.astro', - ...allGrammars.reduce((/** @type string[] */ previous, path) => [...previous, '-g', path], []), + ...allGrammars.flatMap((path) => ['-g', path]), ...extraArgs, ].map((arg) => (isWindows && arg.includes(' ') ? `"${arg}"` : arg)); diff --git a/packages/upgrade/upgrade.mjs b/packages/upgrade/upgrade.mjs index 6a7359eb4f0c..271abafcd20f 100755 --- a/packages/upgrade/upgrade.mjs +++ b/packages/upgrade/upgrade.mjs @@ -3,7 +3,7 @@ 'use strict'; const currentVersion = process.versions.node; -const requiredMajorVersion = parseInt(currentVersion.split('.')[0], 10); +const requiredMajorVersion = Number.parseInt(currentVersion.split('.')[0], 10); // TODO: remove once Stackblitz supports Node 22 const IS_STACKBLITZ = !!process.versions.webcontainer; const minimumMajorVersion = IS_STACKBLITZ ? 20 : 22;