diff --git a/.github/ISSUE_TEMPLATE/formatting.md b/.github/ISSUE_TEMPLATE/formatting.md index 16ede0267efa..0336a4f0673b 100644 --- a/.github/ISSUE_TEMPLATE/formatting.md +++ b/.github/ISSUE_TEMPLATE/formatting.md @@ -26,7 +26,7 @@ Don't fill the form below manually! Let a program create a report for you: --> -**Prettier 3.7.3** +**Prettier 3.7.4** [Playground link](https://prettier.io/playground/#.....) ```sh diff --git a/.github/ISSUE_TEMPLATE/integration.md b/.github/ISSUE_TEMPLATE/integration.md index d0f95dd8a632..9e81974b06f2 100644 --- a/.github/ISSUE_TEMPLATE/integration.md +++ b/.github/ISSUE_TEMPLATE/integration.md @@ -20,7 +20,7 @@ BEFORE SUBMITTING AN ISSUE: **Environments:** -- Prettier Version: 3.7.3 +- Prettier Version: 3.7.4 - Running Prettier via: - Runtime: - Operating System: diff --git a/.gitignore b/.gitignore index 1fdac1a6a402..da5d7428d7b5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,8 +33,6 @@ package-lock.json .nyc_output .devcontainer .node-version -# When installing software on gitpod.io, `core.*` files are generated -/core.* *.swp # For 0x flamegraph directory (https://github.com/davidmarkclements/0x) diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index cf602b03403c..000000000000 --- a/.gitpod.yml +++ /dev/null @@ -1,6 +0,0 @@ -# https://gitpod.io/#https://github.com/prettier/prettier - -tasks: - - init: | - brew install hyperfine - yarn diff --git a/CHANGELOG.md b/CHANGELOG.md index ff5f71b22346..a261d018091e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,63 @@ +# 3.7.4 + +[diff](https://github.com/prettier/prettier/compare/3.7.3...3.7.4) + +#### LWC: Avoid quote around interpolations ([#18383](https://github.com/prettier/prettier/pull/18383) by [@kovsu](https://github.com/kovsu)) + + +```html + +
+ + +
+ + +
+``` + +#### TypeScript: Fix comment inside union type get duplicated ([#18393](https://github.com/prettier/prettier/pull/18393) by [@fisker](https://github.com/fisker)) + + +```tsx +// Input +type Foo = (/** comment */ a | b) | c; + +// Prettier 3.7.3 +type Foo = /** comment */ (/** comment */ a | b) | c; + +// Prettier 3.7.4 +type Foo = /** comment */ (a | b) | c; +``` + +#### TypeScript: Fix unstable comment print in union type comments ([#18395](https://github.com/prettier/prettier/pull/18395) by [@fisker](https://github.com/fisker)) + + +```tsx +// Input +type X = (A | B) & ( + // comment + A | B +); + +// Prettier 3.7.3 (first format) +type X = (A | B) & + (// comment + A | B); + +// Prettier 3.7.3 (second format) +type X = ( + | A + | B // comment +) & + (A | B); + +// Prettier 3.7.4 +type X = (A | B) & + // comment + (A | B); +``` + # 3.7.3 [diff](https://github.com/prettier/prettier/compare/3.7.2...3.7.3) diff --git a/benchmarks/get-preferred-quote.js b/benchmarks/get-preferred-quote.js new file mode 100644 index 000000000000..6497ac8f7ff7 --- /dev/null +++ b/benchmarks/get-preferred-quote.js @@ -0,0 +1,30 @@ +import assert from "node:assert/strict"; +// import * as prettierDevBundled from "../dist/prettier/index.mjs"; +import * as prettierProduction from "../node_modules/prettier/index.mjs"; +import * as prettierDevelopment from "../src/index.js"; +import { runBenchmark } from "./utilities.js"; + +assert.notEqual(prettierProduction.version, prettierDevelopment.version); + +for (const size of [1, 1e1, 1e2, 1e3, 1e4, 1e5]) { + const text = Array.from({ length: size }, () => + Math.random() > 0.5 ? " " : Math.random() > 0.5 ? "'" : '"', + ).join(""); + const run = (prettier) => prettier.util.getPreferredQuote(text, "'"); + const expected = run(prettierProduction); + + await runBenchmark( + { + name: `getPreferredQuote (${size} characters)`, + assert: (result) => assert.deepEqual(result, expected), + }, + [ + { name: "Development", prettier: prettierDevelopment }, + // { name: "Development(Bundled)", prettier: prettierDevBundled }, + { name: "Production", prettier: prettierProduction }, + ].map(({ name, prettier }) => ({ + name, + implementation: () => run(prettier), + })), + ); +} diff --git a/changelog_unreleased/api/18375.md b/changelog_unreleased/api/18375.md deleted file mode 100644 index 0361c1b64643..000000000000 --- a/changelog_unreleased/api/18375.md +++ /dev/null @@ -1,3 +0,0 @@ -#### Fix `prettier.getFileInfo()` change that breaks VSCode extension (#18375 by @fisker) - -An internal refactor accidentally broke the VSCode extension plugin loading. diff --git a/changelog_unreleased/lwc/18383.md b/changelog_unreleased/lwc/18383.md new file mode 100644 index 000000000000..7ef64be48bd3 --- /dev/null +++ b/changelog_unreleased/lwc/18383.md @@ -0,0 +1,13 @@ +#### Avoid quote around interpolations (#18383 by @kovsu) + + +```html + +
+ + +
+ + +
+``` diff --git a/changelog_unreleased/typescript/18393.md b/changelog_unreleased/typescript/18393.md new file mode 100644 index 000000000000..0193de0e6c81 --- /dev/null +++ b/changelog_unreleased/typescript/18393.md @@ -0,0 +1,13 @@ +#### Fix comment inside union type get duplicated (#18393 by @fisker) + + +```tsx +// Input +type Foo = (/** comment */ a | b) | c; + +// Prettier stable +type Foo = /** comment */ (/** comment */ a | b) | c; + +// Prettier main +type Foo = /** comment */ (a | b) | c; +``` diff --git a/changelog_unreleased/typescript/18395.md b/changelog_unreleased/typescript/18395.md new file mode 100644 index 000000000000..14646309e3f4 --- /dev/null +++ b/changelog_unreleased/typescript/18395.md @@ -0,0 +1,27 @@ +#### Fix unstable comment print in union type comments (#18395 by @fisker) + + +```tsx +// Input +type X = (A | B) & ( + // comment + A | B +); + +// Prettier stable (first format) +type X = (A | B) & + (// comment + A | B); + +// Prettier stable (second format) +type X = ( + | A + | B // comment +) & + (A | B); + +// Prettier main +type X = (A | B) & + // comment + (A | B); +``` diff --git a/commands.md b/commands.md index f42ab0831436..8cbfd1c7c279 100644 --- a/commands.md +++ b/commands.md @@ -1,4 +1,4 @@ -The core of the algorithm is implemented in `src/document/{printer,builders,utils}.js`. The printer uses the basic formatting abstractions provided to construct a format when printing a node. +The core of the algorithm is implemented in `src/document/{printer,builders,utilities}/`. The printer uses the basic formatting abstractions provided to construct a format when printing a node. ## Prettier's intermediate representation: `Doc` diff --git a/eslint.config.js b/eslint.config.js index 4cadfb36ebf8..b32e79a9046d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -435,7 +435,7 @@ const configs = [ "prettier-internal-rules/no-node-comments": [ "error", { - file: "src/language-js/utils/index.js", + file: "src/language-js/utilities/index.js", functions: ["hasComment", "getComments"], }, "src/language-js/pragma.js", @@ -445,7 +445,7 @@ const configs = [ "src/language-js/parse/json.js", "src/language-js/parse/acorn.js", "src/language-js/parse/oxc.js", - "src/language-js/parse/utils/wrap-babel-expression.js", + "src/language-js/parse/utilities/wrap-babel-expression.js", ], "prettier-internal-rules/prefer-create-type-check-function": [ "error", diff --git a/jest.config.js b/jest.config.js index 439d658a1ca0..b0b1c5b2396d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -51,7 +51,7 @@ if (nodejsMajorVersion <= 14) { testPathIgnorePatterns.push( "/tests/integration/__tests__/plugin-parsers.js", "/tests/integration/__tests__/normalize-doc.js", - "/tests/integration/__tests__/doc-utils-clean-doc.js", + "/tests/integration/__tests__/doc-utilities-clean-doc.js", "/tests/integration/__tests__/config-invalid.js", // `@prettier/cli` uses `node:stream/consumers`, not available on Node.js v14 "/tests/integration/__tests__/experimental-cli.js", diff --git a/package.json b/package.json index 00cc84d9a729..3fc3bd39690f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prettier", - "version": "3.7.3", + "version": "3.7.4", "description": "Prettier is an opinionated code formatter", "bin": "./bin/prettier.cjs", "repository": "prettier/prettier", @@ -42,7 +42,7 @@ "bin" ], "dependencies": { - "@angular/compiler": "21.0.1", + "@angular/compiler": "21.0.2", "@babel/code-frame": "8.0.0-beta.3", "@babel/parser": "8.0.0-beta.3", "@babel/types": "8.0.0-beta.3", @@ -165,7 +165,7 @@ "node-style-text": "2.1.2", "npm-run-all2": "8.0.4", "open-editor": "6.0.0", - "prettier": "3.7.2", + "prettier": "3.7.3", "pretty-bytes": "7.1.0", "pretty-ms": "9.3.0", "rollup-plugin-license": "3.6.0", @@ -229,7 +229,7 @@ "src/index.cjs", "src/document/debug.js", "src/universal/*.browser.js", - "src/utils/unexpected-node-error.js", + "src/utilities/unexpected-node-error.js", "src/language-js/types/estree.d.ts", "src/**/**/*.d.ts" ] diff --git a/packages/plugin-hermes/package.json b/packages/plugin-hermes/package.json index 4ab07fac2788..cf7397b702f4 100644 --- a/packages/plugin-hermes/package.json +++ b/packages/plugin-hermes/package.json @@ -1,6 +1,6 @@ { "name": "@prettier/plugin-hermes", - "version": "0.1.2", + "version": "0.1.3", "description": "Prettier Hermes plugin.", "type": "module", "exports": { diff --git a/packages/plugin-oxc/package.json b/packages/plugin-oxc/package.json index 631a636546f7..fa754d4b50f2 100644 --- a/packages/plugin-oxc/package.json +++ b/packages/plugin-oxc/package.json @@ -1,6 +1,6 @@ { "name": "@prettier/plugin-oxc", - "version": "0.1.2", + "version": "0.1.3", "description": "Prettier Oxc plugin.", "type": "module", "exports": { diff --git a/scripts/benchmark/bench.js b/scripts/benchmark/bench.js index 59bc10cbe10a..d6869417e78e 100644 --- a/scripts/benchmark/bench.js +++ b/scripts/benchmark/bench.js @@ -7,7 +7,8 @@ const groupSize = Number(groupSizeString); const { format } = await import(`./${version}/dist/index.mjs`); const sourceText = readFileSync( - process.env.PRETTIER_PERF_FILENAME || "../../src/language-js/utils/index.js", + process.env.PRETTIER_PERF_FILENAME || + "../../src/language-js/utilities/index.js", "utf8", ); diff --git a/scripts/build-website.js b/scripts/build-website.js index 7f3a90b6736f..226f6b55ad6e 100644 --- a/scripts/build-website.js +++ b/scripts/build-website.js @@ -14,7 +14,7 @@ import { WEBSITE_DIR, writeFile, writeJson, -} from "./utils/index.js"; +} from "./utilities/index.js"; const runYarn = (command, args, options) => spawn("yarn", [command, ...args], { stdio: "inherit", ...options }); diff --git a/scripts/build/build-dependencies-license.js b/scripts/build/build-dependencies-license.js index d75d8e589ebc..c9d1b4b80e28 100644 --- a/scripts/build/build-dependencies-license.js +++ b/scripts/build/build-dependencies-license.js @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { outdent } from "outdent"; import rollupPluginLicense from "rollup-plugin-license"; -import { PROJECT_ROOT } from "../utils/index.js"; +import { PROJECT_ROOT } from "../utilities/index.js"; const separator = `\n${"-".repeat(40)}\n\n`; diff --git a/scripts/build/build-javascript-module.js b/scripts/build/build-javascript-module.js index fe78ab6a79db..f259cdbc9c55 100644 --- a/scripts/build/build-javascript-module.js +++ b/scripts/build/build-javascript-module.js @@ -6,7 +6,7 @@ import createEsmUtils from "esm-utils"; import { PRODUCTION_MINIMAL_NODE_JS_VERSION, PROJECT_ROOT, -} from "../utils/index.js"; +} from "../utilities/index.js"; import esbuildPluginAddDefaultExport from "./esbuild-plugins/add-default-export.js"; import esbuildPluginEvaluate from "./esbuild-plugins/evaluate.js"; import esbuildPluginPrimitiveDefine from "./esbuild-plugins/primitive-define.js"; @@ -17,7 +17,7 @@ import esbuildPluginThrowWarnings from "./esbuild-plugins/throw-warnings.js"; import esbuildPluginUmd from "./esbuild-plugins/umd.js"; import esbuildPluginVisualizer from "./esbuild-plugins/visualizer.js"; import transform from "./transform/index.js"; -import { getPackageFile } from "./utils.js"; +import { getPackageFile } from "./utilities.js"; const { readJsonSync, diff --git a/scripts/build/build-package-json.js b/scripts/build/build-package-json.js index e9f7f7fe6e8a..3283b258078c 100644 --- a/scripts/build/build-package-json.js +++ b/scripts/build/build-package-json.js @@ -4,7 +4,7 @@ import { PROJECT_ROOT, readJson, writeJson, -} from "../utils/index.js"; +} from "../utilities/index.js"; const keysToKeep = [ "name", diff --git a/scripts/build/build-types.js b/scripts/build/build-types.js index 358b25469de9..86f1c9ef03a5 100644 --- a/scripts/build/build-types.js +++ b/scripts/build/build-types.js @@ -3,7 +3,7 @@ import path from "node:path"; import url from "node:url"; import { isValidIdentifier } from "@babel/types"; import { outdent } from "outdent"; -import { PROJECT_ROOT, writeFile } from "../utils/index.js"; +import { PROJECT_ROOT, writeFile } from "../utilities/index.js"; async function typesFileBuilder({ packageConfig, file }) { /** diff --git a/scripts/build/build.js b/scripts/build/build.js index bbf30a308011..584f9623ec5f 100644 --- a/scripts/build/build.js +++ b/scripts/build/build.js @@ -7,7 +7,7 @@ import createEsmUtils from "esm-utils"; import styleText from "node-style-text"; import prettyBytes from "pretty-bytes"; import prettyMilliseconds from "pretty-ms"; -import { DIST_DIR } from "../utils/index.js"; +import { DIST_DIR } from "../utilities/index.js"; import packageConfigs from "./config.js"; import parseArguments from "./parse-arguments.js"; diff --git a/scripts/build/config.js b/scripts/build/config.js index 79b88b9612a9..f26525238a62 100644 --- a/scripts/build/config.js +++ b/scripts/build/config.js @@ -3,7 +3,7 @@ import path from "node:path"; import url from "node:url"; import createEsmUtils from "esm-utils"; import { outdent } from "outdent"; -import { copyFile, DIST_DIR, PROJECT_ROOT } from "../utils/index.js"; +import { copyFile, DIST_DIR, PROJECT_ROOT } from "../utilities/index.js"; import buildDependenciesLicense from "./build-dependencies-license.js"; import buildJavascriptModule from "./build-javascript-module.js"; import buildOxcWasmParser from "./build-oxc-wasm-parser.js"; @@ -15,7 +15,7 @@ import { import buildTypes from "./build-types.js"; import esmifyTypescriptEslint from "./esmify-typescript-eslint.js"; import modifyTypescriptModule from "./modify-typescript-module.js"; -import { getPackageFile } from "./utils.js"; +import { getPackageFile } from "./utilities.js"; const { require, @@ -598,7 +598,10 @@ const pluginFiles = [ "const LOCAL_DEBUG = false &&", ); - text = text.replace(/(?<=\n)export .*?;/u, "export { preprocess };"); + text = text.replace( + /(?<=\n)export .*?;/u, + "export { preprocess, getVoidTags, visitorKeys };", + ); return text; }, diff --git a/scripts/build/esbuild-plugins/evaluate.js b/scripts/build/esbuild-plugins/evaluate.js index 009bdd09027a..7a007aeac451 100644 --- a/scripts/build/esbuild-plugins/evaluate.js +++ b/scripts/build/esbuild-plugins/evaluate.js @@ -1,33 +1,89 @@ +import assert from "node:assert"; +import path from "node:path"; import url from "node:url"; import { isValidIdentifier } from "@babel/types"; +import { outdent } from "outdent"; import serialize from "serialize-javascript"; +function serializeModule(module) { + return Object.entries(module) + .map(([specifier, value]) => { + const code = + value instanceof RegExp + ? `/${value.source}/${value.flags}` + : serialize(value, { space: 2, unsafe: true }); + if (specifier === "default") { + return `export default ${code};`; + } + + if (!isValidIdentifier(specifier)) { + throw new Error(`${specifier} is not a valid specifier`); + } + + return `export const ${specifier} = ${code};`; + }) + .join("\n"); +} + +function serializeVisitorKeys(module) { + const specifiers = Object.keys(module); + assert.deepEqual(specifiers, ["default"]); + + const references = new Map(); + const properties = Object.entries(module.default); + for (const [, keys] of properties) { + if (!references.has(keys)) { + references.set(keys, 1); + } else { + references.set(keys, references.get(keys) + 1); + } + } + + const variables = []; + const propertiesText = []; + for (const [type, keys] of properties) { + const isSingleReference = references.get(keys) === 1; + const key = isValidIdentifier(type) ? type : JSON.stringify(type); + if (isSingleReference) { + propertiesText.push(` ${key}: ${JSON.stringify(keys)},`); + } else { + if (!variables.includes(keys)) { + variables.push(keys); + } + const index = variables.indexOf(keys); + propertiesText.push(` ${key}: vk[${index}],`); + } + } + + const code = outdent` + const vk = [ + ${variables.map((keys) => ` ${JSON.stringify(keys)},`).join("\n")} + ]; + + export default { + ${propertiesText.join("\n")} + }; + `; + + return code; +} + export default function esbuildPluginEvaluate() { return { name: "evaluate", setup(build) { build.onLoad( { filter: /\.evaluate\.[cm]?js$/, namespace: "file" }, - async ({ path }) => { - const module = await import(url.pathToFileURL(path)); - const text = Object.entries(module) - .map(([specifier, value]) => { - const code = - value instanceof RegExp - ? `/${value.source}/${value.flags}` - : serialize(value, { space: 2 }); - - if (specifier === "default") { - return `export default ${code};`; - } - - if (!isValidIdentifier(specifier)) { - throw new Error(`${specifier} is not a valid specifier`); - } - - return `export const ${specifier} = ${code};`; - }) - .join("\n"); + async ({ path: filePath }) => { + const module = await import(url.pathToFileURL(filePath)); + const isVisitorKeys = + path.basename(filePath, path.extname(filePath)) === + "visitor-keys.evaluate"; + + const text = isVisitorKeys + ? serializeVisitorKeys(module) + : serializeModule(module); + return { contents: text }; }, ); diff --git a/scripts/build/esmify-typescript-eslint.js b/scripts/build/esmify-typescript-eslint.js index 694b4e862cdd..f2e5ff92d7ed 100644 --- a/scripts/build/esmify-typescript-eslint.js +++ b/scripts/build/esmify-typescript-eslint.js @@ -2,7 +2,7 @@ import * as path from "node:path"; import { outdent } from "outdent"; -import { PROJECT_ROOT, writeFile } from "../utils/index.js"; +import { PROJECT_ROOT, writeFile } from "../utilities/index.js"; function esmifyTypescriptEslint(text) { /* diff --git a/scripts/build/modify-typescript-module.js b/scripts/build/modify-typescript-module.js index 7a93bd8913b8..1aa8d77df6eb 100644 --- a/scripts/build/modify-typescript-module.js +++ b/scripts/build/modify-typescript-module.js @@ -2,7 +2,7 @@ import path from "node:path"; import escapeStringRegexp from "escape-string-regexp"; import MagicString from "magic-string"; import { outdent } from "outdent"; -import { PROJECT_ROOT, writeFile } from "../utils/index.js"; +import { PROJECT_ROOT, writeFile } from "../utilities/index.js"; import UNUSED_SPECIFIERS from "./typescript-unused-specifiers.js"; function* getModules(text) { diff --git a/scripts/build/parse-arguments.js b/scripts/build/parse-arguments.js index 25c0743c7a0f..fb0b6a99de62 100644 --- a/scripts/build/parse-arguments.js +++ b/scripts/build/parse-arguments.js @@ -1,6 +1,6 @@ import path from "node:path"; import { parseArgs } from "node:util"; -import { DIST_DIR } from "../utils/index.js"; +import { DIST_DIR } from "../utilities/index.js"; const isUnique = (array) => new Set(array).size === array.length; diff --git a/scripts/build/transform/index.js b/scripts/build/transform/index.js index 07e82694ca4f..84a38c86d424 100644 --- a/scripts/build/transform/index.js +++ b/scripts/build/transform/index.js @@ -3,7 +3,7 @@ import generate from "@babel/generator"; import { parse } from "@babel/parser"; import { traverseFast as traverse } from "@babel/types"; import { outdent } from "outdent"; -import { PROJECT_ROOT, SOURCE_DIR } from "../../utils/index.js"; +import { PROJECT_ROOT, SOURCE_DIR } from "../../utilities/index.js"; import * as transforms from "./transforms/index.js"; const packageTransforms = new Map([ diff --git a/scripts/build/transform/transforms/get-public-doc-functionality.js b/scripts/build/transform/transforms/get-public-doc-functionality.js index 7964161569db..9f69faf849a9 100644 --- a/scripts/build/transform/transforms/get-public-doc-functionality.js +++ b/scripts/build/transform/transforms/get-public-doc-functionality.js @@ -1,7 +1,7 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; import makeSynchronized from "make-synchronized"; -import { SOURCE_DIR } from "../../../utils/index.js"; +import { SOURCE_DIR } from "../../../utilities/index.js"; const PUBLIC_MODULE_URL = pathToFileURL( path.join(SOURCE_DIR, "document/public.js"), diff --git a/scripts/build/transform/transforms/transform-doc-module-imports.js b/scripts/build/transform/transforms/transform-doc-module-imports.js index ebf0ee34a3d9..6d635637a7a0 100644 --- a/scripts/build/transform/transforms/transform-doc-module-imports.js +++ b/scripts/build/transform/transforms/transform-doc-module-imports.js @@ -1,5 +1,5 @@ import path from "node:path"; -import { SOURCE_DIR } from "../../../utils/index.js"; +import { SOURCE_DIR } from "../../../utilities/index.js"; import getPublicDocFunctionality from "./get-public-doc-functionality.js"; import { createIdentifier, diff --git a/scripts/build/utils.js b/scripts/build/utilities.js similarity index 96% rename from scripts/build/utils.js rename to scripts/build/utilities.js index a766f719366c..71c1d0d5ca96 100644 --- a/scripts/build/utils.js +++ b/scripts/build/utilities.js @@ -3,7 +3,7 @@ import path from "node:path"; import url from "node:url"; import { resolve } from "import-meta-resolve"; import { isUrl, toUrl } from "url-or-path"; -import { PROJECT_ROOT } from "../utils/index.js"; +import { PROJECT_ROOT } from "../utilities/index.js"; const NODE_MODULES_PATH = path.join(PROJECT_ROOT, "node_modules"); diff --git a/scripts/bundle-eslint-config.js b/scripts/bundle-eslint-config.js index 1a056c243554..972005bb6a74 100644 --- a/scripts/bundle-eslint-config.js +++ b/scripts/bundle-eslint-config.js @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import eslintPluginCompat from "eslint-plugin-compat"; import buildConfig from "./build/config.js"; -import { DIST_DIR } from "./utils/index.js"; +import { DIST_DIR } from "./utilities/index.js"; const { browserslist: targets } = JSON.parse( await fs.readFile(new URL("../package.json", import.meta.url)), diff --git a/scripts/changelog-for-patch.js b/scripts/changelog-for-patch.js index bccd256660be..764a40e70fae 100644 --- a/scripts/changelog-for-patch.js +++ b/scripts/changelog-for-patch.js @@ -10,7 +10,7 @@ import { getEntries, printEntries, replaceVersions, -} from "./utils/changelog.js"; +} from "./utilities/changelog.js"; const { previousVersion, newVersion } = parseArguments(); diff --git a/scripts/draft-blog-post.js b/scripts/draft-blog-post.js index e20cc778e454..305924c3edc5 100644 --- a/scripts/draft-blog-post.js +++ b/scripts/draft-blog-post.js @@ -12,7 +12,7 @@ import { getEntries, printEntries, replaceVersions, -} from "./utils/changelog.js"; +} from "./utilities/changelog.js"; const { __dirname, require } = createEsmUtils(import.meta); const blogDir = path.join(__dirname, "../website/blog"); diff --git a/scripts/format-test-lint.js b/scripts/format-test-lint.js index aed9b1831459..9cf5f7e0e221 100644 --- a/scripts/format-test-lint.js +++ b/scripts/format-test-lint.js @@ -4,7 +4,7 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { PROJECT_ROOT } from "./utils/index.js"; +import { PROJECT_ROOT } from "./utilities/index.js"; const FORMAT_TEST_DIRECTORY = path.join(PROJECT_ROOT, "tests/format/"); const TEST_SCRIPT_FILE_NAME = "format.test.js"; diff --git a/scripts/generate-changelog.js b/scripts/generate-changelog.js index 32d9b472d173..92c776919db4 100755 --- a/scripts/generate-changelog.js +++ b/scripts/generate-changelog.js @@ -12,7 +12,7 @@ import fs from "node:fs/promises"; import enquirer from "enquirer"; import openEditor from "open-editor"; -import { CHANGELOG_CATEGORIES } from "./utils/changelog-categories.js"; +import { CHANGELOG_CATEGORIES } from "./utilities/changelog-categories.js"; const prNumberPrompt = new enquirer.NumberPrompt({ message: "Input your Pull Request number:", diff --git a/scripts/generate-schema.js b/scripts/generate-schema.js index fac2f42b747d..389554dba5a0 100644 --- a/scripts/generate-schema.js +++ b/scripts/generate-schema.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -import { generateSchema } from "./utils/generate-schema.js"; +import { generateSchema } from "./utilities/generate-schema.js"; console.log(await generateSchema()); diff --git a/scripts/lint-changelog.js b/scripts/lint-changelog.js index ea751a6e0c1b..26ceec1c62b1 100644 --- a/scripts/lint-changelog.js +++ b/scripts/lint-changelog.js @@ -6,7 +6,7 @@ import indexToPosition from "index-to-position"; import { outdent } from "outdent"; import remarkParse from "remark-parse"; import unified from "unified"; -import { CHANGELOG_CATEGORIES } from "./utils/changelog-categories.js"; +import { CHANGELOG_CATEGORIES } from "./utilities/changelog-categories.js"; const CHANGELOG_DIR = "changelog_unreleased"; const TEMPLATE_FILE = "TEMPLATE.md"; diff --git a/scripts/release/get-formatted-date.js b/scripts/release/get-formatted-date.js index 0d7c40c8a3cd..e4b71ef4dca0 100644 --- a/scripts/release/get-formatted-date.js +++ b/scripts/release/get-formatted-date.js @@ -1,4 +1,4 @@ -// TODO: Implement this in `utils.js` when jest.importActual is landed. +// TODO: Implement this in `utilities.js` when jest.importActual is landed. export default function getFormattedDate() { const date = new Date(); const isoStr = date.toISOString(); diff --git a/scripts/release/run.js b/scripts/release/run.js index 97bba9c516af..29fb43156aff 100644 --- a/scripts/release/run.js +++ b/scripts/release/run.js @@ -1,7 +1,7 @@ import semver from "semver"; import parseArguments from "./parse-arguments.js"; import * as steps from "./steps/index.js"; -import { logPromise, readJson } from "./utils.js"; +import { logPromise, readJson } from "./utilities.js"; const params = parseArguments(); const { diff --git a/scripts/release/steps/bump-prettier.js b/scripts/release/steps/bump-prettier.js index c77c89082eb4..f21592c5f692 100644 --- a/scripts/release/steps/bump-prettier.js +++ b/scripts/release/steps/bump-prettier.js @@ -1,6 +1,12 @@ import fs from "node:fs"; import semver from "semver"; -import { logPromise, readJson, runGit, runYarn, writeJson } from "../utils.js"; +import { + logPromise, + readJson, + runGit, + runYarn, + writeJson, +} from "../utilities.js"; async function format() { await runYarn(["fix:prettier"]); diff --git a/scripts/release/steps/check-git-status.js b/scripts/release/steps/check-git-status.js index f30abd8c3032..835dcc1d05c1 100644 --- a/scripts/release/steps/check-git-status.js +++ b/scripts/release/steps/check-git-status.js @@ -1,4 +1,4 @@ -import { runGit } from "../utils.js"; +import { runGit } from "../utilities.js"; export default async function checkGitStatus({ next }) { const { stdout: status } = await runGit(["status", "--porcelain"]); diff --git a/scripts/release/steps/clean-changelog.js b/scripts/release/steps/clean-changelog.js index 24482ace12ec..48f8b4cad35a 100644 --- a/scripts/release/steps/clean-changelog.js +++ b/scripts/release/steps/clean-changelog.js @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import { fileURLToPath } from "node:url"; import fastGlob from "fast-glob"; -import { runGit } from "../utils.js"; +import { runGit } from "../utilities.js"; export default async function cleanChangelog({ repo }) { const changelogUnreleasedDir = fileURLToPath( diff --git a/scripts/release/steps/generate-bundles.js b/scripts/release/steps/generate-bundles.js index 5a536be3e7db..7f61b1bd762b 100644 --- a/scripts/release/steps/generate-bundles.js +++ b/scripts/release/steps/generate-bundles.js @@ -1,5 +1,5 @@ import styleText from "node-style-text"; -import { logPromise, readJson, runYarn } from "../utils.js"; +import { logPromise, readJson, runYarn } from "../utilities.js"; export default async function generateBundles({ dry, version, manual }) { if (!manual) { diff --git a/scripts/release/steps/install-dependencies.js b/scripts/release/steps/install-dependencies.js index 3e108d82a00e..67a5a62d99ee 100644 --- a/scripts/release/steps/install-dependencies.js +++ b/scripts/release/steps/install-dependencies.js @@ -1,5 +1,5 @@ import fs from "node:fs/promises"; -import { runGit, runYarn } from "../utils.js"; +import { runGit, runYarn } from "../utilities.js"; const PROJECT_ROOT = new URL("../../../", import.meta.url); diff --git a/scripts/release/steps/lint-files.js b/scripts/release/steps/lint-files.js index 8442d66f2947..011649829fa3 100644 --- a/scripts/release/steps/lint-files.js +++ b/scripts/release/steps/lint-files.js @@ -1,4 +1,4 @@ -import { runYarn } from "../utils.js"; +import { runYarn } from "../utilities.js"; const lintFiles = () => runYarn("lint"); diff --git a/scripts/release/steps/merge-blog-post.js b/scripts/release/steps/merge-blog-post.js index 0b3546d41ca1..e1cfa7e996d5 100644 --- a/scripts/release/steps/merge-blog-post.js +++ b/scripts/release/steps/merge-blog-post.js @@ -1,6 +1,6 @@ import styleText from "node-style-text"; import semver from "semver"; -import { runGit, waitForEnter } from "../utils.js"; +import { runGit, waitForEnter } from "../utilities.js"; export default async function mergeBlogPost({ dry, diff --git a/scripts/release/steps/post-publish-steps.js b/scripts/release/steps/post-publish-steps.js index dfba9401d42e..8e07f39ff25d 100644 --- a/scripts/release/steps/post-publish-steps.js +++ b/scripts/release/steps/post-publish-steps.js @@ -1,6 +1,6 @@ import styleText from "node-style-text"; import outdent from "outdent"; -import { fetchText, logPromise, writeFile } from "../utils.js"; +import { fetchText, logPromise, writeFile } from "../utilities.js"; const SCHEMA_REPO = "SchemaStore/schemastore"; const SCHEMA_PATH = "src/schemas/json/prettierrc.json"; @@ -10,7 +10,7 @@ const EDIT_URL = `https://github.com/${SCHEMA_REPO}/edit/master/${SCHEMA_PATH}`; // Any optional or manual step can be warned in this script. async function checkSchema() { - const { generateSchema } = await import("../../utils/generate-schema.js"); + const { generateSchema } = await import("../../utilities/generate-schema.js"); const schema = await generateSchema(); const remoteSchema = await logPromise( "Checking current schema in SchemaStore", diff --git a/scripts/release/steps/publish-to-npm.js b/scripts/release/steps/publish-to-npm.js index ec6c4ca2320b..7f79956adc59 100644 --- a/scripts/release/steps/publish-to-npm.js +++ b/scripts/release/steps/publish-to-npm.js @@ -1,6 +1,6 @@ import enquirer from "enquirer"; import spawn from "nano-spawn"; -import { waitForEnter } from "../utils.js"; +import { waitForEnter } from "../utilities.js"; export default async function publishToNpm({ dry }) { console.log(`Ready to publish to NPM${dry ? "(--dry-run)" : ""}`); diff --git a/scripts/release/steps/push-to-git.js b/scripts/release/steps/push-to-git.js index 7304c56ac271..381dc21a005f 100644 --- a/scripts/release/steps/push-to-git.js +++ b/scripts/release/steps/push-to-git.js @@ -1,4 +1,4 @@ -import { runGit } from "../utils.js"; +import { runGit } from "../utilities.js"; export default async function pushToGit({ version, repo }) { await runGit(["commit", "-am", `Release ${version}`]); diff --git a/scripts/release/steps/show-instructions-after-npm-publish.js b/scripts/release/steps/show-instructions-after-npm-publish.js index ba23e64406b9..d5663095f28b 100644 --- a/scripts/release/steps/show-instructions-after-npm-publish.js +++ b/scripts/release/steps/show-instructions-after-npm-publish.js @@ -5,7 +5,7 @@ import { getBlogPostInfo, getChangelogContent, waitForEnter, -} from "../utils.js"; +} from "../utilities.js"; const RELEASE_URL_BASE = "https://github.com/prettier/prettier/releases/new?"; export function getReleaseUrl(version, previousVersion) { diff --git a/scripts/release/steps/update-changelog.js b/scripts/release/steps/update-changelog.js index 2c391755d10a..cbcf9407cc85 100644 --- a/scripts/release/steps/update-changelog.js +++ b/scripts/release/steps/update-changelog.js @@ -8,7 +8,7 @@ import { logPromise, runYarn, waitForEnter, -} from "../utils.js"; +} from "../utilities.js"; function writeChangelog(params) { const changelog = fs.readFileSync("CHANGELOG.md", "utf8"); diff --git a/scripts/release/steps/update-dependents-count.js b/scripts/release/steps/update-dependents-count.js index d2b054fe95f5..07dcd083fb93 100644 --- a/scripts/release/steps/update-dependents-count.js +++ b/scripts/release/steps/update-dependents-count.js @@ -1,5 +1,5 @@ import styleText from "node-style-text"; -import { fetchText, logPromise, processFile, runGit } from "../utils.js"; +import { fetchText, logPromise, processFile, runGit } from "../utilities.js"; async function getNpmDependentsCount() { const npmPage = await logPromise( diff --git a/scripts/release/steps/update-version.js b/scripts/release/steps/update-version.js index 210631e117f1..28c69a739ca8 100644 --- a/scripts/release/steps/update-version.js +++ b/scripts/release/steps/update-version.js @@ -1,4 +1,4 @@ -import { processFile, readJson, runYarn, writeJson } from "../utils.js"; +import { processFile, readJson, runYarn, writeJson } from "../utilities.js"; export default async function updateVersion({ version, next }) { const pkg = await readJson("package.json"); diff --git a/scripts/release/steps/wait-for-bot-release.js b/scripts/release/steps/wait-for-bot-release.js index fc3390ccf899..8919e8cfee9f 100644 --- a/scripts/release/steps/wait-for-bot-release.js +++ b/scripts/release/steps/wait-for-bot-release.js @@ -1,6 +1,6 @@ import styleText from "node-style-text"; import outdent from "outdent"; -import { logPromise, waitForEnter } from "../utils.js"; +import { logPromise, waitForEnter } from "../utilities.js"; export async function isVersionReleased(version) { const response = await fetch("https://registry.npmjs.org/prettier/"); diff --git a/scripts/release/utils.js b/scripts/release/utilities.js similarity index 100% rename from scripts/release/utils.js rename to scripts/release/utilities.js diff --git a/scripts/utils/changelog-categories.js b/scripts/utilities/changelog-categories.js similarity index 100% rename from scripts/utils/changelog-categories.js rename to scripts/utilities/changelog-categories.js diff --git a/scripts/utils/changelog.js b/scripts/utilities/changelog.js similarity index 100% rename from scripts/utils/changelog.js rename to scripts/utilities/changelog.js diff --git a/scripts/utils/generate-schema.js b/scripts/utilities/generate-schema.js similarity index 100% rename from scripts/utils/generate-schema.js rename to scripts/utilities/generate-schema.js diff --git a/scripts/utils/index.js b/scripts/utilities/index.js similarity index 100% rename from scripts/utils/index.js rename to scripts/utilities/index.js diff --git a/src/cli/expand-patterns.js b/src/cli/expand-patterns.js index 6e75e585ed68..761633e430a7 100644 --- a/src/cli/expand-patterns.js +++ b/src/cli/expand-patterns.js @@ -4,7 +4,7 @@ import { directoryIgnorerWithoutNodeModules, } from "./directory-ignorer.js"; import { fastGlob } from "./prettier-internal.js"; -import { lstatSafe, normalizeToPosix } from "./utils.js"; +import { lstatSafe, normalizeToPosix } from "./utilities.js"; /** @import {Context} from './context.js' */ diff --git a/src/cli/file-info.js b/src/cli/file-info.js index 1b21bdfbc2d4..ead85ffe5e44 100644 --- a/src/cli/file-info.js +++ b/src/cli/file-info.js @@ -1,7 +1,7 @@ import path from "node:path"; import stringify from "fast-json-stable-stringify"; import { format, getFileInfo } from "../index.js"; -import { printToScreen } from "./utils.js"; +import { printToScreen } from "./utilities.js"; // Note: This does not work with `--config-precedence` async function logFileInfoOrDie(context) { diff --git a/src/cli/find-cache-file.js b/src/cli/find-cache-file.js index 7de9eee46175..8d94ed78a5dc 100644 --- a/src/cli/find-cache-file.js +++ b/src/cli/find-cache-file.js @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import findCacheDirectory from "find-cache-directory"; -import { isJson, statSafe } from "./utils.js"; +import { isJson, statSafe } from "./utilities.js"; /** * Find default cache file (`./node_modules/.cache/prettier/.prettier-cache`) using https://github.com/sindresorhus/find-cache-directory diff --git a/src/cli/find-config-path.js b/src/cli/find-config-path.js index bc752efbb3bd..2337eae96983 100644 --- a/src/cli/find-config-path.js +++ b/src/cli/find-config-path.js @@ -1,6 +1,6 @@ import path from "node:path"; import { resolveConfigFile } from "../index.js"; -import { normalizeToPosix, printToScreen } from "./utils.js"; +import { normalizeToPosix, printToScreen } from "./utilities.js"; async function logResolvedConfigPathOrDie(context) { const file = context.argv.findConfigPath; diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js index 6fddcb20ac5c..1dec77ce012b 100644 --- a/src/cli/format-results-cache.js +++ b/src/cli/format-results-cache.js @@ -5,7 +5,7 @@ import fs from "node:fs"; import stringify from "fast-json-stable-stringify"; import fileEntryCache from "file-entry-cache"; import { version as prettierVersion } from "../index.js"; -import { createHash } from "./utils.js"; +import { createHash } from "./utilities.js"; const optionsHashCache = new WeakMap(); const nodeVersion = process.version; diff --git a/src/cli/format.js b/src/cli/format.js index c0a50e3fbad5..0c41e1aea595 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -13,7 +13,7 @@ import { errors, picocolors, } from "./prettier-internal.js"; -import { normalizeToPosix, statSafe } from "./utils.js"; +import { normalizeToPosix, statSafe } from "./utilities.js"; function diff(a, b) { return createTwoFilesPatch("", "", a, b, "", "", { context: 2 }); diff --git a/src/cli/index.js b/src/cli/index.js index 0b97d62cab7d..7d9959e70811 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -8,7 +8,7 @@ import mockable from "./mockable.js"; import { parseArgvWithoutPlugins } from "./options/parse-cli-arguments.js"; import printSupportInfo from "./print-support-info.js"; import { createDetailedUsage, createUsage } from "./usage.js"; -import { printToScreen } from "./utils.js"; +import { printToScreen } from "./utilities.js"; async function run(rawArguments = process.argv.slice(2)) { // Create a default level logger, so we can log errors during `logLevel` parsing diff --git a/src/cli/mockable.js b/src/cli/mockable.js index 804da58fd66b..f807ad18b6f1 100644 --- a/src/cli/mockable.js +++ b/src/cli/mockable.js @@ -5,7 +5,7 @@ import { isCI } from "ci-info"; import { __internal as sharedWithCli } from "../index.js"; import clearStreamText from "./utilities/clear-stream-text.js"; -const mockable = sharedWithCli.utils.createMockable({ +const mockable = sharedWithCli.utilities.createMockable({ clearStreamText, getTimestamp: performance.now.bind(performance), isCI: () => isCI, diff --git a/src/cli/options/parse-cli-arguments.js b/src/cli/options/parse-cli-arguments.js index ba6b69eff8e7..7681e9249a4f 100644 --- a/src/cli/options/parse-cli-arguments.js +++ b/src/cli/options/parse-cli-arguments.js @@ -1,5 +1,5 @@ import camelCase from "camelcase"; -import { pick } from "../utils.js"; +import { pick } from "../utilities.js"; import createMinimistOptions from "./create-minimist-options.js"; import { getContextOptionsWithoutPlugins } from "./get-context-options.js"; import minimist from "./minimist.js"; diff --git a/src/cli/print-support-info.js b/src/cli/print-support-info.js index 5dc91d7662d4..fc46bae0b0b1 100644 --- a/src/cli/print-support-info.js +++ b/src/cli/print-support-info.js @@ -1,6 +1,6 @@ import stringify from "fast-json-stable-stringify"; import { format, getSupportInfo } from "../index.js"; -import { omit, printToScreen } from "./utils.js"; +import { omit, printToScreen } from "./utilities.js"; const sortByName = (array) => array.sort((a, b) => a.name.localeCompare(b.name)); diff --git a/src/cli/usage.js b/src/cli/usage.js index f40337c87aeb..3a6c8e15c0c1 100644 --- a/src/cli/usage.js +++ b/src/cli/usage.js @@ -1,7 +1,7 @@ import camelCase from "camelcase"; import { categoryOrder, usageSummary } from "./constants.evaluate.js"; import { formatOptionsHiddenDefaults } from "./prettier-internal.js"; -import { groupBy } from "./utils.js"; +import { groupBy } from "./utilities.js"; const OPTION_USAGE_THRESHOLD = 25; const CHOICE_USAGE_MARGIN = 3; diff --git a/src/cli/utils.js b/src/cli/utilities.js similarity index 97% rename from src/cli/utils.js rename to src/cli/utilities.js index 01de5ed484a3..16a0d6f52691 100644 --- a/src/cli/utils.js +++ b/src/cli/utilities.js @@ -106,7 +106,7 @@ const normalizeToPosix = ? (filepath) => filepath.replaceAll("\\", "/") : (filepath) => filepath; -export const { omit } = sharedWithCli.utils; +export const { omit } = sharedWithCli.utilities; export { createHash, groupBy, diff --git a/src/common/end-of-line.js b/src/common/end-of-line.js index 5d971ff2eda6..22e04b9878ee 100644 --- a/src/common/end-of-line.js +++ b/src/common/end-of-line.js @@ -1,73 +1,80 @@ +import * as assert from "#universal/assert"; + /** -@typedef {"auto" | "lf" | "crlf" | "cr"} EndOfLineOption -@typedef {"\r" | "\r\n" | "\n"} EndOfLine +@typedef {"auto" | OPTION_CR | OPTION_CRLF | OPTION_LF} EndOfLineOption +@typedef {CHARACTER_CR | CHARACTER_CRLF | CHARACTER_LF} EndOfLine */ +const OPTION_CR = "cr"; +const OPTION_CRLF = "crlf"; +const OPTION_LF = "lf"; +const DEFAULT_OPTION = OPTION_LF; + +const CHARACTER_CR = "\r"; +const CHARACTER_CRLF = "\r\n"; +const CHARACTER_LF = "\n"; +const DEFAULT_EOL = CHARACTER_LF; + /** @param {string} text @returns {EndOfLineOption} */ function guessEndOfLine(text) { - const index = text.indexOf("\r"); + const index = text.indexOf(CHARACTER_CR); if (index !== -1) { - return text.charAt(index + 1) === "\n" ? "crlf" : "cr"; + return text.charAt(index + 1) === CHARACTER_LF ? OPTION_CRLF : OPTION_CR; } - return "lf"; + return DEFAULT_OPTION; } /** -@param {EndOfLineOption} value +@param {EndOfLineOption} endOfLineOption @returns {EndOfLine} */ -function convertEndOfLineToChars(value) { - switch (value) { - case "cr": - return "\r"; - case "crlf": - return "\r\n"; - default: - return "\n"; - } +function convertEndOfLineOptionToCharacter(endOfLineOption) { + return endOfLineOption === OPTION_CR + ? CHARACTER_CR + : endOfLineOption === OPTION_CRLF + ? CHARACTER_CRLF + : DEFAULT_EOL; } +const regexps = new Map([ + [CHARACTER_LF, /\n/gu], + [CHARACTER_CR, /\r/gu], + [CHARACTER_CRLF, /\r\n/gu], +]); /** @param {string} text -@param {EndOfLine} eol +@param {EndOfLine} endOfLineCharacter @returns {number} */ -function countEndOfLineChars(text, eol) { - let regex; +function countEndOfLineCharacters(text, endOfLineCharacter) { + const regex = regexps.get(endOfLineCharacter); - switch (eol) { - case "\n": - regex = /\n/gu; - break; - case "\r": - regex = /\r/gu; - break; - case "\r\n": - regex = /\r\n/gu; - break; - default: - /* c8 ignore next */ - throw new Error(`Unexpected "eol" ${JSON.stringify(eol)}.`); + /* c8 ignore next */ + if (process.env.NODE_ENV !== "production") { + assert.ok( + regex, + `Unexpected 'endOfLineCharacter': ${JSON.stringify(endOfLineCharacter)}.`, + ); } - const endOfLines = text.match(regex); - return endOfLines ? endOfLines.length : 0; + return text.match(regex)?.length ?? 0; } +const END_OF_LINE_REGEXP = /\r\n?/gu; /** @param {string} text @returns {string} */ function normalizeEndOfLine(text) { - return text.replaceAll(/\r\n?/gu, "\n"); + return text.replaceAll(END_OF_LINE_REGEXP, CHARACTER_LF); } export { - convertEndOfLineToChars, - countEndOfLineChars, + convertEndOfLineOptionToCharacter, + countEndOfLineCharacters, guessEndOfLine, normalizeEndOfLine, }; diff --git a/src/common/get-file-info.js b/src/common/get-file-info.js index d74228c55542..656910f21f7b 100644 --- a/src/common/get-file-info.js +++ b/src/common/get-file-info.js @@ -1,7 +1,7 @@ import { resolveConfig } from "../config/resolve-config.js"; import { loadBuiltinPlugins, loadPlugins } from "../main/plugins/index.js"; -import { isIgnored } from "../utils/ignore.js"; -import inferParser from "../utils/infer-parser.js"; +import { isIgnored } from "../utilities/ignore.js"; +import inferParser from "../utilities/infer-parser.js"; /** * @typedef {{ ignorePath?: string | URL | (string | URL)[], withNodeModules?: boolean, plugins: object, resolveConfig?: boolean }} FileInfoOptions diff --git a/src/common/mockable.js b/src/common/mockable.js index cbf68566c89e..786c2221b42a 100644 --- a/src/common/mockable.js +++ b/src/common/mockable.js @@ -1,4 +1,4 @@ -import createMockable from "../utils/create-mockable.js"; +import createMockable from "../utilities/create-mockable.js"; const mockable = createMockable({ getPrettierConfigSearchStopDirectory: () => undefined, diff --git a/src/config/prettier-config/load-external-config.js b/src/config/prettier-config/load-external-config.js index 0f69763b50e7..fa9a31eba905 100644 --- a/src/config/prettier-config/load-external-config.js +++ b/src/config/prettier-config/load-external-config.js @@ -1,5 +1,5 @@ -import importFromFile from "../../utils/import-from-file.js"; -import requireFromFile from "../../utils/require-from-file.js"; +import importFromFile from "../../utilities/import-from-file.js"; +import requireFromFile from "../../utilities/require-from-file.js"; const requireErrorCodesShouldBeIgnored = new Set([ "MODULE_NOT_FOUND", diff --git a/src/config/prettier-config/loaders.js b/src/config/prettier-config/loaders.js index de8482998c09..5d2862e75657 100644 --- a/src/config/prettier-config/loaders.js +++ b/src/config/prettier-config/loaders.js @@ -2,7 +2,7 @@ import { pathToFileURL } from "node:url"; import json5 from "json5"; import parseJson from "parse-json"; import { parse as parseToml } from "smol-toml"; -import readFile from "../../utils/read-file.js"; +import readFile from "../../utilities/read-file.js"; async function readJson(file) { const content = await readFile(file); diff --git a/src/config/resolve-config.js b/src/config/resolve-config.js index 44fb6017c505..14e19901f230 100644 --- a/src/config/resolve-config.js +++ b/src/config/resolve-config.js @@ -1,7 +1,7 @@ import path from "node:path"; import micromatch from "micromatch"; import { toPath } from "url-or-path"; -import partition from "../utils/partition.js"; +import partition from "../utilities/partition.js"; import { clearEditorconfigCache, loadEditorconfig as loadEditorconfigForFile, diff --git a/src/document/printer/printer.js b/src/document/printer/printer.js index 70fcb9e03952..0e4059dabd83 100644 --- a/src/document/printer/printer.js +++ b/src/document/printer/printer.js @@ -1,5 +1,5 @@ -import { convertEndOfLineToChars } from "../../common/end-of-line.js"; -import getStringWidth from "../../utils/get-string-width.js"; +import { convertEndOfLineOptionToCharacter } from "../../common/end-of-line.js"; +import getStringWidth from "../../utilities/get-string-width.js"; import { DOC_TYPE_ALIGN, DOC_TYPE_ARRAY, @@ -180,7 +180,7 @@ function printDocToString(doc, options) { const groupModeMap = Object.create(null); const width = options.printWidth; - const newLine = convertEndOfLineToChars(options.endOfLine); + const newLine = convertEndOfLineOptionToCharacter(options.endOfLine); let position = 0; // commands is basically a stack. We've turned a recursive call into a // while loop which is much faster. The while loop below adds new diff --git a/src/document/utilities/assert-doc.js b/src/document/utilities/assert-doc.js index 743bc3916622..2c4f5e9e51f2 100644 --- a/src/document/utilities/assert-doc.js +++ b/src/document/utilities/assert-doc.js @@ -1,4 +1,4 @@ -import noop from "../../utils/noop.js"; +import noop from "../../utilities/noop.js"; import { DOC_TYPE_IF_BREAK, DOC_TYPE_LINE, diff --git a/src/index.cjs b/src/index.cjs index b8e3befb0681..e526bbc9085d 100644 --- a/src/index.cjs +++ b/src/index.cjs @@ -39,7 +39,7 @@ for (const name of debugApiFunctionNames) { prettier.__debug = debugApis; if (process.env.NODE_ENV === "production") { - prettier.util = require("./utils/public.js"); + prettier.util = require("./utilities/public.js"); prettier.doc = require("./document/public.js"); prettier.version = require("./main/version.evaluate.js").default; } else { @@ -47,7 +47,7 @@ if (process.env.NODE_ENV === "production") { util: { get() { try { - return require("./utils/public.js"); + return require("./utilities/public.js"); } catch { // No op } diff --git a/src/index.d.ts b/src/index.d.ts index a1b53ab58d5f..de4bdc9c6ca4 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -844,7 +844,7 @@ export function getSupportInfo( */ export const version: string; -// https://github.com/prettier/prettier/blob/next/src/utils/public.js +// https://github.com/prettier/prettier/blob/main/src/utilities/public.js export namespace util { interface SkipOptions { backwards?: boolean | undefined; diff --git a/src/index.js b/src/index.js index 8a8a7050bbc3..3dd68e019bf2 100644 --- a/src/index.js +++ b/src/index.js @@ -30,10 +30,10 @@ import { getSupportInfo as getSupportInfoWithoutPlugins, normalizeOptionSettings, } from "./main/support.js"; -import createMockable from "./utils/create-mockable.js"; -import { createIsIgnoredFunction } from "./utils/ignore.js"; -import inferParserWithoutPlugins from "./utils/infer-parser.js"; -import omit from "./utils/object-omit.js"; +import createMockable from "./utilities/create-mockable.js"; +import { createIsIgnoredFunction } from "./utilities/ignore.js"; +import inferParserWithoutPlugins from "./utilities/infer-parser.js"; +import omit from "./utilities/object-omit.js"; /** * @param {*} fn @@ -109,7 +109,7 @@ const sharedWithCli = { createTwoFilesPatch, picocolors, closetLevenshteinMatch, - utils: { + utilities: { omit, createMockable, }, @@ -139,4 +139,4 @@ export { export { default as getFileInfo } from "./common/get-file-info.js"; export * as doc from "./document/public.js"; export { default as version } from "./main/version.evaluate.js"; -export * as util from "./utils/public.js"; +export * as util from "./utilities/public.js"; diff --git a/src/language-css/get-visitor-keys.js b/src/language-css/get-visitor-keys.js index d58d77cf3bd3..f4d75eb174a8 100644 --- a/src/language-css/get-visitor-keys.js +++ b/src/language-css/get-visitor-keys.js @@ -1,5 +1,5 @@ -import createGetVisitorKeys from "../utils/create-get-visitor-keys.js"; -import visitorKeys from "./visitor-keys.js"; +import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js"; +import visitorKeys from "./visitor-keys.evaluate.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys); diff --git a/src/language-css/languages.evaluate.js b/src/language-css/languages.evaluate.js index 240414dcbc75..26878ea911c1 100644 --- a/src/language-css/languages.evaluate.js +++ b/src/language-css/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; const languages = [ createLanguage(linguistLanguages.CSS, (data) => ({ diff --git a/src/language-css/loc.js b/src/language-css/loc.js index 2832f1e3934b..c781b8caaab4 100644 --- a/src/language-css/loc.js +++ b/src/language-css/loc.js @@ -1,6 +1,6 @@ -import isNonEmptyArray from "../utils/is-non-empty-array.js"; -import lineColumnToIndex from "../utils/line-column-to-index.js"; -import { skipEverythingButNewLine } from "../utils/skip.js"; +import isNonEmptyArray from "../utilities/is-non-empty-array.js"; +import lineColumnToIndex from "../utilities/line-column-to-index.js"; +import { skipEverythingButNewLine } from "../utilities/skip.js"; function fixValueWordLoc(node, originalIndex) { const { value } = node; diff --git a/src/language-css/parse/parse-media-query.js b/src/language-css/parse/parse-media-query.js index 39fed66a005f..ad6c96cc2848 100644 --- a/src/language-css/parse/parse-media-query.js +++ b/src/language-css/parse/parse-media-query.js @@ -1,5 +1,5 @@ import postcssMediaQueryParser from "postcss-media-query-parser"; -import { addMissingType, addTypePrefix } from "./utils.js"; +import { addMissingType, addTypePrefix } from "./utilities.js"; const parse = postcssMediaQueryParser.default; diff --git a/src/language-css/parse/parse-selector.js b/src/language-css/parse/parse-selector.js index 241631db4ff8..9816d7447edf 100644 --- a/src/language-css/parse/parse-selector.js +++ b/src/language-css/parse/parse-selector.js @@ -1,5 +1,5 @@ import PostcssSelectorParser from "postcss-selector-parser/dist/processor.js"; -import { addTypePrefix } from "./utils.js"; +import { addTypePrefix } from "./utilities.js"; function parseSelector(selector) { // If there's a comment inside of a selector, the parser tries to parse diff --git a/src/language-css/parse/parse-value.js b/src/language-css/parse/parse-value.js index ea628fe53f9f..0a275c1243ae 100644 --- a/src/language-css/parse/parse-value.js +++ b/src/language-css/parse/parse-value.js @@ -1,11 +1,12 @@ import PostcssValuesParser from "postcss-values-parser/lib/parser.js"; -import getFunctionArgumentsText from "../utils/get-function-arguments-text.js"; -import getValueRoot from "../utils/get-value-root.js"; -import hasSCSSInterpolation from "../utils/has-scss-interpolation.js"; -import hasStringOrFunction from "../utils/has-string-or-function.js"; -import isSCSSVariable from "../utils/is-scss-variable.js"; +import isObject from "../../utilities/is-object.js"; +import getFunctionArgumentsText from "../utilities/get-function-arguments-text.js"; +import getValueRoot from "../utilities/get-value-root.js"; +import hasSCSSInterpolation from "../utilities/has-scss-interpolation.js"; +import hasStringOrFunction from "../utilities/has-string-or-function.js"; +import isSCSSVariable from "../utilities/is-scss-variable.js"; import parseSelector from "./parse-selector.js"; -import { addTypePrefix } from "./utils.js"; +import { addTypePrefix } from "./utilities.js"; const isClosingParenthesis = (node) => node.type === "paren" && node.value === ")"; @@ -156,7 +157,7 @@ function flattenGroups(node) { } function parseNestedValue(node, options) { - if (node && typeof node === "object") { + if (isObject(node)) { for (const key in node) { if (key !== "parent") { parseNestedValue(node[key], options); diff --git a/src/language-css/parse/utils.js b/src/language-css/parse/utilities.js similarity index 87% rename from src/language-css/parse/utils.js rename to src/language-css/parse/utilities.js index d5e3a82927a2..0cee18b130b7 100644 --- a/src/language-css/parse/utils.js +++ b/src/language-css/parse/utilities.js @@ -1,5 +1,7 @@ +import isObject from "../../utilities/is-object.js"; + function addTypePrefix(node, prefix, skipPrefix) { - if (node && typeof node === "object") { + if (isObject(node)) { delete node.parent; for (const key in node) { addTypePrefix(node[key], prefix, skipPrefix); @@ -17,7 +19,7 @@ function addTypePrefix(node, prefix, skipPrefix) { } function addMissingType(node) { - if (node && typeof node === "object") { + if (isObject(node)) { delete node.parent; for (const key in node) { addMissingType(node[key]); diff --git a/src/language-css/parser-postcss.js b/src/language-css/parser-postcss.js index 605de5111e94..ca8d0a25ae60 100644 --- a/src/language-css/parser-postcss.js +++ b/src/language-css/parser-postcss.js @@ -3,6 +3,7 @@ import postcssLess from "postcss-less"; import postcssScssParse from "postcss-scss/lib/scss-parse"; import createError from "../common/parser-create-error.js"; import { parseFrontMatter } from "../main/front-matter/index.js"; +import isObject from "../utilities/is-object.js"; import { calculateLoc, locEnd, @@ -12,16 +13,16 @@ import { import parseMediaQuery from "./parse/parse-media-query.js"; import parseSelector from "./parse/parse-selector.js"; import parseValue from "./parse/parse-value.js"; -import { addTypePrefix } from "./parse/utils.js"; +import { addTypePrefix } from "./parse/utilities.js"; import { hasIgnorePragma, hasPragma } from "./pragma.js"; -import isModuleRuleName from "./utils/is-module-rule-name.js"; -import isSCSSNestedPropertyNode from "./utils/is-scss-nested-property-node.js"; +import isModuleRuleName from "./utilities/is-module-rule-name.js"; +import isSCSSNestedPropertyNode from "./utilities/is-scss-nested-property-node.js"; const DEFAULT_SCSS_DIRECTIVE = /(\s*)(!default).*$/u; const GLOBAL_SCSS_DIRECTIVE = /(\s*)(!global).*$/u; function parseNestedCSS(node, options) { - if (node && typeof node === "object") { + if (isObject(node)) { delete node.parent; for (const key in node) { diff --git a/src/language-css/print/comma-separated-value-group.js b/src/language-css/print/comma-separated-value-group.js index f1cdeeb2745f..0077005b639a 100644 --- a/src/language-css/print/comma-separated-value-group.js +++ b/src/language-css/print/comma-separated-value-group.js @@ -37,7 +37,7 @@ import { isSCSSControlDirectiveNode, isSubtractionNode, isWordNode, -} from "../utils/index.js"; +} from "../utilities/index.js"; /** * @import AstPath from "../../common/ast-path.js" diff --git a/src/language-css/print/misc.js b/src/language-css/print/misc.js index e6ddc1a1d995..5b90035f93a0 100644 --- a/src/language-css/print/misc.js +++ b/src/language-css/print/misc.js @@ -1,5 +1,5 @@ -import printNumber from "../../utils/print-number.js"; -import printString from "../../utils/print-string.js"; +import printNumber from "../../utilities/print-number.js"; +import printString from "../../utilities/print-string.js"; import CSS_UNITS from "./css-units.evaluate.js"; function printUnit(unit) { diff --git a/src/language-css/print/parenthesized-value-group.js b/src/language-css/print/parenthesized-value-group.js index a520c21d8766..9cf4710efd8d 100644 --- a/src/language-css/print/parenthesized-value-group.js +++ b/src/language-css/print/parenthesized-value-group.js @@ -14,8 +14,8 @@ import { line, softline, } from "../../document/index.js"; -import isNextLineEmpty from "../../utils/is-next-line-empty.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import isNextLineEmpty from "../../utilities/is-next-line-empty.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { locEnd, locStart } from "../loc.js"; import { isConfigurationNode, @@ -24,7 +24,7 @@ import { isSCSSMapItemNode, isURLFunctionNode, isVarFunctionNode, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { shouldPrintTrailingComma } from "./misc.js"; function hasComma({ node, parent }, options) { diff --git a/src/language-css/print/sequence.js b/src/language-css/print/sequence.js index b004a3b2ef8d..97051c7e66a4 100644 --- a/src/language-css/print/sequence.js +++ b/src/language-css/print/sequence.js @@ -1,7 +1,7 @@ import { hardline, line } from "../../document/index.js"; import { isFrontMatter } from "../../main/front-matter/index.js"; -import hasNewline from "../../utils/has-newline.js"; -import isNextLineEmpty from "../../utils/is-next-line-empty.js"; +import hasNewline from "../../utilities/has-newline.js"; +import isNextLineEmpty from "../../utilities/is-next-line-empty.js"; import { locEnd, locStart } from "../loc.js"; function printSequence(path, options, print) { diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index 0c14c8439050..4ee70ae7fa5b 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -10,9 +10,9 @@ import { removeLines, softline, } from "../document/index.js"; -import isNonEmptyArray from "../utils/is-non-empty-array.js"; -import printString from "../utils/print-string.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; +import isNonEmptyArray from "../utilities/is-non-empty-array.js"; +import printString from "../utilities/print-string.js"; +import UnexpectedNodeError from "../utilities/unexpected-node-error.js"; import clean from "./clean.js"; import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; @@ -47,7 +47,7 @@ import { isWideKeywords, lastLineHasInlineComment, maybeToLowerCase, -} from "./utils/index.js"; +} from "./utilities/index.js"; function genericPrint(path, options, print) { const { node } = path; diff --git a/src/language-css/utils/get-function-arguments-text.js b/src/language-css/utilities/get-function-arguments-text.js similarity index 100% rename from src/language-css/utils/get-function-arguments-text.js rename to src/language-css/utilities/get-function-arguments-text.js diff --git a/src/language-css/utils/get-value-root.js b/src/language-css/utilities/get-value-root.js similarity index 100% rename from src/language-css/utils/get-value-root.js rename to src/language-css/utilities/get-value-root.js diff --git a/src/language-css/utils/has-scss-interpolation.js b/src/language-css/utilities/has-scss-interpolation.js similarity index 86% rename from src/language-css/utils/has-scss-interpolation.js rename to src/language-css/utilities/has-scss-interpolation.js index a2e4559594e5..9c4417ae4e50 100644 --- a/src/language-css/utils/has-scss-interpolation.js +++ b/src/language-css/utilities/has-scss-interpolation.js @@ -1,4 +1,4 @@ -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; function hasSCSSInterpolation(groupList) { if (isNonEmptyArray(groupList)) { diff --git a/src/language-css/utils/has-string-or-function.js b/src/language-css/utilities/has-string-or-function.js similarity index 100% rename from src/language-css/utils/has-string-or-function.js rename to src/language-css/utilities/has-string-or-function.js diff --git a/src/language-css/utils/index.js b/src/language-css/utilities/index.js similarity index 100% rename from src/language-css/utils/index.js rename to src/language-css/utilities/index.js diff --git a/src/language-css/utils/is-module-rule-name.js b/src/language-css/utilities/is-module-rule-name.js similarity index 100% rename from src/language-css/utils/is-module-rule-name.js rename to src/language-css/utilities/is-module-rule-name.js diff --git a/src/language-css/utils/is-scss-nested-property-node.js b/src/language-css/utilities/is-scss-nested-property-node.js similarity index 100% rename from src/language-css/utils/is-scss-nested-property-node.js rename to src/language-css/utilities/is-scss-nested-property-node.js diff --git a/src/language-css/utils/is-scss-variable.js b/src/language-css/utilities/is-scss-variable.js similarity index 100% rename from src/language-css/utils/is-scss-variable.js rename to src/language-css/utilities/is-scss-variable.js diff --git a/src/language-css/visitor-keys.js b/src/language-css/visitor-keys.evaluate.js similarity index 89% rename from src/language-css/visitor-keys.js rename to src/language-css/visitor-keys.evaluate.js index 5ba1fe98d8c6..3cfdc9661826 100644 --- a/src/language-css/visitor-keys.js +++ b/src/language-css/visitor-keys.evaluate.js @@ -1,4 +1,6 @@ -const visitorKeys = { +import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js"; + +const visitorKeys = generateReferenceSharedVisitorKeys({ "css-root": ["frontMatter", "nodes"], "css-comment": [], "css-rule": ["selector", "nodes"], @@ -43,6 +45,6 @@ const visitorKeys = { "value-atword": [], "value-unicode-range": [], "value-unknown": [], -}; +}); export default visitorKeys; diff --git a/src/language-graphql/get-visitor-keys.js b/src/language-graphql/get-visitor-keys.js index 0739142ebc81..01f48a9d7700 100644 --- a/src/language-graphql/get-visitor-keys.js +++ b/src/language-graphql/get-visitor-keys.js @@ -1,4 +1,4 @@ -import createGetVisitorKeys from "../utils/create-get-visitor-keys.js"; +import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js"; import visitorKeys from "./visitor-keys.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys, "kind"); diff --git a/src/language-graphql/languages.evaluate.js b/src/language-graphql/languages.evaluate.js index 6f02a96dd0ac..7be21e627a4f 100644 --- a/src/language-graphql/languages.evaluate.js +++ b/src/language-graphql/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; const languages = [ createLanguage(linguistLanguages.GraphQL, () => ({ diff --git a/src/language-graphql/pragma.js b/src/language-graphql/pragma.js index 96e324d3d892..28dcd39da450 100644 --- a/src/language-graphql/pragma.js +++ b/src/language-graphql/pragma.js @@ -2,7 +2,7 @@ import { FORMAT_PRAGMA_TO_INSERT, GRAPHQL_HAS_IGNORE_PRAGMA_REGEXP, GRAPHQL_HAS_PRAGMA_REGEXP, -} from "../utils/pragma/pragma.evaluate.js"; +} from "../utilities/pragma/pragma.evaluate.js"; const hasPragma = (text) => GRAPHQL_HAS_PRAGMA_REGEXP.test(text); const hasIgnorePragma = (text) => GRAPHQL_HAS_IGNORE_PRAGMA_REGEXP.test(text); diff --git a/src/language-graphql/printer-graphql.js b/src/language-graphql/printer-graphql.js index 00f6913c39d4..f87a794f1a95 100644 --- a/src/language-graphql/printer-graphql.js +++ b/src/language-graphql/printer-graphql.js @@ -7,9 +7,9 @@ import { line, softline, } from "../document/index.js"; -import isNextLineEmpty from "../utils/is-next-line-empty.js"; -import isNonEmptyArray from "../utils/is-non-empty-array.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; +import isNextLineEmpty from "../utilities/is-next-line-empty.js"; +import isNonEmptyArray from "../utilities/is-non-empty-array.js"; +import UnexpectedNodeError from "../utilities/unexpected-node-error.js"; import getVisitorKeys from "./get-visitor-keys.js"; import { locEnd, locStart } from "./loc.js"; import { insertPragma } from "./pragma.js"; diff --git a/src/language-handlebars/clean.js b/src/language-handlebars/clean.js index 10300d0a461d..a6b229145b5e 100644 --- a/src/language-handlebars/clean.js +++ b/src/language-handlebars/clean.js @@ -1,4 +1,4 @@ -import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js"; +import htmlWhitespace from "../utilities/html-whitespace.js"; function clean(original, cloned, parent) { // (Glimmer/HTML) ignore TextNode @@ -16,7 +16,7 @@ function clean(original, cloned, parent) { ) { cloned.chars = ""; } else { - cloned.chars = htmlWhitespaceUtils.split(trimmed).join(" "); + cloned.chars = htmlWhitespace.split(trimmed).join(" "); } } diff --git a/src/language-handlebars/get-visitor-keys.js b/src/language-handlebars/get-visitor-keys.js index 50df7ba20f55..558c57e6baa5 100644 --- a/src/language-handlebars/get-visitor-keys.js +++ b/src/language-handlebars/get-visitor-keys.js @@ -1,5 +1,5 @@ -import createGetVisitorKeys from "../utils/create-get-visitor-keys.js"; -import visitorKeys from "./visitor-keys.evaluate.js"; +import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js"; +import visitorKeys from "./visitor-keys.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys); diff --git a/src/language-handlebars/html-void-elements.evaluate.js b/src/language-handlebars/html-void-elements.evaluate.js deleted file mode 100644 index c8116fef9900..000000000000 --- a/src/language-handlebars/html-void-elements.evaluate.js +++ /dev/null @@ -1,5 +0,0 @@ -import { getVoidTags } from "@glimmer/syntax"; - -const htmlVoidElements = new Set(getVoidTags()); - -export default htmlVoidElements; diff --git a/src/language-handlebars/languages.evaluate.js b/src/language-handlebars/languages.evaluate.js index d2183ee806bf..0bfac9ccda45 100644 --- a/src/language-handlebars/languages.evaluate.js +++ b/src/language-handlebars/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; const languages = [ createLanguage(linguistLanguages.Handlebars, () => ({ diff --git a/src/language-handlebars/printer-glimmer.js b/src/language-handlebars/printer-glimmer.js index d673414b62c5..5c2767aa6233 100644 --- a/src/language-handlebars/printer-glimmer.js +++ b/src/language-handlebars/printer-glimmer.js @@ -10,15 +10,19 @@ import { replaceEndOfLine, softline, } from "../document/index.js"; -import getPreferredQuote from "../utils/get-preferred-quote.js"; -import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js"; -import isNonEmptyArray from "../utils/is-non-empty-array.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; +import getPreferredQuote from "../utilities/get-preferred-quote.js"; +import htmlWhitespace from "../utilities/html-whitespace.js"; +import isNonEmptyArray from "../utilities/is-non-empty-array.js"; +import UnexpectedNodeError from "../utilities/unexpected-node-error.js"; import clean from "./clean.js"; import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; import { locEnd, locStart } from "./loc.js"; -import { hasPrettierIgnore, isVoidElement, isWhitespaceNode } from "./utils.js"; +import { + hasPrettierIgnore, + isVoidElement, + isWhitespaceNode, +} from "./utilities.js"; /** @import {Doc} from "../document/index.js" @@ -170,8 +174,8 @@ function print(path, options, print) { if (parent.tag === "style") { text = text.replaceAll(/^\n+/gu, ""); - text = htmlWhitespaceUtils.trimEnd(text); - text = htmlWhitespaceUtils.dedentString(text); + text = htmlWhitespace.trimEnd(text); + text = htmlWhitespace.dedentString(text); return replaceEndOfLine(text, hardline); } @@ -218,7 +222,7 @@ function print(path, options, print) { return replaceEndOfLine(text); } - const isWhitespaceOnly = htmlWhitespaceUtils.isWhitespaceOnly(text); + const isWhitespaceOnly = htmlWhitespace.isWhitespaceOnly(text); const { isFirst, isLast } = path; if (options.htmlWhitespaceSensitivity !== "ignore") { @@ -249,8 +253,7 @@ function print(path, options, print) { return breaks; } - const leadingWhitespace = - htmlWhitespaceUtils.getLeadingWhitespace(text); + const leadingWhitespace = htmlWhitespace.getLeadingWhitespace(text); let leadBreaks = []; if (leadingWhitespace) { @@ -264,8 +267,7 @@ function print(path, options, print) { text = text.slice(leadingWhitespace.length); } - const tailingWhitespace = - htmlWhitespaceUtils.getTrailingWhitespace(text); + const tailingWhitespace = htmlWhitespace.getTrailingWhitespace(text); let trailBreaks = []; if (tailingWhitespace) { if (!shouldTrimTrailingNewlines) { @@ -351,12 +353,12 @@ function print(path, options, print) { trailingSpace = ""; } - if (htmlWhitespaceUtils.hasLeadingWhitespace(text)) { - text = leadingSpace + htmlWhitespaceUtils.trimStart(text); + if (htmlWhitespace.hasLeadingWhitespace(text)) { + text = leadingSpace + htmlWhitespace.trimStart(text); } - if (htmlWhitespaceUtils.hasTrailingWhitespace(text)) { - text = htmlWhitespaceUtils.trimEnd(text) + trailingSpace; + if (htmlWhitespace.hasTrailingWhitespace(text)) { + text = htmlWhitespace.trimEnd(text) + trailingSpace; } return [ @@ -672,7 +674,7 @@ function printInverse(path, options, print) { /* TextNode print helpers */ function getTextValueParts(value) { - return join(line, htmlWhitespaceUtils.split(value)); + return join(line, htmlWhitespace.split(value)); } function getCurrentAttributeName(path) { @@ -712,7 +714,7 @@ function generateHardlines(number = 0) { /* StringLiteral print helpers */ -/** @import {Quote} from "../utils/get-preferred-quote.js" */ +/** @import {Quote} from "../utilities/get-preferred-quote.js" */ /** * Prints a string literal with the correct surrounding quotes based on diff --git a/src/language-handlebars/utils.js b/src/language-handlebars/utilities.js similarity index 90% rename from src/language-handlebars/utils.js rename to src/language-handlebars/utilities.js index 1bd614516d72..fe10c2831afb 100644 --- a/src/language-handlebars/utils.js +++ b/src/language-handlebars/utilities.js @@ -1,4 +1,7 @@ -import htmlVoidElements from "./html-void-elements.evaluate.js"; +// Do not use void tags from other package, won't match +import { getVoidTags } from "@glimmer/syntax"; + +const htmlVoidElements = new Set(getVoidTags()); function isUppercase(string) { return string.toUpperCase() === string; diff --git a/src/language-handlebars/visitor-keys.evaluate.js b/src/language-handlebars/visitor-keys.js similarity index 100% rename from src/language-handlebars/visitor-keys.evaluate.js rename to src/language-handlebars/visitor-keys.js diff --git a/src/language-html/embed.js b/src/language-html/embed.js index 5c9bbb557bee..2a13c90fc65e 100644 --- a/src/language-html/embed.js +++ b/src/language-html/embed.js @@ -5,10 +5,10 @@ import { indent, line, } from "../document/index.js"; -import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js"; +import htmlWhitespace from "../utilities/html-whitespace.js"; import printAngularControlFlowBlockParameters from "./embed/angular-control-flow-block-parameters.js"; import printAttribute from "./embed/attribute.js"; -import { formatAttributeValue } from "./embed/utils.js"; +import { formatAttributeValue } from "./embed/utilities.js"; import getNodeContent from "./get-node-content.js"; import { needsToBorrowPrevClosingTagEndMarker, @@ -22,8 +22,8 @@ import { inferElementParser, isScriptLikeTag, isVueNonHtmlBlock, -} from "./utils/index.js"; -import isVueSfcWithTypescriptScript from "./utils/is-vue-sfc-with-typescript-script.js"; +} from "./utilities/index.js"; +import isVueSfcWithTypescriptScript from "./utilities/is-vue-sfc-with-typescript-script.js"; const embeddedAngularControlFlowBlocks = new Set([ "if", @@ -81,7 +81,7 @@ function embed(path, options) { return async (textToDoc) => { const value = parser === "markdown" - ? htmlWhitespaceUtils.dedentString( + ? htmlWhitespace.dedentString( node.value.replace(/^[^\S\n]*\n/u, ""), ) : node.value; diff --git a/src/language-html/embed/angular-attributes.js b/src/language-html/embed/angular-attributes.js index 602f6a29e96b..7d91f3b7cc4a 100644 --- a/src/language-html/embed/angular-attributes.js +++ b/src/language-html/embed/angular-attributes.js @@ -2,7 +2,7 @@ import { fill } from "../../document/index.js"; import { getTextValueParts, getUnescapedAttributeValue, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { isAngularInterpolation, printAngularInterpolation, @@ -11,7 +11,7 @@ import { formatAttributeValue, printExpand, shouldHugJsExpression, -} from "./utils.js"; +} from "./utilities.js"; /** @import {AttributeValuePrinter} from "./attribute.js" diff --git a/src/language-html/embed/angular-control-flow-block-parameters.js b/src/language-html/embed/angular-control-flow-block-parameters.js index bbdab27ba0fd..cee397078df8 100644 --- a/src/language-html/embed/angular-control-flow-block-parameters.js +++ b/src/language-html/embed/angular-control-flow-block-parameters.js @@ -1,4 +1,4 @@ -import { formatAttributeValue, shouldHugJsExpression } from "./utils.js"; +import { formatAttributeValue, shouldHugJsExpression } from "./utilities.js"; function printAngularControlFlowBlockParameters( textToDoc, diff --git a/src/language-html/embed/angular-interpolation.js b/src/language-html/embed/angular-interpolation.js index 08f9ea4c34b5..188721f7f09e 100644 --- a/src/language-html/embed/angular-interpolation.js +++ b/src/language-html/embed/angular-interpolation.js @@ -1,6 +1,6 @@ import { group, indent, line, replaceEndOfLine } from "../../document/index.js"; -import { getUnescapedAttributeValue } from "../utils/index.js"; -import { formatAttributeValue } from "./utils.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; +import { formatAttributeValue } from "./utilities.js"; const interpolationRegex = /\{\{(.+?)\}\}/su; diff --git a/src/language-html/embed/attribute.js b/src/language-html/embed/attribute.js index 2d89b625a888..a7b56f81eb01 100644 --- a/src/language-html/embed/attribute.js +++ b/src/language-html/embed/attribute.js @@ -1,4 +1,5 @@ import { group, mapDoc } from "../../document/index.js"; +import { shouldUnquoteAttributeValue } from "../utilities/index.js"; import angularAttributePrinters from "./angular-attributes.js"; import { isClassNames, printClassNames } from "./class-names.js"; import { isEventHandler, printEventHandler } from "./event-handler.js"; @@ -43,19 +44,7 @@ function printAttribute(path, options) { return; } - const { valueSpan } = node; - const isQuoted = - valueSpan.end.offset - valueSpan.start.offset === value.length + 2; - - if ( - !isQuoted && - // lit-html: html`` - (/^PRETTIER_HTML_PLACEHOLDER_\d+_\d+_IN_JS$/u.test(value) || - // lwc: html`` - (options.parser === "lwc" && - value.startsWith("{") && - value.endsWith("}"))) - ) { + if (shouldUnquoteAttributeValue(node, options)) { return [node.rawName, "=", value]; } diff --git a/src/language-html/embed/class-names.js b/src/language-html/embed/class-names.js index 0050af13bbab..8cacf92fe18b 100644 --- a/src/language-html/embed/class-names.js +++ b/src/language-html/embed/class-names.js @@ -1,4 +1,4 @@ -import { getUnescapedAttributeValue } from "../utils/index.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; /** @import {AttributeValuePredicate, AttributeValuePrint} from "./attribute.js" diff --git a/src/language-html/embed/event-handler.js b/src/language-html/embed/event-handler.js index 27635ae75c20..48a74293be76 100644 --- a/src/language-html/embed/event-handler.js +++ b/src/language-html/embed/event-handler.js @@ -1,6 +1,6 @@ import htmlEventAttributesArray from "@prettier/html-event-attributes"; -import { getUnescapedAttributeValue } from "../utils/index.js"; -import { formatAttributeValue } from "./utils.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; +import { formatAttributeValue } from "./utilities.js"; /** @import {AttributeValuePredicate, AttributeValuePrint} from "./attribute.js" diff --git a/src/language-html/embed/parse-permissions-policy.js b/src/language-html/embed/parse-permissions-policy.js index 9e38a9573d6e..a4695fa73bee 100644 --- a/src/language-html/embed/parse-permissions-policy.js +++ b/src/language-html/embed/parse-permissions-policy.js @@ -5,7 +5,7 @@ Based on https://github.com/helmetjs/content-security-policy-parser/blob/main/mo 1. Skip ASCII check, so we won't remove invalid directives. 1. Skip directive name normalization, so the printer can know what's the original name. */ -import htmlWhitespaceUtils from "../../utils/html-whitespace-utils.js"; +import htmlWhitespace from "../../utilities/html-whitespace.js"; /** @typedef {{ @@ -28,7 +28,7 @@ function parsePermissionsPolicy(policy) { // U+003B SEMICOLON character (;):" for (let token of policy.split(";")) { // "1. Strip leading and trailing ASCII whitespace from token." - token = htmlWhitespaceUtils.trim(token); + token = htmlWhitespace.trim(token); // "2. If token is an empty string, or if token is not an ASCII string, // continue." @@ -42,7 +42,7 @@ function parsePermissionsPolicy(policy) { // code points from token which are not ASCII whitespace." // "6. Let directive value be the result of splitting token on // ASCII whitespace." - const [name, ...value] = htmlWhitespaceUtils.split(token); + const [name, ...value] = htmlWhitespace.split(token); // "4. Set directive name to be the result of running ASCII lowercase on // directive name." diff --git a/src/language-html/embed/permissions-policy.js b/src/language-html/embed/permissions-policy.js index 5615772728de..a02a1c6239bd 100644 --- a/src/language-html/embed/permissions-policy.js +++ b/src/language-html/embed/permissions-policy.js @@ -1,7 +1,7 @@ import { ifBreak, line } from "../../document/index.js"; -import { getUnescapedAttributeValue } from "../utils/index.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; import parsePermissionsPolicy from "./parse-permissions-policy.js"; -import { printExpand } from "./utils.js"; +import { printExpand } from "./utilities.js"; /** @import {AttributeValuePredicate, AttributeValuePrint} from "./attribute.js" diff --git a/src/language-html/embed/print-vue-script-generic-attribute-value.js b/src/language-html/embed/print-vue-script-generic-attribute-value.js index 986a5b0b96c1..4c65349f92c2 100644 --- a/src/language-html/embed/print-vue-script-generic-attribute-value.js +++ b/src/language-html/embed/print-vue-script-generic-attribute-value.js @@ -1,5 +1,5 @@ -import { getUnescapedAttributeValue } from "../utils/index.js"; -import { formatAttributeValue, shouldHugJsExpression } from "./utils.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; +import { formatAttributeValue, shouldHugJsExpression } from "./utilities.js"; /** * @import {Doc} from "../../document/index.js" diff --git a/src/language-html/embed/srcset.js b/src/language-html/embed/srcset.js index b627e6649aca..0050bc51612a 100644 --- a/src/language-html/embed/srcset.js +++ b/src/language-html/embed/srcset.js @@ -1,7 +1,7 @@ import parseSrcset from "@prettier/parse-srcset"; import { ifBreak, join, line } from "../../document/index.js"; -import { getUnescapedAttributeValue } from "../utils/index.js"; -import { printExpand } from "./utils.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; +import { printExpand } from "./utilities.js"; /** @import {Doc} from "../../document/index.js" diff --git a/src/language-html/embed/style.js b/src/language-html/embed/style.js index d933c5afb2f2..65547f17489d 100644 --- a/src/language-html/embed/style.js +++ b/src/language-html/embed/style.js @@ -1,5 +1,5 @@ -import { getUnescapedAttributeValue } from "../utils/index.js"; -import { printExpand } from "./utils.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; +import { printExpand } from "./utilities.js"; /** @import {AttributeValuePredicate, AttributeValuePrint} from "./attribute.js" diff --git a/src/language-html/embed/utils.js b/src/language-html/embed/utilities.js similarity index 100% rename from src/language-html/embed/utils.js rename to src/language-html/embed/utilities.js diff --git a/src/language-html/embed/vue-attributes.js b/src/language-html/embed/vue-attributes.js index 3ea8db1b3251..c6841c976a49 100644 --- a/src/language-html/embed/vue-attributes.js +++ b/src/language-html/embed/vue-attributes.js @@ -3,10 +3,10 @@ import { isVueScriptTag, isVueSfcBindingsAttribute, isVueSlotAttribute, -} from "../utils/index.js"; -import isVueSfcWithTypescriptScript from "../utils/is-vue-sfc-with-typescript-script.js"; +} from "../utilities/index.js"; +import isVueSfcWithTypescriptScript from "../utilities/is-vue-sfc-with-typescript-script.js"; import { printVueScriptGenericAttributeValue } from "./print-vue-script-generic-attribute-value.js"; -import { formatAttributeValue, shouldHugJsExpression } from "./utils.js"; +import { formatAttributeValue, shouldHugJsExpression } from "./utilities.js"; import { printVueBindings } from "./vue-bindings.js"; import { printVueVForDirective } from "./vue-v-for-directive.js"; diff --git a/src/language-html/embed/vue-bindings.js b/src/language-html/embed/vue-bindings.js index a6ba48d8f01e..6f5ae4016dbc 100644 --- a/src/language-html/embed/vue-bindings.js +++ b/src/language-html/embed/vue-bindings.js @@ -1,6 +1,6 @@ -import { getUnescapedAttributeValue } from "../utils/index.js"; -import isVueSfcWithTypescriptScript from "../utils/is-vue-sfc-with-typescript-script.js"; -import { formatAttributeValue } from "./utils.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; +import isVueSfcWithTypescriptScript from "../utilities/is-vue-sfc-with-typescript-script.js"; +import { formatAttributeValue } from "./utilities.js"; /** * @import {Doc} from "../../document/index.js" diff --git a/src/language-html/embed/vue-v-for-directive.js b/src/language-html/embed/vue-v-for-directive.js index ce908a17e911..929a912e57ef 100644 --- a/src/language-html/embed/vue-v-for-directive.js +++ b/src/language-html/embed/vue-v-for-directive.js @@ -1,7 +1,7 @@ import { group } from "../../document/index.js"; -import { getUnescapedAttributeValue } from "../utils/index.js"; -import isVueSfcWithTypescriptScript from "../utils/is-vue-sfc-with-typescript-script.js"; -import { formatAttributeValue } from "./utils.js"; +import { getUnescapedAttributeValue } from "../utilities/index.js"; +import isVueSfcWithTypescriptScript from "../utilities/is-vue-sfc-with-typescript-script.js"; +import { formatAttributeValue } from "./utilities.js"; /** * @import {Doc} from "../../document/index.js" diff --git a/src/language-html/get-visitor-keys.js b/src/language-html/get-visitor-keys.js index 0739142ebc81..87d21dee0acd 100644 --- a/src/language-html/get-visitor-keys.js +++ b/src/language-html/get-visitor-keys.js @@ -1,5 +1,5 @@ -import createGetVisitorKeys from "../utils/create-get-visitor-keys.js"; -import visitorKeys from "./visitor-keys.js"; +import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js"; +import visitorKeys from "./visitor-keys.evaluate.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys, "kind"); diff --git a/src/language-html/languages.evaluate.js b/src/language-html/languages.evaluate.js index 0f4ef2822061..800abe822e7d 100644 --- a/src/language-html/languages.evaluate.js +++ b/src/language-html/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; const languages = [ createLanguage(linguistLanguages.HTML, () => ({ diff --git a/src/language-html/parse/postprocess.js b/src/language-html/parse/postprocess.js index eae990e2a2e5..2837f0651c11 100644 --- a/src/language-html/parse/postprocess.js +++ b/src/language-html/parse/postprocess.js @@ -4,10 +4,10 @@ import { RecursiveVisitor, visitAll, } from "angular-html-parser"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; -import HTML_ELEMENT_ATTRIBUTES from "../utils/html-elements-attributes.evaluate.js"; -import HTML_TAGS from "../utils/html-tags.evaluate.js"; -import isUnknownNamespace from "../utils/is-unknown-namespace.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; +import HTML_ELEMENT_ATTRIBUTES from "../utilities/html-elements-attributes.evaluate.js"; +import HTML_TAGS from "../utilities/html-tags.evaluate.js"; +import isUnknownNamespace from "../utilities/is-unknown-namespace.js"; import { Node } from "./ast.js"; import { parseIeConditionalComment } from "./conditional-comment.js"; diff --git a/src/language-html/pragma.js b/src/language-html/pragma.js index 4868bd9ddb25..92ae865295af 100644 --- a/src/language-html/pragma.js +++ b/src/language-html/pragma.js @@ -2,7 +2,7 @@ import { FORMAT_PRAGMA_TO_INSERT, HTML_HAS_IGNORE_PRAGMA_REGEXP, HTML_HAS_PRAGMA_REGEXP, -} from "../utils/pragma/pragma.evaluate.js"; +} from "../utilities/pragma/pragma.evaluate.js"; const hasPragma = (text) => HTML_HAS_PRAGMA_REGEXP.test(text); const hasIgnorePragma = (text) => HTML_HAS_IGNORE_PRAGMA_REGEXP.test(text); diff --git a/src/language-html/print-preprocess.js b/src/language-html/print-preprocess.js index bddde3ec5a0d..ec7d5b330b8b 100644 --- a/src/language-html/print-preprocess.js +++ b/src/language-html/print-preprocess.js @@ -1,5 +1,5 @@ import { ParseSourceSpan } from "angular-html-parser"; -import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js"; +import htmlWhitespace from "../utilities/html-whitespace.js"; import { canHaveInterpolation, getLeadingAndTrailingHtmlWhitespace, @@ -9,7 +9,7 @@ import { isLeadingSpaceSensitiveNode, isTrailingSpaceSensitiveNode, isWhitespaceSensitiveNode, -} from "./utils/index.js"; +} from "./utilities/index.js"; const PREPROCESS_PIPELINE = [ removeIgnorableFirstLf, @@ -143,7 +143,7 @@ function mergeSimpleElementIntoText(ast /* , options */) { node.attrs.length === 0 && node.children.length === 1 && node.firstChild.kind === "text" && - !htmlWhitespaceUtils.hasWhitespaceCharacter(node.children[0].value) && + !htmlWhitespace.hasWhitespaceCharacter(node.children[0].value) && !node.firstChild.hasLeadingSpaces && !node.firstChild.hasTrailingSpaces && node.isLeadingSpaceSensitive && @@ -264,7 +264,7 @@ function extractWhitespaces(ast, options) { children.length === 0 || (children.length === 1 && children[0].kind === "text" && - htmlWhitespaceUtils.trim(children[0].value).length === 0) + htmlWhitespace.trim(children[0].value).length === 0) ) { node.hasDanglingSpaces = children.length > 0; node.$children = []; diff --git a/src/language-html/print/angular-control-flow-block.js b/src/language-html/print/angular-control-flow-block.js index 82253bd5088c..e1d0919e0361 100644 --- a/src/language-html/print/angular-control-flow-block.js +++ b/src/language-html/print/angular-control-flow-block.js @@ -6,7 +6,7 @@ import { line, softline, } from "../../document/index.js"; -import { hasPrettierIgnore } from "../utils/index.js"; +import { hasPrettierIgnore } from "../utilities/index.js"; import ANGULAR_CONTROL_FLOW_BLOCK_SETTINGS from "./angular-control-flow-block-settings.evaluate.js"; import { printChildren } from "./children.js"; diff --git a/src/language-html/print/children.js b/src/language-html/print/children.js index bc22a8cbd540..2a98c55212c7 100644 --- a/src/language-html/print/children.js +++ b/src/language-html/print/children.js @@ -7,8 +7,8 @@ import { replaceEndOfLine, softline, } from "../../document/index.js"; -import htmlWhitespaceUtils from "../../utils/html-whitespace-utils.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import htmlWhitespace from "../../utilities/html-whitespace.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { locEnd, locStart } from "../loc.js"; import { forceBreakChildren, @@ -16,7 +16,7 @@ import { hasPrettierIgnore, isTextLikeNode, preferHardlineAsLeadingSpaces, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { needsToBorrowNextOpeningTagStartMarker, needsToBorrowParentClosingTagStartMarker, @@ -51,7 +51,7 @@ function printChild(path, options, print) { return [ printOpeningTagPrefix(child, options), replaceEndOfLine( - htmlWhitespaceUtils.trimEnd( + htmlWhitespace.trimEnd( options.originalText.slice( locStart(child) + (child.prev && needsToBorrowNextOpeningTagStartMarker(child.prev) diff --git a/src/language-html/print/element.js b/src/language-html/print/element.js index dce0d4fdb2a4..3ae5ab6e00ef 100644 --- a/src/language-html/print/element.js +++ b/src/language-html/print/element.js @@ -15,7 +15,7 @@ import { isScriptLikeTag, isVueCustomBlock, shouldPreserveContent, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printChildren } from "./children.js"; import { needsToBorrowLastChildClosingTagEndMarker, diff --git a/src/language-html/print/tag.js b/src/language-html/print/tag.js index 6e8ebd86c9ea..429f1d9e58d3 100644 --- a/src/language-html/print/tag.js +++ b/src/language-html/print/tag.js @@ -11,7 +11,7 @@ import { replaceEndOfLine, softline, } from "../../document/index.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { locEnd, locStart } from "../loc.js"; import { getLastDescendant, @@ -20,7 +20,7 @@ import { isTextLikeNode, isVueSfcBlock, shouldPreserveContent, -} from "../utils/index.js"; +} from "../utilities/index.js"; function printClosingTag(node, options) { return [ diff --git a/src/language-html/printer-html.js b/src/language-html/printer-html.js index 6d51941dbb85..c60e1fc13bee 100644 --- a/src/language-html/printer-html.js +++ b/src/language-html/printer-html.js @@ -10,9 +10,9 @@ import { line, replaceEndOfLine, } from "../document/index.js"; -import getPreferredQuote from "../utils/get-preferred-quote.js"; -import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; +import getPreferredQuote from "../utilities/get-preferred-quote.js"; +import htmlWhitespace from "../utilities/html-whitespace.js"; +import UnexpectedNodeError from "../utilities/unexpected-node-error.js"; import clean from "./clean.js"; import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; @@ -35,7 +35,11 @@ import { printOpeningTagStart, } from "./print/tag.js"; import preprocess from "./print-preprocess.js"; -import { getTextValueParts, unescapeQuoteEntities } from "./utils/index.js"; +import { + getTextValueParts, + shouldUnquoteAttributeValue, + unescapeQuoteEntities, +} from "./utilities/index.js"; function genericPrint(path, options, print) { const { node } = path; @@ -55,7 +59,7 @@ function genericPrint(path, options, print) { case "angularControlFlowBlockParameters": return printAngularControlFlowBlockParameters(path, options, print); case "angularControlFlowBlockParameter": - return htmlWhitespaceUtils.trim(node.expression); + return htmlWhitespace.trim(node.expression); case "angularLetDeclaration": // print like "break-after-operator" layout assignment in estree printer @@ -128,8 +132,12 @@ function genericPrint(path, options, print) { if (node.value === null) { return node.rawName; } + const value = unescapeQuoteEntities(node.value); - const quote = getPreferredQuote(value, '"'); + const quote = shouldUnquoteAttributeValue(node, options) + ? "" + : getPreferredQuote(value, '"'); + return [ node.rawName, "=", diff --git a/src/language-html/utils/html-elements-attributes.evaluate.js b/src/language-html/utilities/html-elements-attributes.evaluate.js similarity index 100% rename from src/language-html/utils/html-elements-attributes.evaluate.js rename to src/language-html/utilities/html-elements-attributes.evaluate.js diff --git a/src/language-html/utils/html-tags.evaluate.js b/src/language-html/utilities/html-tags.evaluate.js similarity index 100% rename from src/language-html/utils/html-tags.evaluate.js rename to src/language-html/utilities/html-tags.evaluate.js diff --git a/src/language-html/utils/index.js b/src/language-html/utilities/index.js similarity index 93% rename from src/language-html/utils/index.js rename to src/language-html/utilities/index.js index f8fc3d15f413..266f2647b89c 100644 --- a/src/language-html/utils/index.js +++ b/src/language-html/utilities/index.js @@ -9,8 +9,8 @@ import { replaceEndOfLine, } from "../../document/index.js"; import { isFrontMatter } from "../../main/front-matter/index.js"; -import htmlWhitespaceUtils from "../../utils/html-whitespace-utils.js"; -import inferParser from "../../utils/infer-parser.js"; +import htmlWhitespace from "../../utilities/html-whitespace.js"; +import inferParser from "../../utilities/infer-parser.js"; import { CSS_DISPLAY_DEFAULT, CSS_DISPLAY_TAGS, @@ -22,14 +22,14 @@ import isUnknownNamespace from "./is-unknown-namespace.js"; const htmlTrimLeadingBlankLines = (string) => string.replaceAll(/^[\t\f\r ]*\n/gu, ""); const htmlTrimPreserveIndentation = (string) => - htmlTrimLeadingBlankLines(htmlWhitespaceUtils.trimEnd(string)); + htmlTrimLeadingBlankLines(htmlWhitespace.trimEnd(string)); const getLeadingAndTrailingHtmlWhitespace = (string) => { let text = string; - const leadingWhitespace = htmlWhitespaceUtils.getLeadingWhitespace(text); + const leadingWhitespace = htmlWhitespace.getLeadingWhitespace(text); if (leadingWhitespace) { text = text.slice(leadingWhitespace.length); } - const trailingWhitespace = htmlWhitespaceUtils.getTrailingWhitespace(text); + const trailingWhitespace = htmlWhitespace.getTrailingWhitespace(text); if (trailingWhitespace) { text = text.slice(0, -trailingWhitespace.length); } @@ -590,16 +590,36 @@ function getTextValueParts(node, value = node.value) { ? node.parent.isIndentationSensitive ? replaceEndOfLine(value) : replaceEndOfLine( - htmlWhitespaceUtils.dedentString(htmlTrimPreserveIndentation(value)), + htmlWhitespace.dedentString(htmlTrimPreserveIndentation(value)), hardline, ) - : join(line, htmlWhitespaceUtils.split(value)); + : join(line, htmlWhitespace.split(value)); } function isVueScriptTag(node, options) { return isVueSfcBlock(node, options) && node.name === "script"; } +function isAttributeValueQuoted(node) { + const { valueSpan, value } = node; + return valueSpan.end.offset - valueSpan.start.offset === value.length + 2; +} + +function shouldUnquoteAttributeValue(node, options) { + if (isAttributeValueQuoted(node)) { + return false; + } + + const { value } = node; + + return ( + // Embedded HTML in JS: `` /* HTML */ `` `` + /^PRETTIER_HTML_PLACEHOLDER_\d+_\d+_IN_JS$/u.test(value) || + // LWC `
` + (options.parser === "lwc" && value.startsWith("{") && value.endsWith("}")) + ); +} + export { canHaveInterpolation, forceBreakChildren, @@ -629,5 +649,6 @@ export { isWhitespaceSensitiveNode, preferHardlineAsLeadingSpaces, shouldPreserveContent, + shouldUnquoteAttributeValue, unescapeQuoteEntities, }; diff --git a/src/language-html/utils/is-unknown-namespace.js b/src/language-html/utilities/is-unknown-namespace.js similarity index 100% rename from src/language-html/utils/is-unknown-namespace.js rename to src/language-html/utilities/is-unknown-namespace.js diff --git a/src/language-html/utils/is-vue-sfc-with-typescript-script.js b/src/language-html/utilities/is-vue-sfc-with-typescript-script.js similarity index 100% rename from src/language-html/utils/is-vue-sfc-with-typescript-script.js rename to src/language-html/utilities/is-vue-sfc-with-typescript-script.js diff --git a/src/language-html/visitor-keys.js b/src/language-html/visitor-keys.evaluate.js similarity index 80% rename from src/language-html/visitor-keys.js rename to src/language-html/visitor-keys.evaluate.js index 534bce879b6a..cf036e2235db 100644 --- a/src/language-html/visitor-keys.js +++ b/src/language-html/visitor-keys.evaluate.js @@ -1,4 +1,6 @@ -const visitorKeys = { +import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js"; + +const visitorKeys = generateReferenceSharedVisitorKeys({ root: ["children"], element: ["attrs", "children"], ieConditionalComment: ["children"], @@ -17,6 +19,6 @@ const visitorKeys = { angularLetDeclarationInitializer: [], angularIcuExpression: ["cases"], angularIcuCase: ["expression"], -}; +}); export default visitorKeys; diff --git a/src/language-js/clean.js b/src/language-js/clean.js index d49d36539bb7..986bcbe4786b 100644 --- a/src/language-js/clean.js +++ b/src/language-js/clean.js @@ -3,7 +3,7 @@ import { isMeaningfulEmptyStatement, isNumericLiteral, isStringLiteral, -} from "./utils/index.js"; +} from "./utilities/index.js"; const ignoredProperties = new Set([ "range", diff --git a/src/language-js/comments/printer-methods.js b/src/language-js/comments/can-attach-comment.js similarity index 66% rename from src/language-js/comments/printer-methods.js rename to src/language-js/comments/can-attach-comment.js index 952e39cc593a..ef7bfe821465 100644 --- a/src/language-js/comments/printer-methods.js +++ b/src/language-js/comments/can-attach-comment.js @@ -1,14 +1,11 @@ -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { createTypeCheckFunction, getFunctionParameters, - hasNodeIgnoreComment, - isJsxElement, isMeaningfulEmptyStatement, isMethod, isTsAsConstExpression, - isUnionType, -} from "../utils/index.js"; +} from "../utilities/index.js"; /** * @import {Node} from "../types/estree.js" @@ -148,56 +145,4 @@ function canAttachComment(node, ancestors) { return true; } -const isClassOrInterface = createTypeCheckFunction([ - "ClassDeclaration", - "ClassExpression", - "DeclareClass", - "DeclareInterface", - "InterfaceDeclaration", - "TSInterfaceDeclaration", - // Can't have `id` or `typeParameters` - // "InterfaceTypeAnnotation", -]); - -/** - * @param {AstPath} path - * @returns {boolean} - */ -function willPrintOwnComments(path) { - const { key, parent } = path; - if ( - (key === "types" && isUnionType(parent)) || - (key === "argument" && parent.type === "JSXSpreadAttribute") || - (key === "expression" && parent.type === "JSXSpreadChild") || - (key === "superClass" && - (parent.type === "ClassDeclaration" || - parent.type === "ClassExpression")) || - ((key === "id" || key === "typeParameters") && - isClassOrInterface(parent)) || - // Not tested, don't know how to - (key === "patterns" && parent.type === "MatchOrPattern") - ) { - return true; - } - - const { node } = path; - if (hasNodeIgnoreComment(node)) { - return false; - } - - return isJsxElement(node) || isUnionType(node); -} - -function isGap(text, { parser }) { - if (parser === "flow" || parser === "hermes" || parser === "babel-flow") { - // Example: (a /* b */ /* : c */) - // gap ^^^^ - text = text.replaceAll(/[\s(]/gu, ""); - return text === "" || text === "/*" || text === "/*::"; - } -} - -export { printComment } from "../print/comment.js"; -export { default as isBlockComment } from "../utils/is-block-comment.js"; -export { default as handleComments } from "./handle-comments.js"; -export { canAttachComment, isGap, willPrintOwnComments }; +export default canAttachComment; diff --git a/src/language-js/comments/handle-comments.js b/src/language-js/comments/handle-comments.js index e03ec7ef156f..ee62a416f288 100644 --- a/src/language-js/comments/handle-comments.js +++ b/src/language-js/comments/handle-comments.js @@ -2,12 +2,12 @@ import { addDanglingComment, addLeadingComment, addTrailingComment, -} from "../../main/comments/utils.js"; -import getNextNonSpaceNonCommentCharacter from "../../utils/get-next-non-space-non-comment-character.js"; -import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js"; -import hasNewline from "../../utils/has-newline.js"; -import hasNewlineInRange from "../../utils/has-newline-in-range.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +} from "../../main/comments/utilities.js"; +import getNextNonSpaceNonCommentCharacter from "../../utilities/get-next-non-space-non-comment-character.js"; +import getNextNonSpaceNonCommentCharacterIndex from "../../utilities/get-next-non-space-non-comment-character-index.js"; +import hasNewline from "../../utilities/has-newline.js"; +import hasNewlineInRange from "../../utilities/has-newline-in-range.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { locEnd, locStart } from "../loc.js"; import { createTypeCheckFunction, @@ -22,10 +22,10 @@ import { isObjectProperty, isPrettierIgnoreComment, isUnionType, -} from "../utils/index.js"; -import isBlockComment from "../utils/is-block-comment.js"; -import isLineComment from "../utils/is-line-comment.js"; -import isTypeCastComment from "../utils/is-type-cast-comment.js"; +} from "../utilities/index.js"; +import isBlockComment from "../utilities/is-block-comment.js"; +import isLineComment from "../utilities/is-line-comment.js"; +import isTypeCastComment from "../utilities/is-type-cast-comment.js"; /** @import * as Estree from "../types/estree.js" */ diff --git a/src/language-js/comments/is-gap.js b/src/language-js/comments/is-gap.js new file mode 100644 index 000000000000..d49562176ff3 --- /dev/null +++ b/src/language-js/comments/is-gap.js @@ -0,0 +1,10 @@ +function isGap(text, { parser }) { + if (parser === "flow" || parser === "hermes" || parser === "babel-flow") { + // Example: (a /* b */ /* : c */) + // gap ^^^^ + text = text.replaceAll(/[\s(]/gu, ""); + return text === "" || text === "/*" || text === "/*::"; + } +} + +export default isGap; diff --git a/src/language-js/comments/will-print-own-comments.js b/src/language-js/comments/will-print-own-comments.js new file mode 100644 index 000000000000..962694f1efab --- /dev/null +++ b/src/language-js/comments/will-print-own-comments.js @@ -0,0 +1,62 @@ +import { + createTypeCheckFunction, + hasNodeIgnoreComment, + isJsxElement, + isUnionType, + shouldUnionTypePrintOwnComments, +} from "../utilities/index.js"; + +/** + * @import {Node} from "../types/estree.js" + * @import AstPath from "../../common/ast-path.js" + */ + +const isClassOrInterface = createTypeCheckFunction([ + "ClassDeclaration", + "ClassExpression", + "DeclareClass", + "DeclareInterface", + "InterfaceDeclaration", + "TSInterfaceDeclaration", + // Can't have `id` or `typeParameters` + // "InterfaceTypeAnnotation", +]); + +/** + * @param {AstPath} path + * @returns {boolean} + */ +function willPrintOwnComments(path) { + const { key, parent } = path; + if ( + (key === "types" && isUnionType(parent)) || + (key === "argument" && parent.type === "JSXSpreadAttribute") || + (key === "expression" && parent.type === "JSXSpreadChild") || + (key === "superClass" && + (parent.type === "ClassDeclaration" || + parent.type === "ClassExpression")) || + ((key === "id" || key === "typeParameters") && + isClassOrInterface(parent)) || + // Not tested, don't know how to + (key === "patterns" && parent.type === "MatchOrPattern") + ) { + return true; + } + + const { node } = path; + if (hasNodeIgnoreComment(node)) { + return false; + } + + if (isUnionType(node)) { + return shouldUnionTypePrintOwnComments(path); + } + + if (isJsxElement(node)) { + return true; + } + + return false; +} + +export default willPrintOwnComments; diff --git a/src/language-js/embed/css.js b/src/language-js/embed/css.js index 393862ed83cb..f7a76de3d4c8 100644 --- a/src/language-js/embed/css.js +++ b/src/language-js/embed/css.js @@ -6,10 +6,10 @@ import { replaceEndOfLine, softline, } from "../../document/index.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { printTemplateExpressions } from "../print/template-literal.js"; -import isNodeMatches from "../utils/is-node-matches.js"; -import { isAngularComponentStyles } from "./utils.js"; +import isNodeMatches from "../utilities/is-node-matches.js"; +import { isAngularComponentStyles } from "./utilities.js"; async function printEmbedCss(textToDoc, print, path /* , options*/) { const { node } = path; diff --git a/src/language-js/embed/graphql.js b/src/language-js/embed/graphql.js index 97b35102f70b..74fe61013f3b 100644 --- a/src/language-js/embed/graphql.js +++ b/src/language-js/embed/graphql.js @@ -3,7 +3,7 @@ import { escapeTemplateCharacters, printTemplateExpressions, } from "../print/template-literal.js"; -import { hasLanguageComment } from "./utils.js"; +import { hasLanguageComment } from "./utilities.js"; async function printEmbedGraphQL(textToDoc, print, path /* , options*/) { const { node } = path; diff --git a/src/language-js/embed/html.js b/src/language-js/embed/html.js index 68476d354ae3..8d04d30f99e8 100644 --- a/src/language-js/embed/html.js +++ b/src/language-js/embed/html.js @@ -10,7 +10,7 @@ import { printTemplateExpressions, uncookTemplateElementValue, } from "../print/template-literal.js"; -import { hasLanguageComment, isAngularComponentTemplate } from "./utils.js"; +import { hasLanguageComment, isAngularComponentTemplate } from "./utilities.js"; // The counter is needed to distinguish nested embeds. let htmlTemplateLiteralCounter = 0; diff --git a/src/language-js/embed/utils.js b/src/language-js/embed/utilities.js similarity index 98% rename from src/language-js/embed/utils.js rename to src/language-js/embed/utilities.js index 9737a34c7aec..86ff723ac22c 100644 --- a/src/language-js/embed/utils.js +++ b/src/language-js/embed/utilities.js @@ -3,7 +3,7 @@ import { hasComment, isArrayExpression, isObjectProperty, -} from "../utils/index.js"; +} from "../utilities/index.js"; const angularComponentObjectExpressionPredicates = [ (node, name) => node.type === "ObjectExpression" && name === "properties", diff --git a/src/language-js/languages.evaluate.js b/src/language-js/languages.evaluate.js index 9e6b43d1eaa3..d618250e70b4 100644 --- a/src/language-js/languages.evaluate.js +++ b/src/language-js/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; const languages = [ createLanguage(linguistLanguages.JavaScript, (data) => ({ diff --git a/src/language-js/needs-parens.js b/src/language-js/parentheses/needs-parentheses.js similarity index 98% rename from src/language-js/needs-parens.js rename to src/language-js/parentheses/needs-parentheses.js index 200fd28f3b8c..9de0107c4d3c 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/parentheses/needs-parentheses.js @@ -1,4 +1,4 @@ -import isNonEmptyArray from "../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { createTypeCheckFunction, getFunctionParameters, @@ -20,17 +20,17 @@ import { isUnionType, shouldFlatten, startsWithNoLookaheadToken, -} from "./utils/index.js"; +} from "../utilities/index.js"; /** - * @import AstPath from "../common/ast-path.js" + * @import AstPath from "../../common/ast-path.js" */ /** * @param {AstPath} path * @returns {boolean} */ -function needsParens(path, options) { +function needsParentheses(path, options) { if (path.isRoot) { return false; } @@ -560,11 +560,7 @@ function needsParens(path, options) { // fallthrough case "TSUnionType": case "TSIntersectionType": - if ( - (isUnionType(parent) || isIntersectionType(parent)) && - parent.types.length > 1 && - (!node.types || node.types.length > 1) - ) { + if (isUnionType(parent) || isIntersectionType(parent)) { return true; } // fallthrough @@ -1145,13 +1141,14 @@ function shouldWrapFunctionForExportDefault(path, options) { // in some cases the function is already wrapped // (e.g. `export default (function() {})();`) // in this case we don't need to add extra parens - !needsParens(path, options) + !needsParentheses(path, options) ); } if ( !hasNakedLeftSide(node) || - (parent.type !== "ExportDefaultDeclaration" && needsParens(path, options)) + (parent.type !== "ExportDefaultDeclaration" && + needsParentheses(path, options)) ) { return false; } @@ -1346,4 +1343,4 @@ function canDecoratorExpressionUnparenthesized(node) { ); } -export default needsParens; +export default needsParentheses; diff --git a/src/language-js/parse/acorn.js b/src/language-js/parse/acorn.js index 98cb314e5ef3..539b90233f41 100644 --- a/src/language-js/parse/acorn.js +++ b/src/language-js/parse/acorn.js @@ -1,16 +1,16 @@ import { Parser as AcornParser } from "acorn"; import acornJsx from "acorn-jsx"; import createError from "../../common/parser-create-error.js"; -import { tryCombinationsSync } from "../../utils/try-combinations.js"; +import { tryCombinationsSync } from "../../utilities/try-combinations.js"; import postprocess from "./postprocess/index.js"; -import createParser from "./utils/create-parser.js"; +import createParser from "./utilities/create-parser.js"; import { getSourceType, SOURCE_TYPE_COMBINATIONS, SOURCE_TYPE_COMMONJS, SOURCE_TYPE_MODULE, SOURCE_TYPE_SCRIPT, -} from "./utils/source-types.js"; +} from "./utilities/source-types.js"; /** @import {Options} from "acorn"; diff --git a/src/language-js/parse/babel.js b/src/language-js/parse/babel.js index f8957df1de06..ab19cb6a2560 100644 --- a/src/language-js/parse/babel.js +++ b/src/language-js/parse/babel.js @@ -1,16 +1,16 @@ import { parse as babelParse, parseExpression } from "@babel/parser"; -import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js"; -import { tryCombinationsSync } from "../../utils/try-combinations.js"; -import getShebang from "../utils/get-shebang.js"; +import getNextNonSpaceNonCommentCharacterIndex from "../../utilities/get-next-non-space-non-comment-character-index.js"; +import { tryCombinationsSync } from "../../utilities/try-combinations.js"; +import getShebang from "../utilities/get-shebang.js"; import postprocess from "./postprocess/index.js"; -import createBabelParseError from "./utils/create-babel-parse-error.js"; -import createParser from "./utils/create-parser.js"; +import createBabelParseError from "./utilities/create-babel-parse-error.js"; +import createParser from "./utilities/create-parser.js"; import { getSourceType, SOURCE_TYPE_COMMONJS, SOURCE_TYPE_MODULE, -} from "./utils/source-types.js"; -import wrapBabelExpression from "./utils/wrap-babel-expression.js"; +} from "./utilities/source-types.js"; +import wrapBabelExpression from "./utilities/wrap-babel-expression.js"; const createBabelParser = (options) => createParser(createParse(options)); diff --git a/src/language-js/parse/espree.js b/src/language-js/parse/espree.js index 5369d111efbc..79d04f028e67 100644 --- a/src/language-js/parse/espree.js +++ b/src/language-js/parse/espree.js @@ -1,12 +1,12 @@ import { parse as espreeParse } from "espree"; import createError from "../../common/parser-create-error.js"; -import { tryCombinationsSync } from "../../utils/try-combinations.js"; +import { tryCombinationsSync } from "../../utilities/try-combinations.js"; import postprocess from "./postprocess/index.js"; -import createParser from "./utils/create-parser.js"; +import createParser from "./utilities/create-parser.js"; import { getSourceType, SOURCE_TYPE_COMBINATIONS, -} from "./utils/source-types.js"; +} from "./utilities/source-types.js"; /** @import {Options} from "espree" */ diff --git a/src/language-js/parse/flow.js b/src/language-js/parse/flow.js index 578e4402f522..c6d0f97b966f 100644 --- a/src/language-js/parse/flow.js +++ b/src/language-js/parse/flow.js @@ -1,7 +1,7 @@ import flowParser from "flow-parser"; import createError from "../../common/parser-create-error.js"; import postprocess from "./postprocess/index.js"; -import createParser from "./utils/create-parser.js"; +import createParser from "./utilities/create-parser.js"; // https://github.com/facebook/flow/tree/main/packages/flow-parser#options // Keep this sync with `/scripts/sync-flow-test.js` diff --git a/src/language-js/parse/hermes.js b/src/language-js/parse/hermes.js index 0dd3d34a75da..cc81c9047955 100644 --- a/src/language-js/parse/hermes.js +++ b/src/language-js/parse/hermes.js @@ -1,7 +1,7 @@ import { parse as hermesParse } from "hermes-parser"; import createError from "../../common/parser-create-error.js"; import postprocess from "./postprocess/index.js"; -import createParser from "./utils/create-parser.js"; +import createParser from "./utilities/create-parser.js"; function createParseError(error) { let { message, loc } = error; diff --git a/src/language-js/parse/meriyah.js b/src/language-js/parse/meriyah.js index d4c51853b291..75f6455a2aeb 100644 --- a/src/language-js/parse/meriyah.js +++ b/src/language-js/parse/meriyah.js @@ -1,16 +1,16 @@ import { parse as meriyahParse } from "meriyah"; import createError from "../../common/parser-create-error.js"; -import { tryCombinationsSync } from "../../utils/try-combinations.js"; +import { tryCombinationsSync } from "../../utilities/try-combinations.js"; import postprocess from "./postprocess/index.js"; -import createParser from "./utils/create-parser.js"; +import createParser from "./utilities/create-parser.js"; import { getSourceType, SOURCE_TYPE_COMBINATIONS, -} from "./utils/source-types.js"; +} from "./utilities/source-types.js"; /** @import {ESTree as MeriyahESTree} from "meriyah"; -@import {SOURCE_TYPE_MODULE, SOURCE_TYPE_COMMONJS} from "./utils/source-types.js"; +@import {SOURCE_TYPE_MODULE, SOURCE_TYPE_COMMONJS} from "./utilities/source-types.js"; */ // https://github.com/meriyah/meriyah/blob/4676f60b6c149d7082bde2c9147f9ae2359c8075/src/parser.ts#L185 diff --git a/src/language-js/parse/oxc.js b/src/language-js/parse/oxc.js index 1e7e5d7351c0..b6093d505803 100644 --- a/src/language-js/parse/oxc.js +++ b/src/language-js/parse/oxc.js @@ -1,15 +1,15 @@ import indexToPosition from "index-to-position"; import { parse as oxcParse } from "oxc-parser"; import createError from "../../common/parser-create-error.js"; -import { tryCombinations } from "../../utils/try-combinations.js"; +import { tryCombinations } from "../../utilities/try-combinations.js"; import postprocess from "./postprocess/index.js"; -import createParser from "./utils/create-parser.js"; -import jsxRegexp from "./utils/jsx-regexp.evaluate.js"; +import createParser from "./utilities/create-parser.js"; +import jsxRegexp from "./utilities/jsx-regexp.evaluate.js"; import { getSourceType, SOURCE_TYPE_COMMONJS, SOURCE_TYPE_SCRIPT, -} from "./utils/source-types.js"; +} from "./utilities/source-types.js"; /** @import {ParseResult, ParserOptions as ParserOptionsWithoutExperimentalRawTransfer} from "oxc-parser" */ /** @typedef {Omit & { diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js index f4c2a6fbed49..b916258522e1 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -1,10 +1,10 @@ import * as assert from "#universal/assert"; import { locEnd, locStart } from "../../loc.js"; -import createTypeCheckFunction from "../../utils/create-type-check-function.js"; -import getRaw from "../../utils/get-raw.js"; -import isBlockComment from "../../utils/is-block-comment.js"; -import isLineComment from "../../utils/is-line-comment.js"; -import isTypeCastComment from "../../utils/is-type-cast-comment.js"; +import createTypeCheckFunction from "../../utilities/create-type-check-function.js"; +import getRaw from "../../utilities/get-raw.js"; +import isBlockComment from "../../utilities/is-block-comment.js"; +import isLineComment from "../../utilities/is-line-comment.js"; +import isTypeCastComment from "../../utilities/is-type-cast-comment.js"; import mergeNestledJsdocComments from "./merge-nestled-jsdoc-comments.js"; import visitNode from "./visit-node.js"; @@ -43,10 +43,31 @@ function postprocess(ast, options) { mergeNestledJsdocComments(comments); + // `InterpreterDirective` from babel parser and flow parser + // Other parsers parse it as comment, babel treat it as comment too + // https://github.com/babel/babel/issues/15116 + const program = ast.type === "File" ? ast.program : ast; + if (program.interpreter) { + comments.unshift(program.interpreter); + delete program.interpreter; + } + + if (isOxcTs && ast.hashbang) { + comments.unshift(ast.hashbang); + delete ast.hashbang; + } + + // In `typescript` and `flow`, `Program` doesn't count whitespace and comments + // See https://github.com/typescript-eslint/typescript-eslint/issues/11026 + // See https://github.com/facebook/flow/issues/8537 + if (ast.type === "Program") { + ast.range = [0, text.length]; + } + let typeCastCommentsEnds; ast = visitNode(ast, { - onLeave(node) { + onEnter(node) { switch (node.type) { case "ParenthesizedExpression": { const { expression } = node; @@ -58,7 +79,7 @@ function postprocess(ast, options) { return expression; } - let keepTypeCast = false; + let shouldKeepParenthesizedExpression = false; if (!isOxcTs) { if (!typeCastCommentsEnds) { typeCastCommentsEnds = []; @@ -74,27 +95,21 @@ function postprocess(ast, options) { const previousCommentEnd = typeCastCommentsEnds.findLast( (end) => end <= start, ); - keepTypeCast = + shouldKeepParenthesizedExpression = previousCommentEnd && // check that there are only white spaces between the comment and the parenthesis text.slice(previousCommentEnd, start).trim().length === 0; } - if (!keepTypeCast) { - expression.extra = { ...expression.extra, parenthesized: true }; - return expression; + if (shouldKeepParenthesizedExpression) { + return; } - break; - } - case "LogicalExpression": - // We remove unneeded parens around same-operator LogicalExpressions - if (isUnbalancedLogicalTree(node)) { - return rebalanceLogicalTree(node); - } - break; + expression.extra = { ...expression.extra, parenthesized: true }; + return expression; + } - // This happens when use `oxc-parser` to parse `` `${foo satisfies bar}`; `` + // This happened when use `oxc-parser` to parse `` `${foo satisfies bar}`; `` // https://github.com/oxc-project/oxc/issues/11313 case "TemplateLiteral": /* c8 ignore next 3 */ @@ -128,6 +143,7 @@ function postprocess(ast, options) { } break; } + // remove redundant TypeScript nodes case "TSParenthesizedType": return node.typeAnnotation; @@ -135,9 +151,7 @@ function postprocess(ast, options) { // For hack-style pipeline case "TopicReference": ast.extra = { ...ast.extra, __isUsingHackPipeline: true }; - break; - - // In Flow parser, it doesn't generate union/intersection types for single type + break; // In Flow parser, it doesn't generate union/intersection types for single type case "TSUnionType": case "TSIntersectionType": if (node.types.length === 1) { @@ -151,9 +165,21 @@ function postprocess(ast, options) { node.options = node.attributes; } break; + } + }, + onLeave(node) { + switch (node.type) { + // Children can be parenthesized, need do this in `onLeave` + case "LogicalExpression": + // We remove unneeded parens around same-operator LogicalExpressions + if (isUnbalancedLogicalTree(node)) { + return rebalanceLogicalTree(node); + } + break; // https://github.com/babel/babel/issues/17506 // https://github.com/oxc-project/oxc/issues/16074 + // It's possible to have parenthesized `argument`, need do this in `onLeave` case "TSImportType": if (!node.source && node.argument.type === "TSLiteralType") { node.source = node.argument.literal; @@ -169,31 +195,10 @@ function postprocess(ast, options) { }, }); - // `InterpreterDirective` from babel parser and flow parser - // Other parsers parse it as comment, babel treat it as comment too - // https://github.com/babel/babel/issues/15116 - const program = ast.type === "File" ? ast.program : ast; - if (program.interpreter) { - comments.unshift(program.interpreter); - delete program.interpreter; - } - - if (isOxcTs && ast.hashbang) { - comments.unshift(ast.hashbang); - delete ast.hashbang; - } - /* c8 ignore next 3 */ if (process.env.NODE_ENV !== "production") { assertComments(comments, text); } - - // In `typescript` and `flow`, `Program` doesn't count whitespace and comments - // See https://github.com/typescript-eslint/typescript-eslint/issues/11026 - // See https://github.com/facebook/flow/issues/8537 - if (ast.type === "Program") { - ast.range = [0, text.length]; - } return ast; } diff --git a/src/language-js/parse/postprocess/merge-nestled-jsdoc-comments.js b/src/language-js/parse/postprocess/merge-nestled-jsdoc-comments.js index 549e7513ceb1..53cf3108fe31 100644 --- a/src/language-js/parse/postprocess/merge-nestled-jsdoc-comments.js +++ b/src/language-js/parse/postprocess/merge-nestled-jsdoc-comments.js @@ -1,7 +1,7 @@ import { locEnd, locStart } from "../../loc.js"; -import isBlockComment from "../../utils/is-block-comment.js"; -import isIndentableBlockComment from "../../utils/is-indentable-block-comment.js"; -import isLineComment from "../../utils/is-line-comment.js"; +import isBlockComment from "../../utilities/is-block-comment.js"; +import isIndentableBlockComment from "../../utilities/is-indentable-block-comment.js"; +import isLineComment from "../../utilities/is-line-comment.js"; function mergeNestledJsdocComments(comments) { if (comments.length < 2) { diff --git a/src/language-js/parse/postprocess/visit-node.js b/src/language-js/parse/postprocess/visit-node.js index ed550df1c50e..5672c8a2cdd9 100644 --- a/src/language-js/parse/postprocess/visit-node.js +++ b/src/language-js/parse/postprocess/visit-node.js @@ -1,7 +1,8 @@ +import isObject from "../../../utilities/is-object.js"; import getVisitorKeys from "../../traverse/get-visitor-keys.js"; function visitNode(node, options) { - if (!(node !== null && typeof node === "object")) { + if (!isObject(node)) { return node; } @@ -16,7 +17,13 @@ function visitNode(node, options) { } if (options.onEnter) { - node = options.onEnter(node) || node; + const result = options.onEnter(node) ?? node; + // If node is replaced, re-enter + if (result !== node) { + return visitNode(result, options); + } + + node = result; } const keys = getVisitorKeys(node); @@ -24,7 +31,11 @@ function visitNode(node, options) { node[keys[i]] = visitNode(node[keys[i]], options); } - return options.onLeave(node) || node; + if (options.onLeave) { + node = options.onLeave(node) || node; + } + + return node; } export default visitNode; diff --git a/src/language-js/parse/typescript.js b/src/language-js/parse/typescript.js index a2c6cbab78c5..87c1567b51fc 100644 --- a/src/language-js/parse/typescript.js +++ b/src/language-js/parse/typescript.js @@ -1,14 +1,14 @@ import { parse as parseTypeScript } from "@typescript-eslint/typescript-estree"; import createError from "../../common/parser-create-error.js"; -import { tryCombinationsSync } from "../../utils/try-combinations.js"; +import { tryCombinationsSync } from "../../utilities/try-combinations.js"; import postprocess from "./postprocess/index.js"; -import createParser from "./utils/create-parser.js"; -import jsxRegexp from "./utils/jsx-regexp.evaluate.js"; -import replaceHashbang from "./utils/replace-hashbang.js"; +import createParser from "./utilities/create-parser.js"; +import jsxRegexp from "./utilities/jsx-regexp.evaluate.js"; +import replaceHashbang from "./utilities/replace-hashbang.js"; import { getSourceType, SOURCE_TYPE_COMBINATIONS, -} from "./utils/source-types.js"; +} from "./utilities/source-types.js"; /** @import {TSESTreeOptions} from "@typescript-eslint/typescript-estree" */ diff --git a/src/language-js/parse/utils/create-babel-parse-error.js b/src/language-js/parse/utilities/create-babel-parse-error.js similarity index 100% rename from src/language-js/parse/utils/create-babel-parse-error.js rename to src/language-js/parse/utilities/create-babel-parse-error.js diff --git a/src/language-js/parse/utils/create-parser.js b/src/language-js/parse/utilities/create-parser.js similarity index 100% rename from src/language-js/parse/utils/create-parser.js rename to src/language-js/parse/utilities/create-parser.js diff --git a/src/language-js/parse/utils/jsx-regexp.evaluate.js b/src/language-js/parse/utilities/jsx-regexp.evaluate.js similarity index 100% rename from src/language-js/parse/utils/jsx-regexp.evaluate.js rename to src/language-js/parse/utilities/jsx-regexp.evaluate.js diff --git a/src/language-js/parse/utils/replace-hashbang.js b/src/language-js/parse/utilities/replace-hashbang.js similarity index 100% rename from src/language-js/parse/utils/replace-hashbang.js rename to src/language-js/parse/utilities/replace-hashbang.js diff --git a/src/language-js/parse/utils/source-types.js b/src/language-js/parse/utilities/source-types.js similarity index 100% rename from src/language-js/parse/utils/source-types.js rename to src/language-js/parse/utilities/source-types.js diff --git a/src/language-js/parse/utils/wrap-babel-expression.js b/src/language-js/parse/utilities/wrap-babel-expression.js similarity index 100% rename from src/language-js/parse/utils/wrap-babel-expression.js rename to src/language-js/parse/utilities/wrap-babel-expression.js diff --git a/src/language-js/pragma.js b/src/language-js/pragma.js index 3f7ad568d811..1bef7e05063b 100644 --- a/src/language-js/pragma.js +++ b/src/language-js/pragma.js @@ -4,8 +4,8 @@ import { FORMAT_IGNORE_PRAGMAS, FORMAT_PRAGMA_TO_INSERT, FORMAT_PRAGMAS, -} from "../utils/pragma/pragma.evaluate.js"; -import getShebang from "./utils/get-shebang.js"; +} from "../utilities/pragma/pragma.evaluate.js"; +import getShebang from "./utilities/get-shebang.js"; function parseDocBlock(text) { const shebang = getShebang(text); diff --git a/src/language-js/print/angular.js b/src/language-js/print/angular.js index f208d0593e41..30285d46632a 100644 --- a/src/language-js/print/angular.js +++ b/src/language-js/print/angular.js @@ -1,6 +1,6 @@ import { group, join, line } from "../../document/index.js"; -import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; -import { createTypeCheckFunction, hasNode } from "../utils/index.js"; +import UnexpectedNodeError from "../../utilities/unexpected-node-error.js"; +import { createTypeCheckFunction, hasNode } from "../utilities/index.js"; import { printBinaryishExpression } from "./binaryish.js"; /** @import AstPath from "../../common/ast-path.js" */ diff --git a/src/language-js/print/array-type.js b/src/language-js/print/array-type.js new file mode 100644 index 000000000000..7f07f4b2fcc6 --- /dev/null +++ b/src/language-js/print/array-type.js @@ -0,0 +1,9 @@ +/* +- `TSArrayType` +- `ArrayTypeAnnotation` +*/ +function printArrayType(print) { + return [print("elementType"), "[]"]; +} + +export { printArrayType }; diff --git a/src/language-js/print/array.js b/src/language-js/print/array.js index 130782c52a8c..9da195471a2e 100644 --- a/src/language-js/print/array.js +++ b/src/language-js/print/array.js @@ -8,10 +8,10 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewline from "../../utils/has-newline.js"; -import isNextLineEmptyAfterIndex from "../../utils/is-next-line-empty.js"; -import skipInlineComment from "../../utils/skip-inline-comment.js"; -import skipTrailingComment from "../../utils/skip-trailing-comment.js"; +import hasNewline from "../../utilities/has-newline.js"; +import isNextLineEmptyAfterIndex from "../../utilities/is-next-line-empty.js"; +import skipInlineComment from "../../utilities/skip-inline-comment.js"; +import skipTrailingComment from "../../utilities/skip-trailing-comment.js"; import { locEnd, locStart } from "../loc.js"; import { CommentCheckFlags, @@ -21,7 +21,7 @@ import { isObjectExpression, isSignedNumericLiteral, shouldPrintComma, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printOptionalToken } from "./misc.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; diff --git a/src/language-js/print/arrow-function.js b/src/language-js/print/arrow-function.js index 89ad77fab61c..6eeccfb01e0a 100644 --- a/src/language-js/print/arrow-function.js +++ b/src/language-js/print/arrow-function.js @@ -14,7 +14,7 @@ import { printCommentsSeparately, printDanglingComments, } from "../../main/comments/print.js"; -import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js"; +import getNextNonSpaceNonCommentCharacterIndex from "../../utilities/get-next-non-space-non-comment-character-index.js"; import { locEnd } from "../loc.js"; import { CommentCheckFlags, @@ -29,7 +29,7 @@ import { isTemplateOnItsOwnLine, shouldPrintComma, startsWithNoLookaheadToken, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printReturnType, shouldPrintParamsWithoutParens } from "./function.js"; import { printFunctionParameters } from "./function-parameters.js"; diff --git a/src/language-js/print/assignment.js b/src/language-js/print/assignment.js index 67159c106038..93839723326e 100644 --- a/src/language-js/print/assignment.js +++ b/src/language-js/print/assignment.js @@ -8,8 +8,8 @@ import { lineSuffixBoundary, willBreak, } from "../../document/index.js"; -import getStringWidth from "../../utils/get-string-width.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import getStringWidth from "../../utilities/get-string-width.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { getCallArguments, hasLeadingOwnLineComment, @@ -24,7 +24,7 @@ import { isStringLiteral, isTypeAlias, isUnionType, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { shouldInlineLogicalExpression } from "./binaryish.js"; import { printCallExpression } from "./call-expression.js"; diff --git a/src/language-js/print/binaryish.js b/src/language-js/print/binaryish.js index 0c03109479fe..33d6022bfd3f 100644 --- a/src/language-js/print/binaryish.js +++ b/src/language-js/print/binaryish.js @@ -26,8 +26,8 @@ import { isObjectExpression, isObjectProperty, shouldFlatten, -} from "../utils/index.js"; -import isTypeCastComment from "../utils/is-type-cast-comment.js"; +} from "../utilities/index.js"; +import isTypeCastComment from "../utilities/is-type-cast-comment.js"; /** @import {Doc} from "../../document/index.js" */ diff --git a/src/language-js/print/bind-expression.js b/src/language-js/print/bind-expression.js new file mode 100644 index 000000000000..8f8bf5383c9f --- /dev/null +++ b/src/language-js/print/bind-expression.js @@ -0,0 +1,14 @@ +import { group, indent, softline } from "../../document/index.js"; + +function printBindExpression(path, options, print) { + return [ + print("object"), + group(indent([softline, printBindExpressionCallee(path, options, print)])), + ]; +} + +function printBindExpressionCallee(path, options, print) { + return ["::", print("callee")]; +} + +export { printBindExpression, printBindExpressionCallee }; diff --git a/src/language-js/print/block.js b/src/language-js/print/block.js index 5094c3b3c099..d5b83521dd2f 100644 --- a/src/language-js/print/block.js +++ b/src/language-js/print/block.js @@ -1,11 +1,11 @@ import { hardline, indent } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { CommentCheckFlags, hasComment, isNextLineEmpty, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printStatementSequence } from "./statement.js"; /** @import {Doc} from "../../document/index.js" */ diff --git a/src/language-js/print/call-arguments.js b/src/language-js/print/call-arguments.js index ae7e10d03b42..c076fc87c5b6 100644 --- a/src/language-js/print/call-arguments.js +++ b/src/language-js/print/call-arguments.js @@ -34,7 +34,7 @@ import { isStringLiteral, iterateCallArgumentsPath, shouldPrintComma, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { isConciselyPrintedArray } from "./array.js"; /* diff --git a/src/language-js/print/call-expression.js b/src/language-js/print/call-expression.js index a27a53567688..7e3034d67455 100644 --- a/src/language-js/print/call-expression.js +++ b/src/language-js/print/call-expression.js @@ -1,5 +1,5 @@ import { group, join } from "../../document/index.js"; -import pathNeedsParens from "../needs-parens.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; import { getCallArguments, hasComment, @@ -10,7 +10,7 @@ import { isTemplateOnItsOwnLine, isTestCall, iterateCallArgumentsPath, -} from "../utils/index.js"; +} from "../utilities/index.js"; import printCallArguments from "./call-arguments.js"; import printMemberChain from "./member-chain.js"; import { printOptionalToken } from "./misc.js"; @@ -80,7 +80,7 @@ function printCallExpression(path, options, print) { !isNewExpression && isMemberish(node.callee) && !path.call( - () => pathNeedsParens(path, options), + () => needsParentheses(path, options), "callee", ...(node.callee.type === "ChainExpression" ? ["expression"] : []), ) diff --git a/src/language-js/print/cast-expression.js b/src/language-js/print/cast-expression.js index fe9aca478380..a411c9dc7eed 100644 --- a/src/language-js/print/cast-expression.js +++ b/src/language-js/print/cast-expression.js @@ -3,7 +3,7 @@ import { createTypeCheckFunction, isCallExpression, isMemberExpression, -} from "../utils/index.js"; +} from "../utilities/index.js"; const isSatisfiesExpression = createTypeCheckFunction([ "SatisfiesExpression", diff --git a/src/language-js/print/class-body.js b/src/language-js/print/class-body.js index 34d5dd4c6bd9..3c292110d540 100644 --- a/src/language-js/print/class-body.js +++ b/src/language-js/print/class-body.js @@ -7,8 +7,8 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewline from "../../utils/has-newline.js"; -import hasNewlineInRange from "../../utils/has-newline-in-range.js"; +import hasNewline from "../../utilities/has-newline.js"; +import hasNewlineInRange from "../../utilities/has-newline-in-range.js"; import { locEnd, locStart } from "../loc.js"; import { CommentCheckFlags, @@ -17,7 +17,7 @@ import { hasComment, isNextLineEmpty, shouldPrintComma, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { shouldHugTheOnlyParameter } from "./function-parameters.js"; /* diff --git a/src/language-js/print/class.js b/src/language-js/print/class.js index 876b033b6e19..db49d56508e5 100644 --- a/src/language-js/print/class.js +++ b/src/language-js/print/class.js @@ -12,14 +12,14 @@ import { printCommentsSeparately, printDanglingComments, } from "../../main/comments/print.js"; -import createGroupIdMapper from "../../utils/create-group-id-mapper.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import createGroupIdMapper from "../../utilities/create-group-id-mapper.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { CommentCheckFlags, createTypeCheckFunction, hasComment, isMemberExpression, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printAssignment } from "./assignment.js"; import { printClassMemberDecorators } from "./decorators.js"; import { printMethod } from "./function.js"; diff --git a/src/language-js/print/comment.js b/src/language-js/print/comment.js index 6121d3144850..f2e50f35ea4f 100644 --- a/src/language-js/print/comment.js +++ b/src/language-js/print/comment.js @@ -1,8 +1,8 @@ import { hardline, join, replaceEndOfLine } from "../../document/index.js"; import { locEnd, locStart } from "../loc.js"; -import isBlockComment from "../utils/is-block-comment.js"; -import isIndentableBlockComment from "../utils/is-indentable-block-comment.js"; -import isLineComment from "../utils/is-line-comment.js"; +import isBlockComment from "../utilities/is-block-comment.js"; +import isIndentableBlockComment from "../utilities/is-indentable-block-comment.js"; +import isLineComment from "../utilities/is-line-comment.js"; function printComment(path, options) { const comment = path.node; diff --git a/src/language-js/print/component.js b/src/language-js/print/component.js index e325837ec3b1..e5f2040c39b1 100644 --- a/src/language-js/print/component.js +++ b/src/language-js/print/component.js @@ -7,9 +7,9 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import getNextNonSpaceNonCommentCharacter from "../../utils/get-next-non-space-non-comment-character.js"; +import getNextNonSpaceNonCommentCharacter from "../../utilities/get-next-non-space-non-comment-character.js"; import { locEnd } from "../loc.js"; -import { isNextLineEmpty, shouldPrintComma } from "../utils/index.js"; +import { isNextLineEmpty, shouldPrintComma } from "../utilities/index.js"; import { printDeclareToken } from "./misc.js"; /** diff --git a/src/language-js/print/decorators.js b/src/language-js/print/decorators.js index 7c6d893e9fc4..ac0487211461 100644 --- a/src/language-js/print/decorators.js +++ b/src/language-js/print/decorators.js @@ -5,11 +5,11 @@ import { join, line, } from "../../document/index.js"; -import hasNewline from "../../utils/has-newline.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import hasNewline from "../../utilities/has-newline.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { hasSameLocStart, locEnd } from "../loc.js"; -import { isExportDeclaration } from "../utils/index.js"; -import isIgnored from "../utils/is-ignored.js"; +import { isExportDeclaration } from "../utilities/index.js"; +import isIgnored from "../utilities/is-ignored.js"; function printClassMemberDecorators(path, options, print) { const { node } = path; diff --git a/src/language-js/print/enum.js b/src/language-js/print/enum.js index 3ec414541170..0df9fbbe71b5 100644 --- a/src/language-js/print/enum.js +++ b/src/language-js/print/enum.js @@ -13,6 +13,23 @@ function printEnumBody(path, options, print) { return printEnumMembers(path, options, print); } +function printFlowEnumBody(path, options, print) { + const { node } = path; + return [ + node.type === "EnumSymbolBody" || node.explicitType + ? `of ${node.type + .slice( + // `Enum` + 4, + // `Body` + -4, + ) + .toLowerCase()} ` + : "", + printEnumBody(path, options, print), + ]; +} + /* - `EnumBooleanMember`(flow) - `EnumNumberMember`(flow) @@ -66,4 +83,9 @@ function printEnumDeclaration(path, print) { ]; } -export { printEnumBody, printEnumDeclaration, printEnumMember }; +export { + printEnumBody, + printEnumDeclaration, + printEnumMember, + printFlowEnumBody, +}; diff --git a/src/language-js/print/estree.js b/src/language-js/print/estree.js index 616a01ae5cc6..fbf9a1ef0823 100644 --- a/src/language-js/print/estree.js +++ b/src/language-js/print/estree.js @@ -9,8 +9,8 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewline from "../../utils/has-newline.js"; -import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; +import hasNewline from "../../utilities/has-newline.js"; +import UnexpectedNodeError from "../../utilities/unexpected-node-error.js"; import { locEnd, locStart } from "../loc.js"; import { CommentCheckFlags, @@ -25,8 +25,8 @@ import { isObjectExpression, needsHardlineAfterDanglingComment, startsWithNoLookaheadToken, -} from "../utils/index.js"; -import isBlockComment from "../utils/is-block-comment.js"; +} from "../utilities/index.js"; +import isBlockComment from "../utilities/is-block-comment.js"; import { printArray } from "./array.js"; import { printArrowFunction } from "./arrow-function.js"; import { @@ -34,6 +34,7 @@ import { printVariableDeclarator, } from "./assignment.js"; import { printBinaryishExpression } from "./binaryish.js"; +import { printBindExpression } from "./bind-expression.js"; import { printBlock } from "./block.js"; import { printCallExpression } from "./call-expression.js"; import { @@ -54,11 +55,9 @@ import { printLiteral } from "./literal.js"; import { printMemberExpression } from "./member.js"; import { adjustClause, - printBindExpressionCallee, printDeclareToken, printDefiniteToken, printOptionalToken, - printRestSpread, } from "./misc.js"; import { printExportDeclaration, @@ -67,6 +66,7 @@ import { } from "./module.js"; import { printObject } from "./object.js"; import { printProperty } from "./property.js"; +import { printRestElement, printSpreadElement } from "./rest-element.js"; import { printStatementSequence } from "./statement.js"; import { printTaggedTemplateExpression, @@ -140,12 +140,7 @@ function printEstree(path, options, print, args) { case "MetaProperty": return [print("meta"), ".", print("property")]; case "BindExpression": - return [ - print("object"), - group( - indent([softline, printBindExpressionCallee(path, options, print)]), - ), - ]; + return printBindExpression(path, options, print); case "Identifier": return [ node.name, @@ -157,10 +152,9 @@ function printEstree(path, options, print, args) { case "V8IntrinsicIdentifier": return ["%", node.name]; case "SpreadElement": - case "SpreadElementPattern": - case "SpreadPropertyPattern": + return printSpreadElement(path, print); case "RestElement": - return printRestSpread(path, print); + return printRestElement(path, print); case "FunctionDeclaration": case "FunctionExpression": return printFunction(path, options, print, args); diff --git a/src/language-js/print/expression-statement.js b/src/language-js/print/expression-statement.js index aa0f9cad172b..835f3e78b735 100644 --- a/src/language-js/print/expression-statement.js +++ b/src/language-js/print/expression-statement.js @@ -1,13 +1,13 @@ -import { - isVueEventBindingFunctionExpression, - isVueEventBindingMemberExpression, - unwrapVueEventBindingTsNode, -} from "../utils/vue-event-binding.js"; import { isSingleHtmlEventHandlerExpressionStatement, isSingleJsxExpressionStatementInMarkdown, isSingleVueEventBindingExpressionStatement, -} from "./semicolon.js"; +} from "../semicolon/semicolon.js"; +import { + isVueEventBindingFunctionExpression, + isVueEventBindingMemberExpression, + unwrapVueEventBindingTsNode, +} from "../utilities/vue-event-binding.js"; function shouldPrintSemicolon(path, options) { if (isSingleVueEventBindingExpressionStatement(path, options)) { diff --git a/src/language-js/print/flow.js b/src/language-js/print/flow.js index 4bc58347ea96..24230abfbd90 100644 --- a/src/language-js/print/flow.js +++ b/src/language-js/print/flow.js @@ -2,12 +2,13 @@ import * as assert from "#universal/assert"; import { replaceEndOfLine } from "../../document/index.js"; -import printNumber from "../../utils/print-number.js"; -import printString from "../../utils/print-string.js"; -import getRaw from "../utils/get-raw.js"; -import { isMethod } from "../utils/index.js"; -import isFlowKeywordType from "../utils/is-flow-keyword-type.js"; +import printNumber from "../../utilities/print-number.js"; +import printString from "../../utilities/print-string.js"; +import getRaw from "../utilities/get-raw.js"; +import { isMethod } from "../utilities/index.js"; +import isFlowKeywordType from "../utilities/is-flow-keyword-type.js"; import { printArray } from "./array.js"; +import { printArrayType } from "./array-type.js"; import { printBinaryCastExpression } from "./cast-expression.js"; import { printClass, @@ -20,43 +21,39 @@ import { printComponentTypeParameter, } from "./component.js"; import { - printEnumBody, printEnumDeclaration, printEnumMember, + printFlowEnumBody, } from "./enum.js"; +import { printFunctionType } from "./function-type.js"; import { printDeclareHook, printHook, printHookTypeAnnotation, } from "./hook.js"; +import { printIndexedAccessType } from "./indexed-access-type.js"; +import { printInferType } from "./infer-type.js"; +import { printIntersectionType } from "./intersection-type.js"; import { printBigInt } from "./literal.js"; import { printFlowMappedTypeProperty } from "./mapped-type.js"; import { printMatch, printMatchCase, printMatchPattern } from "./match.js"; -import { - printDeclareToken, - printOptionalToken, - printRestSpread, -} from "./misc.js"; +import { printDeclareToken, printOptionalToken } from "./misc.js"; import { printExportDeclaration } from "./module.js"; +import { printOpaqueType } from "./opaque-type.js"; import { printPropertyKey } from "./property.js"; +import { printSpreadElement } from "./rest-element.js"; +import { printRestType } from "./rest-type.js"; import { printTernary } from "./ternary.js"; +import { printNamedTupleMember } from "./tuple.js"; +import { printTypeAlias } from "./type-alias.js"; import { - printArrayType, - printFunctionType, - printIndexedAccessType, - printInferType, - printIntersectionType, - printNamedTupleMember, - printOpaqueType, - printRestType, - printTypeAlias, printTypeAnnotation, printTypeAnnotationProperty, - printTypePredicate, - printTypeQuery, - printUnionType, } from "./type-annotation.js"; import { printTypeParameter, printTypeParameters } from "./type-parameters.js"; +import { printTypePredicate } from "./type-predicate.js"; +import { printTypeQuery } from "./type-query.js"; +import { printUnionType } from "./union-type.js"; function printFlow(path, options, print) { const { node } = path; @@ -165,19 +162,7 @@ function printFlow(path, options, print) { case "EnumBigIntBody": case "EnumStringBody": case "EnumSymbolBody": - return [ - node.type === "EnumSymbolBody" || node.explicitType - ? `of ${node.type - .slice( - // `Enum` - 4, - // `Body` - -4, - ) - .toLowerCase()} ` - : "", - printEnumBody(path, options, print), - ]; + return printFlowEnumBody(path, options, print); case "EnumBooleanMember": case "EnumNumberMember": @@ -275,7 +260,7 @@ function printFlow(path, options, print) { ]; // Same as `RestElement` case "ObjectTypeSpreadProperty": - return printRestSpread(path, print); + return printSpreadElement(path, print); case "QualifiedTypeofIdentifier": case "QualifiedTypeIdentifier": return [print("qualification"), ".", print("id")]; diff --git a/src/language-js/print/function-parameters.js b/src/language-js/print/function-parameters.js index 9283685655ee..2cfb316a8555 100644 --- a/src/language-js/print/function-parameters.js +++ b/src/language-js/print/function-parameters.js @@ -10,8 +10,8 @@ import { willBreak, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import getNextNonSpaceNonCommentCharacter from "../../utils/get-next-non-space-non-comment-character.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import getNextNonSpaceNonCommentCharacter from "../../utilities/get-next-non-space-non-comment-character.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { locEnd } from "../loc.js"; import { getFunctionParameters, @@ -27,7 +27,7 @@ import { isTypeAnnotationAFunction, iterateFunctionParametersPath, shouldPrintComma, -} from "../utils/index.js"; +} from "../utilities/index.js"; /** @import AstPath from "../../common/ast-path.js" */ diff --git a/src/language-js/print/function-type.js b/src/language-js/print/function-type.js new file mode 100644 index 000000000000..5f1e094c8a51 --- /dev/null +++ b/src/language-js/print/function-type.js @@ -0,0 +1,95 @@ +import { group } from "../../document/index.js"; +import { hasSameLocStart } from "../loc.js"; +import { isFlowObjectTypePropertyAFunction } from "../utilities/index.js"; +import { printClassMemberSemicolon } from "./class.js"; +import { + printFunctionParameters, + shouldGroupFunctionParameters, +} from "./function-parameters.js"; +import { printAbstractToken } from "./misc.js"; +import { printTypeAnnotationProperty } from "./type-annotation.js"; + +/** + * @import {Doc} from "../../document/index.js" + */ + +/* +- `TSFunctionType` (TypeScript) +- `TSCallSignatureDeclaration` (TypeScript) +- `TSConstructorType` (TypeScript) +- `TSConstructSignatureDeclaration` (TypeScript) +- `FunctionTypeAnnotation` (Flow) +*/ +function printFunctionType(path, options, print) { + const { node } = path; + /** @type {Doc[]} */ + const parts = [ + // `TSConstructorType` only + printAbstractToken(path), + ]; + + if ( + node.type === "TSConstructorType" || + node.type === "TSConstructSignatureDeclaration" + ) { + parts.push("new "); + } + + let parametersDoc = printFunctionParameters( + path, + options, + print, + /* shouldExpandArgument */ false, + /* shouldPrintTypeParameters */ true, + ); + + const returnTypeDoc = []; + // `flow` doesn't wrap the `returnType` with `TypeAnnotation`, so the colon + // needs to be added separately. + if (node.type === "FunctionTypeAnnotation") { + returnTypeDoc.push( + isFlowArrowFunctionTypeAnnotation(path) ? " => " : ": ", + print("returnType"), + ); + } else { + returnTypeDoc.push(printTypeAnnotationProperty(path, print, "returnType")); + } + + if (shouldGroupFunctionParameters(node, returnTypeDoc)) { + parametersDoc = group(parametersDoc); + } + + parts.push(parametersDoc, returnTypeDoc); + + return [ + group(parts), + node.type === "TSConstructSignatureDeclaration" || + node.type === "TSCallSignatureDeclaration" + ? printClassMemberSemicolon(path, options) + : "", + ]; +} + +/* +`FunctionTypeAnnotation` is ambiguous: +- `declare function foo(a: B): void;` +- `var A: (a: B) => void;` +*/ +function isFlowArrowFunctionTypeAnnotation(path) { + const { node, parent } = path; + return ( + node.type === "FunctionTypeAnnotation" && + (isFlowObjectTypePropertyAFunction(parent) || + !( + ((parent.type === "ObjectTypeProperty" || + parent.type === "ObjectTypeInternalSlot") && + !parent.variance && + !parent.optional && + hasSameLocStart(parent, node)) || + parent.type === "ObjectTypeCallProperty" || + path.getParentNode(2)?.type === "DeclareFunction" + )) + ); +} + +export { printFunctionType }; diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js index c2c9d2f1179e..c0cc75b37111 100644 --- a/src/language-js/print/function.js +++ b/src/language-js/print/function.js @@ -7,7 +7,7 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewlineInRange from "../../utils/has-newline-in-range.js"; +import hasNewlineInRange from "../../utilities/has-newline-in-range.js"; import { locEnd, locStart } from "../loc.js"; import { CommentCheckFlags, @@ -21,7 +21,7 @@ import { isCallExpression, isJsxElement, isMethod, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printFunctionParameters, shouldBreakFunctionParameters, diff --git a/src/language-js/print/index-signature.js b/src/language-js/print/index-signature.js new file mode 100644 index 000000000000..4c5dd374babf --- /dev/null +++ b/src/language-js/print/index-signature.js @@ -0,0 +1,43 @@ +import { + group, + ifBreak, + indent, + join, + softline, +} from "../../document/index.js"; +import { shouldPrintComma } from "../utilities/index.js"; +import { printClassMemberSemicolon } from "./class.js"; +import { printTypeAnnotationProperty } from "./type-annotation.js"; + +function printIndexSignature(path, options, print) { + const { node } = path; + // The typescript parser accepts multiple parameters here. If you're + // using them, it makes sense to have a trailing comma. But if you + // aren't, this is more like a computed property name than an array. + // So we leave off the trailing comma when there's just one parameter. + const trailingComma = + node.parameters.length > 1 + ? ifBreak(shouldPrintComma(options) ? "," : "") + : ""; + + const parametersGroup = group([ + indent([softline, join([", ", softline], path.map(print, "parameters"))]), + trailingComma, + softline, + ]); + + const isClassMember = path.key === "body" && path.parent.type === "ClassBody"; + + return [ + // `static` only allowed in class member + isClassMember && node.static ? "static " : "", + node.readonly ? "readonly " : "", + "[", + node.parameters ? parametersGroup : "", + "]", + printTypeAnnotationProperty(path, print), + printClassMemberSemicolon(path, options), + ]; +} + +export { printIndexSignature }; diff --git a/src/language-js/print/index.js b/src/language-js/print/index.js index 9532c9338227..f2d53b9d8248 100644 --- a/src/language-js/print/index.js +++ b/src/language-js/print/index.js @@ -1,15 +1,15 @@ import { group, indent, inheritLabel, line } from "../../document/index.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { locEnd, locStart } from "../loc.js"; -import pathNeedsParens from "../needs-parens.js"; -import { createTypeCheckFunction } from "../utils/index.js"; -import isIgnored from "../utils/is-ignored.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; +import { shouldPrintLeadingSemicolon } from "../semicolon/semicolon.js"; +import { createTypeCheckFunction } from "../utilities/index.js"; +import isIgnored from "../utilities/is-ignored.js"; import { printAngular } from "./angular.js"; import { printDecorators } from "./decorators.js"; import { printEstree } from "./estree.js"; import { printFlow } from "./flow.js"; import { printJsx } from "./jsx.js"; -import { shouldPrintLeadingSemicolon } from "./semicolon.js"; import { printTypescript } from "./typescript.js"; /** @@ -81,7 +81,7 @@ function print(path, options, print, args) { return inheritLabel(doc, (doc) => group([decoratorsDoc, doc])); } - const needsParens = pathNeedsParens(path, options); + const needsParens = needsParentheses(path, options); const needsSemi = shouldPrintLeadingSemicolon(path, options); if (!decoratorsDoc && !needsParens && !needsSemi) { diff --git a/src/language-js/print/indexed-access-type.js b/src/language-js/print/indexed-access-type.js new file mode 100644 index 000000000000..f5037daeaca3 --- /dev/null +++ b/src/language-js/print/indexed-access-type.js @@ -0,0 +1,18 @@ +import { printOptionalToken } from "./misc.js"; + +/* +- `TSIndexedAccessType`(TypeScript) +- `IndexedAccessType`(flow) +- `OptionalIndexedAccessType`(flow) +*/ +function printIndexedAccessType(path, options, print) { + return [ + print("objectType"), + printOptionalToken(path), + "[", + print("indexType"), + "]", + ]; +} + +export { printIndexedAccessType }; diff --git a/src/language-js/print/infer-type.js b/src/language-js/print/infer-type.js new file mode 100644 index 000000000000..efd00fd5746e --- /dev/null +++ b/src/language-js/print/infer-type.js @@ -0,0 +1,9 @@ +/* +- `TSInferType`(TypeScript) +- `InferTypeAnnotation`(flow) +*/ +function printInferType(path, options, print) { + return ["infer ", print("typeParameter")]; +} + +export { printInferType }; diff --git a/src/language-js/print/intersection-type.js b/src/language-js/print/intersection-type.js new file mode 100644 index 000000000000..92626d989aa3 --- /dev/null +++ b/src/language-js/print/intersection-type.js @@ -0,0 +1,43 @@ +import { group, indent, line } from "../../document/index.js"; +import { hasLeadingOwnLineComment, isObjectType } from "../utilities/index.js"; + +// `TSIntersectionType` and `IntersectionTypeAnnotation` +function printIntersectionType(path, options, print) { + let wasIndented = false; + return group( + path.map(({ isFirst, previous, node, index }) => { + const doc = print(); + if (isFirst) { + return doc; + } + + const currentIsObjectType = isObjectType(node); + const previousIsObjectType = isObjectType(previous); + + // If both are objects, don't indent + if (previousIsObjectType && currentIsObjectType) { + return [" & ", wasIndented ? indent(doc) : doc]; + } + + if ( + // If no object is involved, go to the next line if it breaks + (!previousIsObjectType && !currentIsObjectType) || + hasLeadingOwnLineComment(options.originalText, node) + ) { + if (options.experimentalOperatorPosition === "start") { + return indent([line, "& ", doc]); + } + return indent([" &", line, doc]); + } + + // If you go from object to non-object or vis-versa, then inline it + if (index > 1) { + wasIndented = true; + } + + return [" & ", index > 1 ? indent(doc) : doc]; + }, "types"), + ); +} + +export { printIntersectionType }; diff --git a/src/language-js/print/js-doc-type.js b/src/language-js/print/js-doc-type.js new file mode 100644 index 000000000000..8e21c30e57d6 --- /dev/null +++ b/src/language-js/print/js-doc-type.js @@ -0,0 +1,13 @@ +import { printTypeAnnotationProperty } from "./type-annotation.js"; + +// `TSJSDocNullableType`, `TSJSDocNonNullableType` +function printJSDocType(path, print, token) { + const { node } = path; + return [ + node.postfix ? "" : token, + printTypeAnnotationProperty(path, print), + node.postfix ? token : "", + ]; +} + +export { printJSDocType }; diff --git a/src/language-js/print/jsx.js b/src/language-js/print/jsx.js index 41d2065ed0f5..b60e84ff848d 100644 --- a/src/language-js/print/jsx.js +++ b/src/language-js/print/jsx.js @@ -18,11 +18,11 @@ import { printComments, printDanglingComments, } from "../../main/comments/print.js"; -import getPreferredQuote from "../../utils/get-preferred-quote.js"; -import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; -import WhitespaceUtils from "../../utils/whitespace-utils.js"; -import pathNeedsParens from "../needs-parens.js"; -import getRaw from "../utils/get-raw.js"; +import getPreferredQuote from "../../utilities/get-preferred-quote.js"; +import UnexpectedNodeError from "../../utilities/unexpected-node-error.js"; +import WhitespaceUtilities from "../../utilities/whitespace-utilities.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; +import getRaw from "../utilities/get-raw.js"; import { CommentCheckFlags, createTypeCheckFunction, @@ -34,7 +34,7 @@ import { isJsxElement, isObjectExpression, isStringLiteral, -} from "../utils/index.js"; +} from "../utilities/index.js"; /* Only the following are treated as whitespace inside JSX. @@ -44,7 +44,7 @@ Only the following are treated as whitespace inside JSX. - U+000D CR - U+0009 TAB */ -const jsxWhitespaceUtils = new WhitespaceUtils(" \n\r\t"); +const jsxWhitespace = new WhitespaceUtilities(" \n\r\t"); const isEmptyStringOrAnyLine = (doc) => doc === "" || doc === line || doc === hardline || doc === softline; @@ -126,7 +126,7 @@ function printJsxElementInternal(path, options, print) { const isMdxBlock = path.parent.rootMarker === "mdx"; const rawJsxWhitespace = options.singleQuote ? "{' '}" : '{" "}'; - const jsxWhitespace = isMdxBlock + const whitespace = isMdxBlock ? line : ifBreak([rawJsxWhitespace, softline], " "); @@ -136,7 +136,7 @@ function printJsxElementInternal(path, options, print) { path, options, print, - jsxWhitespace, + whitespace, isFacebookTranslationTag, ); @@ -157,15 +157,15 @@ function printJsxElementInternal(path, options, print) { const isLineFollowedByJsxWhitespace = (children[i] === softline || children[i] === hardline) && children[i + 1] === "" && - children[i + 2] === jsxWhitespace; + children[i + 2] === whitespace; const isJsxWhitespaceFollowedByLine = - children[i] === jsxWhitespace && + children[i] === whitespace && children[i + 1] === "" && (children[i + 2] === softline || children[i + 2] === hardline); const isDoubleJsxWhitespace = - children[i] === jsxWhitespace && + children[i] === whitespace && children[i + 1] === "" && - children[i + 2] === jsxWhitespace; + children[i + 2] === whitespace; const isPairOfHardOrSoftLines = (children[i] === softline && children[i + 1] === "" && @@ -216,7 +216,7 @@ function printJsxElementInternal(path, options, print) { for (const [i, child] of children.entries()) { // There are a number of situations where we need to ensure we display // whitespace as `{" "}` when outputting this element over multiple lines. - if (child === jsxWhitespace) { + if (child === whitespace) { if (i === 1 && isEmptyDoc(children[i - 1])) { if (children.length === 2) { // Solitary whitespace @@ -317,7 +317,7 @@ function printJsxChildren( path, options, print, - jsxWhitespace, + whitespace, isFacebookTranslationTag, ) { /** @type {Doc} */ @@ -342,10 +342,7 @@ function printJsxChildren( // Contains a non-whitespace character if (isMeaningfulJsxText(node)) { - const words = jsxWhitespaceUtils.split( - text, - /* captureWhitespace */ true, - ); + const words = jsxWhitespace.split(text, /* captureWhitespace */ true); // Starts with whitespace if (words[0] === "") { @@ -360,7 +357,7 @@ function printJsxChildren( ), ); } else { - pushLine(jsxWhitespace); + pushLine(whitespace); } words.shift(); } @@ -396,7 +393,7 @@ function printJsxChildren( ), ); } else { - pushLine(jsxWhitespace); + pushLine(whitespace); } } else { pushLine( @@ -415,7 +412,7 @@ function printJsxChildren( pushLine(hardline); } } else { - pushLine(jsxWhitespace); + pushLine(whitespace); } } else { const printedChild = print(); @@ -424,8 +421,8 @@ function printJsxChildren( const directlyFollowedByMeaningfulText = next && isMeaningfulJsxText(next); if (directlyFollowedByMeaningfulText) { - const trimmed = jsxWhitespaceUtils.trim(getRaw(next)); - const [firstWord] = jsxWhitespaceUtils.split(trimmed); + const trimmed = jsxWhitespace.trim(getRaw(next)); + const [firstWord] = jsxWhitespace.split(trimmed); pushLine( separatorNoWhitespace( isFacebookTranslationTag, @@ -505,7 +502,7 @@ function maybeWrapJsxElementInParens(path, elem, options) { } const shouldBreak = shouldBreakJsxElement(path); - const needsParens = pathNeedsParens(path, options); + const needsParens = needsParentheses(path, options); return group( [ @@ -866,7 +863,7 @@ function isEmptyJsxElement(node) { function isMeaningfulJsxText(node) { return ( node.type === "JSXText" && - (jsxWhitespaceUtils.hasNonWhitespaceCharacter(getRaw(node)) || + (jsxWhitespace.hasNonWhitespaceCharacter(getRaw(node)) || !/\n/u.test(getRaw(node))) ); } diff --git a/src/language-js/print/literal.js b/src/language-js/print/literal.js index da06fa30a140..bd10f9a97e9f 100644 --- a/src/language-js/print/literal.js +++ b/src/language-js/print/literal.js @@ -1,6 +1,6 @@ import { replaceEndOfLine } from "../../document/index.js"; -import printNumber from "../../utils/print-number.js"; -import printString from "../../utils/print-string.js"; +import printNumber from "../../utilities/print-number.js"; +import printString from "../../utilities/print-string.js"; /** * @import {Node} from "../types/estree.js" @@ -70,22 +70,27 @@ function printRegex({ pattern, flags }) { return `/${pattern}/${flags}`; } +const DIRECTIVE_USE_STRICT = "use strict"; function printDirective(rawText, options) { const rawContent = rawText.slice(1, -1); // Check for the alternate quote, to determine if we're allowed to swap // the quotes on a DirectiveLiteral. - if (rawContent.includes('"') || rawContent.includes("'")) { - return rawText; + // Perf https://tinyurl.com/388dmh3v + if ( + rawContent === DIRECTIVE_USE_STRICT || + !(rawContent.includes('"') || rawContent.includes("'")) + ) { + const enclosingQuote = options.singleQuote ? "'" : '"'; + + // Directives are exact code unit sequences, which means that you can't + // change the escape sequences they use. + // See https://github.com/prettier/prettier/issues/1555 + // and https://tc39.github.io/ecma262/#directive-prologue + return enclosingQuote + rawContent + enclosingQuote; } - const enclosingQuote = options.singleQuote ? "'" : '"'; - - // Directives are exact code unit sequences, which means that you can't - // change the escape sequences they use. - // See https://github.com/prettier/prettier/issues/1555 - // and https://tc39.github.io/ecma262/#directive-prologue - return enclosingQuote + rawContent + enclosingQuote; + return rawText; } export { printBigInt, printLiteral }; diff --git a/src/language-js/print/mapped-type.js b/src/language-js/print/mapped-type.js index 2f3102ed1c64..5784272f43e3 100644 --- a/src/language-js/print/mapped-type.js +++ b/src/language-js/print/mapped-type.js @@ -7,10 +7,10 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewlineInRange from "../../utils/has-newline-in-range.js"; +import hasNewlineInRange from "../../utilities/has-newline-in-range.js"; import { locStart } from "../loc.js"; -import getTextWithoutComments from "../utils/get-text-without-comments.js"; -import { CommentCheckFlags, hasComment } from "../utils/index.js"; +import getTextWithoutComments from "../utilities/get-text-without-comments.js"; +import { CommentCheckFlags, hasComment } from "../utilities/index.js"; import { printClassMemberSemicolon } from "./class.js"; /** diff --git a/src/language-js/print/match.js b/src/language-js/print/match.js index 12b42e91a723..b97bbf443997 100644 --- a/src/language-js/print/match.js +++ b/src/language-js/print/match.js @@ -12,14 +12,14 @@ import { printComments, printDanglingComments, } from "../../main/comments/print.js"; -import pathNeedsParens from "../needs-parens.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; import { CommentCheckFlags, createTypeCheckFunction, hasComment, hasLeadingOwnLineComment, isNextLineEmpty, -} from "../utils/index.js"; +} from "../utilities/index.js"; /* - `MatchExpression` (Flow) @@ -233,7 +233,7 @@ function printMatchOrPattern(path, options, print) { const code = [ifBreak(["| "]), join([line, "| "], printed)]; - if (pathNeedsParens(path, options)) { + if (needsParentheses(path, options)) { return group([indent([ifBreak([softline]), code]), softline]); } diff --git a/src/language-js/print/member-chain.js b/src/language-js/print/member-chain.js index 8cf14979fc4e..46dbc3fd4404 100644 --- a/src/language-js/print/member-chain.js +++ b/src/language-js/print/member-chain.js @@ -9,10 +9,10 @@ import { willBreak, } from "../../document/index.js"; import { printComments } from "../../main/comments/print.js"; -import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js"; -import isNextLineEmptyAfterIndex from "../../utils/is-next-line-empty.js"; +import getNextNonSpaceNonCommentCharacterIndex from "../../utilities/get-next-non-space-non-comment-character-index.js"; +import isNextLineEmptyAfterIndex from "../../utilities/is-next-line-empty.js"; import { locEnd } from "../loc.js"; -import pathNeedsParens from "../needs-parens.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; import { CommentCheckFlags, hasComment, @@ -24,10 +24,11 @@ import { isNextLineEmpty, isNumericLiteral, isSimpleCallArgument, -} from "../utils/index.js"; +} from "../utilities/index.js"; +import { printBindExpressionCallee } from "./bind-expression.js"; import printCallArguments from "./call-arguments.js"; import { printMemberLookup } from "./member.js"; -import { printBindExpressionCallee, printOptionalToken } from "./misc.js"; +import { printOptionalToken } from "./misc.js"; /** * @import {Doc} from "../../document/index.js" @@ -122,7 +123,7 @@ function printMemberChain(path, options, print) { } else if (isMemberish(node)) { printedNodes.unshift({ node, - needsParens: pathNeedsParens(path, options), + needsParens: needsParentheses(path, options), printed: printComments( path, isMemberExpression(node) diff --git a/src/language-js/print/member.js b/src/language-js/print/member.js index 52456d7bd386..3fbcfa3c0895 100644 --- a/src/language-js/print/member.js +++ b/src/language-js/print/member.js @@ -4,7 +4,7 @@ import { isCallExpression, isMemberExpression, isNumericLiteral, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printOptionalToken } from "./misc.js"; const isCallExpressionWithArguments = (node) => { diff --git a/src/language-js/print/method-signature.js b/src/language-js/print/method-signature.js new file mode 100644 index 000000000000..3db90cb70606 --- /dev/null +++ b/src/language-js/print/method-signature.js @@ -0,0 +1,49 @@ +import { group } from "../../document/index.js"; +import { printClassMemberSemicolon } from "./class.js"; +import { + printFunctionParameters, + shouldGroupFunctionParameters, +} from "./function-parameters.js"; +import { + printOptionalToken, + printTypeScriptAccessibilityToken, +} from "./misc.js"; +import { printTypeAnnotationProperty } from "./type-annotation.js"; + +function printMethodSignature(path, options, print) { + const { node } = path; + const parts = []; + const kind = node.kind && node.kind !== "method" ? `${node.kind} ` : ""; + parts.push( + printTypeScriptAccessibilityToken(node), + kind, + node.computed ? "[" : "", + print("key"), + node.computed ? "]" : "", + printOptionalToken(path), + ); + + const parametersDoc = printFunctionParameters( + path, + options, + print, + /* shouldExpandArgument */ false, + /* shouldPrintTypeParameters */ true, + ); + + const returnTypeDoc = printTypeAnnotationProperty(path, print, "returnType"); + const shouldGroupParameters = shouldGroupFunctionParameters( + node, + returnTypeDoc, + ); + + parts.push(shouldGroupParameters ? group(parametersDoc) : parametersDoc); + + if (node.returnType) { + parts.push(group(returnTypeDoc)); + } + + return [group(parts), printClassMemberSemicolon(path, options)]; +} + +export { printMethodSignature }; diff --git a/src/language-js/print/misc.js b/src/language-js/print/misc.js index bad1485a1a20..d3635dbc452e 100644 --- a/src/language-js/print/misc.js +++ b/src/language-js/print/misc.js @@ -5,8 +5,7 @@ import { hasComment, isCallExpression, isMemberExpression, -} from "../utils/index.js"; -import { printTypeAnnotationProperty } from "./type-annotation.js"; +} from "../utilities/index.js"; /** * @import AstPath from "../../common/ast-path.js" @@ -98,10 +97,6 @@ function printAbstractToken({ node }) { return node.abstract || isTsAbstractNode(node) ? "abstract " : ""; } -function printBindExpressionCallee(path, options, print) { - return ["::", print("callee")]; -} - function adjustClause(node, clause, forceSpace) { if (node.type === "EmptyStatement") { return hasComment(node, CommentCheckFlags.Leading) ? [" ", clause] : clause; @@ -114,10 +109,6 @@ function adjustClause(node, clause, forceSpace) { return indent([line, clause]); } -function printRestSpread(path, print) { - return ["...", print("argument"), printTypeAnnotationProperty(path, print)]; -} - function printTypeScriptAccessibilityToken(node) { return node.accessibility ? node.accessibility + " " : ""; } @@ -125,10 +116,8 @@ function printTypeScriptAccessibilityToken(node) { export { adjustClause, printAbstractToken, - printBindExpressionCallee, printDeclareToken, printDefiniteToken, printOptionalToken, - printRestSpread, printTypeScriptAccessibilityToken, }; diff --git a/src/language-js/print/module-declaration.js b/src/language-js/print/module-declaration.js new file mode 100644 index 000000000000..3ad87fc27ae6 --- /dev/null +++ b/src/language-js/print/module-declaration.js @@ -0,0 +1,15 @@ +import { group } from "../../document/index.js"; +import { printDeclareToken } from "./misc.js"; + +function printModuleDeclaration(path, options, print) { + const { node } = path; + + return [ + printDeclareToken(path), + node.kind === "global" ? "" : `${node.kind} `, + print("id"), + node.body ? [" ", group(print("body"))] : options.semi ? ";" : "", + ]; +} + +export { printModuleDeclaration }; diff --git a/src/language-js/print/module.js b/src/language-js/print/module.js index e1bcf1386b0b..ee4dbc6e762e 100644 --- a/src/language-js/print/module.js +++ b/src/language-js/print/module.js @@ -9,11 +9,11 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; -import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; +import UnexpectedNodeError from "../../utilities/unexpected-node-error.js"; import { hasSameLoc, locEnd, locStart } from "../loc.js"; -import getRaw from "../utils/get-raw.js"; -import getTextWithoutComments from "../utils/get-text-without-comments.js"; +import getRaw from "../utilities/get-raw.js"; +import getTextWithoutComments from "../utilities/get-text-without-comments.js"; import { CommentCheckFlags, createTypeCheckFunction, @@ -21,7 +21,7 @@ import { isStringLiteral, needsHardlineAfterDanglingComment, shouldPrintComma, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { printDecoratorsBeforeExport } from "./decorators.js"; import { printDeclareToken } from "./misc.js"; import { printObject } from "./object.js"; diff --git a/src/language-js/print/object.js b/src/language-js/print/object.js index c859dfb20f38..38ca29de2044 100644 --- a/src/language-js/print/object.js +++ b/src/language-js/print/object.js @@ -8,11 +8,11 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewline from "../../utils/has-newline.js"; -import hasNewlineInRange from "../../utils/has-newline-in-range.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import hasNewline from "../../utilities/has-newline.js"; +import hasNewlineInRange from "../../utilities/has-newline-in-range.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import { locEnd, locStart } from "../loc.js"; -import getTextWithoutComments from "../utils/get-text-without-comments.js"; +import getTextWithoutComments from "../utilities/get-text-without-comments.js"; import { CommentCheckFlags, createTypeCheckFunction, @@ -21,7 +21,7 @@ import { isNextLineEmpty, isObjectType, shouldPrintComma, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { shouldHugTheOnlyParameter } from "./function-parameters.js"; import { printOptionalToken } from "./misc.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; diff --git a/src/language-js/print/opaque-type.js b/src/language-js/print/opaque-type.js new file mode 100644 index 000000000000..1a756447a8fc --- /dev/null +++ b/src/language-js/print/opaque-type.js @@ -0,0 +1,45 @@ +import { group, indent, line } from "../../document/index.js"; +import { printDeclareToken } from "./misc.js"; + +/* +- `DeclareOpaqueType`(flow) +- `OpaqueType`(flow) +*/ +function printOpaqueType(path, options, print) { + const { node } = path; + const parts = [ + printDeclareToken(path), + "opaque type ", + print("id"), + print("typeParameters"), + ]; + + if (node.supertype) { + parts.push(": ", print("supertype")); + } + + if (node.lowerBound || node.upperBound) { + const lowerAndUpperBoundParts = []; + if (node.lowerBound) { + lowerAndUpperBoundParts.push( + indent([line, "super ", print("lowerBound")]), + ); + } + if (node.upperBound) { + lowerAndUpperBoundParts.push( + indent([line, "extends ", print("upperBound")]), + ); + } + parts.push(group(lowerAndUpperBoundParts)); + } + + if (node.impltype) { + parts.push(" = ", print("impltype")); + } + + parts.push(options.semi ? ";" : ""); + + return parts; +} + +export { printOpaqueType }; diff --git a/src/language-js/print/property.js b/src/language-js/print/property.js index 1d18983305c6..a1a68ac42534 100644 --- a/src/language-js/print/property.js +++ b/src/language-js/print/property.js @@ -1,9 +1,9 @@ import isEs5IdentifierName from "is-es5-identifier-name"; import { printComments } from "../../main/comments/print.js"; -import printNumber from "../../utils/print-number.js"; -import printString from "../../utils/print-string.js"; -import getRaw from "../utils/get-raw.js"; -import { isNumericLiteral, isStringLiteral } from "../utils/index.js"; +import printNumber from "../../utilities/print-number.js"; +import printString from "../../utilities/print-string.js"; +import getRaw from "../utilities/get-raw.js"; +import { isNumericLiteral, isStringLiteral } from "../utilities/index.js"; import { printAssignment } from "./assignment.js"; const needsQuoteProps = new WeakMap(); diff --git a/src/language-js/print/rest-element.js b/src/language-js/print/rest-element.js new file mode 100644 index 000000000000..84899f932db5 --- /dev/null +++ b/src/language-js/print/rest-element.js @@ -0,0 +1,10 @@ +import { printTypeAnnotationProperty } from "./type-annotation.js"; + +function printRestOrSpreadElement(path, print) { + return ["...", print("argument"), printTypeAnnotationProperty(path, print)]; +} + +export { + printRestOrSpreadElement as printRestElement, + printRestOrSpreadElement as printSpreadElement, +}; diff --git a/src/language-js/print/rest-type.js b/src/language-js/print/rest-type.js new file mode 100644 index 000000000000..e0cc1b48a1d8 --- /dev/null +++ b/src/language-js/print/rest-type.js @@ -0,0 +1,17 @@ +/* +- `TSRestType`(TypeScript) +- `TupleTypeSpreadElement`(flow) +*/ +function printRestType(path, options, print) { + const { node } = path; + + return [ + "...", + ...(node.type === "TupleTypeSpreadElement" && node.label + ? [print("label"), ": "] + : []), + print("typeAnnotation"), + ]; +} + +export { printRestType }; diff --git a/src/language-js/print/statement.js b/src/language-js/print/statement.js index 2107fce905a0..80331cf35df9 100644 --- a/src/language-js/print/statement.js +++ b/src/language-js/print/statement.js @@ -1,5 +1,5 @@ import { hardline } from "../../document/index.js"; -import { isNextLineEmpty } from "../utils/index.js"; +import { isNextLineEmpty } from "../utilities/index.js"; /** * @import {Doc} from "../../document/index.js" diff --git a/src/language-js/print/template-literal.js b/src/language-js/print/template-literal.js index 05566ef3b316..d6e483dbad3e 100644 --- a/src/language-js/print/template-literal.js +++ b/src/language-js/print/template-literal.js @@ -11,9 +11,9 @@ import { printDocToString, softline, } from "../../document/index.js"; -import getIndentSize from "../../utils/get-indent-size.js"; -import getStringWidth from "../../utils/get-string-width.js"; -import hasNewlineInRange from "../../utils/has-newline-in-range.js"; +import getIndentSize from "../../utilities/get-indent-size.js"; +import getStringWidth from "../../utilities/get-string-width.js"; +import hasNewlineInRange from "../../utilities/has-newline-in-range.js"; import { locEnd, locStart } from "../loc.js"; import { CommentCheckFlags, @@ -22,7 +22,7 @@ import { isBinaryCastExpression, isBinaryish, isMemberExpression, -} from "../utils/index.js"; +} from "../utilities/index.js"; /** * @import {Doc} from "../../document/index.js" diff --git a/src/language-js/print/ternary-old.js b/src/language-js/print/ternary-old.js index 2d9202dc2f3a..e5bb4464de02 100644 --- a/src/language-js/print/ternary-old.js +++ b/src/language-js/print/ternary-old.js @@ -12,7 +12,7 @@ import { isCallExpression, isJsxElement, isMemberExpression, -} from "../utils/index.js"; +} from "../utilities/index.js"; /** * @import {Doc} from "../../document/index.js" diff --git a/src/language-js/print/ternary.js b/src/language-js/print/ternary.js index 941aa494c6f3..b6ee65f5163b 100644 --- a/src/language-js/print/ternary.js +++ b/src/language-js/print/ternary.js @@ -9,9 +9,9 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewlineInRange from "../../utils/has-newline-in-range.js"; +import hasNewlineInRange from "../../utilities/has-newline-in-range.js"; import { locEnd, locStart } from "../loc.js"; -import pathNeedsParens from "../needs-parens.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; import { CommentCheckFlags, getComments, @@ -23,8 +23,8 @@ import { isLoneShortArgument, isMemberExpression, isSimpleExpressionByNodeCount, -} from "../utils/index.js"; -import isBlockComment from "../utils/is-block-comment.js"; +} from "../utilities/index.js"; +import isBlockComment from "../utilities/is-block-comment.js"; import { printTernaryOld } from "./ternary-old.js"; /** @@ -221,7 +221,8 @@ function printTernary(path, options, print, args) { const shouldExtraIndent = shouldExtraIndentForConditionalExpression(path); const breakClosingParen = shouldBreakClosingParen(node, parent); - const breakTSClosingParen = isTSConditional && pathNeedsParens(path, options); + const breakTSClosingParen = + isTSConditional && needsParentheses(path, options); const fillTab = !isBigTabs ? "" diff --git a/src/language-js/print/tuple.js b/src/language-js/print/tuple.js new file mode 100644 index 000000000000..db94a7dd828a --- /dev/null +++ b/src/language-js/print/tuple.js @@ -0,0 +1,18 @@ +/* +- `TSNamedTupleMember`(TypeScript) +- `TupleTypeLabeledElement`(flow) +*/ +function printNamedTupleMember(path, options, print) { + const { node } = path; + + return [ + // `TupleTypeLabeledElement` only + node.variance ? print("variance") : "", + print("label"), + node.optional ? "?" : "", + ": ", + print("elementType"), + ]; +} + +export { printNamedTupleMember }; diff --git a/src/language-js/print/type-alias.js b/src/language-js/print/type-alias.js new file mode 100644 index 000000000000..0ecdfbd36e1d --- /dev/null +++ b/src/language-js/print/type-alias.js @@ -0,0 +1,26 @@ +import { printAssignment } from "./assignment.js"; +import { printDeclareToken } from "./misc.js"; + +/* +- `DeclareTypeAlias`(flow) +- `TypeAlias`(flow) +- `TSTypeAliasDeclaration`(TypeScript) +*/ +function printTypeAlias(path, options, print) { + const { node } = path; + const parts = [ + printDeclareToken(path), + "type ", + print("id"), + print("typeParameters"), + ]; + + const rightPropertyName = + node.type === "TSTypeAliasDeclaration" ? "typeAnnotation" : "right"; + return [ + printAssignment(path, options, print, parts, " =", rightPropertyName), + options.semi ? ";" : "", + ]; +} + +export { printTypeAlias }; diff --git a/src/language-js/print/type-annotation.js b/src/language-js/print/type-annotation.js index 1f9eed09310c..d56a4e7f2a32 100644 --- a/src/language-js/print/type-annotation.js +++ b/src/language-js/print/type-annotation.js @@ -1,75 +1,16 @@ -import { - align, - group, - ifBreak, - indent, - join, - line, - softline, -} from "../../document/index.js"; -import { - printComments, - printCommentsSeparately, -} from "../../main/comments/print.js"; -import { hasSameLocStart } from "../loc.js"; -import pathNeedsParens from "../needs-parens.js"; import { CommentCheckFlags, - createTypeCheckFunction, hasComment, - hasLeadingOwnLineComment, - isConditionalType, - isFlowObjectTypePropertyAFunction, isObjectType, isSimpleType, - isTypeAlias, isUnionType, -} from "../utils/index.js"; -import { printAssignment } from "./assignment.js"; -import { printClassMemberSemicolon } from "./class.js"; -import { - printFunctionParameters, - shouldGroupFunctionParameters, -} from "./function-parameters.js"; -import { - printAbstractToken, - printDeclareToken, - printOptionalToken, -} from "./misc.js"; +} from "../utilities/index.js"; +import { shouldHugUnionType } from "./union-type.js"; /** * @import {Doc} from "../../document/index.js" */ -const isVoidType = createTypeCheckFunction([ - "VoidTypeAnnotation", - "TSVoidKeyword", - "NullLiteralTypeAnnotation", - "TSNullKeyword", -]); - -const isObjectLikeType = createTypeCheckFunction([ - "ObjectTypeAnnotation", - "TSTypeLiteral", - // This is a bit aggressive but captures Array<{x}> - "GenericTypeAnnotation", - "TSTypeReference", -]); - -function shouldHugUnionType(node) { - const { types } = node; - if (types.some((node) => hasComment(node))) { - return false; - } - - const objectType = types.find((node) => isObjectLikeType(node)); - if (!objectType) { - return false; - } - - return types.every((node) => node === objectType || isVoidType(node)); -} - function shouldHugType(node) { if (isSimpleType(node) || isObjectType(node)) { return true; @@ -82,353 +23,6 @@ function shouldHugType(node) { return false; } -/* -- `DeclareOpaqueType`(flow) -- `OpaqueType`(flow) -*/ -function printOpaqueType(path, options, print) { - const { node } = path; - const parts = [ - printDeclareToken(path), - "opaque type ", - print("id"), - print("typeParameters"), - ]; - - if (node.supertype) { - parts.push(": ", print("supertype")); - } - - if (node.lowerBound || node.upperBound) { - const lowerAndUpperBoundParts = []; - if (node.lowerBound) { - lowerAndUpperBoundParts.push( - indent([line, "super ", print("lowerBound")]), - ); - } - if (node.upperBound) { - lowerAndUpperBoundParts.push( - indent([line, "extends ", print("upperBound")]), - ); - } - parts.push(group(lowerAndUpperBoundParts)); - } - - if (node.impltype) { - parts.push(" = ", print("impltype")); - } - - parts.push(options.semi ? ";" : ""); - - return parts; -} - -/* -- `DeclareTypeAlias`(flow) -- `TypeAlias`(flow) -- `TSTypeAliasDeclaration`(TypeScript) -*/ -function printTypeAlias(path, options, print) { - const { node } = path; - const parts = [ - printDeclareToken(path), - "type ", - print("id"), - print("typeParameters"), - ]; - - const rightPropertyName = - node.type === "TSTypeAliasDeclaration" ? "typeAnnotation" : "right"; - return [ - printAssignment(path, options, print, parts, " =", rightPropertyName), - options.semi ? ";" : "", - ]; -} - -// `TSIntersectionType` and `IntersectionTypeAnnotation` -function printIntersectionType(path, options, print) { - let wasIndented = false; - return group( - path.map(({ isFirst, previous, node, index }) => { - const doc = print(); - if (isFirst) { - return doc; - } - - const currentIsObjectType = isObjectType(node); - const previousIsObjectType = isObjectType(previous); - - // If both are objects, don't indent - if (previousIsObjectType && currentIsObjectType) { - return [" & ", wasIndented ? indent(doc) : doc]; - } - - if ( - // If no object is involved, go to the next line if it breaks - (!previousIsObjectType && !currentIsObjectType) || - hasLeadingOwnLineComment(options.originalText, node) - ) { - if (options.experimentalOperatorPosition === "start") { - return indent([line, "& ", doc]); - } - return indent([" &", line, doc]); - } - - // If you go from object to non-object or vis-versa, then inline it - if (index > 1) { - wasIndented = true; - } - - return [" & ", index > 1 ? indent(doc) : doc]; - }, "types"), - ); -} - -// `TSUnionType` and `UnionTypeAnnotation` -function printUnionType(path, options, print) { - const { node } = path; - // single-line variation - // A | B | C - - // multi-line variation - // | A - // | B - // | C - - const { parent } = path; - - // If there's a leading comment, the parent is doing the indentation - const shouldIndent = - parent.type !== "TypeParameterInstantiation" && - (!isConditionalType(parent) || !options.experimentalTernaries) && - parent.type !== "TSTypeParameterInstantiation" && - parent.type !== "GenericTypeAnnotation" && - parent.type !== "TSTypeReference" && - parent.type !== "TSTypeAssertion" && - parent.type !== "TupleTypeAnnotation" && - parent.type !== "TSTupleType" && - !( - parent.type === "FunctionTypeParam" && - !parent.name && - path.grandparent.this !== parent - ) && - !( - (isTypeAlias(parent) || parent.type === "VariableDeclarator") && - hasLeadingOwnLineComment(options.originalText, node) - ) && - !( - isTypeAlias(parent) && - hasComment(parent.id, CommentCheckFlags.Trailing | CommentCheckFlags.Line) - ); - - // { - // a: string - // } | null | void - // should be inlined and not be printed in the multi-line variant - const shouldHug = shouldHugType(node); - - // We want to align the children but without its comment, so it looks like - // | child1 - // // comment - // | child2 - const printed = path.map(() => { - let printedType = print(); - if (!shouldHug) { - printedType = align(2, printedType); - } - - return printComments(path, printedType, options); - }, "types"); - - const { leading, trailing } = printCommentsSeparately(path, options); - - if (shouldHug) { - return [leading, join(" | ", printed), trailing]; - } - - const shouldAddStartLine = - shouldIndent && !hasLeadingOwnLineComment(options.originalText, node); - - const mainParts = [ - ifBreak([shouldAddStartLine ? line : "", "| "]), - join([line, "| "], printed), - ]; - - if (pathNeedsParens(path, options)) { - return [leading, group([indent(mainParts), softline]), trailing]; - } - - const parts = [leading, group(mainParts)]; - - if (parent.type === "TupleTypeAnnotation" || parent.type === "TSTupleType") { - const elementTypes = - parent[ - // TODO: Remove `types` when babel changes AST of `TupleTypeAnnotation` - parent.type === "TupleTypeAnnotation" && parent.types - ? "types" - : "elementTypes" - ]; - - if (elementTypes.length > 1) { - return [ - group([ - indent([ifBreak(["(", softline]), parts]), - softline, - ifBreak(")"), - ]), - trailing, - ]; - } - } - - return [group(shouldIndent ? indent(parts) : parts), trailing]; -} - -/* -`FunctionTypeAnnotation` is ambiguous: -- `declare function foo(a: B): void;` -- `var A: (a: B) => void;` -*/ -function isFlowArrowFunctionTypeAnnotation(path) { - const { node, parent } = path; - return ( - node.type === "FunctionTypeAnnotation" && - (isFlowObjectTypePropertyAFunction(parent) || - !( - ((parent.type === "ObjectTypeProperty" || - parent.type === "ObjectTypeInternalSlot") && - !parent.variance && - !parent.optional && - hasSameLocStart(parent, node)) || - parent.type === "ObjectTypeCallProperty" || - path.getParentNode(2)?.type === "DeclareFunction" - )) - ); -} - -/* -- `TSFunctionType` (TypeScript) -- `TSCallSignatureDeclaration` (TypeScript) -- `TSConstructorType` (TypeScript) -- `TSConstructSignatureDeclaration` (TypeScript) -- `FunctionTypeAnnotation` (Flow) -*/ -function printFunctionType(path, options, print) { - const { node } = path; - /** @type {Doc[]} */ - const parts = [ - // `TSConstructorType` only - printAbstractToken(path), - ]; - - if ( - node.type === "TSConstructorType" || - node.type === "TSConstructSignatureDeclaration" - ) { - parts.push("new "); - } - - let parametersDoc = printFunctionParameters( - path, - options, - print, - /* shouldExpandArgument */ false, - /* shouldPrintTypeParameters */ true, - ); - - const returnTypeDoc = []; - // `flow` doesn't wrap the `returnType` with `TypeAnnotation`, so the colon - // needs to be added separately. - if (node.type === "FunctionTypeAnnotation") { - returnTypeDoc.push( - isFlowArrowFunctionTypeAnnotation(path) ? " => " : ": ", - print("returnType"), - ); - } else { - returnTypeDoc.push(printTypeAnnotationProperty(path, print, "returnType")); - } - - if (shouldGroupFunctionParameters(node, returnTypeDoc)) { - parametersDoc = group(parametersDoc); - } - - parts.push(parametersDoc, returnTypeDoc); - - return [ - group(parts), - node.type === "TSConstructSignatureDeclaration" || - node.type === "TSCallSignatureDeclaration" - ? printClassMemberSemicolon(path, options) - : "", - ]; -} - -/* -- `TSIndexedAccessType`(TypeScript) -- `IndexedAccessType`(flow) -- `OptionalIndexedAccessType`(flow) -*/ -function printIndexedAccessType(path, options, print) { - return [ - print("objectType"), - printOptionalToken(path), - "[", - print("indexType"), - "]", - ]; -} - -/* -- `TSInferType`(TypeScript) -- `InferTypeAnnotation`(flow) -*/ -function printInferType(path, options, print) { - return ["infer ", print("typeParameter")]; -} - -// `TSJSDocNullableType`, `TSJSDocNonNullableType` -function printJSDocType(path, print, token) { - const { node } = path; - return [ - node.postfix ? "" : token, - printTypeAnnotationProperty(path, print), - node.postfix ? token : "", - ]; -} - -/* -- `TSRestType`(TypeScript) -- `TupleTypeSpreadElement`(flow) -*/ -function printRestType(path, options, print) { - const { node } = path; - - return [ - "...", - ...(node.type === "TupleTypeSpreadElement" && node.label - ? [print("label"), ": "] - : []), - print("typeAnnotation"), - ]; -} - -/* -- `TSNamedTupleMember`(TypeScript) -- `TupleTypeLabeledElement`(flow) -*/ -function printNamedTupleMember(path, options, print) { - const { node } = path; - - return [ - // `TupleTypeLabeledElement` only - node.variance ? print("variance") : "", - print("label"), - node.optional ? "?" : "", - ": ", - print("elementType"), - ]; -} - /* Normally the `(TS)TypeAnnotation` node starts with `:` token. If we print `:` in parent node, `cursorNodeDiff` in `/src/main/core.js` will consider `:` is removed, cause cursor moves, see #12491. @@ -570,60 +164,4 @@ function printTypeAnnotation(path, options, print) { : print("typeAnnotation"); } -/* -- `TSArrayType` -- `ArrayTypeAnnotation` -*/ -function printArrayType(print) { - return [print("elementType"), "[]"]; -} - -/* -- `TSTypeQuery` (TypeScript) -- `TypeofTypeAnnotation` (flow) -*/ -function printTypeQuery({ node }, print) { - const argumentPropertyName = - node.type === "TSTypeQuery" ? "exprName" : "argument"; - return ["typeof ", print(argumentPropertyName), print("typeArguments")]; -} - -/* -- `TSTypePredicate` (TypeScript) -- `TypePredicate` (flow) -*/ -function printTypePredicate(path, print) { - const { node } = path; - const prefix = - node.type === "TSTypePredicate" && node.asserts - ? "asserts " - : node.type === "TypePredicate" && node.kind - ? `${node.kind} ` - : ""; - return [ - prefix, - print("parameterName"), - node.typeAnnotation - ? [" is ", printTypeAnnotationProperty(path, print)] - : "", - ]; -} - -export { - printArrayType, - printFunctionType, - printIndexedAccessType, - printInferType, - printIntersectionType, - printJSDocType, - printNamedTupleMember, - printOpaqueType, - printRestType, - printTypeAlias, - printTypeAnnotation, - printTypeAnnotationProperty, - printTypePredicate, - printTypeQuery, - printUnionType, - shouldHugType, -}; +export { printTypeAnnotation, printTypeAnnotationProperty, shouldHugType }; diff --git a/src/language-js/print/type-assertion.js b/src/language-js/print/type-assertion.js new file mode 100644 index 000000000000..d7c09b87a236 --- /dev/null +++ b/src/language-js/print/type-assertion.js @@ -0,0 +1,40 @@ +import { + conditionalGroup, + group, + ifBreak, + indent, + softline, +} from "../../document/index.js"; +import { isArrayExpression, isObjectExpression } from "../utilities/index.js"; + +function printTypeAssertion(path, options, print) { + const { node } = path; + const shouldBreakAfterCast = !( + isArrayExpression(node.expression) || isObjectExpression(node.expression) + ); + + const castGroup = group([ + "<", + indent([softline, print("typeAnnotation")]), + softline, + ">", + ]); + + const exprContents = [ + ifBreak("("), + indent([softline, print("expression")]), + softline, + ifBreak(")"), + ]; + + if (shouldBreakAfterCast) { + return conditionalGroup([ + [castGroup, print("expression")], + [castGroup, group(exprContents, { shouldBreak: true })], + [castGroup, print("expression")], + ]); + } + return group([castGroup, print("expression")]); +} + +export { printTypeAssertion }; diff --git a/src/language-js/print/type-parameters.js b/src/language-js/print/type-parameters.js index cf55297beb42..19a8aa0fd1a1 100644 --- a/src/language-js/print/type-parameters.js +++ b/src/language-js/print/type-parameters.js @@ -17,7 +17,7 @@ import { isObjectType, isTestCall, shouldPrintComma, -} from "../utils/index.js"; +} from "../utilities/index.js"; import { isArrowFunctionVariableDeclarator } from "./assignment.js"; import { printTypeAnnotationProperty, diff --git a/src/language-js/print/type-predicate.js b/src/language-js/print/type-predicate.js new file mode 100644 index 000000000000..d58cbf506063 --- /dev/null +++ b/src/language-js/print/type-predicate.js @@ -0,0 +1,24 @@ +import { printTypeAnnotationProperty } from "./type-annotation.js"; + +/* +- `TSTypePredicate` (TypeScript) +- `TypePredicate` (flow) +*/ +function printTypePredicate(path, print) { + const { node } = path; + const prefix = + node.type === "TSTypePredicate" && node.asserts + ? "asserts " + : node.type === "TypePredicate" && node.kind + ? `${node.kind} ` + : ""; + return [ + prefix, + print("parameterName"), + node.typeAnnotation + ? [" is ", printTypeAnnotationProperty(path, print)] + : "", + ]; +} + +export { printTypePredicate }; diff --git a/src/language-js/print/type-query.js b/src/language-js/print/type-query.js new file mode 100644 index 000000000000..95b9e80b9d42 --- /dev/null +++ b/src/language-js/print/type-query.js @@ -0,0 +1,11 @@ +/* +- `TSTypeQuery` (TypeScript) +- `TypeofTypeAnnotation` (flow) +*/ +function printTypeQuery({ node }, print) { + const argumentPropertyName = + node.type === "TSTypeQuery" ? "exprName" : "argument"; + return ["typeof ", print(argumentPropertyName), print("typeArguments")]; +} + +export { printTypeQuery }; diff --git a/src/language-js/print/typescript.js b/src/language-js/print/typescript.js index 34ac271cd061..b1135d2f872a 100644 --- a/src/language-js/print/typescript.js +++ b/src/language-js/print/typescript.js @@ -1,19 +1,7 @@ -import { - conditionalGroup, - group, - ifBreak, - indent, - join, - softline, -} from "../../document/index.js"; -import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; -import { - isArrayExpression, - isObjectExpression, - shouldPrintComma, -} from "../utils/index.js"; -import isTsKeywordType from "../utils/is-ts-keyword-type.js"; +import UnexpectedNodeError from "../../utilities/unexpected-node-error.js"; +import isTsKeywordType from "../utilities/is-ts-keyword-type.js"; import { printArray } from "./array.js"; +import { printArrayType } from "./array-type.js"; import { printBlock } from "./block.js"; import { printCallExpression } from "./call-expression.js"; import { printBinaryCastExpression } from "./cast-expression.js"; @@ -30,37 +18,35 @@ import { printEnumMember, } from "./enum.js"; import { printFunction, printMethodValue } from "./function.js"; -import { - printFunctionParameters, - shouldGroupFunctionParameters, -} from "./function-parameters.js"; +import { printFunctionType } from "./function-type.js"; +import { printIndexSignature } from "./index-signature.js"; +import { printIndexedAccessType } from "./indexed-access-type.js"; +import { printInferType } from "./infer-type.js"; +import { printIntersectionType } from "./intersection-type.js"; +import { printJSDocType } from "./js-doc-type.js"; import { printTypeScriptMappedType } from "./mapped-type.js"; +import { printMethodSignature } from "./method-signature.js"; import { - printDeclareToken, printOptionalToken, printTypeScriptAccessibilityToken, } from "./misc.js"; import { printImportKind } from "./module.js"; +import { printModuleDeclaration } from "./module-declaration.js"; import { printPropertyKey } from "./property.js"; +import { printRestType } from "./rest-type.js"; import { printTemplateLiteral } from "./template-literal.js"; import { printTernary } from "./ternary.js"; +import { printNamedTupleMember } from "./tuple.js"; +import { printTypeAlias } from "./type-alias.js"; import { - printArrayType, - printFunctionType, - printIndexedAccessType, - printInferType, - printIntersectionType, - printJSDocType, - printNamedTupleMember, - printRestType, - printTypeAlias, printTypeAnnotation, printTypeAnnotationProperty, - printTypePredicate, - printTypeQuery, - printUnionType, } from "./type-annotation.js"; +import { printTypeAssertion } from "./type-assertion.js"; import { printTypeParameter, printTypeParameters } from "./type-parameters.js"; +import { printTypePredicate } from "./type-predicate.js"; +import { printTypeQuery } from "./type-query.js"; +import { printUnionType } from "./union-type.js"; function printTypescript(path, options, print) { const { node } = path; @@ -78,35 +64,9 @@ function printTypescript(path, options, print) { switch (node.type) { case "TSThisType": return "this"; - case "TSTypeAssertion": { - const shouldBreakAfterCast = !( - isArrayExpression(node.expression) || - isObjectExpression(node.expression) - ); - - const castGroup = group([ - "<", - indent([softline, print("typeAnnotation")]), - softline, - ">", - ]); - - const exprContents = [ - ifBreak("("), - indent([softline, print("expression")]), - softline, - ifBreak(")"), - ]; + case "TSTypeAssertion": + return printTypeAssertion(path, options, print); - if (shouldBreakAfterCast) { - return conditionalGroup([ - [castGroup, print("expression")], - [castGroup, group(exprContents, { shouldBreak: true })], - [castGroup, print("expression")], - ]); - } - return group([castGroup, print("expression")]); - } case "TSDeclareFunction": return printFunction(path, options, print); case "TSExportAssignment": @@ -171,39 +131,9 @@ function printTypescript(path, options, print) { case "TSTypeQuery": return printTypeQuery(path, print); - case "TSIndexSignature": { - // The typescript parser accepts multiple parameters here. If you're - // using them, it makes sense to have a trailing comma. But if you - // aren't, this is more like a computed property name than an array. - // So we leave off the trailing comma when there's just one parameter. - const trailingComma = - node.parameters.length > 1 - ? ifBreak(shouldPrintComma(options) ? "," : "") - : ""; + case "TSIndexSignature": + return printIndexSignature(path, options, print); - const parametersGroup = group([ - indent([ - softline, - join([", ", softline], path.map(print, "parameters")), - ]), - trailingComma, - softline, - ]); - - const isClassMember = - path.key === "body" && path.parent.type === "ClassBody"; - - return [ - // `static` only allowed in class member - isClassMember && node.static ? "static " : "", - node.readonly ? "readonly " : "", - "[", - node.parameters ? parametersGroup : "", - "]", - printTypeAnnotationProperty(path, print), - printClassMemberSemicolon(path, options), - ]; - } case "TSTypePredicate": return printTypePredicate(path, print); case "TSNonNullExpression": @@ -225,44 +155,9 @@ function printTypescript(path, options, print) { case "TSMappedType": return printTypeScriptMappedType(path, options, print); - case "TSMethodSignature": { - const parts = []; - const kind = node.kind && node.kind !== "method" ? `${node.kind} ` : ""; - parts.push( - printTypeScriptAccessibilityToken(node), - kind, - node.computed ? "[" : "", - print("key"), - node.computed ? "]" : "", - printOptionalToken(path), - ); - - const parametersDoc = printFunctionParameters( - path, - options, - print, - /* shouldExpandArgument */ false, - /* shouldPrintTypeParameters */ true, - ); - - const returnTypeDoc = printTypeAnnotationProperty( - path, - print, - "returnType", - ); - const shouldGroupParameters = shouldGroupFunctionParameters( - node, - returnTypeDoc, - ); + case "TSMethodSignature": + return printMethodSignature(path, options, print); - parts.push(shouldGroupParameters ? group(parametersDoc) : parametersDoc); - - if (node.returnType) { - parts.push(group(returnTypeDoc)); - } - - return [group(parts), printClassMemberSemicolon(path, options)]; - } case "TSNamespaceExportDeclaration": return ["export as namespace ", print("id"), options.semi ? ";" : ""]; case "TSEnumDeclaration": @@ -284,12 +179,7 @@ function printTypescript(path, options, print) { case "TSExternalModuleReference": return printCallExpression(path, options, print); case "TSModuleDeclaration": - return [ - printDeclareToken(path), - node.kind === "global" ? "" : `${node.kind} `, - print("id"), - node.body ? [" ", group(print("body"))] : options.semi ? ";" : "", - ]; + return printModuleDeclaration(path, options, print); case "TSConditionalType": return printTernary(path, options, print); diff --git a/src/language-js/print/union-type.js b/src/language-js/print/union-type.js new file mode 100644 index 000000000000..4ab95f90de94 --- /dev/null +++ b/src/language-js/print/union-type.js @@ -0,0 +1,164 @@ +import { + align, + group, + ifBreak, + indent, + join, + line, + softline, +} from "../../document/index.js"; +import { + printComments, + printCommentsSeparately, +} from "../../main/comments/print.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; +import { + CommentCheckFlags, + createTypeCheckFunction, + hasComment, + hasLeadingOwnLineComment, + isConditionalType, + isTypeAlias, + shouldUnionTypePrintOwnComments, +} from "../utilities/index.js"; + +/** +@import {Doc} from "../../document/index.js"; +*/ + +// `TSUnionType` and `UnionTypeAnnotation` +function printUnionType(path, options, print) { + const { node } = path; + // single-line variation + // A | B | C + + // multi-line variation + // | A + // | B + // | C + + const { parent } = path; + + // If there's a leading comment, the parent is doing the indentation + const shouldIndent = + parent.type !== "TypeParameterInstantiation" && + (!isConditionalType(parent) || !options.experimentalTernaries) && + parent.type !== "TSTypeParameterInstantiation" && + parent.type !== "GenericTypeAnnotation" && + parent.type !== "TSTypeReference" && + parent.type !== "TSTypeAssertion" && + parent.type !== "TupleTypeAnnotation" && + parent.type !== "TSTupleType" && + !( + parent.type === "FunctionTypeParam" && + !parent.name && + path.grandparent.this !== parent + ) && + !( + (isTypeAlias(parent) || parent.type === "VariableDeclarator") && + hasLeadingOwnLineComment(options.originalText, node) + ) && + !( + isTypeAlias(parent) && + hasComment(parent.id, CommentCheckFlags.Trailing | CommentCheckFlags.Line) + ); + + // { + // a: string + // } | null | void + // should be inlined and not be printed in the multi-line variant + const shouldHug = shouldHugUnionType(node); + + // We want to align the children but without its comment, so it looks like + // | child1 + // // comment + // | child2 + const printed = path.map(() => { + let printedType = print(); + if (!shouldHug) { + printedType = align(2, printedType); + } + + return printComments(path, printedType, options); + }, "types"); + + /** @type {Doc} */ + let leading = ""; + /** @type {Doc} */ + let trailing = ""; + if (shouldUnionTypePrintOwnComments(path)) { + ({ leading, trailing } = printCommentsSeparately(path, options)); + } + + if (shouldHug) { + return [leading, join(" | ", printed), trailing]; + } + + const shouldAddStartLine = + shouldIndent && !hasLeadingOwnLineComment(options.originalText, node); + + const mainParts = [ + ifBreak([shouldAddStartLine ? line : "", "| "]), + join([line, "| "], printed), + ]; + + if (needsParentheses(path, options)) { + return [leading, group([indent(mainParts), softline]), trailing]; + } + + const parts = [leading, group(mainParts)]; + + if (parent.type === "TupleTypeAnnotation" || parent.type === "TSTupleType") { + const elementTypes = + parent[ + // TODO: Remove `types` when babel changes AST of `TupleTypeAnnotation` + parent.type === "TupleTypeAnnotation" && parent.types + ? "types" + : "elementTypes" + ]; + + if (elementTypes.length > 1) { + return [ + group([ + indent([ifBreak(["(", softline]), parts]), + softline, + ifBreak(")"), + ]), + trailing, + ]; + } + } + + return [group(shouldIndent ? indent(parts) : parts), trailing]; +} + +const isVoidType = createTypeCheckFunction([ + "VoidTypeAnnotation", + "TSVoidKeyword", + "NullLiteralTypeAnnotation", + "TSNullKeyword", +]); + +const isObjectLikeType = createTypeCheckFunction([ + "ObjectTypeAnnotation", + "TSTypeLiteral", + // This is a bit aggressive but captures Array<{x}> + "GenericTypeAnnotation", + "TSTypeReference", +]); + +function shouldHugUnionType(node) { + const { types } = node; + if (types.some((node) => hasComment(node))) { + return false; + } + + const objectType = types.find((node) => isObjectLikeType(node)); + if (!objectType) { + return false; + } + + return types.every((node) => node === objectType || isVoidType(node)); +} + +export { printUnionType, shouldHugUnionType }; diff --git a/src/language-js/printer.js b/src/language-js/printer.js index c7839136febc..27cd50d80090 100644 --- a/src/language-js/printer.js +++ b/src/language-js/printer.js @@ -3,19 +3,17 @@ export const features = { experimental_avoidAstMutation: true, }; export { default as massageAstNode } from "./clean.js"; -export { - canAttachComment, - handleComments, - isBlockComment, - isGap, - printComment, - willPrintOwnComments, -} from "./comments/printer-methods.js"; +export { default as canAttachComment } from "./comments/can-attach-comment.js"; +export { default as handleComments } from "./comments/handle-comments.js"; +export { default as isGap } from "./comments/is-gap.js"; +export { default as willPrintOwnComments } from "./comments/will-print-own-comments.js"; export { default as embed } from "./embed/index.js"; export { insertPragma } from "./pragma.js"; +export { printComment } from "./print/comment.js"; export { default as print, default as printPrettierIgnored, } from "./print/index.js"; export { default as getVisitorKeys } from "./traverse/get-visitor-keys.js"; -export { default as hasPrettierIgnore } from "./utils/is-ignored.js"; +export { default as isBlockComment } from "./utilities/is-block-comment.js"; +export { default as hasPrettierIgnore } from "./utilities/is-ignored.js"; diff --git a/src/language-js/print/semicolon.js b/src/language-js/semicolon/semicolon.js similarity index 93% rename from src/language-js/print/semicolon.js rename to src/language-js/semicolon/semicolon.js index c7a932696817..b6c488035fff 100644 --- a/src/language-js/print/semicolon.js +++ b/src/language-js/semicolon/semicolon.js @@ -1,10 +1,10 @@ -import pathNeedsParens from "../needs-parens.js"; +import needsParentheses from "../parentheses/needs-parentheses.js"; +import { shouldPrintParamsWithoutParens } from "../print/function.js"; import { getLeftSidePathName, hasNakedLeftSide, isJsxElement, -} from "../utils/index.js"; -import { shouldPrintParamsWithoutParens } from "./function.js"; +} from "../utilities/index.js"; function shouldPrintLeadingSemicolon(path, options) { if ( @@ -76,7 +76,7 @@ function expressionNeedsASIProtection(path, options) { } } - if (pathNeedsParens(path, options)) { + if (needsParentheses(path, options)) { return true; } diff --git a/src/language-js/traverse/get-visitor-keys.js b/src/language-js/traverse/get-visitor-keys.js index 96b5dbfd0453..b9bdd2fc4c34 100644 --- a/src/language-js/traverse/get-visitor-keys.js +++ b/src/language-js/traverse/get-visitor-keys.js @@ -1,4 +1,4 @@ -import createGetVisitorKeys from "../../utils/create-get-visitor-keys.js"; +import createGetVisitorKeys from "../../utilities/create-get-visitor-keys.js"; import visitorKeys from "./visitor-keys.evaluate.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys); diff --git a/src/language-js/traverse/visitor-keys.evaluate.js b/src/language-js/traverse/visitor-keys.evaluate.js index 754d23cba63a..e2c40be3ae7a 100644 --- a/src/language-js/traverse/visitor-keys.evaluate.js +++ b/src/language-js/traverse/visitor-keys.evaluate.js @@ -3,10 +3,11 @@ import { visitorKeys as tsVisitorKeys } from "@typescript-eslint/visitor-keys"; import { visitorKeys as angularVisitorKeys } from "angular-estree-parser"; import flowVisitorKeys from "hermes-parser/dist/generated/ESTreeVisitorKeys.js"; import { + generateReferenceSharedVisitorKeys, removeNodeTypes, removeVisitorKeys, unionVisitorKeys, -} from "./utilities.js"; +} from "../../utilities/visitor-keys.js"; const additionalVisitorKeys = { // Prettier @@ -70,4 +71,7 @@ let visitorKeys = unionVisitorKeys( visitorKeys = removeNodeTypes(visitorKeys, excludeNodeTypes); visitorKeys = removeVisitorKeys(visitorKeys, excludeVisitorKeys); +// This should be the last step +visitorKeys = generateReferenceSharedVisitorKeys(visitorKeys); + export default visitorKeys; diff --git a/src/language-js/utils/create-type-check-function.js b/src/language-js/utilities/create-type-check-function.js similarity index 100% rename from src/language-js/utils/create-type-check-function.js rename to src/language-js/utilities/create-type-check-function.js diff --git a/src/language-js/utils/get-raw.js b/src/language-js/utilities/get-raw.js similarity index 100% rename from src/language-js/utils/get-raw.js rename to src/language-js/utilities/get-raw.js diff --git a/src/language-js/utils/get-shebang.js b/src/language-js/utilities/get-shebang.js similarity index 100% rename from src/language-js/utils/get-shebang.js rename to src/language-js/utilities/get-shebang.js diff --git a/src/language-js/utils/get-text-without-comments.js b/src/language-js/utilities/get-text-without-comments.js similarity index 100% rename from src/language-js/utils/get-text-without-comments.js rename to src/language-js/utilities/get-text-without-comments.js diff --git a/src/language-js/utils/index.js b/src/language-js/utilities/index.js similarity index 96% rename from src/language-js/utils/index.js rename to src/language-js/utilities/index.js index a57eada6985e..386bd446660f 100644 --- a/src/language-js/utils/index.js +++ b/src/language-js/utilities/index.js @@ -1,9 +1,10 @@ -import { hasDescendant } from "../../utils/ast-utils.js"; -import getStringWidth from "../../utils/get-string-width.js"; -import hasNewline from "../../utils/has-newline.js"; -import isNextLineEmptyAfterIndex from "../../utils/is-next-line-empty.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; -import printString from "../../utils/print-string.js"; +import { hasDescendant } from "../../utilities/ast.js"; +import getStringWidth from "../../utilities/get-string-width.js"; +import hasNewline from "../../utilities/has-newline.js"; +import isNextLineEmptyAfterIndex from "../../utilities/is-next-line-empty.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; +import isObject from "../../utilities/is-object.js"; +import printString from "../../utilities/print-string.js"; import { hasSameLocStart, locEnd, locStart } from "../loc.js"; import getVisitorKeys from "../traverse/get-visitor-keys.js"; import createTypeCheckFunction from "./create-type-check-function.js"; @@ -417,7 +418,7 @@ function getExpressionInnerNodeCount(node, maxCount) { for (const k in node) { const prop = node[k]; - if (prop && typeof prop === "object" && typeof prop.type === "string") { + if (isObject(prop) && typeof prop.type === "string") { count++; count += getExpressionInnerNodeCount(prop, maxCount - count); } @@ -1084,6 +1085,19 @@ const isTypeAlias = createTypeCheckFunction([ "TypeAlias", ]); +function shouldUnionTypePrintOwnComments({ key, parent }) { + if ( + // If it's `types` of union type, parent will print comment for it + (key === "types" && isUnionType(parent)) || + // Inside intersection type let the comment print outside of parentheses + (key === "types" && isIntersectionType(parent)) + ) { + return false; + } + + return true; +} + export { CommentCheckFlags, createTypeCheckFunction, @@ -1144,6 +1158,7 @@ export { needsHardlineAfterDanglingComment, shouldFlatten, shouldPrintComma, + shouldUnionTypePrintOwnComments, startsWithNoLookaheadToken, }; export { default as isMeaningfulEmptyStatement } from "./is-meaningful-empty-statement.js"; diff --git a/src/language-js/utils/is-block-comment.js b/src/language-js/utilities/is-block-comment.js similarity index 100% rename from src/language-js/utils/is-block-comment.js rename to src/language-js/utilities/is-block-comment.js diff --git a/src/language-js/utils/is-flow-keyword-type.js b/src/language-js/utilities/is-flow-keyword-type.js similarity index 100% rename from src/language-js/utils/is-flow-keyword-type.js rename to src/language-js/utilities/is-flow-keyword-type.js diff --git a/src/language-js/utils/is-ignored.js b/src/language-js/utilities/is-ignored.js similarity index 75% rename from src/language-js/utils/is-ignored.js rename to src/language-js/utilities/is-ignored.js index 13cdbe11f842..a2ec0857ee52 100644 --- a/src/language-js/utils/is-ignored.js +++ b/src/language-js/utilities/is-ignored.js @@ -1,5 +1,5 @@ import { hasJsxIgnoreComment } from "../print/jsx.js"; -import { hasNodeIgnoreComment } from "../utils/index.js"; +import { hasNodeIgnoreComment } from "./index.js"; function isIgnored(path) { return hasNodeIgnoreComment(path.node) || hasJsxIgnoreComment(path); diff --git a/src/language-js/utils/is-indentable-block-comment.js b/src/language-js/utilities/is-indentable-block-comment.js similarity index 100% rename from src/language-js/utils/is-indentable-block-comment.js rename to src/language-js/utilities/is-indentable-block-comment.js diff --git a/src/language-js/utils/is-line-comment.js b/src/language-js/utilities/is-line-comment.js similarity index 100% rename from src/language-js/utils/is-line-comment.js rename to src/language-js/utilities/is-line-comment.js diff --git a/src/language-js/utils/is-meaningful-empty-statement.js b/src/language-js/utilities/is-meaningful-empty-statement.js similarity index 100% rename from src/language-js/utils/is-meaningful-empty-statement.js rename to src/language-js/utilities/is-meaningful-empty-statement.js diff --git a/src/language-js/utils/is-node-matches.js b/src/language-js/utilities/is-node-matches.js similarity index 94% rename from src/language-js/utils/is-node-matches.js rename to src/language-js/utilities/is-node-matches.js index 91ca793a2033..79d62f361b98 100644 --- a/src/language-js/utils/is-node-matches.js +++ b/src/language-js/utilities/is-node-matches.js @@ -1,4 +1,4 @@ -// Copied from https://github.com/sindresorhus/eslint-plugin-unicorn/blob/d53d935951aa815c763fc9441aa452c763294715/rules/utils/is-node-matches.js +// Copied from https://github.com/sindresorhus/eslint-plugin-unicorn/blob/d53d935951aa815c763fc9441aa452c763294715/rules/utilities/is-node-matches.js /** * @import {Node} from "../types/estree.js" diff --git a/src/language-js/utils/is-ts-keyword-type.js b/src/language-js/utilities/is-ts-keyword-type.js similarity index 100% rename from src/language-js/utils/is-ts-keyword-type.js rename to src/language-js/utilities/is-ts-keyword-type.js diff --git a/src/language-js/utils/is-type-cast-comment.js b/src/language-js/utilities/is-type-cast-comment.js similarity index 100% rename from src/language-js/utils/is-type-cast-comment.js rename to src/language-js/utilities/is-type-cast-comment.js diff --git a/src/language-js/utils/vue-event-binding.js b/src/language-js/utilities/vue-event-binding.js similarity index 100% rename from src/language-js/utils/vue-event-binding.js rename to src/language-js/utilities/vue-event-binding.js diff --git a/src/language-json/get-visitor-keys.js b/src/language-json/get-visitor-keys.js index d58d77cf3bd3..f4d75eb174a8 100644 --- a/src/language-json/get-visitor-keys.js +++ b/src/language-json/get-visitor-keys.js @@ -1,5 +1,5 @@ -import createGetVisitorKeys from "../utils/create-get-visitor-keys.js"; -import visitorKeys from "./visitor-keys.js"; +import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js"; +import visitorKeys from "./visitor-keys.evaluate.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys); diff --git a/src/language-json/languages.evaluate.js b/src/language-json/languages.evaluate.js index 949b686a3c24..d2cc41275c34 100644 --- a/src/language-json/languages.evaluate.js +++ b/src/language-json/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; // A list of files that are commonly generated by tools and should not be // formatted by the default JSON parser to avoid unwanted diffs. diff --git a/src/language-json/parser-json.js b/src/language-json/parser-json.js index 2746b5f7e5d2..9f3247b14adc 100644 --- a/src/language-json/parser-json.js +++ b/src/language-json/parser-json.js @@ -1,9 +1,9 @@ import { parse, parseExpression } from "@babel/parser"; import createError from "../common/parser-create-error.js"; -import createBabelParseError from "../language-js/parse/utils/create-babel-parse-error.js"; -import createParser from "../language-js/parse/utils/create-parser.js"; -import wrapBabelExpression from "../language-js/parse/utils/wrap-babel-expression.js"; -import isNonEmptyArray from "../utils/is-non-empty-array.js"; +import createBabelParseError from "../language-js/parse/utilities/create-babel-parse-error.js"; +import createParser from "../language-js/parse/utilities/create-parser.js"; +import wrapBabelExpression from "../language-js/parse/utilities/wrap-babel-expression.js"; +import isNonEmptyArray from "../utilities/is-non-empty-array.js"; const babelParseOptions = { tokens: false, diff --git a/src/language-json/printer-estree-json.js b/src/language-json/printer-estree-json.js index 42a40c524889..dd132aa436dc 100644 --- a/src/language-json/printer-estree-json.js +++ b/src/language-json/printer-estree-json.js @@ -1,5 +1,5 @@ import { hardline, indent, join } from "../document/index.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; +import UnexpectedNodeError from "../utilities/unexpected-node-error.js"; function genericPrint(path, options, print) { const { node } = path; diff --git a/src/language-json/visitor-keys.js b/src/language-json/visitor-keys.evaluate.js similarity index 70% rename from src/language-json/visitor-keys.js rename to src/language-json/visitor-keys.evaluate.js index bb13cbc10144..cd913da24591 100644 --- a/src/language-json/visitor-keys.js +++ b/src/language-json/visitor-keys.evaluate.js @@ -1,4 +1,6 @@ -const visitorKeys = { +import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js"; + +const visitorKeys = generateReferenceSharedVisitorKeys({ JsonRoot: ["node"], ArrayExpression: ["elements"], ObjectExpression: ["properties"], @@ -11,6 +13,6 @@ const visitorKeys = { Identifier: [], TemplateLiteral: ["quasis"], TemplateElement: [], -}; +}); export default visitorKeys; diff --git a/src/language-markdown/embed.js b/src/language-markdown/embed.js index 0ff92ed74e63..b00b7903b750 100644 --- a/src/language-markdown/embed.js +++ b/src/language-markdown/embed.js @@ -1,7 +1,7 @@ import { hardline, markAsRoot, replaceEndOfLine } from "../document/index.js"; -import getMaxContinuousCount from "../utils/get-max-continuous-count.js"; -import inferParser from "../utils/infer-parser.js"; -import { getFencedCodeBlockValue } from "./utils.js"; +import getMaxContinuousCount from "../utilities/get-max-continuous-count.js"; +import inferParser from "../utilities/infer-parser.js"; +import { getFencedCodeBlockValue } from "./utilities.js"; function embed(path, options) { const { node } = path; diff --git a/src/language-markdown/get-visitor-keys.js b/src/language-markdown/get-visitor-keys.js index d58d77cf3bd3..f4d75eb174a8 100644 --- a/src/language-markdown/get-visitor-keys.js +++ b/src/language-markdown/get-visitor-keys.js @@ -1,5 +1,5 @@ -import createGetVisitorKeys from "../utils/create-get-visitor-keys.js"; -import visitorKeys from "./visitor-keys.js"; +import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js"; +import visitorKeys from "./visitor-keys.evaluate.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys); diff --git a/src/language-markdown/languages.evaluate.js b/src/language-markdown/languages.evaluate.js index 15fddbcea3ee..e37bd3be28f9 100644 --- a/src/language-markdown/languages.evaluate.js +++ b/src/language-markdown/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; const languages = [ createLanguage(linguistLanguages.Markdown, (data) => ({ diff --git a/src/language-markdown/pragma.js b/src/language-markdown/pragma.js index 10785416d343..c9509494c849 100644 --- a/src/language-markdown/pragma.js +++ b/src/language-markdown/pragma.js @@ -3,7 +3,7 @@ import { FORMAT_PRAGMA_TO_INSERT, MARKDOWN_HAS_IGNORE_PRAGMA_REGEXP, MARKDOWN_HAS_PRAGMA_REGEXP, -} from "../utils/pragma/pragma.evaluate.js"; +} from "../utilities/pragma/pragma.evaluate.js"; const hasPragma = (text) => parseFrontMatter(text).content.trimStart().match(MARKDOWN_HAS_PRAGMA_REGEXP) diff --git a/src/language-markdown/print-preprocess.js b/src/language-markdown/print-preprocess.js index 521a91dece45..4bfa85d0deb3 100644 --- a/src/language-markdown/print-preprocess.js +++ b/src/language-markdown/print-preprocess.js @@ -1,5 +1,5 @@ -import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js"; -import { getOrderedListItemInfo, mapAst, splitText } from "./utils.js"; +import htmlWhitespace from "../utilities/html-whitespace.js"; +import { getOrderedListItemInfo, mapAst, splitText } from "./utilities.js"; // 0x0 ~ 0x10ffff const isSingleCharRegex = /^\\?.$/su; @@ -97,10 +97,10 @@ function splitTextIntoSentences(ast) { // CommonMark doesn't remove trailing/leading \f, but it should be // removed in the HTML rendering process if (index === 0) { - value = htmlWhitespaceUtils.trimStart(value); + value = htmlWhitespace.trimStart(value); } if (index === parentNode.children.length - 1) { - value = htmlWhitespaceUtils.trimEnd(value); + value = htmlWhitespace.trimEnd(value); } } diff --git a/src/language-markdown/print-whitespace.js b/src/language-markdown/print-whitespace.js index 3713b67f4eab..1f01f46e69c4 100644 --- a/src/language-markdown/print-whitespace.js +++ b/src/language-markdown/print-whitespace.js @@ -4,10 +4,10 @@ import { KIND_CJK_PUNCTUATION, KIND_K_LETTER, KIND_NON_CJK, -} from "./utils.js"; +} from "./utilities.js"; /** - * @import {WordNode, WhitespaceValue, WordKind} from "./utils.js" + * @import {WordNode, WhitespaceValue, WordKind} from "./utilities.js" * @import AstPath from "../common/ast-path.js" * @typedef {"always" | "never" | "preserve"} ProseWrap * @typedef {{ next?: WordNode | null, previous?: WordNode | null }} diff --git a/src/language-markdown/print/children.js b/src/language-markdown/print/children.js index 8980a9c1a7cb..daaa5e0a326f 100644 --- a/src/language-markdown/print/children.js +++ b/src/language-markdown/print/children.js @@ -3,7 +3,7 @@ import { INLINE_NODE_TYPES, INLINE_NODE_WRAPPER_TYPES, isPrettierIgnore, -} from "../utils.js"; +} from "../utilities.js"; /** * @import AstPath from "../../common/ast-path.js" diff --git a/src/language-markdown/print/list.js b/src/language-markdown/print/list.js index 5f9ee860a114..86ae1e66319a 100644 --- a/src/language-markdown/print/list.js +++ b/src/language-markdown/print/list.js @@ -2,7 +2,7 @@ import { align } from "../../document/index.js"; import { getNthListSiblingIndex, hasGitDiffFriendlyOrderedList, -} from "../utils.js"; +} from "../utilities.js"; import { printChildren } from "./children.js"; /** diff --git a/src/language-markdown/print/table.js b/src/language-markdown/print/table.js index c69218f7f9c0..9f1165199d3a 100644 --- a/src/language-markdown/print/table.js +++ b/src/language-markdown/print/table.js @@ -6,7 +6,7 @@ import { join, } from "../../document/index.js"; import { printDocToString } from "../../document/printer/printer.js"; -import getStringWidth from "../../utils/get-string-width.js"; +import getStringWidth from "../../utilities/get-string-width.js"; function printTable(path, options, print) { const { node } = path; diff --git a/src/language-markdown/print/word.js b/src/language-markdown/print/word.js index 66f4d1c8ff94..35feed9e496c 100644 --- a/src/language-markdown/print/word.js +++ b/src/language-markdown/print/word.js @@ -1,5 +1,5 @@ import { PUNCTUATION_REGEXP } from "../constants.evaluate.js"; -import { isAutolink } from "../utils.js"; +import { isAutolink } from "../utilities.js"; /** * @import AstPath from "../../common/ast-path.js" diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index 22a2a063efcd..60b572c655a0 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -14,10 +14,10 @@ import { replaceEndOfLine, softline, } from "../document/index.js"; -import getMaxContinuousCount from "../utils/get-max-continuous-count.js"; -import getMinNotPresentContinuousCount from "../utils/get-min-not-present-continuous-count.js"; -import getPreferredQuote from "../utils/get-preferred-quote.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; +import getMaxContinuousCount from "../utilities/get-max-continuous-count.js"; +import getMinNotPresentContinuousCount from "../utilities/get-min-not-present-continuous-count.js"; +import getPreferredQuote from "../utilities/get-preferred-quote.js"; +import UnexpectedNodeError from "../utilities/unexpected-node-error.js"; import clean from "./clean.js"; import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; @@ -37,7 +37,7 @@ import { isAutolink, isPrettierIgnore, splitText, -} from "./utils.js"; +} from "./utilities.js"; /** * @import {Doc} from "../document/index.js" diff --git a/src/language-markdown/unified-plugins/html-to-jsx.js b/src/language-markdown/unified-plugins/html-to-jsx.js index c8d8666326bb..cf0c4a6095f0 100644 --- a/src/language-markdown/unified-plugins/html-to-jsx.js +++ b/src/language-markdown/unified-plugins/html-to-jsx.js @@ -1,5 +1,5 @@ import { COMMENT_REGEX } from "../mdx.js"; -import { INLINE_NODE_WRAPPER_TYPES, mapAst } from "../utils.js"; +import { INLINE_NODE_WRAPPER_TYPES, mapAst } from "../utilities.js"; function htmlToJsx() { return (ast) => diff --git a/src/language-markdown/utils.js b/src/language-markdown/utilities.js similarity index 100% rename from src/language-markdown/utils.js rename to src/language-markdown/utilities.js diff --git a/src/language-markdown/visitor-keys.js b/src/language-markdown/visitor-keys.evaluate.js similarity index 84% rename from src/language-markdown/visitor-keys.js rename to src/language-markdown/visitor-keys.evaluate.js index f906d15b6a3f..ae4ab2e0b3ab 100644 --- a/src/language-markdown/visitor-keys.js +++ b/src/language-markdown/visitor-keys.evaluate.js @@ -1,4 +1,6 @@ -const visitorKeys = { +import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js"; + +const visitorKeys = generateReferenceSharedVisitorKeys({ root: ["children"], paragraph: ["children"], sentence: ["children"], @@ -36,6 +38,6 @@ const visitorKeys = { tableRow: ["children"], listItem: ["children"], text: [], -}; +}); export default visitorKeys; diff --git a/src/language-yaml/get-visitor-keys.js b/src/language-yaml/get-visitor-keys.js index d58d77cf3bd3..f4d75eb174a8 100644 --- a/src/language-yaml/get-visitor-keys.js +++ b/src/language-yaml/get-visitor-keys.js @@ -1,5 +1,5 @@ -import createGetVisitorKeys from "../utils/create-get-visitor-keys.js"; -import visitorKeys from "./visitor-keys.js"; +import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js"; +import visitorKeys from "./visitor-keys.evaluate.js"; const getVisitorKeys = createGetVisitorKeys(visitorKeys); diff --git a/src/language-yaml/languages.evaluate.js b/src/language-yaml/languages.evaluate.js index a0ded8978415..5a709b1ff916 100644 --- a/src/language-yaml/languages.evaluate.js +++ b/src/language-yaml/languages.evaluate.js @@ -1,5 +1,5 @@ import * as linguistLanguages from "linguist-languages"; -import createLanguage from "../utils/create-language.js"; +import createLanguage from "../utilities/create-language.js"; const ignoredFilenames = new Set([ // `yarn.lock` is not YAML in Yarn v1: https://github.com/yarnpkg/yarn/issues/5629 diff --git a/src/language-yaml/pragma.js b/src/language-yaml/pragma.js index 822bbc897f1f..2c0878bd3ffa 100644 --- a/src/language-yaml/pragma.js +++ b/src/language-yaml/pragma.js @@ -3,7 +3,7 @@ import { YAML_HAS_IGNORE_PRAGMA_REGEXP, YAML_HAS_PRAGMA_REGEXP, YAML_IS_PRAGMA_REGEXP, -} from "../utils/pragma/pragma.evaluate.js"; +} from "../utilities/pragma/pragma.evaluate.js"; const isPragma = (text) => YAML_IS_PRAGMA_REGEXP.test(text); const hasPragma = (text) => YAML_HAS_PRAGMA_REGEXP.test(text); diff --git a/src/language-yaml/print-preprocess.js b/src/language-yaml/print-preprocess.js index 2857036d7e0f..f3ddf8aaee54 100644 --- a/src/language-yaml/print-preprocess.js +++ b/src/language-yaml/print-preprocess.js @@ -1,4 +1,4 @@ -import { defineShortcut, mapNode } from "./utils.js"; +import { defineShortcut, mapNode } from "./utilities.js"; function preprocess(ast) { return mapNode(ast, defineShortcuts); diff --git a/src/language-yaml/print/block.js b/src/language-yaml/print/block.js index f756ef61b2ae..cddba7d33ce7 100644 --- a/src/language-yaml/print/block.js +++ b/src/language-yaml/print/block.js @@ -14,7 +14,7 @@ import { getBlockValueLineContents, hasIndicatorComment, isLastDescendantNode, -} from "../utils.js"; +} from "../utilities.js"; import { alignWithSpaces } from "./misc.js"; function printBlock(path, options, print) { diff --git a/src/language-yaml/print/flow-mapping-sequence.js b/src/language-yaml/print/flow-mapping-sequence.js index 634319f24947..56e82cb3bfab 100644 --- a/src/language-yaml/print/flow-mapping-sequence.js +++ b/src/language-yaml/print/flow-mapping-sequence.js @@ -5,7 +5,7 @@ import { line, softline, } from "../../document/index.js"; -import { hasEndComments, isEmptyNode } from "../utils.js"; +import { hasEndComments, isEmptyNode } from "../utilities.js"; import { alignWithSpaces, printNextEmptyLine } from "./misc.js"; function printFlowMapping(path, options, print) { diff --git a/src/language-yaml/print/mapping-item.js b/src/language-yaml/print/mapping-item.js index 3eb1645c22c5..e0781eecac48 100644 --- a/src/language-yaml/print/mapping-item.js +++ b/src/language-yaml/print/mapping-item.js @@ -15,7 +15,7 @@ import { isEmptyNode, isInlineNode, isNode, -} from "../utils.js"; +} from "../utilities.js"; import { alignWithSpaces } from "./misc.js"; function printMappingItem(path, options, print) { diff --git a/src/language-yaml/print/misc.js b/src/language-yaml/print/misc.js index c8ce4c5aeba2..96154f344ef0 100644 --- a/src/language-yaml/print/misc.js +++ b/src/language-yaml/print/misc.js @@ -1,5 +1,5 @@ import { align, softline } from "../../document/index.js"; -import { hasEndComments, isNextLineEmpty, isNode } from "../utils.js"; +import { hasEndComments, isNextLineEmpty, isNode } from "../utilities.js"; const printedEmptyLineCache = new WeakMap(); function printNextEmptyLine(path, originalText) { diff --git a/src/language-yaml/printer-yaml.js b/src/language-yaml/printer-yaml.js index 5253eb554682..834fad171b6c 100644 --- a/src/language-yaml/printer-yaml.js +++ b/src/language-yaml/printer-yaml.js @@ -10,8 +10,8 @@ import { lineSuffix, replaceEndOfLine, } from "../document/index.js"; -import isPreviousLineEmpty from "../utils/is-previous-line-empty.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; +import isPreviousLineEmpty from "../utilities/is-previous-line-empty.js"; +import UnexpectedNodeError from "../utilities/unexpected-node-error.js"; import clean from "./clean.js"; import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; @@ -40,7 +40,7 @@ import { isInlineNode, isLastDescendantNode, isNode, -} from "./utils.js"; +} from "./utilities.js"; function genericPrint(path, options, print) { const { node } = path; diff --git a/src/language-yaml/utils.js b/src/language-yaml/utilities.js similarity index 99% rename from src/language-yaml/utils.js rename to src/language-yaml/utilities.js index 9632ff9ea5fa..90e5183eee65 100644 --- a/src/language-yaml/utils.js +++ b/src/language-yaml/utilities.js @@ -1,4 +1,4 @@ -import isNonEmptyArray from "../utils/is-non-empty-array.js"; +import isNonEmptyArray from "../utilities/is-non-empty-array.js"; /** * @param {any} value diff --git a/src/language-yaml/visitor-keys.js b/src/language-yaml/visitor-keys.evaluate.js similarity index 61% rename from src/language-yaml/visitor-keys.js rename to src/language-yaml/visitor-keys.evaluate.js index d30a060e306b..a50ef8ae4b9d 100644 --- a/src/language-yaml/visitor-keys.js +++ b/src/language-yaml/visitor-keys.evaluate.js @@ -1,4 +1,21 @@ -const visitorKeys = Object.fromEntries( +import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js"; + +/** +@import {VisitorKeys} from "../utilities/visitor-keys.js"; +*/ + +const commentsKeys = [ + "indicatorComment", + "leadingComments", + "middleComments", + "trailingComment", + "endComments", +]; + +const tagAndAnchor = ["anchor", "tag"]; + +/** @type {VisitorKeys} */ +let visitorKeys = Object.fromEntries( Object.entries({ root: ["children"], document: ["head", "body", "children"], @@ -28,15 +45,12 @@ const visitorKeys = Object.fromEntries( type, [ ...keys, - "anchor", - "tag", - "indicatorComment", - "leadingComments", - "middleComments", - "trailingComment", - "endComments", + ...(type !== "tag" && type !== "anchor" ? tagAndAnchor : []), + ...commentsKeys, ], ]), ); +visitorKeys = generateReferenceSharedVisitorKeys(visitorKeys); + export default visitorKeys; diff --git a/src/main/ast-to-doc.js b/src/main/ast-to-doc.js index 909f006ae35c..641681403c5e 100644 --- a/src/main/ast-to-doc.js +++ b/src/main/ast-to-doc.js @@ -1,5 +1,6 @@ import AstPath from "../common/ast-path.js"; import { cursor, inheritLabel } from "../document/index.js"; +import isObject from "../utilities/is-object.js"; import { attachComments } from "./comments/attach.js"; import { ensureAllCommentsPrinted, printComments } from "./comments/print.js"; import createPrintPreCheckFunction from "./create-print-pre-check-function.js"; @@ -82,8 +83,7 @@ async function printAstToDoc(ast, options) { return ""; } - const shouldCache = - value && typeof value === "object" && args === undefined; + const shouldCache = isObject(value) && args === undefined; if (shouldCache && cache.has(value)) { return cache.get(value); diff --git a/src/main/comments/attach.js b/src/main/comments/attach.js index 2a18ba4cb865..ae8ed3b9cd80 100644 --- a/src/main/comments/attach.js +++ b/src/main/comments/attach.js @@ -1,12 +1,12 @@ import * as assert from "#universal/assert"; -import hasNewline from "../../utils/has-newline.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import hasNewline from "../../utilities/has-newline.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import getSortedChildNodes from "../utilities/get-sorted-child-nodes.js"; import { addDanglingComment, addLeadingComment, addTrailingComment, -} from "./utils.js"; +} from "./utilities.js"; /** * @import AstPath from "../../common/ast-path.js" diff --git a/src/main/comments/print.js b/src/main/comments/print.js index d9f84db6cfe5..517bc9b11af7 100644 --- a/src/main/comments/print.js +++ b/src/main/comments/print.js @@ -7,11 +7,11 @@ import { line, lineSuffix, } from "../../document/index.js"; -import hasNewline from "../../utils/has-newline.js"; -import isNonEmptyArray from "../../utils/is-non-empty-array.js"; -import isPreviousLineEmpty from "../../utils/is-previous-line-empty.js"; -import { skipSpaces } from "../../utils/skip.js"; -import skipNewline from "../../utils/skip-newline.js"; +import hasNewline from "../../utilities/has-newline.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; +import isPreviousLineEmpty from "../../utilities/is-previous-line-empty.js"; +import { skipSpaces } from "../../utilities/skip.js"; +import skipNewline from "../../utilities/skip-newline.js"; /** * @import AstPath from "../../common/ast-path.js" @@ -155,6 +155,9 @@ function printDanglingComments( return shouldIndent ? indent([hardline, doc]) : doc; } +/** +@returns {{leading?: Doc, trailing?: Doc}} +*/ function printCommentsSeparately(path, options) { const value = path.node; if (!value) { diff --git a/src/main/comments/utils.js b/src/main/comments/utilities.js similarity index 100% rename from src/main/comments/utils.js rename to src/main/comments/utilities.js diff --git a/src/main/core.js b/src/main/core.js index 9a1ee5aa3c61..36067f14c0b4 100644 --- a/src/main/core.js +++ b/src/main/core.js @@ -1,7 +1,7 @@ import { diffArrays } from "diff"; import { - convertEndOfLineToChars, - countEndOfLineChars, + convertEndOfLineOptionToCharacter, + countEndOfLineCharacters, guessEndOfLine, normalizeEndOfLine, } from "../common/end-of-line.js"; @@ -11,7 +11,7 @@ import { printDocToDebug, printDocToString as printDocToStringWithoutNormalizeOptions, } from "../document/index.js"; -import getAlignmentSize from "../utils/get-alignment-size.js"; +import getAlignmentSize from "../utilities/get-alignment-size.js"; import { prepareToPrint, printAstToDoc } from "./ast-to-doc.js"; import getCursorLocation from "./get-cursor-node.js"; import massageAst from "./massage-ast.js"; @@ -65,7 +65,8 @@ async function coreFormat(originalText, opts, addAlignmentSize = 0) { } } - result.formatted = trimmed + convertEndOfLineToChars(opts.endOfLine); + result.formatted = + trimmed + convertEndOfLineOptionToCharacter(opts.endOfLine); } const comments = opts[Symbol.for("comments")]; @@ -229,9 +230,9 @@ async function formatRange(originalText, opts) { let formatted = text.slice(0, rangeStart) + rangeTrimmed + text.slice(rangeEnd); if (opts.endOfLine !== "lf") { - const eol = convertEndOfLineToChars(opts.endOfLine); + const eol = convertEndOfLineOptionToCharacter(opts.endOfLine); if (cursorOffset >= 0 && eol === "\r\n") { - cursorOffset += countEndOfLineChars( + cursorOffset += countEndOfLineCharacters( formatted.slice(0, cursorOffset), "\n", ); @@ -287,7 +288,7 @@ function normalizeInputAndOptions(text, options) { // get rid of CR/CRLF parsing if (text.includes("\r")) { const countCrlfBefore = (index) => - countEndOfLineChars(text.slice(0, Math.max(index, 0)), "\r\n"); + countEndOfLineCharacters(text.slice(0, Math.max(index, 0)), "\r\n"); cursorOffset -= countCrlfBefore(cursorOffset); rangeStart -= countCrlfBefore(rangeStart); diff --git a/src/main/create-print-pre-check-function.js b/src/main/create-print-pre-check-function.js index 352cd603b9ff..191a74df028d 100644 --- a/src/main/create-print-pre-check-function.js +++ b/src/main/create-print-pre-check-function.js @@ -1,4 +1,4 @@ -import noop from "../utils/noop.js"; +import noop from "../utilities/noop.js"; function createPrintPreCheckFunction(options) { // All core plugins have full list of keys for possible child nodes diff --git a/src/main/front-matter/embed.js b/src/main/front-matter/embed.js index cc7544a0fe17..6b09022b56e6 100644 --- a/src/main/front-matter/embed.js +++ b/src/main/front-matter/embed.js @@ -1,5 +1,5 @@ import { hardline, markAsRoot } from "../../document/index.js"; -import inferParser from "../../utils/infer-parser.js"; +import inferParser from "../../utilities/infer-parser.js"; import isFrontMatter from "./is-front-matter.js"; const SUPPORTED_EMBED_LANGUAGES = new Set(["yaml", "toml"]); diff --git a/src/main/get-cursor-node.js b/src/main/get-cursor-node.js index 487b295e66c7..4d0585c236fd 100644 --- a/src/main/get-cursor-node.js +++ b/src/main/get-cursor-node.js @@ -1,4 +1,4 @@ -import { getChildren, getDescendants, isLeaf } from "../utils/ast-utils.js"; +import { getChildren, getDescendants, isLeaf } from "../utilities/ast.js"; /** * Find the location of the cursor in the AST, represented in one of the diff --git a/src/main/massage-ast.js b/src/main/massage-ast.js index 6d356c43dbcc..49449ddb29c9 100644 --- a/src/main/massage-ast.js +++ b/src/main/massage-ast.js @@ -1,4 +1,4 @@ -import isObject from "../utils/is-object.js"; +import isObject from "../utilities/is-object.js"; function massageAst(ast, options) { const { printer } = options; diff --git a/src/main/normalize-format-options.js b/src/main/normalize-format-options.js index e44ac92bc7e2..08a1c2503ebd 100644 --- a/src/main/normalize-format-options.js +++ b/src/main/normalize-format-options.js @@ -1,6 +1,6 @@ import { UndefinedParserError } from "../common/errors.js"; import { getSupportInfo } from "../main/support.js"; -import inferParser from "../utils/infer-parser.js"; +import inferParser from "../utilities/infer-parser.js"; import normalizeOptions from "./normalize-options.js"; import { getParserPluginByParserName, diff --git a/src/main/plugins/load-plugin.js b/src/main/plugins/load-plugin.js index dd805ff9c295..37f7c3bd73ac 100644 --- a/src/main/plugins/load-plugin.js +++ b/src/main/plugins/load-plugin.js @@ -1,7 +1,7 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; import { isUrl, toPath } from "url-or-path"; -import importFromDirectory from "../../utils/import-from-directory.js"; +import importFromDirectory from "../../utilities/import-from-directory.js"; /** @param {string | URL} name diff --git a/src/main/utilities/get-sorted-child-nodes.js b/src/main/utilities/get-sorted-child-nodes.js index c0a3d33d42a2..b36f81710d67 100644 --- a/src/main/utilities/get-sorted-child-nodes.js +++ b/src/main/utilities/get-sorted-child-nodes.js @@ -1,4 +1,4 @@ -import { getChildren } from "../../utils/ast-utils.js"; +import { getChildren } from "../../utilities/ast.js"; function getSortedChildNodes(node, ancestors, options) { const { cache: childNodesCache } = options; diff --git a/src/standalone.js b/src/standalone.js index 8277956eed58..e5ed71f06fe8 100644 --- a/src/standalone.js +++ b/src/standalone.js @@ -53,4 +53,4 @@ export { }; export * as doc from "./document/public.js"; export { default as version } from "./main/version.evaluate.js"; -export * as util from "./utils/public.js"; +export * as util from "./utilities/public.js"; diff --git a/src/universal/assert.browser.js b/src/universal/assert.browser.js index 06ff36e4cc91..b214413affbb 100644 --- a/src/universal/assert.browser.js +++ b/src/universal/assert.browser.js @@ -2,4 +2,4 @@ export { default as equal, default as ok, default as strictEqual, -} from "../utils/noop.js"; +} from "../utilities/noop.js"; diff --git a/src/universal/index.js b/src/universal/index.js index 91d2eaf1af4f..f56419142b4d 100644 --- a/src/universal/index.js +++ b/src/universal/index.js @@ -14,6 +14,6 @@ const getFileBasename = (file) => { }; export { getFileBasename }; -export { default as getInterpreter } from "../utils/get-interpreter.js"; +export { default as getInterpreter } from "../utilities/get-interpreter.js"; export { fileURLToPath } from "node:url"; export { isUrl } from "url-or-path"; diff --git a/src/utils/ast-utils.js b/src/utilities/ast.js similarity index 95% rename from src/utils/ast-utils.js rename to src/utilities/ast.js index 7d73e9dfe0bb..b3437e5d1eda 100644 --- a/src/utils/ast-utils.js +++ b/src/utilities/ast.js @@ -1,7 +1,7 @@ import isObject from "./is-object.js"; /** -@import {GetVisitorKeys, Node} from "../utils/create-get-visitor-keys.js" +@import {GetVisitorKeys, Node} from "./create-get-visitor-keys.js" @typedef {(node: Node) => boolean} Predicate */ diff --git a/src/utils/create-get-visitor-keys.js b/src/utilities/create-get-visitor-keys.js similarity index 100% rename from src/utils/create-get-visitor-keys.js rename to src/utilities/create-get-visitor-keys.js diff --git a/src/utils/create-group-id-mapper.js b/src/utilities/create-group-id-mapper.js similarity index 100% rename from src/utils/create-group-id-mapper.js rename to src/utilities/create-group-id-mapper.js diff --git a/src/utils/create-language.js b/src/utilities/create-language.js similarity index 100% rename from src/utils/create-language.js rename to src/utilities/create-language.js diff --git a/src/utils/create-mockable.js b/src/utilities/create-mockable.js similarity index 100% rename from src/utils/create-mockable.js rename to src/utilities/create-mockable.js diff --git a/src/utils/get-alignment-size.js b/src/utilities/get-alignment-size.js similarity index 100% rename from src/utils/get-alignment-size.js rename to src/utilities/get-alignment-size.js diff --git a/src/utils/get-indent-size.js b/src/utilities/get-indent-size.js similarity index 100% rename from src/utils/get-indent-size.js rename to src/utilities/get-indent-size.js diff --git a/src/utils/get-interpreter.js b/src/utilities/get-interpreter.js similarity index 100% rename from src/utils/get-interpreter.js rename to src/utilities/get-interpreter.js diff --git a/src/utils/get-max-continuous-count.js b/src/utilities/get-max-continuous-count.js similarity index 100% rename from src/utils/get-max-continuous-count.js rename to src/utilities/get-max-continuous-count.js diff --git a/src/utils/get-min-not-present-continuous-count.js b/src/utilities/get-min-not-present-continuous-count.js similarity index 100% rename from src/utils/get-min-not-present-continuous-count.js rename to src/utilities/get-min-not-present-continuous-count.js diff --git a/src/utils/get-next-non-space-non-comment-character-index.js b/src/utilities/get-next-non-space-non-comment-character-index.js similarity index 100% rename from src/utils/get-next-non-space-non-comment-character-index.js rename to src/utilities/get-next-non-space-non-comment-character-index.js diff --git a/src/utils/get-next-non-space-non-comment-character.js b/src/utilities/get-next-non-space-non-comment-character.js similarity index 100% rename from src/utils/get-next-non-space-non-comment-character.js rename to src/utilities/get-next-non-space-non-comment-character.js diff --git a/src/utilities/get-preferred-quote.js b/src/utilities/get-preferred-quote.js new file mode 100644 index 000000000000..dbf2ebac4557 --- /dev/null +++ b/src/utilities/get-preferred-quote.js @@ -0,0 +1,55 @@ +/** + * @typedef {SINGLE_QUOTE | DOUBLE_QUOTE} Quote + */ + +const SINGLE_QUOTE = "'"; +const DOUBLE_QUOTE = '"'; + +const SINGLE_QUOTE_DATA = Object.freeze({ + character: SINGLE_QUOTE, + codePoint: 39, +}); +const DOUBLE_QUOTE_DATA = Object.freeze({ + character: DOUBLE_QUOTE, + codePoint: 34, +}); + +const SINGLE_QUOTE_SETTINGS = Object.freeze({ + preferred: SINGLE_QUOTE_DATA, + alternate: DOUBLE_QUOTE_DATA, +}); +const DOUBLE_QUOTE_SETTINGS = Object.freeze({ + preferred: DOUBLE_QUOTE_DATA, + alternate: SINGLE_QUOTE_DATA, +}); + +/** + * @param {string} text + * @param {Quote | boolean} preferredQuoteOrPreferSingleQuote + * @returns {Quote} + */ +function getPreferredQuote(text, preferredQuoteOrPreferSingleQuote) { + const { preferred, alternate } = + preferredQuoteOrPreferSingleQuote === true || + preferredQuoteOrPreferSingleQuote === SINGLE_QUOTE + ? SINGLE_QUOTE_SETTINGS + : DOUBLE_QUOTE_SETTINGS; + const { length } = text; + let preferredQuoteCount = 0; + let alternateQuoteCount = 0; + + // `for..of` loop is known slower + for (let index = 0; index < length; index++) { + const codePoint = text.charCodeAt(index); + if (codePoint === preferred.codePoint) { + preferredQuoteCount++; + } else if (codePoint === alternate.codePoint) { + alternateQuoteCount++; + } + } + + return (preferredQuoteCount > alternateQuoteCount ? alternate : preferred) + .character; +} + +export default getPreferredQuote; diff --git a/src/utils/get-string-width.js b/src/utilities/get-string-width.js similarity index 100% rename from src/utils/get-string-width.js rename to src/utilities/get-string-width.js diff --git a/src/utils/has-newline-in-range.js b/src/utilities/has-newline-in-range.js similarity index 100% rename from src/utils/has-newline-in-range.js rename to src/utilities/has-newline-in-range.js diff --git a/src/utils/has-newline.js b/src/utilities/has-newline.js similarity index 100% rename from src/utils/has-newline.js rename to src/utilities/has-newline.js diff --git a/src/utils/has-spaces.js b/src/utilities/has-spaces.js similarity index 100% rename from src/utils/has-spaces.js rename to src/utilities/has-spaces.js diff --git a/src/utilities/html-whitespace.js b/src/utilities/html-whitespace.js new file mode 100644 index 000000000000..8923bd1888ed --- /dev/null +++ b/src/utilities/html-whitespace.js @@ -0,0 +1,7 @@ +import WhitespaceUtilities from "./whitespace-utilities.js"; + +// https://infra.spec.whatwg.org/#ascii-whitespace +const HTML_WHITESPACE_CHARACTERS = ["\t", "\n", "\f", "\r", " "]; +const htmlWhitespace = new WhitespaceUtilities(HTML_WHITESPACE_CHARACTERS); + +export default htmlWhitespace; diff --git a/src/utils/ignore.js b/src/utilities/ignore.js similarity index 98% rename from src/utils/ignore.js rename to src/utilities/ignore.js index d86dffcb10c0..552498682479 100644 --- a/src/utils/ignore.js +++ b/src/utilities/ignore.js @@ -2,7 +2,7 @@ import path from "node:path"; import url from "node:url"; import createIgnore from "ignore"; import { isUrl, toPath } from "url-or-path"; -import readFile from "../utils/read-file.js"; +import readFile from "./read-file.js"; /** @type {(filePath: string) => string} */ const slash = diff --git a/src/utils/import-from-directory.js b/src/utilities/import-from-directory.js similarity index 100% rename from src/utils/import-from-directory.js rename to src/utilities/import-from-directory.js diff --git a/src/utils/import-from-file.js b/src/utilities/import-from-file.js similarity index 100% rename from src/utils/import-from-file.js rename to src/utilities/import-from-file.js diff --git a/src/utils/infer-parser.js b/src/utilities/infer-parser.js similarity index 100% rename from src/utils/infer-parser.js rename to src/utilities/infer-parser.js diff --git a/src/utils/is-next-line-empty.js b/src/utilities/is-next-line-empty.js similarity index 100% rename from src/utils/is-next-line-empty.js rename to src/utilities/is-next-line-empty.js diff --git a/src/utils/is-non-empty-array.js b/src/utilities/is-non-empty-array.js similarity index 100% rename from src/utils/is-non-empty-array.js rename to src/utilities/is-non-empty-array.js diff --git a/src/utils/is-object.js b/src/utilities/is-object.js similarity index 100% rename from src/utils/is-object.js rename to src/utilities/is-object.js diff --git a/src/utils/is-previous-line-empty.js b/src/utilities/is-previous-line-empty.js similarity index 100% rename from src/utils/is-previous-line-empty.js rename to src/utilities/is-previous-line-empty.js diff --git a/src/utils/line-column-to-index.js b/src/utilities/line-column-to-index.js similarity index 100% rename from src/utils/line-column-to-index.js rename to src/utilities/line-column-to-index.js diff --git a/src/utils/make-string.js b/src/utilities/make-string.js similarity index 100% rename from src/utils/make-string.js rename to src/utilities/make-string.js diff --git a/src/utils/narrow-emojis.evaluate.js b/src/utilities/narrow-emojis.evaluate.js similarity index 100% rename from src/utils/narrow-emojis.evaluate.js rename to src/utilities/narrow-emojis.evaluate.js diff --git a/src/utils/noop.js b/src/utilities/noop.js similarity index 100% rename from src/utils/noop.js rename to src/utilities/noop.js diff --git a/src/utils/object-omit.js b/src/utilities/object-omit.js similarity index 100% rename from src/utils/object-omit.js rename to src/utilities/object-omit.js diff --git a/src/utils/partition.js b/src/utilities/partition.js similarity index 100% rename from src/utils/partition.js rename to src/utilities/partition.js diff --git a/src/utils/pragma/pragma.evaluate.js b/src/utilities/pragma/pragma.evaluate.js similarity index 100% rename from src/utils/pragma/pragma.evaluate.js rename to src/utilities/pragma/pragma.evaluate.js diff --git a/src/utils/print-number.js b/src/utilities/print-number.js similarity index 100% rename from src/utils/print-number.js rename to src/utilities/print-number.js diff --git a/src/utils/print-string.js b/src/utilities/print-string.js similarity index 100% rename from src/utils/print-string.js rename to src/utilities/print-string.js diff --git a/src/utils/public.js b/src/utilities/public.js similarity index 99% rename from src/utils/public.js rename to src/utilities/public.js index e967c59270ca..444db8690cfe 100644 --- a/src/utils/public.js +++ b/src/utilities/public.js @@ -137,7 +137,7 @@ export { addDanglingComment, addLeadingComment, addTrailingComment, -} from "../main/comments/utils.js"; +} from "../main/comments/utilities.js"; export { default as getAlignmentSize } from "./get-alignment-size.js"; export { default as getIndentSize } from "./get-indent-size.js"; export { default as getMaxContinuousCount } from "./get-max-continuous-count.js"; diff --git a/src/utils/read-file.js b/src/utilities/read-file.js similarity index 100% rename from src/utils/read-file.js rename to src/utilities/read-file.js diff --git a/src/utils/require-from-file.js b/src/utilities/require-from-file.js similarity index 100% rename from src/utils/require-from-file.js rename to src/utilities/require-from-file.js diff --git a/src/utils/skip-inline-comment.js b/src/utilities/skip-inline-comment.js similarity index 100% rename from src/utils/skip-inline-comment.js rename to src/utilities/skip-inline-comment.js diff --git a/src/utils/skip-newline.js b/src/utilities/skip-newline.js similarity index 100% rename from src/utils/skip-newline.js rename to src/utilities/skip-newline.js diff --git a/src/utils/skip-trailing-comment.js b/src/utilities/skip-trailing-comment.js similarity index 100% rename from src/utils/skip-trailing-comment.js rename to src/utilities/skip-trailing-comment.js diff --git a/src/utils/skip.js b/src/utilities/skip.js similarity index 100% rename from src/utils/skip.js rename to src/utilities/skip.js diff --git a/src/utils/try-combinations.js b/src/utilities/try-combinations.js similarity index 100% rename from src/utils/try-combinations.js rename to src/utilities/try-combinations.js diff --git a/src/utils/unexpected-node-error.js b/src/utilities/unexpected-node-error.js similarity index 100% rename from src/utils/unexpected-node-error.js rename to src/utilities/unexpected-node-error.js diff --git a/src/language-js/traverse/utilities.js b/src/utilities/visitor-keys.js similarity index 69% rename from src/language-js/traverse/utilities.js rename to src/utilities/visitor-keys.js index de4c1584a11c..4ce303eec21a 100644 --- a/src/language-js/traverse/utilities.js +++ b/src/utilities/visitor-keys.js @@ -1,4 +1,6 @@ -/** @typedef {Record} VisitorKeys */ +/** +@typedef {readonly string[]} Keys +@typedef {Record} VisitorKeys */ const unique = (values) => [...new Set(values)]; @@ -64,4 +66,30 @@ function removeNodeTypes(visitorKeys, nodeTypesToRemove) { return result; } -export { removeNodeTypes, removeVisitorKeys, unionVisitorKeys }; +/** +@param {VisitorKeys} visitorKeys +@returns {VisitorKeys} +*/ +function generateReferenceSharedVisitorKeys(visitorKeys) { + /** @type {Map} */ + const cache = new Map(); + /** @type {VisitorKeys} */ + const result = {}; + + for (const [type, keys] of Object.entries(visitorKeys)) { + const cacheKey = keys.toSorted().join("\0"); + if (!cache.has(cacheKey)) { + cache.set(cacheKey, keys); + } + result[type] = cache.get(cacheKey); + } + + return result; +} + +export { + generateReferenceSharedVisitorKeys, + removeNodeTypes, + removeVisitorKeys, + unionVisitorKeys, +}; diff --git a/src/utils/whitespace-utils.js b/src/utilities/whitespace-utilities.js similarity index 98% rename from src/utils/whitespace-utils.js rename to src/utilities/whitespace-utilities.js index 9e9ee9c9be79..7d451467a7ac 100644 --- a/src/utils/whitespace-utils.js +++ b/src/utilities/whitespace-utilities.js @@ -1,6 +1,6 @@ import escapeStringRegexp from "escape-string-regexp"; -class WhitespaceUtils { +class WhitespaceUtilities { #whitespaceCharacters; constructor(whitespaceCharacters) { @@ -151,4 +151,4 @@ class WhitespaceUtils { } } -export default WhitespaceUtils; +export default WhitespaceUtilities; diff --git a/src/utils/get-preferred-quote.js b/src/utils/get-preferred-quote.js deleted file mode 100644 index ec1a85ff7c34..000000000000 --- a/src/utils/get-preferred-quote.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @typedef {SINGLE_QUOTE | DOUBLE_QUOTE} Quote - */ - -const SINGLE_QUOTE = "'"; -const DOUBLE_QUOTE = '"'; - -/** - * - * @param {string} text - * @param {Quote | boolean} preferredQuoteOrPreferSingleQuote - * @returns {Quote} - */ -function getPreferredQuote(text, preferredQuoteOrPreferSingleQuote) { - const preferred = - preferredQuoteOrPreferSingleQuote === true || - preferredQuoteOrPreferSingleQuote === SINGLE_QUOTE - ? SINGLE_QUOTE - : DOUBLE_QUOTE; - const alternate = preferred === SINGLE_QUOTE ? DOUBLE_QUOTE : SINGLE_QUOTE; - - let preferredQuoteCount = 0; - let alternateQuoteCount = 0; - for (const character of text) { - if (character === preferred) { - preferredQuoteCount++; - } else if (character === alternate) { - alternateQuoteCount++; - } - } - - return preferredQuoteCount > alternateQuoteCount ? alternate : preferred; -} - -export default getPreferredQuote; diff --git a/src/utils/html-whitespace-utils.js b/src/utils/html-whitespace-utils.js deleted file mode 100644 index d87e832fb36f..000000000000 --- a/src/utils/html-whitespace-utils.js +++ /dev/null @@ -1,7 +0,0 @@ -import WhitespaceUtils from "./whitespace-utils.js"; - -// https://infra.spec.whatwg.org/#ascii-whitespace -const HTML_WHITESPACE_CHARACTERS = ["\t", "\n", "\f", "\r", " "]; -const htmlWhitespaceUtils = new WhitespaceUtils(HTML_WHITESPACE_CHARACTERS); - -export default htmlWhitespaceUtils; diff --git a/tests/config/prettier-plugins/prettier-plugin-dummy-toml/index.js b/tests/config/prettier-plugins/prettier-plugin-dummy-toml/index.js index 8523216a4d92..217d66ec154e 100644 --- a/tests/config/prettier-plugins/prettier-plugin-dummy-toml/index.js +++ b/tests/config/prettier-plugins/prettier-plugin-dummy-toml/index.js @@ -1,5 +1,5 @@ import { outdent } from "outdent"; -import createPlugin from "../../utils/create-plugin.cjs"; +import createPlugin from "../../utilities/create-plugin.cjs"; const plugin = createPlugin({ name: "toml", diff --git a/tests/config/prettier-plugins/prettier-plugin-uppercase-rocks/index.js b/tests/config/prettier-plugins/prettier-plugin-uppercase-rocks/index.js index e3d8ea8a3d11..c2fca396b7dc 100644 --- a/tests/config/prettier-plugins/prettier-plugin-uppercase-rocks/index.js +++ b/tests/config/prettier-plugins/prettier-plugin-uppercase-rocks/index.js @@ -1,4 +1,4 @@ -import createPlugin from "../../utils/create-plugin.cjs"; +import createPlugin from "../../utilities/create-plugin.cjs"; const plugin = createPlugin({ name: "uppercase-rocks", diff --git a/tests/config/require-standalone.cjs b/tests/config/require-standalone.cjs index c02c30979df2..5a0ac7777115 100644 --- a/tests/config/require-standalone.cjs +++ b/tests/config/require-standalone.cjs @@ -1,7 +1,7 @@ "use strict"; const vm = require("vm"); const fastGlob = require("fast-glob"); -const createSandBox = require("./utils/create-sandbox.cjs"); +const createSandBox = require("./utilities/create-sandbox.cjs"); const sandbox = createSandBox({ files: fastGlob.sync(["standalone.js", "plugins/*.js"], { diff --git a/tests/config/run-format-test.js b/tests/config/run-format-test.js index 64bf003bfe5c..c7fab1beb5e3 100644 --- a/tests/config/run-format-test.js +++ b/tests/config/run-format-test.js @@ -3,11 +3,11 @@ import path from "node:path"; import url from "node:url"; import createEsmUtils from "esm-utils"; import getPrettier from "./get-prettier.js"; -import checkParsers from "./utils/check-parsers.js"; -import consistentEndOfLine from "./utils/consistent-end-of-line.js"; -import createSnapshot from "./utils/create-snapshot.js"; -import stringifyOptionsForTitle from "./utils/stringify-options-for-title.js"; -import visualizeEndOfLine from "./utils/visualize-end-of-line.js"; +import checkParsers from "./utilities/check-parsers.js"; +import consistentEndOfLine from "./utilities/consistent-end-of-line.js"; +import createSnapshot from "./utilities/create-snapshot.js"; +import stringifyOptionsForTitle from "./utilities/stringify-options-for-title.js"; +import visualizeEndOfLine from "./utilities/visualize-end-of-line.js"; const { __dirname } = createEsmUtils(import.meta); diff --git a/tests/config/utils/check-parsers.js b/tests/config/utilities/check-parsers.js similarity index 100% rename from tests/config/utils/check-parsers.js rename to tests/config/utilities/check-parsers.js diff --git a/tests/config/utils/consistent-end-of-line.js b/tests/config/utilities/consistent-end-of-line.js similarity index 100% rename from tests/config/utils/consistent-end-of-line.js rename to tests/config/utilities/consistent-end-of-line.js diff --git a/tests/config/utils/create-plugin.cjs b/tests/config/utilities/create-plugin.cjs similarity index 100% rename from tests/config/utils/create-plugin.cjs rename to tests/config/utilities/create-plugin.cjs diff --git a/tests/config/utils/create-sandbox.cjs b/tests/config/utilities/create-sandbox.cjs similarity index 100% rename from tests/config/utils/create-sandbox.cjs rename to tests/config/utilities/create-sandbox.cjs diff --git a/tests/config/utils/create-snapshot.js b/tests/config/utilities/create-snapshot.js similarity index 100% rename from tests/config/utils/create-snapshot.js rename to tests/config/utilities/create-snapshot.js diff --git a/tests/config/utils/stringify-options-for-title.js b/tests/config/utilities/stringify-options-for-title.js similarity index 100% rename from tests/config/utils/stringify-options-for-title.js rename to tests/config/utilities/stringify-options-for-title.js diff --git a/tests/config/utils/visualize-end-of-line.js b/tests/config/utilities/visualize-end-of-line.js similarity index 100% rename from tests/config/utils/visualize-end-of-line.js rename to tests/config/utilities/visualize-end-of-line.js diff --git a/tests/config/utils/visualize-range.js b/tests/config/utilities/visualize-range.js similarity index 100% rename from tests/config/utils/visualize-range.js rename to tests/config/utilities/visualize-range.js diff --git a/tests/format/lwc/attribute/quotes/__snapshots__/format.test.js.snap b/tests/format/lwc/attribute/quotes/__snapshots__/format.test.js.snap index 19cf51248bab..e1f8a47f2552 100644 --- a/tests/format/lwc/attribute/quotes/__snapshots__/format.test.js.snap +++ b/tests/format/lwc/attribute/quotes/__snapshots__/format.test.js.snap @@ -13,10 +13,28 @@ unquoted={data}>
+
+ =====================================output===================================== -
+
+ +
-
+
================================================================================ `; @@ -33,10 +51,28 @@ unquoted={data}>
+
+ =====================================output=====================================
+
+ ================================================================================ `; diff --git a/tests/format/lwc/attribute/quotes/quotes.html b/tests/format/lwc/attribute/quotes/quotes.html index ad41f3f05a27..a254cabdb3e4 100644 --- a/tests/format/lwc/attribute/quotes/quotes.html +++ b/tests/format/lwc/attribute/quotes/quotes.html @@ -3,3 +3,12 @@
+ +
diff --git a/tests/format/typescript/union/comments/18379.ts b/tests/format/typescript/union/comments/18379.ts new file mode 100644 index 000000000000..59fa250e11ed --- /dev/null +++ b/tests/format/typescript/union/comments/18379.ts @@ -0,0 +1,31 @@ +type A1 = + ( + A | B // comment 1 + ) & ( + // comment2 + A | B + ) + +type A2 = + ( + A | B // prettier-ignore + ) & ( + // prettier-ignore + A | B + ) + +type A1 = + // comment 1 + (A | B) + & ( + // comment2 + A | B + ) + +type A1 = + // prettier-ignore + (A | B) + & ( + // prettier-ignore + A | B + ) diff --git a/tests/format/typescript/union/comments/18389.ts b/tests/format/typescript/union/comments/18389.ts new file mode 100644 index 000000000000..b3611ca97a46 --- /dev/null +++ b/tests/format/typescript/union/comments/18389.ts @@ -0,0 +1,19 @@ +type A1 = | /** + * octahedralRhinocerosTransformer + */ + a + | (b | c); + +type A2 = | /** + * hippopotamicKangarooMutator + */ + (a | b) + | c; + +type A4 = + ( + A | B // comment 1 + ) | ( + // comment2 + A | B + ) diff --git a/tests/format/typescript/union/comments/__snapshots__/format.test.js.snap b/tests/format/typescript/union/comments/__snapshots__/format.test.js.snap index 093f8c013965..83dc8a974ad8 100644 --- a/tests/format/typescript/union/comments/__snapshots__/format.test.js.snap +++ b/tests/format/typescript/union/comments/__snapshots__/format.test.js.snap @@ -95,3 +95,122 @@ type A3 = ================================================================================ `; + +exports[`18379.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript", "flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +type A1 = + ( + A | B // comment 1 + ) & ( + // comment2 + A | B + ) + +type A2 = + ( + A | B // prettier-ignore + ) & ( + // prettier-ignore + A | B + ) + +type A1 = + // comment 1 + (A | B) + & ( + // comment2 + A | B + ) + +type A1 = + // prettier-ignore + (A | B) + & ( + // prettier-ignore + A | B + ) + +=====================================output===================================== +type A1 = ( + | A + | B // comment 1 +) & + // comment2 + (A | B); + +type A2 = ( + | A + | B // prettier-ignore +) & + // prettier-ignore + (A | B); + +type A1 = + // comment 1 + (A | B) & + // comment2 + (A | B); + +type A1 = + // prettier-ignore + (A | B) + & ( + // prettier-ignore + A | B + ); + +================================================================================ +`; + +exports[`18389.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript", "flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +type A1 = | /** + * octahedralRhinocerosTransformer + */ + a + | (b | c); + +type A2 = | /** + * hippopotamicKangarooMutator + */ + (a | b) + | c; + +type A4 = + ( + A | B // comment 1 + ) | ( + // comment2 + A | B + ) + +=====================================output===================================== +type A1 = + | /** + * octahedralRhinocerosTransformer + */ + a + | (b | c); + +type A2 = + | /** + * hippopotamicKangarooMutator + */ + (a | b) + | c; + +type A4 = + | (A | B) // comment 1 + // comment2 + | (A | B); + +================================================================================ +`; diff --git a/tests/integration/__tests__/bundle.js b/tests/integration/__tests__/bundle.js index 9e60e8708b52..cbba5d3fa3bf 100644 --- a/tests/integration/__tests__/bundle.js +++ b/tests/integration/__tests__/bundle.js @@ -1,11 +1,12 @@ import fs from "node:fs/promises"; import path from "node:path"; +import { inspect } from "node:util"; import createEsmUtils from "esm-utils"; import fastGlob from "fast-glob"; import coreOptions from "../../../src/main/core-options.evaluate.js"; import codeSamples from "../../../website/playground/codeSamples.mjs"; import prettier from "../../config/prettier-entry.js"; -import createSandBox from "../../config/utils/create-sandbox.cjs"; +import createSandBox from "../../config/utilities/create-sandbox.cjs"; import { projectRoot } from "../env.js"; const { require, importModule } = createEsmUtils(import.meta); @@ -70,6 +71,111 @@ describe("standalone", () => { expect(esmOutput).toBe(umdOutput); }); } + + const printerVisitorKeysSettings = new Map([ + [ + "estree", + { + sharedVisitorKeys: true, + nodes: [ + { type: "FunctionDeclaration" }, + { type: "FunctionExpression" }, + ], + }, + ], + [ + "estree-json", + { + sharedVisitorKeys: true, + nodes: [{ type: "StringLiteral" }, { type: "Identifier" }], + }, + ], + [ + "glimmer", + { + sharedVisitorKeys: false, + nodes: [{ type: "Template" }, { type: "Block" }], + }, + ], + [ + "graphql", + { + sharedVisitorKeys: false, + nodes: [{ kind: "StringValue" }, { kind: "BooleanValue" }], + }, + ], + [ + "html", + { + sharedVisitorKeys: true, + nodes: [{ kind: "comment" }, { kind: "cdata" }], + }, + ], + [ + "mdast", + { + sharedVisitorKeys: true, + nodes: [{ type: "code" }, { type: "image" }], + }, + ], + [ + "postcss", + { + sharedVisitorKeys: true, + nodes: [{ type: "media-query-list" }, { type: "selector-pseudo" }], + }, + ], + [ + "yaml", + { + sharedVisitorKeys: true, + nodes: [{ type: "blockLiteral" }, { type: "quoteSingle" }], + }, + ], + ]); + + test("visitor keys with shared reference", () => { + for (const [name, printer] of esmPlugins.flatMap((plugin) => + Object.entries(plugin.printers ?? {}), + )) { + try { + expect(printerVisitorKeysSettings.has(name)).toBe(true); + } catch { + throw new Error(`Missing settings for printer '${name}'.`); + } + const { getVisitorKeys } = printer; + expect(typeof getVisitorKeys).toBe("function"); + const { sharedVisitorKeys, nodes } = printerVisitorKeysSettings.get(name); + expect( + typeof sharedVisitorKeys === "boolean" && + Array.isArray(nodes) && + nodes.length > 1, + ).toBe(true); + const keys = nodes.map((node) => getVisitorKeys(node)); + + try { + expect(keys.every((keys) => Array.isArray(keys))).toBe(true); + } catch { + throw new Error( + `Missing visitor keys for '${name}' nodes: ${inspect(nodes)}.`, + ); + } + const [firstNodeKeys, ...restNodeKeys] = keys; + if (sharedVisitorKeys) { + // Should be same reference + expect(restNodeKeys.every((keys) => keys === firstNodeKeys)).toBe(true); + } else { + // Should be same, but not same reference + expect( + restNodeKeys.every( + (keys) => + keys !== firstNodeKeys && + JSON.stringify(keys) === JSON.stringify(firstNodeKeys), + ), + ).toBe(true); + } + } + }); }); test("global objects", async () => { diff --git a/tests/integration/__tests__/doc-utils-clean-doc.js b/tests/integration/__tests__/doc-utilities-clean-doc.js similarity index 100% rename from tests/integration/__tests__/doc-utils-clean-doc.js rename to tests/integration/__tests__/doc-utilities-clean-doc.js diff --git a/tests/integration/__tests__/plugin-override-buitin-plugins.js b/tests/integration/__tests__/plugin-override-buitin-plugins.js index 9bc08ff03073..50d408fbad09 100644 --- a/tests/integration/__tests__/plugin-override-buitin-plugins.js +++ b/tests/integration/__tests__/plugin-override-buitin-plugins.js @@ -1,5 +1,5 @@ import prettier from "../../config/prettier-entry.js"; -import createPlugin from "../../config/utils/create-plugin.cjs"; +import createPlugin from "../../config/utilities/create-plugin.cjs"; test("plugins can override builtin plugins", async () => { const outputWithoutPlugin = await prettier.format("foo()", { diff --git a/tests/integration/__tests__/util-shared.js b/tests/integration/__tests__/public-utilities.js similarity index 85% rename from tests/integration/__tests__/util-shared.js rename to tests/integration/__tests__/public-utilities.js index 3cb6eaf08e69..62095c11d9d1 100644 --- a/tests/integration/__tests__/util-shared.js +++ b/tests/integration/__tests__/public-utilities.js @@ -1,36 +1,36 @@ import prettier from "../../config/prettier-entry.js"; -const sharedUtil = prettier.util; +const utilities = prettier.util; test("shared util has correct structure", () => { - expect(typeof sharedUtil.getMaxContinuousCount).toBe("function"); - expect(typeof sharedUtil.getStringWidth).toBe("function"); - expect(typeof sharedUtil.getAlignmentSize).toBe("function"); - expect(typeof sharedUtil.getIndentSize).toBe("function"); - expect(typeof sharedUtil.skip).toBe("function"); - expect(typeof sharedUtil.skipWhitespace).toBe("function"); - expect(typeof sharedUtil.skipSpaces).toBe("function"); - expect(typeof sharedUtil.skipToLineEnd).toBe("function"); - expect(typeof sharedUtil.skipEverythingButNewLine).toBe("function"); - expect(typeof sharedUtil.skipInlineComment).toBe("function"); - expect(typeof sharedUtil.skipTrailingComment).toBe("function"); - expect(typeof sharedUtil.skipNewline).toBe("function"); - expect(typeof sharedUtil.hasNewline).toBe("function"); - expect(typeof sharedUtil.hasNewlineInRange).toBe("function"); - expect(typeof sharedUtil.hasSpaces).toBe("function"); - expect(typeof sharedUtil.isNextLineEmpty).toBe("function"); - expect(typeof sharedUtil.isNextLineEmptyAfterIndex).toBe("function"); - expect(typeof sharedUtil.isPreviousLineEmpty).toBe("function"); - expect(typeof sharedUtil.getNextNonSpaceNonCommentCharacter).toBe("function"); - expect(typeof sharedUtil.getNextNonSpaceNonCommentCharacterIndex).toBe( + expect(typeof utilities.getMaxContinuousCount).toBe("function"); + expect(typeof utilities.getStringWidth).toBe("function"); + expect(typeof utilities.getAlignmentSize).toBe("function"); + expect(typeof utilities.getIndentSize).toBe("function"); + expect(typeof utilities.skip).toBe("function"); + expect(typeof utilities.skipWhitespace).toBe("function"); + expect(typeof utilities.skipSpaces).toBe("function"); + expect(typeof utilities.skipToLineEnd).toBe("function"); + expect(typeof utilities.skipEverythingButNewLine).toBe("function"); + expect(typeof utilities.skipInlineComment).toBe("function"); + expect(typeof utilities.skipTrailingComment).toBe("function"); + expect(typeof utilities.skipNewline).toBe("function"); + expect(typeof utilities.hasNewline).toBe("function"); + expect(typeof utilities.hasNewlineInRange).toBe("function"); + expect(typeof utilities.hasSpaces).toBe("function"); + expect(typeof utilities.isNextLineEmpty).toBe("function"); + expect(typeof utilities.isNextLineEmptyAfterIndex).toBe("function"); + expect(typeof utilities.isPreviousLineEmpty).toBe("function"); + expect(typeof utilities.getNextNonSpaceNonCommentCharacter).toBe("function"); + expect(typeof utilities.getNextNonSpaceNonCommentCharacterIndex).toBe( "function", ); - expect(typeof sharedUtil.makeString).toBe("function"); - expect(typeof sharedUtil.getPreferredQuote).toBe("function"); + expect(typeof utilities.makeString).toBe("function"); + expect(typeof utilities.getPreferredQuote).toBe("function"); }); test("sharedUtil.getMaxContinuousCount", () => { - const { getMaxContinuousCount } = sharedUtil; + const { getMaxContinuousCount } = utilities; expect(getMaxContinuousCount("|---|--|-|--|---|", "-")).toBe(3); expect(getMaxContinuousCount("|...|", ".")).toBe(3); @@ -50,7 +50,7 @@ test("sharedUtil.getMaxContinuousCount", () => { }); test("sharedUtil.getStringWidth", () => { - const { getStringWidth } = sharedUtil; + const { getStringWidth } = utilities; // From https://github.com/sindresorhus/string-width/blob/main/test.js expect(getStringWidth("abcde")).toBe(5); @@ -93,7 +93,7 @@ test("sharedUtil.getStringWidth", () => { }); test("sharedUtil.getAlignmentSize", () => { - const { getAlignmentSize } = sharedUtil; + const { getAlignmentSize } = utilities; expect(getAlignmentSize(" ")).toBe(3); expect(getAlignmentSize(" ", /* tabWidth */ 2, /* startIndex */ 2)).toBe(1); expect(getAlignmentSize("\t\t", /* tabWidth */ 2)).toBe(4); @@ -104,7 +104,7 @@ test("sharedUtil.getAlignmentSize", () => { }); test("sharedUtil.getIndentSize", () => { - const { getIndentSize } = sharedUtil; + const { getIndentSize } = utilities; expect(getIndentSize("\n a")).toBe(3); expect(getIndentSize("\n a", /* tabWidth */ 2)).toBe(3); expect(getIndentSize("\n\t\ta", /* tabWidth */ 2)).toBe(4); @@ -118,7 +118,7 @@ test("sharedUtil.getNextNonSpaceNonCommentCharacter and sharedUtil.getNextNonSpa const { getNextNonSpaceNonCommentCharacter, getNextNonSpaceNonCommentCharacterIndex, - } = sharedUtil; + } = utilities; const FAKE_NODE = { type: "Identifier", name: "a" }; { @@ -169,7 +169,7 @@ test("sharedUtil.getNextNonSpaceNonCommentCharacter and sharedUtil.getNextNonSpa test("sharedUtil.isPreviousLineEmpty, sharedUtil.isNextLineEmpty and sharedUtil.isNextLineEmptyAfterIndex", () => { const { isPreviousLineEmpty, isNextLineEmpty, isNextLineEmptyAfterIndex } = - sharedUtil; + utilities; const FAKE_NODE_A = { type: "Identifier", name: "a" }; const FAKE_NODE_B = { type: "Identifier", name: "b" }; @@ -213,7 +213,7 @@ test("sharedUtil.isPreviousLineEmpty, sharedUtil.isNextLineEmpty and sharedUtil. }); test("sharedUtil.makeString", () => { - const { makeString } = sharedUtil; + const { makeString } = utilities; const DOUBLE_QUOTE = '"'; const SINGLE_QUOTE = "'"; @@ -268,7 +268,7 @@ test("sharedUtil.makeString", () => { }); test("sharedUtil.getPreferredQuote", () => { - const { getPreferredQuote } = sharedUtil; + const { getPreferredQuote } = utilities; const DOUBLE_QUOTE = '"'; const SINGLE_QUOTE = "'"; diff --git a/tests/integration/__tests__/schema.js b/tests/integration/__tests__/schema.js index 31f8b51dbdec..218b9178f045 100644 --- a/tests/integration/__tests__/schema.js +++ b/tests/integration/__tests__/schema.js @@ -1,4 +1,4 @@ -import { generateSchemaData } from "../../../scripts/utils/generate-schema.js"; +import { generateSchemaData } from "../../../scripts/utilities/generate-schema.js"; import prettier from "../../config/prettier-entry.js"; test("schema", async () => { diff --git a/tests/integration/cli/infer-parser/override-builtin-plugin-languages/dummy-js-plugin.js b/tests/integration/cli/infer-parser/override-builtin-plugin-languages/dummy-js-plugin.js index ed9c1f03cef4..e73ff6e8dcd0 100644 --- a/tests/integration/cli/infer-parser/override-builtin-plugin-languages/dummy-js-plugin.js +++ b/tests/integration/cli/infer-parser/override-builtin-plugin-languages/dummy-js-plugin.js @@ -1,5 +1,5 @@ -import createPlugin from "../../../../config/utils/create-plugin.cjs"; +import createPlugin from "../../../../config/utilities/create-plugin.cjs"; const PARSER_NAME = 'dummy-js-parser' const PRINT_MARK = `formatted by '${PARSER_NAME}' parser` diff --git a/tests/integration/plugins/automatic/prettier-plugin-baz.js b/tests/integration/plugins/automatic/prettier-plugin-baz.js index 61fa15fb7aac..a4dd32b3d176 100644 --- a/tests/integration/plugins/automatic/prettier-plugin-baz.js +++ b/tests/integration/plugins/automatic/prettier-plugin-baz.js @@ -1,4 +1,4 @@ -import createPlugin from "../../../config/utils/create-plugin.cjs"; +import createPlugin from "../../../config/utilities/create-plugin.cjs"; const plugin = createPlugin({ name: "baz", diff --git a/tests/integration/plugins/languages/is-supported.js b/tests/integration/plugins/languages/is-supported.js index 5db7588f85c2..6bea9766523b 100644 --- a/tests/integration/plugins/languages/is-supported.js +++ b/tests/integration/plugins/languages/is-supported.js @@ -1,5 +1,5 @@ import path from "node:path"; -import createPlugin from "../../../config/utils/create-plugin.cjs"; +import createPlugin from "../../../config/utilities/create-plugin.cjs"; const PARSER_NAME = "parser-name-inferred-from-language-is-supported"; const PRINT_MARK = `formatted by '${PARSER_NAME}' parser`; diff --git a/tests/unit/__snapshots__/visitor-keys.js.snap b/tests/unit/__snapshots__/visitor-keys.js.snap index 0d816d2eb97f..75c96533b074 100644 --- a/tests/unit/__snapshots__/visitor-keys.js.snap +++ b/tests/unit/__snapshots__/visitor-keys.js.snap @@ -1668,12 +1668,10 @@ exports[`visitor keys yaml 1`] = ` "trailingComment", ], "anchor": [ - "anchor", "endComments", "indicatorComment", "leadingComments", "middleComments", - "tag", "trailingComment", ], "blockFolded": [ @@ -1892,12 +1890,10 @@ exports[`visitor keys yaml 1`] = ` "trailingComment", ], "tag": [ - "anchor", "endComments", "indicatorComment", "leadingComments", "middleComments", - "tag", "trailingComment", ], } diff --git a/tests/unit/get-descendants.js b/tests/unit/get-descendants.js index 521d5999c6a5..037b15d1b1ed 100644 --- a/tests/unit/get-descendants.js +++ b/tests/unit/get-descendants.js @@ -1,4 +1,4 @@ -import { getDescendants } from "../../src/utils/ast-utils.js"; +import { getDescendants } from "../../src/utilities/ast.js"; const tree = { id: "tree", diff --git a/tests/unit/get-parser-plugin-by-parser-name.js b/tests/unit/get-parser-plugin-by-parser-name.js index cf3fdd2a4963..5abf2640486c 100644 --- a/tests/unit/get-parser-plugin-by-parser-name.js +++ b/tests/unit/get-parser-plugin-by-parser-name.js @@ -1,6 +1,6 @@ import { ConfigError } from "../../src/common/errors.js"; import { getParserPluginByParserName } from "../../src/main/parser-and-printer.js"; -import createPlugin from "../config/utils/create-plugin.cjs"; +import createPlugin from "../config/utilities/create-plugin.cjs"; describe("unit tests for getParserPluginByParserName", () => { const getMockPlugins = (name) => [ diff --git a/tests/unit/get-printer-plugin-by-ast-format.js b/tests/unit/get-printer-plugin-by-ast-format.js index 61f75e735a62..d257293c3678 100644 --- a/tests/unit/get-printer-plugin-by-ast-format.js +++ b/tests/unit/get-printer-plugin-by-ast-format.js @@ -1,6 +1,6 @@ import { ConfigError } from "../../src/common/errors.js"; import { getPrinterPluginByAstFormat } from "../../src/main/parser-and-printer.js"; -import createPlugin from "../config/utils/create-plugin.cjs"; +import createPlugin from "../config/utilities/create-plugin.cjs"; describe("unit tests for getPrinterPluginByAstFormat", () => { const getMockPlugins = (name) => [ diff --git a/tests/unit/make-string.js b/tests/unit/make-string.js index b8f04bf667cd..206a1efae52f 100644 --- a/tests/unit/make-string.js +++ b/tests/unit/make-string.js @@ -1,4 +1,4 @@ -import makeString from "../../src/utils/make-string.js"; +import makeString from "../../src/utilities/make-string.js"; const DOUBLE_QUOTE = '"'; const SINGLE_QUOTE = "'"; diff --git a/tests/unit/visitor-keys.js b/tests/unit/visitor-keys.js index 5411b42b79f4..cce4eb192892 100644 --- a/tests/unit/visitor-keys.js +++ b/tests/unit/visitor-keys.js @@ -1,11 +1,11 @@ -import postcssVisitorKeys from "../../src/language-css/visitor-keys.js"; +import postcssVisitorKeys from "../../src/language-css/visitor-keys.evaluate.js"; import graphqlVisitorKeys from "../../src/language-graphql/visitor-keys.js"; -import glimmerVisitorKeys from "../../src/language-handlebars/visitor-keys.evaluate.js"; -import htmlVisitorKeys from "../../src/language-html/visitor-keys.js"; +import glimmerVisitorKeys from "../../src/language-handlebars/visitor-keys.js"; +import htmlVisitorKeys from "../../src/language-html/visitor-keys.evaluate.js"; import estreeVisitorKeys from "../../src/language-js/traverse/visitor-keys.evaluate.js"; -import jsonVisitorKeys from "../../src/language-json/visitor-keys.js"; -import remarkVisitorKeys from "../../src/language-markdown/visitor-keys.js"; -import yamlVisitorKeys from "../../src/language-yaml/visitor-keys.js"; +import jsonVisitorKeys from "../../src/language-json/visitor-keys.evaluate.js"; +import remarkVisitorKeys from "../../src/language-markdown/visitor-keys.evaluate.js"; +import yamlVisitorKeys from "../../src/language-yaml/visitor-keys.evaluate.js"; // Keep eye on package change describe("visitor keys", () => { diff --git a/tests/unit/whitespace-utils.js b/tests/unit/whitespace-utilities.js similarity index 73% rename from tests/unit/whitespace-utils.js rename to tests/unit/whitespace-utilities.js index 47193ce46b74..b2d22cd37f3b 100644 --- a/tests/unit/whitespace-utils.js +++ b/tests/unit/whitespace-utilities.js @@ -1,20 +1,22 @@ -import WhitespaceUtils from "../../src/utils/whitespace-utils.js"; +import WhitespaceUtilities from "../../src/utilities/whitespace-utilities.js"; it("constructor", () => { - expect(() => new WhitespaceUtils()).toThrow(TypeError); - expect(() => new WhitespaceUtils(1)).toThrow(TypeError); - expect(() => new WhitespaceUtils("")).toThrow(TypeError); - expect(() => new WhitespaceUtils([])).toThrow(TypeError); - expect(() => new WhitespaceUtils([""])).toThrow(TypeError); - expect(() => new WhitespaceUtils(["a"])).toThrow(TypeError); - expect(() => new WhitespaceUtils("a")).toThrow(TypeError); - expect(() => new WhitespaceUtils(["\r\n"])).toThrow(TypeError); + expect(() => new WhitespaceUtilities()).toThrow(TypeError); + expect(() => new WhitespaceUtilities(1)).toThrow(TypeError); + expect(() => new WhitespaceUtilities("")).toThrow(TypeError); + expect(() => new WhitespaceUtilities([])).toThrow(TypeError); + expect(() => new WhitespaceUtilities([""])).toThrow(TypeError); + expect(() => new WhitespaceUtilities(["a"])).toThrow(TypeError); + expect(() => new WhitespaceUtilities("a")).toThrow(TypeError); + expect(() => new WhitespaceUtilities(["\r\n"])).toThrow(TypeError); - expect(new WhitespaceUtils("\r\n")).toBeInstanceOf(WhitespaceUtils); - expect(new WhitespaceUtils(["\r", "\n"])).toBeInstanceOf(WhitespaceUtils); + expect(new WhitespaceUtilities("\r\n")).toBeInstanceOf(WhitespaceUtilities); + expect(new WhitespaceUtilities(["\r", "\n"])).toBeInstanceOf( + WhitespaceUtilities, + ); }); -const utils = new WhitespaceUtils(" "); +const utils = new WhitespaceUtilities(" "); describe("trimStart", () => { for (const [string, expected] of [ ["", ""], diff --git a/website/blog/2023-07-05-3.0.0.md b/website/blog/2023-07-05-3.0.0.md index fdf99b40ec6c..104a4639fd32 100644 --- a/website/blog/2023-07-05-3.0.0.md +++ b/website/blog/2023-07-05-3.0.0.md @@ -2092,14 +2092,14 @@ Align with other tools like ESLint and Stylelint. ```jsx // Prettier 2.8 Checking formatting... -[warn] src\utils\create-get-visitor-keys.js -[warn] src\utils\unexpected-node-error.js +[warn] src\utilities\create-get-visitor-keys.js +[warn] src\utilities\unexpected-node-error.js [warn] Code style issues found in 2 files. Forgot to run Prettier? // Prettier 3.0 Checking formatting... -[warn] src/utils/create-get-visitor-keys.js -[warn] src/utils/unexpected-node-error.js +[warn] src/utilities/create-get-visitor-keys.js +[warn] src/utilities/unexpected-node-error.js [warn] Code style issues found in 2 files. Forgot to run Prettier? ``` diff --git a/website/package.json b/website/package.json index 257d507f0cf4..5807f05a049f 100644 --- a/website/package.json +++ b/website/package.json @@ -34,7 +34,7 @@ "@vitejs/plugin-react": "5.1.1", "concurrently": "9.2.1", "vite": "7.2.4", - "yaml": "2.8.1" + "yaml": "2.8.2" }, "browserslist": { "production": [ diff --git a/website/playground/Playground.jsx b/website/playground/Playground.jsx index 5edd3b20125c..190bfa101f42 100644 --- a/website/playground/Playground.jsx +++ b/website/playground/Playground.jsx @@ -11,7 +11,14 @@ import { Checkbox } from "./sidebar/inputs.jsx"; import Option from "./sidebar/options.jsx"; import SidebarOptions from "./sidebar/SidebarOptions.jsx"; import * as urlHash from "./urlHash.js"; -import * as util from "./util.js"; +import { + buildCliArgs, + convertOffsetToSelection, + convertSelectionToRange, + getAstAutoFold, + getCodemirrorMode, + getDefaults, +} from "./utilities.js"; const { React } = window; @@ -33,6 +40,7 @@ const ENABLED_OPTIONS = [ "printWidth", "tabWidth", "useTabs", + "endOfLine", "semi", "singleQuote", "bracketSpacing", @@ -61,10 +69,7 @@ class Playground extends React.Component { const original = urlHash.read(); - const defaultOptions = util.getDefaults( - props.availableOptions, - ENABLED_OPTIONS, - ); + const defaultOptions = getDefaults(props.availableOptions, ENABLED_OPTIONS); const options = Object.assign(defaultOptions, original.options); @@ -110,13 +115,13 @@ class Playground extends React.Component { if (this.state.trackCursorOffset) { this.handleOptionValueChange( this.cursorOffsetOption, - util.convertSelectionToRange(selection, this.state.content)[0], + convertSelectionToRange(selection, this.state.content)[0], ); } }; this.setSelectionAsRange = () => { const { selection, content, options } = this.state; - const [rangeStart, rangeEnd] = util.convertSelectionToRange( + const [rangeStart, rangeEnd] = convertSelectionToRange( selection, content, ); @@ -182,7 +187,7 @@ class Playground extends React.Component { "rangeEnd", "cursorOffset", ]); - const cliOptions = util.buildCliArgs(orderedOptions, options); + const cliOptions = buildCliArgs(orderedOptions, options); return formatMarkdown({ input: content, @@ -207,7 +212,7 @@ class Playground extends React.Component { return this.props.worker .format(content, { parser: "__js_expression", - cursorOffset: util.convertSelectionToRange(selection, content)[0], + cursorOffset: convertSelectionToRange(selection, content)[0], }) .then(({ error, formatted, cursorOffset }) => { if (error) { @@ -216,7 +221,7 @@ class Playground extends React.Component { this.setState({ content: formatted, - selection: util.convertOffsetToSelection(cursorOffset, formatted), + selection: convertOffsetToSelection(cursorOffset, formatted), }); }); } @@ -224,13 +229,13 @@ class Playground extends React.Component { insertDummyId() { const { content, selection } = this.state; const dummyId = generateDummyId(); - const range = util.convertSelectionToRange(selection, content); + const range = convertSelectionToRange(selection, content); const modifiedContent = content.slice(0, range[0]) + dummyId + content.slice(range[1]); this.setState({ content: modifiedContent, - selection: util.convertOffsetToSelection( + selection: convertOffsetToSelection( range[0] + dummyId.length, modifiedContent, ), @@ -420,7 +425,7 @@ class Playground extends React.Component {
{editorState.showInput ? ( ) : null} {editorState.showPreprocessedAst && !isDocExplorer ? ( ) : null} {editorState.showDoc && !isDocExplorer ? ( @@ -454,12 +459,12 @@ class Playground extends React.Component { {editorState.showComments && !isDocExplorer ? ( ) : null} {editorState.showOutput ? ( diff --git a/website/playground/index.jsx b/website/playground/index.jsx index ef86d50d9a9d..f26d4b325468 100644 --- a/website/playground/index.jsx +++ b/website/playground/index.jsx @@ -2,7 +2,7 @@ import "codemirror-graphql/cm6-legacy/mode.esm.js"; import "./install-service-worker.js"; import Playground from "./Playground.jsx"; -import { fixPrettierVersion } from "./util.js"; +import { fixPrettierVersion } from "./utilities.js"; import VersionLink from "./VersionLink.jsx"; import WorkerApi from "./WorkerApi.js"; diff --git a/website/playground/sidebar/SidebarOptions.jsx b/website/playground/sidebar/SidebarOptions.jsx index f3f8bdbc70d1..f3b01d440feb 100644 --- a/website/playground/sidebar/SidebarOptions.jsx +++ b/website/playground/sidebar/SidebarOptions.jsx @@ -1,7 +1,7 @@ import { SidebarCategory } from "./components.jsx"; import Option from "./options.jsx"; -// Copied from `/src/cli/utils.js` +// Copied from `/src/cli/utilities.js` function groupBy(array, iteratee) { const result = Object.create(null); diff --git a/website/playground/util.js b/website/playground/utilities.js similarity index 100% rename from website/playground/util.js rename to website/playground/utilities.js diff --git a/website/versioned_docs/version-stable/browser.md b/website/versioned_docs/version-stable/browser.md index 379ac6ccaf28..723421131171 100644 --- a/website/versioned_docs/version-stable/browser.md +++ b/website/versioned_docs/version-stable/browser.md @@ -21,7 +21,7 @@ Required options: - **[`parser`](options.md#parser) (or [`filepath`](options.md#file-path))**: One of these options has to be specified for Prettier to know which parser to use. -- **`plugins`**: Unlike the `format` function from the [Node.js-based API](api.md#prettierformatsource-options), this function doesn’t load plugins automatically. The `plugins` option is required because all the parsers included in the Prettier package come as plugins (for reasons of file size). These plugins are files in [https://unpkg.com/browse/prettier@3.7.3/plugins](https://unpkg.com/browse/prettier@3.7.3/plugins). Note that `estree` plugin should be loaded when printing JavaScript, TypeScript, Flow, or JSON. +- **`plugins`**: Unlike the `format` function from the [Node.js-based API](api.md#prettierformatsource-options), this function doesn’t load plugins automatically. The `plugins` option is required because all the parsers included in the Prettier package come as plugins (for reasons of file size). These plugins are files in [https://unpkg.com/browse/prettier@3.7.4/plugins](https://unpkg.com/browse/prettier@3.7.4/plugins). Note that `estree` plugin should be loaded when printing JavaScript, TypeScript, Flow, or JSON. You need to load the ones that you’re going to use and pass them to `prettier.format` using the `plugins` option. @@ -32,8 +32,8 @@ See below for examples. ### Global ```html - - + +