feat(playground): Add JSON support
MODIFY
crates/rome_service/src/file_handlers/json.rs
crates/rome_service/src/file_handlers/json.rs
@@ -6,17 +6,15 @@ use crate::settings::{
};
use crate::workspace::GetSyntaxTreeResult;
use crate::RomeError;
-#[cfg(debug_assertions)]
-use rome_formatter::FormatError;
-use rome_formatter::Printed;
+#[cfg(any(debug_assertions, target_family = "wasm"))]
+use rome_formatter::{FormatError, Printed};
use rome_fs::RomePath;
use rome_json_formatter::context::JsonFormatOptions;
use rome_json_formatter::format_node;
use rome_json_syntax::{JsonLanguage, JsonRoot, JsonSyntaxNode};
use rome_parser::AnyParse;
-#[cfg(debug_assertions)]
+#[cfg(any(debug_assertions, target_family = "wasm"))]
use rome_rowan::{TextRange, TextSize, TokenAtOffset};
-use tracing::debug;
impl Language for JsonLanguage {
type FormatterSettings = ();
@@ -68,7 +66,7 @@ impl ExtensionHandler for JsonFileHandler {
}
}
-#[cfg(debug_assertions)]
+#[cfg(any(debug_assertions, target_family = "wasm"))]
fn formatter_capabilities() -> FormatterCapabilities {
FormatterCapabilities {
format: Some(format),
@@ -77,7 +75,7 @@ fn formatter_capabilities() -> FormatterCapabilities {
}
}
-#[cfg(not(debug_assertions))]
+#[cfg(all(not(debug_assertions), not(target_family = "wasm")))]
fn formatter_capabilities() -> FormatterCapabilities {
FormatterCapabilities::default()
}
@@ -112,6 +110,7 @@ fn debug_formatter_ir(
Ok(root_element.to_string())
}
+#[cfg(any(debug_assertions, target_family = "wasm"))]
#[tracing::instrument(level = "debug", skip(parse))]
fn format(
rome_path: &RomePath,
@@ -120,7 +119,7 @@ fn format(
) -> Result<Printed, RomeError> {
let options = settings.format_options::<JsonLanguage>(rome_path);
- debug!("Format with the following options: \n{}", options);
+ tracing::debug!("Format with the following options: \n{}", options);
let tree = parse.syntax();
let formatted = format_node(options, &tree)?;
@@ -131,7 +130,7 @@ fn format(
}
}
-#[cfg(debug_assertions)]
+#[cfg(any(debug_assertions, target_family = "wasm"))]
fn format_range(
rome_path: &RomePath,
parse: AnyParse,
@@ -145,7 +144,7 @@ fn format_range(
Ok(printed)
}
-#[cfg(debug_assertions)]
+#[cfg(any(debug_assertions, target_family = "wasm"))]
fn format_on_type(
rome_path: &RomePath,
parse: AnyParse,

MODIFY
website/package.json
website/package.json
"@astrojs/react": "^1.2.2",
"@astrojs/rss": "^1.0.3",
"@codemirror/lang-javascript": "^6.1.0",
+ "@codemirror/lang-json": "^6.0.1",
"@codemirror/lint": "^6.0.0",
"@codemirror/state": "6.1.2",
"@codemirror/view": "6.4.0",

MODIFY
website/pnpm-lock.yaml
website/pnpm-lock.yaml
@@ -6,6 +6,7 @@ specifiers:
'@astrojs/react': ^1.2.2
'@astrojs/rss': ^1.0.3
'@codemirror/lang-javascript': ^6.1.0
+ '@codemirror/lang-json': ^6.0.1
'@codemirror/lint': ^6.0.0
'@codemirror/state': 6.1.2
'@codemirror/view': 6.4.0
@@ -49,6 +50,7 @@ dependencies:
'@astrojs/react': 1.2.2_74b3ggvk3akyhuq4eydyx3fqim
'@astrojs/rss': 1.0.3
'@codemirror/lang-javascript': 6.1.1
+ '@codemirror/lang-json': 6.0.1
'@codemirror/lint': 6.0.0
'@codemirror/state': 6.1.2
'@codemirror/view': 6.4.0
@@ -61,7 +63,7 @@ dependencies:
'@types/prettier': 2.7.1
'@types/react': 17.0.52
'@types/react-dom': 17.0.18
- '@uiw/react-codemirror': 4.13.2_ai5dpkfsh6bew42tf7jao77pla
+ '@uiw/react-codemirror': 4.13.2_j2evrivwfzdgwlskrcnfwd23wi
'@vitejs/plugin-react': 2.2.0_vite@3.1.8
astro: 1.6.7_ajklay5k626t46b6fyghkbup3i
astro-compress: 1.1.3
@@ -623,11 +625,8 @@ packages:
resolution: {integrity: sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==}
dev: false
- /@codemirror/autocomplete/6.3.0_ebfztrivxsawr3nzgdquexqq6q:
+ /@codemirror/autocomplete/6.3.0:
resolution: {integrity: sha512-4jEvh3AjJZTDKazd10J6ZsCIqaYxDMCeua5ouQxY8hlFIml+nr7le0SgBhT3SIytFBmdzPK3AUhXGuW3T79nVg==}
- peerDependencies:
- '@codemirror/state': ^6.0.0
- '@codemirror/view': ^6.0.0
dependencies:
'@codemirror/language': 6.3.0
'@codemirror/state': 6.1.2
@@ -647,7 +646,7 @@ packages:
/@codemirror/lang-javascript/6.1.1:
resolution: {integrity: sha512-F4+kiuC5d5dUSJmff96tJQwpEXs/tX/4bapMRnZWW6bHKK1Fx6MunTzopkCUWRa9bF87GPmb9m7Qtg7Yv8f3uQ==}
dependencies:
- '@codemirror/autocomplete': 6.3.0_ebfztrivxsawr3nzgdquexqq6q
+ '@codemirror/autocomplete': 6.3.0
'@codemirror/language': 6.3.0
'@codemirror/lint': 6.0.0
'@codemirror/state': 6.1.2
@@ -656,6 +655,13 @@ packages:
'@lezer/javascript': 1.0.2
dev: false
+ /@codemirror/lang-json/6.0.1:
+ resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==}
+ dependencies:
+ '@codemirror/language': 6.3.0
+ '@lezer/json': 1.0.0
+ dev: false
+
/@codemirror/language/6.3.0:
resolution: {integrity: sha512-6jOE5DEt6sKD46SXhn3xPbBehn+l48ACcA6Uxs2k+E2YNH9XGF5WdGMTYr2DlggfK4h0QZBK6zEb5S7lkTriWA==}
dependencies:
@@ -859,6 +865,13 @@ packages:
'@lezer/lr': 1.2.4
dev: false
+ /@lezer/json/1.0.0:
+ resolution: {integrity: sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==}
+ dependencies:
+ '@lezer/highlight': 1.1.2
+ '@lezer/lr': 1.2.4
+ dev: false
+
/@lezer/lr/1.2.4:
resolution: {integrity: sha512-L/52/oMJBFXXx8qBYF4UgktLP2geQ/qn5Fd8+5L/mqlLLCB9+qdKktFAtejd9FdFMaFx6lrP5rmLz4sN3Kplcg==}
dependencies:
@@ -1312,14 +1325,10 @@ packages:
resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
dev: false
- /@uiw/codemirror-extensions-basic-setup/4.13.2_taect3vlcl4b4hfcnqjzfij36u:
+ /@uiw/codemirror-extensions-basic-setup/4.13.2:
resolution: {integrity: sha512-ATJA7WaZ5g7/0teNBQRIalK3RxfU9X7hviwh6BOelaeJrJjkY6x+jVaACYywP1GStsIKcjNCumakpmRPO9w4sQ==}
- peerDependencies:
- '@codemirror/lint': '>=6.0.0'
- '@codemirror/state': '>=6.0.0'
- '@codemirror/view': '>=6.0.0'
dependencies:
- '@codemirror/autocomplete': 6.3.0_ebfztrivxsawr3nzgdquexqq6q
+ '@codemirror/autocomplete': 6.3.0
'@codemirror/commands': 6.1.2
'@codemirror/language': 6.3.0
'@codemirror/lint': 6.0.0
@@ -1328,10 +1337,9 @@ packages:
'@codemirror/view': 6.4.0
dev: false
- /@uiw/react-codemirror/4.13.2_ai5dpkfsh6bew42tf7jao77pla:
+ /@uiw/react-codemirror/4.13.2_j2evrivwfzdgwlskrcnfwd23wi:
resolution: {integrity: sha512-sMfb8F82yLSqyq1gEUlr14E4KGXDfvsCnkGLLwJZqsq/TvJTRVcy0GX87wn3GXBtGhNp5TvGFUDaCnFzbU4bIQ==}
peerDependencies:
- '@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
react: '>=16.8.0'
react-dom: '>=16.8.0'
@@ -1341,12 +1349,10 @@ packages:
'@codemirror/state': 6.1.2
'@codemirror/theme-one-dark': 6.1.0
'@codemirror/view': 6.4.0
- '@uiw/codemirror-extensions-basic-setup': 4.13.2_taect3vlcl4b4hfcnqjzfij36u
+ '@uiw/codemirror-extensions-basic-setup': 4.13.2
codemirror: 6.0.1
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
- transitivePeerDependencies:
- - '@codemirror/lint'
dev: false
/@vitejs/plugin-react/2.2.0_vite@3.1.8:
@@ -1785,7 +1791,7 @@ packages:
/codemirror-lang-rome-ast/0.0.6:
resolution: {integrity: sha512-2E4E4rNcj6XxLsVjCZJeOsdEXpfdgiejuLrbq6O4muXzf0KSv5X0ScQrs3LK5LmGwoQUC0AowYbfdj3lsaLWkQ==}
dependencies:
- '@codemirror/autocomplete': 6.3.0_ebfztrivxsawr3nzgdquexqq6q
+ '@codemirror/autocomplete': 6.3.0
'@codemirror/language': 6.3.0
'@codemirror/state': 6.1.2
'@codemirror/view': 6.4.0
@@ -1797,7 +1803,7 @@ packages:
/codemirror/6.0.1:
resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
dependencies:
- '@codemirror/autocomplete': 6.3.0_ebfztrivxsawr3nzgdquexqq6q
+ '@codemirror/autocomplete': 6.3.0
'@codemirror/commands': 6.1.2
'@codemirror/language': 6.3.0
'@codemirror/lint': 6.0.0
@@ -3780,7 +3786,7 @@ packages:
/lang-rome-formatter-ir/0.0.2:
resolution: {integrity: sha512-Zx7uZ6SA1vmMWkM7UbDZlex8MGOCQNBkxN9sAjeGgGHx69DNuR8E4GPfHFHI2P7oFLvXMrmAMPUY2jbEH9wtOA==}
dependencies:
- '@codemirror/autocomplete': 6.3.0_ebfztrivxsawr3nzgdquexqq6q
+ '@codemirror/autocomplete': 6.3.0
'@codemirror/language': 6.3.0
'@codemirror/state': 6.1.2
'@codemirror/view': 6.4.0

MODIFY
website/src/playground/Playground.tsx
website/src/playground/Playground.tsx
@@ -4,6 +4,7 @@ import CodeMirror from "./CodeMirror";
import type { ViewUpdate } from "@codemirror/view";
import * as codeMirrorLangRomeAST from "codemirror-lang-rome-ast";
import { javascript } from "@codemirror/lang-javascript";
+import { json } from "@codemirror/lang-json";
import SettingsPane from "./components/SettingsPane";
import {
createRef,
@@ -21,6 +22,7 @@ import FormatterIRTab from "./tabs/FormatterIRTab";
import {
getCurrentCode,
getFileState,
+ isJSONFilename,
isJSXFilename,
isTypeScriptFilename,
useWindowSize,
@@ -46,18 +48,18 @@ export default function PlaygroundLoader({
const prettierOutput = file.prettier;
// rome-ignore lint/nursery/useExhaustiveDependencies: dynamic dependencies
- const codeMirrorExtensions = useMemo(
- () => [
+ const codeMirrorExtensions = useMemo(() => {
+ if (isJSONFilename(playgroundState.currentFile)) {
+ return [json()];
+ } else {
+ return [
javascript({
jsx: isJSXFilename(playgroundState.currentFile),
typescript: isTypeScriptFilename(playgroundState.currentFile),
}),
- ],
- [
- isJSXFilename(playgroundState.currentFile),
- isTypeScriptFilename(playgroundState.currentFile),
- ],
- );
+ ];
+ }
+ }, [playgroundState.currentFile]);
const romeAstSyntacticDataRef = useRef<RomeAstSyntacticData | null>(null);

MODIFY
website/src/playground/utils.ts
website/src/playground/utils.ts
import { Dispatch, SetStateAction, useEffect, useState } from "react";
-import prettier, { Options as PrettierOptions } from "prettier";
import type { ThemeName } from "../frontend-scripts/util";
-// @ts-ignore
-import parserBabel from "prettier/esm/parser-babel";
import {
- IndentStyle,
PlaygroundState,
- QuoteStyle,
- QuoteProperties,
- TrailingComma,
- Semicolons,
- PrettierOutput,
PlaygroundSettings,
emptyPrettierOutput,
emptyRomeOutput,
@@ -147,76 +138,6 @@ export function createPlaygroundSettingsSetter<
};
}
-export function formatWithPrettier(
- code: string,
- options: {
- lineWidth: number;
- indentStyle: IndentStyle;
- indentWidth: number;
- language: "js" | "ts";
- quoteStyle: QuoteStyle;
- quoteProperties: QuoteProperties;
- trailingComma: TrailingComma;
- semicolons: Semicolons;
- },
-): PrettierOutput {
- try {
- const prettierOptions: PrettierOptions = {
- useTabs: options.indentStyle === IndentStyle.Tab,
- tabWidth: options.indentWidth,
- printWidth: options.lineWidth,
- parser: getPrettierParser(options.language),
- plugins: [parserBabel],
- singleQuote: options.quoteStyle === QuoteStyle.Single,
- quoteProps: options.quoteProperties,
- trailingComma: options.trailingComma,
- semi: options.semicolons === Semicolons.Always,
- };
-
- // @ts-ignore
- const debug = prettier.__debug;
- const document = debug.printToDoc(code, prettierOptions);
-
- // formatDoc must be before printDocToString because printDocToString mutates the document and breaks the ir
- const ir = debug.formatDoc(document, {
- parser: "babel",
- plugins: [parserBabel],
- });
-
- const formattedCode = debug.printDocToString(
- document,
- prettierOptions,
- ).formatted;
-
- return {
- type: "SUCCESS",
- code: formattedCode,
- ir,
- };
- } catch (err: unknown) {
- if (err instanceof SyntaxError) {
- return {
- type: "ERROR",
- stack: err.message,
- };
- } else {
- return {
- type: "ERROR",
- stack: (err as Error).stack ?? "",
- };
- }
- }
-}
-
-function getPrettierParser(language: "js" | "ts"): string {
- switch (language) {
- case "js":
- return "babel";
- case "ts":
- return "babel-ts";
- }
-}
-
// See https://developer.mozilla.org/en-US/docs/Web/API/btoa#unicode_strings
export function encodeCode(code: string): string {
return btoa(toBinary(code));
@@ -300,6 +221,10 @@ export function isModuleFilename(filename: string): boolean {
);
}
+export function isJSONFilename(filename: string): boolean {
+ return filename.endsWith(".json");
+}
+
export function modifyFilename(
filename: string,
opts: ExtensionOptions,
@@ -344,7 +269,8 @@ export function isValidExtension(filename: string): boolean {
isScriptFilename(filename) ||
isModuleFilename(filename) ||
isTypeScriptFilename(filename) ||
- isJSXFilename(filename)
+ isJSXFilename(filename) ||
+ isJSONFilename(filename)
);
}

MODIFY
website/src/playground/workers/prettierWorker.ts
website/src/playground/workers/prettierWorker.ts
-import { formatWithPrettier, isTypeScriptFilename } from "../utils";
-import { defaultPlaygroundState, PlaygroundSettings } from "../types";
+import {
+ IndentStyle,
+ PlaygroundSettings,
+ PrettierOutput,
+ QuoteProperties,
+ QuoteStyle,
+ Semicolons,
+ TrailingComma,
+ defaultPlaygroundState,
+} from "../types";
+import { isJSONFilename, isTypeScriptFilename } from "../utils";
+import prettier, { Options as PrettierOptions } from "prettier";
+// @ts-expect-error
+import parserBabel from "prettier/esm/parser-babel";
let settings = defaultPlaygroundState.settings;
@@ -27,7 +39,7 @@ self.addEventListener("message", (e) => {
lineWidth,
indentStyle,
indentWidth,
- language: isTypeScriptFilename(filename) ? "ts" : "js",
+ filepath: filename,
quoteStyle,
quoteProperties,
trailingComma,
@@ -47,3 +59,75 @@ self.addEventListener("message", (e) => {
console.error(`Unknown message ${e.data.type}.`);
}
});
+
+function formatWithPrettier(
+ code: string,
+ options: {
+ lineWidth: number;
+ indentStyle: IndentStyle;
+ indentWidth: number;
+ filepath: string;
+ quoteStyle: QuoteStyle;
+ quoteProperties: QuoteProperties;
+ trailingComma: TrailingComma;
+ semicolons: Semicolons;
+ },
+): PrettierOutput {
+ try {
+ const prettierOptions: PrettierOptions = {
+ useTabs: options.indentStyle === IndentStyle.Tab,
+ tabWidth: options.indentWidth,
+ printWidth: options.lineWidth,
+ filepath: options.filepath,
+ plugins: [parserBabel],
+ parser: getPrettierParser(options.filepath),
+ singleQuote: options.quoteStyle === QuoteStyle.Single,
+ quoteProps: options.quoteProperties,
+ trailingComma: options.trailingComma,
+ semi: options.semicolons === Semicolons.Always,
+ };
+
+ // @ts-expect-error
+ const debug = prettier.__debug;
+ const document = debug.printToDoc(code, prettierOptions);
+
+ // formatDoc must be before printDocToString because printDocToString mutates the document and breaks the ir
+ const ir = debug.formatDoc(document, {
+ parser: "babel",
+ plugins: [parserBabel],
+ });
+
+ const formattedCode = debug.printDocToString(
+ document,
+ prettierOptions,
+ ).formatted;
+
+ return {
+ type: "SUCCESS",
+ code: formattedCode,
+ ir,
+ };
+ } catch (err: unknown) {
+ if (err instanceof SyntaxError) {
+ return {
+ type: "ERROR",
+ stack: err.message,
+ };
+ } else {
+ return {
+ type: "ERROR",
+ stack: (err as Error).stack ?? "",
+ };
+ }
+ }
+}
+
+function getPrettierParser(filename: string): string {
+ if (isTypeScriptFilename(filename)) {
+ return "babel-ts";
+ } else if (isJSONFilename(filename)) {
+ return "json5";
+ } else {
+ return "babel";
+ }
+}

MODIFY
website/src/playground/workers/romeWorker.ts
website/src/playground/workers/romeWorker.ts
@@ -15,6 +15,7 @@ import {
RomeOutput,
Semicolons,
} from "../types";
+import { isJSONFilename } from "../utils";
let workspace: Workspace | null = null;
let fileCounter = 0;
@@ -189,10 +190,12 @@ self.addEventListener("message", async (e) => {
path,
});
- const controlFlowGraph = workspace.getControlFlowGraph({
+ const controlFlowGraph = !isJSONFilename(filename)
+ ? workspace.getControlFlowGraph({
path,
cursor: cursorPosition,
- });
+ })
+ : "";
const formatterIr = workspace.getFormatterIr({
path,

feat(playground): Add JSON support (#3968)