@formspec/ts-plugin 0.1.0-alpha.21 → 0.1.0-alpha.22
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/README.md +56 -0
- package/dist/__tests__/downstream-authoring-host.test.d.ts +2 -0
- package/dist/__tests__/downstream-authoring-host.test.d.ts.map +1 -0
- package/dist/__tests__/semantic-service.test.d.ts +2 -0
- package/dist/__tests__/semantic-service.test.d.ts.map +1 -0
- package/dist/index.cjs +465 -257
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +479 -269
- package/dist/index.js.map +1 -1
- package/dist/perf-utils.d.ts +3 -0
- package/dist/perf-utils.d.ts.map +1 -0
- package/dist/reference-host-example.d.ts +14 -0
- package/dist/reference-host-example.d.ts.map +1 -0
- package/dist/semantic-service.d.ts +116 -0
- package/dist/semantic-service.d.ts.map +1 -0
- package/dist/service.d.ts +38 -34
- package/dist/service.d.ts.map +1 -1
- package/dist/ts-plugin.d.ts +458 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -30,16 +30,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION: () => import_protocol4.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
34
|
+
FORMSPEC_ANALYSIS_SCHEMA_VERSION: () => import_protocol4.FORMSPEC_ANALYSIS_SCHEMA_VERSION,
|
|
35
|
+
FormSpecPluginService: () => FormSpecPluginService,
|
|
36
|
+
FormSpecSemanticService: () => FormSpecSemanticService,
|
|
37
|
+
createLanguageServiceProxy: () => createLanguageServiceProxy,
|
|
33
38
|
init: () => init
|
|
34
39
|
});
|
|
35
40
|
module.exports = __toCommonJS(index_exports);
|
|
41
|
+
var import_protocol4 = require("@formspec/analysis/protocol");
|
|
36
42
|
|
|
37
43
|
// src/service.ts
|
|
38
44
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
39
45
|
var import_node_net = __toESM(require("net"), 1);
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
var
|
|
46
|
+
var ts2 = require("typescript");
|
|
47
|
+
var import_protocol3 = require("@formspec/analysis/protocol");
|
|
48
|
+
var import_internal2 = require("@formspec/analysis/internal");
|
|
43
49
|
|
|
44
50
|
// src/workspace.ts
|
|
45
51
|
var import_node_os = __toESM(require("os"), 1);
|
|
@@ -93,112 +99,139 @@ function sanitizeScopeSegment(value) {
|
|
|
93
99
|
return sanitized.length > 0 ? sanitized : "formspec";
|
|
94
100
|
}
|
|
95
101
|
|
|
102
|
+
// src/semantic-service.ts
|
|
103
|
+
var ts = require("typescript");
|
|
104
|
+
var import_protocol2 = require("@formspec/analysis/protocol");
|
|
105
|
+
var import_internal = require("@formspec/analysis/internal");
|
|
106
|
+
|
|
96
107
|
// src/constants.ts
|
|
97
108
|
var FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES = 256 * 1024;
|
|
98
109
|
var FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS = 3e4;
|
|
99
110
|
var FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS = 50;
|
|
100
111
|
var FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS = 250;
|
|
101
|
-
var FORM_SPEC_PLUGIN_PERFORMANCE_EVENT = {
|
|
102
|
-
handleQuery: "plugin.handleQuery"
|
|
103
|
-
};
|
|
104
112
|
|
|
105
|
-
// src/
|
|
106
|
-
|
|
113
|
+
// src/perf-utils.ts
|
|
114
|
+
function formatPerformanceEvent(event) {
|
|
115
|
+
const detailEntries = Object.entries(event.detail ?? {}).map(([key, value]) => `${key}=${String(value)}`).join(" ");
|
|
116
|
+
return `${event.durationMs.toFixed(1)}ms ${event.name}${detailEntries === "" ? "" : ` ${detailEntries}`}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/semantic-service.ts
|
|
120
|
+
var STATS_ONLY_EVENT_NAMES = /* @__PURE__ */ new Set([
|
|
121
|
+
"analysis.syntheticCheckBatch.cacheHit",
|
|
122
|
+
"analysis.narrowSyntheticCheckBatch.cacheHit",
|
|
123
|
+
"analysis.syntheticCheckBatch.cacheMiss",
|
|
124
|
+
"analysis.narrowSyntheticCheckBatch.cacheMiss",
|
|
125
|
+
"analysis.syntheticCheckBatch.createProgram",
|
|
126
|
+
"analysis.narrowSyntheticCheckBatch.createProgram"
|
|
127
|
+
]);
|
|
128
|
+
var StatsOnlyPerformanceRecorder = class {
|
|
129
|
+
mutableEvents = [];
|
|
130
|
+
get events() {
|
|
131
|
+
return this.mutableEvents;
|
|
132
|
+
}
|
|
133
|
+
measure(name, detail, callback) {
|
|
134
|
+
const result = callback();
|
|
135
|
+
if (STATS_ONLY_EVENT_NAMES.has(name)) {
|
|
136
|
+
this.mutableEvents.push({
|
|
137
|
+
name,
|
|
138
|
+
durationMs: 0,
|
|
139
|
+
...detail === void 0 ? {} : { detail }
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
record(event) {
|
|
145
|
+
if (STATS_ONLY_EVENT_NAMES.has(event.name)) {
|
|
146
|
+
this.mutableEvents.push(event);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
var FormSpecSemanticService = class {
|
|
107
151
|
constructor(options) {
|
|
108
152
|
this.options = options;
|
|
109
|
-
this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);
|
|
110
|
-
this.manifest = createFormSpecAnalysisManifest(
|
|
111
|
-
options.workspaceRoot,
|
|
112
|
-
options.typescriptVersion,
|
|
113
|
-
Date.now()
|
|
114
|
-
);
|
|
115
153
|
}
|
|
116
|
-
manifest;
|
|
117
|
-
runtimePaths;
|
|
118
154
|
snapshotCache = /* @__PURE__ */ new Map();
|
|
119
155
|
refreshTimers = /* @__PURE__ */ new Map();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
156
|
+
stats = {
|
|
157
|
+
queryTotals: {
|
|
158
|
+
completion: 0,
|
|
159
|
+
hover: 0,
|
|
160
|
+
diagnostics: 0,
|
|
161
|
+
fileSnapshot: 0
|
|
162
|
+
},
|
|
163
|
+
queryPathTotals: {
|
|
164
|
+
diagnostics: { cold: 0, warm: 0 },
|
|
165
|
+
fileSnapshot: { cold: 0, warm: 0 }
|
|
166
|
+
},
|
|
167
|
+
fileSnapshotCacheHits: 0,
|
|
168
|
+
fileSnapshotCacheMisses: 0,
|
|
169
|
+
syntheticBatchCacheHits: 0,
|
|
170
|
+
syntheticBatchCacheMisses: 0,
|
|
171
|
+
syntheticCompileCount: 0,
|
|
172
|
+
syntheticCompileApplications: 0
|
|
173
|
+
};
|
|
174
|
+
/** Resolves semantic completion context for a comment cursor position. */
|
|
175
|
+
getCompletionContext(filePath, offset) {
|
|
176
|
+
this.stats.queryTotals.completion += 1;
|
|
177
|
+
return this.runMeasured(
|
|
178
|
+
"semantic.getCompletionContext",
|
|
179
|
+
{ filePath, offset },
|
|
180
|
+
(performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
|
|
181
|
+
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
182
|
+
sourceHash: context.sourceHash,
|
|
183
|
+
context: (0, import_internal.serializeCompletionContext)(
|
|
184
|
+
(0, import_internal.getSemanticCommentCompletionContextAtOffset)(context.sourceFile.text, offset, {
|
|
185
|
+
checker: context.checker,
|
|
186
|
+
...context.placement === null ? {} : { placement: context.placement },
|
|
187
|
+
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
188
|
+
})
|
|
189
|
+
)
|
|
190
|
+
}))
|
|
191
|
+
);
|
|
123
192
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
const newlineIndex = buffer.indexOf("\n");
|
|
155
|
-
if (newlineIndex < 0) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const payload = buffer.slice(0, newlineIndex);
|
|
159
|
-
const remaining = buffer.slice(newlineIndex + 1);
|
|
160
|
-
if (remaining.trim().length > 0) {
|
|
161
|
-
this.options.logger?.info(
|
|
162
|
-
`[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
buffer = remaining;
|
|
166
|
-
this.respondToSocket(socket, payload);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
await new Promise((resolve, reject) => {
|
|
170
|
-
const handleError = (error) => {
|
|
171
|
-
reject(error);
|
|
193
|
+
/** Resolves semantic hover payload for a comment cursor position. */
|
|
194
|
+
getHover(filePath, offset) {
|
|
195
|
+
this.stats.queryTotals.hover += 1;
|
|
196
|
+
return this.runMeasured(
|
|
197
|
+
"semantic.getHover",
|
|
198
|
+
{ filePath, offset },
|
|
199
|
+
(performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
|
|
200
|
+
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
201
|
+
sourceHash: context.sourceHash,
|
|
202
|
+
hover: (0, import_internal.serializeHoverInfo)(
|
|
203
|
+
(0, import_internal.getCommentHoverInfoAtOffset)(context.sourceFile.text, offset, {
|
|
204
|
+
checker: context.checker,
|
|
205
|
+
...context.placement === null ? {} : { placement: context.placement },
|
|
206
|
+
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
207
|
+
})
|
|
208
|
+
)
|
|
209
|
+
}))
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
/** Returns canonical FormSpec diagnostics for a file in the current host program. */
|
|
213
|
+
getDiagnostics(filePath) {
|
|
214
|
+
this.stats.queryTotals.diagnostics += 1;
|
|
215
|
+
return this.runMeasured("semantic.getDiagnostics", { filePath }, (performance2) => {
|
|
216
|
+
const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance2);
|
|
217
|
+
this.recordQueryPath("diagnostics", cacheState);
|
|
218
|
+
return {
|
|
219
|
+
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
220
|
+
sourceHash: snapshot.sourceHash,
|
|
221
|
+
diagnostics: snapshot.diagnostics
|
|
172
222
|
};
|
|
173
|
-
this.server?.once("error", handleError);
|
|
174
|
-
this.server?.listen(this.runtimePaths.endpoint.address, () => {
|
|
175
|
-
this.server?.off("error", handleError);
|
|
176
|
-
resolve();
|
|
177
|
-
});
|
|
178
223
|
});
|
|
179
|
-
await this.writeManifest();
|
|
180
224
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (server?.listening === true) {
|
|
190
|
-
await new Promise((resolve, reject) => {
|
|
191
|
-
server.close((error) => {
|
|
192
|
-
if (error === void 0) {
|
|
193
|
-
resolve();
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
reject(error);
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
await this.cleanupRuntimeArtifacts();
|
|
225
|
+
/** Returns the full serialized semantic snapshot for a file. */
|
|
226
|
+
getFileSnapshot(filePath) {
|
|
227
|
+
this.stats.queryTotals.fileSnapshot += 1;
|
|
228
|
+
return this.runMeasured("semantic.getFileSnapshot", { filePath }, (performance2) => {
|
|
229
|
+
const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance2);
|
|
230
|
+
this.recordQueryPath("fileSnapshot", cacheState);
|
|
231
|
+
return snapshot;
|
|
232
|
+
});
|
|
201
233
|
}
|
|
234
|
+
/** Schedules a debounced background refresh for the file snapshot cache. */
|
|
202
235
|
scheduleSnapshotRefresh(filePath) {
|
|
203
236
|
const existing = this.refreshTimers.get(filePath);
|
|
204
237
|
if (existing !== void 0) {
|
|
@@ -206,7 +239,7 @@ var FormSpecPluginService = class {
|
|
|
206
239
|
}
|
|
207
240
|
const timer = setTimeout(() => {
|
|
208
241
|
try {
|
|
209
|
-
this.getFileSnapshot(filePath
|
|
242
|
+
this.getFileSnapshot(filePath);
|
|
210
243
|
} catch (error) {
|
|
211
244
|
this.options.logger?.info(
|
|
212
245
|
`[FormSpec] Failed to refresh semantic snapshot for ${filePath}: ${String(error)}`
|
|
@@ -214,76 +247,58 @@ var FormSpecPluginService = class {
|
|
|
214
247
|
}
|
|
215
248
|
this.refreshTimers.delete(filePath);
|
|
216
249
|
}, this.options.snapshotDebounceMs ?? FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS);
|
|
250
|
+
timer.unref();
|
|
217
251
|
this.refreshTimers.set(filePath, timer);
|
|
218
252
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
FORM_SPEC_PLUGIN_PERFORMANCE_EVENT.handleQuery,
|
|
224
|
-
{
|
|
225
|
-
kind: query.kind,
|
|
226
|
-
...query.kind === "health" ? {} : { filePath: query.filePath }
|
|
227
|
-
},
|
|
228
|
-
() => this.executeQuery(query, performance)
|
|
229
|
-
);
|
|
230
|
-
if (performance !== void 0) {
|
|
231
|
-
this.logPerformanceEvents(performance.events);
|
|
232
|
-
}
|
|
233
|
-
return response;
|
|
234
|
-
}
|
|
235
|
-
respondToSocket(socket, payload) {
|
|
236
|
-
try {
|
|
237
|
-
const query = JSON.parse(payload);
|
|
238
|
-
if (!(0, import_protocol2.isFormSpecSemanticQuery)(query)) {
|
|
239
|
-
throw new Error("Invalid FormSpec semantic query payload");
|
|
240
|
-
}
|
|
241
|
-
const response = this.handleQuery(query);
|
|
242
|
-
socket.end(`${JSON.stringify(response)}
|
|
243
|
-
`);
|
|
244
|
-
} catch (error) {
|
|
245
|
-
socket.end(
|
|
246
|
-
`${JSON.stringify({
|
|
247
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
248
|
-
kind: "error",
|
|
249
|
-
error: error instanceof Error ? error.message : String(error)
|
|
250
|
-
})}
|
|
251
|
-
`
|
|
252
|
-
);
|
|
253
|
+
/** Clears pending timers and cached semantic snapshots. */
|
|
254
|
+
dispose() {
|
|
255
|
+
for (const timer of this.refreshTimers.values()) {
|
|
256
|
+
clearTimeout(timer);
|
|
253
257
|
}
|
|
258
|
+
this.refreshTimers.clear();
|
|
259
|
+
this.snapshotCache.clear();
|
|
254
260
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
261
|
+
/** Returns a copy of the current performance and cache counters. */
|
|
262
|
+
getStats() {
|
|
263
|
+
return {
|
|
264
|
+
queryTotals: { ...this.stats.queryTotals },
|
|
265
|
+
queryPathTotals: {
|
|
266
|
+
diagnostics: { ...this.stats.queryPathTotals.diagnostics },
|
|
267
|
+
fileSnapshot: { ...this.stats.queryPathTotals.fileSnapshot }
|
|
268
|
+
},
|
|
269
|
+
fileSnapshotCacheHits: this.stats.fileSnapshotCacheHits,
|
|
270
|
+
fileSnapshotCacheMisses: this.stats.fileSnapshotCacheMisses,
|
|
271
|
+
syntheticBatchCacheHits: this.stats.syntheticBatchCacheHits,
|
|
272
|
+
syntheticBatchCacheMisses: this.stats.syntheticBatchCacheMisses,
|
|
273
|
+
syntheticCompileCount: this.stats.syntheticCompileCount,
|
|
274
|
+
syntheticCompileApplications: this.stats.syntheticCompileApplications
|
|
275
|
+
};
|
|
260
276
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
277
|
+
runMeasured(name, detail, fn) {
|
|
278
|
+
const performance2 = this.options.enablePerformanceLogging === true ? (0, import_internal.createFormSpecPerformanceRecorder)() : new StatsOnlyPerformanceRecorder();
|
|
279
|
+
const result = (0, import_internal.optionalMeasure)(performance2, name, detail, () => fn(performance2));
|
|
280
|
+
this.updateStatsFromPerformanceEvents(performance2.events);
|
|
281
|
+
if (this.options.enablePerformanceLogging === true) {
|
|
282
|
+
this.logPerformanceEvents(name, performance2.events);
|
|
265
283
|
}
|
|
284
|
+
return result;
|
|
266
285
|
}
|
|
267
|
-
withCommentQueryContext(filePath, offset,
|
|
286
|
+
withCommentQueryContext(filePath, offset, performance2, handler) {
|
|
268
287
|
return (0, import_internal.optionalMeasure)(
|
|
269
|
-
|
|
270
|
-
"
|
|
288
|
+
performance2,
|
|
289
|
+
"semantic.resolveCommentQueryContext",
|
|
271
290
|
{
|
|
272
291
|
filePath,
|
|
273
292
|
offset
|
|
274
293
|
},
|
|
275
294
|
() => {
|
|
276
|
-
const environment = this.getSourceEnvironment(filePath,
|
|
295
|
+
const environment = this.getSourceEnvironment(filePath, performance2);
|
|
277
296
|
if (environment === null) {
|
|
278
|
-
return
|
|
279
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
280
|
-
kind: "error",
|
|
281
|
-
error: `Unable to resolve TypeScript source file for ${filePath}`
|
|
282
|
-
};
|
|
297
|
+
return null;
|
|
283
298
|
}
|
|
284
299
|
const declaration = (0, import_internal.optionalMeasure)(
|
|
285
|
-
|
|
286
|
-
"
|
|
300
|
+
performance2,
|
|
301
|
+
"semantic.findDeclarationForCommentOffset",
|
|
287
302
|
{
|
|
288
303
|
filePath,
|
|
289
304
|
offset
|
|
@@ -291,14 +306,14 @@ var FormSpecPluginService = class {
|
|
|
291
306
|
() => (0, import_internal.findDeclarationForCommentOffset)(environment.sourceFile, offset)
|
|
292
307
|
);
|
|
293
308
|
const placement = declaration === null ? null : (0, import_internal.optionalMeasure)(
|
|
294
|
-
|
|
295
|
-
"
|
|
309
|
+
performance2,
|
|
310
|
+
"semantic.resolveDeclarationPlacement",
|
|
296
311
|
void 0,
|
|
297
312
|
() => (0, import_internal.resolveDeclarationPlacement)(declaration)
|
|
298
313
|
);
|
|
299
314
|
const subjectType = declaration === null ? void 0 : (0, import_internal.optionalMeasure)(
|
|
300
|
-
|
|
301
|
-
"
|
|
315
|
+
performance2,
|
|
316
|
+
"semantic.getSubjectType",
|
|
302
317
|
void 0,
|
|
303
318
|
() => (0, import_internal.getSubjectType)(declaration, environment.checker)
|
|
304
319
|
);
|
|
@@ -311,11 +326,12 @@ var FormSpecPluginService = class {
|
|
|
311
326
|
}
|
|
312
327
|
);
|
|
313
328
|
}
|
|
314
|
-
|
|
329
|
+
getFileSnapshotWithCacheState(filePath, performance2) {
|
|
315
330
|
const startedAt = (0, import_internal.getFormSpecPerformanceNow)();
|
|
316
|
-
const environment = this.getSourceEnvironment(filePath,
|
|
331
|
+
const environment = this.getSourceEnvironment(filePath, performance2);
|
|
317
332
|
if (environment === null) {
|
|
318
|
-
|
|
333
|
+
this.stats.fileSnapshotCacheMisses += 1;
|
|
334
|
+
const snapshot2 = {
|
|
319
335
|
filePath,
|
|
320
336
|
sourceHash: "",
|
|
321
337
|
generatedAt: this.getNow().toISOString(),
|
|
@@ -323,131 +339,83 @@ var FormSpecPluginService = class {
|
|
|
323
339
|
diagnostics: [
|
|
324
340
|
{
|
|
325
341
|
code: "MISSING_SOURCE_FILE",
|
|
342
|
+
category: "infrastructure",
|
|
326
343
|
message: `Unable to resolve TypeScript source file for ${filePath}`,
|
|
327
344
|
range: { start: 0, end: 0 },
|
|
328
|
-
severity: "warning"
|
|
345
|
+
severity: "warning",
|
|
346
|
+
relatedLocations: [],
|
|
347
|
+
data: {
|
|
348
|
+
filePath
|
|
349
|
+
}
|
|
329
350
|
}
|
|
330
351
|
]
|
|
331
352
|
};
|
|
332
|
-
|
|
333
|
-
name: "
|
|
353
|
+
performance2.record({
|
|
354
|
+
name: "semantic.getFileSnapshot.result",
|
|
334
355
|
durationMs: (0, import_internal.getFormSpecPerformanceNow)() - startedAt,
|
|
335
356
|
detail: {
|
|
336
357
|
filePath,
|
|
337
358
|
cache: "missing-source"
|
|
338
359
|
}
|
|
339
360
|
});
|
|
340
|
-
return
|
|
361
|
+
return {
|
|
362
|
+
snapshot: snapshot2,
|
|
363
|
+
cacheState: "missing-source"
|
|
364
|
+
};
|
|
341
365
|
}
|
|
342
366
|
const cached = this.snapshotCache.get(filePath);
|
|
343
367
|
if (cached?.sourceHash === environment.sourceHash) {
|
|
344
|
-
|
|
345
|
-
|
|
368
|
+
this.stats.fileSnapshotCacheHits += 1;
|
|
369
|
+
performance2.record({
|
|
370
|
+
name: "semantic.getFileSnapshot.result",
|
|
346
371
|
durationMs: (0, import_internal.getFormSpecPerformanceNow)() - startedAt,
|
|
347
372
|
detail: {
|
|
348
373
|
filePath,
|
|
349
374
|
cache: "hit"
|
|
350
375
|
}
|
|
351
376
|
});
|
|
352
|
-
return
|
|
377
|
+
return {
|
|
378
|
+
snapshot: cached.snapshot,
|
|
379
|
+
cacheState: "hit"
|
|
380
|
+
};
|
|
353
381
|
}
|
|
382
|
+
this.stats.fileSnapshotCacheMisses += 1;
|
|
354
383
|
const snapshot = (0, import_internal.buildFormSpecAnalysisFileSnapshot)(environment.sourceFile, {
|
|
355
384
|
checker: environment.checker,
|
|
356
385
|
now: () => this.getNow(),
|
|
357
|
-
|
|
386
|
+
performance: performance2
|
|
358
387
|
});
|
|
359
388
|
this.snapshotCache.set(filePath, {
|
|
360
389
|
sourceHash: environment.sourceHash,
|
|
361
390
|
snapshot
|
|
362
391
|
});
|
|
363
|
-
|
|
364
|
-
name: "
|
|
392
|
+
performance2.record({
|
|
393
|
+
name: "semantic.getFileSnapshot.result",
|
|
365
394
|
durationMs: (0, import_internal.getFormSpecPerformanceNow)() - startedAt,
|
|
366
395
|
detail: {
|
|
367
396
|
filePath,
|
|
368
397
|
cache: "miss"
|
|
369
398
|
}
|
|
370
399
|
});
|
|
371
|
-
return
|
|
400
|
+
return {
|
|
401
|
+
snapshot,
|
|
402
|
+
cacheState: "miss"
|
|
403
|
+
};
|
|
372
404
|
}
|
|
373
405
|
getNow() {
|
|
374
406
|
return this.options.now?.() ?? /* @__PURE__ */ new Date();
|
|
375
407
|
}
|
|
376
|
-
|
|
377
|
-
switch (query.kind) {
|
|
378
|
-
case "health":
|
|
379
|
-
return {
|
|
380
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
381
|
-
kind: "health",
|
|
382
|
-
manifest: this.manifest
|
|
383
|
-
};
|
|
384
|
-
case "completion":
|
|
385
|
-
return this.withCommentQueryContext(
|
|
386
|
-
query.filePath,
|
|
387
|
-
query.offset,
|
|
388
|
-
(context) => ({
|
|
389
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
390
|
-
kind: "completion",
|
|
391
|
-
sourceHash: context.sourceHash,
|
|
392
|
-
context: (0, import_internal.serializeCompletionContext)(
|
|
393
|
-
(0, import_internal.getSemanticCommentCompletionContextAtOffset)(context.sourceFile.text, query.offset, {
|
|
394
|
-
checker: context.checker,
|
|
395
|
-
...context.placement === null ? {} : { placement: context.placement },
|
|
396
|
-
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
397
|
-
})
|
|
398
|
-
)
|
|
399
|
-
}),
|
|
400
|
-
performance
|
|
401
|
-
);
|
|
402
|
-
case "hover":
|
|
403
|
-
return this.withCommentQueryContext(
|
|
404
|
-
query.filePath,
|
|
405
|
-
query.offset,
|
|
406
|
-
(context) => ({
|
|
407
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
408
|
-
kind: "hover",
|
|
409
|
-
sourceHash: context.sourceHash,
|
|
410
|
-
hover: (0, import_internal.serializeHoverInfo)(
|
|
411
|
-
(0, import_internal.getCommentHoverInfoAtOffset)(context.sourceFile.text, query.offset, {
|
|
412
|
-
checker: context.checker,
|
|
413
|
-
...context.placement === null ? {} : { placement: context.placement },
|
|
414
|
-
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
415
|
-
})
|
|
416
|
-
)
|
|
417
|
-
}),
|
|
418
|
-
performance
|
|
419
|
-
);
|
|
420
|
-
case "diagnostics": {
|
|
421
|
-
const snapshot = this.getFileSnapshot(query.filePath, performance);
|
|
422
|
-
return {
|
|
423
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
424
|
-
kind: "diagnostics",
|
|
425
|
-
sourceHash: snapshot.sourceHash,
|
|
426
|
-
diagnostics: snapshot.diagnostics
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
case "file-snapshot":
|
|
430
|
-
return {
|
|
431
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
432
|
-
kind: "file-snapshot",
|
|
433
|
-
snapshot: this.getFileSnapshot(query.filePath, performance)
|
|
434
|
-
};
|
|
435
|
-
default: {
|
|
436
|
-
throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
getSourceEnvironment(filePath, performance) {
|
|
408
|
+
getSourceEnvironment(filePath, performance2) {
|
|
441
409
|
return (0, import_internal.optionalMeasure)(
|
|
442
|
-
|
|
443
|
-
"
|
|
410
|
+
performance2,
|
|
411
|
+
"semantic.getSourceEnvironment",
|
|
444
412
|
{
|
|
445
413
|
filePath
|
|
446
414
|
},
|
|
447
415
|
() => {
|
|
448
416
|
const program = (0, import_internal.optionalMeasure)(
|
|
449
|
-
|
|
450
|
-
"
|
|
417
|
+
performance2,
|
|
418
|
+
"semantic.sourceEnvironment.getProgram",
|
|
451
419
|
void 0,
|
|
452
420
|
() => this.options.getProgram()
|
|
453
421
|
);
|
|
@@ -455,8 +423,8 @@ var FormSpecPluginService = class {
|
|
|
455
423
|
return null;
|
|
456
424
|
}
|
|
457
425
|
const sourceFile = (0, import_internal.optionalMeasure)(
|
|
458
|
-
|
|
459
|
-
"
|
|
426
|
+
performance2,
|
|
427
|
+
"semantic.sourceEnvironment.getSourceFile",
|
|
460
428
|
void 0,
|
|
461
429
|
() => program.getSourceFile(filePath)
|
|
462
430
|
);
|
|
@@ -464,14 +432,14 @@ var FormSpecPluginService = class {
|
|
|
464
432
|
return null;
|
|
465
433
|
}
|
|
466
434
|
const checker = (0, import_internal.optionalMeasure)(
|
|
467
|
-
|
|
468
|
-
"
|
|
435
|
+
performance2,
|
|
436
|
+
"semantic.sourceEnvironment.getTypeChecker",
|
|
469
437
|
void 0,
|
|
470
438
|
() => program.getTypeChecker()
|
|
471
439
|
);
|
|
472
440
|
const sourceHash = (0, import_internal.optionalMeasure)(
|
|
473
|
-
|
|
474
|
-
"
|
|
441
|
+
performance2,
|
|
442
|
+
"semantic.sourceEnvironment.computeTextHash",
|
|
475
443
|
void 0,
|
|
476
444
|
() => (0, import_protocol2.computeFormSpecTextHash)(sourceFile.text)
|
|
477
445
|
);
|
|
@@ -483,19 +451,38 @@ var FormSpecPluginService = class {
|
|
|
483
451
|
}
|
|
484
452
|
);
|
|
485
453
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
454
|
+
recordQueryPath(kind, cacheState) {
|
|
455
|
+
if (cacheState === "hit") {
|
|
456
|
+
this.stats.queryPathTotals[kind].warm += 1;
|
|
489
457
|
return;
|
|
490
458
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
459
|
+
this.stats.queryPathTotals[kind].cold += 1;
|
|
460
|
+
}
|
|
461
|
+
updateStatsFromPerformanceEvents(events) {
|
|
462
|
+
for (const event of events) {
|
|
463
|
+
if (event.name === "analysis.syntheticCheckBatch.cacheHit" || event.name === "analysis.narrowSyntheticCheckBatch.cacheHit") {
|
|
464
|
+
this.stats.syntheticBatchCacheHits += 1;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (event.name === "analysis.syntheticCheckBatch.cacheMiss" || event.name === "analysis.narrowSyntheticCheckBatch.cacheMiss") {
|
|
468
|
+
this.stats.syntheticBatchCacheMisses += 1;
|
|
469
|
+
const applicationCount = event.detail?.["applicationCount"];
|
|
470
|
+
if (typeof applicationCount === "number") {
|
|
471
|
+
this.stats.syntheticCompileApplications += applicationCount;
|
|
472
|
+
}
|
|
473
|
+
continue;
|
|
497
474
|
}
|
|
475
|
+
if (event.name === "analysis.syntheticCheckBatch.createProgram" || event.name === "analysis.narrowSyntheticCheckBatch.createProgram") {
|
|
476
|
+
this.stats.syntheticCompileCount += 1;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
logPerformanceEvents(rootEventName, events) {
|
|
481
|
+
const logger = this.options.logger;
|
|
482
|
+
if (logger === void 0 || events.length === 0) {
|
|
483
|
+
return;
|
|
498
484
|
}
|
|
485
|
+
const rootEvent = [...events].reverse().find((event) => event.name === rootEventName);
|
|
499
486
|
if (rootEvent === void 0) {
|
|
500
487
|
return;
|
|
501
488
|
}
|
|
@@ -503,18 +490,234 @@ var FormSpecPluginService = class {
|
|
|
503
490
|
if (rootEvent.durationMs < thresholdMs) {
|
|
504
491
|
return;
|
|
505
492
|
}
|
|
506
|
-
const sortedHotspots = [...events].filter((event) => event.name !==
|
|
493
|
+
const sortedHotspots = [...events].filter((event) => event.name !== rootEventName).sort((left, right) => right.durationMs - left.durationMs).slice(0, 8);
|
|
507
494
|
const lines = [
|
|
508
|
-
`[FormSpec][perf] ${
|
|
495
|
+
`[FormSpec][perf] ${formatPerformanceEvent(rootEvent)}`,
|
|
509
496
|
...sortedHotspots.map((event) => ` ${formatPerformanceEvent(event)}`)
|
|
510
497
|
];
|
|
511
498
|
logger.info(lines.join("\n"));
|
|
512
499
|
}
|
|
513
500
|
};
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
501
|
+
|
|
502
|
+
// src/service.ts
|
|
503
|
+
var FormSpecPluginService = class {
|
|
504
|
+
constructor(options) {
|
|
505
|
+
this.options = options;
|
|
506
|
+
this.semanticService = new FormSpecSemanticService(options);
|
|
507
|
+
this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);
|
|
508
|
+
this.manifest = createFormSpecAnalysisManifest(
|
|
509
|
+
options.workspaceRoot,
|
|
510
|
+
options.typescriptVersion,
|
|
511
|
+
Date.now()
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
manifest;
|
|
515
|
+
runtimePaths;
|
|
516
|
+
semanticService;
|
|
517
|
+
server = null;
|
|
518
|
+
getManifest() {
|
|
519
|
+
return this.manifest;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Returns the underlying semantic service used by this reference wrapper.
|
|
523
|
+
*
|
|
524
|
+
* @public
|
|
525
|
+
*/
|
|
526
|
+
getSemanticService() {
|
|
527
|
+
return this.semanticService;
|
|
528
|
+
}
|
|
529
|
+
async start() {
|
|
530
|
+
if (this.server !== null) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
await import_promises.default.mkdir(this.runtimePaths.runtimeDirectory, { recursive: true });
|
|
534
|
+
if (this.runtimePaths.endpoint.kind === "unix-socket") {
|
|
535
|
+
await import_promises.default.rm(this.runtimePaths.endpoint.address, { force: true });
|
|
536
|
+
}
|
|
537
|
+
this.server = import_node_net.default.createServer((socket) => {
|
|
538
|
+
let buffer = "";
|
|
539
|
+
socket.setEncoding("utf8");
|
|
540
|
+
socket.setTimeout(FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS, () => {
|
|
541
|
+
this.options.logger?.info(
|
|
542
|
+
`[FormSpec] Closing idle semantic query socket for ${this.runtimePaths.workspaceRoot}`
|
|
543
|
+
);
|
|
544
|
+
socket.destroy();
|
|
545
|
+
});
|
|
546
|
+
socket.on("data", (chunk) => {
|
|
547
|
+
buffer += String(chunk);
|
|
548
|
+
if (buffer.length > FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES) {
|
|
549
|
+
socket.end(
|
|
550
|
+
`${JSON.stringify({
|
|
551
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
552
|
+
kind: "error",
|
|
553
|
+
error: `FormSpec semantic query exceeded ${String(FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES)} bytes`
|
|
554
|
+
})}
|
|
555
|
+
`
|
|
556
|
+
);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
560
|
+
if (newlineIndex < 0) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const payload = buffer.slice(0, newlineIndex);
|
|
564
|
+
const remaining = buffer.slice(newlineIndex + 1);
|
|
565
|
+
if (remaining.trim().length > 0) {
|
|
566
|
+
this.options.logger?.info(
|
|
567
|
+
`[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
buffer = remaining;
|
|
571
|
+
this.respondToSocket(socket, payload);
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
await new Promise((resolve, reject) => {
|
|
575
|
+
const handleError = (error) => {
|
|
576
|
+
reject(error);
|
|
577
|
+
};
|
|
578
|
+
this.server?.once("error", handleError);
|
|
579
|
+
this.server?.listen(this.runtimePaths.endpoint.address, () => {
|
|
580
|
+
this.server?.off("error", handleError);
|
|
581
|
+
resolve();
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
await this.writeManifest();
|
|
585
|
+
}
|
|
586
|
+
async stop() {
|
|
587
|
+
this.semanticService.dispose();
|
|
588
|
+
const server = this.server;
|
|
589
|
+
this.server = null;
|
|
590
|
+
if (server?.listening === true) {
|
|
591
|
+
await new Promise((resolve, reject) => {
|
|
592
|
+
server.close((error) => {
|
|
593
|
+
if (error === void 0) {
|
|
594
|
+
resolve();
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
reject(error);
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
await this.cleanupRuntimeArtifacts();
|
|
602
|
+
}
|
|
603
|
+
scheduleSnapshotRefresh(filePath) {
|
|
604
|
+
this.semanticService.scheduleSnapshotRefresh(filePath);
|
|
605
|
+
}
|
|
606
|
+
handleQuery(query) {
|
|
607
|
+
if (this.options.enablePerformanceLogging === true) {
|
|
608
|
+
const startedAt = performance.now();
|
|
609
|
+
const response = this.executeQuery(query);
|
|
610
|
+
this.logQueryDuration(query, performance.now() - startedAt);
|
|
611
|
+
return response;
|
|
612
|
+
}
|
|
613
|
+
return this.executeQuery(query);
|
|
614
|
+
}
|
|
615
|
+
executeQuery(query) {
|
|
616
|
+
switch (query.kind) {
|
|
617
|
+
case "health":
|
|
618
|
+
return {
|
|
619
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
620
|
+
kind: "health",
|
|
621
|
+
manifest: this.manifest
|
|
622
|
+
};
|
|
623
|
+
case "completion": {
|
|
624
|
+
const result = this.semanticService.getCompletionContext(query.filePath, query.offset);
|
|
625
|
+
if (result === null) {
|
|
626
|
+
return {
|
|
627
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
628
|
+
kind: "error",
|
|
629
|
+
error: `Unable to resolve TypeScript source file for ${query.filePath}`
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
...result,
|
|
634
|
+
kind: "completion"
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
case "hover": {
|
|
638
|
+
const result = this.semanticService.getHover(query.filePath, query.offset);
|
|
639
|
+
if (result === null) {
|
|
640
|
+
return {
|
|
641
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
642
|
+
kind: "error",
|
|
643
|
+
error: `Unable to resolve TypeScript source file for ${query.filePath}`
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
...result,
|
|
648
|
+
kind: "hover"
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
case "diagnostics": {
|
|
652
|
+
const result = this.semanticService.getDiagnostics(query.filePath);
|
|
653
|
+
return {
|
|
654
|
+
...result,
|
|
655
|
+
kind: "diagnostics"
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
case "file-snapshot":
|
|
659
|
+
return {
|
|
660
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
661
|
+
kind: "file-snapshot",
|
|
662
|
+
snapshot: this.semanticService.getFileSnapshot(query.filePath)
|
|
663
|
+
};
|
|
664
|
+
default: {
|
|
665
|
+
throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
respondToSocket(socket, payload) {
|
|
670
|
+
try {
|
|
671
|
+
const query = JSON.parse(payload);
|
|
672
|
+
if (!(0, import_protocol3.isFormSpecSemanticQuery)(query)) {
|
|
673
|
+
throw new Error("Invalid FormSpec semantic query payload");
|
|
674
|
+
}
|
|
675
|
+
const response = this.handleQuery(query);
|
|
676
|
+
socket.end(`${JSON.stringify(response)}
|
|
677
|
+
`);
|
|
678
|
+
} catch (error) {
|
|
679
|
+
socket.end(
|
|
680
|
+
`${JSON.stringify({
|
|
681
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
682
|
+
kind: "error",
|
|
683
|
+
error: error instanceof Error ? error.message : String(error)
|
|
684
|
+
})}
|
|
685
|
+
`
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
async writeManifest() {
|
|
690
|
+
const tempManifestPath = `${this.runtimePaths.manifestPath}.tmp`;
|
|
691
|
+
await import_promises.default.writeFile(tempManifestPath, `${JSON.stringify(this.manifest, null, 2)}
|
|
692
|
+
`, "utf8");
|
|
693
|
+
await import_promises.default.rename(tempManifestPath, this.runtimePaths.manifestPath);
|
|
694
|
+
}
|
|
695
|
+
async cleanupRuntimeArtifacts() {
|
|
696
|
+
await import_promises.default.rm(this.runtimePaths.manifestPath, { force: true });
|
|
697
|
+
if (this.runtimePaths.endpoint.kind === "unix-socket") {
|
|
698
|
+
await import_promises.default.rm(this.runtimePaths.endpoint.address, { force: true });
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
logQueryDuration(query, durationMs) {
|
|
702
|
+
const logger = this.options.logger;
|
|
703
|
+
if (logger === void 0) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
const thresholdMs = this.options.performanceLogThresholdMs ?? FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS;
|
|
707
|
+
if (durationMs < thresholdMs) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const event = {
|
|
711
|
+
name: "plugin.handleQuery",
|
|
712
|
+
durationMs,
|
|
713
|
+
detail: {
|
|
714
|
+
kind: query.kind,
|
|
715
|
+
...query.kind === "health" ? {} : { filePath: query.filePath }
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
logger.info(`[FormSpec][perf] ${formatPerformanceEvent(event)}`);
|
|
719
|
+
}
|
|
720
|
+
};
|
|
518
721
|
function createLanguageServiceProxy(languageService, semanticService) {
|
|
519
722
|
const wrapWithSnapshotRefresh = (fn) => {
|
|
520
723
|
return (fileName, ...args) => {
|
|
@@ -623,12 +826,17 @@ function init(modules) {
|
|
|
623
826
|
return {
|
|
624
827
|
create(info) {
|
|
625
828
|
const service = getOrCreateService(info, typescriptVersion);
|
|
626
|
-
return createLanguageServiceProxy(info.languageService, service);
|
|
829
|
+
return createLanguageServiceProxy(info.languageService, service.getSemanticService());
|
|
627
830
|
}
|
|
628
831
|
};
|
|
629
832
|
}
|
|
630
833
|
// Annotate the CommonJS export names for ESM import in node:
|
|
631
834
|
0 && (module.exports = {
|
|
835
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
836
|
+
FORMSPEC_ANALYSIS_SCHEMA_VERSION,
|
|
837
|
+
FormSpecPluginService,
|
|
838
|
+
FormSpecSemanticService,
|
|
839
|
+
createLanguageServiceProxy,
|
|
632
840
|
init
|
|
633
841
|
});
|
|
634
842
|
//# sourceMappingURL=index.cjs.map
|