diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 068aadc4e..b5ef6254b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -271,6 +271,7 @@ module.exports = { "tests/format/**/jsfmt.spec.js", "tests/integration/**/*.js", "tests/unit/**/*.js", + "tests/dts/unit/**/*.js", "scripts/release/__tests__/**/*.spec.js", ], env: { diff --git a/changelog_unreleased/api/14212.md b/changelog_unreleased/api/14212.md new file mode 100644 index 000000000..87312673f --- /dev/null +++ b/changelog_unreleased/api/14212.md @@ -0,0 +1,3 @@ +#### Add `.d.ts` files (#14212 by @sosukesuzuki, @fisker) + +Add type definition files required to use Prettier's JavaScript API from TypeScript. This eliminates the need for users to install `@types/prettier`. diff --git a/jest.config.mjs b/jest.config.mjs index 745294a9b..f3b9ad3f8 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -61,6 +61,7 @@ const config = { "/tests/format/**/jsfmt.spec.js", "/tests/integration/__tests__/**/*.js", "/tests/unit/**/*.js", + "/tests/dts/unit/**/*.js", ], testPathIgnorePatterns, // collectCoverage: ENABLE_CODE_COVERAGE, diff --git a/package.json b/package.json index 67a7b9e17..2529bf770 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "exports": { ".": { "require": "./src/index.cjs", + "types": "./src/index.d.ts", "default": "./src/index.js" }, "./*": "./*" @@ -147,7 +148,8 @@ "semver": "7.3.8", "serialize-javascript": "6.0.1", "snapshot-diff": "0.10.0", - "tempy": "3.0.0" + "tempy": "3.0.0", + "ts-expect": "1.3.0" }, "resolutions": { "trim": "1.0.1" @@ -192,7 +194,8 @@ "src/index.cjs", "src/document/debug.js", "src/utils/unexpected-node-error.js", - "src/language-js/types/estree.d.ts" + "src/language-js/types/estree.d.ts", + "src/**/**/*.d.ts" ] }, "browserslist": [ diff --git a/scripts/build/build-license.js b/scripts/build/build-license.js index 4c0d215c2..57bfdf5d6 100644 --- a/scripts/build/build-license.js +++ b/scripts/build/build-license.js @@ -128,7 +128,7 @@ async function buildLicense({ file, files, shouldCollectLicenses }) { return; } - const javascriptFiles = files.filter((file) => !file.isMetaFile); + const javascriptFiles = files.filter((file) => file.kind === "javascript"); if (javascriptFiles.some((file) => !Array.isArray(file.dependencies))) { return { skipped: true }; } diff --git a/scripts/build/build-package-json.js b/scripts/build/build-package-json.js index 8daf79a6d..ae13d7a4d 100644 --- a/scripts/build/build-package-json.js +++ b/scripts/build/build-package-json.js @@ -6,8 +6,8 @@ import { writeJson, } from "../utils/index.mjs"; -async function buildPackageJson({ files }) { - const packageJson = await readJson(path.join(PROJECT_ROOT, "package.json")); +async function buildPackageJson({ file, files }) { + const packageJson = await readJson(path.join(PROJECT_ROOT, file.input)); const bin = files.find( (file) => @@ -20,6 +20,7 @@ async function buildPackageJson({ files }) { packageJson.exports = { ".": { require: "./index.cjs", + types: "./index.d.ts", default: "./index.mjs", }, "./*": "./*", @@ -32,6 +33,7 @@ async function buildPackageJson({ files }) { file.isPlugin ? `./plugins/${basename}` : `./${basename}`, { require: `./${file.output.file}`, + types: `./${file.output.file.replace(/\.js$/, ".d.ts")}`, default: `./${file.output.file.replace(/\.js$/, ".mjs")}`, }, ]; @@ -74,7 +76,7 @@ async function buildPackageJson({ files }) { }; packageJson.files = files.map(({ output: { file } }) => file).sort(); - await writeJson(path.join(DIST_DIR, "package.json"), packageJson); + await writeJson(path.join(DIST_DIR, file.output.file), packageJson); } export default buildPackageJson; diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 795f454dc..ed278f223 100644 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -48,7 +48,8 @@ async function buildFile({ file, files, shouldCollectLicenses, cliOptions }) { let displayName = file.output.file; if ( (file.platform === "universal" && file.output.format !== "esm") || - (file.output.file.startsWith("index.") && file.output.format !== "esm") + (file.output.file.startsWith("index.") && file.output.format !== "esm") || + file.kind === "types" ) { displayName = ` ${displayName}`; } diff --git a/scripts/build/config.mjs b/scripts/build/config.mjs index c1a1fac1e..713021e7e 100644 --- a/scripts/build/config.mjs +++ b/scripts/build/config.mjs @@ -1,8 +1,14 @@ import path from "node:path"; import url from "node:url"; +import fs from "node:fs"; import { createRequire } from "node:module"; import createEsmUtils from "esm-utils"; -import { PROJECT_ROOT, DIST_DIR, copyFile } from "../utils/index.mjs"; +import { + PROJECT_ROOT, + DIST_DIR, + copyFile, + writeFile, +} from "../utils/index.mjs"; import buildJavascriptModule from "./build-javascript-module.js"; import buildPackageJson from "./build-package-json.js"; import buildLicense from "./build-license.js"; @@ -16,6 +22,58 @@ const { } = createEsmUtils(import.meta); const resolveEsmModulePath = async (specifier) => url.fileURLToPath(await importMetaResolve(specifier)); +const copyFileBuilder = ({ file }) => + copyFile( + path.join(PROJECT_ROOT, file.input), + path.join(DIST_DIR, file.output.file) + ); + +async function typesFileBuilder({ file }) { + /** + * @typedef {{ from: string, to: string }} ImportPathReplacement + * @typedef {{ [input: string]: Array }} ReplacementMap + */ + + /** @type {Array} */ + const jsParsersImportReplacement = [ + { from: "../../index.js", to: "../index.js" }, + ]; + /** @type {ReplacementMap} */ + const pathReplacementMap = { + "src/index.d.ts": [{ from: "./document/index.js", to: "./doc.js" }], + "src/language-js/parse/acorn-and-espree.d.ts": jsParsersImportReplacement, + "src/language-js/parse/angular.d.ts": jsParsersImportReplacement, + "src/language-js/parse/babel.d.ts": jsParsersImportReplacement, + "src/language-js/parse/flow.d.ts": jsParsersImportReplacement, + "src/language-js/parse/meriyah.d.ts": jsParsersImportReplacement, + "src/language-js/parse/typescript.d.ts": jsParsersImportReplacement, + }; + const replacements = pathReplacementMap[file.input] ?? []; + let data = await fs.promises.readFile(file.input, "utf8"); + for (const { from, to } of replacements) { + data = data.replaceAll( + new RegExp(` from "${from}";`, "g"), + ` from "${to}";` + ); + } + await writeFile(path.join(DIST_DIR, file.output.file), data); +} + +function getTypesFileConfig({ input: jsFileInput, outputBaseName }) { + const input = jsFileInput.replace(/\.[cm]?js$/, ".d.ts"); + if (!fs.existsSync(path.join(PROJECT_ROOT, input))) { + return; + } + + return { + input, + output: { + file: outputBaseName + ".d.ts", + }, + kind: "types", + build: typesFileBuilder, + }; +} /** * @typedef {Object} BuildOptions @@ -25,18 +83,18 @@ const resolveEsmModulePath = async (specifier) => * @property {boolean?} minify - disable code minification * * @typedef {Object} Output - * @property {'esm' | 'umd' | 'cjs' | 'text' | 'json'} format - File format + * @property {"esm" | "umd" | "cjs" | "text" | "json"} format - File format * @property {string} file - path of the output file in the `dist/` folder * @property {string?} umdVariableName - name for the UMD file (for plugins, it'll be `prettierPlugins.${name}`) * * @typedef {Object} File - * @property {string?} input - input of the file + * @property {string} input - input of the file * @property {Output} output - output of the file + * @property {"javascript" | "types" | "meta"} kind - file kind * @property {function} build - file generate function - * @property {'node' | 'universal'} platform - ESBuild platform + * @property {"node" | "universal"} platform - ESBuild platform * @property {BuildOptions} buildOptions - ESBuild options * @property {boolean?} isPlugin - file is a plugin - * @property {boolean?} isMeta - file is a meta file (package.json, LICENSE README.md) */ /* @@ -400,22 +458,27 @@ const universalFiles = [...nonPluginUniversalFiles, ...pluginFiles].flatMap( outputBaseName ??= path.basename(input); return [ - { - format: "esm", - file: `${outputBaseName}${extensions.esm}`, - }, - { - format: "umd", - file: `${outputBaseName}${extensions.umd}`, - umdVariableName, - }, - ].map((output) => ({ - input, - output, - platform: "universal", - buildOptions, - isPlugin, - })); + ...[ + { + format: "esm", + file: `${outputBaseName}${extensions.esm}`, + }, + { + format: "umd", + file: `${outputBaseName}${extensions.umd}`, + umdVariableName, + }, + ].map((output) => ({ + input, + output, + platform: "universal", + buildOptions, + isPlugin, + build: buildJavascriptModule, + kind: "javascript", + })), + getTypesFileConfig({ input, outputBaseName }), + ]; } ); @@ -481,21 +544,26 @@ const nodejsFiles = [ }, ], }, -].map((file) => { +].flatMap((file) => { let { input, output, outputBaseName, ...buildOptions } = file; const format = input.endsWith(".cjs") ? "cjs" : "esm"; outputBaseName ??= path.basename(input, path.extname(input)); - return { - input, - output: { - format, - file: `${outputBaseName}${extensions[format]}`, + return [ + { + input, + output: { + format, + file: `${outputBaseName}${extensions[format]}`, + }, + platform: "node", + buildOptions, + build: buildJavascriptModule, + kind: "javascript", }, - platform: "node", - buildOptions, - }; + getTypesFileConfig({ input, outputBaseName }), + ]; }); const metaFiles = [ @@ -503,37 +571,23 @@ const metaFiles = [ input: "package.json", output: { format: "json", - file: "package.json", }, build: buildPackageJson, }, { - output: { - format: "text", - file: "README.md", - }, - async build() { - await copyFile( - path.join(PROJECT_ROOT, "README.md"), - path.join(DIST_DIR, "README.md") - ); - }, + input: "README.md", + build: copyFileBuilder, }, { - output: { - format: "text", - file: "LICENSE", - }, + input: "LICENSE", build: buildLicense, }, -].map((file) => ({ ...file, isMetaFile: true })); +].map((file) => ({ + ...file, + output: { file: file.input, ...file.output }, + kind: "meta", +})); /** @type {Files[]} */ -const files = [ - ...[...nodejsFiles, ...universalFiles].map((file) => ({ - ...file, - build: buildJavascriptModule, - })), - ...metaFiles, -]; +const files = [...nodejsFiles, ...universalFiles, ...metaFiles].filter(Boolean); export default files; diff --git a/src/cli/prettier-internal.js b/src/cli/prettier-internal.js index 4390e211b..6e7ef8cf6 100644 --- a/src/cli/prettier-internal.js +++ b/src/cli/prettier-internal.js @@ -1,3 +1,4 @@ +// @ts-expect-error import { __internal as sharedWithCli } from "../index.js"; export const { diff --git a/src/cli/utils.js b/src/cli/utils.js index ba0203bd7..81577ce80 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import sdbm from "sdbm"; +// @ts-expect-error import { __internal as sharedWithCli } from "../index.js"; const { arrayify, isNonEmptyArray, partition } = sharedWithCli.utils; diff --git a/src/document/index.d.ts b/src/document/index.d.ts new file mode 100644 index 000000000..b0cdd8f4f --- /dev/null +++ b/src/document/index.d.ts @@ -0,0 +1,248 @@ +// https://github.com/prettier/prettier/blob/next/src/document/index.js +export namespace builders { + type DocCommand = + | Align + | BreakParent + | Cursor + | Fill + | Group + | IfBreak + | Indent + | IndentIfBreak + | Label + | Line + | LineSuffix + | LineSuffixBoundary + | Trim; + type Doc = string | Doc[] | DocCommand; + + interface Align { + type: "align"; + contents: Doc; + n: number | string | { type: "root" }; + } + + interface BreakParent { + type: "break-parent"; + } + + interface Cursor { + type: "cursor"; + placeholder: symbol; + } + + interface Fill { + type: "fill"; + parts: Doc[]; + } + + interface Group { + type: "group"; + contents: Doc; + break: boolean; + expandedStates: Doc[]; + } + + interface HardlineWithoutBreakParent extends Line { + hard: true; + } + + interface IfBreak { + type: "if-break"; + breakContents: Doc; + flatContents: Doc; + } + + interface Indent { + type: "indent"; + contents: Doc; + } + + interface IndentIfBreak { + type: "indent-if-break"; + } + + interface Label { + type: "label"; + } + + interface Line { + type: "line"; + soft?: boolean | undefined; + hard?: boolean | undefined; + literal?: boolean | undefined; + } + + interface LineSuffix { + type: "line-suffix"; + contents: Doc; + } + + interface LineSuffixBoundary { + type: "line-suffix-boundary"; + } + + interface LiterallineWithoutBreakParent extends Line { + hard: true; + literal: true; + } + + type LiteralLine = [LiterallineWithoutBreakParent, BreakParent]; + + interface Softline extends Line { + soft: true; + } + + type Hardline = [HardlineWithoutBreakParent, BreakParent]; + + interface Trim { + type: "trim"; + } + + interface GroupOptions { + shouldBreak?: boolean | undefined; + id?: symbol | undefined; + } + + function addAlignmentToDoc(doc: Doc, size: number, tabWidth: number): Doc; + + /** @see [align](https://github.com/prettier/prettier/blob/main/commands.md#align) */ + function align(widthOrString: Align["n"], doc: Doc): Align; + + /** @see [breakParent](https://github.com/prettier/prettier/blob/main/commands.md#breakparent) */ + const breakParent: BreakParent; + + /** @see [conditionalGroup](https://github.com/prettier/prettier/blob/main/commands.md#conditionalgroup) */ + function conditionalGroup(alternatives: Doc[], options?: GroupOptions): Group; + + /** @see [dedent](https://github.com/prettier/prettier/blob/main/commands.md#dedent) */ + function dedent(doc: Doc): Align; + + /** @see [dedentToRoot](https://github.com/prettier/prettier/blob/main/commands.md#dedenttoroot) */ + function dedentToRoot(doc: Doc): Align; + + /** @see [fill](https://github.com/prettier/prettier/blob/main/commands.md#fill) */ + function fill(docs: Doc[]): Fill; + + /** @see [group](https://github.com/prettier/prettier/blob/main/commands.md#group) */ + function group(doc: Doc, opts?: GroupOptions): Group; + + /** @see [hardline](https://github.com/prettier/prettier/blob/main/commands.md#hardline) */ + const hardline: Hardline; + + /** @see [hardlineWithoutBreakParent](https://github.com/prettier/prettier/blob/main/commands.md#hardlinewithoutbreakparent-and-literallinewithoutbreakparent) */ + const hardlineWithoutBreakParent: HardlineWithoutBreakParent; + + /** @see [ifBreak](https://github.com/prettier/prettier/blob/main/commands.md#ifbreak) */ + function ifBreak( + ifBreak: Doc, + noBreak?: Doc, + options?: { groupId?: symbol | undefined } + ): IfBreak; + + /** @see [indent](https://github.com/prettier/prettier/blob/main/commands.md#indent) */ + function indent(doc: Doc): Indent; + + /** @see [indentIfBreak](https://github.com/prettier/prettier/blob/main/commands.md#indentifbreak) */ + function indentIfBreak( + doc: Doc, + opts: { groupId: symbol; negate?: boolean | undefined } + ): IndentIfBreak; + + /** @see [join](https://github.com/prettier/prettier/blob/main/commands.md#join) */ + function join(sep: Doc, docs: Doc[]): Doc[]; + + /** @see [label](https://github.com/prettier/prettier/blob/main/commands.md#label) */ + function label(label: any | undefined, contents: Doc): Doc; + + /** @see [line](https://github.com/prettier/prettier/blob/main/commands.md#line) */ + const line: Line; + + /** @see [lineSuffix](https://github.com/prettier/prettier/blob/main/commands.md#linesuffix) */ + function lineSuffix(suffix: Doc): LineSuffix; + + /** @see [lineSuffixBoundary](https://github.com/prettier/prettier/blob/main/commands.md#linesuffixboundary) */ + const lineSuffixBoundary: LineSuffixBoundary; + + /** @see [literalline](https://github.com/prettier/prettier/blob/main/commands.md#literalline) */ + const literalline: LiteralLine; + + /** @see [literallineWithoutBreakParent](https://github.com/prettier/prettier/blob/main/commands.md#hardlinewithoutbreakparent-and-literallinewithoutbreakparent) */ + const literallineWithoutBreakParent: LiterallineWithoutBreakParent; + + /** @see [markAsRoot](https://github.com/prettier/prettier/blob/main/commands.md#markasroot) */ + function markAsRoot(doc: Doc): Align; + + /** @see [softline](https://github.com/prettier/prettier/blob/main/commands.md#softline) */ + const softline: Softline; + + /** @see [trim](https://github.com/prettier/prettier/blob/main/commands.md#trim) */ + const trim: Trim; + + /** @see [cursor](https://github.com/prettier/prettier/blob/main/commands.md#cursor) */ + const cursor: Cursor; +} + +export namespace debug { + function printDocToDebug(doc: builders.Doc): string; +} + +export namespace printer { + function printDocToString( + doc: builders.Doc, + options: Options + ): { + formatted: string; + cursorNodeStart?: number | undefined; + cursorNodeText?: string | undefined; + }; + interface Options { + /** + * Specify the line length that the printer will wrap on. + * @default 80 + */ + printWidth: number; + /** + * Specify the number of spaces per indentation-level. + * @default 2 + */ + tabWidth: number; + /** + * Indent lines with tabs instead of spaces + * @default false + */ + useTabs: boolean; + parentParser?: string | undefined; + __embeddedInHtml?: boolean | undefined; + } +} + +export namespace utils { + function getDocParts(doc: builders.Doc): builders.Doc; + function willBreak(doc: builders.Doc): boolean; + function traverseDoc( + doc: builders.Doc, + onEnter?: (doc: builders.Doc) => void | boolean, + onExit?: (doc: builders.Doc) => void, + shouldTraverseConditionalGroups?: boolean + ): void; + function findInDoc( + doc: builders.Doc, + callback: (doc: builders.Doc) => T, + defaultValue: T + ): T; + function mapDoc( + doc: builders.Doc, + callback: (doc: builders.Doc) => T + ): T; + function propagateBreaks(doc: builders.Doc): void; + function removeLines(doc: builders.Doc): builders.Doc; + function stripTrailingHardline(doc: builders.Doc): builders.Doc; + function cleanDoc(doc: builders.Doc): builders.Doc; + function replaceEndOfLine( + doc: builders.Doc, + replacement?: builders.Doc + ): builders.Doc; + function canBreak(doc: builders.Doc): boolean; + function getDocType(doc: builders.Doc): string; +} diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 000000000..7e3294eb0 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,903 @@ +// Copied from `@types/prettier` +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/5bb07fc4b087cb7ee91084afa6fe750551a7bbb1/types/prettier/index.d.ts + +// Minimum TypeScript Version: 4.2 + +// Add `export {}` here to shut off automatic exporting from index.d.ts. There +// are quite a few utility types here that don't need to be shipped with the +// exported module. +export {}; + +import { builders, debug, printer, utils } from "./document/index.js"; + +export namespace doc { + export { builders, debug, printer, utils }; +} + +// This utility is here to handle the case where you have an explicit union +// between string literals and the generic string type. It would normally +// resolve out to just the string type, but this generic LiteralUnion maintains +// the intellisense of the original union. +// +// It comes from this issue: microsoft/TypeScript#29729: +// https://github.com/microsoft/TypeScript/issues/29729#issuecomment-700527227 +export type LiteralUnion = + | T + | (Pick & { _?: never | undefined }); + +export type AST = any; +export type Doc = doc.builders.Doc; + +// The type of elements that make up the given array T. +type ArrayElement = T extends Array ? E : never; + +// A union of the properties of the given object that are arrays. +type ArrayProperties = { + [K in keyof T]: NonNullable extends any[] ? K : never; +}[keyof T]; + +// A union of the properties of the given array T that can be used to index it. +// If the array is a tuple, then that's going to be the explicit indices of the +// array, otherwise it's going to just be number. +type IndexProperties = IsTuple extends true + ? Exclude["length"], T["length"]> + : number; + +// Effectively performing T[P], except that it's telling TypeScript that it's +// safe to do this for tuples, arrays, or objects. +type IndexValue = T extends any[] + ? P extends number + ? T[P] + : never + : P extends keyof T + ? T[P] + : never; + +// Determines if an object T is an array like string[] (in which case this +// evaluates to false) or a tuple like [string] (in which case this evaluates to +// true). +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type IsTuple = T extends [] + ? true + : T extends [infer First, ...infer Remain] + ? IsTuple + : false; + +type CallProperties = T extends any[] ? IndexProperties : keyof T; +type IterProperties = T extends any[] + ? IndexProperties + : ArrayProperties; + +type CallCallback = (path: AstPath, index: number, value: any) => U; +type EachCallback = ( + path: AstPath>, + index: number, + value: any +) => void; +type MapCallback = ( + path: AstPath>, + index: number, + value: any +) => U; + +// https://github.com/prettier/prettier/blob/next/src/common/ast-path.js +export class AstPath { + constructor(value: T); + + get key(): string | null; + get index(): number | null; + get node(): T; + get parent(): T | null; + get grandparent(): T | null; + get isInArray(): boolean; + get siblings(): T[] | null; + get next(): T | null; + get previous(): T | null; + get isFirst(): boolean; + get isLast(): boolean; + get isRoot(): boolean; + get root(): T; + get ancestors(): T[]; + + stack: T[]; + + callParent(callback: (path: this) => U, count?: number): U; + + /** + * @deprecated Please use `AstPath#key` or `AstPath#index` + */ + getName(): PropertyKey | null; + + /** + * @deprecated Please use `AstPath#node` or `AstPath#siblings` + */ + getValue(): T; + + getNode(count?: number): T | null; + + getParentNode(count?: number): T | null; + + match( + ...predicates: Array< + (node: any, name: string | null, number: number | null) => boolean + > + ): boolean; + + // For each of the tree walk functions (call, each, and map) this provides 5 + // strict type signatures, along with a fallback at the end if you end up + // calling more than 5 properties deep. This helps a lot with typing because + // for the majority of cases you're calling fewer than 5 properties, so the + // tree walk functions have a clearer understanding of what you're doing. + // + // Note that resolving these types is somewhat complicated, and it wasn't + // even supported until TypeScript 4.2 (before it would just say that the + // type instantiation was excessively deep and possibly infinite). + + call(callback: CallCallback): U; + call>( + callback: CallCallback, U>, + prop1: P1 + ): U; + call>( + callback: CallCallback, P2>, U>, + prop1: P1, + prop2: P2 + ): U; + call< + U, + P1 extends keyof T, + P2 extends CallProperties, + P3 extends CallProperties> + >( + callback: CallCallback< + IndexValue, P2>, P3>, + U + >, + prop1: P1, + prop2: P2, + prop3: P3 + ): U; + call< + U, + P1 extends keyof T, + P2 extends CallProperties, + P3 extends CallProperties>, + P4 extends CallProperties, P3>> + >( + callback: CallCallback< + IndexValue, P2>, P3>, P4>, + U + >, + prop1: P1, + prop2: P2, + prop3: P3, + prop4: P4 + ): U; + call( + callback: CallCallback, + prop1: P, + prop2: P, + prop3: P, + prop4: P, + ...props: P[] + ): U; + + each(callback: EachCallback): void; + each>( + callback: EachCallback>, + prop1: P1 + ): void; + each>( + callback: EachCallback, P2>>, + prop1: P1, + prop2: P2 + ): void; + each< + P1 extends keyof T, + P2 extends IterProperties, + P3 extends IterProperties> + >( + callback: EachCallback, P2>, P3>>, + prop1: P1, + prop2: P2, + prop3: P3 + ): void; + each< + P1 extends keyof T, + P2 extends IterProperties, + P3 extends IterProperties>, + P4 extends IterProperties, P3>> + >( + callback: EachCallback< + IndexValue, P2>, P3>, P4> + >, + prop1: P1, + prop2: P2, + prop3: P3, + prop4: P4 + ): void; + each( + callback: EachCallback, + prop1: PropertyKey, + prop2: PropertyKey, + prop3: PropertyKey, + prop4: PropertyKey, + ...props: PropertyKey[] + ): void; + + map(callback: MapCallback): U[]; + map>( + callback: MapCallback, U>, + prop1: P1 + ): U[]; + map>( + callback: MapCallback, P2>, U>, + prop1: P1, + prop2: P2 + ): U[]; + map< + U, + P1 extends keyof T, + P2 extends IterProperties, + P3 extends IterProperties> + >( + callback: MapCallback, P2>, P3>, U>, + prop1: P1, + prop2: P2, + prop3: P3 + ): U[]; + map< + U, + P1 extends keyof T, + P2 extends IterProperties, + P3 extends IterProperties>, + P4 extends IterProperties, P3>> + >( + callback: MapCallback< + IndexValue, P2>, P3>, P4>, + U + >, + prop1: P1, + prop2: P2, + prop3: P3, + prop4: P4 + ): U[]; + map( + callback: MapCallback, + prop1: PropertyKey, + prop2: PropertyKey, + prop3: PropertyKey, + prop4: PropertyKey, + ...props: PropertyKey[] + ): U[]; +} + +/** @deprecated `FastPath` was renamed to `AstPath` */ +export type FastPath = AstPath; + +export type BuiltInParser = (text: string, options?: any) => AST; +export type BuiltInParserName = + | "acorn" + | "angular" + | "babel-flow" + | "babel-ts" + | "babel" + | "css" + | "espree" + | "flow" + | "glimmer" + | "graphql" + | "html" + | "json-stringify" + | "json" + | "json5" + | "less" + | "lwc" + | "markdown" + | "mdx" + | "meriyah" + | "scss" + | "typescript" + | "vue" + | "yaml"; +export type BuiltInParsers = Record; + +export type CustomParser = ( + text: string, + options: Options +) => AST | Promise; + +/** + * For use in `.prettierrc.js`, `.prettierrc.cjs`, `prettierrc.mjs`, `prettier.config.js`, `prettier.config.cjs`, `prettier.config.mjs` + */ +export interface Config extends Options { + overrides?: Array<{ + files: string | string[]; + excludeFiles?: string | string[]; + options?: Options; + }>; +} + +export interface Options extends Partial {} + +export interface RequiredOptions extends doc.printer.Options { + /** + * Print semicolons at the ends of statements. + * @default true + */ + semi: boolean; + /** + * Use single quotes instead of double quotes. + * @default false + */ + singleQuote: boolean; + /** + * Use single quotes in JSX. + * @default false + */ + jsxSingleQuote: boolean; + /** + * Print trailing commas wherever possible. + * @default "all" + */ + trailingComma: "none" | "es5" | "all"; + /** + * Print spaces between brackets in object literals. + * @default true + */ + bracketSpacing: boolean; + /** + * Put the `>` of a multi-line HTML (HTML, JSX, Vue, Angular) element at the end of the last line instead of being + * alone on the next line (does not apply to self closing elements). + * @default false + */ + bracketSameLine: boolean; + /** + * Put the `>` of a multi-line JSX element at the end of the last line instead of being alone on the next line. + * @default false + * @deprecated use bracketSameLine instead + */ + jsxBracketSameLine: boolean; + /** + * Format only a segment of a file. + * @default 0 + */ + rangeStart: number; + /** + * Format only a segment of a file. + * @default Number.POSITIVE_INFINITY + */ + rangeEnd: number; + /** + * Specify which parser to use. + */ + parser: LiteralUnion | CustomParser; + /** + * Specify the input filepath. This will be used to do parser inference. + */ + filepath: string; + /** + * Prettier can restrict itself to only format files that contain a special comment, called a pragma, at the top of the file. + * This is very useful when gradually transitioning large, unformatted codebases to prettier. + * @default false + */ + requirePragma: boolean; + /** + * Prettier can insert a special @format marker at the top of files specifying that + * the file has been formatted with prettier. This works well when used in tandem with + * the --require-pragma option. If there is already a docblock at the top of + * the file then this option will add a newline to it with the @format marker. + * @default false + */ + insertPragma: boolean; + /** + * By default, Prettier will wrap markdown text as-is since some services use a linebreak-sensitive renderer. + * In some cases you may want to rely on editor/viewer soft wrapping instead, so this option allows you to opt out. + * @default "preserve" + */ + proseWrap: "always" | "never" | "preserve"; + /** + * Include parentheses around a sole arrow function parameter. + * @default "always" + */ + arrowParens: "avoid" | "always"; + /** + * Provide ability to support new languages to prettier. + */ + plugins: Array; + /** + * Specify plugin directory paths to search for plugins if not installed in the same `node_modules` where prettier is located. + */ + pluginSearchDirs: string[] | false; + /** + * How to handle whitespaces in HTML. + * @default "css" + */ + htmlWhitespaceSensitivity: "css" | "strict" | "ignore"; + /** + * Which end of line characters to apply. + * @default "lf" + */ + endOfLine: "auto" | "lf" | "crlf" | "cr"; + /** + * Change when properties in objects are quoted. + * @default "as-needed" + */ + quoteProps: "as-needed" | "consistent" | "preserve"; + /** + * Whether or not to indent the code inside