feat(npm): hook `*Conent` APIs to WASM bindings
MODIFY
.github/workflows/pull_request.yml
.github/workflows/pull_request.yml
-
# Jobs run on pull request
name: Pull request
on:

MODIFY
.github/workflows/runtime.yml
.github/workflows/runtime.yml
@@ -40,6 +40,11 @@ jobs:
- name: Install libraries
working-directory: npm/rome
run: pnpm i
+ - name: Compile WASM bindings
+ working-directory: npm/rome
+ run: |
+ pnpm build:wasm
+ pnpm i
- name: CI checks
working-directory: npm/rome
run: pnpm ci

MODIFY
npm/backend-jsonrpc/package.json
npm/backend-jsonrpc/package.json
"scripts": {
"test": "vitest",
"test:ci": "vitest --run",
- "tsc": "tsc"
+ "tsc": "tsc --noEmit",
+ "build": "tsc"
},
"homepage": "https://rome.tools",
"author": "Rome Tools Developers and Contributors",

MODIFY
npm/backend-jsonrpc/src/index.ts
npm/backend-jsonrpc/src/index.ts
import { getCommand } from "./command";
import { createSocket } from "./socket";
import { Transport } from "./transport";
-import { createWorkspace as wrapTransport, type Workspace } from "./workspace";
+import { createWorkspace as wrapTransport, type Workspace, type RomePath } from "./workspace";
/**
* Create an instance of the Workspace client connected to a remote daemon
@@ -42,3 +42,5 @@ export async function createWorkspaceWithBinary(
return wrapTransport(transport);
}
+
+export * from "./workspace";

MODIFY
npm/backend-jsonrpc/tsconfig.json
npm/backend-jsonrpc/tsconfig.json
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
- "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */

MODIFY
npm/rome/package.json
npm/rome/package.json
"format": "cargo rome-cli-dev format ./ --write",
"ci": "cargo rome-cli-dev ci ./src && pnpm test:ci && tsc --noEmit",
"check": "cargo rome-cli-dev check ./src && tsc --noEmit",
+ "build:wasm-dev": "wasm-pack build --out-dir ../../npm/wasm-nodejs --target nodejs --dev --scope rometools ../../crates/rome_wasm",
+ "build:wasm": "wasm-pack build --out-dir ../../npm/wasm-nodejs --target nodejs --release --scope rometools ../../crates/rome_wasm",
"test": "vitest",
"test:ci": "vitest --run",
"build": "tsc "
"devDependencies": {
"typescript": "^4.7.4",
"vitest": "^0.22.0",
- "vite": "^3.0.8"
+ "vite": "^3.0.8",
+ "@rometools/wasm-nodejs": "../wasm-nodejs",
+ "wasm-pack": "^0.10.3"
}
}
\ No newline at end of file

MODIFY
npm/rome/pnpm-lock.yaml
npm/rome/pnpm-lock.yaml
lockfileVersion: 5.4
specifiers:
+ '@rometools/wasm-nodejs': ../wasm-nodejs
typescript: ^4.7.4
vite: ^3.0.8
vitest: ^0.22.0
+ wasm-pack: ^0.10.3
devDependencies:
+ '@rometools/wasm-nodejs': link:../wasm-nodejs
typescript: 4.7.4
vite: 3.0.8
vitest: 0.22.0
+ wasm-pack: 0.10.3
packages:
@@ -39,6 +43,36 @@ packages:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
dev: true
+ /axios/0.21.4:
+ resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
+ dependencies:
+ follow-redirects: 1.15.1
+ transitivePeerDependencies:
+ - debug
+ dev: true
+
+ /balanced-match/1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ dev: true
+
+ /binary-install/0.1.1:
+ resolution: {integrity: sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ axios: 0.21.4
+ rimraf: 3.0.2
+ tar: 6.1.11
+ transitivePeerDependencies:
+ - debug
+ dev: true
+
+ /brace-expansion/1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+ dev: true
+
/chai/4.3.6:
resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==}
engines: {node: '>=4'}
@@ -56,6 +90,15 @@ packages:
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
dev: true
+ /chownr/2.0.0:
+ resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /concat-map/0.0.1:
+ resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
+ dev: true
+
/debug/4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@@ -284,6 +327,27 @@ packages:
esbuild-windows-arm64: 0.14.54
dev: true
+ /follow-redirects/1.15.1:
+ resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+ dev: true
+
+ /fs-minipass/2.1.0:
+ resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ minipass: 3.3.4
+ dev: true
+
+ /fs.realpath/1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+ dev: true
+
/fsevents/2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -300,6 +364,17 @@ packages:
resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
dev: true
+ /glob/7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+ dev: true
+
/has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
@@ -307,6 +382,17 @@ packages:
function-bind: 1.1.1
dev: true
+ /inflight/1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+ dev: true
+
+ /inherits/2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ dev: true
+
/is-core-module/2.10.0:
resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
dependencies:
@@ -324,6 +410,33 @@ packages:
get-func-name: 2.0.0
dev: true
+ /minimatch/3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ dependencies:
+ brace-expansion: 1.1.11
+ dev: true
+
+ /minipass/3.3.4:
+ resolution: {integrity: sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==}
+ engines: {node: '>=8'}
+ dependencies:
+ yallist: 4.0.0
+ dev: true
+
+ /minizlib/2.1.2:
+ resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ minipass: 3.3.4
+ yallist: 4.0.0
+ dev: true
+
+ /mkdirp/1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dev: true
+
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
@@ -334,6 +447,17 @@ packages:
hasBin: true
dev: true
+ /once/1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ dependencies:
+ wrappy: 1.0.2
+ dev: true
+
+ /path-is-absolute/1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
@@ -364,6 +488,13 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
+ /rimraf/3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.3
+ dev: true
+
/rollup/2.77.3:
resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==}
engines: {node: '>=10.0.0'}
@@ -382,6 +513,18 @@ packages:
engines: {node: '>= 0.4'}
dev: true
+ /tar/6.1.11:
+ resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==}
+ engines: {node: '>= 10'}
+ dependencies:
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ minipass: 3.3.4
+ minizlib: 2.1.2
+ mkdirp: 1.0.4
+ yallist: 4.0.0
+ dev: true
+
/tinypool/0.2.4:
resolution: {integrity: sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==}
engines: {node: '>=14.0.0'}
@@ -468,3 +611,21 @@ packages:
- supports-color
- terser
dev: true
+
+ /wasm-pack/0.10.3:
+ resolution: {integrity: sha512-dg1PPyp+QwWrhfHsgG12K/y5xzwfaAoK1yuVC/DUAuQsDy5JywWDuA7Y/ionGwQz+JBZVw8jknaKBnaxaJfwTA==}
+ hasBin: true
+ requiresBuild: true
+ dependencies:
+ binary-install: 0.1.1
+ transitivePeerDependencies:
+ - debug
+ dev: true
+
+ /wrappy/1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ dev: true
+
+ /yallist/4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ dev: true

MODIFY
npm/rome/src/index.ts
npm/rome/src/index.ts
+import { main, Workspace, RomePath } from "@rometools/wasm-nodejs";
+
interface FormatFilesDebugOptions extends FormatFilesOptions {
/**
* If `true`, you'll be able to inspect the IR of the formatter
@@ -32,19 +34,25 @@ interface FormatContentOptions {
}
interface FormatResult {
- // Not final
+ /**
+ * The new formatted content
+ */
content: string;
// Not final
errors: string[];
}
interface FormatDebugResult {
- // Not final
+ /**
+ * The new formatted content
+ */
content: string;
// Not final
errors: string[];
- // Available when in debug mode
- ir: string | null;
+ /**
+ * The IR emitted by the formatter
+ */
+ ir: string;
}
interface ParseOptions {
@@ -80,7 +88,31 @@ function isFormatContentDebug(
return options?.debug !== undefined;
}
+interface CurrentFile {
+ version: number;
+ path: RomePath;
+}
+
export class Rome {
+ private workspace: Workspace;
+
+ private constructor(workspace: Workspace) {
+ this.workspace = workspace;
+ }
+
+ /**
+ * It creates a new instance of the class {Rome}
+ */
+ public static async create(): Promise<Rome> {
+ return new Rome(await Rome.loadWorkspace());
+ }
+
+ private static async loadWorkspace(): Promise<Workspace> {
+ // load the web assembly module
+ main();
+ return Promise.resolve(new Workspace());
+ }
+
async formatFiles(paths: string[]): Promise<FormatResult>;
async formatFiles(
paths: string[],
@@ -121,17 +153,45 @@ export class Rome {
content: string,
options: FormatContentOptions | FormatContentDebugOptions,
): Promise<FormatResult | FormatDebugResult> {
- content;
- options.filePath;
+ const path: RomePath = {
+ id: 0,
+ path: options.filePath,
+ };
+ await this.workspace.open_file({
+ content,
+ version: 0,
+ path,
+ });
+
+ let code;
+ if (options.range) {
+ const result = await this.workspace.format_range({
+ path: path,
+ // @ts-expect-error Types are currently wrong for range, need to fix them
+ range: options.range,
+ });
+ code = result.code;
+ } else {
+ const result = await this.workspace.format_file({
+ path,
+ });
+ code = result.code;
+ }
+
if (isFormatContentDebug(options)) {
+ const ir = await this.workspace.get_formatter_ir({
+ path,
+ });
+ this.workspace.close_file({ path });
return {
- content: "",
+ content: code,
errors: [],
- ir: "",
+ ir,
};
}
+ this.workspace.close_file({ path });
return {
- content: "",
+ content: code,
errors: [],
};
}

MODIFY
npm/rome/tests/index.test.ts
npm/rome/tests/index.test.ts
@@ -3,7 +3,7 @@ import { Rome } from "../src/index";
describe("Rome formatter", () => {
it("should not format files", async () => {
- const rome = new Rome();
+ const rome = await Rome.create();
let result = await rome.formatFiles(["./path/to/file.js"]);
@@ -12,7 +12,7 @@ describe("Rome formatter", () => {
});
it("should not format files in debug mode", async () => {
- const rome = new Rome();
+ const rome = await Rome.create();
let result = await rome.formatFiles(["./path/to/file.js"], {
debug: true,
@@ -23,60 +23,79 @@ describe("Rome formatter", () => {
expect(result.ir).toEqual("");
});
- it("should not format content", async () => {
- const rome = new Rome();
+ it("should format content", async () => {
+ const rome = await Rome.create();
let result = await rome.formatContent("function f () { }", {
filePath: "example.js",
});
- expect(result.content).toEqual("");
+ expect(result.content).toEqual("function f() {}\n");
expect(result.errors).toEqual([]);
});
- it("should not format content in debug mode", async () => {
- const rome = new Rome();
+ it("should format content in debug mode", async () => {
+ const rome = await Rome.create();
let result = await rome.formatContent("function f() {}", {
filePath: "example.js",
debug: true,
});
- expect(result.content).toEqual("");
+ expect(result.content).toEqual("function f() {}\n");
expect(result.errors).toEqual([]);
- expect(result.ir).toEqual("");
+ expect(result.ir).toEqual(
+ '["function", " ", "f", group(["(", ")"]), " ", "{", "}", hard_line_break]',
+ );
});
it("should not format content with range", async () => {
- const rome = new Rome();
+ const rome = await Rome.create();
- let result = await rome.formatContent("function f() {}", {
+ let result = await rome.formatContent("let a ; function g () { }", {
filePath: "file.js",
- range: [5, 10],
+ range: [20, 25],
});
- expect(result.content).toEqual("");
+ expect(result.content).toEqual("function g() {}");
expect(result.errors).toEqual([]);
});
it("should not format content with range in debug mode", async () => {
- const rome = new Rome();
+ const rome = await Rome.create();
- let result = await rome.formatContent("function f() {}", {
+ let result = await rome.formatContent("let a ; function g () { }", {
filePath: "file.js",
- range: [5, 10],
+ range: [20, 25],
debug: true,
});
- expect(result.content).toEqual("");
+ console.log(result.ir);
+ expect(result.content).toEqual("function g() {}");
expect(result.errors).toEqual([]);
- expect(result.ir).toEqual("");
+ expect(result.ir).toEqual(
+ `[
+ "let",
+ " ",
+ group(["a"]),
+ ";",
+ hard_line_break,
+ "function",
+ " ",
+ "g",
+ group(["(", ")"]),
+ " ",
+ "{",
+ "}",
+ hard_line_break
+]`,
+ );
});
});
describe("Rome parser", () => {
it("should not parse content", async () => {
- const rome = new Rome();
+ const rome = await Rome.create();
let result = await rome.parseContent("function f() {}", {
filePath: "example.js",

feat(npm): hook `*Conent` APIs to WASM bindings (#3097)