vzcode 0.81.0 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{worker-tyOiPqgH.js → index-CI1sbUJw.js} +62 -62
- package/dist/assets/{index-CnwdGSLW.js → index-CnHJy69d.js} +40 -40
- package/dist/index.html +1 -1
- package/package.json +3 -3
- package/src/client/AIAssist/AIAssistWidget/index.tsx +12 -1
- package/src/client/App/index.tsx +1 -1
- package/src/client/CodeEditor/getOrCreateEditor.ts +3 -0
- package/src/client/CodeEditor/index.tsx +4 -1
- package/src/client/CodeEditor/typeScriptLinter.ts +2 -0
- package/src/client/CodeEditor/widgets.ts +2 -4
- package/src/client/VZMiddle.tsx +3 -0
- package/src/client/VZSettings.tsx +43 -9
- package/src/client/useTypeScript/worker/constants.ts +73 -0
- package/src/client/useTypeScript/worker/getTSFileName.ts +4 -0
- package/src/client/useTypeScript/worker/handleMessageAutocompleteRequest.ts +63 -0
- package/src/client/useTypeScript/worker/handleMessageLintRequest.ts +84 -0
- package/src/client/useTypeScript/worker/handleMessageTranspileRequest.ts +20 -0
- package/src/client/useTypeScript/worker/handleMessageUpdateContent.ts +38 -0
- package/src/client/useTypeScript/worker/index.ts +121 -0
- package/src/client/useTypeScript/worker/isTS.ts +3 -0
- package/src/client/useTypeScript/{requestTypes.ts → worker/requestTypes.ts} +3 -1
- package/src/client/CodeEditor/codemirror-interact.ts +0 -409
- package/src/client/useTypeScript/worker.ts +0 -322
package/dist/index.html
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
|
|
21
21
|
rel="stylesheet"
|
|
22
22
|
/>
|
|
23
|
-
<script type="module" crossorigin src="/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-CnHJy69d.js"></script>
|
|
24
24
|
<link rel="stylesheet" crossorigin href="/assets/index-J8-kRnnE.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"@codemirror/view": "^6.26.0",
|
|
75
75
|
"@lezer/highlight": "^1.2.0",
|
|
76
76
|
"@replit/codemirror-indentation-markers": "^6.5.1",
|
|
77
|
-
"@replit/codemirror-interact": "^6.3.
|
|
77
|
+
"@replit/codemirror-interact": "^6.3.1",
|
|
78
78
|
"@replit/codemirror-lang-svelte": "^6.0.0",
|
|
79
79
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
80
80
|
"@teamwork/websocket-json-stream": "^2.0.0",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"ws": "^8.16.0"
|
|
113
113
|
},
|
|
114
114
|
"devDependencies": {
|
|
115
|
-
"@types/react": "^18.2.
|
|
115
|
+
"@types/react": "^18.2.72",
|
|
116
116
|
"@types/react-dom": "^18.2.22",
|
|
117
117
|
"@vitejs/plugin-react": "^4.2.1",
|
|
118
118
|
"concurrently": "^8.2.2",
|
|
@@ -55,6 +55,17 @@ export const AIAssistWidget = ({
|
|
|
55
55
|
aiAssistOptions,
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
+
// Introduce a delay before running Prettier
|
|
59
|
+
// and the code, to give the ShareDB ops a chance
|
|
60
|
+
// to be applied and to propagate via WebSockets
|
|
61
|
+
// to the client. This is necessary because sometimes
|
|
62
|
+
// the ShareDB ops from the AI assist actually don't
|
|
63
|
+
// reach the client until AFTER the response comes back
|
|
64
|
+
// from `await startAIAssist`.
|
|
65
|
+
await new Promise((resolve) =>
|
|
66
|
+
setTimeout(resolve, 1000),
|
|
67
|
+
);
|
|
68
|
+
|
|
58
69
|
// Trigger a Prettier run after the AI Assist.
|
|
59
70
|
const runPrettier = runPrettierRef.current;
|
|
60
71
|
if (runPrettier !== null) {
|
|
@@ -69,7 +80,7 @@ export const AIAssistWidget = ({
|
|
|
69
80
|
|
|
70
81
|
// Handles the case that the user has started,
|
|
71
82
|
// stopped, and started again before the first request
|
|
72
|
-
// has finished,
|
|
83
|
+
// has finished, by comparing the current stream ID
|
|
73
84
|
// with the stream ID that was active when the request
|
|
74
85
|
// was started.
|
|
75
86
|
setAiStreamId((currentAIStreamId) =>
|
package/src/client/App/index.tsx
CHANGED
|
@@ -91,6 +91,7 @@ export const getOrCreateEditor = ({
|
|
|
91
91
|
usernameRef,
|
|
92
92
|
typeScriptWorker,
|
|
93
93
|
customInteractRules,
|
|
94
|
+
allowGlobals,
|
|
94
95
|
}: {
|
|
95
96
|
fileId: FileId;
|
|
96
97
|
|
|
@@ -119,6 +120,7 @@ export const getOrCreateEditor = ({
|
|
|
119
120
|
aiAssistEndpoint?: string;
|
|
120
121
|
typeScriptWorker: Worker;
|
|
121
122
|
customInteractRules?: Array<InteractRule>;
|
|
123
|
+
allowGlobals: boolean;
|
|
122
124
|
}): EditorCacheValue => {
|
|
123
125
|
// Cache hit
|
|
124
126
|
if (editorCache.has(fileId)) {
|
|
@@ -276,6 +278,7 @@ export const getOrCreateEditor = ({
|
|
|
276
278
|
fileName: name,
|
|
277
279
|
shareDBDoc,
|
|
278
280
|
fileId,
|
|
281
|
+
allowGlobals,
|
|
279
282
|
}) as unknown as () => Diagnostic[],
|
|
280
283
|
//Needs the unknown because we are returning a Promise<Diagnostic>
|
|
281
284
|
),
|
|
@@ -9,16 +9,18 @@ import { Username } from '../../types';
|
|
|
9
9
|
import { EditorCacheValue } from '../useEditorCache';
|
|
10
10
|
import { getOrCreateEditor } from './getOrCreateEditor';
|
|
11
11
|
import { VZCodeContext } from '../VZCodeContext';
|
|
12
|
-
import './style.scss';
|
|
13
12
|
import { InteractRule } from '@replit/codemirror-interact';
|
|
13
|
+
import './style.scss';
|
|
14
14
|
|
|
15
15
|
// The path in the ShareDB document where the files live.
|
|
16
16
|
const filesPath = ['files'];
|
|
17
17
|
|
|
18
18
|
export const CodeEditor = ({
|
|
19
19
|
customInteractRules,
|
|
20
|
+
allowGlobals,
|
|
20
21
|
}: {
|
|
21
22
|
customInteractRules?: Array<InteractRule>;
|
|
23
|
+
allowGlobals: boolean;
|
|
22
24
|
}) => {
|
|
23
25
|
const {
|
|
24
26
|
activeFileId,
|
|
@@ -85,6 +87,7 @@ export const CodeEditor = ({
|
|
|
85
87
|
usernameRef,
|
|
86
88
|
typeScriptWorker,
|
|
87
89
|
customInteractRules,
|
|
90
|
+
allowGlobals,
|
|
88
91
|
}),
|
|
89
92
|
[
|
|
90
93
|
activeFileId,
|
|
@@ -10,6 +10,7 @@ export const typeScriptLinter = ({
|
|
|
10
10
|
fileName,
|
|
11
11
|
shareDBDoc,
|
|
12
12
|
fileId,
|
|
13
|
+
allowGlobals,
|
|
13
14
|
}) => {
|
|
14
15
|
return async () => {
|
|
15
16
|
const requestId = generateRequestId();
|
|
@@ -20,6 +21,7 @@ export const typeScriptLinter = ({
|
|
|
20
21
|
fileName,
|
|
21
22
|
fileContent,
|
|
22
23
|
requestId,
|
|
24
|
+
allowGlobals,
|
|
23
25
|
};
|
|
24
26
|
typeScriptWorker.postMessage(linterRequest);
|
|
25
27
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
// TODO move back to this way of importing when this PR is merged:
|
|
2
|
-
// https://github.com/replit/codemirror-interact/pull/19/files
|
|
3
|
-
// import interact from '@replit/codemirror-interact';
|
|
4
1
|
import interact, {
|
|
5
2
|
InteractRule,
|
|
6
|
-
} from '
|
|
3
|
+
} from '@replit/codemirror-interact';
|
|
7
4
|
|
|
8
5
|
import {
|
|
9
6
|
ViewPlugin,
|
|
@@ -56,6 +53,7 @@ export const widgets = ({
|
|
|
56
53
|
const updateHex = (e: Event) => {
|
|
57
54
|
const el: HTMLInputElement =
|
|
58
55
|
e.target as HTMLInputElement;
|
|
56
|
+
if (onInteract) onInteract();
|
|
59
57
|
if (el.value) {
|
|
60
58
|
setText(
|
|
61
59
|
valueIsUpper
|
package/src/client/VZMiddle.tsx
CHANGED
|
@@ -22,6 +22,7 @@ export const VZMiddle = ({
|
|
|
22
22
|
aiAssistTooltipText,
|
|
23
23
|
aiAssistClickOverride,
|
|
24
24
|
customInteractRules,
|
|
25
|
+
allowGlobals = false,
|
|
25
26
|
}: {
|
|
26
27
|
enableAIAssist?: boolean;
|
|
27
28
|
aiAssistEndpoint?: string;
|
|
@@ -29,6 +30,7 @@ export const VZMiddle = ({
|
|
|
29
30
|
aiAssistTooltipText?: string;
|
|
30
31
|
aiAssistClickOverride?: () => void;
|
|
31
32
|
customInteractRules?: Array<InteractRule>;
|
|
33
|
+
allowGlobals?: boolean;
|
|
32
34
|
}) => {
|
|
33
35
|
const { codeEditorWidth } = useContext(
|
|
34
36
|
SplitPaneResizeContext,
|
|
@@ -58,6 +60,7 @@ export const VZMiddle = ({
|
|
|
58
60
|
{isClient && content && activeFileId && (
|
|
59
61
|
<CodeEditor
|
|
60
62
|
customInteractRules={customInteractRules}
|
|
63
|
+
allowGlobals={allowGlobals}
|
|
61
64
|
/>
|
|
62
65
|
)}
|
|
63
66
|
{isClient &&
|
|
@@ -64,9 +64,6 @@ export const VZSettings = ({
|
|
|
64
64
|
}, [selectedFont]);
|
|
65
65
|
|
|
66
66
|
useEffect(() => {
|
|
67
|
-
// const fontSize = event.target.value;
|
|
68
|
-
// const root = document.documentElement;
|
|
69
|
-
// root.style.setProperty('--vzcode-font-size', fontSize);
|
|
70
67
|
document.body.style.setProperty(
|
|
71
68
|
'--vzcode-font-size',
|
|
72
69
|
selectedFontSize,
|
|
@@ -109,7 +106,6 @@ export const VZSettings = ({
|
|
|
109
106
|
|
|
110
107
|
<Form.Group className="mb-3" controlId="formFork">
|
|
111
108
|
<Form.Label>Theme</Form.Label>
|
|
112
|
-
|
|
113
109
|
<select
|
|
114
110
|
className="form-select"
|
|
115
111
|
value={theme}
|
|
@@ -121,7 +117,6 @@ export const VZSettings = ({
|
|
|
121
117
|
</option>
|
|
122
118
|
))}
|
|
123
119
|
</select>
|
|
124
|
-
|
|
125
120
|
<Form.Text className="text-muted">
|
|
126
121
|
Select a color theme for the editor
|
|
127
122
|
</Form.Text>
|
|
@@ -129,7 +124,6 @@ export const VZSettings = ({
|
|
|
129
124
|
|
|
130
125
|
<Form.Group className="mb-3" controlId="formFork">
|
|
131
126
|
<Form.Label>Font</Form.Label>
|
|
132
|
-
|
|
133
127
|
<select
|
|
134
128
|
className="form-select"
|
|
135
129
|
onChange={handleFontChange}
|
|
@@ -141,7 +135,6 @@ export const VZSettings = ({
|
|
|
141
135
|
</option>
|
|
142
136
|
))}
|
|
143
137
|
</select>
|
|
144
|
-
|
|
145
138
|
<Form.Text className="text-muted">
|
|
146
139
|
Select font for the editor
|
|
147
140
|
</Form.Text>
|
|
@@ -149,7 +142,6 @@ export const VZSettings = ({
|
|
|
149
142
|
|
|
150
143
|
<Form.Group className="mb-3" controlId="formFork">
|
|
151
144
|
<Form.Label>Font Size</Form.Label>
|
|
152
|
-
|
|
153
145
|
<select
|
|
154
146
|
className="form-select"
|
|
155
147
|
onChange={handleFontSizeChange}
|
|
@@ -161,11 +153,53 @@ export const VZSettings = ({
|
|
|
161
153
|
</option>
|
|
162
154
|
))}
|
|
163
155
|
</select>
|
|
164
|
-
|
|
165
156
|
<Form.Text className="text-muted">
|
|
166
157
|
Select a font size for the editor
|
|
167
158
|
</Form.Text>
|
|
168
159
|
</Form.Group>
|
|
160
|
+
|
|
161
|
+
{/* Keyboard Shortcuts Documentation */}
|
|
162
|
+
<Form.Group className="mb-3" controlId="formFork">
|
|
163
|
+
<Form.Label>Keyboard Shortcuts</Form.Label>
|
|
164
|
+
<ul>
|
|
165
|
+
<li>
|
|
166
|
+
<strong>Alt-w</strong> <span>⌥W</span>
|
|
167
|
+
<br />
|
|
168
|
+
<span>Close the current tab</span>
|
|
169
|
+
</li>
|
|
170
|
+
<li>
|
|
171
|
+
<strong>Alt-n</strong> <span>⌥N</span>
|
|
172
|
+
<br />
|
|
173
|
+
<span>Open the create file modal</span>
|
|
174
|
+
</li>
|
|
175
|
+
<li>
|
|
176
|
+
<strong>Alt-PageUp</strong> <span>⌥⇞</span>
|
|
177
|
+
<br />
|
|
178
|
+
<span>
|
|
179
|
+
Change the active tab to the previous one
|
|
180
|
+
</span>
|
|
181
|
+
</li>
|
|
182
|
+
<li>
|
|
183
|
+
<strong>Alt-PageDown</strong> <span>⌥⇟</span>
|
|
184
|
+
<br />
|
|
185
|
+
<span>
|
|
186
|
+
Change the active tab to the next one
|
|
187
|
+
</span>
|
|
188
|
+
</li>
|
|
189
|
+
<li>
|
|
190
|
+
<strong>Ctrl-s</strong> or{' '}
|
|
191
|
+
<strong>Shift-Enter</strong> <span>⌘S</span>{' '}
|
|
192
|
+
or <span>⇧↩</span>
|
|
193
|
+
<br />
|
|
194
|
+
<span>
|
|
195
|
+
Run the code and format it with Prettier
|
|
196
|
+
</span>
|
|
197
|
+
</li>
|
|
198
|
+
</ul>
|
|
199
|
+
<Form.Text className="text-muted">
|
|
200
|
+
Keyboard shortcuts for quick actions
|
|
201
|
+
</Form.Text>
|
|
202
|
+
</Form.Group>
|
|
169
203
|
</Modal.Body>
|
|
170
204
|
<Modal.Footer>
|
|
171
205
|
<Button variant="primary" onClick={closeSettings}>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
export const compilerOptions: ts.CompilerOptions = {
|
|
4
|
+
lib: ['dom', 'esnext'],
|
|
5
|
+
|
|
6
|
+
// Disable warnings around "Any type".
|
|
7
|
+
noImplicitAny: false,
|
|
8
|
+
|
|
9
|
+
// Disable warnings around "Implicit any in parameter".
|
|
10
|
+
noImplicitThis: false,
|
|
11
|
+
|
|
12
|
+
// Allow JavaScript files to be processed
|
|
13
|
+
allowJs: true,
|
|
14
|
+
|
|
15
|
+
// Disable type checking for JavaScript files
|
|
16
|
+
// checkJs: false,
|
|
17
|
+
|
|
18
|
+
// Skip type checking of declaration files
|
|
19
|
+
skipLibCheck: true,
|
|
20
|
+
|
|
21
|
+
// Support React JSX
|
|
22
|
+
jsx: ts.JsxEmit.React,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Be less aggressive for non-TS files,
|
|
26
|
+
// e.g. files that end in .js or .jsx.
|
|
27
|
+
// if (!isTS(fileName)) {
|
|
28
|
+
// This code is for errors like:
|
|
29
|
+
// "Binding element 'data' implicitly has an 'any' type."
|
|
30
|
+
const LINT_ERROR_CODE_ANY = 7031;
|
|
31
|
+
|
|
32
|
+
// This code is for errors like:
|
|
33
|
+
// "Parameter 'selection' implicitly has an 'any' type.""
|
|
34
|
+
const LINT_ERROR_CODE_ANY_PARAM = 7006;
|
|
35
|
+
|
|
36
|
+
// This code is for errors like:
|
|
37
|
+
// "Cannot find module 'd3' or its corresponding type declarations."
|
|
38
|
+
const LINT_ERROR_CODE_IMPORT = 2307;
|
|
39
|
+
|
|
40
|
+
// This code is for errors like:
|
|
41
|
+
// "Variable 'mic' implicitly has type 'any' in some locations where its type cannot be determined."
|
|
42
|
+
const LINT_ERROR_CODE_ANY_TYPE = 7034;
|
|
43
|
+
|
|
44
|
+
// "Element implicitly has an 'any' type because expression
|
|
45
|
+
// of type '"test"' can't be used to index type '{}'."
|
|
46
|
+
const LINT_ERROR_CODE_ANY_TYPE_KEYS = 7053;
|
|
47
|
+
|
|
48
|
+
// "Property 'id' does not exist on type { ... }"
|
|
49
|
+
// Happens on objects with dynamic keys.
|
|
50
|
+
// Not valid in TypeScript, but common in JavaScript.
|
|
51
|
+
const LINT_ERROR_CODE_NON_EXISTENT_PROPERTY = 2339;
|
|
52
|
+
|
|
53
|
+
// "Type 'Set<unknown>' can only be iterated through when using the '--downlevelIteration'
|
|
54
|
+
// flag or with a '--target' of 'es2015' or higher."
|
|
55
|
+
const LINT_ERROR_CODE_ITERATED_THROUGH = 2802;
|
|
56
|
+
|
|
57
|
+
// Argument of type '{ Month: string; High: number; Temp: number; Low: number; }'
|
|
58
|
+
// is not assignable to parameter of type 'never'.
|
|
59
|
+
const LINT_ERROR_CODE_ASSIGNABLE_TO_NEVER = 2345;
|
|
60
|
+
|
|
61
|
+
// Cannot find name 'd3'.
|
|
62
|
+
export const LINT_ERROR_CODE_CANNOT_FIND_NAME = 2304;
|
|
63
|
+
|
|
64
|
+
export const excludedErrorCodes = new Set([
|
|
65
|
+
LINT_ERROR_CODE_ANY,
|
|
66
|
+
LINT_ERROR_CODE_IMPORT,
|
|
67
|
+
LINT_ERROR_CODE_ANY_PARAM,
|
|
68
|
+
LINT_ERROR_CODE_ANY_TYPE,
|
|
69
|
+
LINT_ERROR_CODE_ANY_TYPE_KEYS,
|
|
70
|
+
LINT_ERROR_CODE_NON_EXISTENT_PROPERTY,
|
|
71
|
+
LINT_ERROR_CODE_ITERATED_THROUGH,
|
|
72
|
+
LINT_ERROR_CODE_ASSIGNABLE_TO_NEVER,
|
|
73
|
+
]);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getTSFileName } from './getTSFileName';
|
|
2
|
+
import { isTS } from './isTS';
|
|
3
|
+
import {
|
|
4
|
+
AutocompleteRequest,
|
|
5
|
+
AutocompleteResponse,
|
|
6
|
+
} from './requestTypes';
|
|
7
|
+
|
|
8
|
+
// This function is called when the worker receives a message with the type
|
|
9
|
+
// 'autocomplete-request'. It gets completions at the specified position in the
|
|
10
|
+
// file and sends them back to the main thread.
|
|
11
|
+
export const handleMessageAutocompleteRequest = async ({
|
|
12
|
+
debug,
|
|
13
|
+
data,
|
|
14
|
+
env,
|
|
15
|
+
setFile,
|
|
16
|
+
}) => {
|
|
17
|
+
if (debug) {
|
|
18
|
+
console.log('autocomplete-request message received');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Should not happen.
|
|
22
|
+
if (env === null) {
|
|
23
|
+
console.log('env is null');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Example of `data`:
|
|
28
|
+
// {
|
|
29
|
+
// "event": "autocomplete-request",
|
|
30
|
+
// "pos": 8,
|
|
31
|
+
// "location": "index.js",
|
|
32
|
+
// "requestId": "0.9090605799171392"
|
|
33
|
+
// }
|
|
34
|
+
|
|
35
|
+
const autocompleteRequest: AutocompleteRequest = data;
|
|
36
|
+
const { fileName, fileContent, position, requestId } =
|
|
37
|
+
autocompleteRequest;
|
|
38
|
+
|
|
39
|
+
const tsFileName = getTSFileName(fileName);
|
|
40
|
+
|
|
41
|
+
let completions = null;
|
|
42
|
+
if (isTS(tsFileName) && fileContent !== '') {
|
|
43
|
+
// Update the file in the file system to the
|
|
44
|
+
// absolute latest version. This is critical
|
|
45
|
+
// for correct completions.
|
|
46
|
+
setFile(tsFileName, fileContent);
|
|
47
|
+
|
|
48
|
+
completions =
|
|
49
|
+
env.languageService.getCompletionsAtPosition(
|
|
50
|
+
tsFileName,
|
|
51
|
+
position,
|
|
52
|
+
{},
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const autocompleteResponse: AutocompleteResponse = {
|
|
57
|
+
event: 'post-completions',
|
|
58
|
+
completions,
|
|
59
|
+
requestId,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
postMessage(autocompleteResponse);
|
|
63
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { Diagnostic } from '@codemirror/lint';
|
|
3
|
+
import {
|
|
4
|
+
LinterRequest,
|
|
5
|
+
LinterResponse,
|
|
6
|
+
} from './requestTypes';
|
|
7
|
+
import { getTSFileName } from './getTSFileName';
|
|
8
|
+
import { isTS } from './isTS';
|
|
9
|
+
import {
|
|
10
|
+
LINT_ERROR_CODE_CANNOT_FIND_NAME,
|
|
11
|
+
excludedErrorCodes,
|
|
12
|
+
} from './constants';
|
|
13
|
+
|
|
14
|
+
// Inspired by: https://stackblitz.com/edit/codemirror-6-typescript?file=client%2Findex.ts%3AL44-L44
|
|
15
|
+
const convertToCodeMirrorDiagnostic = (
|
|
16
|
+
tsErrors: ts.Diagnostic[],
|
|
17
|
+
): Array<Diagnostic> =>
|
|
18
|
+
tsErrors.map((tsError: ts.Diagnostic) => ({
|
|
19
|
+
from: tsError.start,
|
|
20
|
+
to: tsError.start + tsError.length,
|
|
21
|
+
severity: 'error',
|
|
22
|
+
message:
|
|
23
|
+
typeof tsError.messageText === 'string'
|
|
24
|
+
? tsError.messageText
|
|
25
|
+
: tsError.messageText.messageText,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
export const handleMessageLintRequest = async ({
|
|
29
|
+
debug,
|
|
30
|
+
data,
|
|
31
|
+
env,
|
|
32
|
+
setFile,
|
|
33
|
+
}) => {
|
|
34
|
+
if (debug) {
|
|
35
|
+
console.log('Lint Request');
|
|
36
|
+
}
|
|
37
|
+
const linterRequest: LinterRequest = data;
|
|
38
|
+
const { fileName, fileContent, requestId, allowGlobals } =
|
|
39
|
+
linterRequest;
|
|
40
|
+
|
|
41
|
+
const tsFileName = getTSFileName(fileName);
|
|
42
|
+
let tsErrors = [];
|
|
43
|
+
if (isTS(tsFileName) && fileContent !== '') {
|
|
44
|
+
// We are updating the server with the latest content
|
|
45
|
+
// when we autocomplete, so it's always up to date.
|
|
46
|
+
setFile(tsFileName, fileContent);
|
|
47
|
+
|
|
48
|
+
// Creates an array of diagnostic objects containing
|
|
49
|
+
// both semantic and syntactic diagnostics.
|
|
50
|
+
tsErrors = env.languageService
|
|
51
|
+
.getSemanticDiagnostics(tsFileName)
|
|
52
|
+
.concat(
|
|
53
|
+
env.languageService.getSyntacticDiagnostics(
|
|
54
|
+
tsFileName,
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (debug) {
|
|
59
|
+
console.log('tsErrors');
|
|
60
|
+
console.log(tsErrors);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
tsErrors = tsErrors.filter(
|
|
64
|
+
(error: { code: number }) => {
|
|
65
|
+
if (
|
|
66
|
+
allowGlobals &&
|
|
67
|
+
error.code === LINT_ERROR_CODE_CANNOT_FIND_NAME
|
|
68
|
+
) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return !excludedErrorCodes.has(error.code);
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
tsErrors = convertToCodeMirrorDiagnostic(tsErrors);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const linterResponse: LinterResponse = {
|
|
79
|
+
event: 'post-error-linter',
|
|
80
|
+
tsErrors,
|
|
81
|
+
requestId,
|
|
82
|
+
};
|
|
83
|
+
postMessage(linterResponse);
|
|
84
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { compilerOptions } from './constants';
|
|
3
|
+
|
|
4
|
+
// Handle the transpile-request event, which
|
|
5
|
+
// transpiles TypeScript to JavaScript.
|
|
6
|
+
export const handleMessageTranspileRequest = async ({
|
|
7
|
+
data,
|
|
8
|
+
}) => {
|
|
9
|
+
const tsCode = data.tsCode;
|
|
10
|
+
|
|
11
|
+
const jsCode = ts.transpileModule(tsCode, {
|
|
12
|
+
compilerOptions: compilerOptions,
|
|
13
|
+
}).outputText;
|
|
14
|
+
|
|
15
|
+
postMessage({
|
|
16
|
+
event: 'transpile-response',
|
|
17
|
+
jsCode,
|
|
18
|
+
fileId: data.fileId,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { File, Files, VZCodeContent } from '../../../types';
|
|
2
|
+
import { getTSFileName } from './getTSFileName';
|
|
3
|
+
import { isTS } from './isTS';
|
|
4
|
+
|
|
5
|
+
// Handle the update-content event, which
|
|
6
|
+
// updates the files as they change.
|
|
7
|
+
// This handles the cases where:
|
|
8
|
+
// * The file system is populated for the first time.
|
|
9
|
+
// * Files are edited by remote users.
|
|
10
|
+
export const handleMessageUpdateContent = async ({
|
|
11
|
+
debug,
|
|
12
|
+
data,
|
|
13
|
+
setFile,
|
|
14
|
+
}) => {
|
|
15
|
+
if (debug) {
|
|
16
|
+
console.log('update-content message received');
|
|
17
|
+
}
|
|
18
|
+
// Unpack the files
|
|
19
|
+
const content: VZCodeContent = data.details;
|
|
20
|
+
const files: Files = content.files;
|
|
21
|
+
|
|
22
|
+
// Iterate over the files
|
|
23
|
+
for (const fileId of Object.keys(files)) {
|
|
24
|
+
const file: File = files[fileId];
|
|
25
|
+
const { name, text } = file;
|
|
26
|
+
|
|
27
|
+
const tsFileName = getTSFileName(name);
|
|
28
|
+
|
|
29
|
+
if (!isTS(tsFileName)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setFile(tsFileName, text);
|
|
34
|
+
// TODO - Handle renaming files.
|
|
35
|
+
// TODO - Handle deleting files.
|
|
36
|
+
// TODO - Handle directories.
|
|
37
|
+
}
|
|
38
|
+
};
|