From a9fc23b788b5fce830f93356405f48611268e689 Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Wed, 30 Jul 2025 17:08:03 -0700 Subject: [PATCH 01/10] feat: add enhanced MonacoEditor props for feature parity - Add line prop for jumping to specific line numbers - Add beforeMount callback for pre-editor setup - Add onValidate callback for validation markers - Add line positioning effect with proper reactivity - Add comprehensive TypeScript interfaces - Add devcontainer configuration for Node.js 24 - Add basic tests for new props functionality These changes improve monaco-react compatibility and provide better developer experience for Monaco Editor integration. --- .devcontainer/devcontainer.json | 33 +++++++++++++++++ src/MonacoEditor.test.tsx | 37 +++++++++++++++++-- src/MonacoEditor.tsx | 64 +++++++++++++++++++++++++++++---- 3 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..2a4f18b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +{ + "name": "Solid Monaco Development", + "image": "node:24-bullseye", + "features": { + "ghcr.io/devcontainers/features/git:1": {} + }, + "customizations": { + "vscode": { + "extensions": [ + "esbenp.prettier-vscode", + "ms-vscode.vscode-typescript-next" + ], + "settings": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "typescript.preferences.importModuleSpecifier": "relative" + } + } + }, + "postCreateCommand": "pnpm install", + "remoteUser": "node", + "workspaceFolder": "/workspace", + "mounts": [ + "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached" + ], + "forwardPorts": [5173], + "portsAttributes": { + "5173": { + "label": "Vite Dev Server", + "onAutoForward": "notify" + } + } +} \ No newline at end of file diff --git a/src/MonacoEditor.test.tsx b/src/MonacoEditor.test.tsx index 0d8346d..ff170ca 100644 --- a/src/MonacoEditor.test.tsx +++ b/src/MonacoEditor.test.tsx @@ -1,8 +1,7 @@ import { createRoot } from 'solid-js' -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { MonacoEditor } from '../src' -// TODO: add real tests describe('MonacoEditor', () => { it('renders a MonacoEditor component', async () => { createRoot(() => { @@ -10,4 +9,38 @@ describe('MonacoEditor', () => { expect(container.outerHTML).toMatchSnapshot() }) }) + + it('accepts new props without errors', async () => { + createRoot(() => { + const beforeMount = vi.fn() + const onValidate = vi.fn() + + const container = ( + + ) as HTMLDivElement + + expect(container).toBeDefined() + }) + }) + + it('has correct TypeScript interface for new props', () => { + // This test ensures the interface compiles correctly + const props: Parameters[0] = { + line: 10, + beforeMount: (monaco) => { + // beforeMount callback should receive Monaco instance + expect(monaco).toBeDefined() + }, + onValidate: (markers) => { + // onValidate callback should receive markers array + expect(Array.isArray(markers)).toBe(true) + } + } + + expect(props).toBeDefined() + }) }) diff --git a/src/MonacoEditor.tsx b/src/MonacoEditor.tsx index dc75b4e..de3e286 100644 --- a/src/MonacoEditor.tsx +++ b/src/MonacoEditor.tsx @@ -9,6 +9,7 @@ import { LoaderParams } from './types' const viewStates = new Map() export interface MonacoEditorProps { + // Existing props language?: string value?: string loadingState?: JSX.Element @@ -24,6 +25,11 @@ export interface MonacoEditorProps { onChange?: (value: string, event: monacoEditor.editor.IModelContentChangedEvent) => void onMount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneCodeEditor) => void onBeforeUnmount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneCodeEditor) => void + + // NEW: Enhanced props for better monaco-react compatibility + line?: number // Jump to specific line number + beforeMount?: (monaco: Monaco) => void // Pre-editor setup callback + onValidate?: (markers: monacoEditor.editor.IMarker[]) => void // Validation markers callback } export const MonacoEditor = (inputProps: MonacoEditorProps) => { @@ -38,13 +44,14 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { inputProps, ) - let containerRef: HTMLDivElement + let containerRef: HTMLDivElement | undefined const [monaco, setMonaco] = createSignal() const [editor, setEditor] = createSignal() let abortInitialization: (() => void) | undefined let monacoOnChangeSubscription: any + let validationSubscription: monacoEditor.IDisposable | undefined let isOnChangeSuppressed = false onMount(async () => { @@ -55,16 +62,40 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { try { const monaco = await loadMonaco + + // Call beforeMount callback before editor creation + props.beforeMount?.(monaco) + const editor = createEditor(monaco) setMonaco(monaco) setEditor(editor) + + // Handle initial line positioning + if (props.line !== undefined) { + editor.revealLine(props.line) + } + props.onMount?.(monaco, editor) - monacoOnChangeSubscription = editor.onDidChangeModelContent(event => { + monacoOnChangeSubscription = editor.onDidChangeModelContent((event: monacoEditor.editor.IModelContentChangedEvent) => { if (!isOnChangeSuppressed) { props.onChange?.(editor.getValue(), event) } }) + + // Setup validation subscription if onValidate is provided + if (props.onValidate) { + validationSubscription = monaco.editor.onDidChangeMarkers((uris: monacoEditor.Uri[]) => { + const editorUri = editor.getModel()?.uri + if (editorUri) { + const currentEditorHasMarkerChanges = uris.find((uri: monacoEditor.Uri) => uri.path === editorUri.path) + if (currentEditorHasMarkerChanges) { + const markers = monaco.editor.getModelMarkers({ resource: editorUri }) + props.onValidate!(markers) + } + } + }) + } } catch (error: any) { if (error?.type === 'cancelation') { return @@ -83,6 +114,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { props.onBeforeUnmount?.(monaco()!, _editor) monacoOnChangeSubscription?.dispose() + validationSubscription?.dispose() _editor.getModel()?.dispose() _editor.dispose() }) @@ -90,7 +122,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { createEffect( on( () => props.value, - value => { + (value: string | undefined) => { const _editor = editor() if (!_editor || typeof value === 'undefined') { return @@ -123,7 +155,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { createEffect( on( () => props.options, - options => { + (options: monacoEditor.editor.IStandaloneEditorConstructionOptions | undefined) => { editor()?.updateOptions(options ?? {}) }, { defer: true }, @@ -133,7 +165,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { createEffect( on( () => props.theme, - theme => { + (theme: monacoEditor.editor.BuiltinTheme | string) => { monaco()?.editor.setTheme(theme) }, { defer: true }, @@ -143,7 +175,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { createEffect( on( () => props.language, - language => { + (language: string | undefined) => { const model = editor()?.getModel() if (!language || !model) { return @@ -158,7 +190,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { createEffect( on( () => props.path, - (path, prevPath) => { + (path: string | undefined, prevPath: string | undefined) => { const _monaco = monaco() if (!_monaco) { return @@ -180,7 +212,25 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { ), ) + // NEW: Line positioning effect + createEffect( + on( + () => props.line, + (line: number | undefined) => { + const currentEditor = editor() + if (line !== undefined && currentEditor) { + currentEditor.revealLine(line) + } + }, + { defer: true }, + ), + ) + const createEditor = (monaco: Monaco) => { + if (!containerRef) { + throw new Error('Container ref not available') + } + const model = getOrCreateModel(monaco, props.value ?? '', props.language, props.path) return monaco.editor.create( From 7010fc2f938f7bbea0026c7e741b84cb65e4c843 Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Wed, 30 Jul 2025 17:09:04 -0700 Subject: [PATCH 02/10] docs: add documentation for new enhanced MonacoEditor props - Document line prop for line positioning - Document beforeMount callback for pre-editor setup - Document onValidate callback for validation markers - Add comprehensive usage examples for each new feature - Highlight new props in props table with bold formatting These docs help developers understand and adopt the new monaco-react compatibility features. --- README.md | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/README.md b/README.md index 39f3522..d649214 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,9 @@ The `MonacoEditor` component accepts the following props: | `onChange` | `(value: string, event: editor.IModelContentChangedEvent) => void` | - | Callback triggered when the content of the editor changes. | | `onMount` | `(monaco: Monaco, editor: editor.IStandaloneCodeEditor) => void` | - | Callback triggered when the editor mounts. | | `onBeforeUnmount` | `(monaco: Monaco, editor: editor.IStandaloneCodeEditor) => void` | - | Callback triggered before the editor unmounts. | +| **`line`** | `number` | - | **NEW:** Jump to specific line number in the editor. | +| **`beforeMount`** | `(monaco: Monaco) => void` | - | **NEW:** Callback triggered before editor creation for setup. | +| **`onValidate`** | `(markers: editor.IMarker[]) => void` | - | **NEW:** Callback triggered when validation markers change. | ### Getting Monaco and Editor Instances @@ -82,6 +85,98 @@ function MyEditor() { } ``` +### New Enhanced Features + +#### Line Positioning + +Jump to a specific line number in the editor: + +```jsx +import { MonacoEditor } from 'solid-monaco'; +import { createSignal } from 'solid-js'; + +function MyEditor() { + const [currentLine, setCurrentLine] = createSignal(42); + + return ( +
+ + +
+ ); +} +``` + +#### Pre-Editor Setup + +Use the `beforeMount` callback to configure Monaco before the editor is created: + +```jsx +import { MonacoEditor } from 'solid-monaco'; + +function MyEditor() { + const handleBeforeMount = (monaco) => { + // Configure Monaco before editor creation + monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + noSyntaxValidation: false, + }); + + // Register custom themes, languages, etc. + monaco.editor.defineTheme('myCustomTheme', { + base: 'vs-dark', + inherit: true, + rules: [], + colors: { + 'editor.background': '#1e1e1e', + } + }); + }; + + return ( + + ); +} +``` + +#### Validation Markers + +Monitor validation errors and warnings in real-time: + +```jsx +import { MonacoEditor } from 'solid-monaco'; +import { createSignal } from 'solid-js'; + +function MyEditor() { + const [errors, setErrors] = createSignal([]); + + const handleValidate = (markers) => { + setErrors(markers.filter(marker => marker.severity === 8)); // Errors only + console.log('Validation markers:', markers); + }; + + return ( +
+
Errors: {errors().length}
+ +
+ ); +} +``` + ## MonacoDiffEditor For a side-by-side comparison view of code, the package provides a `MonacoDiffEditor` component. From 3327812291c9d46f8189e7d49407073b47416ff8 Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Wed, 30 Jul 2025 17:14:19 -0700 Subject: [PATCH 03/10] feat: enhance MonacoDiffEditor with correct TypeScript types and new props - Fix options prop to use IStandaloneDiffEditorConstructionOptions instead of IStandaloneEditorConstructionOptions - Add beforeMount prop with full implementation for pre-editor setup - Improve TypeScript type annotations throughout - Fix container ref handling with proper undefined checks - Update documentation to reflect all enhancements This resolves the incorrect options type that was preventing proper diff editor configuration and adds feature parity with MonacoEditor for the beforeMount callback functionality. --- README.md | 3 ++- src/MonacoDiffEditor.tsx | 33 ++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d649214..74247c5 100644 --- a/README.md +++ b/README.md @@ -218,11 +218,12 @@ The `MonacoDiffEditor` component accepts the following props: | `overrideServices` | `object` | - | Services to override the default ones provided by Monaco. | | `width` | `string` | `"100%"` | Width of the diff editor container. | | `height` | `string` | `"100%"` | Height of the diff editor container. | -| `options` | `object` | - | Additional options for the Monaco diff editor. | +| `options` | `IStandaloneDiffEditorConstructionOptions` | - | **FIXED:** Correct diff editor options type (was incorrectly using regular editor options). | | `saveViewState` | `boolean` | `true` | Whether to save the model view state. | | `onChange` | `(value: string) => void` | - | Callback triggered when the content of the modified editor changes. | | `onMount` | `(monaco: Monaco, editor: editor.IStandaloneDiffEditor) => void` | - | Callback triggered when the diff editor mounts. | | `onBeforeUnmount` | `(monaco: Monaco, editor: editor.IStandaloneDiffEditor) => void` | - | Callback triggered before the diff editor unmounts. | +| **`beforeMount`** | `(monaco: Monaco) => void` | - | **NEW:** Callback triggered before diff editor creation for setup. | ## Contributing diff --git a/src/MonacoDiffEditor.tsx b/src/MonacoDiffEditor.tsx index 8967f36..dd797f1 100644 --- a/src/MonacoDiffEditor.tsx +++ b/src/MonacoDiffEditor.tsx @@ -9,6 +9,7 @@ import { LoaderParams } from './types' const viewStates = new Map() export interface MonacoDiffEditorProps { + // Content props original?: string modified?: string @@ -18,18 +19,24 @@ export interface MonacoDiffEditorProps { originalPath?: string modifiedPath?: string + // UI props loadingState?: JSX.Element class?: string theme?: monacoEditor.editor.BuiltinTheme | string overrideServices?: monacoEditor.editor.IEditorOverrideServices width?: string height?: string - options?: monacoEditor.editor.IStandaloneEditorConstructionOptions + options?: monacoEditor.editor.IStandaloneDiffEditorConstructionOptions // FIXED: Use correct diff editor options type saveViewState?: boolean loaderParams?: LoaderParams + + // Callback props onChange?: (value: string) => void onMount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneDiffEditor) => void onBeforeUnmount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneDiffEditor) => void + + // NEW: Enhanced props for feature parity + beforeMount?: (monaco: Monaco) => void // Pre-editor setup callback } export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { @@ -44,13 +51,13 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { inputProps, ) - let containerRef: HTMLDivElement + let containerRef: HTMLDivElement | undefined const [monaco, setMonaco] = createSignal() const [editor, setEditor] = createSignal() let abortInitialization: (() => void) | undefined - let monacoOnChangeSubscription: any + let monacoOnChangeSubscription: monacoEditor.IDisposable | undefined let isOnChangeSuppressed = false onMount(async () => { @@ -61,6 +68,10 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { try { const monaco = await loadMonaco + + // Call beforeMount callback before editor creation + props.beforeMount?.(monaco) + const editor = createEditor(monaco) setMonaco(monaco) setEditor(editor) @@ -97,7 +108,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { createEffect( on( () => props.modified, - modified => { + (modified: string | undefined) => { const _editor = editor()?.getModifiedEditor() if (!_editor || typeof modified === 'undefined') { return @@ -130,7 +141,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { createEffect( on( () => props.original, - original => { + (original: string | undefined) => { const _editor = editor()?.getOriginalEditor() if (!_editor || typeof original === 'undefined') { return @@ -147,7 +158,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { createEffect( on( () => props.options, - options => { + (options: monacoEditor.editor.IStandaloneDiffEditorConstructionOptions | undefined) => { editor()?.updateOptions(options ?? {}) }, { defer: true }, @@ -157,7 +168,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { createEffect( on( () => props.theme, - theme => { + (theme: monacoEditor.editor.BuiltinTheme | string) => { monaco()?.editor.setTheme(theme) }, { defer: true }, @@ -167,7 +178,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { createEffect( on( () => props.originalLanguage, - language => { + (language: string | undefined) => { const model = editor()?.getModel() if (!language || !model) { return @@ -182,7 +193,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { createEffect( on( () => props.modifiedLanguage, - language => { + (language: string | undefined) => { const model = editor()?.getModel() if (!language || !model) { return @@ -247,6 +258,10 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { ) const createEditor = (monaco: Monaco) => { + if (!containerRef) { + throw new Error('Container ref not available') + } + const originalModel = getOrCreateModel( monaco, props.original ?? '', From 0e7b72ed50d472814c4b21e03231e650454950f8 Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Thu, 31 Jul 2025 09:55:06 -0700 Subject: [PATCH 04/10] chore: update devcontainer config and gitignore - Enable corepack for pnpm in devcontainer - Change to root user for better permissions - Add .pnpm-store to gitignore --- .devcontainer/devcontainer.json | 4 ++-- .gitignore | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2a4f18b..58d9cf7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,8 +17,8 @@ } } }, - "postCreateCommand": "pnpm install", - "remoteUser": "node", + "postCreateCommand": "corepack enable pnpm && pnpm install", + "remoteUser": "root", "workspaceFolder": "/workspace", "mounts": [ "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached" diff --git a/.gitignore b/.gitignore index fb13f66..5a1d742 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ dist gitignore .idea .vscode +.pnpm-store # tsup tsup.config.bundled_*.{m,c,}s From 0b41954206776c3881271628519499e88f14e5cc Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Thu, 31 Jul 2025 09:57:38 -0700 Subject: [PATCH 05/10] fix: resolve TypeScript and ESLint issues - Fix readonly array type for Monaco validation markers - Remove unnecessary type assertions in ref assignments - Ensure all linting passes with zero warnings All validation complete: TypeScript compilation, tests, build, and linting all pass successfully. --- src/MonacoDiffEditor.tsx | 2 +- src/MonacoEditor.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MonacoDiffEditor.tsx b/src/MonacoDiffEditor.tsx index dd797f1..51e0057 100644 --- a/src/MonacoDiffEditor.tsx +++ b/src/MonacoDiffEditor.tsx @@ -295,7 +295,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { return ( {!editor() && {props.loadingState}} -
+
) } diff --git a/src/MonacoEditor.tsx b/src/MonacoEditor.tsx index de3e286..39564e2 100644 --- a/src/MonacoEditor.tsx +++ b/src/MonacoEditor.tsx @@ -85,7 +85,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { // Setup validation subscription if onValidate is provided if (props.onValidate) { - validationSubscription = monaco.editor.onDidChangeMarkers((uris: monacoEditor.Uri[]) => { + validationSubscription = monaco.editor.onDidChangeMarkers((uris: readonly monacoEditor.Uri[]) => { const editorUri = editor.getModel()?.uri if (editorUri) { const currentEditorHasMarkerChanges = uris.find((uri: monacoEditor.Uri) => uri.path === editorUri.path) @@ -247,7 +247,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { return ( {!editor() && {props.loadingState}} -
+
) } From 63ae5c71f9a26339c580e31996b085283adcf4dc Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Fri, 1 Aug 2025 14:54:28 -0700 Subject: [PATCH 06/10] Don't import the entire monaco-editor using '* as' since it explodes the bundle size of any downstream project consuming this library. Instead import only the types and use CDN URL as the fallback. --- src/MonacoDiffEditor.tsx | 8 ++++++-- src/MonacoEditor.tsx | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/MonacoDiffEditor.tsx b/src/MonacoDiffEditor.tsx index 51e0057..5600591 100644 --- a/src/MonacoDiffEditor.tsx +++ b/src/MonacoDiffEditor.tsx @@ -1,5 +1,5 @@ import { createSignal, createEffect, onCleanup, JSX, onMount, mergeProps, on } from 'solid-js' -import * as monacoEditor from 'monaco-editor' +import type * as monacoEditor from 'monaco-editor' import loader, { Monaco } from '@monaco-editor/loader' import { Loader } from './Loader' import { MonacoContainer } from './MonacoContainer' @@ -61,7 +61,11 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { let isOnChangeSuppressed = false onMount(async () => { - loader.config(inputProps.loaderParams ?? { monaco: monacoEditor }) + loader.config(inputProps.loaderParams ?? { + paths: { + vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.48.0/min/vs' + } + }) const loadMonaco = loader.init() abortInitialization = () => loadMonaco.cancel() diff --git a/src/MonacoEditor.tsx b/src/MonacoEditor.tsx index 39564e2..7da92a0 100644 --- a/src/MonacoEditor.tsx +++ b/src/MonacoEditor.tsx @@ -1,5 +1,5 @@ import { createSignal, createEffect, onCleanup, JSX, onMount, mergeProps, on } from 'solid-js' -import * as monacoEditor from 'monaco-editor' +import type * as monacoEditor from 'monaco-editor' import loader, { Monaco } from '@monaco-editor/loader' import { Loader } from './Loader' import { MonacoContainer } from './MonacoContainer' @@ -55,7 +55,11 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { let isOnChangeSuppressed = false onMount(async () => { - loader.config(inputProps.loaderParams ?? { monaco: monacoEditor }) + loader.config(inputProps.loaderParams ?? { + paths: { + vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.48.0/min/vs' + } + }) const loadMonaco = loader.init() abortInitialization = () => loadMonaco.cancel() From 4b243486697dfd892db2617d7172cba6d03f1eff Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Fri, 1 Aug 2025 23:00:08 +0000 Subject: [PATCH 07/10] Cleanup add README help. --- README.md | 297 ++++++++++++++++++++++++++++++-------- src/MonacoDiffEditor.tsx | 22 +-- src/MonacoEditor.test.tsx | 36 +---- src/MonacoEditor.tsx | 24 +-- 4 files changed, 253 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index 74247c5..9ee6d48 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,15 @@ Install it: ```bash npm i solid-monaco ``` -*or* + +_or_ ```bash yarn add solid-monaco ``` -*or* + +_or_ + ```bash pnpm add solid-monaco ``` @@ -32,10 +35,10 @@ Basic usage: You can import and use the `MonacoEditor` component in your Solid application: ```jsx -import { MonacoEditor } from 'solid-monaco'; +import { MonacoEditor } from 'solid-monaco' function MyEditor() { - return ; + return } ``` @@ -44,9 +47,10 @@ function MyEditor() { The `MonacoEditor` component accepts the following props: | Prop | Type | Default | Description | -|--------------------|--------------------------------------------------------------------|--------------|--------------------------------------------------------------------------------| +| ------------------ | ------------------------------------------------------------------ | ------------ | ------------------------------------------------------------------------------ | | `language` | `string` | - | The programming language for the editor. E.g., `"javascript"`, `"typescript"`. | | `value` | `string` | - | Content of the editor. | +| `line` | `number` | - | Jump to specific line number in the editor. | | `loadingState` | `JSX.Element` | `"Loading…"` | JSX element to be displayed during the loading state. | | `class` | `string` | - | CSS class for the editor container. | | `theme` | `BuiltinTheme` or `string` | `"vs"` | The theme to be applied to the editor. | @@ -57,46 +61,39 @@ The `MonacoEditor` component accepts the following props: | `options` | `object` | - | Additional options for the Monaco editor. | | `saveViewState` | `string` | `true` | Whether to save the model view state for a given path of the editor. | | `onChange` | `(value: string, event: editor.IModelContentChangedEvent) => void` | - | Callback triggered when the content of the editor changes. | +| `onBeforeMount` | `(monaco: Monaco) => void` | - | Callback triggered before editor creation for setup. | | `onMount` | `(monaco: Monaco, editor: editor.IStandaloneCodeEditor) => void` | - | Callback triggered when the editor mounts. | | `onBeforeUnmount` | `(monaco: Monaco, editor: editor.IStandaloneCodeEditor) => void` | - | Callback triggered before the editor unmounts. | -| **`line`** | `number` | - | **NEW:** Jump to specific line number in the editor. | -| **`beforeMount`** | `(monaco: Monaco) => void` | - | **NEW:** Callback triggered before editor creation for setup. | -| **`onValidate`** | `(markers: editor.IMarker[]) => void` | - | **NEW:** Callback triggered when validation markers change. | +| `onValidate` | `(markers: editor.IMarker[]) => void` | - | Callback triggered when validation markers change. | ### Getting Monaco and Editor Instances You can get instances of both `monaco` and the `editor` by using the `onMount` callback: ```jsx -import { MonacoEditor } from 'solid-monaco'; +import { MonacoEditor } from 'solid-monaco' function MyEditor() { const handleMount = (monaco, editor) => { // Use monaco and editor instances here - }; + } return ( - - ); + + ) } ``` -### New Enhanced Features - #### Line Positioning Jump to a specific line number in the editor: ```jsx -import { MonacoEditor } from 'solid-monaco'; -import { createSignal } from 'solid-js'; +import { MonacoEditor } from 'solid-monaco' +import { createSignal } from 'solid-js' function MyEditor() { - const [currentLine, setCurrentLine] = createSignal(42); + const [currentLine, setCurrentLine] = createSignal(42) return (
@@ -107,7 +104,7 @@ function MyEditor() { line={currentLine()} />
- ); + ) } ``` @@ -116,16 +113,16 @@ function MyEditor() { Use the `beforeMount` callback to configure Monaco before the editor is created: ```jsx -import { MonacoEditor } from 'solid-monaco'; +import { MonacoEditor } from 'solid-monaco' function MyEditor() { - const handleBeforeMount = (monaco) => { + const handleBeforeMount = monaco => { // Configure Monaco before editor creation monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ noSemanticValidation: true, noSyntaxValidation: false, - }); - + }) + // Register custom themes, languages, etc. monaco.editor.defineTheme('myCustomTheme', { base: 'vs-dark', @@ -133,9 +130,9 @@ function MyEditor() { rules: [], colors: { 'editor.background': '#1e1e1e', - } - }); - }; + }, + }) + } return ( - ); + ) } ``` @@ -153,16 +150,16 @@ function MyEditor() { Monitor validation errors and warnings in real-time: ```jsx -import { MonacoEditor } from 'solid-monaco'; -import { createSignal } from 'solid-js'; +import { MonacoEditor } from 'solid-monaco' +import { createSignal } from 'solid-js' function MyEditor() { - const [errors, setErrors] = createSignal([]); + const [errors, setErrors] = createSignal([]) - const handleValidate = (markers) => { - setErrors(markers.filter(marker => marker.severity === 8)); // Errors only - console.log('Validation markers:', markers); - }; + const handleValidate = markers => { + setErrors(markers.filter(marker => marker.severity === 8)) // Errors only + console.log('Validation markers:', markers) + } return (
@@ -173,7 +170,7 @@ function MyEditor() { onValidate={handleValidate} />
- ); + ) } ``` @@ -186,7 +183,7 @@ For a side-by-side comparison view of code, the package provides a `MonacoDiffEd You can incorporate the `MonacoDiffEditor` component into your Solid application: ```jsx -import { MonacoDiffEditor } from 'solid-monaco'; +import { MonacoDiffEditor } from 'solid-monaco' function MyDiffEditor() { return ( @@ -196,7 +193,7 @@ function MyDiffEditor() { originalLanguage="javascript" modifiedLanguage="javascript" /> - ); + ) } ``` @@ -204,26 +201,206 @@ function MyDiffEditor() { The `MonacoDiffEditor` component accepts the following props: -| Prop | Type | Default | Description | -|--------------------|------------------------------------------------------------------|--------------|------------------------------------------------------------------------| -| `original` | `string` | - | Original content to be displayed on the left side of the diff editor. | -| `modified` | `string` | - | Modified content to be displayed on the right side of the diff editor. | -| `originalLanguage` | `string` | - | Language for the original content. | -| `modifiedLanguage` | `string` | - | Language for the modified content. | -| `originalPath` | `string` | - | Path for the original content used in Monaco model management. | -| `modifiedPath` | `string` | - | Path for the modified content used in Monaco model management. | -| `loadingState` | `JSX.Element` | `"Loading…"` | JSX element displayed during the loading state. | -| `class` | `string` | - | CSS class for the diff editor container. | -| `theme` | `BuiltinTheme` or `string` | `"vs"` | Theme applied to the diff editor. | -| `overrideServices` | `object` | - | Services to override the default ones provided by Monaco. | -| `width` | `string` | `"100%"` | Width of the diff editor container. | -| `height` | `string` | `"100%"` | Height of the diff editor container. | -| `options` | `IStandaloneDiffEditorConstructionOptions` | - | **FIXED:** Correct diff editor options type (was incorrectly using regular editor options). | -| `saveViewState` | `boolean` | `true` | Whether to save the model view state. | -| `onChange` | `(value: string) => void` | - | Callback triggered when the content of the modified editor changes. | -| `onMount` | `(monaco: Monaco, editor: editor.IStandaloneDiffEditor) => void` | - | Callback triggered when the diff editor mounts. | -| `onBeforeUnmount` | `(monaco: Monaco, editor: editor.IStandaloneDiffEditor) => void` | - | Callback triggered before the diff editor unmounts. | -| **`beforeMount`** | `(monaco: Monaco) => void` | - | **NEW:** Callback triggered before diff editor creation for setup. | +| Prop | Type | Default | Description | +| ------------------ | ---------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------- | +| `original` | `string` | - | Original content to be displayed on the left side of the diff editor. | +| `modified` | `string` | - | Modified content to be displayed on the right side of the diff editor. | +| `originalLanguage` | `string` | - | Language for the original content. | +| `modifiedLanguage` | `string` | - | Language for the modified content. | +| `originalPath` | `string` | - | Path for the original content used in Monaco model management. | +| `modifiedPath` | `string` | - | Path for the modified content used in Monaco model management. | +| `loadingState` | `JSX.Element` | `"Loading…"` | JSX element displayed during the loading state. | +| `class` | `string` | - | CSS class for the diff editor container. | +| `theme` | `BuiltinTheme` or `string` | `"vs"` | Theme applied to the diff editor. | +| `overrideServices` | `object` | - | Services to override the default ones provided by Monaco. | +| `width` | `string` | `"100%"` | Width of the diff editor container. | +| `height` | `string` | `"100%"` | Height of the diff editor container. | +| `options` | `IStandaloneDiffEditorConstructionOptions` | - | Correct diff editor options type (was incorrectly using regular editor options). | +| `saveViewState` | `boolean` | `true` | Whether to save the model view state. | +| `onChange` | `(value: string) => void` | - | Callback triggered when the content of the modified editor changes. | +| `onMount` | `(monaco: Monaco, editor: editor.IStandaloneDiffEditor) => void` | - | Callback triggered when the diff editor mounts. | +| `onBeforeUnmount` | `(monaco: Monaco, editor: editor.IStandaloneDiffEditor) => void` | - | Callback triggered before the diff editor unmounts. | +| `onBeforeMount` | `(monaco: Monaco) => void` | - | Callback triggered before diff editor creation for setup. | + +## Production Setup Guide + +### Asset Loading Configuration + +The library supports both CDN and local asset loading strategies. + +#### CDN Loading (Default) +```jsx +// Uses CDN by default - no configuration needed + +``` + +#### Local Asset Loading + +**1. Install Dependencies** + +Ensure `monaco-editor` is a regular dependency (not devDependency) in your `package.json`: + +```json +{ + "dependencies": { + "monaco-editor": "^0.45.0", + "solid-monaco": "^0.x.x" + } +} +``` + +**2. Environment Configuration** + +Create environment-specific configurations: + +```bash +# .env (development) +VITE_MONACO_ASSETS_PATH=/node_modules/monaco-editor/dev/vs + +# .env.production +VITE_MONACO_ASSETS_PATH=/assets/monaco-assets/vs +``` + +**3. Vite Configuration** + +Add a custom plugin to copy Monaco assets during build: + +```typescript +// vite.config.ts +import { cpSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { defineConfig } from 'vite'; +import solidPlugin from 'vite-plugin-solid'; + +const monacoAssetsPlugin = () => { + return { + name: 'monaco-assets', + generateBundle() { + // Copy Monaco Editor assets to build directory + const monacoSrc = join(process.cwd(), 'node_modules/monaco-editor/min/vs'); + const buildDest = join(process.cwd(), 'build/assets/monaco-assets/vs'); + + if (existsSync(monacoSrc)) { + cpSync(monacoSrc, buildDest, { recursive: true }); + console.log('✓ Monaco Editor assets copied to build directory'); + } + }, + }; +}; + +export default defineConfig({ + plugins: [solidPlugin(), monacoAssetsPlugin()], + // ... other config +}); +``` + +**4. Component Usage** + +Use the environment variable to configure asset loading: + +```jsx +import { MonacoDiffEditor } from 'solid-monaco'; + +function MyDiffEditor() { + const configureDiffEditor = (monaco) => { + // Configure JSON language features + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + allowComments: false, + schemas: [], + enableSchemaRequest: false, + }); + + // Configure JSON formatting + monaco.languages.json.jsonDefaults.setModeConfiguration({ + documentFormattingEdits: true, + documentRangeFormattingEdits: true, + completionItems: true, + hovers: true, + documentSymbols: true, + tokens: true, + colors: true, + foldingRanges: true, + diagnostics: true, + selectionRanges: true, + }); + }; + + return ( + + ); +} +``` + +### Build Optimization + +#### Bundle Splitting + +Configure Vite to split Monaco into separate chunks for better loading performance: + +```typescript +// vite.config.ts +export default defineConfig({ + build: { + rollupOptions: { + output: { + manualChunks: { + 'solid-monaco': ['solid-monaco'], + 'monaco-editor': ['monaco-editor'], + }, + }, + }, + }, +}); +``` + +### Troubleshooting + +#### Common Issues + +**Monaco assets not loading in production:** +- Ensure `monaco-editor` is in `dependencies`, not `devDependencies` +- Verify the Vite plugin is copying assets to the correct build directory +- Check that `VITE_MONACO_ASSETS_PATH` matches your actual asset path + +**Large bundle size:** +- Use `import type` for Monaco types to avoid bundling the entire library +- Configure bundle splitting to separate Monaco into its own chunk +- Consider lazy loading Monaco for non-critical editor instances + +**Web workers failing:** +- Ensure web worker files are accessible at the configured asset path +- Check browser console for 404 errors on worker files +- Verify CORS settings if serving assets from a different domain ## Contributing diff --git a/src/MonacoDiffEditor.tsx b/src/MonacoDiffEditor.tsx index 5600591..4445c2a 100644 --- a/src/MonacoDiffEditor.tsx +++ b/src/MonacoDiffEditor.tsx @@ -9,7 +9,6 @@ import { LoaderParams } from './types' const viewStates = new Map() export interface MonacoDiffEditorProps { - // Content props original?: string modified?: string @@ -19,24 +18,20 @@ export interface MonacoDiffEditorProps { originalPath?: string modifiedPath?: string - // UI props loadingState?: JSX.Element class?: string theme?: monacoEditor.editor.BuiltinTheme | string overrideServices?: monacoEditor.editor.IEditorOverrideServices width?: string height?: string - options?: monacoEditor.editor.IStandaloneDiffEditorConstructionOptions // FIXED: Use correct diff editor options type + options?: monacoEditor.editor.IStandaloneDiffEditorConstructionOptions saveViewState?: boolean loaderParams?: LoaderParams - - // Callback props + onChange?: (value: string) => void + onBeforeMount?: (monaco: Monaco) => void onMount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneDiffEditor) => void onBeforeUnmount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneDiffEditor) => void - - // NEW: Enhanced props for feature parity - beforeMount?: (monaco: Monaco) => void // Pre-editor setup callback } export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { @@ -51,7 +46,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { inputProps, ) - let containerRef: HTMLDivElement | undefined + let containerRef: HTMLDivElement const [monaco, setMonaco] = createSignal() const [editor, setEditor] = createSignal() @@ -74,7 +69,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { const monaco = await loadMonaco // Call beforeMount callback before editor creation - props.beforeMount?.(monaco) + props.onBeforeMount?.(monaco) const editor = createEditor(monaco) setMonaco(monaco) @@ -262,9 +257,6 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { ) const createEditor = (monaco: Monaco) => { - if (!containerRef) { - throw new Error('Container ref not available') - } const originalModel = getOrCreateModel( monaco, @@ -280,7 +272,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { ) const editor = monaco.editor.createDiffEditor( - containerRef, + containerRef!, { automaticLayout: true, ...props.options, @@ -299,7 +291,7 @@ export const MonacoDiffEditor = (inputProps: MonacoDiffEditorProps) => { return ( {!editor() && {props.loadingState}} -
+
) } diff --git a/src/MonacoEditor.test.tsx b/src/MonacoEditor.test.tsx index ff170ca..324f097 100644 --- a/src/MonacoEditor.test.tsx +++ b/src/MonacoEditor.test.tsx @@ -1,5 +1,5 @@ import { createRoot } from 'solid-js' -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import { MonacoEditor } from '../src' describe('MonacoEditor', () => { @@ -9,38 +9,4 @@ describe('MonacoEditor', () => { expect(container.outerHTML).toMatchSnapshot() }) }) - - it('accepts new props without errors', async () => { - createRoot(() => { - const beforeMount = vi.fn() - const onValidate = vi.fn() - - const container = ( - - ) as HTMLDivElement - - expect(container).toBeDefined() - }) - }) - - it('has correct TypeScript interface for new props', () => { - // This test ensures the interface compiles correctly - const props: Parameters[0] = { - line: 10, - beforeMount: (monaco) => { - // beforeMount callback should receive Monaco instance - expect(monaco).toBeDefined() - }, - onValidate: (markers) => { - // onValidate callback should receive markers array - expect(Array.isArray(markers)).toBe(true) - } - } - - expect(props).toBeDefined() - }) }) diff --git a/src/MonacoEditor.tsx b/src/MonacoEditor.tsx index 7da92a0..184f9e4 100644 --- a/src/MonacoEditor.tsx +++ b/src/MonacoEditor.tsx @@ -9,9 +9,9 @@ import { LoaderParams } from './types' const viewStates = new Map() export interface MonacoEditorProps { - // Existing props language?: string value?: string + line?: number loadingState?: JSX.Element class?: string theme?: monacoEditor.editor.BuiltinTheme | string @@ -23,13 +23,10 @@ export interface MonacoEditorProps { saveViewState?: boolean loaderParams?: LoaderParams onChange?: (value: string, event: monacoEditor.editor.IModelContentChangedEvent) => void + onBeforeMount?: (monaco: Monaco) => void onMount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneCodeEditor) => void onBeforeUnmount?: (monaco: Monaco, editor: monacoEditor.editor.IStandaloneCodeEditor) => void - - // NEW: Enhanced props for better monaco-react compatibility - line?: number // Jump to specific line number - beforeMount?: (monaco: Monaco) => void // Pre-editor setup callback - onValidate?: (markers: monacoEditor.editor.IMarker[]) => void // Validation markers callback + onValidate?: (markers: monacoEditor.editor.IMarker[]) => void } export const MonacoEditor = (inputProps: MonacoEditorProps) => { @@ -44,7 +41,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { inputProps, ) - let containerRef: HTMLDivElement | undefined + let containerRef: HTMLDivElement const [monaco, setMonaco] = createSignal() const [editor, setEditor] = createSignal() @@ -68,7 +65,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { const monaco = await loadMonaco // Call beforeMount callback before editor creation - props.beforeMount?.(monaco) + props.onBeforeMount?.(monaco) const editor = createEditor(monaco) setMonaco(monaco) @@ -216,7 +213,6 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { ), ) - // NEW: Line positioning effect createEffect( on( () => props.line, @@ -230,15 +226,11 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { ), ) - const createEditor = (monaco: Monaco) => { - if (!containerRef) { - throw new Error('Container ref not available') - } - + const createEditor = (monaco: Monaco) => { const model = getOrCreateModel(monaco, props.value ?? '', props.language, props.path) return monaco.editor.create( - containerRef, + containerRef!, { model: model, automaticLayout: true, @@ -251,7 +243,7 @@ export const MonacoEditor = (inputProps: MonacoEditorProps) => { return ( {!editor() && {props.loadingState}} -
+
) } From f374c57c36fb84f3621cb08ebc23305908fd4de5 Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Fri, 1 Aug 2025 23:10:06 +0000 Subject: [PATCH 08/10] Update github workflows --- .github/workflows/codeql.yml | 35 ++++++++++----- .github/workflows/format.yml | 13 +++--- .github/workflows/release.yml | 55 ++++++++++++++++++++++++ .github/workflows/tests.yml | 81 ++++++++++++++++++++++++++++++----- 4 files changed, 159 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3f37208..53f6635 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,35 +12,50 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest + timeout-minutes: 360 permissions: security-events: write + packages: read + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: javascript-typescript + build-mode: none steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: - languages: javascript + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 with: + category: "/language:${{ matrix.language }}" upload: false output: sarif-results # Only include files that are public - name: filter-sarif - uses: advanced-security/filter-sarif@main + uses: advanced-security/filter-sarif@v1 with: patterns: | - /src/**/*.* + +src/**/*.ts + +src/**/*.tsx -**/*.test.* - input: sarif-results/javascript.sarif - output: sarif-results/javascript.sarif + -**/*.spec.* + input: sarif-results/${{ matrix.language }}.sarif + output: sarif-results/${{ matrix.language }}.sarif - name: Upload SARIF - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v3 with: - sarif_file: sarif-results/javascript.sarif + sarif_file: sarif-results/${{ matrix.language }}.sarif diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 4280668..37de4be 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -14,13 +14,16 @@ jobs: contents: write steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2.2.4 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 8 - name: Setup Node.js environment - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 + cache: pnpm # "git restore ." discards changes to package-lock.json - name: Install dependencies @@ -32,6 +35,6 @@ jobs: run: pnpm run format - name: Add, Commit and Push - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: 'Format' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e4cd7e1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,55 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run tests + run: pnpm run test + + - name: Build package + run: pnpm run build + + - name: Publish to NPM + run: pnpm publish --no-git-checks + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f31b0a3..2e741f9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,25 +7,39 @@ on: branches: [main] jobs: - build: + test: + name: Test (Node ${{ matrix.node-version }}) runs-on: ubuntu-latest - + + strategy: + matrix: + node-version: [18, 20, 22] + steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - - uses: pnpm/action-setup@v2.2.4 + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 - - name: Setup Node.js environment - uses: actions/setup-node@v3 + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 with: - node-version: 18 + node-version: ${{ matrix.node-version }} cache: pnpm - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile + + - name: Type check + run: pnpm run lint:types + + - name: Lint + run: pnpm run lint:code - name: Build run: pnpm run build @@ -35,5 +49,52 @@ jobs: env: CI: true - - name: Lint - run: pnpm run lint + build-check: + name: Build Check + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build library + run: pnpm run build + + - name: Check build output + run: | + if [ ! -d "dist" ]; then + echo "❌ Build failed: dist directory not found" + exit 1 + fi + if [ ! -f "dist/index.js" ]; then + echo "❌ Build failed: index.js not found" + exit 1 + fi + if [ ! -f "dist/index.d.ts" ]; then + echo "❌ Build failed: index.d.ts not found" + exit 1 + fi + echo "✅ Build output verified" + + - name: Test package installation + run: | + # Create a test project to verify the package can be installed + mkdir test-install + cd test-install + npm init -y + npm install ../ + echo "✅ Package installation test passed" From c0c49549ba259c549c10443c2491de296fe88f62 Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Sun, 3 Aug 2025 09:15:10 -0700 Subject: [PATCH 09/10] README tweaks --- README.md | 56 ++++++++++++++++++++++++++-------------------- dev/vite.config.ts | 2 +- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 9ee6d48..d39a0b4 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,7 @@ The `MonacoDiffEditor` component accepts the following props: The library supports both CDN and local asset loading strategies. #### CDN Loading (Default) + ```jsx // Uses CDN by default - no configuration needed @@ -243,7 +244,7 @@ Ensure `monaco-editor` is a regular dependency (not devDependency) in your `pack ```json { "dependencies": { - "monaco-editor": "^0.45.0", + "monaco-editor": "^0.48.0", "solid-monaco": "^0.x.x" } } @@ -255,10 +256,14 @@ Create environment-specific configurations: ```bash # .env (development) +# This allows vite to serve monaco assets directly from +# node_modules when in dev mode. VITE_MONACO_ASSETS_PATH=/node_modules/monaco-editor/dev/vs # .env.production -VITE_MONACO_ASSETS_PATH=/assets/monaco-assets/vs +# Configure the path where minified monaco assets will be +# found. See `monacoAssetsPlugin` below. +VITE_MONACO_ASSETS_PATH=/monaco-assets/vs ``` **3. Vite Configuration** @@ -267,31 +272,31 @@ Add a custom plugin to copy Monaco assets during build: ```typescript // vite.config.ts -import { cpSync, existsSync } from 'node:fs'; -import { join } from 'node:path'; -import { defineConfig } from 'vite'; -import solidPlugin from 'vite-plugin-solid'; +import { cpSync, existsSync } from 'node:fs' +import { join } from 'node:path' +import { defineConfig } from 'vite' +import solidPlugin from 'vite-plugin-solid' const monacoAssetsPlugin = () => { return { - name: 'monaco-assets', + name: 'monaco-assets-plugin', generateBundle() { - // Copy Monaco Editor assets to build directory - const monacoSrc = join(process.cwd(), 'node_modules/monaco-editor/min/vs'); - const buildDest = join(process.cwd(), 'build/assets/monaco-assets/vs'); + // Copy Monaco Editor assets to build output directory + const monacoSrc = join(process.cwd(), 'node_modules/monaco-editor/min/vs') + const buildDest = join(process.cwd(), 'dist/monaco-assets/vs') if (existsSync(monacoSrc)) { - cpSync(monacoSrc, buildDest, { recursive: true }); - console.log('✓ Monaco Editor assets copied to build directory'); + cpSync(monacoSrc, buildDest, { recursive: true }) + console.log('✓ Monaco Editor assets copied to dist directory') } }, - }; -}; + } +} export default defineConfig({ plugins: [solidPlugin(), monacoAssetsPlugin()], // ... other config -}); +}) ``` **4. Component Usage** @@ -299,17 +304,17 @@ export default defineConfig({ Use the environment variable to configure asset loading: ```jsx -import { MonacoDiffEditor } from 'solid-monaco'; +import { MonacoDiffEditor } from 'solid-monaco' function MyDiffEditor() { - const configureDiffEditor = (monaco) => { + const configureDiffEditor = monaco => { // Configure JSON language features monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ validate: true, allowComments: false, schemas: [], enableSchemaRequest: false, - }); + }) // Configure JSON formatting monaco.languages.json.jsonDefaults.setModeConfiguration({ @@ -323,13 +328,13 @@ function MyDiffEditor() { foldingRanges: true, diagnostics: true, selectionRanges: true, - }); - }; + }) + } return ( - ); + ) } ``` @@ -380,7 +385,7 @@ export default defineConfig({ }, }, }, -}); +}) ``` ### Troubleshooting @@ -388,16 +393,19 @@ export default defineConfig({ #### Common Issues **Monaco assets not loading in production:** + - Ensure `monaco-editor` is in `dependencies`, not `devDependencies` - Verify the Vite plugin is copying assets to the correct build directory - Check that `VITE_MONACO_ASSETS_PATH` matches your actual asset path **Large bundle size:** + - Use `import type` for Monaco types to avoid bundling the entire library - Configure bundle splitting to separate Monaco into its own chunk - Consider lazy loading Monaco for non-critical editor instances **Web workers failing:** + - Ensure web worker files are accessible at the configured asset path - Check browser console for 404 errors on worker files - Verify CORS settings if serving assets from a different domain diff --git a/dev/vite.config.ts b/dev/vite.config.ts index a198a70..d43f8c3 100644 --- a/dev/vite.config.ts +++ b/dev/vite.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ plugins: [ solidPlugin(), { - name: 'Reaplace env variables', + name: 'Replace env variables', transform(code, id) { if (id.includes('node_modules')) { return code From 1cd55f8d0473934520045f0849335e39127228f1 Mon Sep 17 00:00:00 2001 From: Siddhesh Vichare Date: Sun, 3 Aug 2025 16:19:32 +0000 Subject: [PATCH 10/10] Add semicolons back. --- README.md | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index d39a0b4..3e15896 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,10 @@ Basic usage: You can import and use the `MonacoEditor` component in your Solid application: ```jsx -import { MonacoEditor } from 'solid-monaco' +import { MonacoEditor } from 'solid-monaco'; function MyEditor() { - return + return ; } ``` @@ -71,16 +71,16 @@ The `MonacoEditor` component accepts the following props: You can get instances of both `monaco` and the `editor` by using the `onMount` callback: ```jsx -import { MonacoEditor } from 'solid-monaco' +import { MonacoEditor } from 'solid-monaco'; function MyEditor() { const handleMount = (monaco, editor) => { // Use monaco and editor instances here - } + }; return ( - ) + ); } ``` @@ -89,11 +89,11 @@ function MyEditor() { Jump to a specific line number in the editor: ```jsx -import { MonacoEditor } from 'solid-monaco' -import { createSignal } from 'solid-js' +import { MonacoEditor } from 'solid-monaco'; +import { createSignal } from 'solid-js'; function MyEditor() { - const [currentLine, setCurrentLine] = createSignal(42) + const [currentLine, setCurrentLine] = createSignal(42); return (
@@ -104,7 +104,7 @@ function MyEditor() { line={currentLine()} />
- ) + ); } ``` @@ -113,7 +113,7 @@ function MyEditor() { Use the `beforeMount` callback to configure Monaco before the editor is created: ```jsx -import { MonacoEditor } from 'solid-monaco' +import { MonacoEditor } from 'solid-monaco'; function MyEditor() { const handleBeforeMount = monaco => { @@ -121,7 +121,7 @@ function MyEditor() { monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ noSemanticValidation: true, noSyntaxValidation: false, - }) + }); // Register custom themes, languages, etc. monaco.editor.defineTheme('myCustomTheme', { @@ -131,8 +131,8 @@ function MyEditor() { colors: { 'editor.background': '#1e1e1e', }, - }) - } + }); + }; return ( - ) + ); } ``` @@ -150,16 +150,16 @@ function MyEditor() { Monitor validation errors and warnings in real-time: ```jsx -import { MonacoEditor } from 'solid-monaco' -import { createSignal } from 'solid-js' +import { MonacoEditor } from 'solid-monaco'; +import { createSignal } from 'solid-js'; function MyEditor() { - const [errors, setErrors] = createSignal([]) + const [errors, setErrors] = createSignal([]); const handleValidate = markers => { - setErrors(markers.filter(marker => marker.severity === 8)) // Errors only - console.log('Validation markers:', markers) - } + setErrors(markers.filter(marker => marker.severity === 8)); // Errors only + console.log('Validation markers:', markers); + }; return (
@@ -170,7 +170,7 @@ function MyEditor() { onValidate={handleValidate} />
- ) + ); } ``` @@ -183,7 +183,7 @@ For a side-by-side comparison view of code, the package provides a `MonacoDiffEd You can incorporate the `MonacoDiffEditor` component into your Solid application: ```jsx -import { MonacoDiffEditor } from 'solid-monaco' +import { MonacoDiffEditor } from 'solid-monaco'; function MyDiffEditor() { return ( @@ -193,7 +193,7 @@ function MyDiffEditor() { originalLanguage="javascript" modifiedLanguage="javascript" /> - ) + ); } ``` @@ -272,31 +272,31 @@ Add a custom plugin to copy Monaco assets during build: ```typescript // vite.config.ts -import { cpSync, existsSync } from 'node:fs' -import { join } from 'node:path' -import { defineConfig } from 'vite' -import solidPlugin from 'vite-plugin-solid' +import { cpSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { defineConfig } from 'vite'; +import solidPlugin from 'vite-plugin-solid'; const monacoAssetsPlugin = () => { return { name: 'monaco-assets-plugin', generateBundle() { // Copy Monaco Editor assets to build output directory - const monacoSrc = join(process.cwd(), 'node_modules/monaco-editor/min/vs') - const buildDest = join(process.cwd(), 'dist/monaco-assets/vs') + const monacoSrc = join(process.cwd(), 'node_modules/monaco-editor/min/vs'); + const buildDest = join(process.cwd(), 'dist/monaco-assets/vs'); if (existsSync(monacoSrc)) { - cpSync(monacoSrc, buildDest, { recursive: true }) - console.log('✓ Monaco Editor assets copied to dist directory') + cpSync(monacoSrc, buildDest, { recursive: true }); + console.log('✓ Monaco Editor assets copied to dist directory'); } }, - } -} + }; +}; export default defineConfig({ plugins: [solidPlugin(), monacoAssetsPlugin()], // ... other config -}) +}); ``` **4. Component Usage** @@ -304,7 +304,7 @@ export default defineConfig({ Use the environment variable to configure asset loading: ```jsx -import { MonacoDiffEditor } from 'solid-monaco' +import { MonacoDiffEditor } from 'solid-monaco'; function MyDiffEditor() { const configureDiffEditor = monaco => { @@ -314,7 +314,7 @@ function MyDiffEditor() { allowComments: false, schemas: [], enableSchemaRequest: false, - }) + }); // Configure JSON formatting monaco.languages.json.jsonDefaults.setModeConfiguration({ @@ -328,8 +328,8 @@ function MyDiffEditor() { foldingRanges: true, diagnostics: true, selectionRanges: true, - }) - } + }); + }; return ( - ) + ); } ``` @@ -385,7 +385,7 @@ export default defineConfig({ }, }, }, -}) +}); ``` ### Troubleshooting