ripple 0.2.185 → 0.2.187

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.185",
6
+ "version": "0.2.187",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -86,6 +86,6 @@
86
86
  "vscode-languageserver-types": "^3.17.5"
87
87
  },
88
88
  "peerDependencies": {
89
- "ripple": "0.2.185"
89
+ "ripple": "0.2.187"
90
90
  }
91
91
  }
@@ -24,6 +24,12 @@ export interface CompileResult {
24
24
  css: string;
25
25
  }
26
26
 
27
+ export interface DefinitionLocation {
28
+ embeddedId: string; // e.g., 'style_0', 'style_1'
29
+ start: number; // start offset
30
+ end: number; // end offset
31
+ }
32
+
27
33
  export interface PluginActionOverrides {
28
34
  /** Whether to enable word document highlighting for this mapping */
29
35
  wordHighlight?: {
@@ -40,13 +46,17 @@ export interface PluginActionOverrides {
40
46
  /** Custom definition info for this mapping, false to disable */
41
47
  definition?:
42
48
  | {
43
- description: string;
49
+ description?: string; // just for reference
50
+ // Generic location for embedded content (CSS, etc.)
51
+ location?: DefinitionLocation;
44
52
  }
45
53
  | false;
46
54
  }
47
55
 
48
56
  export interface CustomMappingData extends PluginActionOverrides {
49
57
  generatedLengths: number[];
58
+ embeddedId?: string; // e.g. css regions: 'style_0', 'style_1', etc.
59
+ content?: string; // (e.g., css code)
50
60
  }
51
61
 
52
62
  export interface MappingData extends VolarCodeInformation {
@@ -61,7 +71,6 @@ export interface VolarMappingsResult {
61
71
  code: string;
62
72
  mappings: CodeMapping[];
63
73
  cssMappings: CodeMapping[];
64
- cssSources: string[];
65
74
  }
66
75
 
67
76
  /**
@@ -1,13 +1,10 @@
1
- /** @import * as AST from 'estree' */
2
- /** @import * as ESTreeJSX from 'estree-jsx' */
3
- /** @import { Parse } from '#parser' */
4
-
5
1
  /**
6
- * @import {
7
- * RipplePluginConfig
8
- * } from '#compiler' */
9
-
10
- /** @import { ParseOptions } from 'ripple/compiler' */
2
+ @import * as AST from 'estree'
3
+ @import * as ESTreeJSX from 'estree-jsx'
4
+ @import { Parse } from '#parser'
5
+ @import { RipplePluginConfig } from '#compiler';
6
+ @import { ParseOptions } from 'ripple/compiler'
7
+ */
11
8
 
12
9
  import * as acorn from 'acorn';
13
10
  import { tsPlugin } from '@sveltejs/acorn-typescript';
@@ -1741,6 +1738,7 @@ function RipplePlugin(config) {
1741
1738
  throw new Error('Components can only have one style tag');
1742
1739
  }
1743
1740
  component.css = parsed_css;
1741
+ /** @type {AST.Element} */ (element).metadata.styleScopeHash = parsed_css.hash;
1744
1742
  }
1745
1743
 
1746
1744
  const newLines = content.match(regex_newline_characters)?.length;
@@ -2167,6 +2165,7 @@ function RipplePlugin(config) {
2167
2165
  * @param {string} source
2168
2166
  * @param {AST.CommentWithLocation[]} comments
2169
2167
  * @param {number} [index=0] - Starting index
2168
+ * @returns {{onComment: Parse.Options['onComment'], add_comments: (ast: AST.Node) => void}}
2170
2169
  */
2171
2170
  function get_comment_handlers(source, comments, index = 0) {
2172
2171
  /**
@@ -2230,11 +2229,7 @@ function get_comment_handlers(source, comments, index = 0) {
2230
2229
  context,
2231
2230
  }));
2232
2231
 
2233
- /**
2234
- * @param {AST.Node} ast
2235
- */
2236
2232
  walk(ast, null, {
2237
- /** @param {AST.Node} node */
2238
2233
  _(node, { next, path }) {
2239
2234
  const metadata = node?.metadata;
2240
2235
 
@@ -41,7 +41,7 @@ function is_global(relative_selector) {
41
41
  export function analyze_css(css) {
42
42
  walk(
43
43
  css,
44
- { rule: /** @type {AST.CSS.Rule | null} */ (null) },
44
+ /** @type {{ rule: AST.CSS.Rule | null }} */ ({ rule: null }),
45
45
  {
46
46
  Rule(node, context) {
47
47
  node.metadata.parent_rule = context.state.rule;
@@ -90,7 +90,7 @@ export function analyze_css(css) {
90
90
  // Set the rule metadata before analyzing children
91
91
  node.metadata.rule = context.state.rule;
92
92
 
93
- context.next(); // analyse relevant selectors first
93
+ context.next(); // analyze relevant selectors first
94
94
 
95
95
  {
96
96
  const global = node.children.find(is_global);
@@ -6,7 +6,6 @@
6
6
  AnalysisContext,
7
7
  ScopeInterface,
8
8
  Visitors,
9
- Visitor,
10
9
  } from '#compiler';
11
10
  */
12
11
  /** @import * as AST from 'estree' */
@@ -423,25 +422,11 @@ const visitors = {
423
422
  context.visit(node.discriminant, context.state);
424
423
 
425
424
  for (const switch_case of node.cases) {
426
- // Fallthrough
425
+ // Skip empty cases
427
426
  if (switch_case.consequent.length === 0) {
428
427
  continue;
429
428
  }
430
429
 
431
- // Validate that each cases ends in a break statement, except for the last case
432
- const last = switch_case.consequent?.[switch_case.consequent.length - 1];
433
-
434
- if (
435
- last.type !== 'BreakStatement' &&
436
- node.cases.indexOf(switch_case) !== node.cases.length - 1
437
- ) {
438
- error(
439
- 'Template switch cases must end with a break statement (with the exception of the last case).',
440
- context.state.analysis.module.filename,
441
- switch_case,
442
- );
443
- }
444
-
445
430
  node.metadata = {
446
431
  ...node.metadata,
447
432
  has_template: false,
@@ -5,13 +5,17 @@
5
5
  import { walk } from 'zimmerframe';
6
6
  import { is_element_dom_element } from '../../utils.js';
7
7
 
8
- const seen = new Set();
9
8
  const regex_backslash_and_following_character = /\\(.)/g;
10
9
  /** @type {Direction} */
11
10
  const FORWARD = 0;
12
11
  /** @type {Direction} */
13
12
  const BACKWARD = 1;
14
13
 
14
+ // this will be set for every prune_css call
15
+ // since the code is synchronous, this is safe
16
+ /** @type {string} */
17
+ let css_hash;
18
+
15
19
  // CSS selector constants
16
20
  /**
17
21
  * @param {number} start
@@ -196,6 +200,47 @@ function apply_selector(relative_selectors, rule, element, direction) {
196
200
  if (matched) {
197
201
  if (!is_outer_global(relative_selector)) {
198
202
  relative_selector.metadata.scoped = true;
203
+
204
+ // Store scoped class information on element for language server features
205
+ if (!relative_selector.metadata.is_global && !relative_selector.metadata.is_global_like) {
206
+ // Extract class selectors from the relative selector
207
+ for (const selector of relative_selector.selectors) {
208
+ if (selector.type === 'ClassSelector') {
209
+ const name = selector.name.replace(regex_backslash_and_following_character, '$1');
210
+
211
+ if (!element.metadata.css) {
212
+ element.metadata.css = {
213
+ scopedClasses: new Map(),
214
+ topScopedClasses: new Map(),
215
+ hash: css_hash,
216
+ };
217
+ }
218
+
219
+ // Store class name → CSS location in scopedClasses
220
+ if (!element.metadata.css.scopedClasses.has(name)) {
221
+ element.metadata.css.scopedClasses.set(name, {
222
+ start: selector.start,
223
+ end: selector.end,
224
+ selector: selector,
225
+ });
226
+ }
227
+
228
+ // Also store in topScopedClasses if standalone
229
+ // Standalone = only this ClassSelector in the RelativeSelector, no pseudo-classes/elements
230
+ const isStandalone =
231
+ relative_selector.selectors.length === 1 &&
232
+ relative_selector.selectors[0] === selector;
233
+
234
+ if (isStandalone && !element.metadata.css.topScopedClasses.has(name)) {
235
+ element.metadata.css.topScopedClasses.set(name, {
236
+ start: selector.start,
237
+ end: selector.end,
238
+ selector: selector,
239
+ });
240
+ }
241
+ }
242
+ }
243
+ }
199
244
  }
200
245
 
201
246
  element.metadata.scoped = true;
@@ -1005,6 +1050,8 @@ function rule_has_animation(rule) {
1005
1050
  * @return {void}
1006
1051
  */
1007
1052
  export function prune_css(css, element) {
1053
+ css_hash = css.hash;
1054
+
1008
1055
  /** @type {Visitors<AST.CSS.Node, null>} */
1009
1056
  const visitors = {
1010
1057
  Rule(node, context) {
@@ -1017,8 +1064,6 @@ export function prune_css(css, element) {
1017
1064
  ComplexSelector(node, context) {
1018
1065
  const selectors = get_relative_selectors(node);
1019
1066
 
1020
- seen.clear();
1021
-
1022
1067
  const rule = /** @type {AST.CSS.Rule} */ (node.metadata.rule);
1023
1068
 
1024
1069
  if (apply_selector(selectors, rule, element, BACKWARD) || rule_has_animation(rule)) {
@@ -1,8 +1,7 @@
1
- /** @import * as AST from 'estree' */
2
- /** @import * as ESTreeJSX from 'estree-jsx' */
3
- /** @import { SourceMapMappings } from '@jridgewell/sourcemap-codec' */
4
- /** @import * as ESRap from 'esrap' */
5
1
  /**
2
+ @import * as AST from 'estree';
3
+ @import * as ESTreeJSX from 'estree-jsx';
4
+ @import { SourceMapMappings } from '@jridgewell/sourcemap-codec';
6
5
  @import {
7
6
  AnalysisResult,
8
7
  TransformClientContext,
@@ -14,8 +13,10 @@
14
13
  } from '#compiler';
15
14
  */
16
15
 
17
- /** @typedef {Map<number, {offset: number, delta: number}>} PostProcessingChanges */
18
- /** @typedef {number[]} LineOffsets */
16
+ /**
17
+ @typedef {Map<number, {offset: number, delta: number}>} PostProcessingChanges;
18
+ @typedef {number[]} LineOffsets;
19
+ */
19
20
 
20
21
  import { walk } from 'zimmerframe';
21
22
  import path from 'node:path';
@@ -1829,31 +1830,41 @@ const visitors = {
1829
1830
  const statements = [];
1830
1831
  const cases = [];
1831
1832
 
1832
- let i = 1;
1833
-
1833
+ let id_gen = 0;
1834
+ let counter = 0;
1834
1835
  for (const switch_case of node.cases) {
1835
1836
  const case_body = [];
1837
+ const consequent = switch_case.consequent;
1838
+
1839
+ if (consequent.length !== 0) {
1840
+ const consequent_scope = context.state.scopes.get(consequent) || context.state.scope;
1836
1841
 
1837
- if (switch_case.consequent.length !== 0) {
1838
- const consequent_scope =
1839
- context.state.scopes.get(switch_case.consequent) || context.state.scope;
1842
+ const block = transform_body(consequent, {
1843
+ ...context,
1844
+ state: { ...context.state, scope: consequent_scope },
1845
+ });
1846
+ const has_break = consequent.some((stmt) => stmt.type === 'BreakStatement');
1847
+ const is_last = counter === node.cases.length - 1;
1848
+ const is_default = switch_case.test == null;
1840
1849
  const consequent_id = context.state.scope.generate(
1841
- 'switch_case_' + (switch_case.test == null ? 'default' : i),
1842
- );
1843
- const consequent = b.block(
1844
- transform_body(switch_case.consequent, {
1845
- ...context,
1846
- state: { ...context.state, scope: consequent_scope },
1847
- }),
1850
+ 'switch_case_' + (is_default ? 'default' : id_gen),
1848
1851
  );
1849
1852
 
1850
- statements.push(b.var(b.id(consequent_id), b.arrow([b.id('__anchor')], consequent)));
1851
-
1852
- case_body.push(b.return(b.id(consequent_id)));
1853
+ statements.push(b.var(b.id(consequent_id), b.arrow([b.id('__anchor')], b.block(block))));
1854
+ case_body.push(
1855
+ b.stmt(b.call(b.member(b.id('result'), b.id('push'), false), b.id(consequent_id))),
1856
+ );
1853
1857
 
1854
- i++;
1858
+ // in js, `default:` can be in the middle without a break
1859
+ // so we only add return for the last case or cases with a break
1860
+ if (has_break || is_last) {
1861
+ case_body.push(b.return(b.id('result')));
1862
+ }
1863
+ id_gen++;
1855
1864
  }
1856
1865
 
1866
+ counter++;
1867
+
1857
1868
  cases.push(
1858
1869
  b.switch_case(
1859
1870
  switch_case.test ? /** @type {AST.Expression} */ (context.visit(switch_case.test)) : null,
@@ -1869,6 +1880,7 @@ const visitors = {
1869
1880
  id,
1870
1881
  b.thunk(
1871
1882
  b.block([
1883
+ b.var(b.id('result'), b.array([])),
1872
1884
  b.switch(/** @type {AST.Expression} */ (context.visit(node.discriminant)), cases),
1873
1885
  ]),
1874
1886
  ),
@@ -2996,7 +3008,7 @@ function create_tsx_with_typescript_support() {
2996
3008
  context.write(node.computed ? ']: ' : ': ');
2997
3009
  context.visit(node.value);
2998
3010
  } else {
2999
- base_tsx.Property?.(node, /** @type {ESRap.} */ (context));
3011
+ base_tsx.Property?.(node, context);
3000
3012
  }
3001
3013
  } else {
3002
3014
  // Use default handler for non-component properties