Moves @types/prettier to this repository (#14212)

* Rename `types` to `thirs-party-types`

* Copy `index.d.ts` from `@types/prettier`

* Make public APIs async

* Add missing `formatAST`

* Fix `CustomParser`

* Fix `Parser.parse` signature

* Add comment

* Add `standalonw.d.ts`

* first implement

* Fix path

* Add missing parser `.d.ts`

* Fix

* Fix path

* Introduce valid tests

* Add unit tests for dts files

* Add unit tests

* Simplify unit testing

* Tweaks structures

* Revert needless change

* Fix types path

* Introduce `ts-expect`

* Fix doc utils api

* Add AstPath API

* Fix API

* Fix tests

* Fix changelog

* Move dts

* Update module specifiers

* Remove generate dts config file

* Update build scripts

* Fix type errors

* Refactor with namespace imports

* Update comments

* Fix build script

* Improve build script

* `typing` -> `types`

* Clean up

* Minor tweak

* Minor tweak

* Modify `exports` field

* Fix paths in builder

* Refactor

* Refactor

* Address review

* Update changelog

* Address reviews

* Remove `since` from tests

* Improve utils type def

* Mark deprecated methods

* Address review

* Move docs types

* Address review

* Update c8 config

* Minor tweak

---------

Co-authored-by: fisker Cheung <lionkay@gmail.com>
This commit is contained in:
SUZUKI Sosukeandfisker Cheung authored and GitHub committed 2023-02-23 22:15:18 +09:00
1 parent 164eab2f86
commit 813df18df6
33 files changed
+1899 -60

No files matched your search

+1
View File
@@ -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: {
+3
View File
@@ -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`.
+1
View File
@@ -61,6 +61,7 @@ const config = {
"<rootDir>/tests/format/**/jsfmt.spec.js",
"<rootDir>/tests/integration/__tests__/**/*.js",
"<rootDir>/tests/unit/**/*.js",
"<rootDir>/tests/dts/unit/**/*.js",
],
testPathIgnorePatterns,
// collectCoverage: ENABLE_CODE_COVERAGE,
+5 -2
View File
@@ -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": [
+1 -1
View File
@@ -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 };
}
+5 -3
View File
@@ -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;
+2 -1
View File
@@ -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}`;
}
+107 -53
View File
@@ -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<ImportPathReplacement> }} ReplacementMap
*/
/** @type {Array<ImportPathReplacement>} */
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;
+1
View File
@@ -1,3 +1,4 @@
// @ts-expect-error
import { __internal as sharedWithCli } from "../index.js";
export const {
+1
View File
@@ -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;
+248
View File
@@ -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<T = builders.Doc>(
doc: builders.Doc,
callback: (doc: builders.Doc) => T,
defaultValue: T
): T;
function mapDoc<T = builders.Doc>(
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;
}
+903
View File
@@ -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 extends U, U = string> =
| T
| (Pick<U, never> & { _?: 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> = T extends Array<infer E> ? E : never;
// A union of the properties of the given object that are arrays.
type ArrayProperties<T> = {
[K in keyof T]: NonNullable<T[K]> 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<T extends { length: number }> = IsTuple<T> extends true
? Exclude<Partial<T>["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, P> = 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> = T extends []
? true
: T extends [infer First, ...infer Remain]
? IsTuple<Remain>
: false;
type CallProperties<T> = T extends any[] ? IndexProperties<T> : keyof T;
type IterProperties<T> = T extends any[]
? IndexProperties<T>
: ArrayProperties<T>;
type CallCallback<T, U> = (path: AstPath<T>, index: number, value: any) => U;
type EachCallback<T> = (
path: AstPath<ArrayElement<T>>,
index: number,
value: any
) => void;
type MapCallback<T, U> = (
path: AstPath<ArrayElement<T>>,
index: number,
value: any
) => U;
// https://github.com/prettier/prettier/blob/next/src/common/ast-path.js
export class AstPath<T = any> {
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<U>(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<U>(callback: CallCallback<T, U>): U;
call<U, P1 extends CallProperties<T>>(
callback: CallCallback<IndexValue<T, P1>, U>,
prop1: P1
): U;
call<U, P1 extends keyof T, P2 extends CallProperties<T[P1]>>(
callback: CallCallback<IndexValue<IndexValue<T, P1>, P2>, U>,
prop1: P1,
prop2: P2
): U;
call<
U,
P1 extends keyof T,
P2 extends CallProperties<T[P1]>,
P3 extends CallProperties<IndexValue<T[P1], P2>>
>(
callback: CallCallback<
IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>,
U
>,
prop1: P1,
prop2: P2,
prop3: P3
): U;
call<
U,
P1 extends keyof T,
P2 extends CallProperties<T[P1]>,
P3 extends CallProperties<IndexValue<T[P1], P2>>,
P4 extends CallProperties<IndexValue<IndexValue<T[P1], P2>, P3>>
>(
callback: CallCallback<
IndexValue<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, P4>,
U
>,
prop1: P1,
prop2: P2,
prop3: P3,
prop4: P4
): U;
call<U, P extends PropertyKey>(
callback: CallCallback<any, U>,
prop1: P,
prop2: P,
prop3: P,
prop4: P,
...props: P[]
): U;
each(callback: EachCallback<T>): void;
each<P1 extends IterProperties<T>>(
callback: EachCallback<IndexValue<T, P1>>,
prop1: P1
): void;
each<P1 extends keyof T, P2 extends IterProperties<T[P1]>>(
callback: EachCallback<IndexValue<IndexValue<T, P1>, P2>>,
prop1: P1,
prop2: P2
): void;
each<
P1 extends keyof T,
P2 extends IterProperties<T[P1]>,
P3 extends IterProperties<IndexValue<T[P1], P2>>
>(
callback: EachCallback<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>>,
prop1: P1,
prop2: P2,
prop3: P3
): void;
each<
P1 extends keyof T,
P2 extends IterProperties<T[P1]>,
P3 extends IterProperties<IndexValue<T[P1], P2>>,
P4 extends IterProperties<IndexValue<IndexValue<T[P1], P2>, P3>>
>(
callback: EachCallback<
IndexValue<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, P4>
>,
prop1: P1,
prop2: P2,
prop3: P3,
prop4: P4
): void;
each(
callback: EachCallback<any[]>,
prop1: PropertyKey,
prop2: PropertyKey,
prop3: PropertyKey,
prop4: PropertyKey,
...props: PropertyKey[]
): void;
map<U>(callback: MapCallback<T, U>): U[];
map<U, P1 extends IterProperties<T>>(
callback: MapCallback<IndexValue<T, P1>, U>,
prop1: P1
): U[];
map<U, P1 extends keyof T, P2 extends IterProperties<T[P1]>>(
callback: MapCallback<IndexValue<IndexValue<T, P1>, P2>, U>,
prop1: P1,
prop2: P2
): U[];
map<
U,
P1 extends keyof T,
P2 extends IterProperties<T[P1]>,
P3 extends IterProperties<IndexValue<T[P1], P2>>
>(
callback: MapCallback<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, U>,
prop1: P1,
prop2: P2,
prop3: P3
): U[];
map<
U,
P1 extends keyof T,
P2 extends IterProperties<T[P1]>,
P3 extends IterProperties<IndexValue<T[P1], P2>>,
P4 extends IterProperties<IndexValue<IndexValue<T[P1], P2>, P3>>
>(
callback: MapCallback<
IndexValue<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, P4>,
U
>,
prop1: P1,
prop2: P2,
prop3: P3,
prop4: P4
): U[];
map<U>(
callback: MapCallback<any[], U>,
prop1: PropertyKey,
prop2: PropertyKey,
prop3: PropertyKey,
prop4: PropertyKey,
...props: PropertyKey[]
): U[];
}
/** @deprecated `FastPath` was renamed to `AstPath` */
export type FastPath<T = any> = AstPath<T>;
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<BuiltInParserName, BuiltInParser>;
export type CustomParser = (
text: string,
options: Options
) => AST | Promise<AST>;
/**
* 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<RequiredOptions> {}
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<BuiltInParserName> | 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<string | Plugin>;
/**
* 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 <script> and <style> tags in Vue files.
* @default false
*/
vueIndentScriptAndStyle: boolean;
/**
* Control whether Prettier formats quoted code embedded in the file.
* @default "auto"
*/
embeddedLanguageFormatting: "auto" | "off";
/**
* Enforce single attribute per line in HTML, Vue and JSX.
* @default false
*/
singleAttributePerLine: boolean;
}
export interface ParserOptions<T = any> extends RequiredOptions {
locStart: (node: T) => number;
locEnd: (node: T) => number;
originalText: string;
}
export interface Plugin<T = any> {
languages?: SupportLanguage[] | undefined;
parsers?:
| { [parserName: string]: Parser<T> | (() => Promise<Parser<T>>) }
| undefined;
printers?: { [astFormat: string]: Printer<T> } | undefined;
options?: SupportOptions | undefined;
defaultOptions?: Partial<RequiredOptions> | undefined;
}
export interface Parser<T = any> {
parse: (text: string, options: ParserOptions<T>) => T | Promise<T>;
astFormat: string;
hasPragma?: ((text: string) => boolean) | undefined;
locStart: (node: T) => number;
locEnd: (node: T) => number;
preprocess?:
| ((text: string, options: ParserOptions<T>) => string)
| undefined;
}
export interface Printer<T = any> {
print(
path: AstPath<T>,
options: ParserOptions<T>,
print: (path: AstPath<T>) => Doc,
args?: unknown
): Doc;
embed?:
| ((
path: AstPath<T>,
print: (path: AstPath<T>) => Doc,
textToDoc: (text: string, options: Options) => Doc,
options: ParserOptions<T>
) => Doc | null)
| undefined;
insertPragma?: (text: string) => string;
/**
* @returns `null` if you want to remove this node
* @returns `void` if you want to use modified newNode
* @returns anything if you want to replace the node with it
*/
massageAstNode?: ((node: any, newNode: any, parent: any) => any) | undefined;
hasPrettierIgnore?: ((path: AstPath<T>) => boolean) | undefined;
canAttachComment?: ((node: T) => boolean) | undefined;
isBlockComment?: ((node: T) => boolean) | undefined;
willPrintOwnComments?: ((path: AstPath<T>) => boolean) | undefined;
printComment?:
| ((commentPath: AstPath<T>, options: ParserOptions<T>) => Doc)
| undefined;
handleComments?:
| {
ownLine?:
| ((
commentNode: any,
text: string,
options: ParserOptions<T>,
ast: T,
isLastComment: boolean
) => boolean)
| undefined;
endOfLine?:
| ((
commentNode: any,
text: string,
options: ParserOptions<T>,
ast: T,
isLastComment: boolean
) => boolean)
| undefined;
remaining?:
| ((
commentNode: any,
text: string,
options: ParserOptions<T>,
ast: T,
isLastComment: boolean
) => boolean)
| undefined;
}
| undefined;
}
export interface CursorOptions extends Options {
/**
* Specify where the cursor is.
*/
cursorOffset: number;
rangeStart?: never;
rangeEnd?: never;
}
export interface CursorResult {
formatted: string;
cursorOffset: number;
}
/**
* `format` is used to format text using Prettier. [Options](https://prettier.io/docs/en/options.html) may be provided to override the defaults.
*/
export function format(source: string, options?: Options): Promise<string>;
/**
* `check` checks to see if the file has been formatted with Prettier given those options and returns a `Boolean`.
* This is similar to the `--list-different` parameter in the CLI and is useful for running Prettier in CI scenarios.
*/
export function check(source: string, options?: Options): Promise<boolean>;
/**
* `formatWithCursor` both formats the code, and translates a cursor position from unformatted code to formatted code.
* This is useful for editor integrations, to prevent the cursor from moving when code is formatted.
*
* The `cursorOffset` option should be provided, to specify where the cursor is. This option cannot be used with `rangeStart` and `rangeEnd`.
*/
export function formatWithCursor(
source: string,
options: CursorOptions
): Promise<CursorResult>;
export function formatAST(ast: any, options?: Options): Promise<string>;
export interface ResolveConfigOptions {
/**
* If set to `false`, all caching will be bypassed.
*/
useCache?: boolean | undefined;
/**
* Pass directly the path of the config file if you don't wish to search for it.
*/
config?: string | undefined;
/**
* If set to `true` and an `.editorconfig` file is in your project,
* Prettier will parse it and convert its properties to the corresponding prettier configuration.
* This configuration will be overridden by `.prettierrc`, etc. Currently,
* the following EditorConfig properties are supported:
* - indent_style
* - indent_size/tab_width
* - max_line_length
*/
editorconfig?: boolean | undefined;
}
/**
* `resolveConfig` can be used to resolve configuration for a given source file,
* passing its path as the first argument. The config search will start at the
* file path and continue to search up the directory.
* (You can use `process.cwd()` to start searching from the current directory).
*
* A promise is returned which will resolve to:
*
* - An options object, providing a [config file](https://prettier.io/docs/en/configuration.html) was found.
* - `null`, if no file was found.
*
* The promise will be rejected if there was an error parsing the configuration file.
*/
export function resolveConfig(
filePath: string,
options?: ResolveConfigOptions
): Promise<Options | null>;
/**
* `resolveConfigFile` can be used to find the path of the Prettier configuration file,
* that will be used when resolving the config (i.e. when calling `resolveConfig`).
*
* A promise is returned which will resolve to:
*
* - The path of the configuration file.
* - `null`, if no file was found.
*
* The promise will be rejected if there was an error parsing the configuration file.
*/
export function resolveConfigFile(filePath?: string): Promise<string | null>;
/**
* As you repeatedly call `resolveConfig`, the file system structure will be cached for performance. This function will clear the cache.
* Generally this is only needed for editor integrations that know that the file system has changed since the last format took place.
*/
export function clearConfigCache(): Promise<void>;
export interface SupportLanguage {
name: string;
since?: string | undefined;
parsers: BuiltInParserName[] | string[];
group?: string | undefined;
tmScope?: string | undefined;
aceMode?: string | undefined;
codemirrorMode?: string | undefined;
codemirrorMimeType?: string | undefined;
aliases?: string[] | undefined;
extensions?: string[] | undefined;
filenames?: string[] | undefined;
linguistLanguageId?: number | undefined;
vscodeLanguageIds?: string[] | undefined;
interpreters?: string[] | undefined;
}
export interface SupportOptionRange {
start: number;
end: number;
step: number;
}
export type SupportOptionType =
| "int"
| "string"
| "boolean"
| "choice"
| "path";
export type CoreCategoryType =
| "Config"
| "Editor"
| "Format"
| "Other"
| "Output"
| "Global"
| "Special";
export interface BaseSupportOption<Type extends SupportOptionType> {
readonly name?: string | undefined;
/**
* Usually you can use {@link CoreCategoryType}
*/
category: string;
/**
* The type of the option.
*
* When passing a type other than the ones listed below, the option is
* treated as taking any string as argument, and `--option <${type}>` will
* be displayed in --help.
*/
type: Type;
/**
* Indicate that the option is deprecated.
*
* Use a string to add an extra message to --help for the option,
* for example to suggest a replacement option.
*/
deprecated?: true | string | undefined;
/**
* Description to be displayed in --help. If omitted, the option won't be
* shown at all in --help.
*/
description?: string | undefined;
}
export interface IntSupportOption extends BaseSupportOption<"int"> {
default?: number | undefined;
array?: false | undefined;
range?: SupportOptionRange | undefined;
}
export interface IntArraySupportOption extends BaseSupportOption<"int"> {
default?: Array<{ value: number[] }> | undefined;
array: true;
}
export interface StringSupportOption extends BaseSupportOption<"string"> {
default?: string | undefined;
array?: false | undefined;
}
export interface StringArraySupportOption extends BaseSupportOption<"string"> {
default?: Array<{ value: string[] }> | undefined;
array: true;
}
export interface BooleanSupportOption extends BaseSupportOption<"boolean"> {
default?: boolean | undefined;
array?: false | undefined;
description: string;
oppositeDescription?: string | undefined;
}
export interface BooleanArraySupportOption
extends BaseSupportOption<"boolean"> {
default?: Array<{ value: boolean[] }> | undefined;
array: true;
}
export interface ChoiceSupportOption<Value = any>
extends BaseSupportOption<"choice"> {
default?: Value | Array<{ value: Value }> | undefined;
description: string;
choices: Array<{
since?: string | undefined;
value: Value;
description: string;
}>;
}
export interface PathSupportOption extends BaseSupportOption<"path"> {
default?: string | undefined;
array?: false | undefined;
}
export interface PathArraySupportOption extends BaseSupportOption<"path"> {
default?: Array<{ value: string[] }> | undefined;
array: true;
}
export type SupportOption =
| IntSupportOption
| IntArraySupportOption
| StringSupportOption
| StringArraySupportOption
| BooleanSupportOption
| BooleanArraySupportOption
| ChoiceSupportOption
| PathSupportOption
| PathArraySupportOption;
export interface SupportOptions extends Record<string, SupportOption> {}
export interface SupportInfo {
languages: SupportLanguage[];
options: SupportOption[];
}
export interface FileInfoOptions {
ignorePath?: string | undefined;
withNodeModules?: boolean | undefined;
plugins?: string[] | undefined;
resolveConfig?: boolean | undefined;
}
export interface FileInfoResult {
ignored: boolean;
inferredParser: string | null;
}
export function getFileInfo(
filePath: string,
options?: FileInfoOptions
): Promise<FileInfoResult>;
/**
* Returns an object representing the parsers, languages and file types Prettier supports for the current version.
*/
export function getSupportInfo(): Promise<SupportInfo>;
/**
* `version` field in `package.json`
*/
export const version: string;
// https://github.com/prettier/prettier/blob/next/src/utils/public.js
export namespace util {
interface SkipOptions {
backwards?: boolean | undefined;
}
type Quote = "'" | '"';
function getMaxContinuousCount(text: string, searchString: string): number;
function getStringWidth(text: string): number;
function getAlignmentSize(
text: string,
tabWidth: number,
startIndex?: number | undefined
): number;
function getIndentSize(value: string, tabWidth: number): number;
function skipNewline(
text: string,
startIndex: number | false,
options?: SkipOptions | undefined
): number | false;
function skipInlineComment(
text: string,
startIndex: number | false
): number | false;
function skipTrailingComment(
text: string,
startIndex: number | false
): number | false;
function skipTrailingComment(
text: string,
startIndex: number | false
): number | false;
function hasNewline(
text: string,
startIndex: number,
options?: SkipOptions | undefined
): boolean;
function hasNewlineInRange(
text: string,
startIndex: number,
endIndex: number
): boolean;
function hasSpaces(
text: string,
startIndex: number,
options?: SkipOptions | undefined
): boolean;
function getNextNonSpaceNonCommentCharacter(
text: string,
startIndex: number
): string;
function makeString(
rawText: string,
enclosingQuote: Quote,
unescapeUnnecessaryEscapes?: boolean | undefined
): string;
function skip(
characters: string | RegExp
): (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | false;
const skipWhitespace: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | false;
const skipSpaces: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | false;
const skipToLineEnd: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | false;
const skipEverythingButNewLine: (
text: string,
startIndex: number | false,
options?: SkipOptions
) => number | false;
function addLeadingComment(node: any, comment: any): void;
function addDanglingComment(node: any, comment: any, marker: any): void;
function addTrailingComment(node: any, comment: any): void;
}
+10
View File
@@ -0,0 +1,10 @@
import { Parser } from "../index.js";
declare const parser: {
parsers: {
css: Parser;
less: Parser;
scss: Parser;
};
};
export = parser;
+8
View File
@@ -0,0 +1,8 @@
import { Parser } from "../index.js";
declare const parser: {
parsers: {
graphql: Parser;
};
};
export = parser;
+8
View File
@@ -0,0 +1,8 @@
import { Parser } from "../index.js";
declare const parser: {
parsers: {
glimmer: Parser;
};
};
export = parser;
+11
View File
@@ -0,0 +1,11 @@
import { Parser } from "../index.js";
declare const parser: {
parsers: {
html: Parser;
angular: Parser;
vue: Parser;
lwc: Parser;
};
};
export = parser;
+10
View File
@@ -0,0 +1,10 @@
import { Parser } from "../../index.js";
declare const parser: {
parsers: {
acorn: Parser;
espree: Parser;
};
};
export = parser;
+11
View File
@@ -0,0 +1,11 @@
import { Parser } from "../../index.js";
declare const parser: {
parsers: {
__ng_action: Parser;
__ng_binding: Parser;
__ng_interpolation: Parser;
__ng_directive: Parser;
};
};
export = parser;
+16
View File
@@ -0,0 +1,16 @@
import { Parser } from "../../index.js";
declare const parser: {
parsers: {
babel: Parser;
"babel-flow": Parser;
"babel-ts": Parser;
json: Parser;
json5: Parser;
"json-stringify": Parser;
__js_expression: Parser;
__vue_expression: Parser;
__vue_event_binding: Parser;
};
};
export = parser;
+8
View File
@@ -0,0 +1,8 @@
import { Parser } from "../../index.js";
declare const parser: {
parsers: {
flow: Parser;
};
};
export = parser;
+8
View File
@@ -0,0 +1,8 @@
import { Parser } from "../../index.js";
declare const parser: {
parsers: {
meriyah: Parser;
};
};
export = parser;
+8
View File
@@ -0,0 +1,8 @@
import { Parser } from "../../index.js";
declare const parser: {
parsers: {
typescript: Parser;
};
};
export = parser;
+10
View File
@@ -0,0 +1,10 @@
import { Parser } from "../index.js";
declare const parser: {
parsers: {
remark: Parser;
markdown: Parser;
mdx: Parser;
};
};
export = parser;
+8
View File
@@ -0,0 +1,8 @@
import { Parser } from "../index.js";
declare const parser: {
parsers: {
yaml: Parser;
};
};
export = parser;
+33
View File
@@ -0,0 +1,33 @@
import { CursorOptions, CursorResult, Options, SupportInfo } from "./index.js";
/**
* formatWithCursor both formats the code, and translates a cursor position from unformatted code to formatted code.
* This is useful for editor integrations, to prevent the cursor from moving when code is formatted
*
* The cursorOffset option should be provided, to specify where the cursor is. This option cannot be used with rangeStart and rangeEnd.
*
* ```js
* await prettier.formatWithCursor(" 1", { cursorOffset: 2, parser: "babel" });
* ```
* `-> { formatted: "1;\n", cursorOffset: 1 }`
*/
export function formatWithCursor(
source: string,
options: CursorOptions
): Promise<CursorResult>;
/**
* `format` is used to format text using Prettier. [Options](https://prettier.io/docs/en/options.html) may be provided to override the defaults.
*/
export function format(source: string, options?: Options): Promise<string>;
/**
* `check` checks to see if the file has been formatted with Prettier given those options and returns a `Boolean`.
* This is similar to the `--list-different` parameter in the CLI and is useful for running Prettier in CI scenarios.
*/
export function check(source: string, options?: Options): Promise<boolean>;
/**
* Returns an object representing the parsers, languages and file types Prettier supports for the current version.
*/
export function getSupportInfo(): Promise<SupportInfo>;
+5
View File
@@ -0,0 +1,5 @@
import * as doc from "../../../../src/document";
doc.builders.dedent;
doc.printer.printDocToString;
doc.debug.printDocToDebug;
+60
View File
@@ -0,0 +1,60 @@
import * as prettier from "../../../../src/index";
prettier.format("foo ( );", { semi: false });
prettier.check("foo ( );", { semi: false });
prettier.formatWithCursor(" 1", { cursorOffset: 2 });
prettier.resolveConfig("path/to/somewhere").then((options) => {
if (options !== null) {
prettier.format("hello world", options);
}
});
prettier.getFileInfo("./tsconfig.json").then((result) => {
if (result.inferredParser !== "json") {
throw new Error("Bad parser");
}
});
// @ts-expect-error
prettier.resolveConfig();
prettier.resolveConfigFile().then((filePath) => {
if (filePath !== null) {
prettier.resolveConfig(filePath);
}
});
prettier.resolveConfigFile("/path").then((filePath) => {
if (filePath !== null) {
prettier.resolveConfig(filePath);
}
});
prettier.clearConfigCache();
prettier.getSupportInfo();
prettier.doc.builders.trim;
prettier.doc.builders.trim.type;
prettier.doc.builders.cursor;
prettier.doc.builders.cursor.type;
prettier.doc.builders.cursor.placeholder;
prettier.format("pluginSearchDir is empty", {
pluginSearchDirs: [],
});
prettier.format("pluginSearchDir is not empty", {
pluginSearchDirs: ["/a", "/b"],
});
prettier.format("pluginSearchDir is not empty and mixed with weird stuff", {
pluginSearchDirs: ["c", "d", ""],
});
prettier.format("pluginSearchDir is false", {
pluginSearchDirs: false,
});
prettier.format("pluginSearchDir can not be true", {
// @ts-expect-error
pluginSearchDirs: true,
});
prettier.format("singleAttributePerLine is available", {
singleAttributePerLine: true,
});
+50
View File
@@ -0,0 +1,50 @@
import * as prettier from "../../../../src/index";
import acornEspreeParser from "../../../../src/language-js/parse/acorn-and-espree";
import angularParser from "../../../../src/language-js/parse/angular";
import babelParser from "../../../../src/language-js/parse/babel";
import flowParser from "../../../../src/language-js/parse/flow";
import glimmerParser from "../../../../src/language-handlebars/parser-glimmer";
import graphqlParser from "../../../../src/language-graphql/parser-graphql";
import htmlParser from "../../../../src/language-html/parser-html";
import markdownParser from "../../../../src/language-markdown/parser-markdown";
import meriyahParser from "../../../../src/language-js/parse/meriyah";
import postcssParser from "../../../../src/language-css/parser-postcss";
import typescriptParser from "../../../../src/language-js/parse/typescript";
import yamlParser from "../../../../src/language-yaml/parser-yaml";
acornEspreeParser.parsers.acorn.parse;
acornEspreeParser.parsers.espree.parse;
angularParser.parsers.__ng_action.parse;
babelParser.parsers.babel.parse;
flowParser.parsers.flow.parse;
glimmerParser.parsers.glimmer.parse;
graphqlParser.parsers.graphql.parse;
htmlParser.parsers.html.parse;
htmlParser.parsers.vue.parse;
htmlParser.parsers.lwc.parse;
htmlParser.parsers.angular.parse;
markdownParser.parsers.markdown.parse;
markdownParser.parsers.mdx.parse;
meriyahParser.parsers.meriyah.parse;
postcssParser.parsers.css.parse;
postcssParser.parsers.less.parse;
postcssParser.parsers.scss.parse;
typescriptParser.parsers.typescript.parse;
yamlParser.parsers.yaml.parse;
prettier.format("hello world", {
plugins: [
acornEspreeParser,
angularParser,
babelParser,
flowParser,
glimmerParser,
htmlParser,
markdownParser,
meriyahParser,
postcssParser,
typescriptParser,
yamlParser,
],
});
+143
View File
@@ -0,0 +1,143 @@
import * as prettier from "../../../../src/index";
import { expectType } from "ts-expect";
interface PluginAST {
kind: "line";
value: string;
}
const plugin: prettier.Plugin<PluginAST> = {
languages: [
{
name: "Shell",
parsers: ["sh"],
interpreters: ["bash", "zsh"],
},
],
parsers: {
lines: {
parse(text, options) {
return { kind: "line", value: "This is a line" };
},
astFormat: "lines",
locStart: (node) => {
expectType<PluginAST>(node);
return 0;
},
locEnd: (node) => {
expectType<PluginAST>(node);
return 0;
},
},
},
printers: {
lines: {
print(path, options, print) {
expectType<prettier.AstPath<PluginAST>>(path);
expectType<(path: prettier.AstPath<PluginAST>) => prettier.Doc>(print);
const node = path.getValue();
expectType<PluginAST>(node);
return node.value;
},
printComment(commentPath, options) {
const comment = commentPath.getValue();
return comment.value;
},
},
},
options: {
testBoolOption: {
type: "boolean",
category: "Test",
default: true,
description: "Move open brace for code blocks onto new line.",
oppositeDescription:
"Don't move open brace for code blocks onto new line.",
},
testBoolArrOption: {
type: "boolean",
array: true,
category: "Test",
default: [{ value: [true, false, true] }],
deprecated: true,
description: "Move open brace for code blocks onto new line.",
},
testIntOption: {
type: "int",
category: "Global",
default: 15,
range: {
start: 5,
end: 100,
step: 5,
},
deprecated: "Deprecated can be a string describing deprecation status.",
description: "This is a number.",
},
testIntArrOption: {
type: "int",
category: "Test",
default: [{ value: [3, 8, 12] }],
array: true,
description: "This is a number.",
},
testStringOption: {
type: "string",
category: "Test",
default: "default",
description: "This is a string.",
},
testStringArrOption: {
type: "string",
category: "Test",
default: [{ value: ["one", "two", "three"] }],
array: true,
description: "This is a string.",
},
testChoiceOption: {
type: "choice",
default: "one",
choices: [
{ value: "one", description: "The number one" },
{ value: "two", description: "The number two" },
{ value: "three", description: "The number three" },
],
category: "Test",
description: "Choose one of three.",
},
testChoiceComplexOption: {
type: "choice",
default: [{ since: "1.0.7", value: "banana" }, { value: "apple" }],
choices: [
{ value: "apple", description: "A fruit." },
{ value: "orange", since: "1.0.6", description: "A different fruit." },
{
value: "banana",
since: "1.0.5",
description: "Added in 1.0.5, made default in 1.0.7.",
},
],
category: "Test",
description: "Choose one of three.",
},
testPathOption: {
type: "path",
category: "Test",
default: "./path.js",
},
testPathArrOption: {
type: "path",
category: "Test",
array: true,
default: [{ value: ["./pathA.js", "./pathB.js"] }],
},
testNoDefaultOption: {
type: "path",
category: "Test",
},
},
};
prettier.format("a line!", { parser: "lines", plugins: [plugin] });
+159
View File
@@ -0,0 +1,159 @@
import * as prettier from "../../../../src/index";
import { expectType } from "ts-expect";
type NestedAst = Nested1 | Nested2 | Nested3;
interface Nested1 {
kind: "1";
item2: Nested2;
list2: Nested2[];
list4?: Nested2[];
}
interface Nested2 {
kind: "2";
item3: Nested3;
list3: Nested3[];
}
interface Nested3 {
kind: "3";
item1: Nested1;
list1: Nested1[];
}
function print(
// Using Nested1 because we're assuming you've already determined the kind
// of node based on some discriminated union. If you're determining the kind
// of node within the same place that you need access to the path, it's
// easiest to do something the to effect of:
//
// if (node.kind === "1") {
// const nodePath = path as AstPath<typeof node>;
// }
//
// In the example above, nodePath will then be a type-narrowed version of
// the path variable that you can then use to correctly type the tree-walk
// functions.
path: prettier.AstPath<Nested1>,
options: prettier.ParserOptions<NestedAst>,
print: (path: prettier.AstPath<NestedAst>) => prettier.doc.builders.Doc
): prettier.doc.builders.Doc {
path.call((child) => {
expectType<prettier.AstPath<Nested1>>(child);
});
path.call((child) => {
expectType<prettier.AstPath<Nested2>>(child);
}, "item2");
path.call(
(child) => {
expectType<prettier.AstPath<Nested3>>(child);
},
"item2",
"item3"
);
path.call(
(child) => {
expectType<prettier.AstPath<Nested1>>(child);
},
"item2",
"item3",
"item1"
);
path.call(
(child) => {
expectType<prettier.AstPath<Nested2>>(child);
},
"item2",
"item3",
"item1",
"item2"
);
path.call(
(child) => {
expectType<prettier.AstPath<any>>(child);
},
"item2",
"item3",
"item1",
"item2",
"item3"
);
path.each((child) => {
expectType<prettier.AstPath<Nested2>>(child);
}, "list2");
path.each((child) => {
expectType<prettier.AstPath<Nested2>>(child);
}, "list4");
path.each(
(child) => {
expectType<prettier.AstPath<Nested3>>(child);
},
"list2",
0,
"list3"
);
path.each(
(child) => {
expectType<prettier.AstPath<any>>(child);
},
"list2",
0,
"list3",
0,
"list1"
);
path.map((child) => {
expectType<prettier.AstPath<Nested2>>(child);
}, "list2");
path.map((child) => {
expectType<prettier.AstPath<Nested2>>(child);
}, "list4");
path.map(
(child) => {
expectType<prettier.AstPath<Nested3>>(child);
},
"list2",
0,
"list3"
);
path.map(
(child) => {
expectType<prettier.AstPath<any>>(child);
},
"list2",
0,
"list3",
0,
"list1"
);
// @ts-expect-error
path.call(print, "list2");
// @ts-expect-error
path.call(print, "list4");
// @ts-expect-error
path.call(print, "item2", "list3");
// @ts-expect-error
path.each(print, "item2");
// @ts-expect-error
path.each(print, "item2", "item3");
// @ts-expect-error
path.map(print, "item2");
// @ts-expect-error
path.map(print, "item2", "item3");
return "";
}
+18
View File
@@ -0,0 +1,18 @@
import * as prettierStandalone from "../../../../src/standalone";
prettierStandalone.formatWithCursor(" 1", { cursorOffset: 2, parser: "babel" });
prettierStandalone.formatWithCursor(" 1", {
cursorOffset: 2,
parser: "babel",
// @ts-expect-error
rangeStart: 2,
});
prettierStandalone.formatWithCursor(" 1", {
cursorOffset: 2,
parser: "babel",
// @ts-expect-error
rangeEnd: 2,
});
prettierStandalone.format(" 1", { parser: "babel" });
prettierStandalone.check(" console.log(b)");
prettierStandalone.getSupportInfo();
+29
View File
@@ -0,0 +1,29 @@
import fs from "node:fs";
import path from "node:path";
import url from "node:url";
import { default as ts } from "typescript";
describe("Unit tests for dts files", () => {
test("no errors", () => {
const testCasesDirPath = url.fileURLToPath(
new URL("cases", import.meta.url)
);
const testCaseFiles = fs
.readdirSync(testCasesDirPath)
.map((file) => path.join(testCasesDirPath, file));
/** @type {import("typescript").CompilerOptions} */
const compilerOptions = {
noEmit: true,
strict: true,
esModuleInterop: true,
};
const program = ts.createProgram(testCaseFiles, compilerOptions);
/** @type {import("typescript").Diagnostic[]} */
const diagnostics = ts.getPreEmitDiagnostics(program);
expect(diagnostics).toEqual([]);
});
});
+8
View File
@@ -6811,6 +6811,7 @@ __metadata:
strip-ansi: 7.0.1
tempy: 3.0.0
to-fast-properties: 4.0.0
ts-expect: 1.3.0
typescript: 4.9.5
unicode-regex: 3.0.0
unified: 9.2.2
@@ -7977,6 +7978,13 @@ __metadata:
languageName: node
linkType: hard
"ts-expect@npm:1.3.0":
version: 1.3.0
resolution: "ts-expect@npm:1.3.0"
checksum: c007702906354ada640392c26373660a8a034506859b5c084de74376ca2c6fe092c5909235d1bad8ed67423e7ab023dae4dc3032dcdcce70852c0211c5238013
languageName: node
linkType: hard
"tsconfig-paths@npm:^3.14.1":
version: 3.14.1
resolution: "tsconfig-paths@npm:3.14.1"