From 9931c9a63f2dd254d3e8da106c31e31086c23b2d Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Sat, 17 Jun 2023 00:34:07 +0200 Subject: [PATCH 1/5] commit @graphiql/react commit graphiql remove unused and add editor theme add monaco-editor-webpack-plugin remove --- packages/graphiql-react/package.json | 5 +- packages/graphiql-react/src/constants.ts | 449 ++++++++++++++++++ packages/graphiql-react/src/create-editor.ts | 57 +++ .../graphiql-react/src/editor/completion.ts | 19 +- .../src/editor/components/header-editor.tsx | 1 + .../src/editor/components/headers-editor.tsx | 48 ++ .../src/editor/components/index.ts | 4 + .../editor/components/operations-editor.tsx | 8 + .../src/editor/components/results-editor.tsx | 29 ++ .../src/editor/components/variable-editor.tsx | 5 +- .../editor/components/variables-editor.tsx | 47 ++ .../graphiql-react/src/editor/context.tsx | 40 +- .../src/editor/header-editor.ts | 1 + packages/graphiql-react/src/editor/hooks.ts | 71 +-- packages/graphiql-react/src/editor/index.ts | 4 + .../src/editor/operations-editor.ts | 150 ++++++ .../graphiql-react/src/editor/query-editor.ts | 13 +- .../src/editor/response-editor.tsx | 1 + .../src/editor/style/editor.css | 5 + packages/graphiql-react/src/editor/tabs.ts | 20 +- .../src/editor/variable-editor.ts | 1 + packages/graphiql-react/src/execution.tsx | 4 + .../graphiql-react/src/explorer/context.tsx | 143 +++--- packages/graphiql-react/src/index.ts | 8 +- packages/graphiql-react/src/plugin.tsx | 5 +- .../graphiql-react/src/toolbar/execute.tsx | 3 + packages/graphiql-react/tsconfig.json | 12 +- packages/graphiql-react/tsconfig.node.json | 3 +- packages/graphiql-react/vite.config.ts | 8 + packages/graphiql/package.json | 1 + packages/graphiql/resources/webpack.config.js | 14 + packages/graphiql/src/components/GraphiQL.tsx | 99 ++-- yarn.lock | 5 + 33 files changed, 1074 insertions(+), 209 deletions(-) create mode 100644 packages/graphiql-react/src/constants.ts create mode 100644 packages/graphiql-react/src/create-editor.ts create mode 100644 packages/graphiql-react/src/editor/components/headers-editor.tsx create mode 100644 packages/graphiql-react/src/editor/components/operations-editor.tsx create mode 100644 packages/graphiql-react/src/editor/components/results-editor.tsx create mode 100644 packages/graphiql-react/src/editor/components/variables-editor.tsx create mode 100644 packages/graphiql-react/src/editor/operations-editor.ts diff --git a/packages/graphiql-react/package.json b/packages/graphiql-react/package.json index 1375d87cd31..8d5d70eb926 100644 --- a/packages/graphiql-react/package.json +++ b/packages/graphiql-react/package.json @@ -38,8 +38,7 @@ "types" ], "scripts": { - "prebuild": "rimraf dist types", - "dev": "concurrently 'tsc --emitDeclarationOnly --watch' 'vite build --watch'", + "dev": "vite build --watch", "build": "tsc --emitDeclarationOnly && vite build" }, "peerDependencies": { @@ -49,6 +48,8 @@ }, "dependencies": { "@graphiql/toolkit": "^0.9.1", + "monaco-editor": "^0.39.0", + "monaco-graphql": "^1.2.3", "@headlessui/react": "^1.7.15", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.5", diff --git a/packages/graphiql-react/src/constants.ts b/packages/graphiql-react/src/constants.ts new file mode 100644 index 00000000000..7461d2353e0 --- /dev/null +++ b/packages/graphiql-react/src/constants.ts @@ -0,0 +1,449 @@ +import { initializeMode } from 'monaco-graphql/esm/initializeMode'; +import { editor, Uri } from 'monaco-editor'; +import { DEFAULT_QUERY } from './editor/context'; + +const OPERATIONS_URI = 'operations.graphql'; +const VARIABLES_URI = 'variables.json'; +const HEADERS_URI = 'headers.json'; +const RESULTS_URI = 'results.json'; + +export const MONACO_GRAPHQL_API = initializeMode({ + diagnosticSettings: { + validateVariablesJSON: { + [Uri.file(OPERATIONS_URI).toString()]: [ + Uri.file(VARIABLES_URI).toString(), + ], + }, + jsonDiagnosticSettings: { + validate: true, + schemaValidation: 'error', + // set these again, because we are entirely re-setting them here + allowComments: true, + trailingCommas: 'ignore', + }, + }, +}); + +export function getOrCreateModel({ + uri, + value, +}: { + uri: string; + value: string; +}) { + const language = uri.split('.').pop(); + + return ( + editor.getModel(Uri.file(uri)) ?? + editor.createModel(value, language, Uri.file(uri)) + ); +} + +export const OPERATIONS_MODEL = getOrCreateModel({ + uri: OPERATIONS_URI, + value: DEFAULT_QUERY, +}); + +export const VARIABLES_MODEL = getOrCreateModel({ + uri: VARIABLES_URI, + value: '', +}); + +export const HEADERS_MODEL = getOrCreateModel({ + uri: HEADERS_URI, + value: '', +}); + +export const RESULTS_MODEL = getOrCreateModel({ + uri: RESULTS_URI, + value: '', +}); + +const editorColors = { + dark: { + indentGuides: '#363739', + delimiters: '#55565c', + delimitersActive: '#a4a4a4', + selections: '#363739', + keywords: '#a4a4a4', + operators: '#a4a4a4', + text1: '#fff', + text2: '#bfbfbf', + text3: '#a4a4a4', + fields: '#948ae3', + arguments: '#fc618d', + types: '#5ad4e6', + values: '#7bd88f', + orange_default: '#ee8e57', + yellow_default: '#fce566', + }, + light: { + indentGuides: '#d7d7d7', // surface3 + delimiters: '#bcbcbc', // text4 + delimitersActive: '#757575', // text3 + selections: '#d7d7d7', // surface3 + keywords: '#757575', // text3 + operators: '#757575', // text3 + text1: '#1b1b1b', // text1 + text2: '#4a4a4a', // text2 + text3: '#757575', // text3 + fields: '#5c4cdd', // violet_default + arguments: '#d60690', // pink_default + types: '#0b6af9', // blue_default + values: '#128934', // green_default + orange_default: '#af5f15', // orange_default + yellow_default: '#767800', // yellow_default + }, +}; + +export const editorThemeDark: editor.IStandaloneThemeData = { + base: 'vs-dark', + inherit: true, + colors: { + 'editor.foreground': editorColors.dark.delimiters, // Editor default foreground color. + 'editorCursor.foreground': editorColors.dark.yellow_default, // Color of the editor cursor. + 'editor.selectionBackground': editorColors.dark.selections, // Color of the editor selection. + 'editor.background': '#ffffff00', // white with a 00 alpha value + 'editorLineNumber.foreground': editorColors.dark.delimiters, // Color of editor line numbers. + 'editorLineNumber.activeForeground': editorColors.dark.delimitersActive, // Color of editor active line number. + 'editorError.foreground': editorColors.dark.orange_default, // Foreground color of error squigglies in the editor. + 'editorWarning.foreground': editorColors.dark.orange_default, // Foreground color of warning squigglies in the editor. + 'editor.lineHighlightBorder': '#ffffff00', // Background color for the border around the line at the cursor position. + 'editorBracketMatch.background': '#ffffff00', // Background color behind matching brackets + 'editorBracketMatch.border': editorColors.dark.selections, // Color for matching brackets boxes + 'editorIndentGuide.background': editorColors.dark.indentGuides, // Color of the editor indentation guides. + 'scrollbar.shadow': '#ffffff00', // Scrollbar shadow to indicate that the view is scrolled. + 'editorOverviewRuler.border': '#ffffff00', // Color of the overview ruler border. + // 'editorMarkerNavigationError.background': '#FFFFFF00', // Editor marker navigation widget error color. + 'editorMarkerNavigationWarning.background': '#ffffff00', // Editor marker navigation widget warning color. + }, + rules: [ + // operations editor (graphql) + { + foreground: editorColors.dark.keywords, + token: 'string.quote.gql', + }, + { + foreground: editorColors.dark.orange_default, + token: 'string.invalid.gql', + }, + { + foreground: editorColors.dark.yellow_default, + token: 'string.gql', + }, + { + foreground: editorColors.dark.yellow_default, + token: 'number.gql', + }, + { + foreground: editorColors.dark.yellow_default, + token: 'number.float.gql', + }, + { + foreground: editorColors.dark.keywords, + token: 'keyword.gql', + }, + { + foreground: editorColors.dark.operators, + token: 'operator.gql', + }, + { + foreground: editorColors.dark.types, + token: 'type.identifier.gql', + }, + { + foreground: editorColors.dark.fields, + token: 'key.identifier.gql', + }, + { + foreground: editorColors.dark.arguments, + token: 'argument.identifier.gql', + }, + { + foreground: editorColors.dark.delimiters, + token: 'delimiter.gql', + }, + { + foreground: editorColors.dark.delimiters, + token: 'delimiter.parenthesis.gql', + }, + { + foreground: editorColors.dark.delimiters, + token: 'delimiter.curly.gql', + }, + { + foreground: editorColors.dark.delimiters, + token: 'delimiter.square.gql', + }, + { + foreground: editorColors.dark.text2, + token: 'comment.gql', + }, + // variables editor & results viewer (json) + { + foreground: editorColors.dark.text2, + token: 'delimiter.bracket.json', + }, + { + foreground: editorColors.dark.text2, + token: 'delimiter.array.json', + }, + { + foreground: editorColors.dark.text2, + token: 'delimiter.comma.json', + }, + { + foreground: editorColors.dark.text2, + token: 'delimiter.colon.json', + }, + { + foreground: editorColors.dark.keywords, + token: 'string.key.json', + }, + { + foreground: editorColors.dark.values, + token: 'string.value.json', + }, + { + foreground: editorColors.dark.values, + token: 'number.json', + }, + { + foreground: editorColors.dark.values, + token: 'keyword.json', + }, + ], +}; + +export const editorThemeLight: editor.IStandaloneThemeData = { + base: 'vs', + inherit: true, + colors: { + 'editor.foreground': editorColors.light.delimiters, // Editor default foreground color. + 'editorCursor.foreground': editorColors.light.yellow_default, // Color of the editor cursor. + 'editor.selectionBackground': editorColors.light.selections, // Color of the editor selection. + 'editor.background': '#ffffff00', // white with a 00 alpha value + 'editorLineNumber.foreground': editorColors.light.delimiters, // Color of editor line numbers. + 'editorLineNumber.activeForeground': editorColors.light.delimitersActive, // Color of editor active line number. + 'editorError.foreground': editorColors.light.orange_default, // Foreground color of error squigglies in the editor. + 'editorWarning.foreground': editorColors.light.orange_default, // Foreground color of warning squigglies in the editor. + 'editor.lineHighlightBorder': '#ffffff00', // Background color for the border around the line at the cursor position. + 'editorBracketMatch.background': '#ffffff00', // Background color behind matching brackets + 'editorBracketMatch.border': editorColors.light.selections, // Color for matching brackets boxes + 'editorIndentGuide.background': editorColors.light.indentGuides, // Color of the editor indentation guides. + 'scrollbar.shadow': '#ffffff00', // Scrollbar shadow to indicate that the view is scrolled. + 'editorOverviewRuler.border': '#ffffff00', // Color of the overview ruler border. + // 'editorMarkerNavigationError.background': '#FFFFFF00', // Editor marker navigation widget error color. + 'editorMarkerNavigationWarning.background': '#ffffff00', // Editor marker navigation widget warning color. + }, + rules: [ + // operations editor (graphql) + { + foreground: editorColors.light.keywords, + token: 'string.quote.gql', + }, + { + foreground: editorColors.light.orange_default, + token: 'string.invalid.gql', + }, + { + foreground: editorColors.light.yellow_default, + token: 'string.gql', + }, + { + foreground: editorColors.light.yellow_default, + token: 'number.gql', + }, + { + foreground: editorColors.light.yellow_default, + token: 'number.float.gql', + }, + { + foreground: editorColors.light.operators, + token: 'operator.gql', + }, + { + foreground: editorColors.light.types, + token: 'type.identifier.gql', + }, + { + foreground: editorColors.light.fields, + token: 'key.identifier.gql', + }, + { + foreground: editorColors.light.arguments, + token: 'argument.identifier.gql', + }, + { + foreground: editorColors.light.delimiters, + token: 'delimiter.gql', + }, + { + foreground: editorColors.light.delimiters, + token: 'delimiter.parenthesis.gql', + }, + { + foreground: editorColors.light.delimiters, + token: 'delimiter.curly.gql', + }, + { + foreground: editorColors.light.delimiters, + token: 'delimiter.square.gql', + }, + { + foreground: editorColors.light.text2, + token: 'comment.gql', + }, + // variables editor & results viewer (json) + { + foreground: editorColors.light.text2, + token: 'delimiter.bracket.json', + }, + { + foreground: editorColors.light.text2, + token: 'delimiter.array.json', + }, + { + foreground: editorColors.light.text2, + token: 'delimiter.comma.json', + }, + { + foreground: editorColors.light.text2, + token: 'delimiter.colon.json', + }, + { + foreground: editorColors.light.keywords, + token: 'string.key.json', + }, + { + foreground: editorColors.light.values, + token: 'string.value.json', + }, + { + foreground: editorColors.light.values, + token: 'number.json', + }, + { + foreground: editorColors.light.values, + token: 'keyword.json', + }, + ], +}; + +// A list of color names: +// 'foreground': "#FFFFFF00", // Overall foreground color. This color is only used if not overridden by a component. +// 'errorForeground': "#FFFFFF00", // Overall foreground color for error messages. This color is only used if not overridden by a component. +// 'descriptionForeground': "#FFFFFF00", // Foreground color for description text providing additional information, for example for a label. +// 'focusBorder': "#FFFFFF00", // Overall border color for focused elements. This color is only used if not overridden by a component. +// 'contrastBorder': "#FFFFFF00", // An extra border around elements to separate them from others for greater contrast. +// 'contrastActiveBorder': "#FFFFFF00", // An extra border around active elements to separate them from others for greater contrast. +// 'selection.background': "#FFFFFF00", // The background color of text selections in the workbench (e.g. for input fields or text areas). Note that this does not apply to selections within the editor. +// 'textSeparator.foreground': "#FFFFFF00", // Color for text separators. +// 'textLink.foreground': "#FFFFFF00", // Foreground color for links in text. +// 'textLink.activeForeground': "#FFFFFF00", // Foreground color for active links in text. +// 'textPreformat.foreground': "#FFFFFF00", // Foreground color for preformatted text segments. +// 'textBlockQuote.background': "#FFFFFF00", // Background color for block quotes in text. +// 'textBlockQuote.border': "#FFFFFF00", // Border color for block quotes in text. +// 'textCodeBlock.background': "#FFFFFF00", // Background color for code blocks in text. +// 'widget.shadow': "#FFFFFF00", // Shadow color of widgets such as find/replace inside the editor. +// 'input.background': "#FFFFFF00", // Input box background. +// 'input.foreground': "#FFFFFF00", // Input box foreground. +// 'input.border': "#FFFFFF00", // Input box border. +// 'inputOption.activeBorder': "#FFFFFF00", // Border color of activated options in input fields. +// 'input.placeholderForeground': "#FFFFFF00", // Input box foreground color for placeholder text. +// 'inputValidation.infoBackground': "#FFFFFF00", // Input validation background color for information severity. +// 'inputValidation.infoBorder': "#FFFFFF00", // Input validation border color for information severity. +// 'inputValidation.warningBackground': "#FFFFFF00", // Input validation background color for information warning. +// 'inputValidation.warningBorder': "#FFFFFF00", // Input validation border color for warning severity. +// 'inputValidation.errorBackground': "#FFFFFF00", // Input validation background color for error severity. +// 'inputValidation.errorBorder': "#FFFFFF00", // Input validation border color for error severity. +// 'dropdown.background': "#FFFFFF00", // Dropdown background. +// 'dropdown.foreground': "#FFFFFF00", // Dropdown foreground. +// 'dropdown.border': "#FFFFFF00", // Dropdown border. +// 'list.focusBackground': "#FFFFFF00", // List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not. +// 'list.focusForeground': "#FFFFFF00", // List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not. +// 'list.activeSelectionBackground': "#FFFFFF00", // List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not. +// 'list.activeSelectionForeground': "#FFFFFF00", // List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not. +// 'list.inactiveSelectionBackground': "#FFFFFF00", // List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not. +// 'list.inactiveSelectionForeground': "#FFFFFF00", // List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not. +// 'list.hoverBackground': "#FFFFFF00", // List/Tree background when hovering over items using the mouse. +// 'list.hoverForeground': "#FFFFFF00", // List/Tree foreground when hovering over items using the mouse. +// 'list.dropBackground': "#FFFFFF00", // List/Tree drag and drop background when moving items around using the mouse. +// 'list.highlightForeground': "#FFFFFF00", // List/Tree foreground color of the match highlights when searching inside the list/tree. +// 'pickerGroup.foreground': "#FFFFFF00", // Quick picker color for grouping labels. +// 'pickerGroup.border': "#FFFFFF00", // Quick picker color for grouping borders. +// 'button.foreground': "#FFFFFF00", // Button foreground color. +// 'button.background': "#FFFFFF00", // Button background color. +// 'button.hoverBackground': "#FFFFFF00", // Button background color when hovering. +// 'badge.background': "#FFFFFF00", // Badge background color. Badges are small information labels, e.g. for search results count. +// 'badge.foreground': "#FFFFFF00", // Badge foreground color. Badges are small information labels, e.g. for search results count. +// 'scrollbar.shadow': "#FFFFFF00", // Scrollbar shadow to indicate that the view is scrolled. +// 'scrollbarSlider.background': "#FFFFFF00", // Slider background color. +// 'scrollbarSlider.hoverBackground': "#FFFFFF00", // Slider background color when hovering. +// 'scrollbarSlider.activeBackground': "#FFFFFF00", // Slider background color when active. +// 'progressBar.background': "#FFFFFF00", // Background color of the progress bar that can show for long running operations. +// 'editor.background': "#FFFFFF00", // Editor background color. +// 'editor.foreground': "#FFFFFF00", // Editor default foreground color. +// 'editorWidget.background': "#FFFFFF00", // Background color of editor widgets, such as find/replace. +// 'editorWidget.border': "#FFFFFF00", // Border color of editor widgets. The color is only used if the widget chooses to have a border and if the color is not overridden by a widget. +// 'editor.selectionBackground': "#FFFFFF00", // Color of the editor selection. +// 'editor.selectionForeground': "#FFFFFF00", // Color of the selected text for high contrast. +// 'editor.inactiveSelectionBackground': "#FFFFFF00", // Color of the selection in an inactive editor. +// 'editor.selectionHighlightBackground': "#FFFFFF00", // Color for regions with the same content as the selection. +// 'editor.findMatchBackground': "#FFFFFF00", // Color of the current search match. +// 'editor.findMatchHighlightBackground': "#FFFFFF00", // Color of the other search matches. +// 'editor.findRangeHighlightBackground': "#FFFFFF00", // Color the range limiting the search. +// 'editor.hoverHighlightBackground': "#FFFFFF00", // Highlight below the word for which a hover is shown. +// 'editorHoverWidget.background': "#FFFFFF00", // Background color of the editor hover. +// 'editorHoverWidget.border': "#FFFFFF00", // Border color of the editor hover. +// 'editorLink.activeForeground': "#FFFFFF00", // Color of active links. +// 'diffEditor.insertedTextBackground': "#FFFFFF00", // Background color for text that got inserted. +// 'diffEditor.removedTextBackground': "#FFFFFF00", // Background color for text that got removed. +// 'diffEditor.insertedTextBorder': "#FFFFFF00", // Outline color for the text that got inserted. +// 'diffEditor.removedTextBorder': "#FFFFFF00", // Outline color for text that got removed. +// 'editorOverviewRuler.currentContentForeground': "#FFFFFF00", // Current overview ruler foreground for inline merge-conflicts. +// 'editorOverviewRuler.incomingContentForeground': "#FFFFFF00", // Incoming overview ruler foreground for inline merge-conflicts. +// 'editorOverviewRuler.commonContentForeground': "#FFFFFF00", // Common ancestor overview ruler foreground for inline merge-conflicts. +// 'editor.lineHighlightBackground': "#FFFFFF00", // Background color for the highlight of line at the cursor position. +// 'editor.lineHighlightBorder': "#FFFFFF00", // Background color for the border around the line at the cursor position. +// 'editor.rangeHighlightBackground': "#FFFFFF00", // Background color of highlighted ranges, like by quick open and find features. +// 'editorCursor.foreground': "#FFFFFF00", // Color of the editor cursor. +// 'editorWhitespace.foreground': "#FFFFFF00", // Color of whitespace characters in the editor. +// 'editorIndentGuide.background': "#FFFFFF00", // Color of the editor indentation guides. +// 'editorLineNumber.foreground': "#FFFFFF00", // Color of editor line numbers. +// 'editorLineNumber.activeForeground': "#FFFFFF00", // Color of editor active line number. +// 'editorRuler.foreground': "#FFFFFF00", // Color of the editor rulers. +// 'editorCodeLens.foreground': "#FFFFFF00", // Foreground color of editor code lenses +// 'editorInlayHint.foreground': "#FFFFFF00", // Foreground color of editor inlay hints +// 'editorInlayHint.background': "#FFFFFF00", // Background color of editor inlay hints +// 'editorBracketMatch.background': "#FFFFFF00", // Background color behind matching brackets +// 'editorBracketMatch.border': "#FFFFFF00", // Color for matching brackets boxes +// 'editorOverviewRuler.border': "#FFFFFF00", // Color of the overview ruler border. +// 'editorGutter.background': "#FFFFFF00", // Background color of the editor gutter. The gutter contains the glyph margins and the line numbers. +// 'editorError.foreground': "#FFFFFF00", // Foreground color of error squigglies in the editor. +// 'editorError.border': "#FFFFFF00", // Border color of error squigglies in the editor. +// 'editorWarning.foreground': "#FFFFFF00", // Foreground color of warning squigglies in the editor. +// 'editorWarning.border': "#FFFFFF00", // Border color of warning squigglies in the editor. +// 'editorMarkerNavigationError.background': "#FFFFFF00", // Editor marker navigation widget error color. +// 'editorMarkerNavigationWarning.background': "#FFFFFF00", // Editor marker navigation widget warning color. +// 'editorMarkerNavigation.background': "#FFFFFF00", // Editor marker navigation widget background. +// 'editorSuggestWidget.background': "#FFFFFF00", // Background color of the suggest widget. +// 'editorSuggestWidget.border': "#FFFFFF00", // Border color of the suggest widget. +// 'editorSuggestWidget.foreground': "#FFFFFF00", // Foreground color of the suggest widget. +// 'editorSuggestWidget.selectedBackground': "#FFFFFF00", // Background color of the selected entry in the suggest widget. +// 'editorSuggestWidget.highlightForeground': "#FFFFFF00", // Color of the match highlights in the suggest widget. +// 'editor.wordHighlightBackground': "#FFFFFF00", // Background color of a symbol during read-access, like reading a variable. +// 'editor.wordHighlightStrongBackground': "#FFFFFF00", // Background color of a symbol during write-access, like writing to a variable. +// 'peekViewTitle.background': "#FFFFFF00", // Background color of the peek view title area. +// 'peekViewTitleLabel.foreground': "#FFFFFF00", // Color of the peek view title. +// 'peekViewTitleDescription.foreground': "#FFFFFF00", // Color of the peek view title info. +// 'peekView.border': "#FFFFFF00", // Color of the peek view borders and arrow. +// 'peekViewResult.background': "#FFFFFF00", // Background color of the peek view result list. +// 'peekViewResult.lineForeground': "#FFFFFF00", // Foreground color for line nodes in the peek view result list. +// 'peekViewResult.fileForeground': "#FFFFFF00", // Foreground color for file nodes in the peek view result list. +// 'peekViewResult.selectionBackground': "#FFFFFF00", // Background color of the selected entry in the peek view result list. +// 'peekViewResult.selectionForeground': "#FFFFFF00", // Foreground color of the selected entry in the peek view result list. +// 'peekViewEditor.background': "#FFFFFF00", // Background color of the peek view editor. +// 'peekViewEditorGutter.background': "#FFFFFF00", // Background color of the gutter in the peek view editor. +// 'peekViewResult.matchHighlightBackground': "#FFFFFF00", // Match highlight color in the peek view result list. +// 'peekViewEditor.matchHighlightBackground': "#FFFFFF00", // Match highlight color in the peek view editor. diff --git a/packages/graphiql-react/src/create-editor.ts b/packages/graphiql-react/src/create-editor.ts new file mode 100644 index 00000000000..c951e296e57 --- /dev/null +++ b/packages/graphiql-react/src/create-editor.ts @@ -0,0 +1,57 @@ +import { editor as MONACO_EDITOR } from 'monaco-editor'; +import { + OPERATIONS_MODEL, + VARIABLES_MODEL, + HEADERS_MODEL, + RESULTS_MODEL, + editorThemeDark, + editorThemeLight, +} from './constants'; + +// this should be called somewhere else, but fine here for now +MONACO_EDITOR.defineTheme('graphiql-DARK', editorThemeDark); +MONACO_EDITOR.defineTheme('graphiql-LIGHT', editorThemeLight); + +export function createEditor( + type: 'operations' | 'variables' | 'headers' | 'results', + domElement: HTMLDivElement, +) { + return MONACO_EDITOR.create(domElement, { + language: type === 'operations' ? 'graphql' : 'json', + // the default theme + theme: 'graphiql-DARK', + automaticLayout: true, + // folding: false, // disable folding + fontFamily: "'Fira Code', monospace", // TODO: set the font (this is problematic because the font has to be installed locally) + fontSize: 13, // default is 12 + // lineDecorationsWidth: 100, + lineNumbersMinChars: 2, + minimap: { + enabled: false, // disable the minimap + }, + overviewRulerLanes: 0, // remove unnecessary cruft on right side of editors + scrollbar: { + // hide the scrollbars + horizontal: 'hidden', + // vertical: 'hidden', + verticalScrollbarSize: 4, + }, + // scrollPredominantAxis: false, + scrollBeyondLastLine: false, // cleans up unnecessary "padding" on the bottom of each editor + tabSize: 2, + wordWrap: 'on', + // wrappingIndent: 'none', + wrappingStrategy: 'advanced', + fixedOverflowWidgets: true, + ...(type === 'results' && { + readOnly: true, + lineNumbers: 'off', + }), + model: { + operations: OPERATIONS_MODEL, + variables: VARIABLES_MODEL, + headers: HEADERS_MODEL, + results: RESULTS_MODEL, + }[type], + }); +} diff --git a/packages/graphiql-react/src/editor/completion.ts b/packages/graphiql-react/src/editor/completion.ts index 231a53851ec..23015949953 100644 --- a/packages/graphiql-react/src/editor/completion.ts +++ b/packages/graphiql-react/src/editor/completion.ts @@ -161,8 +161,23 @@ export function onHasCompletion( hintsUl.addEventListener( 'DOMNodeRemoved', (onRemoveFn = (event: Event) => { - if (event.target !== hintsUl) { - return; + if (event.target === hintsUl) { + hintsUl.removeEventListener('scroll', handleScroll); + hintsUl.removeEventListener('DOMNodeRemoved', onRemoveFn); + information?.removeEventListener( + 'click', + onClickHintInformation, + ); + information = null; + fieldName = null; + typeNamePill = null; + typeNamePrefix = null; + typeName = null; + typeNameSuffix = null; + description = null; + deprecation = null; + deprecationReason = null; + onRemoveFn = null; } hintsUl.removeEventListener('scroll', handleScroll); hintsUl.removeEventListener('DOMNodeRemoved', onRemoveFn); diff --git a/packages/graphiql-react/src/editor/components/header-editor.tsx b/packages/graphiql-react/src/editor/components/header-editor.tsx index 672a2b759a2..70e826a359e 100644 --- a/packages/graphiql-react/src/editor/components/header-editor.tsx +++ b/packages/graphiql-react/src/editor/components/header-editor.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck -- codemirror editor complain about type errors import { useEffect } from 'react'; import { clsx } from 'clsx'; diff --git a/packages/graphiql-react/src/editor/components/headers-editor.tsx b/packages/graphiql-react/src/editor/components/headers-editor.tsx new file mode 100644 index 00000000000..9084259ce0f --- /dev/null +++ b/packages/graphiql-react/src/editor/components/headers-editor.tsx @@ -0,0 +1,48 @@ +import { useEffect, useRef } from 'react'; +import { clsx } from 'clsx'; +import { useEditorContext } from '../context'; +import { UseHeaderEditorArgs } from '../header-editor'; +import '../style/editor.css'; +import { createEditor } from '@/create-editor'; +import { HEADERS_MODEL } from '@/constants'; +import debounce from '@/utility/debounce'; +import { useStorageContext } from '@/storage'; + +type HeaderEditorProps = UseHeaderEditorArgs & { + /** + * Visually hide the header editor. + * @default false + */ + isHidden?: boolean; +}; + +export function HeadersEditor({ isHidden, ...hookArgs }: HeaderEditorProps) { + const { setHeaderEditor, updateActiveTabValues, shouldPersistHeaders } = + useEditorContext({ + nonNull: true, + caller: HeadersEditor, + }); + const ref = useRef(null); + const storage = useStorageContext(); + + useEffect(() => { + setHeaderEditor(createEditor('headers', ref.current!)); + if (!shouldPersistHeaders) { + return; + } + HEADERS_MODEL.onDidChangeContent( + debounce(500, () => { + const value = HEADERS_MODEL.getValue(); + storage?.set(STORAGE_KEY, value); + updateActiveTabValues({ headers: value }); + }), + ); + // eslint-disable-next-line react-hooks/exhaustive-deps -- only on mount + }, []); + + return ( +
+ ); +} + +export const STORAGE_KEY = 'headers'; diff --git a/packages/graphiql-react/src/editor/components/index.ts b/packages/graphiql-react/src/editor/components/index.ts index 9fbe6db2a47..9fa93354609 100644 --- a/packages/graphiql-react/src/editor/components/index.ts +++ b/packages/graphiql-react/src/editor/components/index.ts @@ -3,3 +3,7 @@ export { ImagePreview } from './image-preview'; export { QueryEditor } from './query-editor'; export { ResponseEditor } from './response-editor'; export { VariableEditor } from './variable-editor'; +export { OperationsEditor } from './operations-editor'; +export { VariablesEditor } from './variables-editor'; +export { HeadersEditor } from './headers-editor'; +export { ResultsEditor } from './results-editor'; diff --git a/packages/graphiql-react/src/editor/components/operations-editor.tsx b/packages/graphiql-react/src/editor/components/operations-editor.tsx new file mode 100644 index 00000000000..a15d0aa7703 --- /dev/null +++ b/packages/graphiql-react/src/editor/components/operations-editor.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { useQueryEditor, UseQueryEditorArgs } from '../operations-editor'; + +export function OperationsEditor(props: UseQueryEditorArgs) { + const ref = useQueryEditor(props, OperationsEditor); + + return
; +} diff --git a/packages/graphiql-react/src/editor/components/results-editor.tsx b/packages/graphiql-react/src/editor/components/results-editor.tsx new file mode 100644 index 00000000000..0e5567af839 --- /dev/null +++ b/packages/graphiql-react/src/editor/components/results-editor.tsx @@ -0,0 +1,29 @@ +import { UseResponseEditorArgs } from '../response-editor'; +import { useEffect, useRef } from 'react'; +import { createEditor } from '@/create-editor'; +import { useEditorContext } from '@/editor'; +import '../style/editor.css'; + +export function ResultsEditor(props: UseResponseEditorArgs) { + const ref = useRef(null); + + const { setResponseEditor } = useEditorContext({ + nonNull: true, + caller: ResultsEditor, + }); + + useEffect(() => { + setResponseEditor(createEditor('results', ref.current!)); + // eslint-disable-next-line react-hooks/exhaustive-deps -- only on mount + }, []); + + return ( +
+ ); +} diff --git a/packages/graphiql-react/src/editor/components/variable-editor.tsx b/packages/graphiql-react/src/editor/components/variable-editor.tsx index 3d354157d7e..8621eb2490b 100644 --- a/packages/graphiql-react/src/editor/components/variable-editor.tsx +++ b/packages/graphiql-react/src/editor/components/variable-editor.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck -- codemirror editor complain about type errors import { useEffect } from 'react'; import { clsx } from 'clsx'; @@ -26,8 +27,8 @@ export function VariableEditor({ isHidden, ...hookArgs }: VariableEditorProps) { const ref = useVariableEditor(hookArgs, VariableEditor); useEffect(() => { - if (variableEditor && !isHidden) { - variableEditor.refresh(); + if (!isHidden) { + variableEditor?.refresh(); } }, [variableEditor, isHidden]); diff --git a/packages/graphiql-react/src/editor/components/variables-editor.tsx b/packages/graphiql-react/src/editor/components/variables-editor.tsx new file mode 100644 index 00000000000..e69149eab06 --- /dev/null +++ b/packages/graphiql-react/src/editor/components/variables-editor.tsx @@ -0,0 +1,47 @@ +import { useEffect, useRef } from 'react'; +import { clsx } from 'clsx'; +import { UseVariableEditorArgs } from '../variable-editor'; +import { createEditor } from '@/create-editor'; +import { VARIABLES_MODEL } from '@/constants'; +import { useStorageContext } from '@/storage'; +import { useEditorContext } from '@/editor'; +import debounce from '@/utility/debounce'; + +type VariableEditorProps = UseVariableEditorArgs & { + /** + * Visually hide the header editor. + * @default false + */ + isHidden?: boolean; +}; + +export function VariablesEditor({ + isHidden, + ...hookArgs +}: VariableEditorProps) { + const ref = useRef(null); + const storage = useStorageContext(); + + const { updateActiveTabValues, setVariableEditor } = useEditorContext({ + nonNull: true, + caller: VariablesEditor, + }); + + useEffect(() => { + setVariableEditor(createEditor('variables', ref.current!)); + VARIABLES_MODEL.onDidChangeContent( + debounce(500, () => { + const value = VARIABLES_MODEL.getValue(); + storage?.set(STORAGE_KEY, value); + updateActiveTabValues({ variables: value }); + }), + ); + // eslint-disable-next-line react-hooks/exhaustive-deps -- only on mount + }, []); + + return ( +
+ ); +} + +const STORAGE_KEY = 'variables'; diff --git a/packages/graphiql-react/src/editor/context.tsx b/packages/graphiql-react/src/editor/context.tsx index 8515cb8c763..d78f4b7b8cb 100644 --- a/packages/graphiql-react/src/editor/context.tsx +++ b/packages/graphiql-react/src/editor/context.tsx @@ -37,6 +37,7 @@ import { } from './tabs'; import { CodeMirrorEditor } from './types'; import { STORAGE_KEY as STORAGE_KEY_VARIABLES } from './variable-editor'; +import { editor } from 'monaco-editor'; export type CodeMirrorEditorWithOperationFacts = CodeMirrorEditor & { documentAST: DocumentNode | null; @@ -79,39 +80,39 @@ export type EditorContextType = TabsState & { ): void; /** - * The CodeMirror editor instance for the headers editor. + * The CodeMirror editor instance for the headers' editor. */ - headerEditor: CodeMirrorEditor | null; + headerEditor: editor.IStandaloneCodeEditor | null; /** * The CodeMirror editor instance for the query editor. This editor also * stores the operation facts that are derived from the current editor * contents. */ - queryEditor: CodeMirrorEditorWithOperationFacts | null; + queryEditor: editor.IStandaloneCodeEditor | null; /** * The CodeMirror editor instance for the response editor. */ - responseEditor: CodeMirrorEditor | null; + responseEditor: editor.IStandaloneCodeEditor | null; /** - * The CodeMirror editor instance for the variables editor. + * The CodeMirror editor instance for the variables' editor. */ - variableEditor: CodeMirrorEditor | null; + variableEditor: editor.IStandaloneCodeEditor | null; /** - * Set the CodeMirror editor instance for the headers editor. + * Set the CodeMirror editor instance for the headers' editor. */ - setHeaderEditor(newEditor: CodeMirrorEditor): void; + setHeaderEditor(newEditor: editor.IStandaloneCodeEditor): void; /** * Set the CodeMirror editor instance for the query editor. */ - setQueryEditor(newEditor: CodeMirrorEditorWithOperationFacts): void; + setQueryEditor(newEditor: editor.IStandaloneCodeEditor): void; /** * Set the CodeMirror editor instance for the response editor. */ - setResponseEditor(newEditor: CodeMirrorEditor): void; + setResponseEditor(newEditor: editor.IStandaloneCodeEditor): void; /** - * Set the CodeMirror editor instance for the variables editor. + * Set the CodeMirror editor instance for the variables' editor. */ - setVariableEditor(newEditor: CodeMirrorEditor): void; + setVariableEditor(newEditor: editor.IStandaloneCodeEditor): void; /** * Changes the operation name and invokes the `onEditOperationName` callback. @@ -207,7 +208,7 @@ export type EditorContextProviderProps = { /** * Invoked when the operation name changes. Possible triggers are: * - Editing the contents of the query editor - * - Selecting a operation for execution in a document that contains multiple + * - Selecting an operation for execution in a document that contains multiple * operation definitions * @param operationName The operation name after it has been changed. */ @@ -248,7 +249,7 @@ export type EditorContextProviderProps = { */ validationRules?: ValidationRule[]; /** - * This prop can be used to set the contents of the variables editor. Every + * This prop can be used to set the contents of the variables' editor. Every * time this prop changes, the contents of the variables editor are replaced. * Note that the editor contents can be changed in between these updates by * typing in the editor. @@ -263,15 +264,15 @@ export type EditorContextProviderProps = { export function EditorContextProvider(props: EditorContextProviderProps) { const storage = useStorageContext(); - const [headerEditor, setHeaderEditor] = useState( + const [headerEditor, setHeaderEditor] = useState( null, ); const [queryEditor, setQueryEditor] = - useState(null); - const [responseEditor, setResponseEditor] = useState( + useState(null); + const [responseEditor, setResponseEditor] = useState( null, ); - const [variableEditor, setVariableEditor] = useState( + const [variableEditor, setVariableEditor] = useState( null, ); @@ -459,6 +460,7 @@ export function EditorContextProvider(props: EditorContextProviderProps) { return; } + // @ts-expect-error FIXME: MONACO queryEditor.operationName = operationName; updateActiveTabValues({ operationName }); onEditOperationName?.(operationName); @@ -556,7 +558,7 @@ export const useEditorContext = createContextHook(EditorContext); const PERSIST_HEADERS_STORAGE_KEY = 'shouldPersistHeaders'; -const DEFAULT_QUERY = `# Welcome to GraphiQL +export const DEFAULT_QUERY = `# Welcome to GraphiQL # # GraphiQL is an in-browser tool for writing, validating, and # testing GraphQL queries. diff --git a/packages/graphiql-react/src/editor/header-editor.ts b/packages/graphiql-react/src/editor/header-editor.ts index db700f56fea..bf03301e99f 100644 --- a/packages/graphiql-react/src/editor/header-editor.ts +++ b/packages/graphiql-react/src/editor/header-editor.ts @@ -1,3 +1,4 @@ +// @ts-nocheck -- codemirror editor complain about type errors import { useEffect, useRef } from 'react'; import { useExecutionContext } from '../execution'; diff --git a/packages/graphiql-react/src/editor/hooks.ts b/packages/graphiql-react/src/editor/hooks.ts index a9c37f5da68..d13fdac4543 100644 --- a/packages/graphiql-react/src/editor/hooks.ts +++ b/packages/graphiql-react/src/editor/hooks.ts @@ -13,10 +13,11 @@ import debounce from '../utility/debounce'; import { onHasCompletion } from './completion'; import { useEditorContext } from './context'; import { CodeMirrorEditor } from './types'; +import { editor as MONACO_EDITOR } from 'monaco-editor'; export function useSynchronizeValue( - editor: CodeMirrorEditor | null, - value: string | undefined, + editor: MONACO_EDITOR.IStandaloneCodeEditor | null, + value?: string, ) { useEffect(() => { if (editor && typeof value === 'string' && value !== editor.getValue()) { @@ -31,9 +32,7 @@ export function useSynchronizeOption( value: EditorConfiguration[K], ) { useEffect(() => { - if (editor) { - editor.setOption(option, value); - } + editor?.setOption(option, value); }, [editor, option, value]); } @@ -130,7 +129,7 @@ type EmptyCallback = () => void; export function useKeyMap( editor: CodeMirrorEditor | null, keys: string[], - callback: EmptyCallback | undefined, + callback?: EmptyCallback, ) { useEffect(() => { if (!editor) { @@ -194,6 +193,7 @@ export function useMergeQuery({ caller }: UseMergeQueryArgs = {}) { }); const { schema } = useSchemaContext({ nonNull: true, caller: useMergeQuery }); return useCallback(() => { + // @ts-expect-error FIXME: MONACO const documentAST = queryEditor?.documentAST; const query = queryEditor?.getValue(); if (!documentAST || !query) { @@ -298,35 +298,36 @@ export function useAutoCompleteLeafs({ getDefaultFieldNames, ); if (insertions && insertions.length > 0) { - queryEditor.operation(() => { - const cursor = queryEditor.getCursor(); - const cursorIndex = queryEditor.indexFromPos(cursor); - queryEditor.setValue(result || ''); - let added = 0; - const markers = insertions.map(({ index, string }) => - queryEditor.markText( - queryEditor.posFromIndex(index + added), - queryEditor.posFromIndex(index + (added += string.length)), - { - className: 'auto-inserted-leaf', - clearOnEnter: true, - title: 'Automatically added leaf fields', - }, - ), - ); - setTimeout(() => { - for (const marker of markers) { - marker.clear(); - } - }, 7000); - let newCursorIndex = cursorIndex; - for (const { index, string } of insertions) { - if (index < cursorIndex) { - newCursorIndex += string.length; - } - } - queryEditor.setCursor(queryEditor.posFromIndex(newCursorIndex)); - }); + // FIXME: MONACO + // queryEditor.operation(() => { + // const cursor = queryEditor.getCursor(); + // const cursorIndex = queryEditor.indexFromPos(cursor); + // queryEditor.setValue(result || ''); + // let added = 0; + // const markers = insertions.map(({ index, string }) => + // queryEditor.markText( + // queryEditor.posFromIndex(index + added), + // queryEditor.posFromIndex(index + (added += string.length)), + // { + // className: 'auto-inserted-leaf', + // clearOnEnter: true, + // title: 'Automatically added leaf fields', + // }, + // ), + // ); + // setTimeout(() => { + // for (const marker of markers) { + // marker.clear(); + // } + // }, 7000); + // let newCursorIndex = cursorIndex; + // for (const { index, string } of insertions) { + // if (index < cursorIndex) { + // newCursorIndex += string.length; + // } + // } + // queryEditor.setCursor(queryEditor.posFromIndex(newCursorIndex)); + // }); } return result; diff --git a/packages/graphiql-react/src/editor/index.ts b/packages/graphiql-react/src/editor/index.ts index 8a4f8fe1687..11fd5517f1c 100644 --- a/packages/graphiql-react/src/editor/index.ts +++ b/packages/graphiql-react/src/editor/index.ts @@ -4,6 +4,10 @@ export { QueryEditor, ResponseEditor, VariableEditor, + OperationsEditor, + VariablesEditor, + HeadersEditor, + ResultsEditor } from './components'; export { EditorContext, diff --git a/packages/graphiql-react/src/editor/operations-editor.ts b/packages/graphiql-react/src/editor/operations-editor.ts new file mode 100644 index 00000000000..f571becb12a --- /dev/null +++ b/packages/graphiql-react/src/editor/operations-editor.ts @@ -0,0 +1,150 @@ +import type { SchemaReference } from 'codemirror-graphql/utils/SchemaReference'; +import type { DocumentNode } from 'graphql'; +import { useEffect, useRef } from 'react'; + +import { useExecutionContext } from '../execution'; +import { useExplorerContext } from '../explorer'; +import { DOC_EXPLORER_PLUGIN, usePluginContext } from '../plugin'; +import { useSchemaContext } from '../schema'; +import { useStorageContext } from '../storage'; +import debounce from '../utility/debounce'; +import { DEFAULT_EDITOR_THEME, DEFAULT_KEY_MAP } from './common'; +import { useEditorContext } from './context'; +import { + useCopyQuery, + UseCopyQueryArgs, + useMergeQuery, + usePrettifyEditors, +} from './hooks'; +import { WriteableEditorProps } from './types'; +import { KeyCode, KeyMod } from 'monaco-editor'; +import { MONACO_GRAPHQL_API, OPERATIONS_MODEL } from '@/constants'; +import { createEditor } from '@/create-editor'; + +export type UseQueryEditorArgs = WriteableEditorProps & + Pick & { + /** + * Invoked when a reference to the GraphQL schema (type or field) is clicked + * as part of the editor or one of its tooltips. + * @param reference The reference that has been clicked. + */ + onClickReference?(reference: SchemaReference): void; + /** + * Invoked when the contents of the query editor change. + * @param value The new contents of the editor. + * @param documentAST The editor contents parsed into a GraphQL document. + */ + onEdit?(value: string, documentAST?: DocumentNode): void; + }; + +export function useQueryEditor( + { + editorTheme = DEFAULT_EDITOR_THEME, + keyMap = DEFAULT_KEY_MAP, + onClickReference, + onCopyQuery, + onEdit, + readOnly = false, + }: UseQueryEditorArgs = {}, + caller?: Function, +) { + const { schema } = useSchemaContext({ + nonNull: true, + caller: caller || useQueryEditor, + }); + const { + externalFragments, + initialQuery, + queryEditor, + setOperationName, + setQueryEditor, + validationRules, + variableEditor, + updateActiveTabValues, + } = useEditorContext({ + nonNull: true, + caller: caller || useQueryEditor, + }); + const executionContext = useExecutionContext(); + const storage = useStorageContext(); + const explorer = useExplorerContext(); + const plugin = usePluginContext(); + const copy = useCopyQuery({ caller: caller || useQueryEditor, onCopyQuery }); + const merge = useMergeQuery({ caller: caller || useQueryEditor }); + const prettify = usePrettifyEditors({ caller: caller || useQueryEditor }); + const ref = useRef(null); + const { run } = executionContext!; + + useEffect(() => { + setQueryEditor(createEditor('operations', ref.current!)); + OPERATIONS_MODEL.onDidChangeContent( + debounce(100, () => { + const value = OPERATIONS_MODEL.getValue(); + storage?.set(STORAGE_KEY_QUERY, value); + updateActiveTabValues({ + query: value, + operationName: /* operationFacts?.operationName ?? */ null, + }); + }), + ); + // eslint-disable-next-line react-hooks/exhaustive-deps -- only on mount + }, []); + + useEffect(() => { + // add the runOperationAction to the operation and variables editors + queryEditor?.addAction({ + id: 'graphql-run-operation', + label: 'Run Operation', + contextMenuOrder: 0, + contextMenuGroupId: 'graphql', + // eslint-disable-next-line no-bitwise + keybindings: [KeyMod.CtrlCmd | KeyCode.Enter], + run, + }); + }, [queryEditor, run]); + + useEffect(() => { + if (schema) { + MONACO_GRAPHQL_API.setSchemaConfig([{ uri: 'schema.graphql', schema }]); + } + }, [schema]); + + const onClickReferenceRef = useRef< + NonNullable + >(() => {}); + useEffect(() => { + onClickReferenceRef.current = reference => { + if (!explorer || !plugin) { + return; + } + plugin.setVisiblePlugin(DOC_EXPLORER_PLUGIN); + switch (reference.kind) { + case 'Type': { + explorer.push({ name: reference.type.name, def: reference.type }); + break; + } + case 'Field': { + explorer.push({ name: reference.field.name, def: reference.field }); + break; + } + case 'Argument': { + if (reference.field) { + explorer.push({ name: reference.field.name, def: reference.field }); + } + break; + } + case 'EnumValue': { + if (reference.type) { + explorer.push({ name: reference.type.name, def: reference.type }); + } + break; + } + } + onClickReference?.(reference); + }; + }, [explorer, onClickReference, plugin]); + + return ref; +} + +export const STORAGE_KEY_QUERY = 'query'; diff --git a/packages/graphiql-react/src/editor/query-editor.ts b/packages/graphiql-react/src/editor/query-editor.ts index 33147427dd2..c5fca98d696 100644 --- a/packages/graphiql-react/src/editor/query-editor.ts +++ b/packages/graphiql-react/src/editor/query-editor.ts @@ -1,3 +1,4 @@ +// @ts-nocheck -- codemirror editor complain about type errors import { getSelectedOperationName } from '@graphiql/toolkit'; import type { SchemaReference } from 'codemirror-graphql/utils/SchemaReference'; import type { @@ -449,8 +450,8 @@ function useSynchronizeSchema( editor.options.info.schema = schema; editor.options.jump.schema = schema; - if (didChange && codeMirrorRef.current) { - codeMirrorRef.current.signal(editor, 'change', editor); + if (didChange) { + codeMirrorRef.current?.signal(editor, 'change', editor); } }, [editor, schema, codeMirrorRef]); } @@ -470,8 +471,8 @@ function useSynchronizeValidationRules( editor.state.lint.linterOptions.validationRules = validationRules; editor.options.lint.validationRules = validationRules; - if (didChange && codeMirrorRef.current) { - codeMirrorRef.current.signal(editor, 'change', editor); + if (didChange) { + codeMirrorRef.current?.signal(editor, 'change', editor); } }, [editor, validationRules, codeMirrorRef]); } @@ -498,8 +499,8 @@ function useSynchronizeExternalFragments( editor.options.lint.externalFragments = externalFragmentList; editor.options.hintOptions.externalFragments = externalFragmentList; - if (didChange && codeMirrorRef.current) { - codeMirrorRef.current.signal(editor, 'change', editor); + if (didChange) { + codeMirrorRef.current?.signal(editor, 'change', editor); } }, [editor, externalFragmentList, codeMirrorRef]); } diff --git a/packages/graphiql-react/src/editor/response-editor.tsx b/packages/graphiql-react/src/editor/response-editor.tsx index c7b7cb31b75..f4731827a81 100644 --- a/packages/graphiql-react/src/editor/response-editor.tsx +++ b/packages/graphiql-react/src/editor/response-editor.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck -- codemirror editor complain about type errors import { formatError } from '@graphiql/toolkit'; import type { Position, Token } from 'codemirror'; import { ComponentType, useEffect, useRef } from 'react'; diff --git a/packages/graphiql-react/src/editor/style/editor.css b/packages/graphiql-react/src/editor/style/editor.css index c9d0767d20c..bc4d2ce5c7e 100644 --- a/packages/graphiql-react/src/editor/style/editor.css +++ b/packages/graphiql-react/src/editor/style/editor.css @@ -11,3 +11,8 @@ visibility: hidden; } } + +.monaco-editor { + /* otherwise editor could grow the width and not shrink it */ + position: absolute; +} diff --git a/packages/graphiql-react/src/editor/tabs.ts b/packages/graphiql-react/src/editor/tabs.ts index b9110dd5135..45dbc5a766f 100644 --- a/packages/graphiql-react/src/editor/tabs.ts +++ b/packages/graphiql-react/src/editor/tabs.ts @@ -1,9 +1,8 @@ import { StorageAPI } from '@graphiql/toolkit'; import { useCallback, useMemo } from 'react'; +import { editor } from 'monaco-editor'; import debounce from '../utility/debounce'; -import { CodeMirrorEditorWithOperationFacts } from './context'; -import { CodeMirrorEditor } from './types'; export type TabDefinition = { /** @@ -194,16 +193,17 @@ export function useSynchronizeActiveTabValues({ headerEditor, responseEditor, }: { - queryEditor: CodeMirrorEditorWithOperationFacts | null; - variableEditor: CodeMirrorEditor | null; - headerEditor: CodeMirrorEditor | null; - responseEditor: CodeMirrorEditor | null; + queryEditor: editor.IStandaloneCodeEditor | null; + variableEditor: editor.IStandaloneCodeEditor | null; + headerEditor: editor.IStandaloneCodeEditor | null; + responseEditor: editor.IStandaloneCodeEditor | null; }) { return useCallback<(state: TabsState) => TabsState>( state => { const query = queryEditor?.getValue() ?? null; const variables = variableEditor?.getValue() ?? null; const headers = headerEditor?.getValue() ?? null; + // @ts-expect-error FIXME: MONACO const operationName = queryEditor?.operationName ?? null; const response = responseEditor?.getValue() ?? null; return setPropertiesInActiveTab(state, { @@ -259,10 +259,10 @@ export function useSetEditorValues({ headerEditor, responseEditor, }: { - queryEditor: CodeMirrorEditorWithOperationFacts | null; - variableEditor: CodeMirrorEditor | null; - headerEditor: CodeMirrorEditor | null; - responseEditor: CodeMirrorEditor | null; + queryEditor: editor.IStandaloneCodeEditor | null; + variableEditor: editor.IStandaloneCodeEditor | null; + headerEditor: editor.IStandaloneCodeEditor | null; + responseEditor: editor.IStandaloneCodeEditor | null; }) { return useCallback( ({ diff --git a/packages/graphiql-react/src/editor/variable-editor.ts b/packages/graphiql-react/src/editor/variable-editor.ts index 2213c383e27..2be523c9cbe 100644 --- a/packages/graphiql-react/src/editor/variable-editor.ts +++ b/packages/graphiql-react/src/editor/variable-editor.ts @@ -1,3 +1,4 @@ +// @ts-nocheck -- codemirror editor complain about type errors import type { SchemaReference } from 'codemirror-graphql/utils/SchemaReference'; import { useEffect, useRef } from 'react'; diff --git a/packages/graphiql-react/src/execution.tsx b/packages/graphiql-react/src/execution.tsx index d66b4eea78b..ee9a8bfc9a6 100644 --- a/packages/graphiql-react/src/execution.tsx +++ b/packages/graphiql-react/src/execution.tsx @@ -155,8 +155,10 @@ export function ExecutionContextProvider({ } if (externalFragments) { + // @ts-expect-error FIXME: MONACO const fragmentDependencies = queryEditor.documentAST ? getFragmentDependenciesForAST( + // @ts-expect-error FIXME: MONACO queryEditor.documentAST, externalFragments, ) @@ -173,6 +175,7 @@ export function ExecutionContextProvider({ setResponse(''); setIsFetching(true); + // @ts-expect-error FIXME: MONACO const opName = operationName ?? queryEditor.operationName ?? undefined; history?.addToHistory({ @@ -255,6 +258,7 @@ export function ExecutionContextProvider({ }, { headers: headers ?? undefined, + // @ts-expect-error FIXME: MONACO documentAST: queryEditor.documentAST ?? undefined, }, ); diff --git a/packages/graphiql-react/src/explorer/context.tsx b/packages/graphiql-react/src/explorer/context.tsx index cf545334306..2d2fd7bffe8 100644 --- a/packages/graphiql-react/src/explorer/context.tsx +++ b/packages/graphiql-react/src/explorer/context.tsx @@ -109,85 +109,84 @@ export function ExplorerContextProvider(props: ExplorerContextProviderProps) { // Whenever the schema changes, we must revalidate/replace the nav stack. if (schema == null || validationErrors.length > 0) { reset(); - } else { - // Replace the nav stack with an updated version using the new schema - setNavStack(oldNavStack => { - if (oldNavStack.length === 1) { - return oldNavStack; + return; + } + // Replace the nav stack with an updated version using the new schema + setNavStack(oldNavStack => { + if (oldNavStack.length === 1) { + return oldNavStack; + } + const newNavStack: ExplorerNavStack = [initialNavStackItem]; + let lastEntity: GraphQLNamedType | GraphQLField | null = null; + for (const item of oldNavStack) { + if (item === initialNavStackItem) { + // No need to copy the initial item + continue; } - const newNavStack: ExplorerNavStack = [initialNavStackItem]; - let lastEntity: GraphQLNamedType | GraphQLField | null = - null; - for (const item of oldNavStack) { - if (item === initialNavStackItem) { - // No need to copy the initial item - continue; - } - if (item.def) { - // If item.def isn't a named type, it must be a field, inputField, or argument - if (isNamedType(item.def)) { - // The type needs to be replaced with the new schema type of the same name - const newType = schema.getType(item.def.name); - if (newType) { - newNavStack.push({ - name: item.name, - def: newType, - }); - lastEntity = newType; - } else { - // This type no longer exists; the stack cannot be built beyond here - break; - } - } else if (lastEntity === null) { - // We can't have a sub-entity if we have no entity; stop rebuilding the nav stack - break; - } else if ( - isObjectType(lastEntity) || - isInputObjectType(lastEntity) - ) { - // item.def must be a Field / input field; replace with the new field of the same name - const field = lastEntity.getFields()[item.name]; - if (field) { - newNavStack.push({ - name: item.name, - def: field, - }); - } else { - // This field no longer exists; the stack cannot be built beyond here - break; - } - } else if ( - isScalarType(lastEntity) || - isEnumType(lastEntity) || - isInterfaceType(lastEntity) || - isUnionType(lastEntity) - ) { - // These don't (currently) have non-type sub-entries; something has gone wrong. - // Handle gracefully by discontinuing rebuilding the stack. + if (item.def) { + // If item.def isn't a named type, it must be a field, inputField, or argument + if (isNamedType(item.def)) { + // The type needs to be replaced with the new schema type of the same name + const newType = schema.getType(item.def.name); + if (newType) { + newNavStack.push({ + name: item.name, + def: newType, + }); + lastEntity = newType; + } else { + // This type no longer exists; the stack cannot be built beyond here break; + } + } else if (lastEntity === null) { + // We can't have a sub-entity if we have no entity; stop rebuilding the nav stack + break; + } else if ( + isObjectType(lastEntity) || + isInputObjectType(lastEntity) + ) { + // item.def must be a Field / input field; replace with the new field of the same name + const field = lastEntity.getFields()[item.name]; + if (field) { + newNavStack.push({ + name: item.name, + def: field, + }); } else { - // lastEntity must be a field (because it's not a named type) - const field: GraphQLField = lastEntity; - // Thus item.def must be an argument, so find the same named argument in the new schema - const arg = field.args.find(a => a.name === item.name); - if (arg) { - newNavStack.push({ - name: item.name, - def: field, - }); - } else { - // This argument no longer exists; the stack cannot be built beyond here - break; - } + // This field no longer exists; the stack cannot be built beyond here + break; } + } else if ( + isScalarType(lastEntity) || + isEnumType(lastEntity) || + isInterfaceType(lastEntity) || + isUnionType(lastEntity) + ) { + // These don't (currently) have non-type sub-entries; something has gone wrong. + // Handle gracefully by discontinuing rebuilding the stack. + break; } else { - lastEntity = null; - newNavStack.push(item); + // lastEntity must be a field (because it's not a named type) + const field: GraphQLField = lastEntity; + // Thus item.def must be an argument, so find the same named argument in the new schema + const arg = field.args.find(a => a.name === item.name); + if (arg) { + newNavStack.push({ + name: item.name, + def: field, + }); + } else { + // This argument no longer exists; the stack cannot be built beyond here + break; + } } + } else { + lastEntity = null; + newNavStack.push(item); } - return newNavStack; - }); - } + } + return newNavStack; + }); }, [reset, schema, validationErrors]); const value = useMemo( diff --git a/packages/graphiql-react/src/index.ts b/packages/graphiql-react/src/index.ts index 26cf7756193..b66ecf5d73e 100644 --- a/packages/graphiql-react/src/index.ts +++ b/packages/graphiql-react/src/index.ts @@ -3,14 +3,10 @@ import './style/root.css'; export { EditorContext, EditorContextProvider, - HeaderEditor, ImagePreview, - QueryEditor, - ResponseEditor, useAutoCompleteLeafs, useCopyQuery, useEditorContext, - useHeaderEditor, useMergeQuery, usePrettifyEditors, useQueryEditor, @@ -19,6 +15,10 @@ export { useOperationsEditorState, useVariablesEditorState, VariableEditor, + OperationsEditor, + VariablesEditor, + HeadersEditor, + ResultsEditor, } from './editor'; export { ExecutionContext, diff --git a/packages/graphiql-react/src/plugin.tsx b/packages/graphiql-react/src/plugin.tsx index 27194b13f97..e0c19ff2117 100644 --- a/packages/graphiql-react/src/plugin.tsx +++ b/packages/graphiql-react/src/plugin.tsx @@ -120,10 +120,9 @@ export function PluginContextProvider(props: PluginContextProviderProps) { throw new Error( `All GraphiQL plugins must have a unique title, found two plugins with the title '${plugin.title}'`, ); - } else { - pluginList.push(plugin); - pluginTitles[plugin.title] = true; } + pluginList.push(plugin); + pluginTitles[plugin.title] = true; } return pluginList; diff --git a/packages/graphiql-react/src/toolbar/execute.tsx b/packages/graphiql-react/src/toolbar/execute.tsx index ff7eb1e70f9..74ac1c3c5de 100644 --- a/packages/graphiql-react/src/toolbar/execute.tsx +++ b/packages/graphiql-react/src/toolbar/execute.tsx @@ -16,6 +16,7 @@ export function ExecuteButton() { caller: ExecuteButton, }); + // @ts-expect-error FIXME: MONACO const operations = queryEditor?.operations || []; const hasOptions = operations.length > 1 && typeof operationName !== 'string'; const isRunning = isFetching || isSubscribed; @@ -35,6 +36,7 @@ export function ExecuteButton() { + {/* @ts-expect-error FIXME: MONACO */} {operations.map((operation, i) => { const opName = operation.name ? operation.name.value @@ -47,6 +49,7 @@ export function ExecuteButton() { if ( queryEditor && selectedOperationName && + // @ts-expect-error FIXME: MONACO selectedOperationName !== queryEditor.operationName ) { setOperationName(selectedOperationName); diff --git a/packages/graphiql-react/tsconfig.json b/packages/graphiql-react/tsconfig.json index 95480738c1d..1b35e647332 100644 --- a/packages/graphiql-react/tsconfig.json +++ b/packages/graphiql-react/tsconfig.json @@ -16,8 +16,16 @@ "jsx": "react-jsx", "declaration": true, "declarationDir": "types", - "outDir": "tsc" + "outDir": "tsc", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } }, "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] + "references": [ + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/packages/graphiql-react/tsconfig.node.json b/packages/graphiql-react/tsconfig.node.json index e993792cb12..a336f895aab 100644 --- a/packages/graphiql-react/tsconfig.node.json +++ b/packages/graphiql-react/tsconfig.node.json @@ -2,7 +2,8 @@ "compilerOptions": { "composite": true, "module": "esnext", - "moduleResolution": "node" + "moduleResolution": "node", + "allowSyntheticDefaultImports": true }, "include": ["vite.config.ts"] } diff --git a/packages/graphiql-react/vite.config.ts b/packages/graphiql-react/vite.config.ts index 08df2c79573..ced4aa24020 100644 --- a/packages/graphiql-react/vite.config.ts +++ b/packages/graphiql-react/vite.config.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import svgr from 'vite-plugin-svgr'; @@ -34,6 +35,7 @@ export default defineConfig({ rollupOptions: { external: [ 'react/jsx-runtime', + 'monaco-graphql/esm/initializeMode', // Exclude peer dependencies and dependencies from bundle ...Object.keys(packageJSON.peerDependencies), ...Object.keys(packageJSON.dependencies).filter( @@ -44,5 +46,11 @@ export default defineConfig({ chunkFileNames: '[name].[format].js', }, }, + emptyOutDir: false, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, 'src'), + }, }, }); diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json index 73eea286322..710a296ea6d 100644 --- a/packages/graphiql/package.json +++ b/packages/graphiql/package.json @@ -58,6 +58,7 @@ "react-dom": "^16.8.0 || ^17 || ^18" }, "devDependencies": { + "monaco-editor-webpack-plugin": "^7.0.1", "@cypress/webpack-preprocessor": "^5.5.0", "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "14.0.0", diff --git a/packages/graphiql/resources/webpack.config.js b/packages/graphiql/resources/webpack.config.js index 15b1f7d8619..b73c2e1b411 100644 --- a/packages/graphiql/resources/webpack.config.js +++ b/packages/graphiql/resources/webpack.config.js @@ -7,6 +7,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const graphql = require('graphql'); const rimraf = require('rimraf'); +const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); const relPath = (...args) => path.resolve(__dirname, ...args); const rootPath = (...args) => relPath('../', ...args); @@ -105,6 +106,19 @@ const resultConfig = ({ isDev = false }) => { }); } })(), + new MonacoWebpackPlugin({ + languages: ['json', 'graphql'], + publicPath: '/', + customLanguages: [ + { + label: 'graphql', + worker: { + id: 'graphql', + entry: 'monaco-graphql/esm/graphql.worker.js', + }, + }, + ], + }), ], resolve: { extensions: ['.mjs', '.js', '.json', '.jsx', '.css', '.ts', '.tsx'], diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index 0bb9cd1c5f2..34fee444740 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -26,14 +26,11 @@ import { ExecuteButton, GraphiQLProvider, GraphiQLProviderProps, - HeaderEditor, KeyboardShortcutIcon, MergeIcon, PlusIcon, PrettifyIcon, - QueryEditor, ReloadIcon, - ResponseEditor, SettingsIcon, Spinner, Tab, @@ -55,8 +52,11 @@ import { useStorageContext, useTheme, UseVariableEditorArgs, - VariableEditor, WriteableEditorProps, + OperationsEditor, + VariablesEditor, + HeadersEditor, + ResultsEditor, } from '@graphiql/react'; const majorVersion = parseInt(React.version.slice(0, 2), 10); @@ -569,14 +569,16 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { className="graphiql-query-editor" aria-label="Query Editor" > - +
+ +
-
- -
-
- - {isHeadersEditorEnabled && ( - +
+ - )} -
+ {isHeadersEditorEnabled && ( + + )} +
+
- - -
- -
-
- {executionContext.isFetching ? : null} - - {footer} +
+
+
+
+
+ {executionContext.isFetching ? : null} + + {footer} +
diff --git a/yarn.lock b/yarn.lock index 55a5dcea20d..03b86f10d7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13668,6 +13668,11 @@ monaco-editor@^0.39.0: resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.39.0.tgz#3cf8e3718d6aac347d374516a6837d1c13d967d2" integrity sha512-zhbZ2Nx93tLR8aJmL2zI1mhJpsl87HMebNBM6R8z4pLfs8pj604pIVIVwyF1TivcfNtIPpMXL+nb3DsBmE/x6Q== +monaco-editor@^0.39.0: + version "0.39.0" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.39.0.tgz#3cf8e3718d6aac347d374516a6837d1c13d967d2" + integrity sha512-zhbZ2Nx93tLR8aJmL2zI1mhJpsl87HMebNBM6R8z4pLfs8pj604pIVIVwyF1TivcfNtIPpMXL+nb3DsBmE/x6Q== + mri@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" From 7869934e5b598bca362d1aa6e276d0b297134b3f Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Sun, 30 Jul 2023 17:29:08 +0200 Subject: [PATCH 2/5] cleanup after rebase --- packages/graphiql-react/src/index.ts | 4 - packages/graphiql/src/cdn.ts | 1 + packages/graphiql/src/components/GraphiQL.tsx | 300 +++++++++--------- 3 files changed, 154 insertions(+), 151 deletions(-) diff --git a/packages/graphiql-react/src/index.ts b/packages/graphiql-react/src/index.ts index b66ecf5d73e..3402fd8036d 100644 --- a/packages/graphiql-react/src/index.ts +++ b/packages/graphiql-react/src/index.ts @@ -9,12 +9,8 @@ export { useEditorContext, useMergeQuery, usePrettifyEditors, - useQueryEditor, - useResponseEditor, - useVariableEditor, useOperationsEditorState, useVariablesEditorState, - VariableEditor, OperationsEditor, VariablesEditor, HeadersEditor, diff --git a/packages/graphiql/src/cdn.ts b/packages/graphiql/src/cdn.ts index d7a5914f9dc..aabe1e7d931 100644 --- a/packages/graphiql/src/cdn.ts +++ b/packages/graphiql/src/cdn.ts @@ -12,6 +12,7 @@ import { GraphiQL } from './components/GraphiQL'; import '@graphiql/react/font/roboto.css'; import '@graphiql/react/font/fira-code.css'; import '@graphiql/react/dist/style.css'; + import './style.css'; /** diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index 34fee444740..cc06185814c 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -310,7 +310,7 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { - {props.toolbar?.additionalContent} + {props.toolbar?.additionalContent || null} ); @@ -384,17 +384,21 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { [pluginContext, pluginResize], ); - const handleToolsTabClick: MouseEventHandler = useCallback( - event => { + const handleVariablesTabClick: MouseEventHandler = + useCallback(() => { if (editorToolsResize.hiddenElement === 'second') { editorToolsResize.setHiddenElement(null); } - setActiveSecondaryEditor( - event.currentTarget.dataset.name as 'variables' | 'headers', - ); - }, - [editorToolsResize], - ); + setActiveSecondaryEditor('variables'); + }, [editorToolsResize]); + + const handleHeadersTabClick: MouseEventHandler = + useCallback(() => { + if (editorToolsResize.hiddenElement === 'second') { + editorToolsResize.setHiddenElement(null); + } + setActiveSecondaryEditor('headers'); + }, [editorToolsResize]); const toggleEditorTools: MouseEventHandler = useCallback(() => { @@ -502,74 +506,75 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { {PluginContent ? : null}
- {pluginContext?.visiblePlugin && ( -
- )} -
-
- - {editorContext.tabs.length > 1 && ( - <> - {editorContext.tabs.map((tab, index) => ( - - { - executionContext.stop(); - editorContext.changeTab(index); - }} +
+ {pluginContext?.visiblePlugin ? ( +
+ ) : null} +
+
+
+
+ + {editorContext.tabs.length > 1 ? ( + <> + {editorContext.tabs.map((tab, index) => ( + - {tab.title} - - { - if (editorContext.activeTabIndex === index) { + { executionContext.stop(); - } - editorContext.closeTab(index); - }} - /> - - ))} - {addTab} - - )} - -
- {editorContext.tabs.length === 1 && addTab} - {logo} + editorContext.changeTab(index); + }} + > + {tab.title} + + { + if (editorContext.activeTabIndex === index) { + executionContext.stop(); + } + editorContext.closeTab(index); + }} + /> + + ))} + {addTab} + + ) : null} + +
+ {editorContext.tabs.length === 1 ? ( +
{addTab}
+ ) : null} + {logo} +
-
-
-
-
-
-
-
+
+
+
+
+
-
-
- - {toolbar} -
-
-
- -
-
- - Variables - - {isHeadersEditorEnabled && ( +
+ + {toolbar} +
+ +
+
+
- Headers + Variables - )} - + {isHeadersEditorEnabled ? ( + + Headers + + ) : null} +
{editorToolsResize.hiddenElement === 'second' ? ( {isHeadersEditorEnabled && ( @@ -764,31 +765,33 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { Adjust how the interface looks like.
- - - - - +
+ + + + + +
{storageContext ? (
@@ -800,17 +803,20 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { Remove all locally stored data and start fresh.
- +
+ +
) : null} From dd8b9a1ca9115fd0bbb27997d07a244bf554dddf Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Sun, 30 Jul 2023 18:19:49 +0200 Subject: [PATCH 3/5] get partially working with netlify/webpack consuming module build --- .browserslistrc | 6 +- .gitignore | 2 + examples/graphiql-webpack/src/index.html.ejs | 25 ++++++ examples/graphiql-webpack/src/index.jsx | 2 +- examples/graphiql-webpack/webpack.config.js | 84 ++++++++++++++++++- packages/graphiql-react/src/constants.ts | 4 +- packages/graphiql-react/src/create-editor.ts | 2 +- .../graphiql-react/src/editor/context.tsx | 17 ++-- packages/graphiql-react/src/editor/hooks.ts | 2 +- .../src/editor/operations-editor.ts | 2 +- packages/graphiql-react/src/editor/tabs.ts | 2 +- packages/monaco-graphql/README.md | 4 +- packages/monaco-graphql/package.json | 2 +- resources/babel.config.js | 2 +- resources/webpack.config.js | 5 +- yarn.lock | 5 -- 16 files changed, 130 insertions(+), 36 deletions(-) create mode 100644 examples/graphiql-webpack/src/index.html.ejs diff --git a/.browserslistrc b/.browserslistrc index 4c86570bcd4..969bb8e5ed0 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,5 +1 @@ -last 2 versions -Firefox ESR -not dead -not IE 11 -not ios 10 +defaults and supports es6-module diff --git a/.gitignore b/.gitignore index b6865f3e8a3..cd1ede3b3e6 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ packages/codemirror-graphql/*.d.ts packages/codemirror-graphql/*.map !packages/codemirror-graphql/*.config.js +# cdn bundle build assets packages/graphiql/index.html packages/graphiql/dev.html packages/graphiql/analyzer.html @@ -50,3 +51,4 @@ packages/graphiql/*.map packages/graphiql/cypress/screenshots/ packages/graphiql/typedoc/ packages/graphiql/webpack/ +packages/graphiql/*.worker.js diff --git a/examples/graphiql-webpack/src/index.html.ejs b/examples/graphiql-webpack/src/index.html.ejs new file mode 100644 index 00000000000..39371bee61a --- /dev/null +++ b/examples/graphiql-webpack/src/index.html.ejs @@ -0,0 +1,25 @@ + + + + + + GraphiQL Webpack Example! + + + + +
+ + diff --git a/examples/graphiql-webpack/src/index.jsx b/examples/graphiql-webpack/src/index.jsx index 6701dc35c17..07c53077c65 100644 --- a/examples/graphiql-webpack/src/index.jsx +++ b/examples/graphiql-webpack/src/index.jsx @@ -1,4 +1,4 @@ -import 'regenerator-runtime/runtime.js'; +import 'regenerator-runtime/runtime'; import * as React from 'react'; import { createRoot } from 'react-dom/client'; import { GraphiQL } from 'graphiql'; diff --git a/examples/graphiql-webpack/webpack.config.js b/examples/graphiql-webpack/webpack.config.js index a4bee98f5ac..29c08aa505e 100644 --- a/examples/graphiql-webpack/webpack.config.js +++ b/examples/graphiql-webpack/webpack.config.js @@ -1 +1,83 @@ -module.exports = require('../../resources/webpack.config'); +const path = require('node:path'); +const webpack = require('webpack'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); + +const relPath = (...args) => path.resolve(__dirname, ...args); +const rootPath = (...args) => relPath(...args); + +const resultConfig = { + mode: process.env.NODE_ENV, + entry: './index.jsx', + context: rootPath('src'), + output: { + path: rootPath('dist'), + filename: '[name].js', + }, + module: { + rules: [ + // you can also use ts-loader of course + // i prefer to use babel-loader & @babel/plugin-typescript + // so we can experiment with how changing browserslistrc targets impacts + // monaco-graphql bundling + { + test: /\.(js|jsx|ts|tsx)$/, + use: [{ loader: 'babel-loader' }], + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.svg$/, + use: [{ loader: 'svg-inline-loader' }], + }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/, + type: 'asset/resource', + }, + ], + }, + resolve: { + extensions: ['.mjs', '.js', '.json', '.jsx', '.css', '.ts', '.tsx'], + }, + plugins: [ + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + // in order to prevent async modules for CDN builds + // until we can guarantee it will work with the CDN properly + // and so that graphiql.min.js can retain parity + new HtmlWebpackPlugin({ + template: relPath('src/index.html.ejs'), + filename: 'index.html', + }), + + new MonacoWebpackPlugin({ + languages: ['json', 'graphql'], + publicPath: '/', + customLanguages: [ + { + label: 'graphql', + worker: { + id: 'graphql', + entry: 'monaco-graphql/esm/graphql.worker.js', + }, + }, + ], + }), + ], +}; + +if (process.env.ANALYZE) { + resultConfig.plugins.push( + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + openAnalyzer: false, + reportFilename: rootPath('build/analyzer.html'), + }), + ); +} + +module.exports = resultConfig; diff --git a/packages/graphiql-react/src/constants.ts b/packages/graphiql-react/src/constants.ts index 7461d2353e0..ab89134c671 100644 --- a/packages/graphiql-react/src/constants.ts +++ b/packages/graphiql-react/src/constants.ts @@ -1,5 +1,5 @@ -import { initializeMode } from 'monaco-graphql/esm/initializeMode'; -import { editor, Uri } from 'monaco-editor'; +import { initializeMode } from 'monaco-graphql/esm/initializeMode.js'; +import { editor, Uri } from 'monaco-graphql/esm/monaco-editor.js'; import { DEFAULT_QUERY } from './editor/context'; const OPERATIONS_URI = 'operations.graphql'; diff --git a/packages/graphiql-react/src/create-editor.ts b/packages/graphiql-react/src/create-editor.ts index c951e296e57..d63d6c7b4a3 100644 --- a/packages/graphiql-react/src/create-editor.ts +++ b/packages/graphiql-react/src/create-editor.ts @@ -1,4 +1,4 @@ -import { editor as MONACO_EDITOR } from 'monaco-editor'; +import { editor as MONACO_EDITOR } from 'monaco-graphql/esm/monaco-editor.js'; import { OPERATIONS_MODEL, VARIABLES_MODEL, diff --git a/packages/graphiql-react/src/editor/context.tsx b/packages/graphiql-react/src/editor/context.tsx index d78f4b7b8cb..c684b706483 100644 --- a/packages/graphiql-react/src/editor/context.tsx +++ b/packages/graphiql-react/src/editor/context.tsx @@ -37,7 +37,7 @@ import { } from './tabs'; import { CodeMirrorEditor } from './types'; import { STORAGE_KEY as STORAGE_KEY_VARIABLES } from './variable-editor'; -import { editor } from 'monaco-editor'; +import { editor } from 'monaco-graphql/esm/monaco-editor.js'; export type CodeMirrorEditorWithOperationFacts = CodeMirrorEditor & { documentAST: DocumentNode | null; @@ -264,17 +264,14 @@ export type EditorContextProviderProps = { export function EditorContextProvider(props: EditorContextProviderProps) { const storage = useStorageContext(); - const [headerEditor, setHeaderEditor] = useState( - null, - ); + const [headerEditor, setHeaderEditor] = + useState(null); const [queryEditor, setQueryEditor] = useState(null); - const [responseEditor, setResponseEditor] = useState( - null, - ); - const [variableEditor, setVariableEditor] = useState( - null, - ); + const [responseEditor, setResponseEditor] = + useState(null); + const [variableEditor, setVariableEditor] = + useState(null); const [shouldPersistHeaders, setShouldPersistHeadersInternal] = useState( () => { diff --git a/packages/graphiql-react/src/editor/hooks.ts b/packages/graphiql-react/src/editor/hooks.ts index d13fdac4543..742735e53ea 100644 --- a/packages/graphiql-react/src/editor/hooks.ts +++ b/packages/graphiql-react/src/editor/hooks.ts @@ -13,7 +13,7 @@ import debounce from '../utility/debounce'; import { onHasCompletion } from './completion'; import { useEditorContext } from './context'; import { CodeMirrorEditor } from './types'; -import { editor as MONACO_EDITOR } from 'monaco-editor'; +import { editor as MONACO_EDITOR } from 'monaco-graphql/esm/monaco-editor.js'; export function useSynchronizeValue( editor: MONACO_EDITOR.IStandaloneCodeEditor | null, diff --git a/packages/graphiql-react/src/editor/operations-editor.ts b/packages/graphiql-react/src/editor/operations-editor.ts index f571becb12a..71ab9a9b402 100644 --- a/packages/graphiql-react/src/editor/operations-editor.ts +++ b/packages/graphiql-react/src/editor/operations-editor.ts @@ -17,7 +17,7 @@ import { usePrettifyEditors, } from './hooks'; import { WriteableEditorProps } from './types'; -import { KeyCode, KeyMod } from 'monaco-editor'; +import { KeyCode, KeyMod } from 'monaco-graphql/esm/monaco-editor.js'; import { MONACO_GRAPHQL_API, OPERATIONS_MODEL } from '@/constants'; import { createEditor } from '@/create-editor'; diff --git a/packages/graphiql-react/src/editor/tabs.ts b/packages/graphiql-react/src/editor/tabs.ts index 45dbc5a766f..b33f188bede 100644 --- a/packages/graphiql-react/src/editor/tabs.ts +++ b/packages/graphiql-react/src/editor/tabs.ts @@ -1,6 +1,6 @@ import { StorageAPI } from '@graphiql/toolkit'; import { useCallback, useMemo } from 'react'; -import { editor } from 'monaco-editor'; +import { editor } from 'monaco-graphql/esm/monaco-editor.js'; import debounce from '../utility/debounce'; diff --git a/packages/monaco-graphql/README.md b/packages/monaco-graphql/README.md index 8e317452352..fe8f71fdb2b 100644 --- a/packages/monaco-graphql/README.md +++ b/packages/monaco-graphql/README.md @@ -263,7 +263,7 @@ const { api } = monaco.languages.graphql; import 'monaco-graphql'; // also this -import { languages } from 'monaco-editor'; +import { languages } from 'monaco-graphql/esm/monaco-editor.js'; // now the api will be available on the `monaco.languages` global const { api } = languages.graphql; ``` @@ -443,7 +443,7 @@ you'll want to create your own `my-graphql.worker.ts` file, and add your custom config such as `schemaLoader` to `createData`: ```ts -import type { worker as WorkerNamespace } from 'monaco-editor'; +import type { worker as WorkerNamespace } from 'monaco-graphql/esm/monaco-editor.js'; // @ts-expect-error - ignore missing types import * as worker from 'monaco-editor/esm/vs/editor/editor.worker'; diff --git a/packages/monaco-graphql/package.json b/packages/monaco-graphql/package.json index b488d42848a..c727c9c1de0 100644 --- a/packages/monaco-graphql/package.json +++ b/packages/monaco-graphql/package.json @@ -27,7 +27,7 @@ ], "scripts": { "test": "vitest run", - "postbuild": "node scripts/patch-monaco-editor-types.mjs" + "build": "node scripts/patch-monaco-editor-types.mjs" }, "dependencies": { "graphql-language-service": "^5.1.7", diff --git a/resources/babel.config.js b/resources/babel.config.js index 9b9dfe9756f..7de20fa5a05 100644 --- a/resources/babel.config.js +++ b/resources/babel.config.js @@ -1,7 +1,7 @@ module.exports = { sourceMaps: true, presets: [ - [require.resolve('@babel/preset-env')], + [require.resolve('@babel/preset-env'), { modules: false }], require.resolve('@babel/preset-react'), ], plugins: [require.resolve('@babel/plugin-proposal-class-properties')], diff --git a/resources/webpack.config.js b/resources/webpack.config.js index ee788d463fa..28ace305a8d 100644 --- a/resources/webpack.config.js +++ b/resources/webpack.config.js @@ -35,10 +35,7 @@ module.exports = { { loader: 'babel-loader', options: { - presets: [ - ['@babel/preset-env', { modules: false }], - '@babel/preset-react', - ], + presets: ['@babel/preset-env', '@babel/preset-react'], }, }, ], diff --git a/yarn.lock b/yarn.lock index 03b86f10d7a..55a5dcea20d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13668,11 +13668,6 @@ monaco-editor@^0.39.0: resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.39.0.tgz#3cf8e3718d6aac347d374516a6837d1c13d967d2" integrity sha512-zhbZ2Nx93tLR8aJmL2zI1mhJpsl87HMebNBM6R8z4pLfs8pj604pIVIVwyF1TivcfNtIPpMXL+nb3DsBmE/x6Q== -monaco-editor@^0.39.0: - version "0.39.0" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.39.0.tgz#3cf8e3718d6aac347d374516a6837d1c13d967d2" - integrity sha512-zhbZ2Nx93tLR8aJmL2zI1mhJpsl87HMebNBM6R8z4pLfs8pj604pIVIVwyF1TivcfNtIPpMXL+nb3DsBmE/x6Q== - mri@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" From ca776edf959d3d7f3d23501598aa3e26082c7e2f Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Sun, 30 Jul 2023 18:31:39 +0200 Subject: [PATCH 4/5] use normal monaco-editor imports in graphiql react --- packages/graphiql-react/src/constants.ts | 2 +- packages/graphiql-react/src/create-editor.ts | 2 +- packages/graphiql-react/src/editor/context.tsx | 2 +- packages/graphiql-react/src/editor/hooks.ts | 2 +- packages/graphiql-react/src/editor/operations-editor.ts | 2 +- packages/graphiql-react/src/editor/tabs.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphiql-react/src/constants.ts b/packages/graphiql-react/src/constants.ts index ab89134c671..a5e068309cb 100644 --- a/packages/graphiql-react/src/constants.ts +++ b/packages/graphiql-react/src/constants.ts @@ -1,5 +1,5 @@ import { initializeMode } from 'monaco-graphql/esm/initializeMode.js'; -import { editor, Uri } from 'monaco-graphql/esm/monaco-editor.js'; +import { editor, Uri } from 'monaco-editor'; import { DEFAULT_QUERY } from './editor/context'; const OPERATIONS_URI = 'operations.graphql'; diff --git a/packages/graphiql-react/src/create-editor.ts b/packages/graphiql-react/src/create-editor.ts index d63d6c7b4a3..c951e296e57 100644 --- a/packages/graphiql-react/src/create-editor.ts +++ b/packages/graphiql-react/src/create-editor.ts @@ -1,4 +1,4 @@ -import { editor as MONACO_EDITOR } from 'monaco-graphql/esm/monaco-editor.js'; +import { editor as MONACO_EDITOR } from 'monaco-editor'; import { OPERATIONS_MODEL, VARIABLES_MODEL, diff --git a/packages/graphiql-react/src/editor/context.tsx b/packages/graphiql-react/src/editor/context.tsx index c684b706483..38379750dc1 100644 --- a/packages/graphiql-react/src/editor/context.tsx +++ b/packages/graphiql-react/src/editor/context.tsx @@ -37,7 +37,7 @@ import { } from './tabs'; import { CodeMirrorEditor } from './types'; import { STORAGE_KEY as STORAGE_KEY_VARIABLES } from './variable-editor'; -import { editor } from 'monaco-graphql/esm/monaco-editor.js'; +import { editor } from 'monaco-editor'; export type CodeMirrorEditorWithOperationFacts = CodeMirrorEditor & { documentAST: DocumentNode | null; diff --git a/packages/graphiql-react/src/editor/hooks.ts b/packages/graphiql-react/src/editor/hooks.ts index 742735e53ea..d13fdac4543 100644 --- a/packages/graphiql-react/src/editor/hooks.ts +++ b/packages/graphiql-react/src/editor/hooks.ts @@ -13,7 +13,7 @@ import debounce from '../utility/debounce'; import { onHasCompletion } from './completion'; import { useEditorContext } from './context'; import { CodeMirrorEditor } from './types'; -import { editor as MONACO_EDITOR } from 'monaco-graphql/esm/monaco-editor.js'; +import { editor as MONACO_EDITOR } from 'monaco-editor'; export function useSynchronizeValue( editor: MONACO_EDITOR.IStandaloneCodeEditor | null, diff --git a/packages/graphiql-react/src/editor/operations-editor.ts b/packages/graphiql-react/src/editor/operations-editor.ts index 71ab9a9b402..f571becb12a 100644 --- a/packages/graphiql-react/src/editor/operations-editor.ts +++ b/packages/graphiql-react/src/editor/operations-editor.ts @@ -17,7 +17,7 @@ import { usePrettifyEditors, } from './hooks'; import { WriteableEditorProps } from './types'; -import { KeyCode, KeyMod } from 'monaco-graphql/esm/monaco-editor.js'; +import { KeyCode, KeyMod } from 'monaco-editor'; import { MONACO_GRAPHQL_API, OPERATIONS_MODEL } from '@/constants'; import { createEditor } from '@/create-editor'; diff --git a/packages/graphiql-react/src/editor/tabs.ts b/packages/graphiql-react/src/editor/tabs.ts index b33f188bede..45dbc5a766f 100644 --- a/packages/graphiql-react/src/editor/tabs.ts +++ b/packages/graphiql-react/src/editor/tabs.ts @@ -1,6 +1,6 @@ import { StorageAPI } from '@graphiql/toolkit'; import { useCallback, useMemo } from 'react'; -import { editor } from 'monaco-graphql/esm/monaco-editor.js'; +import { editor } from 'monaco-editor'; import debounce from '../utility/debounce'; From f02956ada15c54eddc88c2f87d134916b73ed616 Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Mon, 31 Jul 2023 11:38:21 +0200 Subject: [PATCH 5/5] add changeset - now all merges/commits to monaco4 will release a graphiql@4 tagged monaco --- .changeset/pre.json | 25 +++++++++++++++++++++++++ .changeset/soft-cars-notice.md | 7 +++++++ 2 files changed, 32 insertions(+) create mode 100644 .changeset/pre.json create mode 100644 .changeset/soft-cars-notice.md diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 00000000000..25fdba9c9b2 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,25 @@ +{ + "mode": "pre", + "tag": "monaco", + "initialVersions": { + "example-graphiql-webpack": "1.1.1-alpha.8", + "example-monaco-graphql-nextjs": "0.0.0", + "example-monaco-graphql-react-vite": "0.0.0", + "example-monaco-graphql-webpack": "1.1.1", + "cm6-graphql": "0.0.9", + "codemirror-graphql": "2.0.9", + "graphiql": "3.0.5", + "@graphiql/plugin-code-exporter": "0.3.4", + "@graphiql/plugin-explorer": "0.3.4", + "@graphiql/react": "0.19.3", + "@graphiql/toolkit": "0.9.1", + "graphql-language-service": "5.1.7", + "graphql-language-service-cli": "3.3.25", + "graphql-language-service-server": "2.11.3", + "monaco-graphql": "1.3.0", + "vscode-graphql": "0.8.17", + "vscode-graphql-execution": "0.2.4", + "vscode-graphql-syntax": "1.2.2" + }, + "changesets": [] +} diff --git a/.changeset/soft-cars-notice.md b/.changeset/soft-cars-notice.md new file mode 100644 index 00000000000..cc058dd93ad --- /dev/null +++ b/.changeset/soft-cars-notice.md @@ -0,0 +1,7 @@ +--- +'graphiql': major +'@graphiql/react': major +'monaco-graphql': major +--- + +release `graphiql` with `monaco-editor` support