From 195203e9715897d093efe17a75c0ffd3f55d973e Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:16:53 -0700 Subject: [PATCH] Add regex eslint plugin, fix lints (#59371) --- eslint.config.mjs | 3 + package-lock.json | 136 ++++++++++++++++++ package.json | 1 + scripts/build/tests.mjs | 2 +- scripts/configurePrerelease.mjs | 2 +- scripts/failed-tests.cjs | 2 +- .../generateLocalizedDiagnosticMessages.mjs | 4 +- scripts/processDiagnosticMessages.mjs | 2 +- .../regenerate-unicode-identifier-parts.mjs | 4 +- src/compiler/checker.ts | 4 +- src/compiler/commandLineParser.ts | 2 +- src/compiler/debug.ts | 2 +- src/compiler/emitter.ts | 4 +- src/compiler/parser.ts | 6 +- src/compiler/path.ts | 4 +- src/compiler/program.ts | 2 +- src/compiler/resolutionCache.ts | 4 +- src/compiler/semver.ts | 10 +- src/compiler/sourcemap.ts | 4 +- src/compiler/sys.ts | 4 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/utilities.ts | 22 +-- src/compiler/utilitiesPublic.ts | 4 +- src/harness/documentsUtil.ts | 4 +- src/harness/fourslashImpl.ts | 6 +- src/harness/harnessIO.ts | 14 +- src/harness/harnessUtils.ts | 8 +- src/harness/incrementalUtils.ts | 2 +- src/harness/runnerbase.ts | 2 +- src/harness/typeWriter.ts | 2 +- src/harness/util.ts | 4 +- src/harness/vpathUtil.ts | 2 +- src/server/editorServices.ts | 2 +- src/services/classifier.ts | 4 +- .../codefixes/convertTypedefToType.ts | 2 +- src/services/navigationBar.ts | 2 +- src/services/outliningElementsCollector.ts | 18 ++- src/services/services.ts | 2 +- src/services/sourcemaps.ts | 2 +- src/testRunner/runner.ts | 4 +- .../unittests/config/initializeTSConfig.ts | 2 +- src/testRunner/unittests/config/showConfig.ts | 2 +- src/testRunner/unittests/helpers/baseline.ts | 4 +- .../unittests/jsonParserRecovery.ts | 2 +- src/testRunner/unittests/printer.ts | 4 +- .../unittests/services/transpile.ts | 2 +- .../tsserver/cachingFileSystemInformation.ts | 2 +- 47 files changed, 236 insertions(+), 92 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index cadd9bb1f38..195ca4293e4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,6 @@ // @ts-check import eslint from "@eslint/js"; +import * as regexpPlugin from "eslint-plugin-regexp"; import fs from "fs"; import globals from "globals"; import { createRequire } from "module"; @@ -36,6 +37,7 @@ export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, ...tseslint.configs.stylistic, + regexpPlugin.configs["flat/recommended"], { plugins: { local: { @@ -208,6 +210,7 @@ export default tseslint.config( files: ["src/harness/**", "src/testRunner/**"], rules: { "no-restricted-globals": "off", + "regexp/no-super-linear-backtracking": "off", "local/no-direct-import": "off", }, }, diff --git a/package-lock.json b/package-lock.json index 5f2bbab16ea..eb0db3e839a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "esbuild": "^0.23.0", "eslint": "^9.9.0", "eslint-formatter-autolinkable-stylish": "^1.4.0", + "eslint-plugin-regexp": "^2.6.0", "fast-xml-parser": "^4.4.1", "glob": "^10.4.5", "globals": "^15.9.0", @@ -1844,6 +1845,15 @@ "node": ">= 6" } }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2239,6 +2249,27 @@ "eslint": "^8.3.0 || ^9.0.0" } }, + "node_modules/eslint-plugin-regexp": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.6.0.tgz", + "integrity": "sha512-FCL851+kislsTEQEMioAlpDuK5+E5vs0hi1bF8cFlPlHcEjeRhuAzEsGikXRreE+0j4WhW2uO54MqTjXtYOi3A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.9.1", + "comment-parser": "^1.4.0", + "jsdoc-type-pratt-parser": "^4.0.0", + "refa": "^0.12.1", + "regexp-ast-analysis": "^0.7.1", + "scslre": "^0.3.0" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "eslint": ">=8.44.0" + } + }, "node_modules/eslint-scope": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", @@ -3058,6 +3089,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3945,6 +3985,31 @@ "node": ">=6" } }, + "node_modules/refa": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.8.0" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/regexp-ast-analysis": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.1" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4016,6 +4081,20 @@ } ] }, + "node_modules/scslre": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.0", + "regexp-ast-analysis": "^0.7.0" + }, + "engines": { + "node": "^14.0.0 || >=16.0.0" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -5926,6 +6005,12 @@ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true }, + "comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6244,6 +6329,21 @@ "plur": "^4.0.0" } }, + "eslint-plugin-regexp": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.6.0.tgz", + "integrity": "sha512-FCL851+kislsTEQEMioAlpDuK5+E5vs0hi1bF8cFlPlHcEjeRhuAzEsGikXRreE+0j4WhW2uO54MqTjXtYOi3A==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.9.1", + "comment-parser": "^1.4.0", + "jsdoc-type-pratt-parser": "^4.0.0", + "refa": "^0.12.1", + "regexp-ast-analysis": "^0.7.1", + "scslre": "^0.3.0" + } + }, "eslint-scope": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", @@ -6800,6 +6900,12 @@ "argparse": "^2.0.1" } }, + "jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true + }, "json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -7418,6 +7524,25 @@ "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", "dev": true }, + "refa": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.8.0" + } + }, + "regexp-ast-analysis": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.1" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7451,6 +7576,17 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "scslre": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.0", + "regexp-ast-analysis": "^0.7.0" + } + }, "semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", diff --git a/package.json b/package.json index ec6a428e909..05e08606a75 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "esbuild": "^0.23.0", "eslint": "^9.9.0", "eslint-formatter-autolinkable-stylish": "^1.4.0", + "eslint-plugin-regexp": "^2.6.0", "fast-xml-parser": "^4.4.1", "glob": "^10.4.5", "globals": "^15.9.0", diff --git a/scripts/build/tests.mjs b/scripts/build/tests.mjs index c2d11b3b569..ba9e03548a0 100644 --- a/scripts/build/tests.mjs +++ b/scripts/build/tests.mjs @@ -105,7 +105,7 @@ export async function runConsoleTests(runJs, defaultReporter, runInParallel, opt } if (failed) { const grep = fs.readFileSync(".failed-tests", "utf8") - .split(/\r?\n/g) + .split(/\r?\n/) .map(test => test.trim()) .filter(test => test.length > 0) .map(regExpEscape) diff --git a/scripts/configurePrerelease.mjs b/scripts/configurePrerelease.mjs index 03795196ddf..1e7d9483c46 100644 --- a/scripts/configurePrerelease.mjs +++ b/scripts/configurePrerelease.mjs @@ -112,7 +112,7 @@ function getPrereleasePatch(tag, plainPatch) { // but we'd prefer to just remove separators and limit ourselves to YYYYMMDD. // UTC time will always be implicit here. const now = new Date(); - const timeStr = now.toISOString().replace(/:|T|\.|-/g, "").slice(0, 8); + const timeStr = now.toISOString().replace(/[:T.-]/g, "").slice(0, 8); return `${plainPatch}-${tag}.${timeStr}`; } diff --git a/scripts/failed-tests.cjs b/scripts/failed-tests.cjs index e871cb37a28..4ef79cfb68a 100644 --- a/scripts/failed-tests.cjs +++ b/scripts/failed-tests.cjs @@ -124,7 +124,7 @@ class FailedTestsReporter extends Mocha.reporters.Base { function readTests() { return fs.readFileSync(file, "utf8") - .split(/\r?\n/g) + .split(/\r?\n/) .map(line => line.trim()) .filter(line => line.length > 0); } diff --git a/scripts/generateLocalizedDiagnosticMessages.mjs b/scripts/generateLocalizedDiagnosticMessages.mjs index 531c0702bfa..578a5f8f323 100644 --- a/scripts/generateLocalizedDiagnosticMessages.mjs +++ b/scripts/generateLocalizedDiagnosticMessages.mjs @@ -113,7 +113,7 @@ async function main() { ItemId = ItemId.slice(1); // remove leading semicolon } - val = val.replace(/]5D;/, "]"); // unescape `]` + val = val.replace(/\]5D;/, "]"); // unescape `]` out[ItemId] = val; } return JSON.stringify(out, undefined, 2); @@ -146,7 +146,7 @@ async function main() { */ function getItemXML(key, value) { // escape entrt value - value = value.replace(/]/g, "]5D;"); + value = value.replace(/\]/g, "]5D;"); return ` diff --git a/scripts/processDiagnosticMessages.mjs b/scripts/processDiagnosticMessages.mjs index b3f398b802c..85f029e1d37 100644 --- a/scripts/processDiagnosticMessages.mjs +++ b/scripts/processDiagnosticMessages.mjs @@ -150,7 +150,7 @@ function convertPropertyName(origName) { result = result.replace(/_+/g, "_"); // remove any leading underscore, unless it is followed by a number. - result = result.replace(/^_([^\d])/, "$1"); + result = result.replace(/^_(\D)/, "$1"); // get rid of all trailing underscores. result = result.replace(/_$/, ""); diff --git a/scripts/regenerate-unicode-identifier-parts.mjs b/scripts/regenerate-unicode-identifier-parts.mjs index 4dbc0354ebe..0abd43be69e 100644 --- a/scripts/regenerate-unicode-identifier-parts.mjs +++ b/scripts/regenerate-unicode-identifier-parts.mjs @@ -1,8 +1,8 @@ const MAX_UNICODE_CODEPOINT = 0x10FFFF; /** @type {(c: string) => boolean} */ -const isStart = c => /[\p{ID_Start}\u{2118}\u{212E}\u{309B}\u{309C}]/u.test(c); // Other_ID_Start explicitly included for back compat - see http://www.unicode.org/reports/tr31/#Introduction +const isStart = c => /\p{ID_Start}/u.test(c); // Other_ID_Start explicitly included for back compat - see http://www.unicode.org/reports/tr31/#Introduction /** @type {(c: string) => boolean} */ -const isPart = c => /[\p{ID_Continue}\u{00B7}\u{0387}\u{19DA}\u{1369}\u{136A}\u{136B}\u{136C}\u{136D}\u{136E}\u{136F}\u{1370}\u{1371}]/u.test(c) || isStart(c); // Likewise for Other_ID_Continue +const isPart = c => /\p{ID_Continue}/u.test(c) || isStart(c); // Likewise for Other_ID_Continue const parts = []; let partsActive = false; let startsActive = false; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 71256ad7160..6008bb2da1f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10678,7 +10678,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (localName === InternalSymbolName.ExportEquals) { localName = "_exports"; } - localName = isIdentifierText(localName, languageVersion) && !isStringANonContextualKeyword(localName) ? localName : "_" + localName.replace(/[^a-zA-Z0-9]/g, "_"); + localName = isIdentifierText(localName, languageVersion) && !isStringANonContextualKeyword(localName) ? localName : "_" + localName.replace(/[^a-z0-9]/gi, "_"); return localName; } @@ -34355,7 +34355,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function containerSeemsToBeEmptyDomElement(containingType: Type) { return (compilerOptions.lib && !compilerOptions.lib.includes("dom")) && - everyContainedType(containingType, type => type.symbol && /^(EventTarget|Node|((HTML[a-zA-Z]*)?Element))$/.test(unescapeLeadingUnderscores(type.symbol.escapedName))) && + everyContainedType(containingType, type => type.symbol && /^(?:EventTarget|Node|(?:HTML[a-zA-Z]*)?Element)$/.test(unescapeLeadingUnderscores(type.symbol.escapedName))) && isEmptyObjectType(containingType); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 1775bce7123..86bb598c2a8 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -3762,7 +3762,7 @@ function convertJsonOptionOfListType( * \*\* # matches the recursive directory wildcard "**". * \/?$ # matches an optional trailing directory separator at the end of the string. */ -const invalidTrailingRecursionPattern = /(^|\/)\*\*\/?$/; +const invalidTrailingRecursionPattern = /(?:^|\/)\*\*\/?$/; /** * Matches the portion of a wildcard path that does not contain wildcards. diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 298c628907e..7468b2aecea 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -581,7 +581,7 @@ export namespace Debug { // This regex can trigger slow backtracking because of overlapping potential captures. // We don't care, this is debug code that's only enabled with a debugger attached - // we're just taking note of it for anyone checking regex performance in the future. - defaultValue = String(defaultValue).replace(/(?:,[\s\w\d_]+:[^,]+)+\]$/, "]"); + defaultValue = String(defaultValue).replace(/(?:,[\s\w]+:[^,]+)+\]$/, "]"); return `NodeArray ${defaultValue}`; }, }, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 493e60af2b8..f79fdf21364 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4005,7 +4005,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri if (node.comment) { const text = getTextOfJSDocComment(node.comment); if (text) { - const lines = text.split(/\r\n?|\n/g); + const lines = text.split(/\r\n?|\n/); for (const line of lines) { writeLine(); writeSpace(); @@ -4880,7 +4880,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } function writeLines(text: string): void { - const lines = text.split(/\r\n?|\n/g); + const lines = text.split(/\r\n?|\n/); const indentation = guessIndentation(lines); for (const lineText of lines) { const line = indentation ? lineText.slice(indentation) : lineText; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b20d4b8b8b5..c95c4759b9c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -10653,8 +10653,8 @@ function getNamedArgRegEx(name: string): RegExp { return result; } -const tripleSlashXMLCommentStartRegEx = /^\/\/\/\s*<(\S+)\s.*?\/>/im; -const singleLinePragmaRegEx = /^\/\/\/?\s*@([^\s:]+)(.*)\s*$/im; +const tripleSlashXMLCommentStartRegEx = /^\/\/\/\s*<(\S+)\s.*?\/>/m; +const singleLinePragmaRegEx = /^\/\/\/?\s*@([^\s:]+)((?:[^\S\r\n]|:).*)?$/m; function extractPragmas(pragmas: PragmaPseudoMapEntry[], range: CommentRange, text: string) { const tripleSlash = range.kind === SyntaxKind.SingleLineCommentTrivia && tripleSlashXMLCommentStartRegEx.exec(text); if (tripleSlash) { @@ -10700,7 +10700,7 @@ function extractPragmas(pragmas: PragmaPseudoMapEntry[], range: CommentRange, te } if (range.kind === SyntaxKind.MultiLineCommentTrivia) { - const multiLinePragmaRegEx = /@(\S+)(\s+.*)?$/gim; // Defined inline since it uses the "g" flag, which keeps a persistent index (for iterating) + const multiLinePragmaRegEx = /@(\S+)(\s+(?:\S.*)?)?$/gm; // Defined inline since it uses the "g" flag, which keeps a persistent index (for iterating) let multiLineMatch: RegExpExecArray | null; // eslint-disable-line no-restricted-syntax while (multiLineMatch = multiLinePragmaRegEx.exec(text)) { addPragmaForMatch(pragmas, range, PragmaKindFlags.MultiLine, multiLineMatch); diff --git a/src/compiler/path.ts b/src/compiler/path.ts index f4841c97cdf..00c0e4b671b 100644 --- a/src/compiler/path.ts +++ b/src/compiler/path.ts @@ -98,7 +98,7 @@ export function pathIsAbsolute(path: string): boolean { * @internal */ export function pathIsRelative(path: string): boolean { - return /^\.\.?($|[\\/])/.test(path); + return /^\.\.?(?:$|[\\/])/.test(path); } /** @@ -779,7 +779,7 @@ export function changeFullExtension(path: string, newExtension: string) { //// Path Comparisons // check path for these segments: '', '.'. '..' -const relativePathSegmentRegExp = /(?:\/\/)|(?:^|\/)\.\.?(?:$|\/)/; +const relativePathSegmentRegExp = /\/\/|(?:^|\/)\.\.?(?:$|\/)/; function comparePathsWorker(a: string, b: string, componentComparer: (a: string, b: string) => Comparison) { if (a === b) return Comparison.EqualTo; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bd9b42caae9..06bb19f84d1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -3109,7 +3109,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // Stop searching if the line is not empty and not a comment const lineText = file.text.slice(lineStarts[line], lineStarts[line + 1]).trim(); - if (lineText !== "" && !/^(\s*)\/\/(.*)$/.test(lineText)) { + if (lineText !== "" && !/^\s*\/\/.*$/.test(lineText)) { return -1; } diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 65c41be92f8..cfe6b955552 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -267,11 +267,11 @@ function perceivedOsRootLengthForWatching(pathComponents: Readonly Numeric identifiers MUST NOT include leading zeroes. -const numericIdentifierRegExp = /^(0|[1-9]\d*)$/; +const numericIdentifierRegExp = /^(?:0|[1-9]\d*)$/; /** * Describes a precise semantic version number, https://semver.org @@ -244,8 +244,8 @@ interface Comparator { // range-set ::= range ( logical-or range ) * // range ::= hyphen | simple ( ' ' simple ) * | '' // logical-or ::= ( ' ' ) * '||' ( ' ' ) * -const logicalOrRegExp = /\|\|/g; -const whitespaceRegExp = /\s+/g; +const logicalOrRegExp = /\|\|/; +const whitespaceRegExp = /\s+/; // https://github.com/npm/node-semver#range-grammar // @@ -257,7 +257,7 @@ const whitespaceRegExp = /\s+/g; // build ::= parts // parts ::= part ( '.' part ) * // part ::= nr | [-0-9A-Za-z]+ -const partialRegExp = /^([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i; +const partialRegExp = /^([x*0]|[1-9]\d*)(?:\.([x*0]|[1-9]\d*)(?:\.([x*0]|[1-9]\d*)(?:-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i; // https://github.com/npm/node-semver#range-grammar // @@ -270,7 +270,7 @@ const hyphenRegExp = /^\s*([a-z0-9-+.*]+)\s+-\s+([a-z0-9-+.*]+)\s*$/i; // primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial // tilde ::= '~' partial // caret ::= '^' partial -const rangeRegExp = /^(~|\^|<|<=|>|>=|=)?\s*([a-z0-9-+.*]+)$/i; +const rangeRegExp = /^([~^<>=]|<=|>=)?\s*([a-z0-9-+.*]+)$/i; function parseRange(text: string) { const alternatives: Comparator[][] = []; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index fff807e98cf..9f3c5a05d42 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -360,9 +360,9 @@ export function createSourceMapGenerator(host: EmitHost, file: string, sourceRoo // Sometimes tools can see the following line as a source mapping url comment, so we mangle it a bit (the [M]) /** @internal */ -export const sourceMapCommentRegExpDontCareLineStart = /\/\/[@#] source[M]appingURL=(.+)\r?\n?$/; +export const sourceMapCommentRegExpDontCareLineStart = /\/\/[@#] source[M]appingURL=(.+)\r?\n?$/; // eslint-disable-line regexp/no-useless-character-class /** @internal */ -export const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)\r?\n?$/; +export const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)\r?\n?$/; // eslint-disable-line regexp/no-useless-character-class /** @internal */ export const whitespaceOrMapCommentRegExp = /^\s*(\/\/[@#] .*)?$/; diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 13340095ce1..a5b24bf8543 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1466,7 +1466,7 @@ export let sys: System = (() => { const byteOrderMarkIndicator = "\uFEFF"; function getNodeSystem(): System { - const nativePattern = /^native |^\([^)]+\)$|^(internal[\\/]|[a-zA-Z0-9_\s]+(\.js)?$)/; + const nativePattern = /^native |^\([^)]+\)$|^(?:internal[\\/]|[\w\s]+(?:\.js)?$)/; const _fs: typeof import("fs") = require("fs"); const _path: typeof import("path") = require("path"); const _os = require("os"); @@ -1592,7 +1592,7 @@ export let sys: System = (() => { disableCPUProfiler, cpuProfilingEnabled: () => !!activeSession || contains(process.execArgv, "--cpu-prof") || contains(process.execArgv, "--prof"), realpath, - debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)) || !!(process as any).recordreplay, + debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(process.execArgv, arg => /^--(?:inspect|debug)(?:-brk)?(?:=\d+)?$/i.test(arg)) || !!(process as any).recordreplay, tryEnableSourceMapsForHost() { try { (require("source-map-support") as typeof import("source-map-support")).install(); diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 49d04a2fd82..bcb0cbf8ee2 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -667,7 +667,7 @@ export function transformJsx(context: TransformationContext): (x: SourceFile | B const name = node.name; if (isIdentifier(name)) { const text = idText(name); - return (/^[A-Za-z_]\w*$/.test(text)) ? name : factory.createStringLiteral(text); + return (/^[A-Z_]\w*$/i.test(text)) ? name : factory.createStringLiteral(text); } return factory.createStringLiteral(idText(name.namespace) + ":" + idText(name.name)); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2c647c697c4..76849b84703 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2622,12 +2622,12 @@ export function getJSDocCommentRanges(node: Node, text: string) { text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash); } -const fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*/; -const fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*/; -const fullTripleSlashLibReferenceRegEx = /^(\/\/\/\s*/; -const fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*/; -const fullTripleSlashAMDModuleRegEx = /^\/\/\/\s*/; -const defaultLibReferenceRegEx = /^(\/\/\/\s*/; +const fullTripleSlashReferencePathRegEx = /^\/\/\/\s*/; +const fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^\/\/\/\s*/; +const fullTripleSlashLibReferenceRegEx = /^\/\/\/\s*/; +const fullTripleSlashAMDReferencePathRegEx = /^\/\/\/\s*/; +const fullTripleSlashAMDModuleRegEx = /^\/\/\/\s*/; +const defaultLibReferenceRegEx = /^\/\/\/\s*/; export function isPartOfTypeNode(node: Node): boolean { if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) { @@ -5988,10 +5988,10 @@ export function hasInvalidEscape(template: TemplateLiteral): boolean { // the language service. These characters should be escaped when printing, and if any characters are added, // the map below must be updated. Note that this regexp *does not* include the 'delete' character. // There is no reason for this other than that JSON.stringify does not handle it either. -const doubleQuoteEscapedCharsRegExp = /[\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; -const singleQuoteEscapedCharsRegExp = /[\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; +const doubleQuoteEscapedCharsRegExp = /[\\"\u0000-\u001f\u2028\u2029\u0085]/g; +const singleQuoteEscapedCharsRegExp = /[\\'\u0000-\u001f\u2028\u2029\u0085]/g; // Template strings preserve simple LF newlines, still encode CRLF (or CR) -const backtickQuoteEscapedCharsRegExp = /\r\n|[\\`\u0000-\u001f\t\v\f\b\r\u2028\u2029\u0085]/g; +const backtickQuoteEscapedCharsRegExp = /\r\n|[\\`\u0000-\u001f\u2028\u2029\u0085]/g; const escapedCharsMap = new Map(Object.entries({ "\t": "\\t", "\v": "\\v", @@ -8365,7 +8365,7 @@ export function setObjectAllocator(alloc: ObjectAllocator) { /** @internal */ export function formatStringFromArgs(text: string, args: DiagnosticArguments): string { - return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.checkDefined(args[+index])); + return text.replace(/\{(\d+)\}/g, (_match, index: string) => "" + Debug.checkDefined(args[+index])); } let localizedDiagnosticMessages: MapLike | undefined; @@ -10623,7 +10623,7 @@ export function isFunctionExpressionOrArrowFunction(node: Node): node is Functio /** @internal */ export function escapeSnippetText(text: string): string { - return text.replace(/\$/gm, () => "\\$"); + return text.replace(/\$/g, () => "\\$"); } /** @internal */ diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index a5b8b44ca59..325f9739c56 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -681,7 +681,7 @@ export function validateLocaleAndSetLanguage( errors?: Diagnostic[], ) { const lowerCaseLocale = locale.toLowerCase(); - const matchResult = /^([a-z]+)([_-]([a-z]+))?$/.exec(lowerCaseLocale); + const matchResult = /^([a-z]+)(?:[_-]([a-z]+))?$/.exec(lowerCaseLocale); if (!matchResult) { if (errors) { @@ -691,7 +691,7 @@ export function validateLocaleAndSetLanguage( } const language = matchResult[1]; - const territory = matchResult[3]; + const territory = matchResult[2]; // First try the entire locale, then fall back to just language if that's all we have. // Either ways do not fail, and fallback to the English diagnostic strings. diff --git a/src/harness/documentsUtil.ts b/src/harness/documentsUtil.ts index 8a96d4d290f..cf3d7c7f081 100644 --- a/src/harness/documentsUtil.ts +++ b/src/harness/documentsUtil.ts @@ -72,8 +72,8 @@ export class SourceMap { public readonly mappings: readonly Mapping[] = []; public readonly names: readonly string[] | undefined; - private static readonly _mappingRegExp = /([A-Za-z0-9+/]+),?|(;)|./g; - private static readonly _sourceMappingURLRegExp = /^\/\/[#@]\s*sourceMappingURL\s*=\s*(.*?)\s*$/mig; + private static readonly _mappingRegExp = /([A-Z0-9+/]+),?|(;)|./gi; + private static readonly _sourceMappingURLRegExp = /^\/\/[#@]\s*sourceMappingURL\s*=\s*(.*?)\s*$/gim; private static readonly _dataURLRegExp = /^data:application\/json;base64,([a-z0-9+/=]+)$/i; private static readonly _base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 4d3d6cea32e..04beb984ae5 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -966,7 +966,7 @@ export class TestState { const annotations = ts.map(hints.sort(sortHints), hint => { if (hint.displayParts) { hint.displayParts = ts.map(hint.displayParts, part => { - if (part.file && /lib(?:.*)\.d\.ts$/.test(part.file)) { + if (part.file && /lib.*\.d\.ts$/.test(part.file)) { part.span!.start = -1; } return part; @@ -1547,7 +1547,7 @@ export class TestState { }: BaselineDocumentSpansWithFileContentsOptions, spanToContextId: Map, ) { - const isLibFile = /lib(?:.*)\.d\.ts$/.test(fileName); + const isLibFile = /lib.*\.d\.ts$/.test(fileName); let readableContents = `// === ${fileName} ===`; let newContent = ""; interface Detail { @@ -2514,7 +2514,7 @@ export class TestState { for (const part of tag.text ?? ts.emptyArray) { if (part.kind === "linkName") { const link = part as ts.JSDocLinkDisplayPart; - if (/lib(?:.*)\.d\.ts$/.test(link.target.fileName)) { + if (/lib.*\.d\.ts$/.test(link.target.fileName)) { // The object literal isn't a complete TextSpan, but we're only going to // use these results in the baseline for diffing, so just overwrite. (link.target.textSpan as any) = { start: "--", length: "--" }; diff --git a/src/harness/harnessIO.ts b/src/harness/harnessIO.ts index d73092a297a..e27cf3cf949 100644 --- a/src/harness/harnessIO.ts +++ b/src/harness/harnessIO.ts @@ -582,7 +582,7 @@ export namespace Compiler { let location = info.file ? " " + ts.formatLocation(info.file, info.start!, formatDiagnsoticHost, ts.identity) : ""; location = Utils.removeTestPathPrefixes(location); if (location && isDefaultLibraryFile(info.file!.fileName)) { - location = location.replace(/(lib(?:.*)\.d\.ts):\d+:\d+/i, "$1:--:--"); + location = location.replace(/(lib.*\.d\.ts):\d+:\d+/i, "$1:--:--"); } errLines.push(`!!! related TS${info.code}${location}: ${ts.flattenDiagnosticMessageText(info.messageText, IO.newLine())}`); } @@ -603,7 +603,7 @@ export namespace Compiler { let topDiagnostics = minimalDiagnosticsToString(diagnostics, options && options.pretty); topDiagnostics = Utils.removeTestPathPrefixes(topDiagnostics); - topDiagnostics = topDiagnostics.replace(/^(lib(?:.*)\.d\.ts)\(\d+,\d+\)/igm, "$1(--,--)"); + topDiagnostics = topDiagnostics.replace(/^(lib.*\.d\.ts)\(\d+,\d+\)/gim, "$1(--,--)"); yield [diagnosticSummaryMarker, topDiagnostics + IO.newLine() + IO.newLine(), diagnostics.length]; @@ -666,7 +666,7 @@ export namespace Compiler { // Calculate the start of the squiggle const squiggleStart = Math.max(0, relativeOffset); // TODO/REVIEW: this doesn't work quite right in the browser if a multi file test has files whose names are just the right length relative to one another - outputLines += newLine() + " " + line.substr(0, squiggleStart).replace(/[^\s]/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~"); + outputLines += newLine() + " " + line.substr(0, squiggleStart).replace(/\S/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~"); // If the error ended here, or we're at the end of the file, emit its message if ((lineIndex === lines.length - 1) || nextLineStart > end) { @@ -862,7 +862,7 @@ export namespace Compiler { for (const file of allFiles) { const { unitName } = file; let typeLines = "=== " + unitName + " ===\r\n"; - const codeLines = ts.flatMap(file.content.split(/\r?\n/g), e => e.split(/[\r\u2028\u2029]/g)); + const codeLines = ts.flatMap(file.content.split(/\r?\n/), e => e.split(/[\r\u2028\u2029]/)); const gen: IterableIterator = isSymbolBaseline ? fullWalker.getSymbols(unitName) : fullWalker.getTypes(unitName); let lastIndexWritten: number | undefined; for (const result of gen) { @@ -1103,7 +1103,7 @@ function splitVaryBySettingValue(text: string, varyBy: string): string[] | undef let star = false; const includes: string[] = []; const excludes: string[] = []; - for (let s of text.split(/,/g)) { + for (let s of text.split(/,/)) { s = s.trim().toLowerCase(); if (s.length === 0) continue; if (s === "*") { @@ -1247,8 +1247,8 @@ export namespace TestCaseParser { } // Regex for parsing options in the format "@Alpha: Value of any sort" - const optionRegex = /^[/]{2}\s*@(\w+)\s*:\s*([^\r\n]*)/gm; // multiple matches on multiple lines - const linkRegex = /^[/]{2}\s*@link\s*:\s*([^\r\n]*)\s*->\s*([^\r\n]*)/gm; // multiple matches on multiple lines + const optionRegex = /^\/{2}\s*@(\w+)\s*:\s*([^\r\n]*)/gm; // multiple matches on multiple lines + const linkRegex = /^\/{2}\s*@link\s*:\s*([^\r\n]*)\s*->\s*([^\r\n]*)/gm; // multiple matches on multiple lines export function parseSymlinkFromTest(line: string, symlinks: vfs.FileSet | undefined, absoluteRootDir?: string) { const linkMetaData = linkRegex.exec(line); diff --git a/src/harness/harnessUtils.ts b/src/harness/harnessUtils.ts index c93cf652656..372202d08e4 100644 --- a/src/harness/harnessUtils.ts +++ b/src/harness/harnessUtils.ts @@ -333,7 +333,7 @@ const maxHarnessFrames = 1; export function filterStack(error: Error, stackTraceLimit = Infinity) { const stack = (error as any).stack as string; if (stack) { - const lines = stack.split(/\r\n?|\n/g); + const lines = stack.split(/\r\n?|\n/); const filtered: string[] = []; let frameCount = 0; let harnessFrameCount = 0; @@ -355,7 +355,7 @@ export function filterStack(error: Error, stackTraceLimit = Infinity) { harnessFrameCount++; } - line = line.replace(/\bfile:\/\/\/(.*?)(?=(:\d+)*($|\)))/, (_, path) => ts.sys.resolvePath(path)); + line = line.replace(/\bfile:\/\/\/(.*?)(?=(?::\d+)*(?:$|\)))/, (_, path) => ts.sys.resolvePath(path)); frameCount++; } @@ -373,11 +373,11 @@ function isStackFrame(line: string) { } function isMocha(line: string) { - return /[\\/](node_modules|components)[\\/]mocha(js)?[\\/]|[\\/]mocha\.js/.test(line); + return /[\\/](?:node_modules|components)[\\/]mocha(?:js)?[\\/]|[\\/]mocha\.js/.test(line); } function isNode(line: string) { - return /\((timers|events|node|module)\.js:/.test(line); + return /\((?:timers|events|node|module)\.js:/.test(line); } function isHarness(line: string) { diff --git a/src/harness/incrementalUtils.ts b/src/harness/incrementalUtils.ts index 72df2718621..42464ceb4c1 100644 --- a/src/harness/incrementalUtils.ts +++ b/src/harness/incrementalUtils.ts @@ -159,7 +159,7 @@ function getProgramStructure(program: ts.Program | undefined) { const baseline: string[] = []; program?.getSourceFiles().slice().sort((f1, f2) => ts.comparePathsCaseSensitive(f1.path, f2.path)).forEach(f => { baseline.push(` File: ${f.fileName} Path: ${f.path} ResolvedPath: ${f.resolvedPath} impliedNodeFormat: ${f.impliedNodeFormat}`); - baseline.push(f.text.split(/\r?\n/g).map(l => l ? " " + l : "").join("\n")); + baseline.push(f.text.split(/\r?\n/).map(l => l ? " " + l : "").join("\n")); getResolutionCacheDetails( baseline, "Modules", diff --git a/src/harness/runnerbase.ts b/src/harness/runnerbase.ts index 17c6e5cb556..7173eb3ab4f 100644 --- a/src/harness/runnerbase.ts +++ b/src/harness/runnerbase.ts @@ -55,7 +55,7 @@ export abstract class RunnerBase { /** Replaces instances of full paths with fileNames only */ static removeFullPaths(path: string) { // If its a full path (starts with "C:" or "/") replace with just the filename - let fixedPath = /^(\w:|\/)/.test(path) ? ts.getBaseFileName(path) : path; + let fixedPath = /^(?:\w:|\/)/.test(path) ? ts.getBaseFileName(path) : path; // when running in the browser the 'full path' is the host name, shows up in error baselines const localHost = /http:\/localhost:\d+/g; diff --git a/src/harness/typeWriter.ts b/src/harness/typeWriter.ts index 96bd527077d..6bee2b5fe9f 100644 --- a/src/harness/typeWriter.ts +++ b/src/harness/typeWriter.ts @@ -321,7 +321,7 @@ export class TypeWriterWalker { const declSourceFile = declaration.getSourceFile(); const declLineAndCharacter = declSourceFile.getLineAndCharacterOfPosition(declaration.pos); const fileName = ts.getBaseFileName(declSourceFile.fileName); - const isLibFile = /lib(.*)\.d\.ts/i.test(fileName); + const isLibFile = /lib.*\.d\.ts/i.test(fileName); const declText = `Decl(${fileName}, ${isLibFile ? "--" : declLineAndCharacter.line}, ${isLibFile ? "--" : declLineAndCharacter.character})`; symbolString += declText; (declaration as any).__symbolTestOutputCache = declText; diff --git a/src/harness/util.ts b/src/harness/util.ts index 4b0f7e28b9a..a31a60ada18 100644 --- a/src/harness/util.ts +++ b/src/harness/util.ts @@ -4,13 +4,13 @@ import * as ts from "./_namespaces/ts.js"; * Common utilities */ -const testPathPrefixRegExp = /(?:(file:\/{3})|\/)\.(ts|lib|src)\//g; +const testPathPrefixRegExp = /(?:(file:\/{3})|\/)\.(?:ts|lib|src)\//g; export function removeTestPathPrefixes(text: string, retainTrailingDirectorySeparator?: boolean): string { return text !== undefined ? text.replace(testPathPrefixRegExp, (_, scheme) => scheme || (retainTrailingDirectorySeparator ? "/" : "")) : undefined!; // TODO: GH#18217 } function createDiagnosticMessageReplacer string[]>(diagnosticMessage: ts.DiagnosticMessage, replacer: R) { - const messageParts = diagnosticMessage.message.split(/{\d+}/g); + const messageParts = diagnosticMessage.message.split(/\{\d+\}/); const regExp = new RegExp(`^(?:${messageParts.map(ts.regExpEscape).join("(.*?)")})$`); type Args = R extends (messageArgs: string[], ...args: infer A) => string[] ? A : []; return (text: string, ...args: Args) => text.replace(regExp, (_, ...fixedArgs) => ts.formatStringFromArgs(diagnosticMessage.message, replacer(fixedArgs, ...args))); diff --git a/src/harness/vpathUtil.ts b/src/harness/vpathUtil.ts index c7c1ef4ce47..fa26c4a7fd1 100644 --- a/src/harness/vpathUtil.ts +++ b/src/harness/vpathUtil.ts @@ -26,7 +26,7 @@ export import changeExtension = ts.changeAnyExtension; export import isTypeScript = ts.hasTSFileExtension; export import isJavaScript = ts.hasJSFileExtension; -const invalidRootComponentRegExp = /^(?!(\/|\/\/\w+\/|[a-zA-Z]:\/?|)$)/; +const invalidRootComponentRegExp = /^(?!(?:\/|\/\/\w+\/|[a-z]:\/?)?$)/i; const invalidNavigableComponentRegExp = /[:*?"<>|]/; const invalidNavigableComponentWithWildcardsRegExp = /[:"<>|]/; const invalidNonNavigableComponentRegExp = /^\.{1,2}$|[:*?"<>|]/; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 14427e48b45..012873f2112 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -5041,7 +5041,7 @@ export class ProjectService { if ( !pluginConfigEntry.name || isExternalModuleNameRelative(pluginConfigEntry.name) || - /[\\/]\.\.?($|[\\/])/.test(pluginConfigEntry.name) + /[\\/]\.\.?(?:$|[\\/])/.test(pluginConfigEntry.name) ) { this.logger.info(`Skipped loading plugin ${pluginConfigEntry.name || JSON.stringify(pluginConfigEntry)} because only package name is allowed plugin name`); return; diff --git a/src/services/classifier.ts b/src/services/classifier.ts index 0e9e509edca..cf2b087aeb8 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -927,9 +927,9 @@ export function getEncodedSyntacticClassifications(cancellationToken: Cancellati } function tryClassifyTripleSlashComment(start: number, width: number): boolean { - const tripleSlashXMLCommentRegEx = /^(\/\/\/\s*)(<)(?:(\S+)((?:[^/]|\/[^>])*)(\/>)?)?/im; + const tripleSlashXMLCommentRegEx = /^(\/\/\/\s*)(<)(?:(\S+)((?:[^/]|\/[^>])*)(\/>)?)?/m; // Require a leading whitespace character (the parser already does) to prevent terrible backtracking performance - const attributeRegex = /(\s)(\S+)(\s*)(=)(\s*)('[^']+'|"[^"]+")/img; + const attributeRegex = /(\s)(\S+)(\s*)(=)(\s*)('[^']+'|"[^"]+")/g; const text = sourceFile.text.substr(start, width); const match = tripleSlashXMLCommentRegEx.exec(text); diff --git a/src/services/codefixes/convertTypedefToType.ts b/src/services/codefixes/convertTypedefToType.ts index 70d02fb8466..51e6290d46e 100644 --- a/src/services/codefixes/convertTypedefToType.ts +++ b/src/services/codefixes/convertTypedefToType.ts @@ -147,7 +147,7 @@ function findEndOfTextBetween(jsDocComment: JSDoc, from: number, to: number): nu const comment = jsDocComment.getText().substring(from - jsDocComment.getStart(), to - jsDocComment.getStart()); for (let i = comment.length; i > 0; i--) { - if (!/[*/\s]/g.test(comment.substring(i - 1, i))) { + if (!/[*/\s]/.test(comment.substring(i - 1, i))) { return from + i; } } diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index fc2a316361c..6ca2a1804a8 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -1100,5 +1100,5 @@ function cleanText(text: string): string { // \r - Carriage Return // \u2028 - Line separator // \u2029 - Paragraph separator - return text.replace(/\\?(\r?\n|\r|\u2028|\u2029)/g, ""); + return text.replace(/\\?(?:\r?\n|[\r\u2028\u2029])/g, ""); } diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 19d1438c6ea..0254620c1e6 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -138,14 +138,14 @@ function addRegionOutliningSpans(sourceFile: SourceFile, out: OutliningSpan[]): for (const currentLineStart of lineStarts) { const lineEnd = sourceFile.getLineEndOfPosition(currentLineStart); const lineText = sourceFile.text.substring(currentLineStart, lineEnd); - const result = isRegionDelimiter(lineText); + const result = parseRegionDelimiter(lineText); if (!result || isInComment(sourceFile, currentLineStart)) { continue; } - if (!result[1]) { + if (result.isStart) { const span = createTextSpanFromBounds(sourceFile.text.indexOf("//", currentLineStart), lineEnd); - regions.push(createOutliningSpan(span, OutliningSpanKind.Region, span, /*autoCollapse*/ false, result[2] || "#region")); + regions.push(createOutliningSpan(span, OutliningSpanKind.Region, span, /*autoCollapse*/ false, result.name || "#region")); } else { const region = regions.pop(); @@ -158,8 +158,8 @@ function addRegionOutliningSpans(sourceFile: SourceFile, out: OutliningSpan[]): } } -const regionDelimiterRegExp = /^#(end)?region(?:\s+(.*))?(?:\r)?$/; -function isRegionDelimiter(lineText: string) { +const regionDelimiterRegExp = /^#(end)?region(.*)\r?$/; +function parseRegionDelimiter(lineText: string) { // We trim the leading whitespace and // without the regex since the // multiple potential whitespace matches can make for some gnarly backtracking behavior lineText = lineText.trimStart(); @@ -167,7 +167,11 @@ function isRegionDelimiter(lineText: string) { return null; // eslint-disable-line no-restricted-syntax } lineText = lineText.slice(2).trim(); - return regionDelimiterRegExp.exec(lineText); + const result = regionDelimiterRegExp.exec(lineText); + if (result) { + return { isStart: !result[1], name: result[2].trim() }; + } + return undefined; } function addOutliningForLeadingCommentsForPos(pos: number, sourceFile: SourceFile, cancellationToken: CancellationToken, out: OutliningSpan[]): void { @@ -184,7 +188,7 @@ function addOutliningForLeadingCommentsForPos(pos: number, sourceFile: SourceFil case SyntaxKind.SingleLineCommentTrivia: // never fold region delimiters into single-line comment regions const commentText = sourceText.slice(pos, end); - if (isRegionDelimiter(commentText)) { + if (parseRegionDelimiter(commentText)) { combineAndAddMultipleSingleLineComments(); singleLineCommentCount = 0; break; diff --git a/src/services/services.ts b/src/services/services.ts index f20e99cc88c..5bccc5fc677 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3166,7 +3166,7 @@ export function createLanguageService( // // The following three regexps are used to match the start of the text up to the TODO // comment portion. - const singleLineCommentStart = /(?:\/\/+\s*)/.source; + const singleLineCommentStart = /(?:\/{2,}\s*)/.source; const multiLineCommentStart = /(?:\/\*+\s*)/.source; const anyNumberOfSpacesAndAsterisksAtStartOfLine = /(?:^(?:\s|\*)*)/.source; diff --git a/src/services/sourcemaps.ts b/src/services/sourcemaps.ts index dffc7f51ea8..a8c168eb320 100644 --- a/src/services/sourcemaps.ts +++ b/src/services/sourcemaps.ts @@ -27,7 +27,7 @@ import { tryParseRawSourceMap, } from "./_namespaces/ts.js"; -const base64UrlRegExp = /^data:(?:application\/json(?:;charset=[uU][tT][fF]-8);base64,([A-Za-z0-9+/=]+)$)?/; +const base64UrlRegExp = /^data:(?:application\/json;charset=[uU][tT][fF]-8;base64,([A-Za-z0-9+/=]+)$)?/; /** @internal */ export interface SourceMapper { diff --git a/src/testRunner/runner.ts b/src/testRunner/runner.ts index b460ab810c5..f33b71114e1 100644 --- a/src/testRunner/runner.ts +++ b/src/testRunner/runner.ts @@ -31,7 +31,7 @@ function runTests(runners: RunnerBase[]) { for (const full of runner.enumerateTestFiles()) { const base = vpath.basename(full).toLowerCase(); // allow existing dupes in fourslash/shims and fourslash/server - if (seen.has(base) && !/fourslash\/(shim|server)/.test(full)) { + if (seen.has(base) && !/fourslash\/(?:shim|server)/.test(full)) { dupes.push([seen.get(base)!, full]); } else { @@ -52,7 +52,7 @@ function tryGetConfig(args: string[]) { const prefix = "--config="; const configPath = ts.forEach(args, arg => arg.lastIndexOf(prefix, 0) === 0 && arg.substr(prefix.length)); // strip leading and trailing quotes from the path (necessary on Windows since shell does not do it automatically) - return configPath && configPath.replace(/(^["'])|(["']$)/g, ""); + return configPath && configPath.replace(/^["']|["']$/g, ""); } export function createRunner(kind: TestRunnerKind): RunnerBase { diff --git a/src/testRunner/unittests/config/initializeTSConfig.ts b/src/testRunner/unittests/config/initializeTSConfig.ts index 713c693d0e8..916bc9f3ac3 100644 --- a/src/testRunner/unittests/config/initializeTSConfig.ts +++ b/src/testRunner/unittests/config/initializeTSConfig.ts @@ -6,7 +6,7 @@ describe("unittests:: config:: initTSConfig", () => { describe(name, () => { const commandLine = ts.parseCommandLine(commandLinesArgs); const initResult = ts.generateTSConfig(commandLine.options, commandLine.fileNames, "\n"); - const outputFileName = `config/initTSConfig/${name.replace(/[^a-z0-9\-. ]/ig, "")}/tsconfig.json`; + const outputFileName = `config/initTSConfig/${name.replace(/[^a-z0-9\-. ]/gi, "")}/tsconfig.json`; it(`Correct output for ${outputFileName}`, () => { Harness.Baseline.runBaseline(outputFileName, initResult, { PrintDiff: true }); diff --git a/src/testRunner/unittests/config/showConfig.ts b/src/testRunner/unittests/config/showConfig.ts index 0061df800a2..350b18c06ed 100644 --- a/src/testRunner/unittests/config/showConfig.ts +++ b/src/testRunner/unittests/config/showConfig.ts @@ -4,7 +4,7 @@ import * as ts from "../../_namespaces/ts.js"; describe("unittests:: config:: showConfig", () => { function showTSConfigCorrectly(name: string, commandLinesArgs: string[], configJson?: object) { describe(name, () => { - const outputFileName = `config/showConfig/${name.replace(/[^a-z0-9\-./ ]/ig, "")}/tsconfig.json`; + const outputFileName = `config/showConfig/${name.replace(/[^a-z0-9\-./ ]/gi, "")}/tsconfig.json`; it(`Correct output for ${outputFileName}`, () => { const cwd = `/${name}`; diff --git a/src/testRunner/unittests/helpers/baseline.ts b/src/testRunner/unittests/helpers/baseline.ts index 7f5cc3e7390..aeff3885b94 100644 --- a/src/testRunner/unittests/helpers/baseline.ts +++ b/src/testRunner/unittests/helpers/baseline.ts @@ -7,8 +7,8 @@ import { TestServerHost } from "./virtualFileSystemWithWatch.js"; export function sanitizeSysOutput(output: string) { return output - .replace(/Elapsed::\s[0-9]+(?:\.\d+)?ms/g, "Elapsed:: *ms") - .replace(/[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\s(A|P)M/g, "HH:MM:SS AM"); + .replace(/Elapsed::\s\d+(?:\.\d+)?ms/g, "Elapsed:: *ms") + .replace(/\d\d:\d\d:\d\d\s(?:A|P)M/g, "HH:MM:SS AM"); } export type CommandLineProgram = [ts.Program, ts.BuilderProgram?]; diff --git a/src/testRunner/unittests/jsonParserRecovery.ts b/src/testRunner/unittests/jsonParserRecovery.ts index fb03cbf045a..84458544784 100644 --- a/src/testRunner/unittests/jsonParserRecovery.ts +++ b/src/testRunner/unittests/jsonParserRecovery.ts @@ -7,7 +7,7 @@ describe("unittests:: jsonParserRecovery", () => { const file = ts.parseJsonText(name, text); assert(file.parseDiagnostics.length, "Should have parse errors"); Harness.Baseline.runBaseline( - `jsonParserRecovery/${name.replace(/[^a-z0-9_-]/ig, "_")}.errors.txt`, + `jsonParserRecovery/${name.replace(/[^\w-]/g, "_")}.errors.txt`, Harness.Compiler.getErrorBaseline([{ content: text, unitName: name, diff --git a/src/testRunner/unittests/printer.ts b/src/testRunner/unittests/printer.ts index 86a7b41d167..2e44c6cb73a 100644 --- a/src/testRunner/unittests/printer.ts +++ b/src/testRunner/unittests/printer.ts @@ -126,7 +126,7 @@ describe("unittests:: PrinterAPI", () => { const file = program.getSourceFile("/test.d.ts")!; const printer = ts.createPrinter({ newLine: ts.NewLineKind.CarriageReturnLineFeed }); const output = printer.printFile(file); - assert.equal(output.split(/\r?\n/g).length, 3); + assert.equal(output.split(/\r?\n/).length, 3); }); it("with statements", () => { const host = new fakes.CompilerHost( @@ -140,7 +140,7 @@ describe("unittests:: PrinterAPI", () => { const file = program.getSourceFile("/test.d.ts")!; const printer = ts.createPrinter({ newLine: ts.NewLineKind.CarriageReturnLineFeed }); const output = printer.printFile(file); - assert.equal(output.split(/\r?\n/g).length, 4); + assert.equal(output.split(/\r?\n/).length, 4); }); }); diff --git a/src/testRunner/unittests/services/transpile.ts b/src/testRunner/unittests/services/transpile.ts index 640923c53c6..3a1d1c24c9a 100644 --- a/src/testRunner/unittests/services/transpile.ts +++ b/src/testRunner/unittests/services/transpile.ts @@ -40,7 +40,7 @@ describe("unittests:: services:: Transpile", () => { transpileOptions.reportDiagnostics = true; - const justName = "transpile/" + name.replace(/[^a-z0-9\-. ()=]/ig, "") + (transpileOptions.compilerOptions.jsx ? ts.Extension.Tsx : ts.Extension.Ts); + const justName = "transpile/" + name.replace(/[^a-z0-9\-. ()=]/gi, "") + (transpileOptions.compilerOptions.jsx ? ts.Extension.Tsx : ts.Extension.Ts); const toBeCompiled = [{ unitName, content: input, diff --git a/src/testRunner/unittests/tsserver/cachingFileSystemInformation.ts b/src/testRunner/unittests/tsserver/cachingFileSystemInformation.ts index 44ea2d57e82..f87ebd36b37 100644 --- a/src/testRunner/unittests/tsserver/cachingFileSystemInformation.ts +++ b/src/testRunner/unittests/tsserver/cachingFileSystemInformation.ts @@ -526,7 +526,7 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS ts.forEach(filesAndFoldersToAdd, f => { f.path = f.path .replace("/a/b/node_modules/.staging", "/a/b/node_modules") - .replace(/[-.][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w]/g, ""); + .replace(/[-.]\w\w\w\w\w\w\w\w/g, ""); }); host.deleteFolder(root + "/a/b/node_modules/.staging", /*recursive*/ true);