├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── continuous-integration.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .yarn └── releases │ └── yarn-4.7.0.cjs ├── .yarnrc ├── .yarnrc.yml ├── a-fake-prettier-to-test.cjs ├── examples ├── format-file.js └── run-format-file.js ├── index.cjs ├── index.d.cts ├── license ├── package.json ├── prettier.config.js ├── readme.md ├── test.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # config file for `editorconfig` 2 | # 3 | # update: wget -O .editorconfig https://git.io/fjVjz 4 | # document: https://editorconfig.org 5 | # 6 | 7 | # top-most EditorConfig file 8 | root = true 9 | 10 | # all files 11 | [*] 12 | 13 | # Set default charset 14 | charset = utf-8 15 | 16 | # Unix-style newlines 17 | end_of_line = lf 18 | 19 | # with a newline ending every file 20 | insert_final_newline = true 21 | 22 | # 2 space indentation 23 | indent_style = space 24 | indent_size = 2 25 | 26 | # remove any whitespace characters preceding newline characters 27 | trim_trailing_whitespace = true 28 | 29 | # overrides 30 | 31 | # python overrides 32 | [*.py] 33 | indent_size = 4 34 | 35 | # cmd overrides 36 | [*.{bat,cmd}] 37 | charset = ansi 38 | end_of_line = crlf 39 | 40 | # markdown 41 | [*.{md,markdown}] 42 | insert_final_newline = false 43 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | # schedule: 9 | # - cron: "0 23 * * 6" 10 | 11 | jobs: 12 | test: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - "ubuntu-latest" 18 | - "macos-latest" 19 | - "windows-latest" 20 | node_version: 21 | - "22" 22 | - "20" 23 | - "18" 24 | # exclude: 25 | # - os: "macos-latest" 26 | # node_version: "12" 27 | name: Node.js ${{ matrix.node_version }} on ${{ matrix.os }} 28 | runs-on: ${{ matrix.os }} 29 | env: 30 | YARN_ENABLE_IMMUTABLE_INSTALLS: false 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: Setup Node.js 36 | uses: actions/setup-node@v4 37 | with: 38 | node-version: ${{ matrix.node_version }} 39 | 40 | - name: Install Dependencies 41 | run: yarn 42 | 43 | - name: Run Test 44 | run: yarn test-coverage 45 | 46 | - uses: codecov/codecov-action@v5 47 | with: 48 | fail_ci_if_error: true 49 | disable_search: true 50 | files: coverage/lcov.info 51 | token: ${{ secrets.CODECOV_TOKEN }} 52 | 53 | lint: 54 | name: Lint 55 | runs-on: ubuntu-latest 56 | env: 57 | YARN_ENABLE_IMMUTABLE_INSTALLS: false 58 | steps: 59 | - name: Checkout 60 | uses: actions/checkout@v4 61 | 62 | - name: Setup Node.js 63 | uses: actions/setup-node@v4 64 | 65 | - name: Install Dependencies 66 | run: yarn 67 | 68 | - name: Run Lint 69 | run: yarn lint 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore file for git project 2 | # 3 | # update: wget -O .gitignore https://git.io/fjXI5 4 | # 5 | 6 | # Base on https://www.gitignore.io/api/node 7 | 8 | ### Node ### 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | lerna-debug.log* 16 | yarn.lock 17 | 18 | # Diagnostic reports (https://nodejs.org/api/report.html) 19 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 20 | 21 | # Runtime data 22 | pids 23 | *.pid 24 | *.seed 25 | *.pid.lock 26 | 27 | # Directory for instrumented libs generated by jscoverage/JSCover 28 | lib-cov 29 | 30 | # Coverage directory used by tools like istanbul 31 | coverage 32 | 33 | # nyc test coverage 34 | .nyc_output 35 | 36 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | bower_components 41 | 42 | # node-waf configuration 43 | .lock-wscript 44 | 45 | # Compiled binary addons (https://nodejs.org/api/addons.html) 46 | build/Release 47 | 48 | # Dependency directories 49 | node_modules/ 50 | jspm_packages/ 51 | 52 | # TypeScript v1 declaration files 53 | typings/ 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional REPL history 62 | .node_repl_history 63 | 64 | # Output of 'npm pack' 65 | *.tgz 66 | 67 | # Yarn Integrity file 68 | .yarn-integrity 69 | 70 | # dotenv environment variables file 71 | .env 72 | .env.test 73 | 74 | # parcel-bundler cache (https://parceljs.org/) 75 | .cache 76 | 77 | # next.js build output 78 | .next 79 | 80 | # nuxt.js build output 81 | .nuxt 82 | 83 | # vuepress build output 84 | .vuepress/dist 85 | 86 | # Serverless directories 87 | .serverless/ 88 | 89 | # FuseBox cache 90 | .fusebox/ 91 | 92 | # DynamoDB Local files 93 | .dynamodb/ 94 | 95 | # Yarn 2 96 | .yarn/cache/ 97 | .yarn/build-state.yml 98 | .yarn/install-state.gz 99 | .pnp.js 100 | 101 | # project ignore files 102 | dist/ 103 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-prefix="" 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # ignore file for `prettier` 2 | # 3 | # update: wget -O .prettierignore https://git.io/fjVj5 4 | # document: https://prettier.io/docs/en/ignore.html#ignoring-files 5 | # 6 | 7 | # also prettier dot files 8 | !.* 9 | 10 | # vendors 11 | **/node_modules/** 12 | **/vendors/** 13 | **/vendor/** 14 | **/third-party/** 15 | 16 | # build file 17 | **/dist/** 18 | **/*.min.* 19 | 20 | # fixtures 21 | **/fixtures/** 22 | 23 | # test 24 | .nyc_output/** 25 | coverage/** 26 | 27 | # Diagnostic reports (https://nodejs.org/api/report.html) 28 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 29 | 30 | # yarn 31 | .yarn 32 | 33 | # project glob 34 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | save-prefix "" 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | defaultSemverRangePrefix: "" 4 | 5 | enableGlobalCache: true 6 | 7 | nodeLinker: node-modules 8 | 9 | yarnPath: .yarn/releases/yarn-4.7.0.cjs 10 | -------------------------------------------------------------------------------- /a-fake-prettier-to-test.cjs: -------------------------------------------------------------------------------- 1 | module.exports.version = "version"; 2 | module.exports.format = (code) => Promise.resolve(code); 3 | -------------------------------------------------------------------------------- /examples/format-file.js: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs/promises"; 2 | import * as prettier from "prettier"; 3 | import makeSynchronized from "make-synchronized"; 4 | 5 | export default makeSynchronized(import.meta, async function formatFile(file) { 6 | const config = await prettier.resolveConfig(file); 7 | const content = await fs.readFile(file, "utf8"); 8 | const formatted = await prettier.format(content, { 9 | ...config, 10 | filepath: file, 11 | }); 12 | await fs.writeFile(file, formatted); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/run-format-file.js: -------------------------------------------------------------------------------- 1 | import * as url from "node:url"; 2 | import formatFile from "./format-file.js"; 3 | 4 | console.log( 5 | formatFile(url.fileURLToPath(new URL("../readme.md", import.meta.url))), 6 | ); 7 | -------------------------------------------------------------------------------- /index.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { makeModuleSynchronized } = require("make-synchronized"); 4 | 5 | function createSynchronizedPrettier({ prettierEntry }) { 6 | return makeModuleSynchronized(prettierEntry); 7 | } 8 | 9 | module.exports = createSynchronizedPrettier({ prettierEntry: "prettier" }); 10 | module.exports.createSynchronizedPrettier = createSynchronizedPrettier; 11 | -------------------------------------------------------------------------------- /index.d.cts: -------------------------------------------------------------------------------- 1 | type Prettier = typeof import("prettier"); 2 | 3 | type SynchronizedPrettier = { 4 | // Prettier static properties 5 | version: Prettier["version"]; 6 | util: Prettier["util"]; 7 | doc: Prettier["doc"]; 8 | 9 | // Prettier functions 10 | formatWithCursor: PrettierSynchronizedFunction<"formatWithCursor">; 11 | format: PrettierSynchronizedFunction<"format">; 12 | check: PrettierSynchronizedFunction<"check">; 13 | resolveConfig: PrettierSynchronizedFunction<"resolveConfig">; 14 | resolveConfigFile: PrettierSynchronizedFunction<"resolveConfigFile">; 15 | clearConfigCache: PrettierSynchronizedFunction<"clearConfigCache">; 16 | getFileInfo: PrettierSynchronizedFunction<"getFileInfo">; 17 | getSupportInfo: PrettierSynchronizedFunction<"getSupportInfo">; 18 | }; 19 | 20 | type PrettierSynchronizedFunction< 21 | FunctionName extends 22 | | "formatWithCursor" 23 | | "format" 24 | | "check" 25 | | "resolveConfig" 26 | | "resolveConfigFile" 27 | | "clearConfigCache" 28 | | "getFileInfo" 29 | | "getSupportInfo", 30 | > = ( 31 | ...args: Parameters 32 | ) => Awaited>; 33 | 34 | declare const synchronizedPrettier: SynchronizedPrettier & { 35 | createSynchronizedPrettier: (options: { 36 | prettierEntry: string | URL; 37 | }) => SynchronizedPrettier; 38 | }; 39 | 40 | export = synchronizedPrettier; 41 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) fisker Cheung (https://www.fiskercheung.com/) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@prettier/sync", 3 | "version": "0.5.5", 4 | "description": "Synchronous version of Prettier", 5 | "homepage": "https://github.com/prettier/prettier-synchronized#readme", 6 | "bugs": { 7 | "url": "https://github.com/prettier/prettier-synchronized/issues" 8 | }, 9 | "repository": "prettier/prettier-synchronized", 10 | "funding": "https://github.com/prettier/prettier-synchronized?sponsor=1", 11 | "license": "MIT", 12 | "author": { 13 | "name": "fisker Cheung", 14 | "email": "lionkay@gmail.com", 15 | "url": "https://www.fiskercheung.com/" 16 | }, 17 | "sideEffects": false, 18 | "type": "module", 19 | "main": "./index.cjs", 20 | "exports": { 21 | ".": { 22 | "types": "./index.d.cts", 23 | "default": "./index.cjs" 24 | }, 25 | "./*": "./*" 26 | }, 27 | "files": [ 28 | "index.cjs", 29 | "index.d.cts" 30 | ], 31 | "scripts": { 32 | "dist": "release-it", 33 | "fix": "prettier . --write", 34 | "lint": "prettier . --check", 35 | "test": "node --test", 36 | "test-coverage": "c8 node --test", 37 | "release": "run-s lint test dist" 38 | }, 39 | "c8": { 40 | "reporter": [ 41 | "lcov", 42 | "text" 43 | ] 44 | }, 45 | "peerDependencies": { 46 | "prettier": "*" 47 | }, 48 | "devDependencies": { 49 | "@types/node": "22.13.13", 50 | "c8": "10.1.3", 51 | "npm-run-all": "4.1.5", 52 | "prettier": "3.5.3", 53 | "release-it": "18.1.2" 54 | }, 55 | "packageManager": "yarn@4.7.0", 56 | "publishConfig": { 57 | "access": "public", 58 | "registry": "https://registry.npmjs.org/" 59 | }, 60 | "dependencies": { 61 | "make-synchronized": "^0.4.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # @prettier/sync 2 | 3 | [![Build Status][github_actions_badge]][github_actions_link] 4 | [![Coverage][codecov_badge]][codecov_link] 5 | [![Npm Version][package_version_badge]][package_link] 6 | [![MIT License][license_badge]][license_link] 7 | 8 | [github_actions_badge]: https://img.shields.io/github/actions/workflow/status/prettier/prettier-synchronized/continuous-integration.yml?style=flat-square 9 | [github_actions_link]: https://github.com/prettier/prettier-synchronized/actions?query=branch%3Amain 10 | [codecov_badge]: https://img.shields.io/codecov/c/github/prettier/prettier-synchronized?style=flat-square 11 | [codecov_link]: https://codecov.io/gh/prettier/prettier-synchronized 12 | [license_badge]: https://img.shields.io/npm/l/@prettier/sync.svg?style=flat-square 13 | [license_link]: https://github.com/prettier/prettier-synchronized/blob/main/license 14 | [package_version_badge]: https://img.shields.io/npm/v/@prettier/sync.svg?style=flat-square 15 | [package_link]: https://www.npmjs.com/package/@prettier/sync 16 | 17 | > Synchronous version of Prettier 18 | 19 | ## Installation 20 | 21 | ```sh 22 | yarn add prettier @prettier/sync 23 | ``` 24 | 25 | ## Usage 26 | 27 | ```js 28 | import synchronizedPrettier from "@prettier/sync"; 29 | 30 | synchronizedPrettier.format("foo( )", { parser: "babel" }); 31 | // => 'foo();\n' 32 | ``` 33 | 34 | This package is a simple wrapper of [`make-synchronized`](https://github.com/fisker/make-synchronized). 35 | 36 | For more complex use cases, it's better to use [`make-synchronized`](https://github.com/fisker/make-synchronized) directly: 37 | 38 | Example 1: 39 | 40 | ```js 41 | // utilities.js 42 | import * as fs from "node:fs/promises"; 43 | import * as prettier from "prettier"; 44 | 45 | export async function formatFile(file) { 46 | const config = await prettier.resolveConfig(file); 47 | const content = await fs.readFile(file, "utf8"); 48 | const formatted = await prettier.format(content, { 49 | ...config, 50 | filepath: file, 51 | }); 52 | await fs.writeFile(file, formatted); 53 | } 54 | 55 | // index.js 56 | import makeSynchronized from "make-synchronized"; 57 | 58 | const utilities = makeSynchronized(new URL("./utilities.js", import.meta.url)); 59 | 60 | utilities.formatFile("/path/to/file.js"); 61 | ``` 62 | 63 | Example 2: 64 | 65 | ```js 66 | import * as fs from "node:fs/promises"; 67 | import * as prettier from "prettier"; 68 | import makeSynchronized from "make-synchronized"; 69 | 70 | export default makeSynchronized(import.meta, async function formatFile(file) { 71 | const config = await prettier.resolveConfig(file); 72 | const content = await fs.readFile(file, "utf8"); 73 | const formatted = await prettier.format(content, { 74 | ...config, 75 | filepath: file, 76 | }); 77 | await fs.writeFile(file, formatted); 78 | }); 79 | ``` 80 | 81 | Alternatively you can also use [`synckit`](https://github.com/un-ts/synckit) or [`make-synchronous`](https://github.com/sindresorhus/make-synchronous). 82 | 83 | ### `createSynchronizedPrettier(options)` 84 | 85 | #### `options` 86 | 87 | Type: `object` 88 | 89 | ##### `prettierEntry` 90 | 91 | Type: `string | URL` 92 | 93 | Path or URL to prettier entry. 94 | 95 | ```js 96 | import { createSynchronizedPrettier } from "@prettier/sync"; 97 | 98 | const synchronizedPrettier = createSynchronizedPrettier({ 99 | prettierEntry: "/path/to/prettier/index.js", 100 | }); 101 | 102 | synchronizedPrettier.format("foo( )", { parser: "babel" }); 103 | // => 'foo();\n' 104 | ``` 105 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from "node:test"; 2 | import assert from "node:assert/strict"; 3 | import fs from "node:fs/promises"; 4 | import { fileURLToPath } from "node:url"; 5 | import prettier from "prettier"; 6 | import synchronizedPrettier, { createSynchronizedPrettier } from "./index.cjs"; 7 | import fakePrettier from "./a-fake-prettier-to-test.cjs"; 8 | 9 | const code = await fs.readFile("./index.cjs", "utf8"); 10 | 11 | test("format", async () => { 12 | const formatOptions = { parser: "meriyah" }; 13 | const formattedBySynchronizedPrettier = synchronizedPrettier.format( 14 | code, 15 | formatOptions, 16 | ); 17 | assert.equal(typeof formattedBySynchronizedPrettier, "string"); 18 | 19 | const formattedByPrettier = await prettier.format(code, formatOptions); 20 | assert.equal(formattedBySynchronizedPrettier, formattedByPrettier); 21 | 22 | let error; 23 | try { 24 | synchronizedPrettier.format("foo(", formatOptions); 25 | } catch (formatError) { 26 | error = formatError; 27 | } 28 | assert.deepEqual(error.loc, { 29 | start: { line: 1, column: 4 }, 30 | end: { line: 1, column: 5 }, 31 | }); 32 | }); 33 | 34 | test("version", () => { 35 | assert.equal(synchronizedPrettier.version, prettier.version); 36 | }); 37 | 38 | test("functions not exported directly", async () => { 39 | const parseOptions = { parser: "meriyah" }; 40 | const { ast: astParsedBySynchronizedPrettier } = 41 | synchronizedPrettier.__debug.parse(code, parseOptions); 42 | 43 | assert.equal(astParsedBySynchronizedPrettier.type, "Program"); 44 | 45 | const { ast: astParsedByPrettier } = await prettier.__debug.parse( 46 | code, 47 | parseOptions, 48 | ); 49 | assert.deepEqual(astParsedBySynchronizedPrettier, astParsedByPrettier); 50 | }); 51 | 52 | { 53 | const fakePrettierRelatedPath = "./a-fake-prettier-to-test.cjs"; 54 | const fakePrettierUrl = new URL(fakePrettierRelatedPath, import.meta.url); 55 | for (const prettierEntry of [ 56 | fakePrettierUrl, 57 | fakePrettierUrl.href, 58 | fileURLToPath(fakePrettierUrl), 59 | ]) { 60 | test(prettierEntry, async () => { 61 | const fakeSynchronizedPrettier = createSynchronizedPrettier({ 62 | prettierEntry, 63 | }); 64 | assert.equal(fakeSynchronizedPrettier.version, fakePrettier.version); 65 | assert.equal( 66 | fakeSynchronizedPrettier.format("code"), 67 | await fakePrettier.format("code"), 68 | ); 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": true, 5 | "noEmit": true, 6 | "target": "esnext", 7 | "module": "NodeNext" 8 | }, 9 | "files": ["index.cjs"] 10 | } 11 | --------------------------------------------------------------------------------