From 1383899e5f11023ba78bf7527563e6f514e17b01 Mon Sep 17 00:00:00 2001 From: patrickhertling Date: Sat, 1 Feb 2025 01:24:57 +0200 Subject: [PATCH 01/12] json formatting in code-block plugin --- .../default/plate-ui/code-block-element.tsx | 4 +- .../plate-ui/code-block-format-button.tsx | 31 ++++++++++++++ .../code-block/src/lib/formatter/formatter.ts | 40 ++++++++++++++++++ .../code-block/src/lib/formatter/index.ts | 6 +++ .../src/lib/formatter/jsonFormatter.spec.tsx | 42 +++++++++++++++++++ .../src/lib/formatter/jsonFormatter.ts | 21 ++++++++++ packages/code-block/src/lib/index.ts | 1 + packages/code-block/src/react/hooks/index.ts | 1 + .../src/react/hooks/useCodeBlockFormat.ts | 32 ++++++++++++++ 9 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 apps/www/src/registry/default/plate-ui/code-block-format-button.tsx create mode 100644 packages/code-block/src/lib/formatter/formatter.ts create mode 100644 packages/code-block/src/lib/formatter/index.ts create mode 100644 packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx create mode 100644 packages/code-block/src/lib/formatter/jsonFormatter.ts create mode 100644 packages/code-block/src/react/hooks/useCodeBlockFormat.ts diff --git a/apps/www/src/registry/default/plate-ui/code-block-element.tsx b/apps/www/src/registry/default/plate-ui/code-block-element.tsx index 5dee8c46b1..6b8a189c43 100644 --- a/apps/www/src/registry/default/plate-ui/code-block-element.tsx +++ b/apps/www/src/registry/default/plate-ui/code-block-element.tsx @@ -6,6 +6,7 @@ import { cn, withRef } from '@udecode/cn'; import { useCodeBlockElementState } from '@udecode/plate-code-block/react'; import { CodeBlockCombobox } from './code-block-combobox'; +import { CodeBlockFormatButton } from './code-block-format-button'; import { PlateElement } from './plate-element'; import './code-block-element.css'; @@ -28,9 +29,10 @@ export const CodeBlockElement = withRef( {state.syntax && (
+
)} diff --git a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx new file mode 100644 index 0000000000..e147a5bbd1 --- /dev/null +++ b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx @@ -0,0 +1,31 @@ +'use client'; + +import type { TElement } from '@udecode/plate'; + +import { useCodeBlockFormat } from '@udecode/plate-code-block/react'; +import { BracesIcon } from 'lucide-react'; + +import { Button } from './button'; + +export function CodeBlockFormatButton({ element }: { element: TElement }) { + const { format, isSupported, validSyntax } = useCodeBlockFormat({ + element, + }); + + if (!isSupported) { + return null; + } + + return ( + + ); +} diff --git a/packages/code-block/src/lib/formatter/formatter.ts b/packages/code-block/src/lib/formatter/formatter.ts new file mode 100644 index 0000000000..98c632a254 --- /dev/null +++ b/packages/code-block/src/lib/formatter/formatter.ts @@ -0,0 +1,40 @@ +import { JsonFormatter } from './jsonFormatter'; + +export interface IFormatter { + format: (code: string) => string; + validSyntax: (code: string) => boolean; +} + +const supportedLanguages = new Set(['json']); + +export class Formatter { + format(code: string, lang?: string) { + if (!lang || !supportedLanguages.has(lang)) { + return ''; + } + + switch (lang) { + case 'json': { + return new JsonFormatter().format(code); + } + } + + return code; + } + + isLangSupported(lang?: string) { + return lang && supportedLanguages.has(lang); + } + + validSyntax(code: string, lang?: string) { + if (!lang || !supportedLanguages.has(lang)) { + return false; + } + + switch (lang) { + case 'json': { + return new JsonFormatter().validSyntax(code); + } + } + } +} diff --git a/packages/code-block/src/lib/formatter/index.ts b/packages/code-block/src/lib/formatter/index.ts new file mode 100644 index 0000000000..70d3362467 --- /dev/null +++ b/packages/code-block/src/lib/formatter/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './formatter'; +export * from './jsonFormatter'; diff --git a/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx new file mode 100644 index 0000000000..7fbeb3d6cd --- /dev/null +++ b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx @@ -0,0 +1,42 @@ +import { createEditor } from '@udecode/plate'; +import { createPlateEditor } from '@udecode/plate/react'; + +import { JsonFormatter } from './jsonFormatter'; + +describe('JsonFormatter', () => { + const formatter = new JsonFormatter(); + + it('should detect valid JSON', () => { + const json = '{ "name": "ChatGPT", "type": "AI" }'; + const isValid = formatter.validSyntax(json); + expect(isValid).toBe(true); + }); + + it('should detect invalid JSON', () => { + const editor = createEditor(); + const plateEditor = createPlateEditor({ editor }); + const json = '{ name: "ChatGPT", type: AI }'; + const isValid = formatter.validSyntax(json); + expect(isValid).toBe(false); + }); + + it('should format JSON', () => { + const editor = createEditor(); + const plateEditor = createPlateEditor({ editor }); + const json = '{"name":"ChatGPT","type":"AI"}'; + const formattedJson = formatter.format(json); + const expected = `{ + "name": "ChatGPT", + "type": "AI" +}`; + expect(formattedJson).toBe(expected); + }); + + it('should not format invalid JSON', () => { + const editor = createEditor(); + const plateEditor = createPlateEditor({ editor }); + const json = '{ name: "ChatGPT", type: AI }'; + const formattedJson = formatter.format(json); + expect(formattedJson).toBe(json); + }); +}); diff --git a/packages/code-block/src/lib/formatter/jsonFormatter.ts b/packages/code-block/src/lib/formatter/jsonFormatter.ts new file mode 100644 index 0000000000..bb5030f921 --- /dev/null +++ b/packages/code-block/src/lib/formatter/jsonFormatter.ts @@ -0,0 +1,21 @@ +import type { IFormatter } from './formatter'; + +export class JsonFormatter implements IFormatter { + format(code: string) { + try { + return JSON.stringify(JSON.parse(code), null, 2); + } catch (error) { + return code; + } + } + + validSyntax(code: string) { + try { + JSON.parse(code); + + return true; + } catch (error) { + return false; + } + } +} diff --git a/packages/code-block/src/lib/index.ts b/packages/code-block/src/lib/index.ts index ca856337a4..2c9aa755ee 100644 --- a/packages/code-block/src/lib/index.ts +++ b/packages/code-block/src/lib/index.ts @@ -10,5 +10,6 @@ export * from './withInsertDataCodeBlock'; export * from './withInsertFragmentCodeBlock'; export * from './withNormalizeCodeBlock'; export * from './deserializer/index'; +export * from './formatter/index'; export * from './queries/index'; export * from './transforms/index'; diff --git a/packages/code-block/src/react/hooks/index.ts b/packages/code-block/src/react/hooks/index.ts index c0ca34cad1..79f136e998 100644 --- a/packages/code-block/src/react/hooks/index.ts +++ b/packages/code-block/src/react/hooks/index.ts @@ -4,5 +4,6 @@ export * from './useCodeBlockCombobox'; export * from './useCodeBlockElement'; +export * from './useCodeBlockFormat'; export * from './useCodeSyntaxLeaf'; export * from './useToggleCodeBlockButton'; diff --git a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts new file mode 100644 index 0000000000..aaa50e436a --- /dev/null +++ b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts @@ -0,0 +1,32 @@ +import { useEditorRef } from '@udecode/plate/react'; + +import type { TCodeBlockElement } from '../../lib'; + +import { Formatter } from '../../lib/formatter/formatter'; + +export const useCodeBlockFormat = ({ + element, +}: { + element: TCodeBlockElement; +}) => { + const editor = useEditorRef(); + + const { lang: language } = element; + const code = editor.api.string(element); + + const formatter = new Formatter(); + const isSupported = formatter.isLangSupported(language); + + const format = () => { + const validSyntax = formatter.validSyntax(code, language); + + if (validSyntax) { + const formattedCode = formatter.format(code, language); + editor.tf.insertText(formattedCode, { at: element }); + } + }; + + const validSyntax = formatter.validSyntax(code, language); + + return { format, isSupported, validSyntax }; +}; From 943f9513bd458aec7ec932bf0a77ba2b38d9682b Mon Sep 17 00:00:00 2001 From: Patrick Hertling Date: Sat, 1 Feb 2025 12:39:34 +0200 Subject: [PATCH 02/12] Update packages/code-block/src/react/hooks/useCodeBlockFormat.ts Co-authored-by: Ziad Beyens --- packages/code-block/src/react/hooks/useCodeBlockFormat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts index aaa50e436a..3f9bad5356 100644 --- a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts +++ b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts @@ -4,7 +4,7 @@ import type { TCodeBlockElement } from '../../lib'; import { Formatter } from '../../lib/formatter/formatter'; -export const useCodeBlockFormat = ({ +export const formatCodeBlock = (editor: Editor, { element, }: { element: TCodeBlockElement; From 3cd4cd5c537321229c858fa88beceb118154165f Mon Sep 17 00:00:00 2001 From: Patrick Hertling Date: Sat, 1 Feb 2025 12:40:45 +0200 Subject: [PATCH 03/12] Update packages/code-block/src/react/hooks/useCodeBlockFormat.ts Co-authored-by: Ziad Beyens --- .../src/react/hooks/useCodeBlockFormat.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts index 3f9bad5356..44cc3b8caa 100644 --- a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts +++ b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts @@ -9,24 +9,17 @@ export const formatCodeBlock = (editor: Editor, { }: { element: TCodeBlockElement; }) => { - const editor = useEditorRef(); - + const formatter = new Formatter(); + const { lang: language } = element; - const code = editor.api.string(element); - - const formatter = new Formatter(); - const isSupported = formatter.isLangSupported(language); - - const format = () => { - const validSyntax = formatter.validSyntax(code, language); + const isSupported = isLangSupported(language); + if (!isSupported) return + const validSyntax = isValidSyntax(code, language); if (validSyntax) { - const formattedCode = formatter.format(code, language); + const code = editor.api.string(element); + const formattedCode = formatCode(code, language); editor.tf.insertText(formattedCode, { at: element }); } }; - - const validSyntax = formatter.validSyntax(code, language); - - return { format, isSupported, validSyntax }; }; From 1acf0c7cb0bd0bc3f0cc906e3feff22cc68267dc Mon Sep 17 00:00:00 2001 From: Patrick Hertling Date: Sat, 1 Feb 2025 12:41:01 +0200 Subject: [PATCH 04/12] Update apps/www/src/registry/default/plate-ui/code-block-format-button.tsx Co-authored-by: Ziad Beyens --- .../src/registry/default/plate-ui/code-block-format-button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx index e147a5bbd1..fc70be16dd 100644 --- a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx +++ b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx @@ -22,7 +22,7 @@ export function CodeBlockFormatButton({ element }: { element: TElement }) { variant="ghost" className="h-5 justify-between px-1 text-xs" disabled={!validSyntax} - onClick={() => format()} + onClick={() => formatCodeBlock(editor, { element })} title="Format code" > From 2d6c1f5351f4b90db985110c67e5142d356b671b Mon Sep 17 00:00:00 2001 From: Patrick Hertling Date: Sat, 1 Feb 2025 12:41:27 +0200 Subject: [PATCH 05/12] Update packages/code-block/src/lib/formatter/jsonFormatter.ts Co-authored-by: Ziad Beyens --- .../src/lib/formatter/jsonFormatter.ts | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/code-block/src/lib/formatter/jsonFormatter.ts b/packages/code-block/src/lib/formatter/jsonFormatter.ts index bb5030f921..0d101c2eac 100644 --- a/packages/code-block/src/lib/formatter/jsonFormatter.ts +++ b/packages/code-block/src/lib/formatter/jsonFormatter.ts @@ -1,21 +1,16 @@ -import type { IFormatter } from './formatter'; - -export class JsonFormatter implements IFormatter { - format(code: string) { - try { - return JSON.stringify(JSON.parse(code), null, 2); - } catch (error) { - return code; - } +export const formatJson = (code: string): string => { + try { + return JSON.stringify(JSON.parse(code), null, 2); + } catch (error) { + return code; } +}; - validSyntax(code: string) { - try { - JSON.parse(code); - - return true; - } catch (error) { - return false; - } +export const isValidJson = (code: string): boolean => { + try { + JSON.parse(code); + return true; + } catch (error) { + return false; } -} +}; From 64c86b1e2188fa54381e71523c7ef41ea0ce7691 Mon Sep 17 00:00:00 2001 From: Patrick Hertling Date: Sat, 1 Feb 2025 12:42:04 +0200 Subject: [PATCH 06/12] Update packages/code-block/src/lib/formatter/formatter.ts Co-authored-by: Ziad Beyens --- .../code-block/src/lib/formatter/formatter.ts | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/packages/code-block/src/lib/formatter/formatter.ts b/packages/code-block/src/lib/formatter/formatter.ts index 98c632a254..8dd367cab9 100644 --- a/packages/code-block/src/lib/formatter/formatter.ts +++ b/packages/code-block/src/lib/formatter/formatter.ts @@ -1,40 +1,32 @@ -import { JsonFormatter } from './jsonFormatter'; - -export interface IFormatter { - format: (code: string) => string; - validSyntax: (code: string) => boolean; -} +import { formatJson, isValidJson } from './jsonFormatter'; const supportedLanguages = new Set(['json']); -export class Formatter { - format(code: string, lang?: string) { - if (!lang || !supportedLanguages.has(lang)) { - return ''; - } +export const isLangSupported = (lang?: string): boolean => + Boolean(lang && supportedLanguages.has(lang)); - switch (lang) { - case 'json': { - return new JsonFormatter().format(code); - } - } +export const formatCode = (code: string, lang?: string): string => { + if (!isLangSupported(lang)) { + return ''; + } - return code; + switch (lang) { + case 'json': + return formatJson(code); + default: + return code; } +}; - isLangSupported(lang?: string) { - return lang && supportedLanguages.has(lang); +export const isValidSyntax = (code: string, lang?: string): boolean => { + if (!isLangSupported(lang)) { + return false; } - validSyntax(code: string, lang?: string) { - if (!lang || !supportedLanguages.has(lang)) { + switch (lang) { + case 'json': + return isValidJson(code); + default: return false; - } - - switch (lang) { - case 'json': { - return new JsonFormatter().validSyntax(code); - } - } } -} +}; From 6487012751e59a907c38edad16c432399d5f5aa1 Mon Sep 17 00:00:00 2001 From: Patrick Hertling Date: Sat, 1 Feb 2025 12:42:37 +0200 Subject: [PATCH 07/12] Update apps/www/src/registry/default/plate-ui/code-block-format-button.tsx Co-authored-by: Ziad Beyens --- .../src/registry/default/plate-ui/code-block-format-button.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx index fc70be16dd..1fd1656570 100644 --- a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx +++ b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx @@ -8,9 +8,6 @@ import { BracesIcon } from 'lucide-react'; import { Button } from './button'; export function CodeBlockFormatButton({ element }: { element: TElement }) { - const { format, isSupported, validSyntax } = useCodeBlockFormat({ - element, - }); if (!isSupported) { return null; From 1bbc720705fd3b5169759027c1746193bafadc78 Mon Sep 17 00:00:00 2001 From: Patrick Hertling Date: Sat, 1 Feb 2025 12:42:54 +0200 Subject: [PATCH 08/12] Update apps/www/src/registry/default/plate-ui/code-block-format-button.tsx Co-authored-by: Ziad Beyens --- .../src/registry/default/plate-ui/code-block-format-button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx index 1fd1656570..ec1820c452 100644 --- a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx +++ b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx @@ -9,7 +9,7 @@ import { Button } from './button'; export function CodeBlockFormatButton({ element }: { element: TElement }) { - if (!isSupported) { + if (!isLangSupported(element.lang)) { return null; } From e76f3358f6a8a5a6605a8a38e4c236b7663d20f0 Mon Sep 17 00:00:00 2001 From: patrickhertling Date: Sat, 1 Feb 2025 13:47:54 +0200 Subject: [PATCH 09/12] Implement suggestions --- .../default/plate-ui/code-block-element.tsx | 37 +++++++++++-- .../plate-ui/code-block-format-button.tsx | 28 ---------- .../code-block/src/lib/formatter/formatter.ts | 14 +++-- .../src/lib/formatter/jsonFormatter.spec.tsx | 12 ++--- .../src/lib/formatter/jsonFormatter.ts | 1 + .../src/react/hooks/useCodeBlockFormat.ts | 52 ++++++++++++------- 6 files changed, 82 insertions(+), 62 deletions(-) delete mode 100644 apps/www/src/registry/default/plate-ui/code-block-format-button.tsx diff --git a/apps/www/src/registry/default/plate-ui/code-block-element.tsx b/apps/www/src/registry/default/plate-ui/code-block-element.tsx index 6b8a189c43..f62aad3dd9 100644 --- a/apps/www/src/registry/default/plate-ui/code-block-element.tsx +++ b/apps/www/src/registry/default/plate-ui/code-block-element.tsx @@ -2,11 +2,18 @@ import React from 'react'; +import type { Editor, TElement } from '@udecode/plate'; + import { cn, withRef } from '@udecode/cn'; -import { useCodeBlockElementState } from '@udecode/plate-code-block/react'; +import { + formatCodeBlock, + isLangSupported, + useCodeBlockElementState, +} from '@udecode/plate-code-block/react'; +import { BracesIcon } from 'lucide-react'; +import { Button } from './button'; import { CodeBlockCombobox } from './code-block-combobox'; -import { CodeBlockFormatButton } from './code-block-format-button'; import { PlateElement } from './plate-element'; import './code-block-element.css'; @@ -32,7 +39,7 @@ export const CodeBlockElement = withRef( className="absolute right-2 top-2 z-10 flex select-none items-center gap-1" contentEditable={false} > - + )} @@ -40,3 +47,27 @@ export const CodeBlockElement = withRef( ); } ); + +export function CodeBlockFormatButton({ + editor, + element, +}: { + editor: Editor; + element: TElement; +}) { + if (!isLangSupported(element)) { + return null; + } + + return ( + + ); +} diff --git a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx b/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx deleted file mode 100644 index ec1820c452..0000000000 --- a/apps/www/src/registry/default/plate-ui/code-block-format-button.tsx +++ /dev/null @@ -1,28 +0,0 @@ -'use client'; - -import type { TElement } from '@udecode/plate'; - -import { useCodeBlockFormat } from '@udecode/plate-code-block/react'; -import { BracesIcon } from 'lucide-react'; - -import { Button } from './button'; - -export function CodeBlockFormatButton({ element }: { element: TElement }) { - - if (!isLangSupported(element.lang)) { - return null; - } - - return ( - - ); -} diff --git a/packages/code-block/src/lib/formatter/formatter.ts b/packages/code-block/src/lib/formatter/formatter.ts index 8dd367cab9..62db5a9dee 100644 --- a/packages/code-block/src/lib/formatter/formatter.ts +++ b/packages/code-block/src/lib/formatter/formatter.ts @@ -2,7 +2,7 @@ import { formatJson, isValidJson } from './jsonFormatter'; const supportedLanguages = new Set(['json']); -export const isLangSupported = (lang?: string): boolean => +export const isLangSupported = (lang?: string): boolean => Boolean(lang && supportedLanguages.has(lang)); export const formatCode = (code: string, lang?: string): string => { @@ -11,10 +11,12 @@ export const formatCode = (code: string, lang?: string): string => { } switch (lang) { - case 'json': + case 'json': { return formatJson(code); - default: + } + default: { return code; + } } }; @@ -24,9 +26,11 @@ export const isValidSyntax = (code: string, lang?: string): boolean => { } switch (lang) { - case 'json': + case 'json': { return isValidJson(code); - default: + } + default: { return false; + } } }; diff --git a/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx index 7fbeb3d6cd..4fd51c2ee7 100644 --- a/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx +++ b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx @@ -1,14 +1,12 @@ import { createEditor } from '@udecode/plate'; import { createPlateEditor } from '@udecode/plate/react'; -import { JsonFormatter } from './jsonFormatter'; +import { formatCode, isValidSyntax } from './formatter'; describe('JsonFormatter', () => { - const formatter = new JsonFormatter(); - it('should detect valid JSON', () => { const json = '{ "name": "ChatGPT", "type": "AI" }'; - const isValid = formatter.validSyntax(json); + const isValid = isValidSyntax(json); expect(isValid).toBe(true); }); @@ -16,7 +14,7 @@ describe('JsonFormatter', () => { const editor = createEditor(); const plateEditor = createPlateEditor({ editor }); const json = '{ name: "ChatGPT", type: AI }'; - const isValid = formatter.validSyntax(json); + const isValid = isValidSyntax(json); expect(isValid).toBe(false); }); @@ -24,7 +22,7 @@ describe('JsonFormatter', () => { const editor = createEditor(); const plateEditor = createPlateEditor({ editor }); const json = '{"name":"ChatGPT","type":"AI"}'; - const formattedJson = formatter.format(json); + const formattedJson = formatCode(json); const expected = `{ "name": "ChatGPT", "type": "AI" @@ -36,7 +34,7 @@ describe('JsonFormatter', () => { const editor = createEditor(); const plateEditor = createPlateEditor({ editor }); const json = '{ name: "ChatGPT", type: AI }'; - const formattedJson = formatter.format(json); + const formattedJson = formatCode(json); expect(formattedJson).toBe(json); }); }); diff --git a/packages/code-block/src/lib/formatter/jsonFormatter.ts b/packages/code-block/src/lib/formatter/jsonFormatter.ts index 0d101c2eac..8c92a5eb32 100644 --- a/packages/code-block/src/lib/formatter/jsonFormatter.ts +++ b/packages/code-block/src/lib/formatter/jsonFormatter.ts @@ -9,6 +9,7 @@ export const formatJson = (code: string): string => { export const isValidJson = (code: string): boolean => { try { JSON.parse(code); + return true; } catch (error) { return false; diff --git a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts index 44cc3b8caa..19dbedb807 100644 --- a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts +++ b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts @@ -1,25 +1,39 @@ -import { useEditorRef } from '@udecode/plate/react'; +import type { Editor } from '@udecode/plate'; import type { TCodeBlockElement } from '../../lib'; -import { Formatter } from '../../lib/formatter/formatter'; +import { + isLangSupported as _isLangSupported, + isValidSyntax as _isValidSyntax, + formatCode, +} from '../../lib/formatter/formatter'; -export const formatCodeBlock = (editor: Editor, { - element, -}: { - element: TCodeBlockElement; -}) => { - const formatter = new Formatter(); - +export const formatCodeBlock = ( + editor: Editor, + { + element, + }: { + element: TCodeBlockElement; + } +) => { const { lang: language } = element; - const isSupported = isLangSupported(language); - if (!isSupported) return - const validSyntax = isValidSyntax(code, language); - - if (validSyntax) { - const code = editor.api.string(element); - const formattedCode = formatCode(code, language); - editor.tf.insertText(formattedCode, { at: element }); - } - }; + + if (!language || !_isLangSupported(language)) { + return; + } + + const code = editor.api.string(element); + + if (_isValidSyntax(code, language)) { + const formattedCode = formatCode(code, language); + editor.tf.insertText(formattedCode, { at: element }); + } +}; + +export const isValidSyntax = (code: string, language: string) => { + return _isValidSyntax(code, language); +}; + +export const isLangSupported = (element: TCodeBlockElement) => { + return _isLangSupported(element.lang); }; From 9b89880e2376a6ab8cee25985a2ac966c9873e73 Mon Sep 17 00:00:00 2001 From: patrickhertling Date: Sat, 1 Feb 2025 14:13:18 +0200 Subject: [PATCH 10/12] Fix tests --- .../src/lib/formatter/jsonFormatter.spec.tsx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx index 4fd51c2ee7..c6744a4664 100644 --- a/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx +++ b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx @@ -1,28 +1,21 @@ -import { createEditor } from '@udecode/plate'; -import { createPlateEditor } from '@udecode/plate/react'; - import { formatCode, isValidSyntax } from './formatter'; describe('JsonFormatter', () => { it('should detect valid JSON', () => { const json = '{ "name": "ChatGPT", "type": "AI" }'; - const isValid = isValidSyntax(json); + const isValid = isValidSyntax(json, 'json'); expect(isValid).toBe(true); }); it('should detect invalid JSON', () => { - const editor = createEditor(); - const plateEditor = createPlateEditor({ editor }); const json = '{ name: "ChatGPT", type: AI }'; - const isValid = isValidSyntax(json); + const isValid = isValidSyntax(json, 'json'); expect(isValid).toBe(false); }); it('should format JSON', () => { - const editor = createEditor(); - const plateEditor = createPlateEditor({ editor }); const json = '{"name":"ChatGPT","type":"AI"}'; - const formattedJson = formatCode(json); + const formattedJson = formatCode(json, 'json'); const expected = `{ "name": "ChatGPT", "type": "AI" @@ -31,10 +24,8 @@ describe('JsonFormatter', () => { }); it('should not format invalid JSON', () => { - const editor = createEditor(); - const plateEditor = createPlateEditor({ editor }); const json = '{ name: "ChatGPT", type: AI }'; - const formattedJson = formatCode(json); + const formattedJson = formatCode(json, 'json'); expect(formattedJson).toBe(json); }); }); From 514bec52cd34fff20ef3c660b316398b79df4124 Mon Sep 17 00:00:00 2001 From: patrickhertling Date: Sat, 1 Feb 2025 16:07:56 +0200 Subject: [PATCH 11/12] refactor out of react hook --- .../default/plate-ui/code-block-element.tsx | 9 ++--- .../code-block/src/lib/formatter/formatter.ts | 40 ++++++++++++++----- .../src/lib/formatter/jsonFormatter.spec.tsx | 10 ++--- packages/code-block/src/react/hooks/index.ts | 5 +-- .../src/react/hooks/useCodeBlockFormat.ts | 39 ------------------ 5 files changed, 40 insertions(+), 63 deletions(-) delete mode 100644 packages/code-block/src/react/hooks/useCodeBlockFormat.ts diff --git a/apps/www/src/registry/default/plate-ui/code-block-element.tsx b/apps/www/src/registry/default/plate-ui/code-block-element.tsx index f62aad3dd9..8c3fedc039 100644 --- a/apps/www/src/registry/default/plate-ui/code-block-element.tsx +++ b/apps/www/src/registry/default/plate-ui/code-block-element.tsx @@ -5,11 +5,8 @@ import React from 'react'; import type { Editor, TElement } from '@udecode/plate'; import { cn, withRef } from '@udecode/cn'; -import { - formatCodeBlock, - isLangSupported, - useCodeBlockElementState, -} from '@udecode/plate-code-block/react'; +import { formatCodeBlock, isLangSupported } from '@udecode/plate-code-block'; +import { useCodeBlockElementState } from '@udecode/plate-code-block/react'; import { BracesIcon } from 'lucide-react'; import { Button } from './button'; @@ -55,7 +52,7 @@ export function CodeBlockFormatButton({ editor: Editor; element: TElement; }) { - if (!isLangSupported(element)) { + if (!isLangSupported(element.lang as string)) { return null; } diff --git a/packages/code-block/src/lib/formatter/formatter.ts b/packages/code-block/src/lib/formatter/formatter.ts index 62db5a9dee..6b86a28151 100644 --- a/packages/code-block/src/lib/formatter/formatter.ts +++ b/packages/code-block/src/lib/formatter/formatter.ts @@ -1,3 +1,7 @@ +import type { Editor } from '@udecode/plate'; + +import type { TCodeBlockElement } from '../types'; + import { formatJson, isValidJson } from './jsonFormatter'; const supportedLanguages = new Set(['json']); @@ -5,32 +9,50 @@ const supportedLanguages = new Set(['json']); export const isLangSupported = (lang?: string): boolean => Boolean(lang && supportedLanguages.has(lang)); -export const formatCode = (code: string, lang?: string): string => { +export const isValidSyntax = (code: string, lang?: string): boolean => { if (!isLangSupported(lang)) { - return ''; + return false; } switch (lang) { case 'json': { - return formatJson(code); + return isValidJson(code); } default: { - return code; + return false; } } }; -export const isValidSyntax = (code: string, lang?: string): boolean => { - if (!isLangSupported(lang)) { - return false; +export const formatCodeBlock = ( + editor: Editor, + { + element, + }: { + element: TCodeBlockElement; } +) => { + const { lang } = element; + if (!lang || !isLangSupported(lang)) { + return; + } + + const code = editor.api.string(element); + + if (isValidSyntax(code, lang)) { + const formattedCode = formatCode(code, lang); + editor.tf.insertText(formattedCode, { at: element }); + } +}; + +const formatCode = (code: string, lang?: string): string => { switch (lang) { case 'json': { - return isValidJson(code); + return formatJson(code); } default: { - return false; + return code; } } }; diff --git a/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx index c6744a4664..1a6808648e 100644 --- a/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx +++ b/packages/code-block/src/lib/formatter/jsonFormatter.spec.tsx @@ -1,21 +1,21 @@ -import { formatCode, isValidSyntax } from './formatter'; +import { formatJson, isValidJson } from './jsonFormatter'; describe('JsonFormatter', () => { it('should detect valid JSON', () => { const json = '{ "name": "ChatGPT", "type": "AI" }'; - const isValid = isValidSyntax(json, 'json'); + const isValid = isValidJson(json); expect(isValid).toBe(true); }); it('should detect invalid JSON', () => { const json = '{ name: "ChatGPT", type: AI }'; - const isValid = isValidSyntax(json, 'json'); + const isValid = isValidJson(json); expect(isValid).toBe(false); }); it('should format JSON', () => { const json = '{"name":"ChatGPT","type":"AI"}'; - const formattedJson = formatCode(json, 'json'); + const formattedJson = formatJson(json); const expected = `{ "name": "ChatGPT", "type": "AI" @@ -25,7 +25,7 @@ describe('JsonFormatter', () => { it('should not format invalid JSON', () => { const json = '{ name: "ChatGPT", type: AI }'; - const formattedJson = formatCode(json, 'json'); + const formattedJson = formatJson(json); expect(formattedJson).toBe(json); }); }); diff --git a/packages/code-block/src/react/hooks/index.ts b/packages/code-block/src/react/hooks/index.ts index 79f136e998..90d44568ea 100644 --- a/packages/code-block/src/react/hooks/index.ts +++ b/packages/code-block/src/react/hooks/index.ts @@ -1,9 +1,6 @@ -/** - * @file Automatically generated by barrelsby. - */ +/** @file Automatically generated by barrelsby. */ export * from './useCodeBlockCombobox'; export * from './useCodeBlockElement'; -export * from './useCodeBlockFormat'; export * from './useCodeSyntaxLeaf'; export * from './useToggleCodeBlockButton'; diff --git a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts b/packages/code-block/src/react/hooks/useCodeBlockFormat.ts deleted file mode 100644 index 19dbedb807..0000000000 --- a/packages/code-block/src/react/hooks/useCodeBlockFormat.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Editor } from '@udecode/plate'; - -import type { TCodeBlockElement } from '../../lib'; - -import { - isLangSupported as _isLangSupported, - isValidSyntax as _isValidSyntax, - formatCode, -} from '../../lib/formatter/formatter'; - -export const formatCodeBlock = ( - editor: Editor, - { - element, - }: { - element: TCodeBlockElement; - } -) => { - const { lang: language } = element; - - if (!language || !_isLangSupported(language)) { - return; - } - - const code = editor.api.string(element); - - if (_isValidSyntax(code, language)) { - const formattedCode = formatCode(code, language); - editor.tf.insertText(formattedCode, { at: element }); - } -}; - -export const isValidSyntax = (code: string, language: string) => { - return _isValidSyntax(code, language); -}; - -export const isLangSupported = (element: TCodeBlockElement) => { - return _isLangSupported(element.lang); -}; From 56d3d21296d157bbbcc80571bbf5e674e59c7840 Mon Sep 17 00:00:00 2001 From: patrickhertling Date: Sat, 1 Feb 2025 16:18:15 +0200 Subject: [PATCH 12/12] changeset --- .changeset/flat-parrots-trade.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/flat-parrots-trade.md diff --git a/.changeset/flat-parrots-trade.md b/.changeset/flat-parrots-trade.md new file mode 100644 index 0000000000..cec146df77 --- /dev/null +++ b/.changeset/flat-parrots-trade.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-code-block': minor +--- + +Ability to format a valid JSON string in a code block.