diff --git a/build/hygiene.js b/build/hygiene.js index 2b5bc151303..bc64a11f8e9 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -23,7 +23,7 @@ const copyrightHeaderLines = [ function hygiene(some, linting = true) { const gulpeslint = require('gulp-eslint'); const gulpstylelint = require('./stylelint'); - const tsfmt = require('typescript-formatter'); + const formatter = require('./lib/formatter'); let errorCount = 0; @@ -111,38 +111,23 @@ function hygiene(some, linting = true) { }); const formatting = es.map(function (file, cb) { - tsfmt - .processString(file.path, file.contents.toString('utf8'), { - verify: false, - tsfmt: true, - // verbose: true, - // keep checkJS happy - editorconfig: undefined, - replace: undefined, - tsconfig: undefined, - tsconfigFile: undefined, - tsfmtFile: undefined, - vscode: undefined, - vscodeFile: undefined, - }) - .then( - (result) => { - const original = result.src.replace(/\r\n/gm, '\n'); - const formatted = result.dest.replace(/\r\n/gm, '\n'); + try { + const rawInput = file.contents.toString('utf8'); + const rawOutput = formatter.format(file.path, rawInput); - if (original !== formatted) { - console.error( - `File not formatted. Run the 'Format Document' command to fix it:`, - file.relative - ); - errorCount++; - } - cb(null, file); - }, - (err) => { - cb(err); - } - ); + const original = rawInput.replace(/\r\n/gm, '\n'); + const formatted = rawOutput.replace(/\r\n/gm, '\n'); + if (original !== formatted) { + console.error( + `File not formatted. Run the 'Format Document' command to fix it:`, + file.relative + ); + errorCount++; + } + cb(null, file); + } catch (err) { + cb(err); + } }); let input; diff --git a/build/lib/formatter.js b/build/lib/formatter.js new file mode 100644 index 00000000000..29f265c8289 --- /dev/null +++ b/build/lib/formatter.js @@ -0,0 +1,76 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.format = format; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +const fs = require("fs"); +const path = require("path"); +const ts = require("typescript"); +class LanguageServiceHost { + files = {}; + addFile(fileName, text) { + this.files[fileName] = ts.ScriptSnapshot.fromString(text); + } + fileExists(path) { + return !!this.files[path]; + } + readFile(path) { + return this.files[path]?.getText(0, this.files[path].getLength()); + } + // for ts.LanguageServiceHost + getCompilationSettings = () => ts.getDefaultCompilerOptions(); + getScriptFileNames = () => Object.keys(this.files); + getScriptVersion = (_fileName) => '0'; + getScriptSnapshot = (fileName) => this.files[fileName]; + getCurrentDirectory = () => process.cwd(); + getDefaultLibFileName = (options) => ts.getDefaultLibFilePath(options); +} +const defaults = { + baseIndentSize: 0, + indentSize: 4, + tabSize: 4, + indentStyle: ts.IndentStyle.Smart, + newLineCharacter: '\r\n', + convertTabsToSpaces: false, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterConstructor: false, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, + insertSpaceAfterTypeAssertion: false, + insertSpaceBeforeFunctionParenthesis: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, + insertSpaceBeforeTypeAnnotation: false, +}; +const getOverrides = (() => { + let value; + return () => { + value ??= JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'tsfmt.json'), 'utf8')); + return value; + }; +})(); +function format(fileName, text) { + const host = new LanguageServiceHost(); + host.addFile(fileName, text); + const languageService = ts.createLanguageService(host); + const edits = languageService.getFormattingEditsForDocument(fileName, { ...defaults, ...getOverrides() }); + edits + .sort((a, b) => a.span.start - b.span.start) + .reverse() + .forEach(edit => { + const head = text.slice(0, edit.span.start); + const tail = text.slice(edit.span.start + edit.span.length); + text = `${head}${edit.newText}${tail}`; + }); + return text; +} +//# sourceMappingURL=formatter.js.map \ No newline at end of file diff --git a/build/lib/formatter.ts b/build/lib/formatter.ts new file mode 100644 index 00000000000..0d9035b3d87 --- /dev/null +++ b/build/lib/formatter.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; +import * as path from 'path'; +import * as ts from 'typescript'; + + +class LanguageServiceHost implements ts.LanguageServiceHost { + files: ts.MapLike = {}; + addFile(fileName: string, text: string) { + this.files[fileName] = ts.ScriptSnapshot.fromString(text); + } + + fileExists(path: string): boolean { + return !!this.files[path]; + } + + readFile(path: string): string | undefined { + return this.files[path]?.getText(0, this.files[path]!.getLength()); + } + + // for ts.LanguageServiceHost + + getCompilationSettings = () => ts.getDefaultCompilerOptions(); + getScriptFileNames = () => Object.keys(this.files); + getScriptVersion = (_fileName: string) => '0'; + getScriptSnapshot = (fileName: string) => this.files[fileName]; + getCurrentDirectory = () => process.cwd(); + getDefaultLibFileName = (options: ts.CompilerOptions) => ts.getDefaultLibFilePath(options); +} + +const defaults: ts.FormatCodeSettings = { + baseIndentSize: 0, + indentSize: 4, + tabSize: 4, + indentStyle: ts.IndentStyle.Smart, + newLineCharacter: '\r\n', + convertTabsToSpaces: false, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterConstructor: false, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, + insertSpaceAfterTypeAssertion: false, + insertSpaceBeforeFunctionParenthesis: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, + insertSpaceBeforeTypeAnnotation: false, +}; + +const getOverrides = (() => { + let value: ts.FormatCodeSettings | undefined; + return () => { + value ??= JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'tsfmt.json'), 'utf8')); + return value; + }; +})(); + +export function format(fileName: string, text: string) { + + const host = new LanguageServiceHost(); + host.addFile(fileName, text); + + const languageService = ts.createLanguageService(host); + const edits = languageService.getFormattingEditsForDocument(fileName, { ...defaults, ...getOverrides() }); + edits + .sort((a, b) => a.span.start - b.span.start) + .reverse() + .forEach(edit => { + const head = text.slice(0, edit.span.start); + const tail = text.slice(edit.span.start + edit.span.length); + text = `${head}${edit.newText}${tail}`; + }); + + return text; +} diff --git a/package.json b/package.json index ce9dd822377..dcde9cd66e9 100644 --- a/package.json +++ b/package.json @@ -208,7 +208,6 @@ "ts-node": "^10.9.1", "tsec": "0.2.7", "typescript": "^5.5.0-dev.20240408", - "typescript-formatter": "7.1.0", "util": "^0.12.4", "vscode-nls-dev": "^3.3.1", "webpack": "^5.91.0", diff --git a/yarn.lock b/yarn.lock index a9ab6181dd6..0079751ce13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1021,13 +1021,6 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== -"@types/commander@^2.11.0": - version "2.12.2" - resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae" - integrity sha512-0QEFiR8ljcHp9bAbWxecjVRuAMr16ivPiGOw6KFQBVrVd0RQIcM3xKdRisH2EDWgVWujiYtHwhSkSUoAAGzH7Q== - dependencies: - commander "*" - "@types/cookie@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" @@ -1214,7 +1207,7 @@ dependencies: "@types/node" "*" -"@types/semver@^5.4.0", "@types/semver@^5.5.0": +"@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== @@ -3090,11 +3083,6 @@ command-line-args@^5.2.1: lodash.camelcase "^4.3.0" typical "^4.0.0" -commander@*: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - commander@2.11.x: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -3105,7 +3093,7 @@ commander@^10.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -commander@^2.11.0, commander@^2.19.0, commander@^2.20.0: +commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3125,11 +3113,6 @@ commander@^9.4.0: resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== -commandpost@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/commandpost/-/commandpost-1.2.1.tgz#2e9c4c7508b9dc704afefaa91cab92ee6054cc68" - integrity sha512-V1wzc+DTFsO96te2W/U+fKNRSOWtOwXhkkZH2WRLLbucrY+YrDNsRr4vtfSf83MUZVF3E6B4nwT30fqaTpzipQ== - comment-parser@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.0.tgz#0f8c560f59698193854f12884c20c0e39a26d32c" @@ -3757,18 +3740,6 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -editorconfig@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.0.tgz#b6dd4a0b6b9e76ce48e066bdc15381aebb8804fd" - integrity sha512-j7JBoj/bpNzvoTQylfRZSc85MlLNKWQiq5y6gwKhmqD2h1eZ+tH4AXbkhEJD468gjDna/XMx2YtSkCxBRX9OGg== - dependencies: - "@types/commander" "^2.11.0" - "@types/semver" "^5.4.0" - commander "^2.11.0" - lru-cache "^4.1.1" - semver "^5.4.1" - sigmund "^1.0.1" - editorconfig@^0.15.2: version "0.15.2" resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.2.tgz#047be983abb9ab3c2eefe5199cb2b7c5689f0702" @@ -6571,14 +6542,6 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.2.tgz#45234b2e6e2f2b33da125624c4664929a0224c3f" - integrity sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^4.1.3: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -8750,7 +8713,7 @@ semver-greatest-satisfied-range@^1.1.0: dependencies: sver-compat "^1.5.0" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -9926,14 +9889,6 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript-formatter@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/typescript-formatter/-/typescript-formatter-7.1.0.tgz#dd1b5547de211065221f765263e15f18c84c66b8" - integrity sha512-XgPUSZ3beF7Xx2ZIEngIonWpDTS0XzWqV0vjtcm6nOPONug4WFXQYjbvulCzY2T0+knceZn5CFQjVUShNkIdLA== - dependencies: - commandpost "^1.0.0" - editorconfig "^0.15.0" - typescript@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"