diff options
65 files changed, 207 insertions, 139 deletions
diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c3e825cfc..a5e9f80d8 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -7,13 +7,14 @@ module.exports = { 'plugin:@typescript-eslint/recommended-type-checked', 'plugin:@typescript-eslint/stylistic-type-checked', 'prettier', + 'plugin:regexp/recommended', ], parser: '@typescript-eslint/parser', parserOptions: { project: ['./packages/*/tsconfig.json', './tsconfig.eslint.json'], tsconfigRootDir: __dirname, }, - plugins: ['@typescript-eslint', 'prettier', 'no-only-tests'], + plugins: ['@typescript-eslint', 'prettier', 'no-only-tests', 'regexp'], rules: { // These off/configured-differently-by-default rules fit well for us '@typescript-eslint/switch-exhaustiveness-check': 'error', @@ -72,6 +73,9 @@ module.exports = { // 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 + 'regexp/use-ignore-case': 'off', }, overrides: [ { diff --git a/package.json b/package.json index f85212d92..d26d4fac2 100644 --- a/package.json +++ b/package.json @@ -59,12 +59,13 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-regexp": "^2.2.0", + "globby": "^14.0.0", "only-allow": "^1.1.1", "organize-imports-cli": "^0.10.0", "prettier": "^3.1.0", "prettier-plugin-astro": "^0.12.2", "tiny-glob": "^0.2.9", - "globby": "^14.0.0", "turbo": "^1.10.12", "typescript": "~5.2.2" }, diff --git a/packages/astro-prism/src/plugin.ts b/packages/astro-prism/src/plugin.ts index cbee66c33..a50b9d7ef 100644 --- a/packages/astro-prism/src/plugin.ts +++ b/packages/astro-prism/src/plugin.ts @@ -16,6 +16,7 @@ export function addAstro(Prism: typeof import('prismjs')) { let script = Prism.util.clone(Prism.languages[scriptLang]); + // eslint-disable-next-line regexp/no-useless-assertions let space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source; let braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source; let spread = /(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source; @@ -39,13 +40,13 @@ export function addAstro(Prism: typeof import('prismjs')) { Prism.languages.astro = Prism.languages.extend('markup', script); (Prism.languages.astro as any).tag.pattern = re( - /<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/ + /<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/ .source ); - (Prism.languages.astro as any).tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i; + (Prism.languages.astro as any).tag.inside['tag'].pattern = /^<\/?[^\s>/]*/; (Prism.languages.astro as any).tag.inside['attr-value'].pattern = - /=(?!\{)(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s'">]+)/i; + /=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/; (Prism.languages.astro as any).tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/; (Prism.languages.astro as any).tag.inside['comment'] = script['comment']; @@ -71,7 +72,7 @@ export function addAstro(Prism: typeof import('prismjs')) { pattern: re(/=<BRACES>/.source), inside: { 'script-punctuation': { - pattern: /^=(?={)/, + pattern: /^=(?=\{)/, alias: 'punctuation', }, rest: Prism.languages.astro, diff --git a/packages/astro-rss/src/util.ts b/packages/astro-rss/src/util.ts index bc1589780..1e49b3d77 100644 --- a/packages/astro-rss/src/util.ts +++ b/packages/astro-rss/src/util.ts @@ -10,10 +10,10 @@ export function createCanonicalURL( let pathname = url.replace(/\/index.html$/, ''); // index.html is not canonical if (trailingSlash === false) { // remove the trailing slash - pathname = pathname.replace(/(\/+)?$/, ''); + pathname = pathname.replace(/\/*$/, ''); } else if (!getUrlExtension(url)) { // add trailing slash if there’s no extension or `trailingSlash` is true - pathname = pathname.replace(/(\/+)?$/, '/'); + pathname = pathname.replace(/\/*$/, '/'); } pathname = pathname.replace(/\/+/g, '/'); // remove duplicate slashes (URL() won’t) diff --git a/packages/astro/src/assets/services/vendor/squoosh/codecs.ts b/packages/astro/src/assets/services/vendor/squoosh/codecs.ts index 80aae7520..e4705e10b 100644 --- a/packages/astro/src/assets/services/vendor/squoosh/codecs.ts +++ b/packages/astro/src/assets/services/vendor/squoosh/codecs.ts @@ -291,6 +291,8 @@ export const codecs = { avif: { name: 'AVIF', extension: 'avif', + // Disable eslint rule to not touch the original code + // eslint-disable-next-line no-control-regex, regexp/control-character-escape detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/], dec: () => instantiateEmscriptenWasm(avifDec as DecodeModuleFactory, avifDecWasm), @@ -321,6 +323,8 @@ export const codecs = { oxipng: { name: 'OxiPNG', extension: 'png', + // Disable eslint rule to not touch the original code + // eslint-disable-next-line no-control-regex, regexp/control-character-escape detectors: [/^\x89PNG\x0D\x0A\x1A\x0A/], dec: async () => { await pngEncDecInit() diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index b8cb1949c..e1b64eb18 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -384,11 +384,11 @@ const toIdent = (name: string) => { const ident = name .trim() // Remove astro or (astrojs) prefix and suffix - .replace(/[-_\.\/]?astro(?:js)?[-_\.]?/g, '') + .replace(/[-_./]?astro(?:js)?[-_.]?/g, '') // drop .js suffix .replace(/\.js/, '') // convert to camel case - .replace(/(?:[\.\-\_\/]+)([a-zA-Z])/g, (_, w) => w.toUpperCase()) + .replace(/[.\-_/]+([a-zA-Z])/g, (_, w) => w.toUpperCase()) // drop invalid first characters .replace(/^[^a-zA-Z$_]+/, ''); return `${ident[0].toLowerCase()}${ident.slice(1)}`; diff --git a/packages/astro/src/core/build/css-asset-name.ts b/packages/astro/src/core/build/css-asset-name.ts index 29fc14294..64852b366 100644 --- a/packages/astro/src/core/build/css-asset-name.ts +++ b/packages/astro/src/core/build/css-asset-name.ts @@ -103,7 +103,7 @@ function getFirstParentId(parents: [ModuleInfo, number, number][]) { return parents[0]?.[0].id; } -const charsToReplaceRe = /[.\[\]]/g; +const charsToReplaceRe = /[.[\]]/g; const underscoresRe = /_+/g; /** * Prettify base names so they're easier to read: diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index e5a1c1b06..6b5413583 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -19,7 +19,7 @@ import type { StaticBuildOptions } from '../types.js'; import { normalizeTheLocale } from '../../../i18n/index.js'; const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@'; -const replaceExp = new RegExp(`['"](${manifestReplace})['"]`, 'g'); +const replaceExp = new RegExp(`['"]${manifestReplace}['"]`, 'g'); export const SSR_MANIFEST_VIRTUAL_MODULE_ID = '@astrojs-manifest'; export const RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID = '\0' + SSR_MANIFEST_VIRTUAL_MODULE_ID; diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index a1278c803..3fc40035d 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -524,7 +524,7 @@ export function makeAstroPageEntryPointFileName( const name = route?.route ?? pageModuleId; return `pages${name .replace(/\/$/, '/index') - .replaceAll(/[\[\]]/g, '_') + .replaceAll(/[[\]]/g, '_') .replaceAll('...', '---')}.astro.mjs`; } diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 0b0c7d47f..b2de6afb5 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -90,7 +90,7 @@ export async function createVite( pkgJson.keywords?.includes('astro') || pkgJson.keywords?.includes('astro-component') || // Attempt: package is named `astro-something` or `@scope/astro-something`. ✅ Likely a community package - /^(@[^\/]+\/)?astro\-/.test(pkgJson.name) + /^(?:@[^/]+\/)?astro-/.test(pkgJson.name) ); }, isFrameworkPkgByName(pkgName) { diff --git a/packages/astro/src/core/dev/restart.ts b/packages/astro/src/core/dev/restart.ts index 23e6af369..7a1b15ed0 100644 --- a/packages/astro/src/core/dev/restart.ts +++ b/packages/astro/src/core/dev/restart.ts @@ -29,8 +29,8 @@ async function createRestartedContainer( return newContainer; } -const configRE = new RegExp(`.*astro\.config\.((mjs)|(cjs)|(js)|(ts))$`); -const preferencesRE = new RegExp(`.*\.astro\/settings\.json$`); +const configRE = /.*astro.config.(?:mjs|cjs|js|ts)$/; +const preferencesRE = /.*\.astro\/settings.json$/; export function shouldRestartContainer( { settings, inlineConfig, restartInFlight }: Container, diff --git a/packages/astro/src/core/errors/dev/utils.ts b/packages/astro/src/core/errors/dev/utils.ts index cda9e4227..c391e462b 100644 --- a/packages/astro/src/core/errors/dev/utils.ts +++ b/packages/astro/src/core/errors/dev/utils.ts @@ -132,7 +132,7 @@ export function collectErrorMetadata(e: any, rootFolder?: URL | undefined): Erro function generateHint(err: ErrorWithMetadata): string | undefined { const commonBrowserAPIs = ['document', 'window']; - if (/Unknown file extension \"\.(jsx|vue|svelte|astro|css)\" for /.test(err.message)) { + if (/Unknown file extension "\.(?:jsx|vue|svelte|astro|css)" for /.test(err.message)) { return 'You likely need to add this package to `vite.ssr.noExternal` in your astro config file.'; } else if (commonBrowserAPIs.some((api) => err.toString().includes(api))) { const hint = `Browser APIs are not available on the server. @@ -172,10 +172,12 @@ function collectInfoFromStacktrace(error: SSRError & { stack: string }): StackIn error.id || // TODO: this could be better, `src` might be something else stackText.split('\n').find((ln) => ln.includes('src') || ln.includes('node_modules')); + // Disable eslint as we're not sure how to improve this regex yet + // eslint-disable-next-line regexp/no-super-linear-backtracking const source = possibleFilePath?.replace(/^[^(]+\(([^)]+).*$/, '$1').replace(/^\s+at\s+/, ''); - let file = source?.replace(/(:[0-9]+)/g, ''); - const location = /:([0-9]+):([0-9]+)/g.exec(source!) ?? []; + let file = source?.replace(/:\d+/g, ''); + const location = /:(\d+):(\d+)/.exec(source!) ?? []; const line = location[1]; const column = location[2]; @@ -195,8 +197,8 @@ function collectInfoFromStacktrace(error: SSRError & { stack: string }): StackIn // Derive plugin from stack (if possible) if (!stackInfo.plugin) { stackInfo.plugin = - /withastro\/astro\/packages\/integrations\/([\w-]+)/gim.exec(stackText)?.at(1) || - /(@astrojs\/[\w-]+)\/(server|client|index)/gim.exec(stackText)?.at(1) || + /withastro\/astro\/packages\/integrations\/([\w-]+)/i.exec(stackText)?.at(1) || + /(@astrojs\/[\w-]+)\/(server|client|index)/i.exec(stackText)?.at(1) || undefined; } @@ -208,7 +210,7 @@ function collectInfoFromStacktrace(error: SSRError & { stack: string }): StackIn function cleanErrorStack(stack: string) { return stack - .split(/\n/g) + .split(/\n/) .map((l) => l.replace(/\/@fs\//g, '/')) .join('\n'); } @@ -233,10 +235,10 @@ export function getDocsForError(err: ErrorWithMetadata): string | undefined { * Render a subset of Markdown to HTML or a CLI output */ export function renderErrorMarkdown(markdown: string, target: 'html' | 'cli') { - const linkRegex = /\[([^\[]+)\]\((.*)\)/gm; - const boldRegex = /\*\*(.+)\*\*/gm; - const urlRegex = / (\b(https?|ftp):\/\/[-A-Z0-9+&@#\\/%?=~_|!:,.;]*[-A-Z0-9+&@#\\/%=~_|])/gim; - const codeRegex = /`([^`]+)`/gim; + const linkRegex = /\[([^[]+)\]\((.*)\)/g; + const boldRegex = /\*\*(.+)\*\*/g; + const urlRegex = / ((?:https?|ftp):\/\/[-\w+&@#\\/%?=~|!:,.;]*[-\w+&@#\\/%=~|])/gi; + const codeRegex = /`([^`]+)`/g; if (target === 'html') { return escape(markdown) diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 11bc3570d..aaa71b02c 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -587,7 +587,7 @@ export const PrerenderDynamicEndpointPathCollide = { message: (pathname: string) => `Could not render \`${pathname}\` with an \`undefined\` param as the generated path will collide during prerendering. Prevent passing \`undefined\` as \`params\` for the endpoint's \`getStaticPaths()\` function, or add an additional extension to the endpoint's filename.`, hint: (filename: string) => - `Rename \`${filename}\` to \`${filename.replace(/\.(js|ts)/, (m) => `.json` + m)}\``, + `Rename \`${filename}\` to \`${filename.replace(/\.(?:js|ts)/, (m) => `.json` + m)}\``, } satisfies ErrorData; /** * @docs diff --git a/packages/astro/src/core/logger/vite.ts b/packages/astro/src/core/logger/vite.ts index 9604a68f0..ca803e0ff 100644 --- a/packages/astro/src/core/logger/vite.ts +++ b/packages/astro/src/core/logger/vite.ts @@ -12,15 +12,15 @@ function isAstroSrcFile(id: string | null) { } // capture "page reload some/Component.vue (additional info)" messages -const vitePageReloadMsg = /page reload (.*)( \(.*\))?/; +const vitePageReloadMsg = /page reload (.*)/; // capture "hmr update some/Component.vue" messages const viteHmrUpdateMsg = /hmr update (.*)/; // capture "vite v5.0.0 building SSR bundle for production..." and "vite v5.0.0 building for production..." messages const viteBuildMsg = /vite.*building.*for production/; // capture "\n Shortcuts" messages -const viteShortcutTitleMsg = /^\s*Shortcuts\s*$/s; +const viteShortcutTitleMsg = /^\s*Shortcuts\s*$/; // capture "press * + enter to ..." messages -const viteShortcutHelpMsg = /press\s+(.*?)\s+to\s+(.*)$/s; +const viteShortcutHelpMsg = /press (.+?) to (.+)$/s; export function createViteLogger( astroLogger: AstroLogger, @@ -39,8 +39,7 @@ export function createViteLogger( // Rewrite HMR page reload message if ((m = vitePageReloadMsg.exec(stripped))) { if (isAstroSrcFile(m[1])) return; - const extra = m[2] ?? ''; - astroLogger.info('watch', m[1] + extra); + astroLogger.info('watch', m[1]); } // Rewrite HMR update message else if ((m = viteHmrUpdateMsg.exec(stripped))) { diff --git a/packages/astro/src/core/messages.ts b/packages/astro/src/core/messages.ts index 5b424944f..f69b84697 100644 --- a/packages/astro/src/core/messages.ts +++ b/packages/astro/src/core/messages.ts @@ -225,7 +225,7 @@ export function formatConfigErrorMessage(err: ZodError) { // a regex to match the first line of a stack trace const STACK_LINE_REGEXP = /^\s+at /g; -const IRRELEVANT_STACK_REGEXP = /(node_modules|astro[\/\\]dist)/g; +const IRRELEVANT_STACK_REGEXP = /node_modules|astro[/\\]dist/g; function formatErrorStackTrace( err: Error | ErrorWithMetadata, showFullStacktrace: boolean 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 9ec940c68..a425807dc 100644 --- a/packages/astro/src/core/preview/vite-plugin-astro-preview.ts +++ b/packages/astro/src/core/preview/vite-plugin-astro-preview.ts @@ -7,7 +7,7 @@ import { notFoundTemplate, subpathNotUsedTemplate } from '../../template/4xx.js' import { cleanUrl } from '../../vite-plugin-utils/index.js'; import { stripBase } from './util.js'; -const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/; +const HAS_FILE_EXTENSION_REGEXP = /\.[^/]+$/; export function vitePluginAstroPreview(settings: AstroSettings): Plugin { const { base, outDir, trailingSlash } = settings.config; diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 3818f08c7..6a1064c05 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -43,13 +43,15 @@ function countOccurrences(needle: string, haystack: string) { function getParts(part: string, file: string) { const result: RoutePart[] = []; + // Disable eslint as we're not sure how to improve this regex yet + // eslint-disable-next-line regexp/no-super-linear-backtracking part.split(/\[(.+?\(.+?\)|.+?)\]/).map((str, i) => { if (!str) return; const dynamic = i % 2 === 1; const [, content] = dynamic ? /([^(]+)$/.exec(str) || [null, null] : [null, str]; - if (!content || (dynamic && !/^(\.\.\.)?[a-zA-Z0-9_$]+$/.test(content))) { + if (!content || (dynamic && !/^(?:\.\.\.)?[\w$]+$/.test(content))) { throw new Error(`Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/`); } diff --git a/packages/astro/src/events/error.ts b/packages/astro/src/events/error.ts index b3326091d..8b8e9767e 100644 --- a/packages/astro/src/events/error.ts +++ b/packages/astro/src/events/error.ts @@ -26,7 +26,7 @@ interface ConfigErrorEventPayload extends ErrorEventPayload { * This is only used for errors that do not come from us so we can get a basic * and anonymous idea of what the error is about. */ -const ANONYMIZE_MESSAGE_REGEX = /^(\w| )+/; +const ANONYMIZE_MESSAGE_REGEX = /^(?:\w| )+/; function anonymizeErrorMessage(msg: string): string | undefined { const matchedMessage = msg.match(ANONYMIZE_MESSAGE_REGEX); if (!matchedMessage?.[0]) { @@ -100,7 +100,7 @@ function getSafeErrorMessage(message: string | Function): string { .trim() .slice(1, -1) .replace( - /\${([^}]+)}/gm, + /\$\{([^}]+)\}/g, (str, match1) => `${match1 .split(/\.?(?=[A-Z])/) diff --git a/packages/astro/src/i18n/middleware.ts b/packages/astro/src/i18n/middleware.ts index 9fabff13a..5e9f17a6a 100644 --- a/packages/astro/src/i18n/middleware.ts +++ b/packages/astro/src/i18n/middleware.ts @@ -32,13 +32,6 @@ function pathnameHasLocale(pathname: string, locales: Locales): boolean { return false; } -type MiddlewareOptions = { - i18n: SSRManifest['i18n']; - base: SSRManifest['base']; - trailingSlash: SSRManifest['trailingSlash']; - buildFormat: SSRManifest['buildFormat']; -}; - export function createI18nMiddleware( i18n: SSRManifest['i18n'], base: SSRManifest['base'], diff --git a/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts b/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts index ac1624cd9..2b4943908 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts @@ -240,7 +240,7 @@ export const a11y: AuditRuleWithSelector[] = [ message: 'Screen readers already announce `img` elements as an image. There is no need to use words such as "image", "photo", and/or "picture".', selector: 'img[alt]:not([aria-hidden])', - match: (img: HTMLImageElement) => /\b(image|picture|photo)\b/i.test(img.alt), + match: (img: HTMLImageElement) => /\b(?:image|picture|photo)\b/i.test(img.alt), }, { code: 'a11y-incorrect-aria-attribute-type', diff --git a/packages/astro/src/runtime/server/render/component.ts b/packages/astro/src/runtime/server/render/component.ts index 3fcb6f2aa..6d2117545 100644 --- a/packages/astro/src/runtime/server/render/component.ts +++ b/packages/astro/src/runtime/server/render/component.ts @@ -65,8 +65,8 @@ function isHTMLComponent(Component: unknown) { return Component && (Component as any)['astro:html'] === true; } -const ASTRO_SLOT_EXP = /\<\/?astro-slot\b[^>]*>/g; -const ASTRO_STATIC_SLOT_EXP = /\<\/?astro-static-slot\b[^>]*>/g; +const ASTRO_SLOT_EXP = /<\/?astro-slot\b[^>]*>/g; +const ASTRO_STATIC_SLOT_EXP = /<\/?astro-static-slot\b[^>]*>/g; function removeStaticAstroSlot(html: string, supportsAstroStaticSlot: boolean) { const exp = supportsAstroStaticSlot ? ASTRO_STATIC_SLOT_EXP : ASTRO_SLOT_EXP; return html.replace(exp, ''); @@ -390,7 +390,7 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr } function sanitizeElementName(tag: string) { - const unsafe = /[&<>'"\s]+/g; + const unsafe = /[&<>'"\s]+/; if (!unsafe.test(tag)) return tag; return tag.trim().split(unsafe)[0].trim(); } diff --git a/packages/astro/src/runtime/server/render/util.ts b/packages/astro/src/runtime/server/render/util.ts index 0e3f41383..91883024e 100644 --- a/packages/astro/src/runtime/server/render/util.ts +++ b/packages/astro/src/runtime/server/render/util.ts @@ -7,17 +7,17 @@ import { HTMLString, markHTMLString } from '../escape.js'; export const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i; const htmlBooleanAttributes = - /^(allowfullscreen|async|autofocus|autoplay|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|itemscope)$/i; -const htmlEnumAttributes = /^(contenteditable|draggable|spellcheck|value)$/i; + /^(?:allowfullscreen|async|autofocus|autoplay|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|itemscope)$/i; +const htmlEnumAttributes = /^(?:contenteditable|draggable|spellcheck|value)$/i; // Note: SVG is case-sensitive! -const svgEnumAttributes = /^(autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i; +const svgEnumAttributes = /^(?:autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i; const STATIC_DIRECTIVES = new Set(['set:html', 'set:text']); // converts (most) arbitrary strings to valid JS identifiers const toIdent = (k: string) => - k.trim().replace(/(?:(?!^)\b\w|\s+|[^\w]+)/g, (match, index) => { - if (/[^\w]|\s/.test(match)) return ''; + k.trim().replace(/(?!^)\b\w|\s+|\W+/g, (match, index) => { + if (/\W/.test(match)) return ''; return index === 0 ? match : match.toUpperCase(); }); diff --git a/packages/astro/src/virtual-modules/content.ts b/packages/astro/src/virtual-modules/content.ts index a3e9a6828..8424f3b06 100644 --- a/packages/astro/src/virtual-modules/content.ts +++ b/packages/astro/src/virtual-modules/content.ts @@ -66,6 +66,7 @@ export const reference = noop; /** Run `astro sync` to generate high fidelity types */ export type CollectionKey = any; /** Run `astro sync` to generate high fidelity types */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars export type CollectionEntry<C> = any; /** Run `astro sync` to generate high fidelity types */ export type ContentCollectionKey = any; diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index f04e23641..0cc8a8193 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -108,7 +108,7 @@ export async function matchRoute( // Try without `.html` extensions or `index.html` in request URLs to mimic // routing behavior in production builds. This supports both file and directory // build formats, and is necessary based on how the manifest tracks build targets. - const altPathname = pathname.replace(/(index)?\.html$/, ''); + const altPathname = pathname.replace(/(?:index)?\.html$/, ''); if (altPathname !== pathname) { return await matchRoute(altPathname, manifestData, pipeline); } @@ -229,6 +229,8 @@ export async function handleRoute({ return ''; }, params: [], + // Disable eslint as we only want to generate an empty RegExp + // eslint-disable-next-line prefer-regex-literals pattern: new RegExp(''), prerender: false, segments: [], diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index 15fc9ba73..3a1d4c6f6 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -4,6 +4,7 @@ import { compile, type CompileProps, type CompileResult } from '../core/compile/ import type { Logger } from '../core/logger/core.js'; import { getFileInfo } from '../vite-plugin-utils/index.js'; import type { CompileMetadata } from './types.js'; +import { frontmatterRE } from './utils.js'; interface CompileAstroOption { compileProps: CompileProps; @@ -23,8 +24,6 @@ interface EnhanceCompilerErrorOptions { logger: Logger; } -const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms; - export async function compileAstro({ compileProps, astroFileToCompileMetadata, @@ -107,7 +106,7 @@ async function enhanceCompileError({ // Before throwing, it is better to verify the frontmatter here, and // let esbuild throw a more specific exception if the code is invalid. // If frontmatter is valid or cannot be parsed, then continue. - const scannedFrontmatter = FRONTMATTER_PARSE_REGEXP.exec(source); + const scannedFrontmatter = frontmatterRE.exec(source); if (scannedFrontmatter) { // Top-level return is not supported, so replace `return` with throw const frontmatter = scannedFrontmatter[1].replace(/\breturn\b/g, 'throw'); diff --git a/packages/astro/src/vite-plugin-astro/hmr.ts b/packages/astro/src/vite-plugin-astro/hmr.ts index 949fc1d6c..28527f90e 100644 --- a/packages/astro/src/vite-plugin-astro/hmr.ts +++ b/packages/astro/src/vite-plugin-astro/hmr.ts @@ -4,6 +4,7 @@ import type { HmrContext } from 'vite'; import type { Logger } from '../core/logger/core.js'; import type { CompileAstroResult } from './compile.js'; import type { CompileMetadata } from './types.js'; +import { frontmatterRE } from './utils.js'; export interface HandleHotUpdateOptions { logger: Logger; @@ -58,8 +59,10 @@ export async function handleHotUpdate( } } -const frontmatterRE = /^\-\-\-.*?^\-\-\-/ms; +// Disable eslint as we're not sure how to improve this regex yet +// eslint-disable-next-line regexp/no-super-linear-backtracking const scriptRE = /<script(?:\s.*?)?>.*?<\/script>/gs; +// eslint-disable-next-line regexp/no-super-linear-backtracking const styleRE = /<style(?:\s.*?)?>.*?<\/style>/gs; function isStyleOnlyChanged(oldCode: string, newCode: string) { diff --git a/packages/astro/src/vite-plugin-astro/utils.ts b/packages/astro/src/vite-plugin-astro/utils.ts new file mode 100644 index 000000000..8bb5b617a --- /dev/null +++ b/packages/astro/src/vite-plugin-astro/utils.ts @@ -0,0 +1 @@ +export const frontmatterRE = /^---(.*?)^---/ms; diff --git a/packages/astro/src/vite-plugin-env/index.ts b/packages/astro/src/vite-plugin-env/index.ts index 2e16cc5bf..6621d2179 100644 --- a/packages/astro/src/vite-plugin-env/index.ts +++ b/packages/astro/src/vite-plugin-env/index.ts @@ -13,7 +13,7 @@ interface EnvPluginOptions { const importMetaEnvOnlyRe = /\bimport\.meta\.env\b(?!\.)/; // Match valid JS variable names (identifiers), which accepts most alphanumeric characters, // except that the first character cannot be a number. -const isValidIdentifierRe = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/; +const isValidIdentifierRe = /^[_$a-zA-Z][\w$]*$/; // Match `export const prerender = import.meta.env.*` since `vite=plugin-scanner` requires // the `import.meta.env.*` to always be replaced. const exportConstPrerenderRe = /\bexport\s+const\s+prerender\s*=\s*import\.meta\.env\.(.+?)\b/; diff --git a/packages/astro/src/vite-plugin-head/index.ts b/packages/astro/src/vite-plugin-head/index.ts index 228e4e437..0350e9d77 100644 --- a/packages/astro/src/vite-plugin-head/index.ts +++ b/packages/astro/src/vite-plugin-head/index.ts @@ -9,7 +9,7 @@ import type { BuildInternals } from '../core/build/internal.js'; import { getAstroMetadata } from '../vite-plugin-astro/index.js'; // Detect this in comments, both in .astro components and in js/ts files. -const injectExp = /(^\/\/|\/\/!)\s*astro-head-inject/; +const injectExp = /(?:^\/\/|\/\/!)\s*astro-head-inject/; export default function configHeadVitePlugin(): vite.Plugin { let server: vite.ViteDevServer; diff --git a/packages/astro/src/vite-plugin-html/transform/utils.ts b/packages/astro/src/vite-plugin-html/transform/utils.ts index 88cb226e5..dd0ebcd14 100644 --- a/packages/astro/src/vite-plugin-html/transform/utils.ts +++ b/packages/astro/src/vite-plugin-html/transform/utils.ts @@ -1,7 +1,7 @@ import type { Element } from 'hast'; import type MagicString from 'magic-string'; -const splitAttrsTokenizer = /([\$\{\}\@a-z0-9_\:\-]*)\s*?=\s*?(['"]?)(.*?)\2\s+/gim; +const splitAttrsTokenizer = /([${}@\w:\-]*)\s*=\s*?(['"]?)(.*?)\2\s+/g; export function replaceAttribute(s: MagicString, node: Element, key: string, newValue: string) { splitAttrsTokenizer.lastIndex = 0; @@ -12,7 +12,7 @@ export function replaceAttribute(s: MagicString, node: Element, key: string, new if (offset === -1) return; const start = node.position!.start.offset! + offset; const tokens = text.slice(offset).split(splitAttrsTokenizer); - const token = tokens[0].replace(/([^>])(\>[\s\S]*$)/gim, '$1'); + const token = tokens[0].replace(/([^>])>[\s\S]*$/gm, '$1'); if (token.trim() === key) { const end = start + key.length; return s.overwrite(start, end, newValue, { contentOnly: true }); diff --git a/packages/astro/src/vite-plugin-scanner/scan.ts b/packages/astro/src/vite-plugin-scanner/scan.ts index 6c277567d..4e0e5fbfe 100644 --- a/packages/astro/src/vite-plugin-scanner/scan.ts +++ b/packages/astro/src/vite-plugin-scanner/scan.ts @@ -65,7 +65,7 @@ export async function scan( .trim(); // For a given export, check the value of the first non-whitespace token. // Basically extract the `true` from the statement `export const prerender = true` - const suffix = code.slice(endOfLocalName).trim().replace(/\=/, '').trim().split(/[;\n]/)[0]; + const suffix = code.slice(endOfLocalName).trim().replace(/=/, '').trim().split(/[;\n]/)[0]; if (prefix !== 'const' || !(isTruthy(suffix) || isFalsy(suffix))) { throw new AstroError({ ...AstroErrorData.InvalidPrerenderExport, diff --git a/packages/astro/src/vite-plugin-utils/index.ts b/packages/astro/src/vite-plugin-utils/index.ts index 6f672d7d9..21ade5b0a 100644 --- a/packages/astro/src/vite-plugin-utils/index.ts +++ b/packages/astro/src/vite-plugin-utils/index.ts @@ -17,7 +17,7 @@ export function getFileInfo(id: string, config: AstroConfig) { let fileUrl = fileId.includes('/pages/') ? fileId .replace(/^.*?\/pages\//, sitePathname) - .replace(/(\/index)?\.(md|markdown|mdown|mkdn|mkd|mdwn|md|astro)$/, '') + .replace(/(?:\/index)?\.(?:md|markdown|mdown|mkdn|mkd|mdwn|astro)$/, '') : undefined; if (fileUrl && config.trailingSlash === 'always') { fileUrl = appendForwardSlash(fileUrl); diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 3a4b9241d..c8c5af6d8 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -42,7 +42,7 @@ describe('CSS', function () { const classes = $('#class'); let scopedAttribute; for (const [key] of Object.entries(classes[0].attribs)) { - if (/^data-astro-cid-[A-Za-z0-9-]+/.test(key)) { + if (/^data-astro-cid-[A-Za-z\d-]+/.test(key)) { // Ema: this is ugly, but for reasons that I don't want to explore, cheerio // lower case the hash of the attribute scopedAttribute = key; @@ -72,7 +72,7 @@ describe('CSS', function () { it('Child inheritance', (done) => { for (const [key] of Object.entries($('#passed-in')[0].attribs)) { - if (/^data-astro-cid-[A-Za-z0-9-]+/.test(key)) { + if (/^data-astro-cid-[A-Za-z\d-]+/.test(key)) { done(); } } @@ -84,25 +84,25 @@ describe('CSS', function () { }); it('<style lang="sass">', async () => { - expect(bundledCSS).to.match(new RegExp('h1\\[data-astro-cid-[^{]*{color:#90ee90}')); + expect(bundledCSS).to.match(/h1\[data-astro-cid-[^{]*\{color:#90ee90\}/); }); it('<style lang="scss">', async () => { - expect(bundledCSS).to.match(new RegExp('h1\\[data-astro-cid-[^{]*{color:#ff69b4}')); + expect(bundledCSS).to.match(/h1\[data-astro-cid-[^{]*\{color:#ff69b4\}/); }); }); describe('Styles in src/', () => { it('.css', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-css[^{]*{color:gold')); + expect(bundledCSS).to.match(/.linked-css[^{]*\{color:gold/); }); it('.sass', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-sass[^{]*{color:#789')); + expect(bundledCSS).to.match(/.linked-sass[^{]*\{color:#789/); }); it('.scss', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-scss[^{]*{color:#6b8e23')); + expect(bundledCSS).to.match(/.linked-scss[^{]*\{color:#6b8e23/); }); }); @@ -118,7 +118,7 @@ describe('CSS', function () { it('.module.css', async () => { const el = $('#react-module-css'); const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + const moduleClass = classes.find((name) => /^_title_[\w-]+/.test(name)); // 1. check HTML expect(el.attr('class')).to.include(moduleClass); @@ -134,7 +134,7 @@ describe('CSS', function () { expect(el.attr('class')).to.include('react-sass-title'); // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy`)); + expect(bundledCSS).to.match(/.react-sass-title[^{]*\{font-family:fantasy/); }); it('.scss', async () => { @@ -144,13 +144,13 @@ describe('CSS', function () { expect(el.attr('class')).to.include('react-scss-title'); // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy`)); + expect(bundledCSS).to.match(/.react-scss-title[^{]*\{font-family:fantasy/); }); it('.module.sass', async () => { const el = $('#react-module-sass'); const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + const moduleClass = classes.find((name) => /^_title_[\w-]+/.test(name)); // 1. check HTML expect(el.attr('class')).to.include(moduleClass); @@ -162,7 +162,7 @@ describe('CSS', function () { it('.module.scss', async () => { const el = $('#react-module-scss'); const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + const moduleClass = classes.find((name) => /^_title_[\w-]+/.test(name)); // 1. check HTML expect(el.attr('class')).to.include(moduleClass); @@ -189,7 +189,7 @@ describe('CSS', function () { expect(el.attr('class')).to.include('vue-css'); // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-css[^{]*{font-family:cursive`)); + expect(bundledCSS).to.match(/.vue-css[^{]*\{font-family:cursive/); }); it('<style scoped>', async () => { @@ -210,7 +210,7 @@ describe('CSS', function () { it('<style module>', async () => { const el = $('#vue-modules'); const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + const moduleClass = classes.find((name) => /^_title_[\w-]+/.test(name)); // 1. check HTML expect(el.attr('class')).to.include(moduleClass); @@ -226,7 +226,7 @@ describe('CSS', function () { expect(el.attr('class')).to.include('vue-sass'); // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-sass[^{]*{font-family:cursive`)); + expect(bundledCSS).to.match(/.vue-sass[^{]*\{font-family:cursive/); }); it('<style lang="scss">', async () => { @@ -236,7 +236,7 @@ describe('CSS', function () { expect(el.attr('class')).to.include('vue-scss'); // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-scss[^{]*{font-family:cursive`)); + expect(bundledCSS).to.match(/.vue-scss[^{]*\{font-family:cursive/); }); }); @@ -245,7 +245,7 @@ describe('CSS', function () { const el = $('#svelte-css'); const classes = el.attr('class').split(' '); const scopedClass = classes.find( - (name) => name !== 'svelte-css' && /^svelte-[A-Za-z0-9-]+/.test(name) + (name) => name !== 'svelte-css' && /^svelte-[A-Za-z\d-]+/.test(name) ); // 1. check HTML @@ -261,7 +261,7 @@ describe('CSS', function () { const el = $('#svelte-sass'); const classes = el.attr('class').split(' '); const scopedClass = classes.find( - (name) => name !== 'svelte-sass' && /^svelte-[A-Za-z0-9-]+/.test(name) + (name) => name !== 'svelte-sass' && /^svelte-[A-Za-z\d-]+/.test(name) ); // 1. check HTML @@ -277,7 +277,7 @@ describe('CSS', function () { const el = $('#svelte-scss'); const classes = el.attr('class').split(' '); const scopedClass = classes.find( - (name) => name !== 'svelte-scss' && /^svelte-[A-Za-z0-9-]+/.test(name) + (name) => name !== 'svelte-scss' && /^svelte-[A-Za-z\d-]+/.test(name) ); // 1. check HTML diff --git a/packages/astro/test/astro-css-bundling.test.js b/packages/astro/test/astro-css-bundling.test.js index ae66ea838..9143a782b 100644 --- a/packages/astro/test/astro-css-bundling.test.js +++ b/packages/astro/test/astro-css-bundling.test.js @@ -102,7 +102,7 @@ describe('CSS Bundling', function () { it('CSS does not include hashes', async () => { const [firstFound] = await fixture.readdir('/assets'); - expect(firstFound).to.not.match(/[a-z]+\.[0-9a-z]{8}\.css/); + expect(firstFound).to.not.match(/[a-z]+\.[\da-z]{8}\.css/); }); it('there are 2 index named CSS files', async () => { diff --git a/packages/astro/test/astro-doctype.test.js b/packages/astro/test/astro-doctype.test.js index 65c0a5543..8c954141e 100644 --- a/packages/astro/test/astro-doctype.test.js +++ b/packages/astro/test/astro-doctype.test.js @@ -29,10 +29,7 @@ describe('Doctype', () => { // test that Doctype included was preserved expect(html).to.match( - new RegExp( - '^<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', - 'i' - ) + /^<!DOCTYPE html PUBLIC "-\/\/W3C\/\/DTD HTML 4.01 Transitional\/\/EN" "http:\/\/www.w3.org\/TR\/html4\/loose.dtd">/i ); }); diff --git a/packages/astro/test/astro-partial-html.test.js b/packages/astro/test/astro-partial-html.test.js index 162c6985d..83dccaf80 100644 --- a/packages/astro/test/astro-partial-html.test.js +++ b/packages/astro/test/astro-partial-html.test.js @@ -26,7 +26,7 @@ describe('Partial HTML', async () => { // test 2: correct CSS present const allInjectedStyles = $('style').text(); - expect(allInjectedStyles).to.match(/\[data-astro-cid-[^{]+{color:red}/); + expect(allInjectedStyles).to.match(/\[data-astro-cid-[^{]+\{color:red\}/); }); it('injects framework styles', async () => { @@ -38,7 +38,7 @@ describe('Partial HTML', async () => { // test 2: link tag present const allInjectedStyles = $('style').text().replace(/\s*/g, ''); - expect(allInjectedStyles).to.match(/h1{color:red;}/); + expect(allInjectedStyles).to.match(/h1\{color:red;\}/); }); it('pages with a head, injection happens inside', async () => { diff --git a/packages/astro/test/component-library.test.js b/packages/astro/test/component-library.test.js index c11f9eed6..a135c40cb 100644 --- a/packages/astro/test/component-library.test.js +++ b/packages/astro/test/component-library.test.js @@ -58,7 +58,7 @@ describe('Component Libraries', () => { expect($('button').text()).to.equal('Click me', "Rendered the component's slot"); - const findEvidence = createFindEvidence(/border-radius:( )*1rem/); + const findEvidence = createFindEvidence(/border-radius:\s*1rem/); expect(await findEvidence('with-astro/index.html')).to.equal( true, "Included the .astro component's <style>" @@ -136,7 +136,7 @@ describe('Component Libraries', () => { expect($('button').text()).to.equal('Click me', "Rendered the component's slot"); - const findEvidence = createFindEvidence(/border-radius:( )*1rem/); + const findEvidence = createFindEvidence(/border-radius:\s*1rem/); expect(await findEvidence('/with-astro/')).to.equal( true, "Included the .astro component's <style>" diff --git a/packages/astro/test/config-vite-css-target.test.js b/packages/astro/test/config-vite-css-target.test.js index 94fa74e74..52e6d4c7f 100644 --- a/packages/astro/test/config-vite-css-target.test.js +++ b/packages/astro/test/config-vite-css-target.test.js @@ -36,7 +36,7 @@ describe('CSS', function () { it('vite.build.cssTarget is respected', async () => { expect(bundledCSS).to.match( - new RegExp('.class\\[data-astro-[^{]*{top:0;right:0;bottom:0;left:0}') + /\.class\[data-astro-[^{]*\{top:0;right:0;bottom:0;left:0\}/ ); }); }); diff --git a/packages/astro/test/config-vite.test.js b/packages/astro/test/config-vite.test.js index b15729dd7..1b4eaf11c 100644 --- a/packages/astro/test/config-vite.test.js +++ b/packages/astro/test/config-vite.test.js @@ -17,6 +17,6 @@ describe('Vite Config', async () => { it('Allows overriding bundle naming options in the build', async () => { const html = await fixture.readFile('/index.html'); const $ = cheerio.load(html); - expect($('link').attr('href')).to.match(/\/assets\/testing-[a-z0-9]+\.css/); + expect($('link').attr('href')).to.match(/\/assets\/testing-[a-z\d]+\.css/); }); }); diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js index 447079fa2..1b5618349 100644 --- a/packages/astro/test/core-image.test.js +++ b/packages/astro/test/core-image.test.js @@ -955,7 +955,7 @@ describe('astro:image', () => { let $script = $('script'); // Find image - const regex = /src:"([^"]*)/gm; + const regex = /src:"([^"]*)/; const imageSrc = regex.exec($script.html())[1]; const data = await fixture.readFile(imageSrc, null); expect(data).to.be.an.instanceOf(Buffer); @@ -967,7 +967,7 @@ describe('astro:image', () => { const srcset = $('#local-2-widths-with-spaces img').attr('srcset'); // Find image - const regex = /^(.+?) [0-9]+[wx]$/gm; + const regex = /^(.+?) \d+[wx]$/m; const imageSrcset = regex.exec(srcset)[1]; expect(imageSrcset).to.not.contain(' '); }); diff --git a/packages/astro/test/css-dangling-references.test.js b/packages/astro/test/css-dangling-references.test.js index c38aa854d..9aa9b2828 100644 --- a/packages/astro/test/css-dangling-references.test.js +++ b/packages/astro/test/css-dangling-references.test.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { loadFixture } from './test-utils.js'; -const cssAssetReferenceRegExp = /_astro\/[A-Za-z0-9\-]+\.[a0-9a-f]{8}\.css/g; +const cssAssetReferenceRegExp = /_astro\/[A-Za-z\d\-]+\.[\da-f]{8}\.css/g; describe("When Vite's preloadModule polyfill is used", async () => { let fixture; diff --git a/packages/astro/test/hoisted-imports.test.js b/packages/astro/test/hoisted-imports.test.js index c5ce98e05..9fc76cac1 100644 --- a/packages/astro/test/hoisted-imports.test.js +++ b/packages/astro/test/hoisted-imports.test.js @@ -19,7 +19,7 @@ describe('Hoisted Imports', () => { const $ = cheerio.load(html); const scriptText = []; - const importRegex = /import\s*?['"]([^'"]+?)['"]/g; + const importRegex = /import\s*['"]([^'"]+)['"]/g; async function resolveImports(text) { const matches = text.matchAll(importRegex); for (const match of matches) { diff --git a/packages/astro/test/markdown.test.js b/packages/astro/test/markdown.test.js index 471205844..1df89715b 100644 --- a/packages/astro/test/markdown.test.js +++ b/packages/astro/test/markdown.test.js @@ -70,7 +70,7 @@ describe('Markdown tests', () => { it('Does not unescape entities', async () => { const html = await fixture.readFile('/entities/index.html'); - expect(html).to.match(new RegExp('<i>This should NOT be italic</i>')); + expect(html).to.match(/<i>This should NOT be italic<\/i>/); }); }); }); diff --git a/packages/astro/test/minification-html.test.js b/packages/astro/test/minification-html.test.js index cd3e70a1d..eec388321 100644 --- a/packages/astro/test/minification-html.test.js +++ b/packages/astro/test/minification-html.test.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { loadFixture } from './test-utils.js'; import testAdapter from './test-adapter.js'; -const NEW_LINES = /[\r\n]+/gm; +const NEW_LINES = /[\r\n]+/g; /** * The doctype declaration is on a line between the rest of the HTML in SSG. diff --git a/packages/astro/test/postcss.test.js b/packages/astro/test/postcss.test.js index 0fcc60409..2ef15c4e1 100644 --- a/packages/astro/test/postcss.test.js +++ b/packages/astro/test/postcss.test.js @@ -26,23 +26,23 @@ describe('PostCSS', function () { /** All test cases check whether nested styles (i.e. &.nested {}) are correctly transformed */ it('works in Astro page styles', () => { - expect(bundledCSS).to.match(new RegExp(`\.astro-page(\.(\w|-)*)*\.nested`)); + expect(bundledCSS).to.match(/\.astro-page\[data-astro-cid-.*?\]\.nested/); }); it('works in Astro component styles', () => { - expect(bundledCSS).to.match(new RegExp(`\.astro-component(\.(\w|-)*)*\.nested`)); + expect(bundledCSS).to.match(/\.astro-component\[data-astro-cid-.*?\]\.nested/); }); it('works in JSX', () => { - expect(bundledCSS).to.match(new RegExp(`\.solid(\.(\w|-)*)*\.nested`)); + expect(bundledCSS).to.match(/\.solid(\.(w|-)*)*\.nested/); }); it('works in Vue', () => { - expect(bundledCSS).to.match(new RegExp(`\.vue(\.(\w|-)*)*\.nested`)); + expect(bundledCSS).to.match(/\.vue(\.(w|-)*)*\.nested/); }); it('works in Svelte', () => { - expect(bundledCSS).to.match(new RegExp(`\.svelte(\.(\w|-)*)*\.nested`)); + expect(bundledCSS).to.match(/\.svelte(\.(w|-)*)*\.nested/); }); it('ignores CSS in public/', async () => { diff --git a/packages/astro/test/root-srcdir-css.test.js b/packages/astro/test/root-srcdir-css.test.js index e033ff35b..abb452db1 100644 --- a/packages/astro/test/root-srcdir-css.test.js +++ b/packages/astro/test/root-srcdir-css.test.js @@ -20,6 +20,6 @@ describe('srcDir', () => { const relPath = $('link').attr('href'); const css = await fixture.readFile(relPath); - expect(css).to.match(/body{color:green}/); + expect(css).to.match(/body\{color:green\}/); }); }); diff --git a/packages/astro/test/ssr-api-route.test.js b/packages/astro/test/ssr-api-route.test.js index e15993b88..d6ea4ac84 100644 --- a/packages/astro/test/ssr-api-route.test.js +++ b/packages/astro/test/ssr-api-route.test.js @@ -108,7 +108,7 @@ describe('API routes in SSR', () => { }); let count = 0; - let exp = /set-cookie\:/g; + let exp = /set-cookie:/g; while (exp.exec(response)) { count++; } diff --git a/packages/astro/test/static-build.test.js b/packages/astro/test/static-build.test.js index 5ec225133..79aff0ef0 100644 --- a/packages/astro/test/static-build.test.js +++ b/packages/astro/test/static-build.test.js @@ -128,7 +128,7 @@ describe('Static build', () => { } describe('Page CSS', () => { - const findEvidence = createFindEvidence(/height:( )*45vw/); + const findEvidence = createFindEvidence(/height:\s*45vw/); it('Page level CSS is added', async () => { const found = await findEvidence('/index.html'); @@ -186,7 +186,7 @@ describe('Static build', () => { let found = false; for (const log of logs) { if ( - /\`Astro\.request\.headers\` is not available in "static" output mode/.test(log.message) + /`Astro\.request\.headers` is not available in "static" output mode/.test(log.message) ) { found = true; } diff --git a/packages/astro/test/tailwindcss.test.js b/packages/astro/test/tailwindcss.test.js index 3c249cc2c..dd41e5a63 100644 --- a/packages/astro/test/tailwindcss.test.js +++ b/packages/astro/test/tailwindcss.test.js @@ -28,20 +28,20 @@ describe('Tailwind', () => { }); it('resolves CSS in src/styles', async () => { - expect(bundledCSS, 'includes used component classes').to.match(/\.bg-purple-600{/); + expect(bundledCSS, 'includes used component classes').to.match(/\.bg-purple-600\{/); // tests a random tailwind class that isn't used on the page - expect(bundledCSS, 'purges unused classes').not.to.match(/\.bg-blue-600{/); + expect(bundledCSS, 'purges unused classes').not.to.match(/\.bg-blue-600\{/); // tailwind escapes colons, `lg:py-3` compiles to `lg\:py-3` - expect(bundledCSS, 'includes responsive classes').to.match(/\.lg\\:py-3{/); + expect(bundledCSS, 'includes responsive classes').to.match(/\.lg\\:py-3\{/); // tailwind escapes brackets, `font-[900]` compiles to `font-\[900\]` - expect(bundledCSS, 'supports arbitrary value classes').to.match(/\.font-\\\[900\\\]{/); + expect(bundledCSS, 'supports arbitrary value classes').to.match(/\.font-\\\[900\\\]\{/); // custom theme colors were included - expect(bundledCSS, 'includes custom theme colors').to.match(/\.text-midnight{/); - expect(bundledCSS, 'includes custom theme colors').to.match(/\.bg-dawn{/); + expect(bundledCSS, 'includes custom theme colors').to.match(/\.text-midnight\{/); + expect(bundledCSS, 'includes custom theme colors').to.match(/\.bg-dawn\{/); }); it('maintains classes in HTML', async () => { @@ -64,7 +64,7 @@ describe('Tailwind', () => { const $md = cheerio.load(html); const bundledCSSHREF = $md('link[rel=stylesheet][href^=/_astro/]').attr('href'); const mdBundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); - expect(mdBundledCSS, 'includes used component classes').to.match(/\.bg-purple-600{/); + expect(mdBundledCSS, 'includes used component classes').to.match(/\.bg-purple-600\{/); }); }); }); diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index 213c98711..2d97969be 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -302,7 +302,7 @@ export async function parseCliDevStart(proc) { const messages = stdout .split('\n') .filter((ln) => !!ln.trim()) - .map((ln) => ln.replace(/[🚀┃]/g, '').replace(/\s+/g, ' ').trim()); + .map((ln) => ln.replace(/[🚀┃]/gu, '').replace(/\s+/g, ' ').trim()); return { messages }; } diff --git a/packages/astro/test/units/correct-path.js b/packages/astro/test/units/correct-path.js index ff741414d..026baeaf6 100644 --- a/packages/astro/test/units/correct-path.js +++ b/packages/astro/test/units/correct-path.js @@ -40,7 +40,7 @@ function normalizePath(str, stripTrailing) { if (typeof str !== 'string') { throw new TypeError('expected a string'); } - str = str.replace(/[\\\/]+/g, '/'); + str = str.replace(/[\\/]+/g, '/'); if (stripTrailing !== false) { str = removeTrailingSeparator(str); } diff --git a/packages/create-astro/src/actions/verify.ts b/packages/create-astro/src/actions/verify.ts index ac3eae484..a567e1d1f 100644 --- a/packages/create-astro/src/actions/verify.ts +++ b/packages/create-astro/src/actions/verify.ts @@ -79,6 +79,8 @@ async function verifyTemplate(tmpl: string, ref?: string) { // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +// Disable eslint rule to not touch the original code +// eslint-disable-next-line regexp/no-misleading-capturing-group const GIT_RE = /^(?<repo>[\w.-]+\/[\w.-]+)(?<subdir>[^#]+)?(?<ref>#[\w.-]+)?/; function parseGitURI(input: string) { diff --git a/packages/integrations/markdoc/src/html/css/parse-inline-css-to-react.ts b/packages/integrations/markdoc/src/html/css/parse-inline-css-to-react.ts index dd429788a..2e445d35f 100644 --- a/packages/integrations/markdoc/src/html/css/parse-inline-css-to-react.ts +++ b/packages/integrations/markdoc/src/html/css/parse-inline-css-to-react.ts @@ -17,7 +17,7 @@ export function parseInlineCSSToReactLikeObject( function convertCssDirectiveNameToReactCamelCase(original: string): string { // capture group 1 is the character to capitalize, the hyphen is omitted by virtue of being outside the capture group - const replaced = original.replace(/-([a-z0-9])/gi, (_match, char) => { + const replaced = original.replace(/-([a-z\d])/gi, (_match, char) => { return char.toUpperCase(); }); return replaced; 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 623b560af..fa3217c89 100644 --- a/packages/integrations/markdoc/src/html/css/parse-inline-styles.ts +++ b/packages/integrations/markdoc/src/html/css/parse-inline-styles.ts @@ -23,9 +23,11 @@ const NEWLINE_REGEX = /\n/g; const WHITESPACE_REGEX = /^\s*/; // declaration -const PROPERTY_REGEX = /^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/; +const PROPERTY_REGEX = /^([-#/*\\\w]+(\[[\da-z_-]+\])?)\s*/; const COLON_REGEX = /^:\s*/; -const VALUE_REGEX = /^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/; +// Disable eslint as we're not sure how to improve this regex yet +// eslint-disable-next-line regexp/no-super-linear-backtracking +const VALUE_REGEX = /^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*\)|[^};])+)/; const SEMICOLON_REGEX = /^[;\s]*/; // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index 78c8ce889..80d8c1b76 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -153,7 +153,7 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI .filter(({ n }) => n === 'astro/jsx-runtime') .map(({ ss, se }) => code.substring(ss, se)); const hasFragmentImport = importsFromJSXRuntime.some((statement) => - /[\s,{](Fragment,|Fragment\s*})/.test(statement) + /[\s,{](?:Fragment,|Fragment\s*\})/.test(statement) ); if (!hasFragmentImport) { code = 'import { Fragment } from "astro/jsx-runtime"\n' + code; diff --git a/packages/integrations/mdx/src/utils.ts b/packages/integrations/mdx/src/utils.ts index a5b198116..199929dc8 100644 --- a/packages/integrations/mdx/src/utils.ts +++ b/packages/integrations/mdx/src/utils.ts @@ -31,7 +31,7 @@ export function getFileInfo(id: string, config: AstroConfig): FileInfo { let fileUrl: string; const isPage = fileId.includes('/pages/'); if (isPage) { - fileUrl = fileId.replace(/^.*?\/pages\//, sitePathname).replace(/(\/index)?\.mdx$/, ''); + fileUrl = fileId.replace(/^.*?\/pages\//, sitePathname).replace(/(?:\/index)?\.mdx$/, ''); } else if (url?.pathname.startsWith(config.root.pathname)) { fileUrl = url.pathname.slice(config.root.pathname.length); } else { diff --git a/packages/integrations/partytown/src/sirv.ts b/packages/integrations/partytown/src/sirv.ts index 7bba54b66..e6f7c6218 100644 --- a/packages/integrations/partytown/src/sirv.ts +++ b/packages/integrations/partytown/src/sirv.ts @@ -175,7 +175,9 @@ export default function (dir, opts = {}) { let ignores = []; if (opts.ignores !== false) { - ignores.push(/[/]([A-Za-z\s\d~$._-]+\.\w+){1,}$/); // any extn + // Disable eslint as we're not sure how to improve this regex yet + // eslint-disable-next-line regexp/no-super-linear-backtracking + ignores.push(/\/([\w\s~$.-]+\.\w+)+$/); // any extn if (opts.dotfiles) ignores.push(/\/\.\w/); else ignores.push(/\/\.well-known/); [].concat(opts.ignores || []).forEach((x) => { @@ -189,9 +191,9 @@ export default function (dir, opts = {}) { if (!opts.dev) { totalist(dir, (name, abs, stats) => { - if (/\.well-known[\\+\/]/.test(name)) { + if (/\.well-known[\\+/]/.test(name)) { } // keep - else if (!opts.dotfiles && /(^\.|[\\+|\/+]\.)/.test(name)) return; + else if (!opts.dotfiles && /^\.|[\\+|/]\./.test(name)) return; let headers = toHeaders(name, stats, isEtag); if (cc) headers['Cache-Control'] = cc; @@ -212,7 +214,7 @@ export default function (dir, opts = {}) { // NEW END let val = req.headers['accept-encoding'] || ''; if (gzips && val.includes('gzip')) extns.unshift(...gzips); - if (brots && /(br|brotli)/i.test(val)) extns.unshift(...brots); + if (brots && /br/i.test(val)) extns.unshift(...brots); extns.push(...extensions); // [...br, ...gz, orig, ...exts] if (pathname.indexOf('%') !== -1) { diff --git a/packages/integrations/preact/src/server.ts b/packages/integrations/preact/src/server.ts index 79461c572..01b2ba100 100644 --- a/packages/integrations/preact/src/server.ts +++ b/packages/integrations/preact/src/server.ts @@ -37,7 +37,7 @@ async function check( // There are edge cases (SolidJS) where Preact *might* render a string, // but components would be <undefined></undefined> // It also might render an empty sting. - return html == '' ? false : !/\<undefined\>/.test(html); + return html == '' ? false : !/<undefined>/.test(html); } catch (err) { return false; } diff --git a/packages/integrations/sitemap/test/staticPaths.test.js b/packages/integrations/sitemap/test/staticPaths.test.js index 0e7353765..4054ad930 100644 --- a/packages/integrations/sitemap/test/staticPaths.test.js +++ b/packages/integrations/sitemap/test/staticPaths.test.js @@ -38,6 +38,6 @@ describe('getStaticPaths support', () => { it('should render the endpoint', async () => { const page = await fixture.readFile('./it/manifest'); - assert.match(page, /I\'m a route in the "it" language./); + assert.match(page, /I'm a route in the "it" language./); }); }); diff --git a/packages/integrations/vue/src/editor.cts b/packages/integrations/vue/src/editor.cts index 0b62e899e..0c9642d67 100644 --- a/packages/integrations/vue/src/editor.cts +++ b/packages/integrations/vue/src/editor.cts @@ -23,7 +23,7 @@ export function toTSX(code: string, className: string): string { const { scriptSetup } = parsedResult.descriptor; if (scriptSetup) { - const definePropsType = scriptSetup.content.match(/defineProps<([\S\s]+?)>\s?\(\)/m); + const definePropsType = scriptSetup.content.match(/defineProps<([\s\S]+?)>\s?\(\)/); const propsGeneric = scriptSetup.attrs.generic; const propsGenericType = propsGeneric ? `<${propsGeneric}>` : ''; @@ -40,7 +40,7 @@ export function toTSX(code: string, className: string): string { // TODO. Find a way to support generics when using defineProps without passing explicit types. // Right now something like this `defineProps({ prop: { type: Array as PropType<T[]> } })` // won't be correctly typed in Astro. - const defineProps = scriptSetup.content.match(/defineProps\([\s\S]+\)/m); + const defineProps = scriptSetup.content.match(/defineProps\([\s\S]+\)/); if (defineProps) { result = ` diff --git a/packages/integrations/vue/test/app-entrypoint.test.js b/packages/integrations/vue/test/app-entrypoint.test.js index 5cdae3c04..5c0437636 100644 --- a/packages/integrations/vue/test/app-entrypoint.test.js +++ b/packages/integrations/vue/test/app-entrypoint.test.js @@ -41,7 +41,7 @@ describe('App Entrypoint', () => { assert.notEqual(client, undefined); const js = await fixture.readFile(client); - assert.match(js, /\w+\.component\(\"Bar\"/gm); + assert.match(js, /\w+\.component\("Bar"/g); }); it('loads svg components without transforming them to assets', async () => { @@ -112,7 +112,7 @@ describe('App Entrypoint no export default', () => { const client = island.getAttribute('renderer-url'); assert.notEqual(client, undefined); const js = await fixture.readFile(client); - assert.doesNotMatch(js, /\w+\.component\(\"Bar\"/gm); + assert.doesNotMatch(js, /\w+\.component\("Bar"/g); }); it('loads svg components without transforming them to assets', async () => { @@ -151,7 +151,7 @@ describe('App Entrypoint relative', () => { assert.notEqual(client, undefined); const js = await fixture.readFile(client); - assert.doesNotMatch(js, /\w+\.component\(\"Bar\"/gm); + assert.doesNotMatch(js, /\w+\.component\("Bar"/g); }); }); @@ -182,7 +182,7 @@ describe('App Entrypoint /src/absolute', () => { assert.notEqual(client, undefined); const js = await fixture.readFile(client); - assert.doesNotMatch(js, /\w+\.component\(\"Bar\"/gm); + assert.doesNotMatch(js, /\w+\.component\("Bar"/g); }); }); diff --git a/packages/internal-helpers/src/path.ts b/packages/internal-helpers/src/path.ts index cc9954ef2..fbbebc7c9 100644 --- a/packages/internal-helpers/src/path.ts +++ b/packages/internal-helpers/src/path.ts @@ -16,7 +16,7 @@ export function prependForwardSlash(path: string) { } export function collapseDuplicateSlashes(path: string) { - return path.replace(/(?<!:)\/\/+/g, '/'); + return path.replace(/(?<!:)\/{2,}/g, '/'); } export function removeTrailingForwardSlash(path: string) { @@ -86,7 +86,7 @@ export function removeQueryString(path: string) { } export function isRemotePath(src: string) { - return /^(http|ftp|https|ws):?\/\//.test(src) || src.startsWith('data:'); + return /^(?:http|ftp|https|ws):?\/\//.test(src) || src.startsWith('data:'); } export function slash(path: string) { diff --git a/packages/markdown/remark/src/shiki.ts b/packages/markdown/remark/src/shiki.ts index 492d5a821..4024d4bc1 100644 --- a/packages/markdown/remark/src/shiki.ts +++ b/packages/markdown/remark/src/shiki.ts @@ -13,7 +13,7 @@ const ASTRO_COLOR_REPLACEMENTS: Record<string, string> = { '--astro-code-background': '--astro-code-color-background', }; const COLOR_REPLACEMENT_REGEX = new RegExp( - `(${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')})`, + `${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')}`, 'g' ); diff --git a/packages/telemetry/src/project-info.ts b/packages/telemetry/src/project-info.ts index 16ea50f3b..79b9e4f44 100644 --- a/packages/telemetry/src/project-info.ts +++ b/packages/telemetry/src/project-info.ts @@ -91,7 +91,7 @@ function getProjectId(isCI: boolean): Pick<ProjectInfo, 'anonymousProjectId' | ' // If we're running in CI, the current working directory is not unique. // If the cwd is a single level deep (ex: '/app'), it's probably not unique. const cwd = process.cwd(); - const isCwdGeneric = (cwd.match(/[\/|\\]/g) || []).length === 1; + const isCwdGeneric = (cwd.match(/[/|\\]/g) || []).length === 1; if (isCI || isCwdGeneric) { return { isGit: false, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61ee0ecac..007f9239d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,6 +51,9 @@ importers: eslint-plugin-prettier: specifier: ^5.0.0 version: 5.1.2(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.1.1) + eslint-plugin-regexp: + specifier: ^2.2.0 + version: 2.2.0(eslint@8.56.0) globby: specifier: ^14.0.0 version: 14.0.0 @@ -8894,6 +8897,11 @@ packages: engines: {node: ^12.20.0 || >=14} dev: true + /comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + dev: true + /common-ancestor-path@1.0.1: resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} dev: false @@ -9633,6 +9641,22 @@ packages: synckit: 0.8.8 dev: true + /eslint-plugin-regexp@2.2.0(eslint@8.56.0): + resolution: {integrity: sha512-0kwpiWiLRVBkVr3oIRQLl196sXP/NF6DQFefv9jtR4ZOgQR+6WID2pIZ0I+wIt54qgBPwBB7Gm2a+ueh8/WsFQ==} + engines: {node: ^18 || >=20} + peerDependencies: + eslint: '>=8.44.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/regexpp': 4.10.0 + comment-parser: 1.4.1 + eslint: 8.56.0 + jsdoc-type-pratt-parser: 4.0.0 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + scslre: 0.3.0 + dev: true + /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -11165,6 +11189,11 @@ packages: dependencies: argparse: 2.0.1 + /jsdoc-type-pratt-parser@4.0.0: + resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} + engines: {node: '>=12.0.0'} + dev: true + /jsdom@22.1.0: resolution: {integrity: sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==} engines: {node: '>=16'} @@ -13755,6 +13784,13 @@ packages: strip-indent: 3.0.0 dev: true + /refa@0.12.1: + resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dependencies: + '@eslint-community/regexpp': 4.10.0 + dev: true + /regenerator-runtime@0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} dev: true @@ -13763,6 +13799,14 @@ packages: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} dev: true + /regexp-ast-analysis@0.7.1: + resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dependencies: + '@eslint-community/regexpp': 4.10.0 + refa: 0.12.1 + dev: true + /regexp.prototype.flags@1.5.1: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} @@ -14162,6 +14206,15 @@ packages: dependencies: loose-envify: 1.4.0 + /scslre@0.3.0: + resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} + engines: {node: ^14.0.0 || >=16.0.0} + dependencies: + '@eslint-community/regexpp': 4.10.0 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + dev: true + /section-matter@1.0.0: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} |