Skip to content

Commit

Permalink
Check type declarations with JSDoc comments (#10)
Browse files Browse the repository at this point in the history
Co-authored-by: fisker <lionkay@gmail.com>
  • Loading branch information
colinrotherham and fisker committed Jul 17, 2023
1 parent 6842eaa commit 253edba
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 26 deletions.
78 changes: 64 additions & 14 deletions index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@ const {
const url = require("url");
const path = require("path");

const PRETTIER_ASYNC_FUNCTIONS = [
/**
@template {keyof PrettierSynchronizedFunctions} FunctionName
@typedef {(...args: Parameters<Prettier[FunctionName]>) => Awaited<ReturnType<Prettier[FunctionName]>> } PrettierSyncFunction
*/

/**
@typedef {import("prettier")} Prettier
@typedef {{ [FunctionName in typeof PRETTIER_ASYNC_FUNCTIONS[number]]: PrettierSyncFunction<FunctionName> }} PrettierSynchronizedFunctions
@typedef {{ [PropertyName in typeof PRETTIER_STATIC_PROPERTIES[number]]: Prettier[PropertyName] }} PrettierStaticProperties
@typedef {PrettierSynchronizedFunctions & PrettierStaticProperties} SynchronizedPrettier
*/

const PRETTIER_ASYNC_FUNCTIONS = /** @type {const} */ ([
"formatWithCursor",
"format",
"check",
Expand All @@ -17,10 +29,15 @@ const PRETTIER_ASYNC_FUNCTIONS = [
"clearConfigCache",
"getFileInfo",
"getSupportInfo",
];
]);

const PRETTIER_STATIC_PROPERTIES = ["version", "util", "doc"];
const PRETTIER_STATIC_PROPERTIES = /** @type {const} */ ([
"version",
"util",
"doc",
]);

/** @type {Worker | undefined} */
let worker;
function createWorker() {
if (!worker) {
Expand All @@ -31,7 +48,13 @@ function createWorker() {
return worker;
}

function createSyncFunction(functionName, prettierEntry) {
/**
* @template {keyof PrettierSynchronizedFunctions} FunctionName
* @param {FunctionName} functionName
* @param {string} prettierEntry
* @returns {PrettierSyncFunction<FunctionName>}
*/
function createSynchronizedFunction(functionName, prettierEntry) {
return (...args) => {
const signal = new Int32Array(new SharedArrayBuffer(4));
const { port1: localPort, port2: workerPort } = new MessageChannel();
Expand All @@ -56,10 +79,19 @@ function createSyncFunction(functionName, prettierEntry) {
};
}

/**
* @template {keyof PrettierStaticProperties} Property
* @param {Property} property
* @param {string} prettierEntry
*/
function getProperty(property, prettierEntry) {
return require(prettierEntry)[property];
return /** @type {Prettier} */ (require(prettierEntry))[property];
}

/**
* @template {keyof SynchronizedPrettier} ExportName
* @param {() => SynchronizedPrettier[ExportName]} getter
*/
function createDescriptor(getter) {
let value;
return {
Expand All @@ -71,6 +103,9 @@ function createDescriptor(getter) {
};
}

/**
* @param {string | URL} entry
*/
function toImportId(entry) {
if (entry instanceof URL) {
return entry.href;
Expand All @@ -83,6 +118,9 @@ function toImportId(entry) {
return entry;
}

/**
* @param {string | URL} entry
*/
function toRequireId(entry) {
if (entry instanceof URL || entry.startsWith("file:")) {
return url.fileURLToPath(entry);
Expand All @@ -91,6 +129,11 @@ function toRequireId(entry) {
return entry;
}

/**
* @param {object} options
* @param {string | URL} options.prettierEntry - Path or URL to prettier entry.
* @returns {SynchronizedPrettier}
*/
function createSynchronizedPrettier({ prettierEntry }) {
const importId = toImportId(prettierEntry);
const requireId = toRequireId(prettierEntry);
Expand All @@ -99,20 +142,27 @@ function createSynchronizedPrettier({ prettierEntry }) {
Object.create(null),
Object.fromEntries(
[
...PRETTIER_ASYNC_FUNCTIONS.map((functionName) => [
functionName,
() => createSyncFunction(functionName, importId),
]),
...PRETTIER_STATIC_PROPERTIES.map((property) => [
property,
() => getProperty(property, requireId),
]),
].map(([property, getter]) => [property, createDescriptor(getter)]),
...PRETTIER_ASYNC_FUNCTIONS.map((functionName) => {
return /** @type {const} */ ([
functionName,
() => createSynchronizedFunction(functionName, importId),
]);
}),
...PRETTIER_STATIC_PROPERTIES.map((property) => {
return /** @type {const} */ ([
property,
() => getProperty(property, requireId),
]);
}),
].map(([property, getter]) => {
return /** @type {const} */ ([property, createDescriptor(getter)]);
}),
),
);

return prettier;
}

module.exports = createSynchronizedPrettier({ prettierEntry: "prettier" });
// @ts-expect-error Property 'createSynchronizedPrettier' for named export compatibility
module.exports.createSynchronizedPrettier = createSynchronizedPrettier;
40 changes: 40 additions & 0 deletions index.d.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
type Prettier = typeof import("prettier");

type SynchronizedPrettier = {
// Prettier static properties
version: Prettier["version"];
util: Prettier["util"];
doc: Prettier["doc"];

// Prettier functions
formatWithCursor: PrettierSynchronizedFunction<"formatWithCursor">;
format: PrettierSynchronizedFunction<"format">;
check: PrettierSynchronizedFunction<"check">;
resolveConfig: PrettierSynchronizedFunction<"resolveConfig">;
resolveConfigFile: PrettierSynchronizedFunction<"resolveConfigFile">;
clearConfigCache: PrettierSynchronizedFunction<"clearConfigCache">;
getFileInfo: PrettierSynchronizedFunction<"getFileInfo">;
getSupportInfo: PrettierSynchronizedFunction<"getSupportInfo">;
};

type PrettierSynchronizedFunction<
FunctionName extends
| "formatWithCursor"
| "format"
| "check"
| "resolveConfig"
| "resolveConfigFile"
| "clearConfigCache"
| "getFileInfo"
| "getSupportInfo",
> = (
...args: Parameters<Prettier[FunctionName]>
) => Awaited<ReturnType<Prettier[FunctionName]>>;

declare const synchronizedPrettier: SynchronizedPrettier & {
createSynchronizedPrettier: (options: {
prettierEntry: string | URL;
}) => SynchronizedPrettier;
};

export = synchronizedPrettier;
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,17 @@
},
"sideEffects": false,
"type": "module",
"exports": "./index.cjs",
"main": "./index.cjs",
"exports": {
".": {
"types": "./index.d.cts",
"default": "./index.cjs"
},
"./*": "./*"
},
"files": [
"index.cjs",
"index.d.cts",
"worker.js"
],
"scripts": {
Expand All @@ -39,6 +47,7 @@
"prettier": "^3.0.0"
},
"devDependencies": {
"@types/node": "20.4.1",
"c8": "8.0.0",
"npm-run-all": "4.1.5",
"prettier": "3.0.0"
Expand Down
8 changes: 4 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ yarn add prettier @prettier/sync
## Usage

```js
import prettierSync from "@prettier/sync";
import synchronizedPrettier from "@prettier/sync";

prettierSync.format("foo( )", { parser: "babel" });
synchronizedPrettier.format("foo( )", { parser: "babel" });
// => 'foo();\n'
```

Expand All @@ -46,10 +46,10 @@ Path or URL to prettier entry.
```js
import { createSynchronizedPrettier } from "@prettier/sync";

const prettierSync = createSynchronizedPrettier({
const synchronizedPrettier = createSynchronizedPrettier({
prettierEntry: "/path/to/prettier/index.js",
});

prettierSync.format("foo( )", { parser: "babel" });
synchronizedPrettier.format("foo( )", { parser: "babel" });
// => 'foo();\n'
```
16 changes: 9 additions & 7 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@ import assert from "node:assert/strict";
import fs from "node:fs/promises";
import { fileURLToPath } from "node:url";
import prettier from "prettier";
import prettierSync, { createSynchronizedPrettier } from "./index.cjs";
import synchronizedPrettier, { createSynchronizedPrettier } from "./index.cjs";

test("format", async () => {
const code = await fs.readFile("./index.cjs", "utf8");
const formatOptions = { parser: "meriyah" };
assert.equal(
prettierSync.format(code, formatOptions),
synchronizedPrettier.format(code, formatOptions),
await prettier.format(code, formatOptions),
);

let error;
try {
prettierSync.format("foo(", formatOptions);
synchronizedPrettier.format("foo(", formatOptions);
} catch (formatError) {
error = formatError;
}
assert.deepEqual(error.loc, { start: { line: 1, column: 4 } });
});

test("version", () => {
assert.equal(prettierSync.version, prettier.version);
assert.equal(synchronizedPrettier.version, prettier.version);
});

{
Expand All @@ -37,10 +37,12 @@ test("version", () => {
fileURLToPath(fakePrettierUrl),
]) {
test(prettierEntry, async () => {
const fakePrettierSync = createSynchronizedPrettier({ prettierEntry });
assert.equal(fakePrettierSync.version, fakePrettier.version);
const fakeSynchronizedPrettier = createSynchronizedPrettier({
prettierEntry,
});
assert.equal(fakeSynchronizedPrettier.version, fakePrettier.version);
assert.equal(
fakePrettierSync.format("code"),
fakeSynchronizedPrettier.format("code"),
await fakePrettier.format("code"),
);
});
Expand Down
10 changes: 10 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noEmit": true,
"target": "esnext",
"module": "NodeNext"
},
"files": ["index.cjs"]
}

0 comments on commit 253edba

Please sign in to comment.