@tsrx/typescript-plugin 0.3.33 → 0.3.35
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/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/language-CK5RucYm.js +4 -0
- package/dist/language-CK5RucYm.js.map +1 -0
- package/dist/package.json +1 -0
- package/dist/tsc.js +3 -0
- package/dist/tsc.js.map +1 -0
- package/package.json +11 -5
- package/CHANGELOG.md +0 -423
- package/src/index.js +0 -10
- package/src/language.js +0 -926
- package/src/utils.js +0 -57
- package/tests/compiler-resolution.test.js +0 -205
- package/tests/fixtures/workspaces/both/package.json +0 -9
- package/tests/fixtures/workspaces/both-react/package.json +0 -8
- package/tests/fixtures/workspaces/react-only/package.json +0 -8
- package/tests/fixtures/workspaces/ripple-only/package.json +0 -9
- package/tests/plugin-integration.test.js +0 -120
- package/tests/workspace-fixtures.js +0 -244
- package/tsconfig.json +0 -17
- package/tsconfig.typecheck.json +0 -8
- package/tsdown.config.js +0 -30
package/src/language.js
DELETED
|
@@ -1,926 +0,0 @@
|
|
|
1
|
-
/** @import { CodeMapping } from '@tsrx/ripple' */
|
|
2
|
-
/** @import {TSRXCompileError, VolarMappingsResult} from '@tsrx/ripple' */
|
|
3
|
-
|
|
4
|
-
/** @typedef {{ compile_to_volar_mappings(source: string, filename: string, options?: { loose?: boolean }): VolarMappingsResult }} TSRXCompilerModule */
|
|
5
|
-
|
|
6
|
-
/** @typedef {Map<string, CodeMapping>} CachedMappings */
|
|
7
|
-
/** @typedef {import('typescript').CompilerOptions} CompilerOptions */
|
|
8
|
-
/** @typedef {import('@volar/language-core').IScriptSnapshot} IScriptSnapshot */
|
|
9
|
-
/** @typedef {import('@volar/language-core').VirtualCode} VirtualCode */
|
|
10
|
-
/** @typedef {string | { fsPath: string }} ScriptId */
|
|
11
|
-
// Side-effect import: augments @volar/language-core's LanguagePlugin with the `typescript` field.
|
|
12
|
-
/** @typedef {typeof import('@volar/typescript')} _VolarTypeScriptAugmentation */
|
|
13
|
-
/** @typedef {import('@volar/language-core').LanguagePlugin<ScriptId, VirtualCode>} RippleLanguagePlugin */
|
|
14
|
-
|
|
15
|
-
/** @typedef {InstanceType<typeof import('./language.js')["TSRXVirtualCode"]>} TSRXVirtualCodeInstance */
|
|
16
|
-
|
|
17
|
-
import ts from 'typescript';
|
|
18
|
-
import { forEachEmbeddedCode } from '@volar/language-core';
|
|
19
|
-
import fs from 'fs';
|
|
20
|
-
import path from 'path';
|
|
21
|
-
import { createRequire } from 'module';
|
|
22
|
-
import { fileURLToPath } from 'url';
|
|
23
|
-
import { createLogging, DEBUG } from './utils.js';
|
|
24
|
-
|
|
25
|
-
const require = createRequire(import.meta.url);
|
|
26
|
-
const root_dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
-
|
|
28
|
-
const { log, logWarning, logError } = createLogging('[Ripple Language]');
|
|
29
|
-
export const RIPPLE_EXTENSIONS = ['.tsrx'];
|
|
30
|
-
/** @typedef {[string, string[], string[], string[]]} CompilerCandidate */
|
|
31
|
-
/** @type {CompilerCandidate[]} */
|
|
32
|
-
export const COMPILER_CANDIDATES = [
|
|
33
|
-
[
|
|
34
|
-
'@tsrx/ripple',
|
|
35
|
-
['node_modules', '@tsrx', 'ripple'],
|
|
36
|
-
['.tsrx'],
|
|
37
|
-
['@tsrx/ripple', 'ripple', '@ripple-ts/vite-plugin', '@ripple-ts/compat-react'],
|
|
38
|
-
],
|
|
39
|
-
[
|
|
40
|
-
'@tsrx/react',
|
|
41
|
-
['node_modules', '@tsrx', 'react'],
|
|
42
|
-
['.tsrx'],
|
|
43
|
-
['@tsrx/react', '@tsrx/vite-plugin-react'],
|
|
44
|
-
],
|
|
45
|
-
[
|
|
46
|
-
'@tsrx/solid',
|
|
47
|
-
['node_modules', '@tsrx', 'solid'],
|
|
48
|
-
['.tsrx'],
|
|
49
|
-
['@tsrx/solid', '@tsrx/vite-plugin-solid'],
|
|
50
|
-
],
|
|
51
|
-
[
|
|
52
|
-
'@tsrx/preact',
|
|
53
|
-
['node_modules', '@tsrx', 'preact'],
|
|
54
|
-
['.tsrx'],
|
|
55
|
-
['@tsrx/preact', '@tsrx/vite-plugin-preact'],
|
|
56
|
-
],
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {string} file_name
|
|
61
|
-
* @returns {boolean}
|
|
62
|
-
*/
|
|
63
|
-
export function is_ripple_file(file_name) {
|
|
64
|
-
return RIPPLE_EXTENSIONS.some((extension) => file_name.endsWith(extension));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @returns {RippleLanguagePlugin}
|
|
69
|
-
*/
|
|
70
|
-
export function getRippleLanguagePlugin() {
|
|
71
|
-
log('Creating Ripple language plugin...');
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
getLanguageId(fileNameOrUri) {
|
|
75
|
-
const file_name =
|
|
76
|
-
typeof fileNameOrUri === 'string'
|
|
77
|
-
? fileNameOrUri
|
|
78
|
-
: fileNameOrUri.fsPath.replace(/\\/g, '/');
|
|
79
|
-
if (is_ripple_file(file_name)) {
|
|
80
|
-
log('Identified Ripple file:', file_name);
|
|
81
|
-
return 'ripple';
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
createVirtualCode(fileNameOrUri, languageId, snapshot) {
|
|
85
|
-
if (languageId === 'ripple') {
|
|
86
|
-
const file_name = normalizeFileNameOrUri(fileNameOrUri);
|
|
87
|
-
const ripple = get_tsrx_compiler(file_name);
|
|
88
|
-
if (!ripple) {
|
|
89
|
-
logError(`Ripple compiler not found for file: ${file_name}`);
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
log('Creating virtual code for:', file_name);
|
|
93
|
-
try {
|
|
94
|
-
return new TSRXVirtualCode(file_name, snapshot, ripple);
|
|
95
|
-
} catch (err) {
|
|
96
|
-
logError('Failed to create virtual code for:', file_name, ':', err);
|
|
97
|
-
throw err;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return undefined;
|
|
101
|
-
},
|
|
102
|
-
updateVirtualCode(fileNameOrUri, virtualCode, snapshot) {
|
|
103
|
-
if (virtualCode instanceof TSRXVirtualCode) {
|
|
104
|
-
log('Updating existing virtual code for:', virtualCode.fileName);
|
|
105
|
-
virtualCode.update(snapshot);
|
|
106
|
-
return virtualCode;
|
|
107
|
-
}
|
|
108
|
-
return undefined;
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
typescript: {
|
|
112
|
-
extraFileExtensions: RIPPLE_EXTENSIONS.map((extension) => ({
|
|
113
|
-
extension: extension.slice(1),
|
|
114
|
-
isMixedContent: false,
|
|
115
|
-
scriptKind: 7,
|
|
116
|
-
})),
|
|
117
|
-
/**
|
|
118
|
-
* @param {VirtualCode} ripple_code
|
|
119
|
-
*/
|
|
120
|
-
getServiceScript(ripple_code) {
|
|
121
|
-
for (const code of forEachEmbeddedCode(ripple_code)) {
|
|
122
|
-
if (code.languageId === 'ripple') {
|
|
123
|
-
return {
|
|
124
|
-
code,
|
|
125
|
-
extension: '.tsx',
|
|
126
|
-
scriptKind: 4,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return undefined;
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* @implements {VirtualCode}
|
|
138
|
-
*/
|
|
139
|
-
export class TSRXVirtualCode {
|
|
140
|
-
/** @type {string} */
|
|
141
|
-
id = 'root';
|
|
142
|
-
/** @type {string} */
|
|
143
|
-
languageId = 'ripple';
|
|
144
|
-
/** @type {unknown[]} */
|
|
145
|
-
codegenStacks = [];
|
|
146
|
-
/** @type {TSRXCompilerModule} */
|
|
147
|
-
tsrx;
|
|
148
|
-
/** @type {string} */
|
|
149
|
-
generatedCode = '';
|
|
150
|
-
/** @type {VirtualCode['embeddedCodes']} */
|
|
151
|
-
embeddedCodes = [];
|
|
152
|
-
/** @type {CodeMapping[]} */
|
|
153
|
-
mappings = [];
|
|
154
|
-
/** @type {TSRXCompileError[]} */
|
|
155
|
-
fatalErrors = [];
|
|
156
|
-
/** @type {TSRXCompileError[]} */
|
|
157
|
-
usageErrors = [];
|
|
158
|
-
/** @type {IScriptSnapshot} */
|
|
159
|
-
snapshot;
|
|
160
|
-
/** @type {IScriptSnapshot} */
|
|
161
|
-
sourceSnapshot;
|
|
162
|
-
/** @type {string} */
|
|
163
|
-
originalCode = '';
|
|
164
|
-
/** @type {unknown[]} */
|
|
165
|
-
diagnostics = [];
|
|
166
|
-
/** @type {CachedMappings | null} */
|
|
167
|
-
#mappingGenToSource = null;
|
|
168
|
-
/** @type {CachedMappings | null} */
|
|
169
|
-
#mappingSourceToGen = null;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* @param {string} file_name
|
|
173
|
-
* @param {IScriptSnapshot} snapshot
|
|
174
|
-
* @param {TSRXCompilerModule} tsrx
|
|
175
|
-
*/
|
|
176
|
-
constructor(file_name, snapshot, tsrx) {
|
|
177
|
-
log('Initializing TSRXVirtualCode for:', file_name);
|
|
178
|
-
|
|
179
|
-
this.fileName = file_name;
|
|
180
|
-
this.tsrx = tsrx;
|
|
181
|
-
this.snapshot = snapshot;
|
|
182
|
-
this.sourceSnapshot = snapshot;
|
|
183
|
-
this.originalCode = snapshot.getText(0, snapshot.getLength());
|
|
184
|
-
|
|
185
|
-
// Validate ripple compiler
|
|
186
|
-
if (!tsrx || typeof tsrx.compile_to_volar_mappings !== 'function') {
|
|
187
|
-
logError('Invalid ripple compiler - missing compile_to_volar_mappings method');
|
|
188
|
-
throw new Error('Invalid ripple compiler');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.update(snapshot);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* @param {IScriptSnapshot} snapshot
|
|
196
|
-
* @returns {void}
|
|
197
|
-
*/
|
|
198
|
-
update(snapshot) {
|
|
199
|
-
log('Updating virtual code for:', this.fileName);
|
|
200
|
-
|
|
201
|
-
const newCode = snapshot.getText(0, snapshot.getLength());
|
|
202
|
-
const changeRange = snapshot.getChangeRange(this.sourceSnapshot);
|
|
203
|
-
this.sourceSnapshot = snapshot;
|
|
204
|
-
|
|
205
|
-
// Only clear mapping index - don't update snapshot/originalCode yet
|
|
206
|
-
this.#mappingGenToSource = null;
|
|
207
|
-
this.#mappingSourceToGen = null;
|
|
208
|
-
|
|
209
|
-
this.fatalErrors = [];
|
|
210
|
-
this.usageErrors = [];
|
|
211
|
-
|
|
212
|
-
/** @type {VolarMappingsResult | undefined} */
|
|
213
|
-
let transpiled;
|
|
214
|
-
|
|
215
|
-
// Check if a single "." was typed using changeRange
|
|
216
|
-
let isDotTyped = false;
|
|
217
|
-
let dotPosition = -1;
|
|
218
|
-
|
|
219
|
-
log('changeRange:', JSON.stringify(changeRange));
|
|
220
|
-
|
|
221
|
-
if (changeRange) {
|
|
222
|
-
const changeStart = changeRange.span.start;
|
|
223
|
-
const changeEnd = changeStart + changeRange.span.length;
|
|
224
|
-
const newEnd = changeStart + changeRange.newLength;
|
|
225
|
-
|
|
226
|
-
// Get the old text (what was replaced) from originalCode
|
|
227
|
-
const oldText = this.originalCode.substring(changeStart, changeEnd);
|
|
228
|
-
// Get the new text (what replaced it) from newCode
|
|
229
|
-
const newText = newCode.substring(changeStart, newEnd);
|
|
230
|
-
|
|
231
|
-
log('Change details:');
|
|
232
|
-
log(' Position:', changeStart, '-', changeEnd, '(length:', changeRange.span.length, ')');
|
|
233
|
-
log(' Old text:', JSON.stringify(oldText));
|
|
234
|
-
log(' New text:', JSON.stringify(newText), '(length:', changeRange.newLength, ')');
|
|
235
|
-
|
|
236
|
-
// Check if a dot was added at the end of the new text
|
|
237
|
-
if (newText.endsWith('.')) {
|
|
238
|
-
// The dot is at position newEnd - 1
|
|
239
|
-
// We need to check the character BEFORE the dot (inside the new text)
|
|
240
|
-
const charBeforeDot = newEnd > 1 ? newCode[newEnd - 2] : '';
|
|
241
|
-
log(' Char before dot:', JSON.stringify(charBeforeDot));
|
|
242
|
-
|
|
243
|
-
if (/[$#_\u200C\u200D\p{ID_Continue}\)\]\}]/u.test(charBeforeDot)) {
|
|
244
|
-
isDotTyped = true;
|
|
245
|
-
dotPosition = newEnd - 1; // Position of the dot
|
|
246
|
-
log('ChangeRange detected dot typed at position', dotPosition);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
// If user typed a ".", compile without it and then stitch it back into
|
|
253
|
-
// the generated output so completions can still resolve.
|
|
254
|
-
if (isDotTyped && dotPosition >= 0) {
|
|
255
|
-
const codeWithoutDot =
|
|
256
|
-
newCode.substring(0, dotPosition) + newCode.substring(dotPosition + 1);
|
|
257
|
-
|
|
258
|
-
log('Compiling without typed dot at position', dotPosition);
|
|
259
|
-
transpiled = this.tsrx.compile_to_volar_mappings(codeWithoutDot, this.fileName, {
|
|
260
|
-
loose: true,
|
|
261
|
-
});
|
|
262
|
-
log('Compilation without dot successful');
|
|
263
|
-
|
|
264
|
-
if (transpiled && transpiled.code && transpiled.mappings.length > 0) {
|
|
265
|
-
const insertedDotPosition = restore_typed_dot_in_transpiled_code(transpiled, dotPosition);
|
|
266
|
-
|
|
267
|
-
if (insertedDotPosition === null) {
|
|
268
|
-
logWarning('Failed to restore typed dot into transpiled output');
|
|
269
|
-
} else {
|
|
270
|
-
log('Inserted typed dot at generated position', insertedDotPosition);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
} else {
|
|
274
|
-
// Normal compilation
|
|
275
|
-
log('Compiling Ripple code...');
|
|
276
|
-
transpiled = this.tsrx.compile_to_volar_mappings(newCode, this.fileName, {
|
|
277
|
-
loose: true,
|
|
278
|
-
});
|
|
279
|
-
log('Compilation successful, generated code length:', transpiled?.code?.length || 0);
|
|
280
|
-
}
|
|
281
|
-
} catch (e) {
|
|
282
|
-
const error = /** @type {TSRXCompileError} */ (e);
|
|
283
|
-
logError('Ripple compilation failed for', this.fileName, ':', error);
|
|
284
|
-
error.type = 'fatal';
|
|
285
|
-
this.fatalErrors.push(error);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (transpiled && transpiled.code) {
|
|
289
|
-
// Successful compilation - update everything
|
|
290
|
-
this.originalCode = newCode;
|
|
291
|
-
this.generatedCode = transpiled.code;
|
|
292
|
-
this.mappings = transpiled.mappings ?? [];
|
|
293
|
-
this.usageErrors = transpiled.errors;
|
|
294
|
-
|
|
295
|
-
const cssMappings = transpiled.cssMappings;
|
|
296
|
-
if (cssMappings.length > 0) {
|
|
297
|
-
log('Creating', cssMappings.length, 'CSS embedded codes');
|
|
298
|
-
|
|
299
|
-
this.embeddedCodes = cssMappings.map((mapping, index) => {
|
|
300
|
-
const cssContent = /** @type {string} */ (mapping.data?.customData?.content);
|
|
301
|
-
log(
|
|
302
|
-
`CSS region ${index}: \
|
|
303
|
-
offset ${mapping.sourceOffsets[0]}-${mapping.sourceOffsets[0] + mapping.lengths[0]}, \
|
|
304
|
-
length ${mapping.lengths[0]}`,
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
id: /** @type {string} */ (mapping.data?.customData?.embeddedId),
|
|
309
|
-
languageId: 'css',
|
|
310
|
-
snapshot: {
|
|
311
|
-
getText: (/** @type {number} */ start, /** @type {number} */ end) =>
|
|
312
|
-
cssContent.substring(start, end),
|
|
313
|
-
getLength: () => mapping.lengths[0],
|
|
314
|
-
getChangeRange: () => undefined,
|
|
315
|
-
},
|
|
316
|
-
mappings: [mapping],
|
|
317
|
-
embeddedCodes: [],
|
|
318
|
-
};
|
|
319
|
-
});
|
|
320
|
-
} else {
|
|
321
|
-
this.embeddedCodes = [];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (DEBUG) {
|
|
325
|
-
log('CSS embedded codes:', (this.embeddedCodes || []).length);
|
|
326
|
-
log('Using transpiled code, mapping count:', this.mappings.length);
|
|
327
|
-
log('Original code length:', newCode.length);
|
|
328
|
-
log('Generated code length:', this.generatedCode.length);
|
|
329
|
-
log('Last 100 chars of original:', JSON.stringify(newCode.slice(-100)));
|
|
330
|
-
log('Last 200 chars of generated:', JSON.stringify(this.generatedCode.slice(-200)));
|
|
331
|
-
log('Last few mappings:');
|
|
332
|
-
const startIdx = Math.max(0, this.mappings.length - 5);
|
|
333
|
-
for (let i = startIdx; i < this.mappings.length; i++) {
|
|
334
|
-
const m = this.mappings[i];
|
|
335
|
-
log(
|
|
336
|
-
` Mapping ${i}: source[${m.sourceOffsets[0]}:${m.sourceOffsets[0] + m.lengths[0]}] -> gen[${m.generatedOffsets[0]}:${m.generatedOffsets[0] + m.lengths[0]}], len=${m.lengths[0]}, completion=${m.data?.completion}`,
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
this.snapshot = /** @type {IScriptSnapshot} */ ({
|
|
342
|
-
getText: (start, end) => this.generatedCode.substring(start, end),
|
|
343
|
-
getLength: () => this.generatedCode.length,
|
|
344
|
-
getChangeRange: () => undefined,
|
|
345
|
-
});
|
|
346
|
-
} else {
|
|
347
|
-
// When compilation fails, show where it failed and disable all
|
|
348
|
-
// TypeScript diagnostics until the compilation error is fixed
|
|
349
|
-
log('Compilation failed, only display where the compilation error occurred.');
|
|
350
|
-
|
|
351
|
-
this.originalCode = newCode;
|
|
352
|
-
this.generatedCode = newCode;
|
|
353
|
-
|
|
354
|
-
// Create 1:1 mappings for the entire content
|
|
355
|
-
this.mappings = [
|
|
356
|
-
{
|
|
357
|
-
sourceOffsets: [0],
|
|
358
|
-
generatedOffsets: [0],
|
|
359
|
-
lengths: [newCode.length],
|
|
360
|
-
generatedLengths: [newCode.length],
|
|
361
|
-
data: {
|
|
362
|
-
verification: true, // disable TS since we're using source code as generated code
|
|
363
|
-
customData: {},
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
];
|
|
367
|
-
|
|
368
|
-
// Extract CSS from <style>...</style> tags for embedded codes
|
|
369
|
-
this.embeddedCodes = extractCssFromSource(newCode);
|
|
370
|
-
|
|
371
|
-
this.snapshot = /** @type {IScriptSnapshot} */ ({
|
|
372
|
-
getText: (start, end) => this.generatedCode.substring(start, end),
|
|
373
|
-
getLength: () => this.generatedCode.length,
|
|
374
|
-
getChangeRange: () => undefined,
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
#buildMappingCache() {
|
|
380
|
-
if (this.#mappingGenToSource || this.#mappingSourceToGen) {
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
this.#mappingGenToSource = new Map();
|
|
385
|
-
this.#mappingSourceToGen = new Map();
|
|
386
|
-
|
|
387
|
-
var mapping, genStart, genLength, genEnd, genKey;
|
|
388
|
-
var sourceStart, sourceLength, sourceEnd, sourceKey;
|
|
389
|
-
for (var i = 0; i < this.mappings.length; i++) {
|
|
390
|
-
mapping = this.mappings[i];
|
|
391
|
-
|
|
392
|
-
genStart = mapping.generatedOffsets[0];
|
|
393
|
-
genLength = mapping.generatedLengths[0];
|
|
394
|
-
genEnd = genStart + genLength;
|
|
395
|
-
genKey = `${genStart}-${genEnd}`;
|
|
396
|
-
this.#mappingGenToSource.set(genKey, mapping);
|
|
397
|
-
|
|
398
|
-
sourceStart = mapping.sourceOffsets[0];
|
|
399
|
-
sourceLength = mapping.lengths[0];
|
|
400
|
-
sourceEnd = sourceStart + sourceLength;
|
|
401
|
-
sourceKey = `${sourceStart}-${sourceEnd}`;
|
|
402
|
-
this.#mappingSourceToGen.set(sourceKey, mapping);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Find mapping by generated range
|
|
408
|
-
* @param {number} start - The start offset of the range
|
|
409
|
-
* @param {number} end - The end offset of the range
|
|
410
|
-
* @returns {CodeMapping | null} The mapping for this range, or null if not found
|
|
411
|
-
*/
|
|
412
|
-
findMappingByGeneratedRange(start, end) {
|
|
413
|
-
this.#buildMappingCache();
|
|
414
|
-
return /** @type {CachedMappings} */ (this.#mappingGenToSource).get(`${start}-${end}`) ?? null;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Find mapping by source range
|
|
419
|
-
* @param {number} start - The start offset of the range
|
|
420
|
-
* @param {number} end - The end offset of the range
|
|
421
|
-
* @returns {CodeMapping | null} The mapping for this range, or null if not found
|
|
422
|
-
*/
|
|
423
|
-
findMappingBySourceRange(start, end) {
|
|
424
|
-
this.#buildMappingCache();
|
|
425
|
-
return /** @type {CachedMappings} */ (this.#mappingSourceToGen).get(`${start}-${end}`) ?? null;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Extract CSS content from <style>...</style> tags in source code
|
|
431
|
-
* @param {string} code - The source code to extract CSS from
|
|
432
|
-
* @returns {VirtualCode[]} Array of embedded CSS virtual codes
|
|
433
|
-
*/
|
|
434
|
-
function extractCssFromSource(code) {
|
|
435
|
-
/** @type {VirtualCode[]} */
|
|
436
|
-
const embeddedCodes = [];
|
|
437
|
-
const styleRegex = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
|
|
438
|
-
let match;
|
|
439
|
-
let index = 0;
|
|
440
|
-
|
|
441
|
-
while ((match = styleRegex.exec(code)) !== null) {
|
|
442
|
-
const fullMatch = match[0];
|
|
443
|
-
const cssContent = match[1];
|
|
444
|
-
const styleTagStart = match.index;
|
|
445
|
-
const openTagEnd = fullMatch.indexOf('>') + 1;
|
|
446
|
-
const cssStart = styleTagStart + openTagEnd;
|
|
447
|
-
const cssLength = cssContent.length;
|
|
448
|
-
|
|
449
|
-
log(`Extracted CSS region ${index}: offset ${cssStart}, length ${cssLength}`);
|
|
450
|
-
|
|
451
|
-
/** @type {CodeMapping} */
|
|
452
|
-
const mapping = {
|
|
453
|
-
sourceOffsets: [cssStart],
|
|
454
|
-
generatedOffsets: [0],
|
|
455
|
-
lengths: [cssLength],
|
|
456
|
-
generatedLengths: [cssLength],
|
|
457
|
-
data: {
|
|
458
|
-
verification: true,
|
|
459
|
-
completion: true,
|
|
460
|
-
semantic: true,
|
|
461
|
-
navigation: true,
|
|
462
|
-
structure: true,
|
|
463
|
-
format: false,
|
|
464
|
-
customData: {
|
|
465
|
-
content: cssContent,
|
|
466
|
-
embeddedId: `style_${index}`,
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
embeddedCodes.push({
|
|
472
|
-
id: `style_${index}`,
|
|
473
|
-
languageId: 'css',
|
|
474
|
-
snapshot: {
|
|
475
|
-
getText: (start, end) => cssContent.substring(start, end),
|
|
476
|
-
getLength: () => cssLength,
|
|
477
|
-
getChangeRange: () => undefined,
|
|
478
|
-
},
|
|
479
|
-
mappings: [mapping],
|
|
480
|
-
embeddedCodes: [],
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
index++;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (embeddedCodes.length > 0) {
|
|
487
|
-
log(`Extracted ${embeddedCodes.length} CSS embedded codes from style tags`);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
return embeddedCodes;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Insert a typed dot back into the transpiled code and update mappings so the
|
|
495
|
-
* source and generated offsets stay aligned for completion requests.
|
|
496
|
-
* @param {VolarMappingsResult} transpiled
|
|
497
|
-
* @param {number} dotPosition
|
|
498
|
-
* @returns {number | null}
|
|
499
|
-
*/
|
|
500
|
-
function restore_typed_dot_in_transpiled_code(transpiled, dotPosition) {
|
|
501
|
-
let dot_mapping = null;
|
|
502
|
-
|
|
503
|
-
for (const mapping of transpiled.mappings) {
|
|
504
|
-
const source_end = mapping.sourceOffsets[0] + mapping.lengths[0];
|
|
505
|
-
if (source_end === dotPosition) {
|
|
506
|
-
dot_mapping = mapping;
|
|
507
|
-
break;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
if (!dot_mapping) {
|
|
512
|
-
return null;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
const generated_length = dot_mapping.generatedLengths[0];
|
|
516
|
-
const insertedDotPosition = dot_mapping.generatedOffsets[0] + generated_length;
|
|
517
|
-
|
|
518
|
-
transpiled.code =
|
|
519
|
-
transpiled.code.substring(0, insertedDotPosition) +
|
|
520
|
-
'.' +
|
|
521
|
-
transpiled.code.substring(insertedDotPosition);
|
|
522
|
-
|
|
523
|
-
// Create a separate 1:1 mapping for the dot character instead of extending
|
|
524
|
-
// the existing mapping. When source and generated lengths differ (e.g.
|
|
525
|
-
// #ripple → _$__u0023_ripple), Volar's translateOffset uses
|
|
526
|
-
// Math.min(relativePos, toLength) which would map the cursor after the dot
|
|
527
|
-
// to the middle of the generated identifier instead of after it.
|
|
528
|
-
|
|
529
|
-
/** @type {CodeMapping} */
|
|
530
|
-
const new_dot_mapping = {
|
|
531
|
-
sourceOffsets: [dotPosition],
|
|
532
|
-
generatedOffsets: [insertedDotPosition],
|
|
533
|
-
lengths: [1],
|
|
534
|
-
generatedLengths: [1],
|
|
535
|
-
data: { ...dot_mapping.data },
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
// Find the index to insert after dot_mapping
|
|
539
|
-
const dot_mapping_index = transpiled.mappings.indexOf(dot_mapping);
|
|
540
|
-
transpiled.mappings.splice(dot_mapping_index + 1, 0, new_dot_mapping);
|
|
541
|
-
|
|
542
|
-
for (const mapping of transpiled.mappings) {
|
|
543
|
-
if (
|
|
544
|
-
mapping !== dot_mapping &&
|
|
545
|
-
mapping !== new_dot_mapping &&
|
|
546
|
-
mapping.generatedOffsets[0] >= insertedDotPosition
|
|
547
|
-
) {
|
|
548
|
-
mapping.generatedOffsets[0] += 1;
|
|
549
|
-
}
|
|
550
|
-
if (
|
|
551
|
-
mapping !== dot_mapping &&
|
|
552
|
-
mapping !== new_dot_mapping &&
|
|
553
|
-
mapping.sourceOffsets[0] >= dotPosition
|
|
554
|
-
) {
|
|
555
|
-
mapping.sourceOffsets[0] += 1;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
return insertedDotPosition;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* @template T
|
|
564
|
-
* @param {{ options?: CompilerOptions } & T} config
|
|
565
|
-
* @returns {{ options: CompilerOptions } & T}
|
|
566
|
-
*/
|
|
567
|
-
export const resolveConfig = (config) => {
|
|
568
|
-
const baseOptions = config.options ?? /** @type {CompilerOptions} */ ({});
|
|
569
|
-
/** @type {CompilerOptions} */
|
|
570
|
-
const options = { ...baseOptions };
|
|
571
|
-
|
|
572
|
-
// Default target: align with modern bundlers while staying configurable.
|
|
573
|
-
if (options.target === undefined) {
|
|
574
|
-
options.target = ts.ScriptTarget.ESNext;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/** @param {string} libName */
|
|
578
|
-
const normalizeLibName = (libName) => {
|
|
579
|
-
if (typeof libName !== 'string' || libName.length === 0) {
|
|
580
|
-
return undefined;
|
|
581
|
-
}
|
|
582
|
-
const trimmed = libName.trim();
|
|
583
|
-
if (trimmed.startsWith('lib.')) {
|
|
584
|
-
return trimmed.toLowerCase();
|
|
585
|
-
}
|
|
586
|
-
return `lib.${trimmed.toLowerCase().replace(/\s+/g, '').replace(/_/g, '.')}\.d.ts`;
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
const normalizedLibs = new Set(
|
|
590
|
-
(options.lib ?? []).map(normalizeLibName).filter((lib) => typeof lib === 'string'),
|
|
591
|
-
);
|
|
592
|
-
|
|
593
|
-
if (normalizedLibs.size === 0) {
|
|
594
|
-
const host = ts.createCompilerHost(options);
|
|
595
|
-
const defaultLibFileName = host.getDefaultLibFileName(options).toLowerCase();
|
|
596
|
-
normalizedLibs.add(defaultLibFileName);
|
|
597
|
-
normalizedLibs.add('lib.dom.d.ts');
|
|
598
|
-
normalizedLibs.add('lib.dom.iterable.d.ts');
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
options.lib = [...normalizedLibs];
|
|
602
|
-
|
|
603
|
-
// Default typeRoots: automatically discover @types like tsserver.
|
|
604
|
-
if (!options.types) {
|
|
605
|
-
const host = ts.createCompilerHost(options);
|
|
606
|
-
const typeRoots = ts.getEffectiveTypeRoots(options, host);
|
|
607
|
-
if (typeRoots && typeRoots.length > 0) {
|
|
608
|
-
options.typeRoots = typeRoots;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
return {
|
|
613
|
-
...config,
|
|
614
|
-
options,
|
|
615
|
-
};
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
/** @type {Map<string, string | null>} */
|
|
619
|
-
export const path2RipplePathMap = new Map();
|
|
620
|
-
/** @type {Map<string, string>} */
|
|
621
|
-
const pathToTypesCache = new Map();
|
|
622
|
-
/** @type {Map<string, RegExpMatchArray>} */
|
|
623
|
-
const typeNameMatchCache = new Map();
|
|
624
|
-
/** @type {Map<string, { name: string | null, dependencies: Set<string> } | null>} */
|
|
625
|
-
const pathToPackageManifestCache = new Map();
|
|
626
|
-
|
|
627
|
-
/**
|
|
628
|
-
* @param {ScriptId} fileNameOrUri
|
|
629
|
-
* @returns {string}
|
|
630
|
-
*/
|
|
631
|
-
export function normalizeFileNameOrUri(fileNameOrUri) {
|
|
632
|
-
return typeof fileNameOrUri === 'string'
|
|
633
|
-
? fileNameOrUri
|
|
634
|
-
: fileNameOrUri.fsPath.replace(/\\/g, '/');
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/**
|
|
638
|
-
* @param {string} start_dir
|
|
639
|
-
* @param {(file_path: import('fs').PathLike) => boolean} [exists_sync]
|
|
640
|
-
* @returns {{ name: string | null, dependencies: Set<string> } | null}
|
|
641
|
-
*/
|
|
642
|
-
function get_nearest_package_manifest(start_dir, exists_sync = fs.existsSync) {
|
|
643
|
-
let current_dir = start_dir;
|
|
644
|
-
/** @type {string[]} */
|
|
645
|
-
const visited_dirs = [];
|
|
646
|
-
|
|
647
|
-
while (current_dir) {
|
|
648
|
-
if (pathToPackageManifestCache.has(current_dir)) {
|
|
649
|
-
const cached_manifest = pathToPackageManifestCache.get(current_dir) ?? null;
|
|
650
|
-
for (const visited_dir of visited_dirs) {
|
|
651
|
-
pathToPackageManifestCache.set(visited_dir, cached_manifest);
|
|
652
|
-
}
|
|
653
|
-
return cached_manifest;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
visited_dirs.push(current_dir);
|
|
657
|
-
|
|
658
|
-
const package_json_path = path.join(current_dir, 'package.json');
|
|
659
|
-
if (exists_sync(package_json_path)) {
|
|
660
|
-
try {
|
|
661
|
-
const package_json = JSON.parse(fs.readFileSync(package_json_path, 'utf8'));
|
|
662
|
-
const dependencies = new Set([
|
|
663
|
-
...Object.keys(package_json.dependencies ?? {}),
|
|
664
|
-
...Object.keys(package_json.devDependencies ?? {}),
|
|
665
|
-
...Object.keys(package_json.peerDependencies ?? {}),
|
|
666
|
-
...Object.keys(package_json.optionalDependencies ?? {}),
|
|
667
|
-
]);
|
|
668
|
-
const package_manifest = {
|
|
669
|
-
name: typeof package_json.name === 'string' ? package_json.name : null,
|
|
670
|
-
dependencies,
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
for (const visited_dir of visited_dirs) {
|
|
674
|
-
pathToPackageManifestCache.set(visited_dir, package_manifest);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
return package_manifest;
|
|
678
|
-
} catch {
|
|
679
|
-
for (const visited_dir of visited_dirs) {
|
|
680
|
-
pathToPackageManifestCache.set(visited_dir, null);
|
|
681
|
-
}
|
|
682
|
-
return null;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
const parent_dir = path.dirname(current_dir);
|
|
687
|
-
if (parent_dir === current_dir) {
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
current_dir = parent_dir;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
for (const visited_dir of visited_dirs) {
|
|
694
|
-
pathToPackageManifestCache.set(visited_dir, null);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
return null;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* @param {{ name: string | null, dependencies: Set<string> } | null} package_manifest
|
|
702
|
-
* @param {string} compiler_name
|
|
703
|
-
* @param {string[]} package_hints
|
|
704
|
-
* @returns {boolean}
|
|
705
|
-
*/
|
|
706
|
-
function package_manifest_matches_compiler(package_manifest, compiler_name, package_hints) {
|
|
707
|
-
if (!package_manifest) {
|
|
708
|
-
return false;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
if (
|
|
712
|
-
package_manifest.name === compiler_name ||
|
|
713
|
-
package_hints.includes(package_manifest.name ?? '')
|
|
714
|
-
) {
|
|
715
|
-
return true;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if (package_manifest.dependencies.has(compiler_name)) {
|
|
719
|
-
return true;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
for (const package_hint of package_hints) {
|
|
723
|
-
if (package_manifest.dependencies.has(package_hint)) {
|
|
724
|
-
return true;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
return false;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
/**
|
|
732
|
-
* @param {string} normalized_file_name
|
|
733
|
-
* @returns {TSRXCompilerModule | undefined}
|
|
734
|
-
*/
|
|
735
|
-
function get_tsrx_compiler(normalized_file_name) {
|
|
736
|
-
const compiler_path = get_compiler_entry_for_file(normalized_file_name);
|
|
737
|
-
if (compiler_path) {
|
|
738
|
-
return require(compiler_path);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
* @param {string} normalized_file_name
|
|
744
|
-
* @param {(file_path: import('fs').PathLike) => boolean} [exists_sync]
|
|
745
|
-
* @param {Map<string, string | null>} [compiler_path_map]
|
|
746
|
-
* @returns {string | undefined}
|
|
747
|
-
*/
|
|
748
|
-
export function find_workspace_compiler_entry_for_file(
|
|
749
|
-
normalized_file_name,
|
|
750
|
-
exists_sync = fs.existsSync,
|
|
751
|
-
compiler_path_map = path2RipplePathMap,
|
|
752
|
-
) {
|
|
753
|
-
const parts = normalized_file_name.split('/');
|
|
754
|
-
const ext = path.extname(normalized_file_name);
|
|
755
|
-
|
|
756
|
-
for (let i = parts.length - 2; i >= 0; i--) {
|
|
757
|
-
const dir = parts.slice(0, i + 1).join('/');
|
|
758
|
-
const cache_key = dir + '\0' + ext;
|
|
759
|
-
|
|
760
|
-
if (!compiler_path_map.has(cache_key)) {
|
|
761
|
-
/** @type {Array<[string, string, string[]]>} */
|
|
762
|
-
const available_candidates = [];
|
|
763
|
-
for (const [
|
|
764
|
-
compiler_name,
|
|
765
|
-
compiler_dir_parts,
|
|
766
|
-
supported_extensions,
|
|
767
|
-
package_hints,
|
|
768
|
-
] of COMPILER_CANDIDATES) {
|
|
769
|
-
if (!supported_extensions.includes(ext)) {
|
|
770
|
-
continue;
|
|
771
|
-
}
|
|
772
|
-
const full_path = [dir, ...compiler_dir_parts, 'src', 'index.js'].join('/');
|
|
773
|
-
if (exists_sync(full_path)) {
|
|
774
|
-
available_candidates.push([compiler_name, full_path, package_hints]);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
let found_path = null;
|
|
779
|
-
if (available_candidates.length > 0) {
|
|
780
|
-
const package_manifest = get_nearest_package_manifest(dir, exists_sync);
|
|
781
|
-
const preferred_candidate = available_candidates.find(([compiler_name, , package_hints]) =>
|
|
782
|
-
package_manifest_matches_compiler(package_manifest, compiler_name, package_hints),
|
|
783
|
-
);
|
|
784
|
-
found_path = preferred_candidate?.[1] ?? available_candidates[0][1];
|
|
785
|
-
log('Found tsrx compiler at:', found_path, 'for extension:', ext);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
compiler_path_map.set(cache_key, found_path);
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
const compiler_path = compiler_path_map.get(cache_key);
|
|
792
|
-
if (compiler_path) {
|
|
793
|
-
return compiler_path;
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
/**
|
|
799
|
-
* @param {string} normalized_file_name
|
|
800
|
-
* @returns {string | undefined}
|
|
801
|
-
*/
|
|
802
|
-
export function get_compiler_entry_for_file(normalized_file_name) {
|
|
803
|
-
const ext = path.extname(normalized_file_name);
|
|
804
|
-
const package_manifest = get_nearest_package_manifest(path.dirname(normalized_file_name));
|
|
805
|
-
|
|
806
|
-
const workspace_compiler_path = find_workspace_compiler_entry_for_file(normalized_file_name);
|
|
807
|
-
if (workspace_compiler_path) {
|
|
808
|
-
return workspace_compiler_path;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
const warn_message = `No supported tsrx compiler found in workspace for ${normalized_file_name}.`;
|
|
812
|
-
|
|
813
|
-
// Fallback: look for a packaged compiler.
|
|
814
|
-
let current_dir = root_dirname;
|
|
815
|
-
|
|
816
|
-
while (current_dir) {
|
|
817
|
-
/** @type {Array<[string, string, string[]]>} */
|
|
818
|
-
const available_candidates = [];
|
|
819
|
-
for (const [
|
|
820
|
-
compiler_name,
|
|
821
|
-
compiler_dir_parts,
|
|
822
|
-
supported_extensions,
|
|
823
|
-
package_hints,
|
|
824
|
-
] of COMPILER_CANDIDATES) {
|
|
825
|
-
if (!supported_extensions.includes(ext)) {
|
|
826
|
-
continue;
|
|
827
|
-
}
|
|
828
|
-
const full_path = path.join(current_dir, ...compiler_dir_parts);
|
|
829
|
-
const entry_path = path.join(full_path, 'src', 'index.js');
|
|
830
|
-
if (fs.existsSync(entry_path)) {
|
|
831
|
-
available_candidates.push([compiler_name, entry_path, package_hints]);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
if (available_candidates.length > 0) {
|
|
836
|
-
const preferred_candidate = available_candidates.find(([compiler_name, , package_hints]) =>
|
|
837
|
-
package_manifest_matches_compiler(package_manifest, compiler_name, package_hints),
|
|
838
|
-
);
|
|
839
|
-
const entry_path = preferred_candidate?.[1] ?? available_candidates[0][1];
|
|
840
|
-
logWarning(`${warn_message} Using packaged version at ${entry_path}`);
|
|
841
|
-
return entry_path;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
const parent_dir = path.dirname(current_dir);
|
|
845
|
-
if (parent_dir === current_dir) {
|
|
846
|
-
break;
|
|
847
|
-
}
|
|
848
|
-
current_dir = parent_dir;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
return undefined;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
/**
|
|
855
|
-
* @param {string} typesFilePath
|
|
856
|
-
* @returns {string | undefined}
|
|
857
|
-
*/
|
|
858
|
-
export function getCachedTypeDefinitionFile(typesFilePath) {
|
|
859
|
-
const cached = pathToTypesCache.get(typesFilePath);
|
|
860
|
-
if (cached) {
|
|
861
|
-
return cached;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
if (!fs.existsSync(typesFilePath)) {
|
|
865
|
-
logWarning(`Types file does not exist at path: ${typesFilePath}`);
|
|
866
|
-
return;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
log(`Found ripple types at: ${typesFilePath}`);
|
|
870
|
-
|
|
871
|
-
// Read the file to find the class definition offset
|
|
872
|
-
const fileContent = fs.readFileSync(typesFilePath, 'utf8');
|
|
873
|
-
|
|
874
|
-
if (!fileContent) {
|
|
875
|
-
logWarning(`Failed to read content of types file at: ${typesFilePath}`);
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
pathToTypesCache.set(typesFilePath, fileContent);
|
|
880
|
-
return fileContent;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/**
|
|
884
|
-
* @param {string} typeName
|
|
885
|
-
* @param {string} text
|
|
886
|
-
* @returns {RegExpMatchArray | undefined}
|
|
887
|
-
*/
|
|
888
|
-
export function getCachedTypeMatches(typeName, text) {
|
|
889
|
-
const cached = typeNameMatchCache.get(typeName);
|
|
890
|
-
if (cached) {
|
|
891
|
-
return cached;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
const searchPattern = new RegExp(
|
|
895
|
-
`(?:export\\s+(?:declare\\s+)?|declare\\s+)(class|function)\\s+${typeName}`,
|
|
896
|
-
);
|
|
897
|
-
|
|
898
|
-
const match = text.match(searchPattern);
|
|
899
|
-
|
|
900
|
-
if (match && match.index !== undefined) {
|
|
901
|
-
typeNameMatchCache.set(typeName, match);
|
|
902
|
-
return match;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
return;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
/**
|
|
909
|
-
* @param {string} normalized_file_name
|
|
910
|
-
* @returns {string | undefined}
|
|
911
|
-
*/
|
|
912
|
-
export function get_compiler_dir_for_file(normalized_file_name) {
|
|
913
|
-
const entry = get_compiler_entry_for_file(normalized_file_name);
|
|
914
|
-
if (entry) {
|
|
915
|
-
// Walk up from .../src/index.js to the package root
|
|
916
|
-
return path.dirname(path.dirname(entry));
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
export { get_compiler_dir_for_file as getRippleDirForFile };
|
|
921
|
-
|
|
922
|
-
/** Reset module-level state used in tests. */
|
|
923
|
-
export function _reset_for_test() {
|
|
924
|
-
path2RipplePathMap.clear();
|
|
925
|
-
pathToPackageManifestCache.clear();
|
|
926
|
-
}
|