@backtickjs/language-service 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jean-François Bisson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,21 @@
1
- # Temporary Holding Version
1
+ # @backtickjs/language-service
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [Backtick](https://backtickjs.com)'s diagnostics and completions, layered on top of TypeScript's language service.
4
+
5
+ ## Internal
6
+
7
+ You don't install this package directly. It is used by [`@backtickjs/typescript-plugin`](https://github.com/backtickjs/backtick/tree/main/packages/typescript-plugin), which the VS Code extension bundles, and by [`@backtickjs/tsc`](https://github.com/backtickjs/backtick/tree/main/packages/tsc) (`backtick-tsc`).
8
+
9
+ ## What it does
10
+
11
+ `decorateLanguageService` wraps a TypeScript language service. It adds Backtick's own compiler diagnostics, collected by [`@backtickjs/language-plugin`](https://github.com/backtickjs/backtick/tree/main/packages/language-plugin), to TypeScript's semantic diagnostics. It also cleans up what TypeScript reports about script code: the virtual code prefixes script identifiers with `__cs_`, and diagnostics, quick info and completions are rewritten to show the names as you wrote them. `backtick-tsc` uses `getBacktickDiagnostics` and `unmangleDiagnostic` to report the same diagnostics from the command line.
12
+
13
+ ## Related
14
+
15
+ - [`@backtickjs/language-plugin`](https://github.com/backtickjs/backtick/tree/main/packages/language-plugin)
16
+ - [Backtick for VS Code](https://marketplace.visualstudio.com/items?itemName=backtickjs.backtick-vscode)
17
+ - [Documentation](https://backtickjs.com/docs)
18
+
19
+ ## License
20
+
21
+ MIT
@@ -0,0 +1,3 @@
1
+ import type { Language } from "@volar/language-core";
2
+ import type ts from "typescript";
3
+ export declare function decorateLanguageService(inner: ts.LanguageService, language: Language<string>): ts.LanguageService;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decorateLanguageService = decorateLanguageService;
4
+ const compiler_1 = require("@backtickjs/compiler");
5
+ const getBacktickDiagnostics_js_1 = require("./getBacktickDiagnostics.js");
6
+ const unmangleCompletions_js_1 = require("./unmangleCompletions.js");
7
+ const unmangleDiagnostics_js_1 = require("./unmangleDiagnostics.js");
8
+ const unmangleQuickInfo_js_1 = require("./unmangleQuickInfo.js");
9
+ function decorateLanguageService(inner, language) {
10
+ const overrides = {
11
+ getSemanticDiagnostics: (fileName) => [
12
+ ...inner.getSemanticDiagnostics(fileName).map(unmangleDiagnostics_js_1.unmangleDiagnostic),
13
+ ...(0, getBacktickDiagnostics_js_1.getBacktickDiagnostics)(language, inner.getProgram()?.getSourceFile(fileName), fileName),
14
+ ],
15
+ getSyntacticDiagnostics: (fileName) => inner.getSyntacticDiagnostics(fileName).map(unmangleDiagnostics_js_1.unmangleDiagnostic),
16
+ getSuggestionDiagnostics: (fileName) => inner.getSuggestionDiagnostics(fileName).map(unmangleDiagnostics_js_1.unmangleDiagnostic),
17
+ getQuickInfoAtPosition: (fileName, position) => {
18
+ const quickInfo = inner.getQuickInfoAtPosition(fileName, position);
19
+ return quickInfo && (0, unmangleQuickInfo_js_1.unmangleQuickInfo)(quickInfo);
20
+ },
21
+ getCompletionsAtPosition: (fileName, position, options, settings) => {
22
+ const completions = inner.getCompletionsAtPosition(fileName, position, options, settings);
23
+ return completions && (0, unmangleCompletions_js_1.unmangleCompletionInfo)(completions);
24
+ },
25
+ getCompletionEntryDetails: (fileName, position, name, ...rest) => {
26
+ // The editor echoes back the unmangled `name` we returned above, but the
27
+ // virtual code is keyed by the mangled name; look that up first, falling
28
+ // back to the name as given for genuine (never-mangled) host symbols.
29
+ const details = inner.getCompletionEntryDetails(fileName, position, (0, compiler_1.mangle)(name), ...rest) ?? inner.getCompletionEntryDetails(fileName, position, name, ...rest);
30
+ return details && (0, unmangleCompletions_js_1.unmangleCompletionEntryDetails)(details);
31
+ },
32
+ };
33
+ return new Proxy(inner, {
34
+ get(target, property, receiver) {
35
+ return Object.hasOwn(overrides, property)
36
+ ? overrides[property]
37
+ : Reflect.get(target, property, receiver);
38
+ },
39
+ });
40
+ }
41
+ //# sourceMappingURL=decorateLanguageService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorateLanguageService.js","sourceRoot":"","sources":["../src/decorateLanguageService.ts"],"names":[],"mappings":";;AAWA,0DAyDC;AApED,mDAA8C;AAG9C,2EAAqE;AACrE,qEAGkC;AAClC,qEAA8D;AAC9D,iEAA2D;AAE3D,SAAgB,uBAAuB,CACrC,KAAyB,EACzB,QAA0B;IAE1B,MAAM,SAAS,GAAgC;QAC7C,sBAAsB,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;YACpC,GAAG,KAAK,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,2CAAkB,CAAC;YACjE,GAAG,IAAA,kDAAsB,EACvB,QAAQ,EACR,KAAK,CAAC,UAAU,EAAE,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC3C,QAAQ,CACT;SACF;QAED,uBAAuB,EAAE,CAAC,QAAQ,EAAE,EAAE,CACpC,KAAK,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,2CAAkB,CAAC;QAEjE,wBAAwB,EAAE,CAAC,QAAQ,EAAE,EAAE,CACrC,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,2CAAkB,CAAC;QAElE,sBAAsB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE;YAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACnE,OAAO,SAAS,IAAI,IAAA,wCAAiB,EAAC,SAAS,CAAC,CAAC;QACnD,CAAC;QAED,wBAAwB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YAClE,MAAM,WAAW,GAAG,KAAK,CAAC,wBAAwB,CAChD,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,QAAQ,CACT,CAAC;YACF,OAAO,WAAW,IAAI,IAAA,+CAAsB,EAAC,WAAW,CAAC,CAAC;QAC5D,CAAC;QAED,yBAAyB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE;YAC/D,yEAAyE;YACzE,yEAAyE;YACzE,sEAAsE;YACtE,MAAM,OAAO,GACX,KAAK,CAAC,yBAAyB,CAC7B,QAAQ,EACR,QAAQ,EACR,IAAA,iBAAM,EAAC,IAAI,CAAC,EACZ,GAAG,IAAI,CACR,IAAI,KAAK,CAAC,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;YAC1E,OAAO,OAAO,IAAI,IAAA,uDAA8B,EAAC,OAAO,CAAC,CAAC;QAC5D,CAAC;KACF,CAAC;IAEF,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE;QACtB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC;gBACvC,CAAC,CAAC,SAAS,CAAC,QAAoC,CAAC;gBACjD,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Language } from "@volar/language-core";
2
+ import type ts from "typescript";
3
+ export declare function getBacktickDiagnostics(language: Language<string> | undefined, file: ts.SourceFile | undefined, fileName: string): ts.Diagnostic[];
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBacktickDiagnostics = getBacktickDiagnostics;
4
+ const language_plugin_1 = require("@backtickjs/language-plugin");
5
+ // Surfaces Backtick's own compiler diagnostics (e.g. unsupported syntax inside
6
+ // a `cs` client script) — the errors Backtick itself produces, which TypeScript
7
+ // knows nothing about. TypeScript's diagnostics already flow through the
8
+ // compiled embedded code; these are additive.
9
+ //
10
+ // The diagnostics are stored on the root virtual code in source coordinates,
11
+ // and `file`'s text mirrors the source 1:1, so the offsets map straight through
12
+ // with no translation. Works against any consumer that can supply the
13
+ // `Language` and the file's `SourceFile` — a language service (editor) or a
14
+ // program (command-line `tsc`).
15
+ function getBacktickDiagnostics(language, file, fileName) {
16
+ const root = language?.scripts.get(fileName)?.generated?.root;
17
+ if (!(root instanceof language_plugin_1.BacktickVirtualCode)) {
18
+ return [];
19
+ }
20
+ return root.diagnostics.map((diagnostic) => ({
21
+ file,
22
+ start: diagnostic.range.start,
23
+ length: diagnostic.range.end - diagnostic.range.start,
24
+ messageText: diagnostic.message,
25
+ category: diagnostic.category,
26
+ code: diagnostic.code,
27
+ source: "backtick",
28
+ }));
29
+ }
30
+ //# sourceMappingURL=getBacktickDiagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getBacktickDiagnostics.js","sourceRoot":"","sources":["../src/getBacktickDiagnostics.ts"],"names":[],"mappings":";;AAcA,wDAmBC;AAjCD,iEAAkE;AAIlE,+EAA+E;AAC/E,gFAAgF;AAChF,yEAAyE;AACzE,8CAA8C;AAC9C,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,sEAAsE;AACtE,4EAA4E;AAC5E,gCAAgC;AAChC,SAAgB,sBAAsB,CACpC,QAAsC,EACtC,IAA+B,EAC/B,QAAgB;IAEhB,MAAM,IAAI,GAAG,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC;IAC9D,IAAI,CAAC,CAAC,IAAI,YAAY,qCAAmB,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAC3C,IAAI;QACJ,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK;QAC7B,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK;QACrD,WAAW,EAAE,UAAU,CAAC,OAAO;QAC/B,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,MAAM,EAAE,UAAU;KACnB,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { decorateLanguageService } from "./decorateLanguageService.js";
2
+ export { getBacktickDiagnostics } from "./getBacktickDiagnostics.js";
3
+ export { unmangleDiagnostic } from "./unmangleDiagnostics.js";
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unmangleDiagnostic = exports.getBacktickDiagnostics = exports.decorateLanguageService = void 0;
4
+ var decorateLanguageService_js_1 = require("./decorateLanguageService.js");
5
+ Object.defineProperty(exports, "decorateLanguageService", { enumerable: true, get: function () { return decorateLanguageService_js_1.decorateLanguageService; } });
6
+ var getBacktickDiagnostics_js_1 = require("./getBacktickDiagnostics.js");
7
+ Object.defineProperty(exports, "getBacktickDiagnostics", { enumerable: true, get: function () { return getBacktickDiagnostics_js_1.getBacktickDiagnostics; } });
8
+ var unmangleDiagnostics_js_1 = require("./unmangleDiagnostics.js");
9
+ Object.defineProperty(exports, "unmangleDiagnostic", { enumerable: true, get: function () { return unmangleDiagnostics_js_1.unmangleDiagnostic; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2EAAuE;AAA9D,qIAAA,uBAAuB,OAAA;AAChC,yEAAqE;AAA5D,mIAAA,sBAAsB,OAAA;AAC/B,mEAA8D;AAArD,4HAAA,kBAAkB,OAAA"}
@@ -0,0 +1,3 @@
1
+ import type ts from "typescript";
2
+ export declare function unmangleCompletionInfo<T extends ts.CompletionInfo>(completions: T): T;
3
+ export declare function unmangleCompletionEntryDetails(details: ts.CompletionEntryDetails): ts.CompletionEntryDetails;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unmangleCompletionInfo = unmangleCompletionInfo;
4
+ exports.unmangleCompletionEntryDetails = unmangleCompletionEntryDetails;
5
+ const compiler_1 = require("@backtickjs/compiler");
6
+ const unmangleDisplayPart_1 = require("./unmangleDisplayPart");
7
+ function unmangleCompletionInfo(completions) {
8
+ return {
9
+ ...completions,
10
+ entries: completions.entries.map((entry) => ({
11
+ ...entry,
12
+ name: (0, compiler_1.unmangle)(entry.name),
13
+ insertText: entry.insertText && (0, compiler_1.unmangle)(entry.insertText),
14
+ })),
15
+ };
16
+ }
17
+ function unmangleCompletionEntryDetails(details) {
18
+ return {
19
+ ...details,
20
+ name: (0, compiler_1.unmangle)(details.name),
21
+ displayParts: details.displayParts.map(unmangleDisplayPart_1.unmangleDisplayPart),
22
+ documentation: details.documentation?.map(unmangleDisplayPart_1.unmangleDisplayPart),
23
+ };
24
+ }
25
+ //# sourceMappingURL=unmangleCompletions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmangleCompletions.js","sourceRoot":"","sources":["../src/unmangleCompletions.ts"],"names":[],"mappings":";;AAIA,wDAWC;AAED,wEASC;AA1BD,mDAAgD;AAEhD,+DAA4D;AAE5D,SAAgB,sBAAsB,CACpC,WAAc;IAEd,OAAO;QACL,GAAG,WAAW;QACd,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3C,GAAG,KAAK;YACR,IAAI,EAAE,IAAA,mBAAQ,EAAC,KAAK,CAAC,IAAI,CAAC;YAC1B,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAA,mBAAQ,EAAC,KAAK,CAAC,UAAU,CAAC;SAC3D,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,SAAgB,8BAA8B,CAC5C,OAAkC;IAElC,OAAO;QACL,GAAG,OAAO;QACV,IAAI,EAAE,IAAA,mBAAQ,EAAC,OAAO,CAAC,IAAI,CAAC;QAC5B,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,yCAAmB,CAAC;QAC3D,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,yCAAmB,CAAC;KAC/D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type ts from "typescript";
2
+ export declare function unmangleDiagnostic<T extends ts.Diagnostic>(diagnostic: T): T;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unmangleDiagnostic = unmangleDiagnostic;
4
+ const compiler_1 = require("@backtickjs/compiler");
5
+ function unmangleDiagnostic(diagnostic) {
6
+ return {
7
+ ...diagnostic,
8
+ messageText: unmangleMessageText(diagnostic.messageText),
9
+ relatedInformation: diagnostic.relatedInformation?.map((info) => ({
10
+ ...info,
11
+ messageText: unmangleMessageText(info.messageText),
12
+ })),
13
+ };
14
+ }
15
+ function unmangleMessageText(messageText) {
16
+ if (typeof messageText === "string") {
17
+ return (0, compiler_1.unmangle)(messageText);
18
+ }
19
+ return {
20
+ ...messageText,
21
+ messageText: (0, compiler_1.unmangle)(messageText.messageText),
22
+ next: messageText.next?.map((chain) => unmangleMessageText(chain)),
23
+ };
24
+ }
25
+ //# sourceMappingURL=unmangleDiagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmangleDiagnostics.js","sourceRoot":"","sources":["../src/unmangleDiagnostics.ts"],"names":[],"mappings":";;AAGA,gDASC;AAZD,mDAAgD;AAGhD,SAAgB,kBAAkB,CAA0B,UAAa;IACvE,OAAO;QACL,GAAG,UAAU;QACb,WAAW,EAAE,mBAAmB,CAAC,UAAU,CAAC,WAAW,CAAC;QACxD,kBAAkB,EAAE,UAAU,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAChE,GAAG,IAAI;YACP,WAAW,EAAE,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC;SACnD,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAC1B,WAA+C;IAE/C,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,IAAA,mBAAQ,EAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO;QACL,GAAG,WAAW;QACd,WAAW,EAAE,IAAA,mBAAQ,EAAC,WAAW,CAAC,WAAW,CAAC;QAC9C,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,GAAG,CACzB,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAA8B,CACnE;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type ts from "typescript";
2
+ export declare function unmangleDisplayPart(part: ts.SymbolDisplayPart): ts.SymbolDisplayPart;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unmangleDisplayPart = unmangleDisplayPart;
4
+ const compiler_1 = require("@backtickjs/compiler");
5
+ function unmangleDisplayPart(part) {
6
+ return { ...part, text: (0, compiler_1.unmangle)(part.text) };
7
+ }
8
+ //# sourceMappingURL=unmangleDisplayPart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmangleDisplayPart.js","sourceRoot":"","sources":["../src/unmangleDisplayPart.ts"],"names":[],"mappings":";;AAGA,kDAIC;AAPD,mDAAgD;AAGhD,SAAgB,mBAAmB,CACjC,IAA0B;IAE1B,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAA,mBAAQ,EAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type ts from "typescript";
2
+ export declare function unmangleQuickInfo(quickInfo: ts.QuickInfo): ts.QuickInfo;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unmangleQuickInfo = unmangleQuickInfo;
4
+ const unmangleDisplayPart_1 = require("./unmangleDisplayPart");
5
+ function unmangleQuickInfo(quickInfo) {
6
+ return {
7
+ ...quickInfo,
8
+ displayParts: quickInfo.displayParts?.map(unmangleDisplayPart_1.unmangleDisplayPart),
9
+ documentation: quickInfo.documentation?.map(unmangleDisplayPart_1.unmangleDisplayPart),
10
+ };
11
+ }
12
+ //# sourceMappingURL=unmangleQuickInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmangleQuickInfo.js","sourceRoot":"","sources":["../src/unmangleQuickInfo.ts"],"names":[],"mappings":";;AAGA,8CAMC;AARD,+DAA4D;AAE5D,SAAgB,iBAAiB,CAAC,SAAuB;IACvD,OAAO;QACL,GAAG,SAAS;QACZ,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,GAAG,CAAC,yCAAmB,CAAC;QAC9D,aAAa,EAAE,SAAS,CAAC,aAAa,EAAE,GAAG,CAAC,yCAAmB,CAAC;KACjE,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "@backtickjs/language-service",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Internal: Backtick's diagnostics and completions, layered on TypeScript's language service",
5
+ "keywords": [
6
+ "backtick",
7
+ "react-native",
8
+ "server-driven-ui",
9
+ "server-components",
10
+ "typescript",
11
+ "language-service",
12
+ "language-tools"
13
+ ],
14
+ "homepage": "https://backtickjs.com",
15
+ "bugs": {
16
+ "url": "https://github.com/backtickjs/backtick/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/backtickjs/backtick.git",
21
+ "directory": "packages/language-service"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Jean-François Bisson <jf@backtickjs.com>",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "!dist/._cache",
36
+ "src"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "@volar/language-core": "^2.4.28",
43
+ "@backtickjs/compiler": "0.1.0",
44
+ "@backtickjs/language-plugin": "0.1.0"
45
+ },
46
+ "devDependencies": {
47
+ "prettier": "^3.6.2",
48
+ "typescript": "^6.0.3"
49
+ },
50
+ "peerDependencies": {
51
+ "typescript": "^6.0.3"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc -b",
55
+ "dev": "tsc -b --watch",
56
+ "format": "prettier --write --ignore-unknown .",
57
+ "format:check": "prettier --check --ignore-unknown .",
58
+ "clean": "rm -rf node_modules dist .turbo"
59
+ }
6
60
  }
@@ -0,0 +1,69 @@
1
+ import { mangle } from "@backtickjs/compiler";
2
+ import type { Language } from "@volar/language-core";
3
+ import type ts from "typescript";
4
+ import { getBacktickDiagnostics } from "./getBacktickDiagnostics.js";
5
+ import {
6
+ unmangleCompletionEntryDetails,
7
+ unmangleCompletionInfo,
8
+ } from "./unmangleCompletions.js";
9
+ import { unmangleDiagnostic } from "./unmangleDiagnostics.js";
10
+ import { unmangleQuickInfo } from "./unmangleQuickInfo.js";
11
+
12
+ export function decorateLanguageService(
13
+ inner: ts.LanguageService,
14
+ language: Language<string>,
15
+ ): ts.LanguageService {
16
+ const overrides: Partial<ts.LanguageService> = {
17
+ getSemanticDiagnostics: (fileName) => [
18
+ ...inner.getSemanticDiagnostics(fileName).map(unmangleDiagnostic),
19
+ ...getBacktickDiagnostics(
20
+ language,
21
+ inner.getProgram()?.getSourceFile(fileName),
22
+ fileName,
23
+ ),
24
+ ],
25
+
26
+ getSyntacticDiagnostics: (fileName) =>
27
+ inner.getSyntacticDiagnostics(fileName).map(unmangleDiagnostic),
28
+
29
+ getSuggestionDiagnostics: (fileName) =>
30
+ inner.getSuggestionDiagnostics(fileName).map(unmangleDiagnostic),
31
+
32
+ getQuickInfoAtPosition: (fileName, position) => {
33
+ const quickInfo = inner.getQuickInfoAtPosition(fileName, position);
34
+ return quickInfo && unmangleQuickInfo(quickInfo);
35
+ },
36
+
37
+ getCompletionsAtPosition: (fileName, position, options, settings) => {
38
+ const completions = inner.getCompletionsAtPosition(
39
+ fileName,
40
+ position,
41
+ options,
42
+ settings,
43
+ );
44
+ return completions && unmangleCompletionInfo(completions);
45
+ },
46
+
47
+ getCompletionEntryDetails: (fileName, position, name, ...rest) => {
48
+ // The editor echoes back the unmangled `name` we returned above, but the
49
+ // virtual code is keyed by the mangled name; look that up first, falling
50
+ // back to the name as given for genuine (never-mangled) host symbols.
51
+ const details =
52
+ inner.getCompletionEntryDetails(
53
+ fileName,
54
+ position,
55
+ mangle(name),
56
+ ...rest,
57
+ ) ?? inner.getCompletionEntryDetails(fileName, position, name, ...rest);
58
+ return details && unmangleCompletionEntryDetails(details);
59
+ },
60
+ };
61
+
62
+ return new Proxy(inner, {
63
+ get(target, property, receiver) {
64
+ return Object.hasOwn(overrides, property)
65
+ ? overrides[property as keyof ts.LanguageService]
66
+ : Reflect.get(target, property, receiver);
67
+ },
68
+ });
69
+ }
@@ -0,0 +1,34 @@
1
+ import { BacktickVirtualCode } from "@backtickjs/language-plugin";
2
+ import type { Language } from "@volar/language-core";
3
+ import type ts from "typescript";
4
+
5
+ // Surfaces Backtick's own compiler diagnostics (e.g. unsupported syntax inside
6
+ // a `cs` client script) — the errors Backtick itself produces, which TypeScript
7
+ // knows nothing about. TypeScript's diagnostics already flow through the
8
+ // compiled embedded code; these are additive.
9
+ //
10
+ // The diagnostics are stored on the root virtual code in source coordinates,
11
+ // and `file`'s text mirrors the source 1:1, so the offsets map straight through
12
+ // with no translation. Works against any consumer that can supply the
13
+ // `Language` and the file's `SourceFile` — a language service (editor) or a
14
+ // program (command-line `tsc`).
15
+ export function getBacktickDiagnostics(
16
+ language: Language<string> | undefined,
17
+ file: ts.SourceFile | undefined,
18
+ fileName: string,
19
+ ): ts.Diagnostic[] {
20
+ const root = language?.scripts.get(fileName)?.generated?.root;
21
+ if (!(root instanceof BacktickVirtualCode)) {
22
+ return [];
23
+ }
24
+
25
+ return root.diagnostics.map((diagnostic) => ({
26
+ file,
27
+ start: diagnostic.range.start,
28
+ length: diagnostic.range.end - diagnostic.range.start,
29
+ messageText: diagnostic.message,
30
+ category: diagnostic.category,
31
+ code: diagnostic.code,
32
+ source: "backtick",
33
+ }));
34
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { decorateLanguageService } from "./decorateLanguageService.js";
2
+ export { getBacktickDiagnostics } from "./getBacktickDiagnostics.js";
3
+ export { unmangleDiagnostic } from "./unmangleDiagnostics.js";
@@ -0,0 +1,27 @@
1
+ import { unmangle } from "@backtickjs/compiler";
2
+ import type ts from "typescript";
3
+ import { unmangleDisplayPart } from "./unmangleDisplayPart";
4
+
5
+ export function unmangleCompletionInfo<T extends ts.CompletionInfo>(
6
+ completions: T,
7
+ ): T {
8
+ return {
9
+ ...completions,
10
+ entries: completions.entries.map((entry) => ({
11
+ ...entry,
12
+ name: unmangle(entry.name),
13
+ insertText: entry.insertText && unmangle(entry.insertText),
14
+ })),
15
+ };
16
+ }
17
+
18
+ export function unmangleCompletionEntryDetails(
19
+ details: ts.CompletionEntryDetails,
20
+ ): ts.CompletionEntryDetails {
21
+ return {
22
+ ...details,
23
+ name: unmangle(details.name),
24
+ displayParts: details.displayParts.map(unmangleDisplayPart),
25
+ documentation: details.documentation?.map(unmangleDisplayPart),
26
+ };
27
+ }
@@ -0,0 +1,28 @@
1
+ import { unmangle } from "@backtickjs/compiler";
2
+ import type ts from "typescript";
3
+
4
+ export function unmangleDiagnostic<T extends ts.Diagnostic>(diagnostic: T): T {
5
+ return {
6
+ ...diagnostic,
7
+ messageText: unmangleMessageText(diagnostic.messageText),
8
+ relatedInformation: diagnostic.relatedInformation?.map((info) => ({
9
+ ...info,
10
+ messageText: unmangleMessageText(info.messageText),
11
+ })),
12
+ };
13
+ }
14
+
15
+ function unmangleMessageText(
16
+ messageText: string | ts.DiagnosticMessageChain,
17
+ ): string | ts.DiagnosticMessageChain {
18
+ if (typeof messageText === "string") {
19
+ return unmangle(messageText);
20
+ }
21
+ return {
22
+ ...messageText,
23
+ messageText: unmangle(messageText.messageText),
24
+ next: messageText.next?.map(
25
+ (chain) => unmangleMessageText(chain) as ts.DiagnosticMessageChain,
26
+ ),
27
+ };
28
+ }
@@ -0,0 +1,8 @@
1
+ import { unmangle } from "@backtickjs/compiler";
2
+ import type ts from "typescript";
3
+
4
+ export function unmangleDisplayPart(
5
+ part: ts.SymbolDisplayPart,
6
+ ): ts.SymbolDisplayPart {
7
+ return { ...part, text: unmangle(part.text) };
8
+ }
@@ -0,0 +1,10 @@
1
+ import type ts from "typescript";
2
+ import { unmangleDisplayPart } from "./unmangleDisplayPart";
3
+
4
+ export function unmangleQuickInfo(quickInfo: ts.QuickInfo): ts.QuickInfo {
5
+ return {
6
+ ...quickInfo,
7
+ displayParts: quickInfo.displayParts?.map(unmangleDisplayPart),
8
+ documentation: quickInfo.documentation?.map(unmangleDisplayPart),
9
+ };
10
+ }