@ripple-ts/typescript-plugin 0.2.192 → 0.2.193

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/language.js +88 -18
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "TypeScript plugin for Ripple",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.192",
6
+ "version": "0.2.193",
7
7
  "main": "src/index.js",
8
8
  "homepage": "https://ripplejs.com",
9
9
  "repository": {
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "peerDependencies": {
25
25
  "typescript": "^5.9.2",
26
- "ripple": "0.2.192"
26
+ "ripple": "0.2.193"
27
27
  },
28
28
  "scripts": {
29
29
  "build": "tsdown",
package/src/language.js CHANGED
@@ -1,16 +1,15 @@
1
- /** @type {import('typescript')} */
2
1
  // @ts-expect-error type-only import from ESM module into CJS is fine
3
2
  /** @import { CodeMapping } from 'ripple/compiler' */
3
+ // @ts-expect-error type-only import from ESM module into CJS is fine
4
+ /** @import {RippleCompiler, RippleCompileError, VolarMappingsResult} from 'ripple/compiler' */
5
+
4
6
  /** @typedef {Map<string, CodeMapping>} CachedMappings */
5
7
  /** @typedef {import('typescript').CompilerOptions} CompilerOptions */
6
- /** @typedef {Error & { pos?: number }} RippleError */
7
8
  /** @typedef {import('@volar/language-core').IScriptSnapshot} IScriptSnapshot */
8
9
  /** @typedef {import('@volar/language-core').VirtualCode} VirtualCode */
9
10
  /** @typedef {string | { fsPath: string }} ScriptId */
10
11
  /** @typedef {import('@volar/typescript')} */
11
12
  /** @typedef {import('@volar/language-core').LanguagePlugin<ScriptId, VirtualCode>} RippleLanguagePlugin */
12
- // @ts-expect-error type-only import from ESM module into CJS is fine
13
- /** @typedef {import('ripple/compiler')} RippleCompiler */
14
13
 
15
14
  const ts = require('typescript');
16
15
  const { forEachEmbeddedCode } = require('@volar/language-core');
@@ -103,10 +102,10 @@ class RippleVirtualCode {
103
102
  embeddedCodes = [];
104
103
  /** @type {CodeMapping[]} */
105
104
  mappings = [];
106
- /** @type {boolean} */
107
- isErrorMode = false;
108
- /** @type {RippleError[]} */
109
- errors = [];
105
+ /** @type {RippleCompileError[]} */
106
+ fatalErrors = [];
107
+ /** @type {RippleCompileError[]} */
108
+ usageErrors = [];
110
109
  /** @type {IScriptSnapshot} */
111
110
  snapshot;
112
111
  /** @type {IScriptSnapshot} */
@@ -158,7 +157,10 @@ class RippleVirtualCode {
158
157
  this.#mappingGenToSource = null;
159
158
  this.#mappingSourceToGen = null;
160
159
 
161
- /** @type {ReturnType<RippleCompiler['compile_to_volar_mappings']> | undefined} */
160
+ this.fatalErrors = [];
161
+ this.usageErrors = [];
162
+
163
+ /** @type {VolarMappingsResult | undefined} */
162
164
  let transpiled;
163
165
 
164
166
  // Check if a single "." was typed using changeRange
@@ -234,7 +236,6 @@ class RippleVirtualCode {
234
236
  log('Replaced placeholder at position', placeholderPosInGenerated, 'with dot');
235
237
  }
236
238
  }
237
- this.errors = [];
238
239
  } else {
239
240
  // Normal compilation
240
241
  log('Compiling Ripple code...');
@@ -242,11 +243,12 @@ class RippleVirtualCode {
242
243
  loose: true,
243
244
  });
244
245
  log('Compilation successful, generated code length:', transpiled?.code?.length || 0);
245
- this.errors = [];
246
246
  }
247
- } catch (error) {
247
+ } catch (e) {
248
+ const error = /** @type {RippleCompileError} */ (e);
248
249
  logError('Ripple compilation failed for', this.fileName, ':', error);
249
- this.errors.push(/** @type {Error & { pos?: number }} */ (error));
250
+ error.type = 'fatal';
251
+ this.fatalErrors.push(error);
250
252
  }
251
253
 
252
254
  if (transpiled && transpiled.code) {
@@ -254,7 +256,7 @@ class RippleVirtualCode {
254
256
  this.originalCode = newCode;
255
257
  this.generatedCode = transpiled.code;
256
258
  this.mappings = transpiled.mappings ?? [];
257
- this.isErrorMode = false;
259
+ this.usageErrors = transpiled.errors;
258
260
 
259
261
  const cssMappings = transpiled.cssMappings;
260
262
  if (cssMappings.length > 0) {
@@ -312,22 +314,25 @@ class RippleVirtualCode {
312
314
  log('Compilation failed, only display where the compilation error occurred.');
313
315
 
314
316
  this.originalCode = newCode;
315
- this.generatedCode = 'export {};\n';
316
- this.isErrorMode = true;
317
+ this.generatedCode = newCode;
317
318
 
318
319
  // Create 1:1 mappings for the entire content
319
320
  this.mappings = [
320
321
  {
321
- sourceOffsets: [this.errors[0]?.pos ?? 0],
322
+ sourceOffsets: [0],
322
323
  generatedOffsets: [0],
323
324
  lengths: [newCode.length],
325
+ generatedLengths: [newCode.length],
324
326
  data: {
325
- verification: true,
327
+ verification: true, // disable TS since we're using source code as generated code
326
328
  customData: { generatedLengths: [newCode.length] },
327
329
  },
328
330
  },
329
331
  ];
330
332
 
333
+ // Extract CSS from <style>...</style> tags for embedded codes
334
+ this.embeddedCodes = extractCssFromSource(newCode);
335
+
331
336
  this.snapshot = /** @type {IScriptSnapshot} */ ({
332
337
  getText: (start, end) => this.generatedCode.substring(start, end),
333
338
  getLength: () => this.generatedCode.length,
@@ -386,6 +391,71 @@ class RippleVirtualCode {
386
391
  }
387
392
  }
388
393
 
394
+ /**
395
+ * Extract CSS content from <style>...</style> tags in source code
396
+ * @param {string} code - The source code to extract CSS from
397
+ * @returns {VirtualCode[]} Array of embedded CSS virtual codes
398
+ */
399
+ function extractCssFromSource(code) {
400
+ /** @type {VirtualCode[]} */
401
+ const embeddedCodes = [];
402
+ const styleRegex = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
403
+ let match;
404
+ let index = 0;
405
+
406
+ while ((match = styleRegex.exec(code)) !== null) {
407
+ const fullMatch = match[0];
408
+ const cssContent = match[1];
409
+ const styleTagStart = match.index;
410
+ const openTagEnd = fullMatch.indexOf('>') + 1;
411
+ const cssStart = styleTagStart + openTagEnd;
412
+ const cssLength = cssContent.length;
413
+
414
+ log(`Extracted CSS region ${index}: offset ${cssStart}, length ${cssLength}`);
415
+
416
+ /** @type {CodeMapping} */
417
+ const mapping = {
418
+ sourceOffsets: [cssStart],
419
+ generatedOffsets: [0],
420
+ lengths: [cssLength],
421
+ generatedLengths: [cssLength],
422
+ data: {
423
+ verification: true,
424
+ completion: true,
425
+ semantic: true,
426
+ navigation: true,
427
+ structure: true,
428
+ format: false,
429
+ customData: {
430
+ generatedLengths: [cssLength],
431
+ content: cssContent,
432
+ embeddedId: `style_${index}`,
433
+ },
434
+ },
435
+ };
436
+
437
+ embeddedCodes.push({
438
+ id: `style_${index}`,
439
+ languageId: 'css',
440
+ snapshot: {
441
+ getText: (start, end) => cssContent.substring(start, end),
442
+ getLength: () => cssLength,
443
+ getChangeRange: () => undefined,
444
+ },
445
+ mappings: [mapping],
446
+ embeddedCodes: [],
447
+ });
448
+
449
+ index++;
450
+ }
451
+
452
+ if (embeddedCodes.length > 0) {
453
+ log(`Extracted ${embeddedCodes.length} CSS embedded codes from style tags`);
454
+ }
455
+
456
+ return embeddedCodes;
457
+ }
458
+
389
459
  /**
390
460
  * @template T
391
461
  * @param {{ options?: CompilerOptions } & T} config