/*--------------------------------------------------------------------------------------------- * 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; }