@ripple-ts/language-server 0.3.41 → 0.3.43

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.
@@ -1,139 +0,0 @@
1
- /**
2
- @import {
3
- LanguageServicePlugin,
4
- LanguageServicePluginInstance,
5
- LanguageServiceContext,
6
- Diagnostic,
7
- } from '@volar/language-server';
8
- @import {TextDocument} from 'vscode-languageserver-textdocument';
9
- */
10
-
11
- import { getVirtualCode, createLogging, deobfuscateIdentifiers } from './utils.js';
12
-
13
- const { log, logError } = createLogging('[Ripple TypeScript Diagnostic Plugin]');
14
-
15
- /**
16
- * @param {Diagnostic} diagnostic
17
- * @param {Diagnostic[]} items
18
- */
19
- function process(diagnostic, items) {
20
- diagnostic.message = deobfuscateIdentifiers(diagnostic.message);
21
- items.push(diagnostic);
22
- }
23
-
24
- /**
25
- * Filter diagnostics based on suppressed diagnostic codes in mappings.
26
- * @param {TextDocument} document
27
- * @param {LanguageServiceContext} context
28
- * @param {Diagnostic[]} diagnostics
29
- * @returns {Diagnostic[]}
30
- */
31
- function processDiagnostics(document, context, diagnostics) {
32
- if (!diagnostics || diagnostics.length === 0) {
33
- return diagnostics;
34
- }
35
-
36
- log(`Filtering ${diagnostics.length} TypeScript diagnostics for ${document.uri}`);
37
-
38
- const { virtualCode } = getVirtualCode(document, context);
39
-
40
- if (!virtualCode || virtualCode.languageId !== 'ripple') {
41
- return diagnostics;
42
- }
43
-
44
- /** @type {Diagnostic[]} */
45
- const result = [];
46
-
47
- for (const diagnostic of diagnostics) {
48
- if (virtualCode.fatalErrors.length > 0 && diagnostic.code !== 'tsrx-compile-error') {
49
- // skip all TS diagnostics since we're dealing directly with the source code
50
- continue;
51
- }
52
-
53
- const range = diagnostic.range;
54
- const rangeStart = document.offsetAt(range.start);
55
- const rangeEnd = document.offsetAt(range.end);
56
- const mapping = virtualCode.findMappingByGeneratedRange(rangeStart, rangeEnd);
57
-
58
- if (!mapping) {
59
- process(diagnostic, result);
60
- continue;
61
- }
62
-
63
- const suppressedCodes = mapping.data.customData?.suppressedDiagnostics;
64
-
65
- if (!suppressedCodes || suppressedCodes.length === 0) {
66
- process(diagnostic, result);
67
- continue;
68
- }
69
-
70
- const diagnosticCode =
71
- typeof diagnostic.code === 'number'
72
- ? diagnostic.code
73
- : typeof diagnostic.code === 'string'
74
- ? parseInt(diagnostic.code)
75
- : null;
76
-
77
- if (diagnosticCode && suppressedCodes.includes(diagnosticCode)) {
78
- log(`Suppressing diagnostic ${diagnosticCode}: ${diagnostic.message}`);
79
- continue;
80
- }
81
-
82
- process(diagnostic, result);
83
- }
84
-
85
- log(`Filtered from ${diagnostics.length} to ${result.length} diagnostics`);
86
- return result;
87
- }
88
-
89
- /**
90
- * Creates a plugin that wraps typescript-semantic's provideDiagnostics
91
- * to filter out suppressed diagnostics while maintaining the original
92
- * plugin association. This is crucial for code actions (like "Add import")
93
- * to work correctly, as volar matches diagnostics by pluginIndex.
94
- * @returns {LanguageServicePlugin}
95
- */
96
- export function createTypeScriptDiagnosticFilterPlugin() {
97
- log('Creating TypeScript diagnostic filter plugin...');
98
-
99
- return {
100
- name: 'ripple-typescript-diagnostic-filter',
101
- // No capabilities - this plugin only wraps typescript-semantic
102
- capabilities: {},
103
- create(context) {
104
- /** @type {LanguageServicePluginInstance['provideDiagnostics'] | undefined} */
105
- let originalProvider;
106
- /** @type {LanguageServicePluginInstance | undefined} */
107
- let originalInstance;
108
-
109
- for (const [plugin, instance] of context.plugins) {
110
- if (plugin.name === 'typescript-semantic') {
111
- originalInstance = instance;
112
- originalProvider = instance.provideDiagnostics;
113
-
114
- // Wrap the original function to filter diagnostics
115
- // This maintains the plugin association for code actions
116
- instance.provideDiagnostics = async function (document, token) {
117
- const diagnostics = await originalProvider?.call(originalInstance, document, token);
118
- return processDiagnostics(document, context, diagnostics ?? []);
119
- };
120
-
121
- log('Successfully wrapped typescript-semantic provideDiagnostics');
122
-
123
- break;
124
- }
125
- }
126
-
127
- if (!originalProvider) {
128
- logError(
129
- "'typescript-semantic plugin' was not found or has no 'provideDiagnostics'. \
130
- This plugin must be loaded after Volar's typescript-semantic plugin.",
131
- );
132
- }
133
-
134
- // This plugin doesn't provide any functionality itself,
135
- // it only wraps typescript-semantic
136
- return {};
137
- },
138
- };
139
- }
@@ -1,49 +0,0 @@
1
- /**
2
- * @import {LanguageServiceContext} from '@volar/language-server'
3
- * @import {TextDocument} from 'vscode-languageserver-textdocument'
4
- */
5
-
6
- import { createRequire } from 'node:module';
7
-
8
- // Monkey-patch getUserPreferences to inject Ripple-specific defaults.
9
- // We use createRequire to get the raw CJS module.exports object, bypassing
10
- // the bundler's __toESM wrapper which interferes with property assignment.
11
- // volar-service-typescript is also externalized (via regex in tsdown config)
12
- // so that its internal consumers (semantic.js, codeAction.js, etc.) load
13
- // getUserPreferences from the same Node module cache entry we patch here.
14
- const require = createRequire(import.meta.url);
15
- const getUserPreferencesModule = require('volar-service-typescript/lib/configs/getUserPreferences');
16
- const { create } = require('volar-service-typescript');
17
-
18
- const originalGetUserPreferences = getUserPreferencesModule.getUserPreferences;
19
-
20
- /**
21
- * Enhanced getUserPreferences to add all ts and ripple preferences
22
- * Specifically makes preferTypeOnlyAutoImports true if not set
23
- * @param {LanguageServiceContext} context
24
- * @param {TextDocument} document
25
- */
26
- getUserPreferencesModule.getUserPreferences = async function (context, document) {
27
- const origPreferences = await originalGetUserPreferences.call(this, context, document);
28
-
29
- const [tsConfig, rippleConfig] = await Promise.all([
30
- context.env.getConfiguration?.('typescript'),
31
- context.env.getConfiguration?.('ripple'),
32
- ]);
33
-
34
- return {
35
- preferTypeOnlyAutoImports: true,
36
- ...origPreferences,
37
- .../** @type {any} */ (tsConfig)?.preferences,
38
- .../** @type {any} */ (rippleConfig)?.preferences,
39
- };
40
- };
41
-
42
- /**
43
- * Create TypeScript services with Ripple-specific enhancements.
44
- * @param {typeof import('typescript')} ts
45
- * @returns {ReturnType<typeof create>}
46
- */
47
- export function createTypeScriptServices(ts) {
48
- return create(ts);
49
- }
package/src/utils.js DELETED
@@ -1,162 +0,0 @@
1
- /** @import { TextDocument } from 'vscode-languageserver-textdocument' */
2
- /** @import { LanguageServiceContext, Mapper, SourceScript } from '@volar/language-server' */
3
- /** @import {TSRXVirtualCodeInstance} from '@tsrx/typescript-plugin/src/language.js'; */
4
- /** @import { isIdentifierObfuscated, deobfuscateIdentifier, IDENTIFIER_OBFUSCATION_PREFIX } from '@tsrx/core' */
5
-
6
- import { URI } from 'vscode-uri';
7
- import {
8
- createLogging,
9
- getWordFromPosition,
10
- charAllowedWordRegex,
11
- DEBUG,
12
- } from '@tsrx/typescript-plugin/src/utils.js';
13
-
14
- const IMPORT_EXPORT_REGEX = {
15
- import: {
16
- findBefore: /import\s+(?:\{[^}]*|\*\s+as\s+\w*|\w*)$/s,
17
- sameLine: /^import\s/,
18
- },
19
- export: {
20
- findBefore: /export\s+(?:\{[^}]*|\*\s+as\s+\w*|\w*)$/s,
21
- sameLine: /^export\s/,
22
- },
23
- from: /from\s*['"][^'"]*['"]\s*;?/,
24
- };
25
-
26
- export const RIPPLE_EXTENSIONS = ['.tsrx'];
27
-
28
- /** @type {typeof isIdentifierObfuscated} */
29
- let is_identifier_obfuscated;
30
- /** @type {typeof deobfuscateIdentifier} */
31
- let deobfuscate_identifier;
32
- /** @type {typeof IDENTIFIER_OBFUSCATION_PREFIX} */
33
- let identifier_obfuscation_prefix;
34
- /** @type {RegExp} */
35
- let obfuscated_identifier_regex;
36
-
37
- import('@tsrx/core').then((imports) => {
38
- is_identifier_obfuscated = imports.isIdentifierObfuscated;
39
- deobfuscate_identifier = imports.deobfuscateIdentifier;
40
- identifier_obfuscation_prefix = imports.IDENTIFIER_OBFUSCATION_PREFIX;
41
- obfuscated_identifier_regex = new RegExp(
42
- escapeRegExp(identifier_obfuscation_prefix) + charAllowedWordRegex.source + '+',
43
- 'gm',
44
- );
45
- });
46
-
47
- /**
48
- * @param {string} source
49
- * @returns {string}
50
- */
51
- function escapeRegExp(source) {
52
- // $& means the whole matched source
53
- return source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
54
- }
55
-
56
- /**
57
- * @param {string} text
58
- * @returns {string}
59
- */
60
- export function deobfuscateIdentifiers(text) {
61
- return text.replace(obfuscated_identifier_regex, (match) => deobfuscate_identifier(match));
62
- }
63
-
64
- /**
65
- * @param {...string} contents
66
- * @returns string
67
- */
68
- export function concatMarkdownContents(...contents) {
69
- return contents.join('\n\n<br>\n\n---\n\n<br><br>\n\n');
70
- }
71
-
72
- /**
73
- * Get virtual code from the encoded document URI
74
- * @param {LanguageServiceContext} context
75
- * @param {TextDocument} document
76
- * @returns
77
- {{
78
- virtualCode: TSRXVirtualCodeInstance;
79
- sourceUri: URI;
80
- sourceScript: SourceScript<URI> | undefined;
81
- sourceMap: Mapper | undefined;
82
- }}
83
- */
84
- export function getVirtualCode(document, context) {
85
- const uri = URI.parse(document.uri);
86
- const decoded = /** @type {[documentUri: URI, embeddedCodeId: string]} */ (
87
- context.decodeEmbeddedDocumentUri(uri)
88
- );
89
- const [sourceUri, virtualCodeId] = decoded;
90
- const sourceScript = context.language.scripts.get(sourceUri);
91
- const virtualCode = /** @type {TSRXVirtualCodeInstance} */ (
92
- sourceScript?.generated?.embeddedCodes.get(virtualCodeId)
93
- );
94
-
95
- const sourceMap =
96
- sourceScript && virtualCode ? context.language.maps.get(virtualCode, sourceScript) : undefined;
97
-
98
- return { virtualCode, sourceUri, sourceScript, sourceMap };
99
- }
100
-
101
- /**
102
- * @param {'import' | 'export'} type
103
- * @param {string} text
104
- * @param {number} start
105
- * @returns {boolean}
106
- */
107
- function isInsideImportOrExport(type, text, start) {
108
- const textBeforeCursor = text.slice(0, start);
109
-
110
- // Find the last 'import' keyword before cursor
111
- const lastImportMatch = textBeforeCursor.match(IMPORT_EXPORT_REGEX[type].findBefore);
112
- if (!lastImportMatch) {
113
- // Check if we're on a line that starts with import
114
- const lineStart = textBeforeCursor.lastIndexOf('\n') + 1;
115
- const lineBeforeCursor = textBeforeCursor.slice(lineStart);
116
- return IMPORT_EXPORT_REGEX[type].sameLine.test(lineBeforeCursor.trim());
117
- }
118
-
119
- // We found an import - check if it's been closed with 'from'
120
- const importStart = textBeforeCursor.lastIndexOf(type);
121
- const textFromImport = text.slice(importStart);
122
-
123
- // Find the end of this import statement (semicolon or newline after 'from "..."')
124
- const fromMatch = textFromImport.match(IMPORT_EXPORT_REGEX.from);
125
- if (!fromMatch || fromMatch.index === undefined) {
126
- // No 'from' found yet - we're inside an incomplete import
127
- return true;
128
- }
129
-
130
- const importEndOffset = importStart + fromMatch.index + fromMatch[0].length;
131
-
132
- // If cursor is before the import ends, we're inside it
133
- return start < importEndOffset;
134
- }
135
-
136
- /**
137
- * @param {string} text
138
- * @param {number} start
139
- * @returns {boolean}
140
- */
141
- export function isInsideImport(text, start) {
142
- return isInsideImportOrExport('import', text, start);
143
- }
144
-
145
- /**
146
- * @param {string} text
147
- * @param {number} start
148
- * @returns {boolean}
149
- */
150
- export function isInsideExport(text, start) {
151
- return isInsideImportOrExport('export', text, start);
152
- }
153
-
154
- /**
155
- * @param {string} document_uri
156
- * @returns {boolean}
157
- */
158
- export function is_ripple_document(document_uri) {
159
- return RIPPLE_EXTENSIONS.some((extension) => document_uri.endsWith(extension));
160
- }
161
-
162
- export { createLogging, getWordFromPosition, DEBUG };
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "esnext",
4
- "moduleResolution": "bundler",
5
- "target": "esnext",
6
- "lib": ["esnext"],
7
- "allowJs": true,
8
- "checkJs": true,
9
- "noEmit": true,
10
- "strict": true,
11
- "esModuleInterop": true,
12
- "resolveJsonModule": true,
13
- "types": []
14
- },
15
- "include": ["src/**/*.js", "index.js", "bin/**/*.js"],
16
- "exclude": ["node_modules", "dist"]
17
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "lib": ["esnext", "dom", "dom.iterable"],
5
- "skipLibCheck": true
6
- }
7
- }
package/tsdown.config.js DELETED
@@ -1,41 +0,0 @@
1
- import { defineConfig } from 'tsdown';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
-
6
- const dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const isDev = process.env.NODE_ENV !== 'production';
8
- const ROOT_EXTERNAL_PACKAGES = [
9
- '@tsrx/react',
10
- '@tsrx/ripple',
11
- '@tsrx/core',
12
- 'typescript',
13
- 'vscode-uri',
14
- // need this for monkey patching
15
- 'volar-service-typescript',
16
- /* also definitely need it for monkey patching */
17
- /^volar-service-typescript(?:\/.*)?$/,
18
- ];
19
-
20
- export default defineConfig({
21
- inlineOnly: false,
22
- entry: ['src/server.js', 'bin/language-server.js'],
23
- format: ['cjs'],
24
- outExtensions: () => ({ js: '.js' }),
25
- platform: 'node',
26
- target: 'node20',
27
- outDir: 'dist',
28
- sourcemap: isDev,
29
- outputOptions: {
30
- legalComments: 'inline',
31
- minify: false,
32
- },
33
- external: [...ROOT_EXTERNAL_PACKAGES],
34
- clean: true,
35
- noExternal: /.+/,
36
- hooks: {
37
- 'build:done': () => {
38
- fs.writeFileSync(path.join(dirname, 'dist', 'package.json'), '{"type":"commonjs"}\n');
39
- },
40
- },
41
- });