node_modules ignore

This commit is contained in:
2025-05-08 23:43:47 +02:00
parent e19d52f172
commit 4574544c9f
65041 changed files with 10593536 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
/* eslint-disable jest/no-conditional-expect */
import React, { useEffect, useRef } from 'react';
import renderer from 'react-test-renderer';
import { render, fireEvent, screen } from '@testing-library/react';
// import userEvent from '@testing-library/user-event';
// import '@testing-library/jest-dom';
import CodeMirror, { ReactCodeMirrorRef } from '..';
it('CodeMirror', async () => {
const component = renderer.create(<CodeMirror />);
let tree = component.toJSON();
if (tree && !Array.isArray(tree)) {
expect(tree.type).toEqual('div');
expect(tree.props.className).toEqual('cm-theme-light');
}
});
it('CodeMirror onChange', async () => {
const handleChange = jest.fn((value) => {
expect(value).toEqual('# title');
return Array.isArray(value) ? value.join() : value;
});
render(<CodeMirror autoFocus value="console.log('Hello world!')" onChange={handleChange} />);
const input = await screen.findByRole<HTMLInputElement>('textbox'); // findByRole('textbox');
fireEvent.change(input, { target: { textContent: '# title' } });
const elm = screen.queryByText('# title');
expect((elm as any).cmView.dom.innerHTML).toEqual('# title');
});
it('CodeMirror onUpdate', async () => {
render(
<CodeMirror
value="console.log('Hello world!')"
autoFocus
onUpdate={(viewUpdate) => {
expect(viewUpdate.state.doc.length).toEqual(27);
}}
/>,
);
});
it('CodeMirror ref', async () => {
function Demo() {
const ref = useRef<ReactCodeMirrorRef>(null);
useEffect(() => {
expect(Object.keys(ref.current!)).toEqual(['editor', 'state', 'view']);
}, [ref]);
return <CodeMirror ref={ref} value="console.log('Hello world!')" />;
}
render(<Demo />);
});
it('CodeMirror theme', async () => {
const component = renderer.create(<CodeMirror theme="dark" />);
let tree = component.toJSON();
if (tree && !Array.isArray(tree)) {
expect(tree.type).toEqual('div');
expect(tree.props.className).toEqual('cm-theme-dark');
}
});
it('CodeMirror className', async () => {
const component = renderer.create(<CodeMirror className="test" />);
let tree = component.toJSON();
if (tree && !Array.isArray(tree)) {
expect(tree.type).toEqual('div');
expect(tree.props.className).toEqual('cm-theme-light test');
}
});
it('CodeMirror placeholder', async () => {
render(<CodeMirror placeholder="Hello World" className="test" />);
const elm = screen.queryByText('Hello World');
expect(elm!.style['pointerEvents']).toEqual('none');
expect(elm!.className).toEqual('cm-placeholder');
});
it('CodeMirror editable', async () => {
render(<CodeMirror editable={false} className="test" />);
const text = screen.getByRole('textbox');
expect(text.className).toEqual('cm-content');
expect(text.tagName).toEqual('DIV');
});
it("CodeMirror doesn't echo changes", async () => {
const handleChange = jest.fn();
const { rerender } = render(<CodeMirror value="value a" onChange={handleChange} />);
rerender(<CodeMirror value="value b" onChange={handleChange} />);
expect(handleChange).not.toHaveBeenCalled();
});

View File

@@ -0,0 +1,65 @@
import { type Extension } from '@codemirror/state';
import { indentWithTab } from '@codemirror/commands';
import { basicSetup, type BasicSetupOptions } from '@uiw/codemirror-extensions-basic-setup';
import { EditorView, keymap, placeholder } from '@codemirror/view';
import { oneDark } from '@codemirror/theme-one-dark';
import { EditorState } from '@codemirror/state';
import { defaultLightThemeOption } from './theme/light';
export * from '@codemirror/theme-one-dark';
export * from './theme/light';
export interface DefaultExtensionsOptions {
indentWithTab?: boolean;
basicSetup?: boolean | BasicSetupOptions;
placeholder?: string | HTMLElement;
theme?: 'light' | 'dark' | 'none' | Extension;
readOnly?: boolean;
editable?: boolean;
}
export const getDefaultExtensions = (optios: DefaultExtensionsOptions = {}): Extension[] => {
const {
indentWithTab: defaultIndentWithTab = true,
editable = true,
readOnly = false,
theme = 'light',
placeholder: placeholderStr = '',
basicSetup: defaultBasicSetup = true,
} = optios;
const getExtensions: Extension[] = [];
if (defaultIndentWithTab) {
getExtensions.unshift(keymap.of([indentWithTab]));
}
if (defaultBasicSetup) {
if (typeof defaultBasicSetup === 'boolean') {
getExtensions.unshift(basicSetup());
} else {
getExtensions.unshift(basicSetup(defaultBasicSetup));
}
}
if (placeholderStr) {
getExtensions.unshift(placeholder(placeholderStr));
}
switch (theme) {
case 'light':
getExtensions.push(defaultLightThemeOption);
break;
case 'dark':
getExtensions.push(oneDark);
break;
case 'none':
break;
default:
getExtensions.push(theme);
break;
}
if (editable === false) {
getExtensions.push(EditorView.editable.of(false));
}
if (readOnly) {
getExtensions.push(EditorState.readOnly.of(true));
}
return [...getExtensions];
};

164
server/node_modules/@uiw/react-codemirror/src/index.tsx generated vendored Normal file
View File

@@ -0,0 +1,164 @@
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
import type { EditorState, EditorStateConfig, Extension, StateField } from '@codemirror/state';
import type { EditorView, ViewUpdate } from '@codemirror/view';
import { type BasicSetupOptions } from '@uiw/codemirror-extensions-basic-setup';
import { useCodeMirror } from './useCodeMirror';
import { type Statistics } from './utils';
export * from '@codemirror/view';
export * from '@codemirror/state';
export * from '@uiw/codemirror-extensions-basic-setup';
export * from './useCodeMirror';
export * from './getDefaultExtensions';
export * from './utils';
export interface ReactCodeMirrorProps
extends Omit<EditorStateConfig, 'doc' | 'extensions'>,
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'placeholder'> {
/** value of the auto created model in the editor. */
value?: string;
height?: string;
minHeight?: string;
maxHeight?: string;
width?: string;
minWidth?: string;
maxWidth?: string;
/** focus on the editor. */
autoFocus?: boolean;
/** Enables a placeholder—a piece of example content to show when the editor is empty. */
placeholder?: string | HTMLElement;
/**
* `light` / `dark` / `Extension` Defaults to `light`.
* @default light
*/
theme?: 'light' | 'dark' | 'none' | Extension;
/**
* Whether to optional basicSetup by default
* @default true
*/
basicSetup?: boolean | BasicSetupOptions;
/**
* This disables editing of the editor content by the user.
* @default true
*/
editable?: boolean;
/**
* This disables editing of the editor content by the user.
* @default false
*/
readOnly?: boolean;
/**
* Controls whether pressing the `Tab` key inserts a tab character and indents the text (`true`)
* or behaves according to the browser's default behavior (`false`).
* @default true
*/
indentWithTab?: boolean;
/** Fired whenever a change occurs to the document. */
onChange?(value: string, viewUpdate: ViewUpdate): void;
/** Some data on the statistics editor. */
onStatistics?(data: Statistics): void;
/** Fired whenever any state change occurs within the editor, including non-document changes like lint results. */
onUpdate?(viewUpdate: ViewUpdate): void;
/** The first time the editor executes the event. */
onCreateEditor?(view: EditorView, state: EditorState): void;
/**
* Extension values can be [provided](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions) when creating a state to attach various kinds of configuration and behavior information.
* They can either be built-in extension-providing objects,
* such as [state fields](https://codemirror.net/6/docs/ref/#state.StateField) or [facet providers](https://codemirror.net/6/docs/ref/#state.Facet.of),
* or objects with an extension in its `extension` property. Extensions can be nested in arrays arbitrarily deep—they will be flattened when processed.
*/
extensions?: Extension[];
/**
* If the view is going to be mounted in a shadow root or document other than the one held by the global variable document (the default), you should pass it here.
* Originally from the [config of EditorView](https://codemirror.net/6/docs/ref/#view.EditorView.constructor%5Econfig.root)
*/
root?: ShadowRoot | Document;
/**
* Create a state from its JSON representation serialized with [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) function
*/
initialState?: {
json: any;
fields?: Record<string, StateField<any>>;
};
}
export interface ReactCodeMirrorRef {
editor?: HTMLDivElement | null;
state?: EditorState;
view?: EditorView;
}
const ReactCodeMirror = forwardRef<ReactCodeMirrorRef, ReactCodeMirrorProps>((props, ref) => {
const {
className,
value = '',
selection,
extensions = [],
onChange,
onStatistics,
onCreateEditor,
onUpdate,
autoFocus,
theme = 'light',
height,
minHeight,
maxHeight,
width,
minWidth,
maxWidth,
basicSetup,
placeholder,
indentWithTab,
editable,
readOnly,
root,
initialState,
...other
} = props;
const editor = useRef<HTMLDivElement>(null);
const { state, view, container } = useCodeMirror({
container: editor.current,
root,
value,
autoFocus,
theme,
height,
minHeight,
maxHeight,
width,
minWidth,
maxWidth,
basicSetup,
placeholder,
indentWithTab,
editable,
readOnly,
selection,
onChange,
onStatistics,
onCreateEditor,
onUpdate,
extensions,
initialState,
});
useImperativeHandle(ref, () => ({ editor: editor.current, state: state, view: view }), [
editor,
container,
state,
view,
]);
// check type of value
if (typeof value !== 'string') {
throw new Error(`value must be typeof string but got ${typeof value}`);
}
const defaultClassNames = typeof theme === 'string' ? `cm-theme-${theme}` : 'cm-theme';
return <div ref={editor} className={`${defaultClassNames}${className ? ` ${className}` : ''}`} {...other}></div>;
});
ReactCodeMirror.displayName = 'CodeMirror';
export default ReactCodeMirror;

View File

@@ -0,0 +1,12 @@
import { EditorView } from '@codemirror/view';
export const defaultLightThemeOption = EditorView.theme(
{
'&': {
backgroundColor: '#fff',
},
},
{
dark: false,
},
);

View File

@@ -0,0 +1,172 @@
import { useEffect, useState } from 'react';
import { Annotation, EditorState, StateEffect, type Extension } from '@codemirror/state';
import { EditorView, type ViewUpdate } from '@codemirror/view';
import { getDefaultExtensions } from './getDefaultExtensions';
import { getStatistics } from './utils';
import { type ReactCodeMirrorProps } from '.';
const External = Annotation.define<boolean>();
export interface UseCodeMirror extends ReactCodeMirrorProps {
container?: HTMLDivElement | null;
}
const emptyExtensions: Extension[] = [];
export function useCodeMirror(props: UseCodeMirror) {
const {
value,
selection,
onChange,
onStatistics,
onCreateEditor,
onUpdate,
extensions = emptyExtensions,
autoFocus,
theme = 'light',
height = null,
minHeight = null,
maxHeight = null,
width = null,
minWidth = null,
maxWidth = null,
placeholder: placeholderStr = '',
editable = true,
readOnly = false,
indentWithTab: defaultIndentWithTab = true,
basicSetup: defaultBasicSetup = true,
root,
initialState,
} = props;
const [container, setContainer] = useState<HTMLDivElement | null>();
const [view, setView] = useState<EditorView>();
const [state, setState] = useState<EditorState>();
const defaultThemeOption = EditorView.theme({
'&': {
height,
minHeight,
maxHeight,
width,
minWidth,
maxWidth,
},
'& .cm-scroller': {
height: '100% !important',
},
});
const updateListener = EditorView.updateListener.of((vu: ViewUpdate) => {
if (
vu.docChanged &&
typeof onChange === 'function' &&
// Fix echoing of the remote changes:
// If transaction is market as remote we don't have to call `onChange` handler again
!vu.transactions.some((tr) => tr.annotation(External))
) {
const doc = vu.state.doc;
const value = doc.toString();
onChange(value, vu);
}
onStatistics && onStatistics(getStatistics(vu));
});
const defaultExtensions = getDefaultExtensions({
theme,
editable,
readOnly,
placeholder: placeholderStr,
indentWithTab: defaultIndentWithTab,
basicSetup: defaultBasicSetup,
});
let getExtensions = [updateListener, defaultThemeOption, ...defaultExtensions];
if (onUpdate && typeof onUpdate === 'function') {
getExtensions.push(EditorView.updateListener.of(onUpdate));
}
getExtensions = getExtensions.concat(extensions);
useEffect(() => {
if (container && !state) {
const config = {
doc: value,
selection,
extensions: getExtensions,
};
const stateCurrent = initialState
? EditorState.fromJSON(initialState.json, config, initialState.fields)
: EditorState.create(config);
setState(stateCurrent);
if (!view) {
const viewCurrent = new EditorView({
state: stateCurrent,
parent: container,
root,
});
setView(viewCurrent);
onCreateEditor && onCreateEditor(viewCurrent, stateCurrent);
}
}
return () => {
if (view) {
setState(undefined);
setView(undefined);
}
};
}, [container, state]);
useEffect(() => setContainer(props.container), [props.container]);
useEffect(
() => () => {
if (view) {
view.destroy();
setView(undefined);
}
},
[view],
);
useEffect(() => {
if (autoFocus && view) {
view.focus();
}
}, [autoFocus, view]);
useEffect(() => {
if (view) {
view.dispatch({ effects: StateEffect.reconfigure.of(getExtensions) });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
theme,
extensions,
height,
minHeight,
maxHeight,
width,
minWidth,
maxWidth,
placeholderStr,
editable,
readOnly,
defaultIndentWithTab,
defaultBasicSetup,
onChange,
onUpdate,
]);
useEffect(() => {
if (value === undefined) {
return;
}
const currentValue = view ? view.state.doc.toString() : '';
if (view && value !== currentValue) {
view.dispatch({
changes: { from: 0, to: currentValue.length, insert: value || '' },
annotations: [External.of(true)],
});
}
}, [value, view]);
return { state, setState, view, setView, container, setContainer };
}

49
server/node_modules/@uiw/react-codemirror/src/utils.ts generated vendored Normal file
View File

@@ -0,0 +1,49 @@
import type { EditorSelection, SelectionRange, Line } from '@codemirror/state';
import type { ViewUpdate } from '@codemirror/view';
export interface Statistics {
/** total length of the document */
length: number;
/** Get the number of lines in the editor. */
lineCount: number;
/** Get the currently line description around the given position. */
line: Line;
/** Get the proper [line-break](https://codemirror.net/docs/ref/#state.EditorState^lineSeparator) string for this state. */
lineBreak: string;
/** Returns true when the editor is [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only. */
readOnly: boolean;
/** The size (in columns) of a tab in the document, determined by the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet. */
tabSize: number;
/** Cursor Position */
selection: EditorSelection;
/** Make sure the selection only has one range. */
selectionAsSingle: SelectionRange;
/** Retrieves a list of all current selections. */
ranges: readonly SelectionRange[];
/** Get the currently selected code. */
selectionCode: string;
/**
* The length of the given array should be the same as the number of active selections.
* Replaces the content of the selections with the strings in the array.
*/
selections: string[];
/** Return true if any text is selected. */
selectedText: boolean;
}
export const getStatistics = (view: ViewUpdate): Statistics => {
return {
line: view.state.doc.lineAt(view.state.selection.main.from),
lineCount: view.state.doc.lines,
lineBreak: view.state.lineBreak,
length: view.state.doc.length,
readOnly: view.state.readOnly,
tabSize: view.state.tabSize,
selection: view.state.selection,
selectionAsSingle: view.state.selection.asSingle().main,
ranges: view.state.selection.ranges,
selectionCode: view.state.sliceDoc(view.state.selection.main.from, view.state.selection.main.to),
selections: view.state.selection.ranges.map((r) => view.state.sliceDoc(r.from, r.to)),
selectedText: view.state.selection.ranges.some((r) => !r.empty),
};
};