main
ts 137 lines 3.61 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 import {TextDocument} from 'vscode-languageserver-textdocument';
9 import {
10 CodeLens,
11 createConnection,
12 type InitializeParams,
13 type InitializeResult,
14 ProposedFeatures,
15 TextDocuments,
16 TextDocumentSyncKind,
17 } from 'vscode-languageserver/node';
18 import {compile, lastResult} from './compiler';
19 import {
20 type CompileSuccessEvent,
21 type LoggerEvent,
22 type PluginOptions,
23 defaultOptions,
24 } from 'babel-plugin-react-compiler';
25 import {babelLocationToRange, getRangeFirstCharacter} from './compiler/compat';
26
27 const SUPPORTED_LANGUAGE_IDS = new Set([
28 'javascript',
29 'javascriptreact',
30 'typescript',
31 'typescriptreact',
32 ]);
33
34 const connection = createConnection(ProposedFeatures.all);
35 const documents = new TextDocuments(TextDocument);
36
37 let compilerOptions: PluginOptions | null = null;
38 let compiledFns: Set<CompileSuccessEvent> = new Set();
39
40 connection.onInitialize((_params: InitializeParams) => {
41 compilerOptions = defaultOptions;
42 compilerOptions = {
43 ...compilerOptions,
44 logger: {
45 logEvent(_filename: string | null, event: LoggerEvent) {
46 connection.console.info(`Received event: ${event.kind}`);
47 connection.console.debug(JSON.stringify(event, null, 2));
48 if (event.kind === 'CompileSuccess') {
49 compiledFns.add(event);
50 }
51 },
52 },
53 };
54 const result: InitializeResult = {
55 capabilities: {
56 textDocumentSync: TextDocumentSyncKind.Full,
57 codeLensProvider: {resolveProvider: true},
58 },
59 };
60 return result;
61 });
62
63 connection.onInitialized(() => {
64 connection.console.log('initialized');
65 });
66
67 documents.onDidChangeContent(async event => {
68 connection.console.info(`Compiling: ${event.document.uri}`);
69 resetState();
70 if (SUPPORTED_LANGUAGE_IDS.has(event.document.languageId)) {
71 const text = event.document.getText();
72 try {
73 await compile({
74 text,
75 file: event.document.uri,
76 options: compilerOptions,
77 });
78 } catch (err) {
79 connection.console.error('Failed to compile');
80 if (err instanceof Error) {
81 connection.console.error(err.stack ?? err.message);
82 } else {
83 connection.console.error(JSON.stringify(err, null, 2));
84 }
85 }
86 }
87 });
88
89 connection.onDidChangeWatchedFiles(change => {
90 resetState();
91 connection.console.log(
92 change.changes.map(c => `File changed: ${c.uri}`).join('\n'),
93 );
94 });
95
96 connection.onCodeLens(params => {
97 connection.console.info(`Handling codelens for: ${params.textDocument.uri}`);
98 if (compiledFns.size === 0) {
99 return;
100 }
101 const lenses: Array<CodeLens> = [];
102 for (const compiled of compiledFns) {
103 if (compiled.fnLoc != null) {
104 const fnLoc = babelLocationToRange(compiled.fnLoc);
105 if (fnLoc === null) continue;
106 const lens = CodeLens.create(
107 getRangeFirstCharacter(fnLoc),
108 compiled.fnLoc,
109 );
110 if (lastResult?.code != null) {
111 lens.command = {
112 title: 'Optimized by React Compiler',
113 command: 'todo',
114 };
115 }
116 lenses.push(lens);
117 }
118 }
119 return lenses;
120 });
121
122 connection.onCodeLensResolve(lens => {
123 connection.console.info(`Resolving codelens for: ${JSON.stringify(lens)}`);
124 if (lastResult?.code != null) {
125 connection.console.log(lastResult.code);
126 }
127 return lens;
128 });
129
130 function resetState() {
131 connection.console.debug('Clearing state');
132 compiledFns.clear();
133 }
134
135 documents.listen(connection);
136 connection.listen();
137 connection.console.info(`React Analyzer running in node ${process.version}`);