@laurence79/wireit 0.14.13-shared-cache.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.
Files changed (54) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1062 -0
  3. package/bin/wireit.js +9 -0
  4. package/lib/analyzer.js +1600 -0
  5. package/lib/caching/cache.js +7 -0
  6. package/lib/caching/github-actions-cache.js +832 -0
  7. package/lib/caching/local-cache.js +78 -0
  8. package/lib/caching/shared-cache.js +256 -0
  9. package/lib/cli-options.js +495 -0
  10. package/lib/cli.js +177 -0
  11. package/lib/config.js +18 -0
  12. package/lib/error.js +160 -0
  13. package/lib/event.js +7 -0
  14. package/lib/execution/base.js +108 -0
  15. package/lib/execution/no-command.js +32 -0
  16. package/lib/execution/service.js +1017 -0
  17. package/lib/execution/standard.js +683 -0
  18. package/lib/executor.js +249 -0
  19. package/lib/fingerprint.js +164 -0
  20. package/lib/ide.js +583 -0
  21. package/lib/language-server.js +135 -0
  22. package/lib/logging/combination-logger.js +41 -0
  23. package/lib/logging/debug-logger.js +43 -0
  24. package/lib/logging/logger.js +38 -0
  25. package/lib/logging/metrics-logger.js +108 -0
  26. package/lib/logging/quiet/run-tracker.js +597 -0
  27. package/lib/logging/quiet/stack-map.js +41 -0
  28. package/lib/logging/quiet/writeover-line.js +197 -0
  29. package/lib/logging/quiet-logger.js +78 -0
  30. package/lib/logging/simple-logger.js +296 -0
  31. package/lib/logging/watch-logger.js +81 -0
  32. package/lib/script-child-process.js +270 -0
  33. package/lib/util/ast.js +71 -0
  34. package/lib/util/async-cache.js +24 -0
  35. package/lib/util/copy.js +120 -0
  36. package/lib/util/deferred.js +35 -0
  37. package/lib/util/delete.js +120 -0
  38. package/lib/util/dispose.js +16 -0
  39. package/lib/util/fs.js +258 -0
  40. package/lib/util/glob.js +255 -0
  41. package/lib/util/line-monitor.js +69 -0
  42. package/lib/util/manifest.js +31 -0
  43. package/lib/util/optimize-mkdirs.js +55 -0
  44. package/lib/util/package-json-reader.js +61 -0
  45. package/lib/util/package-json.js +179 -0
  46. package/lib/util/script-data-dir.js +19 -0
  47. package/lib/util/shuffle.js +16 -0
  48. package/lib/util/unreachable.js +12 -0
  49. package/lib/util/windows.js +87 -0
  50. package/lib/util/worker-pool.js +61 -0
  51. package/lib/watcher.js +396 -0
  52. package/package.json +470 -0
  53. package/schema.json +132 -0
  54. package/wireit.svg +1 -0
package/lib/ide.js ADDED
@@ -0,0 +1,583 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import * as fs from './util/fs.js';
7
+ import { Analyzer } from './analyzer.js';
8
+ import * as url from 'url';
9
+ import * as pathlib from 'path';
10
+ import * as jsonParser from 'jsonc-parser';
11
+ import { offsetInsideRange, OffsetToPositionConverter, } from './error.js';
12
+ class OverlayFilesystem {
13
+ constructor() {
14
+ // filename to contents
15
+ this.overlay = new Map();
16
+ }
17
+ async readFile(path, options) {
18
+ const contents = this.overlay.get(path);
19
+ if (contents !== undefined) {
20
+ return contents;
21
+ }
22
+ return fs.readFile(path, options);
23
+ }
24
+ }
25
+ export const completionItemKinds = {
26
+ service: 24, // CompletionItemKind.Operator
27
+ normalScript: 2, // CompletionItemKind.Method
28
+ dependenciesOnly: 9, // CompletionItemKind.Module
29
+ filesOnly: 17, // CompletionItemKind.File
30
+ folder: 19, // CompletionItemKind.Folder
31
+ };
32
+ /**
33
+ * The interface for an IDE to communicate with wireit's analysis pipeline.
34
+ *
35
+ * An IDE has certain files open with in-memory buffers. These buffers often
36
+ * shadow the files on disk, and in those cases we want to use the buffer if
37
+ * it's available, and fall back on disk contents if not.
38
+ *
39
+ * Generally the user only cares about the in-memory files, at least for
40
+ * most features like diagnostics.
41
+ */
42
+ export class IdeAnalyzer {
43
+ #overlayFs;
44
+ #workspaceRoots = [];
45
+ #analyzer;
46
+ constructor() {
47
+ this.#overlayFs = new OverlayFilesystem();
48
+ this.#analyzer = new Analyzer('npm', undefined, this.#overlayFs);
49
+ }
50
+ setWorkspaceRoots(roots) {
51
+ this.#workspaceRoots = roots;
52
+ }
53
+ /**
54
+ * Adds the file to the set of open files if it wasn't already,
55
+ * and specifies its contents. Open files are defined by their
56
+ * in memory contents, not by their on-disk contents.
57
+ *
58
+ * We also only care about diagnostics for open files.
59
+ *
60
+ * IDEs will typically call this method when a user opens a package.json file
61
+ * for editing, as well as once for each edit the user makes.
62
+ */
63
+ setOpenFileContents(path, contents) {
64
+ this.#overlayFs.overlay.set(path, contents);
65
+ this.#analyzer = new Analyzer('npm', undefined, this.#overlayFs);
66
+ }
67
+ /**
68
+ * Removes a file from the set of open files.
69
+ */
70
+ closeFile(path) {
71
+ this.#overlayFs.overlay.delete(path);
72
+ this.#analyzer = new Analyzer('npm', undefined, this.#overlayFs);
73
+ }
74
+ get openFiles() {
75
+ return this.#overlayFs.overlay.keys();
76
+ }
77
+ /**
78
+ * Calculates and returns diagnostics for open files. If a file has no
79
+ * diagnostics then we don't include an entry for it at all.
80
+ */
81
+ async getDiagnostics() {
82
+ const diagnostics = new Map();
83
+ function addDiagnostic(diagnostic) {
84
+ const path = diagnostic.location.file.path;
85
+ if (!openFiles.has(path)) {
86
+ return;
87
+ }
88
+ const converted = convertDiagnostic(diagnostic);
89
+ let set = diagnostics.get(path);
90
+ if (set === undefined) {
91
+ set = new Set();
92
+ diagnostics.set(path, set);
93
+ }
94
+ set.add(converted);
95
+ }
96
+ const openFiles = new Set(this.openFiles);
97
+ for (const failure of await this.#analyzer.analyzeFiles([...openFiles])) {
98
+ if (failure.diagnostic !== undefined) {
99
+ addDiagnostic(failure.diagnostic);
100
+ }
101
+ if (failure.diagnostics !== undefined) {
102
+ for (const diagnostic of failure.diagnostics) {
103
+ addDiagnostic(diagnostic);
104
+ }
105
+ }
106
+ }
107
+ return diagnostics;
108
+ }
109
+ async getCodeActions(path, range) {
110
+ const codeActions = [];
111
+ // file isn't open
112
+ if (!this.#overlayFs.overlay.has(path)) {
113
+ return codeActions;
114
+ }
115
+ const packageDir = pathlib.dirname(path);
116
+ // If there are any syntax-level errors for the file, we don't want to
117
+ // offer any code actions.
118
+ const packageJsonResult = await this.#analyzer.getPackageJson(packageDir);
119
+ if (!packageJsonResult.ok || packageJsonResult.value.failures.length > 0) {
120
+ return codeActions;
121
+ }
122
+ const packageJson = packageJsonResult.value;
123
+ const ourRange = OffsetToPositionConverter.get(packageJson.jsonFile).ideRangeToRange(range);
124
+ const scriptInfo = await this.#getInfoAboutLocation(packageJson, ourRange.offset);
125
+ if (scriptInfo === undefined) {
126
+ return codeActions;
127
+ }
128
+ if (scriptInfo.kind === 'dependency') {
129
+ // No code actions for dependencies yet.
130
+ return codeActions;
131
+ }
132
+ const { script, scriptSyntaxInfo: { name, scriptNode, wireitConfigNode }, } = scriptInfo;
133
+ if (scriptInfo.kind === 'scripts-section-script' &&
134
+ scriptNode !== undefined &&
135
+ wireitConfigNode === undefined) {
136
+ const edit = getEdit(packageJson.jsonFile, [
137
+ { path: ['scripts', name], value: 'wireit' },
138
+ {
139
+ path: ['wireit', name],
140
+ value: { command: scriptNode.value },
141
+ },
142
+ ]);
143
+ codeActions.push({
144
+ title: `Refactor this script to use wireit.`,
145
+ kind: 'refactor.extract',
146
+ edit,
147
+ });
148
+ }
149
+ if (scriptInfo.kind === 'wireit-section-script' &&
150
+ scriptNode === undefined) {
151
+ const edit = getEdit(packageJson.jsonFile, [
152
+ { path: ['scripts', script.name], value: 'wireit' },
153
+ ]);
154
+ codeActions.push({
155
+ title: `Add this script to the "scripts" section.`,
156
+ /**
157
+ * Quoting https://microsoft.github.io//language-server-protocol/specifications/lsp/3.17/specification/
158
+ *
159
+ * > 'Fix all' actions automatically fix errors that have a clear fix
160
+ * > that do not require user input. They should not suppress errors
161
+ * > or perform unsafe fixes such as generating new types or classes.
162
+ */
163
+ kind: 'source.fixAll',
164
+ edit,
165
+ });
166
+ }
167
+ if (scriptNode === undefined ||
168
+ wireitConfigNode === undefined ||
169
+ scriptNode.value === 'wireit') {
170
+ return codeActions;
171
+ }
172
+ // Ok, so there's definitely a wireit config and an entry in the scripts
173
+ // section, however the scripts section has its own command.
174
+ // In this case, we want to offer the user the option to move that command
175
+ // into the wireit section, but we need to be careful that we don't
176
+ // lose the user's command.
177
+ // Let's find the command, if any, in the wireit config.
178
+ let wireitCommand;
179
+ for (const propNode of wireitConfigNode.children ?? []) {
180
+ if (propNode.type !== 'property') {
181
+ continue;
182
+ }
183
+ const [key, value] = propNode.children ?? [];
184
+ if (key?.value !== 'command') {
185
+ continue;
186
+ }
187
+ if (typeof value?.value !== 'string') {
188
+ return codeActions; // This is invalid, so we don't offer anything.
189
+ }
190
+ wireitCommand = value.value;
191
+ break;
192
+ }
193
+ if (wireitCommand === undefined) {
194
+ // this is the easy case, we can just move the command over
195
+ const edit = getEdit(packageJson.jsonFile, [
196
+ { path: ['scripts', script.name], value: 'wireit' },
197
+ { path: ['wireit', script.name, 'command'], value: scriptNode.value },
198
+ ]);
199
+ codeActions.push({
200
+ title: `Move this script's command into the wireit config.`,
201
+ // This is mostly safe. The user might have moved the command
202
+ // back to the scripts section because they don't want some wireit
203
+ // features, like they don't want it to clean, or to run dependencies.
204
+ // So we don't want it to happen automatically, but it's very safe.
205
+ kind: 'quickfix',
206
+ isPreferred: true,
207
+ edit,
208
+ });
209
+ return codeActions;
210
+ }
211
+ // In the case where the commands are the same, we can just replace the
212
+ // script version with "wireit"
213
+ if (wireitCommand === scriptNode.value) {
214
+ const edit = getEdit(packageJson.jsonFile, [
215
+ { path: ['scripts', script.name], value: 'wireit' },
216
+ ]);
217
+ codeActions.push({
218
+ title: `Run "wireit" in the scripts section.`,
219
+ kind: 'quickfix',
220
+ isPreferred: true,
221
+ edit,
222
+ });
223
+ return codeActions;
224
+ }
225
+ // Ok here's the tricky part, we could lose user data.
226
+ const edit = getEdit(packageJson.jsonFile, [
227
+ { path: ['scripts', script.name], value: 'wireit' },
228
+ {
229
+ path: ['wireit', script.name, '[the script command was]'],
230
+ value: scriptNode.value,
231
+ },
232
+ ]);
233
+ codeActions.push({
234
+ title: `Move this script's command into the wireit config.`,
235
+ kind: 'quickfix',
236
+ isPreferred: false,
237
+ edit,
238
+ });
239
+ return codeActions;
240
+ }
241
+ async getDefinition(path, position) {
242
+ const packageDir = pathlib.dirname(path);
243
+ const packageJsonResult = await this.#analyzer.getPackageJson(packageDir);
244
+ if (!packageJsonResult.ok) {
245
+ return undefined;
246
+ }
247
+ const packageJson = packageJsonResult.value;
248
+ const ourPosition = OffsetToPositionConverter.get(packageJson.jsonFile).idePositionToOffset(position);
249
+ const scriptInfo = await this.#getInfoAboutLocation(packageJson, ourPosition);
250
+ if (scriptInfo?.kind === 'dependency') {
251
+ const dep = scriptInfo.dependency;
252
+ const targetFile = dep.config.declaringFile;
253
+ const targetNode = dep.config.configAstNode ?? dep.config.scriptAstNode;
254
+ if (targetFile === undefined || targetNode === undefined) {
255
+ return;
256
+ }
257
+ const targetConverter = OffsetToPositionConverter.get(targetFile);
258
+ const sourceConverter = OffsetToPositionConverter.get(packageJson.jsonFile);
259
+ return [
260
+ {
261
+ originSelectionRange: sourceConverter.toIdeRange(scriptInfo.dependency.specifier),
262
+ targetUri: url.pathToFileURL(targetFile.path).toString(),
263
+ targetRange: targetConverter.toIdeRange(
264
+ // The parent is the property, including both key and value.
265
+ // So we preview the whole thing when looking at the definition:
266
+ // "build": {"command": "tsc"}
267
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
268
+ targetNode.parent ?? targetNode),
269
+ targetSelectionRange: targetConverter.toIdeRange(targetNode.name),
270
+ },
271
+ ];
272
+ }
273
+ if (scriptInfo?.kind === 'scripts-section-script') {
274
+ const sourceConverter = OffsetToPositionConverter.get(packageJson.jsonFile);
275
+ const syntaxInfo = scriptInfo.scriptSyntaxInfo;
276
+ if (syntaxInfo.scriptNode && syntaxInfo.wireitConfigNode) {
277
+ // we can jump from the script section to the wireit config
278
+ return [
279
+ {
280
+ originSelectionRange: sourceConverter.toIdeRange(syntaxInfo.scriptNode.parent ?? syntaxInfo.scriptNode),
281
+ targetUri: url.pathToFileURL(packageJson.jsonFile.path).toString(),
282
+ targetRange: sourceConverter.toIdeRange(syntaxInfo.wireitConfigNode.parent ?? syntaxInfo.wireitConfigNode),
283
+ targetSelectionRange: sourceConverter.toIdeRange(syntaxInfo.wireitConfigNode.name),
284
+ },
285
+ ];
286
+ }
287
+ }
288
+ }
289
+ async findAllReferences(path, position) {
290
+ const packageDir = pathlib.dirname(path);
291
+ const packageJsonResult = await this.#analyzer.getPackageJson(packageDir);
292
+ if (!packageJsonResult.ok) {
293
+ return undefined;
294
+ }
295
+ const packageJson = packageJsonResult.value;
296
+ const ourPosition = OffsetToPositionConverter.get(packageJson.jsonFile).idePositionToOffset(position);
297
+ const scriptInfo = await this.#getInfoAboutLocation(packageJson, ourPosition);
298
+ if (scriptInfo == null) {
299
+ return undefined;
300
+ }
301
+ let scriptToLookup;
302
+ if (scriptInfo.kind === 'dependency') {
303
+ scriptToLookup = scriptInfo.dependency.config;
304
+ }
305
+ else {
306
+ scriptToLookup = scriptInfo.script;
307
+ }
308
+ const allScripts = await this.#analyzer.analyzeAllScripts([
309
+ ...this.openFiles,
310
+ ...this.#workspaceRoots.map((r) => pathlib.join(r, 'package.json')),
311
+ ]);
312
+ const references = [];
313
+ for (const script of allScripts) {
314
+ const dependencies = script.placeholder.dependencies;
315
+ if (dependencies == null) {
316
+ continue;
317
+ }
318
+ for (const dep of dependencies) {
319
+ const depFile = script.placeholder.declaringFile;
320
+ if (depFile === undefined) {
321
+ continue;
322
+ }
323
+ if (dep.config.name !== scriptToLookup.name) {
324
+ continue;
325
+ }
326
+ if (dep.config.packageDir !== scriptToLookup.packageDir) {
327
+ continue;
328
+ }
329
+ references.push({
330
+ uri: url.pathToFileURL(depFile.path).toString(),
331
+ range: OffsetToPositionConverter.get(depFile).toIdeRange(dep.specifier),
332
+ });
333
+ }
334
+ }
335
+ // sort the references, first by file, then by offset
336
+ references.sort((a, b) => {
337
+ if (a.uri < b.uri) {
338
+ return -1;
339
+ }
340
+ if (a.uri > b.uri) {
341
+ return 1;
342
+ }
343
+ return a.range.start.line - b.range.start.line;
344
+ });
345
+ return references;
346
+ }
347
+ async getCompletions(path, position) {
348
+ const packageDir = pathlib.dirname(path);
349
+ const packageJsonResult = await this.#analyzer.getPackageJson(packageDir);
350
+ if (!packageJsonResult.ok) {
351
+ return undefined;
352
+ }
353
+ const packageJson = packageJsonResult.value;
354
+ const ourPosition = OffsetToPositionConverter.get(packageJson.jsonFile).idePositionToOffset(position);
355
+ const scriptInfo = await this.#getInfoAboutLocation(packageJson, ourPosition);
356
+ if (scriptInfo === undefined) {
357
+ return undefined;
358
+ }
359
+ let scriptSpecifier;
360
+ if (scriptInfo.kind !== 'dependency') {
361
+ // Annoyingly, it's particularly important that we offer completions
362
+ // when the user is typing inside an empty dependency specifier, which
363
+ // is not a syntactically valid script config. So we need special logic
364
+ // here
365
+ const dependenciesProp = scriptInfo.scriptSyntaxInfo.wireitConfigNode?.children?.find((child) => child.type === 'property' &&
366
+ child.children?.[0]?.value === 'dependencies');
367
+ const dependency = dependenciesProp?.children?.[1]?.children?.find((child) => offsetInsideRange(ourPosition, child));
368
+ if (typeof dependency?.value !== 'string') {
369
+ return undefined;
370
+ }
371
+ scriptSpecifier = dependency;
372
+ }
373
+ else {
374
+ scriptSpecifier = scriptInfo.dependency.specifier;
375
+ }
376
+ // Ok, the user is typing inside a dependency specifier, so we want to
377
+ // offer them completion items. Next question, are we in the (optional)
378
+ // file path portion of the specifier, or the script name portion?
379
+ const distanceInto = ourPosition - scriptSpecifier.offset - 1; /* for the leading quote */
380
+ const specifierBeforeCursor = scriptSpecifier.value.slice(0, distanceInto);
381
+ let targetPackageJson;
382
+ let targetPackageDir;
383
+ if (specifierBeforeCursor.startsWith('.')) {
384
+ const indexOfColon = specifierBeforeCursor.indexOf(':');
385
+ if (indexOfColon === -1) {
386
+ // We'd be autocompleting on the file path portion of the specifier.
387
+ // Not implemented yet.
388
+ const items = await this.#completionItemsForPath(packageDir, specifierBeforeCursor);
389
+ if (items == null) {
390
+ return undefined;
391
+ }
392
+ return {
393
+ isIncomplete: true,
394
+ items,
395
+ };
396
+ }
397
+ targetPackageDir = pathlib.join(packageDir, specifierBeforeCursor.slice(0, indexOfColon));
398
+ const result = await this.#analyzer.getPackageJson(targetPackageDir);
399
+ if (!result.ok) {
400
+ return undefined;
401
+ }
402
+ targetPackageJson = result.value;
403
+ }
404
+ else {
405
+ targetPackageJson = packageJson;
406
+ targetPackageDir = packageDir;
407
+ }
408
+ // analyze the scripts in this file
409
+ const potentiallyValidScripts = await Promise.all([...targetPackageJson.scripts].map((script) => {
410
+ return this.#analyzer.analyzeIgnoringErrors({
411
+ name: script.name,
412
+ packageDir: targetPackageDir,
413
+ });
414
+ }));
415
+ const result = {
416
+ // If the user hasn't typed anything yet, our results are incomplete
417
+ // because they could type ./ or ../ and we don't complete those yet.
418
+ isIncomplete: specifierBeforeCursor === '',
419
+ items: potentiallyValidScripts.map((script) => {
420
+ return {
421
+ label: script.name,
422
+ kind: this.#completionKindForScript(script),
423
+ };
424
+ }),
425
+ };
426
+ // Sort results for deterministic tests.
427
+ result.items.sort((a, b) => a.label.localeCompare(b.label));
428
+ return result;
429
+ }
430
+ #completionKindForScript(script) {
431
+ if (script !== undefined) {
432
+ if (script.service !== undefined) {
433
+ return completionItemKinds.service;
434
+ }
435
+ else if (script.command !== undefined) {
436
+ return completionItemKinds.normalScript;
437
+ }
438
+ else if (script.dependencies !== undefined &&
439
+ script.dependencies.length > 0) {
440
+ return completionItemKinds.dependenciesOnly;
441
+ }
442
+ else if (script.files !== undefined) {
443
+ return completionItemKinds.filesOnly;
444
+ }
445
+ }
446
+ return completionItemKinds.normalScript;
447
+ }
448
+ async #completionItemsForPath(packageDir, specifierSoFar) {
449
+ const result = [];
450
+ const relPathToDirCompletingIn = specifierSoFar.endsWith('/')
451
+ ? specifierSoFar
452
+ : pathlib.dirname(specifierSoFar);
453
+ const pathToDirCompletingIn = pathlib.join(packageDir, relPathToDirCompletingIn);
454
+ let dirContents;
455
+ try {
456
+ dirContents = await fs.readdir(pathToDirCompletingIn, {
457
+ withFileTypes: true,
458
+ });
459
+ }
460
+ catch {
461
+ return undefined;
462
+ }
463
+ for (const dirent of dirContents) {
464
+ if (dirent.name === 'node_modules' || dirent.name.startsWith('.')) {
465
+ continue;
466
+ }
467
+ if (dirent.isDirectory()) {
468
+ result.push({
469
+ label: dirent.name,
470
+ kind: completionItemKinds.folder,
471
+ });
472
+ }
473
+ }
474
+ return result;
475
+ }
476
+ async getPackageJsonForTest(filename) {
477
+ const packageDir = pathlib.dirname(filename);
478
+ const packageJsonResult = await this.#analyzer.getPackageJson(packageDir);
479
+ if (!packageJsonResult.ok) {
480
+ return undefined;
481
+ }
482
+ return packageJsonResult.value;
483
+ }
484
+ async #getInfoAboutLocation(packageJson, offset) {
485
+ const locationInfo = packageJson.getInfoAboutLocation(offset);
486
+ if (locationInfo === undefined) {
487
+ return;
488
+ }
489
+ const script = await this.#analyzer.analyzeIgnoringErrors({
490
+ name: locationInfo.scriptSyntaxInfo.name,
491
+ packageDir: pathlib.dirname(packageJson.jsonFile.path),
492
+ });
493
+ for (const dep of script.dependencies ?? []) {
494
+ if (offsetInsideRange(offset, dep.specifier)) {
495
+ return {
496
+ kind: 'dependency',
497
+ dependency: dep,
498
+ script: script,
499
+ scriptSyntax: locationInfo.scriptSyntaxInfo,
500
+ };
501
+ }
502
+ }
503
+ return {
504
+ ...locationInfo,
505
+ script,
506
+ };
507
+ }
508
+ }
509
+ function getEdit(file, modifications) {
510
+ const edits = [];
511
+ for (const { path, value } of modifications) {
512
+ edits.push(...jsonParser.modify(file.contents, path, value, inferModificationOptions(file)));
513
+ }
514
+ const converter = OffsetToPositionConverter.get(file);
515
+ const textEdits = edits.map((e) => {
516
+ return {
517
+ range: converter.toIdeRange(e),
518
+ newText: e.content,
519
+ };
520
+ });
521
+ return { changes: { [file.path]: textEdits } };
522
+ }
523
+ function inferModificationOptions(file) {
524
+ const firstPostNewlineWhitespace = file.contents.match(/\n(\s+)/)?.[1];
525
+ if (firstPostNewlineWhitespace === undefined) {
526
+ return {};
527
+ }
528
+ if (/^ +$/.test(firstPostNewlineWhitespace)) {
529
+ return {
530
+ formattingOptions: {
531
+ insertSpaces: true,
532
+ tabSize: firstPostNewlineWhitespace.length,
533
+ },
534
+ };
535
+ }
536
+ else if (/^\t+$/.test(firstPostNewlineWhitespace)) {
537
+ return {
538
+ formattingOptions: {
539
+ insertSpaces: false,
540
+ tabSize: firstPostNewlineWhitespace.length,
541
+ },
542
+ };
543
+ }
544
+ return {};
545
+ }
546
+ function convertDiagnostic(d) {
547
+ const converter = OffsetToPositionConverter.get(d.location.file);
548
+ let relatedInformation;
549
+ if (d.supplementalLocations) {
550
+ relatedInformation = [];
551
+ for (const loc of d.supplementalLocations) {
552
+ relatedInformation.push({
553
+ location: {
554
+ uri: url.pathToFileURL(loc.location.file.path).toString(),
555
+ range: converter.toIdeRange(loc.location.range),
556
+ },
557
+ message: loc.message,
558
+ });
559
+ }
560
+ }
561
+ return {
562
+ severity: convertSeverity(d.severity),
563
+ message: d.message,
564
+ source: 'wireit',
565
+ range: converter.toIdeRange(d.location.range),
566
+ relatedInformation,
567
+ };
568
+ }
569
+ function convertSeverity(severity) {
570
+ switch (severity) {
571
+ case 'error':
572
+ return 1; // DiagnosticSeverity.Error;
573
+ case 'warning':
574
+ return 2; // DiagnosticSeverity.Warning;
575
+ case 'info':
576
+ return 3; // DiagnosticSeverity.Information;
577
+ default: {
578
+ const never = severity;
579
+ throw new Error(`Unexpected severity: ${String(never)}`);
580
+ }
581
+ }
582
+ }
583
+ //# sourceMappingURL=ide.js.map