ripple 0.2.173 → 0.2.175
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 +4 -3
- package/src/compiler/index.js +8 -6
- package/src/compiler/phases/3-transform/client/index.js +56 -0
- package/src/compiler/phases/3-transform/segments.js +188 -89
- package/src/compiler/source-map-utils.js +7 -3
- package/src/runtime/internal/client/bindings.js +20 -10
- package/src/runtime/internal/client/blocks.js +4 -2
- package/tests/client/basic/basic.components.test.ripple +25 -0
- package/tests/client/composite/composite.props.test.ripple +2 -2
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.
|
|
6
|
+
"version": "0.2.175",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -78,9 +78,10 @@
|
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@types/estree": "^1.0.8",
|
|
80
80
|
"@types/node": "^24.3.0",
|
|
81
|
-
"typescript": "^5.9.2"
|
|
81
|
+
"typescript": "^5.9.2",
|
|
82
|
+
"@volar/language-core": "~2.4.23"
|
|
82
83
|
},
|
|
83
84
|
"peerDependencies": {
|
|
84
|
-
"ripple": "0.2.
|
|
85
|
+
"ripple": "0.2.175"
|
|
85
86
|
}
|
|
86
87
|
}
|
package/src/compiler/index.js
CHANGED
|
@@ -25,20 +25,22 @@ export function parse(source) {
|
|
|
25
25
|
export function compile(source, filename, options = {}) {
|
|
26
26
|
const ast = parse_module(source);
|
|
27
27
|
const analysis = analyze(ast, filename, options);
|
|
28
|
-
const result =
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const result =
|
|
29
|
+
options.mode === 'server'
|
|
30
|
+
? transform_server(filename, source, analysis)
|
|
31
|
+
: transform_client(filename, source, analysis, false);
|
|
31
32
|
|
|
32
33
|
return result;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/** @import { PostProcessingChanges, LineOffsets } from './phases/3-transform/client/index.js' */
|
|
37
|
+
/** @import { MappingsResult } from './phases/3-transform/segments.js' */
|
|
36
38
|
|
|
37
39
|
/**
|
|
38
40
|
* Compile Ripple component to Volar virtual code with TypeScript mappings
|
|
39
41
|
* @param {string} source
|
|
40
42
|
* @param {string} filename
|
|
41
|
-
* @returns {
|
|
43
|
+
* @returns {MappingsResult} Volar mappings object
|
|
42
44
|
*/
|
|
43
45
|
export function compile_to_volar_mappings(source, filename) {
|
|
44
46
|
const ast = parse_module(source);
|
|
@@ -51,7 +53,7 @@ export function compile_to_volar_mappings(source, filename) {
|
|
|
51
53
|
source,
|
|
52
54
|
transformed.js.code,
|
|
53
55
|
transformed.js.map,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
/** @type {PostProcessingChanges} */ (transformed.js.post_processing_changes),
|
|
57
|
+
/** @type {LineOffsets} */ (transformed.js.line_offsets),
|
|
56
58
|
);
|
|
57
59
|
}
|
|
@@ -2527,6 +2527,62 @@ function create_tsx_with_typescript_support() {
|
|
|
2527
2527
|
FunctionExpression(node, context) {
|
|
2528
2528
|
handle_function(node, context);
|
|
2529
2529
|
},
|
|
2530
|
+
// Custom handler for ImportDeclaration to ensure 'import' keyword has source mapping
|
|
2531
|
+
// This creates a source map entry at the start of the import statement
|
|
2532
|
+
// Esrap's default handler writes 'import' without passing the node, so no source map entry
|
|
2533
|
+
ImportDeclaration(node, context) {
|
|
2534
|
+
// Write 'import' keyword with node location for source mapping
|
|
2535
|
+
context.write('import', node);
|
|
2536
|
+
context.write(' ');
|
|
2537
|
+
|
|
2538
|
+
// Write specifiers - handle default, namespace, and named imports
|
|
2539
|
+
if (node.specifiers && node.specifiers.length > 0) {
|
|
2540
|
+
let default_specifier = null;
|
|
2541
|
+
let namespace_specifier = null;
|
|
2542
|
+
const named_specifiers = [];
|
|
2543
|
+
|
|
2544
|
+
for (const spec of node.specifiers) {
|
|
2545
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
2546
|
+
default_specifier = spec;
|
|
2547
|
+
} else if (spec.type === 'ImportNamespaceSpecifier') {
|
|
2548
|
+
namespace_specifier = spec;
|
|
2549
|
+
} else if (spec.type === 'ImportSpecifier') {
|
|
2550
|
+
named_specifiers.push(spec);
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
// Write default import
|
|
2555
|
+
if (default_specifier) {
|
|
2556
|
+
context.visit(default_specifier);
|
|
2557
|
+
if (namespace_specifier || named_specifiers.length > 0) {
|
|
2558
|
+
context.write(', ');
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
// Write namespace import
|
|
2563
|
+
if (namespace_specifier) {
|
|
2564
|
+
context.visit(namespace_specifier);
|
|
2565
|
+
if (named_specifiers.length > 0) {
|
|
2566
|
+
context.write(', ');
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// Write named imports
|
|
2571
|
+
if (named_specifiers.length > 0) {
|
|
2572
|
+
context.write('{ ');
|
|
2573
|
+
for (let i = 0; i < named_specifiers.length; i++) {
|
|
2574
|
+
if (i > 0) context.write(', ');
|
|
2575
|
+
context.visit(named_specifiers[i]);
|
|
2576
|
+
}
|
|
2577
|
+
context.write(' }');
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
context.write(' from ');
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
// Write source
|
|
2584
|
+
context.visit(node.source);
|
|
2585
|
+
},
|
|
2530
2586
|
// Custom handler for TSParenthesizedType: (Type)
|
|
2531
2587
|
TSParenthesizedType(node, context) {
|
|
2532
2588
|
context.write('(');
|
|
@@ -1,19 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} CustomMappingData
|
|
3
|
+
* @property {number[]} generatedLengths
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('estree').Position} Position
|
|
8
|
+
* @typedef {{start: Position, end: Position}} Location
|
|
9
|
+
* @typedef {import('@volar/language-core').CodeMapping} VolarCodeMapping
|
|
10
|
+
* @typedef {VolarCodeMapping['data'] & {customData: CustomMappingData}} MappingData
|
|
11
|
+
* @typedef {VolarCodeMapping & {data: MappingData}} CodeMapping
|
|
12
|
+
* @typedef {{code: string, mappings: CodeMapping[]}} MappingsResult
|
|
13
|
+
*/
|
|
14
|
+
|
|
1
15
|
import { walk } from 'zimmerframe';
|
|
2
16
|
import { build_source_to_generated_map, get_generated_position } from '../../source-map-utils.js';
|
|
3
17
|
|
|
18
|
+
/** @type {VolarCodeMapping['data']} */
|
|
4
19
|
export const mapping_data = {
|
|
5
20
|
verification: true,
|
|
6
21
|
completion: true,
|
|
7
22
|
semantic: true,
|
|
8
23
|
navigation: true,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
formatting: false, // not doing formatting through Volar, using Prettier.
|
|
12
|
-
// these 3 below will be true by default
|
|
13
|
-
// leaving for reference
|
|
14
|
-
// hover: true,
|
|
15
|
-
// definition: true,
|
|
16
|
-
// references: true,
|
|
24
|
+
structure: true,
|
|
25
|
+
format: false,
|
|
17
26
|
};
|
|
18
27
|
|
|
19
28
|
/**
|
|
@@ -28,14 +37,25 @@ export const mapping_data = {
|
|
|
28
37
|
* @param {object} esrap_source_map - Esrap source map for accurate position lookup
|
|
29
38
|
* @param {PostProcessingChanges } post_processing_changes - Optional post-processing changes
|
|
30
39
|
* @param {number[]} line_offsets - Pre-computed line offsets array for generated code
|
|
31
|
-
* @returns {
|
|
40
|
+
* @returns {MappingsResult}
|
|
32
41
|
*/
|
|
33
|
-
export function convert_source_map_to_mappings(
|
|
34
|
-
|
|
42
|
+
export function convert_source_map_to_mappings(
|
|
43
|
+
ast,
|
|
44
|
+
source,
|
|
45
|
+
generated_code,
|
|
46
|
+
esrap_source_map,
|
|
47
|
+
post_processing_changes,
|
|
48
|
+
line_offsets,
|
|
49
|
+
) {
|
|
50
|
+
/** @type {CodeMapping[]} */
|
|
35
51
|
const mappings = [];
|
|
52
|
+
let isImportDeclarationPresent = false;
|
|
36
53
|
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Converts line/column positions to byte offsets
|
|
56
|
+
* @param {string} text
|
|
57
|
+
* @returns {number[]}
|
|
58
|
+
*/
|
|
39
59
|
const build_line_offsets = (text) => {
|
|
40
60
|
const offsets = [0]; // Line 1 starts at offset 0
|
|
41
61
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -52,10 +72,14 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
52
72
|
* @param {number} line
|
|
53
73
|
* @param {number} column
|
|
54
74
|
* @param {number[]} line_offsets
|
|
55
|
-
* @returns {number
|
|
75
|
+
* @returns {number}
|
|
56
76
|
*/
|
|
57
77
|
const loc_to_offset = (line, column, line_offsets) => {
|
|
58
|
-
if (line < 1 || line > line_offsets.length)
|
|
78
|
+
if (line < 1 || line > line_offsets.length) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Location line or line offsets length is out of bounds, line: ${line}, line offsets length: ${line_offsets.length}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
59
83
|
return line_offsets[line - 1] + column;
|
|
60
84
|
};
|
|
61
85
|
|
|
@@ -73,17 +97,20 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
73
97
|
const adjusted_source_map = build_source_to_generated_map(
|
|
74
98
|
esrap_source_map,
|
|
75
99
|
post_processing_changes,
|
|
76
|
-
line_offsets
|
|
100
|
+
line_offsets,
|
|
77
101
|
);
|
|
78
102
|
|
|
79
103
|
// Collect text tokens from AST nodes
|
|
80
104
|
// All tokens must have source/generated text and loc property for accurate positioning
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
/**
|
|
106
|
+
* @type {Array<{
|
|
107
|
+
* source: string,
|
|
108
|
+
* generated: string,
|
|
109
|
+
* is_full_import_statement?: boolean,
|
|
110
|
+
* loc: Location,
|
|
111
|
+
* end_loc?: Location,
|
|
112
|
+
* }>}
|
|
113
|
+
*/
|
|
87
114
|
const tokens = [];
|
|
88
115
|
|
|
89
116
|
// We have to visit everything in generated order to maintain correct indices
|
|
@@ -96,11 +123,19 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
96
123
|
if (node.name && node.loc) {
|
|
97
124
|
// Check if this identifier has tracked_shorthand metadata (e.g., TrackedMap -> #Map)
|
|
98
125
|
if (node.metadata?.tracked_shorthand) {
|
|
99
|
-
tokens.push({
|
|
126
|
+
tokens.push({
|
|
127
|
+
source: node.metadata.tracked_shorthand,
|
|
128
|
+
generated: node.name,
|
|
129
|
+
loc: node.loc,
|
|
130
|
+
});
|
|
100
131
|
} else if (node.metadata?.is_capitalized) {
|
|
101
132
|
// This identifier was capitalized during transformation
|
|
102
133
|
// Map the original lowercase name to the capitalized generated name
|
|
103
|
-
tokens.push({
|
|
134
|
+
tokens.push({
|
|
135
|
+
source: node.metadata.original_name,
|
|
136
|
+
generated: node.name,
|
|
137
|
+
loc: node.loc,
|
|
138
|
+
});
|
|
104
139
|
} else {
|
|
105
140
|
// No transformation - source and generated names are the same
|
|
106
141
|
tokens.push({ source: node.name, generated: node.name, loc: node.loc });
|
|
@@ -111,7 +146,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
111
146
|
// JSXIdentifiers can also be capitalized (for dynamic components)
|
|
112
147
|
if (node.loc && node.name) {
|
|
113
148
|
if (node.metadata?.is_capitalized) {
|
|
114
|
-
tokens.push({
|
|
149
|
+
tokens.push({
|
|
150
|
+
source: node.metadata.original_name,
|
|
151
|
+
generated: node.name,
|
|
152
|
+
loc: node.loc,
|
|
153
|
+
});
|
|
115
154
|
} else {
|
|
116
155
|
tokens.push({ source: node.name, generated: node.name, loc: node.loc });
|
|
117
156
|
}
|
|
@@ -123,14 +162,17 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
123
162
|
}
|
|
124
163
|
return; // Leaf node, don't traverse further
|
|
125
164
|
} else if (node.type === 'ImportDeclaration') {
|
|
165
|
+
isImportDeclarationPresent = true;
|
|
166
|
+
|
|
126
167
|
// Add import declaration as a special token for full-statement mapping
|
|
127
168
|
// TypeScript reports unused imports with diagnostics covering the entire statement
|
|
128
|
-
if (node.loc) {
|
|
169
|
+
if (node.loc && node.source?.loc) {
|
|
129
170
|
tokens.push({
|
|
130
171
|
source: '',
|
|
131
172
|
generated: '',
|
|
132
173
|
loc: node.loc,
|
|
133
|
-
|
|
174
|
+
is_full_import_statement: true,
|
|
175
|
+
end_loc: node.source.loc,
|
|
134
176
|
});
|
|
135
177
|
}
|
|
136
178
|
|
|
@@ -152,7 +194,10 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
152
194
|
visit(node.local);
|
|
153
195
|
}
|
|
154
196
|
return;
|
|
155
|
-
} else if (
|
|
197
|
+
} else if (
|
|
198
|
+
node.type === 'ImportDefaultSpecifier' ||
|
|
199
|
+
node.type === 'ImportNamespaceSpecifier'
|
|
200
|
+
) {
|
|
156
201
|
// Just visit local
|
|
157
202
|
if (node.local) {
|
|
158
203
|
visit(node.local);
|
|
@@ -246,17 +291,32 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
246
291
|
}
|
|
247
292
|
|
|
248
293
|
// 3. Push closing tag name (not visited by AST walker)
|
|
249
|
-
if (
|
|
294
|
+
if (
|
|
295
|
+
!node.openingElement?.selfClosing &&
|
|
296
|
+
node.closingElement?.name?.type === 'JSXIdentifier'
|
|
297
|
+
) {
|
|
250
298
|
const closingNameNode = node.closingElement.name;
|
|
251
299
|
if (closingNameNode.metadata?.is_capitalized) {
|
|
252
|
-
tokens.push({
|
|
300
|
+
tokens.push({
|
|
301
|
+
source: closingNameNode.metadata.original_name,
|
|
302
|
+
generated: closingNameNode.name,
|
|
303
|
+
loc: closingNameNode.loc,
|
|
304
|
+
});
|
|
253
305
|
} else {
|
|
254
|
-
tokens.push({
|
|
306
|
+
tokens.push({
|
|
307
|
+
source: closingNameNode.name,
|
|
308
|
+
generated: closingNameNode.name,
|
|
309
|
+
loc: closingNameNode.loc,
|
|
310
|
+
});
|
|
255
311
|
}
|
|
256
312
|
}
|
|
257
313
|
|
|
258
314
|
return;
|
|
259
|
-
} else if (
|
|
315
|
+
} else if (
|
|
316
|
+
node.type === 'FunctionDeclaration' ||
|
|
317
|
+
node.type === 'FunctionExpression' ||
|
|
318
|
+
node.type === 'ArrowFunctionExpression'
|
|
319
|
+
) {
|
|
260
320
|
// Add function/component keyword token
|
|
261
321
|
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') {
|
|
262
322
|
const source_keyword = node.metadata?.was_component ? 'component' : 'function';
|
|
@@ -266,8 +326,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
266
326
|
generated: 'function',
|
|
267
327
|
loc: {
|
|
268
328
|
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
269
|
-
end: {
|
|
270
|
-
|
|
329
|
+
end: {
|
|
330
|
+
line: node.loc.start.line,
|
|
331
|
+
column: node.loc.start.column + source_keyword.length,
|
|
332
|
+
},
|
|
333
|
+
},
|
|
271
334
|
});
|
|
272
335
|
}
|
|
273
336
|
|
|
@@ -654,7 +717,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
654
717
|
}
|
|
655
718
|
}
|
|
656
719
|
return;
|
|
657
|
-
} else if (
|
|
720
|
+
} else if (
|
|
721
|
+
node.type === 'JSXClosingElement' ||
|
|
722
|
+
node.type === 'JSXClosingFragment' ||
|
|
723
|
+
node.type === 'JSXOpeningFragment'
|
|
724
|
+
) {
|
|
658
725
|
// These are handled by their parent nodes
|
|
659
726
|
return;
|
|
660
727
|
} else if (node.type === 'JSXMemberExpression') {
|
|
@@ -736,7 +803,10 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
736
803
|
}
|
|
737
804
|
// Skip typeAnnotation
|
|
738
805
|
return;
|
|
739
|
-
} else if (
|
|
806
|
+
} else if (
|
|
807
|
+
node.type === 'TSTypeParameterInstantiation' ||
|
|
808
|
+
node.type === 'TSTypeParameterDeclaration'
|
|
809
|
+
) {
|
|
740
810
|
// Generic type parameters - visit to collect type variable names
|
|
741
811
|
if (node.params) {
|
|
742
812
|
for (const param of node.params) {
|
|
@@ -881,7 +951,10 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
881
951
|
visit(node.typeAnnotation);
|
|
882
952
|
}
|
|
883
953
|
return;
|
|
884
|
-
} else if (
|
|
954
|
+
} else if (
|
|
955
|
+
node.type === 'TSCallSignatureDeclaration' ||
|
|
956
|
+
node.type === 'TSConstructSignatureDeclaration'
|
|
957
|
+
) {
|
|
885
958
|
// Call or construct signature
|
|
886
959
|
if (node.typeParameters) {
|
|
887
960
|
visit(node.typeParameters);
|
|
@@ -1078,7 +1151,22 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1078
1151
|
visit(node.typeAnnotation);
|
|
1079
1152
|
}
|
|
1080
1153
|
return;
|
|
1081
|
-
} else if (
|
|
1154
|
+
} else if (
|
|
1155
|
+
node.type === 'TSAnyKeyword' ||
|
|
1156
|
+
node.type === 'TSUnknownKeyword' ||
|
|
1157
|
+
node.type === 'TSNumberKeyword' ||
|
|
1158
|
+
node.type === 'TSObjectKeyword' ||
|
|
1159
|
+
node.type === 'TSBooleanKeyword' ||
|
|
1160
|
+
node.type === 'TSBigIntKeyword' ||
|
|
1161
|
+
node.type === 'TSStringKeyword' ||
|
|
1162
|
+
node.type === 'TSSymbolKeyword' ||
|
|
1163
|
+
node.type === 'TSVoidKeyword' ||
|
|
1164
|
+
node.type === 'TSUndefinedKeyword' ||
|
|
1165
|
+
node.type === 'TSNullKeyword' ||
|
|
1166
|
+
node.type === 'TSNeverKeyword' ||
|
|
1167
|
+
node.type === 'TSThisType' ||
|
|
1168
|
+
node.type === 'TSIntrinsicKeyword'
|
|
1169
|
+
) {
|
|
1082
1170
|
// Primitive type keywords - leaf nodes, no children
|
|
1083
1171
|
return;
|
|
1084
1172
|
} else if (node.type === 'TSDeclareFunction') {
|
|
@@ -1120,67 +1208,77 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1120
1208
|
}
|
|
1121
1209
|
|
|
1122
1210
|
throw new Error(`Unhandled AST node type in mapping walker: ${node.type}`);
|
|
1123
|
-
}
|
|
1211
|
+
},
|
|
1124
1212
|
});
|
|
1125
1213
|
|
|
1126
|
-
// Process each token in order
|
|
1127
|
-
// All tokens now have .loc property - no need for fallback logic
|
|
1128
1214
|
for (const token of tokens) {
|
|
1129
1215
|
const source_text = token.source;
|
|
1216
|
+
const gen_text = token.generated;
|
|
1130
1217
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1218
|
+
const source_start = loc_to_offset(
|
|
1219
|
+
token.loc.start.line,
|
|
1220
|
+
token.loc.start.column,
|
|
1221
|
+
source_line_offsets,
|
|
1222
|
+
);
|
|
1136
1223
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1224
|
+
let source_length = source_text.length;
|
|
1225
|
+
let gen_length = gen_text.length;
|
|
1226
|
+
/** @type {MappingData} */
|
|
1227
|
+
let data;
|
|
1228
|
+
/** @type {number} */
|
|
1229
|
+
let gen_start;
|
|
1140
1230
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
const gen_end = gen_loc_to_offset(gen_end_pos.line, gen_end_pos.column);
|
|
1231
|
+
if (token.is_full_import_statement) {
|
|
1232
|
+
const end_loc = /** @type {Location} */ (token.end_loc).end;
|
|
1233
|
+
const source_end = loc_to_offset(end_loc.line, end_loc.column, source_line_offsets);
|
|
1145
1234
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1235
|
+
// Look up where import keyword and source literal map to in generated code
|
|
1236
|
+
const gen_start_pos = get_generated_position(
|
|
1237
|
+
token.loc.start.line,
|
|
1238
|
+
token.loc.start.column,
|
|
1239
|
+
adjusted_source_map,
|
|
1240
|
+
);
|
|
1241
|
+
const gen_end_pos = get_generated_position(end_loc.line, end_loc.column, adjusted_source_map);
|
|
1148
1242
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
generatedOffsets: [gen_start],
|
|
1152
|
-
lengths: [Math.min(source_length, gen_length)],
|
|
1153
|
-
data: {
|
|
1154
|
-
// only verification (diagnostics) to avoid duplicate hover/completion
|
|
1155
|
-
verification: true
|
|
1156
|
-
},
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
continue;
|
|
1160
|
-
}
|
|
1243
|
+
gen_start = gen_loc_to_offset(gen_start_pos.line, gen_start_pos.column);
|
|
1244
|
+
const gen_end = gen_loc_to_offset(gen_end_pos.line, gen_end_pos.column);
|
|
1161
1245
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1246
|
+
source_length = source_end - source_start;
|
|
1247
|
+
gen_length = gen_end - gen_start;
|
|
1164
1248
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1249
|
+
data = {
|
|
1250
|
+
// we only want verification here, like unused imports
|
|
1251
|
+
// since this is synthetic and otherwise we'll get duplicated actions like intellisense
|
|
1252
|
+
// each imported specifier has its own mapping
|
|
1253
|
+
verification: true,
|
|
1254
|
+
customData: {
|
|
1255
|
+
generatedLengths: [gen_length],
|
|
1256
|
+
},
|
|
1257
|
+
};
|
|
1171
1258
|
} else {
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1259
|
+
const gen_line_col = get_generated_position(
|
|
1260
|
+
token.loc.start.line,
|
|
1261
|
+
token.loc.start.column,
|
|
1262
|
+
adjusted_source_map,
|
|
1263
|
+
);
|
|
1264
|
+
gen_start = gen_loc_to_offset(gen_line_col.line, gen_line_col.column);
|
|
1175
1265
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
});
|
|
1266
|
+
data = {
|
|
1267
|
+
...mapping_data,
|
|
1268
|
+
customData: {
|
|
1269
|
+
generatedLengths: [gen_length],
|
|
1270
|
+
},
|
|
1271
|
+
};
|
|
1183
1272
|
}
|
|
1273
|
+
|
|
1274
|
+
// !IMPORTANT: don't set generatedLengths, otherwise Volar will use that vs our source
|
|
1275
|
+
// We're adding it to our custom metadata instead as we need it for patching positions
|
|
1276
|
+
mappings.push({
|
|
1277
|
+
sourceOffsets: [source_start],
|
|
1278
|
+
generatedOffsets: [gen_start],
|
|
1279
|
+
lengths: [source_length],
|
|
1280
|
+
data,
|
|
1281
|
+
});
|
|
1184
1282
|
}
|
|
1185
1283
|
|
|
1186
1284
|
// Sort mappings by source offset // Sort mappings by source offset
|
|
@@ -1188,16 +1286,17 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1188
1286
|
|
|
1189
1287
|
// Add a mapping for the very beginning of the file to handle import additions
|
|
1190
1288
|
// This ensures that code actions adding imports at the top work correctly
|
|
1191
|
-
if (mappings.length > 0 && mappings[0].sourceOffsets[0] > 0) {
|
|
1289
|
+
if (!isImportDeclarationPresent && mappings.length > 0 && mappings[0].sourceOffsets[0] > 0) {
|
|
1192
1290
|
mappings.unshift({
|
|
1193
1291
|
sourceOffsets: [0],
|
|
1194
1292
|
generatedOffsets: [0],
|
|
1195
1293
|
lengths: [1],
|
|
1196
1294
|
data: {
|
|
1197
1295
|
...mapping_data,
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1296
|
+
customData: {
|
|
1297
|
+
generatedLengths: [1],
|
|
1298
|
+
},
|
|
1299
|
+
},
|
|
1201
1300
|
});
|
|
1202
1301
|
}
|
|
1203
1302
|
|
|
@@ -44,7 +44,10 @@ export function build_source_to_generated_map(source_map, post_processing_change
|
|
|
44
44
|
|
|
45
45
|
while (left <= right) {
|
|
46
46
|
const mid = Math.floor((left + right) / 2);
|
|
47
|
-
if (
|
|
47
|
+
if (
|
|
48
|
+
offset >= line_offsets[mid] &&
|
|
49
|
+
(mid === line_offsets.length - 1 || offset < line_offsets[mid + 1])
|
|
50
|
+
) {
|
|
48
51
|
line = mid + 1;
|
|
49
52
|
break;
|
|
50
53
|
} else if (offset < line_offsets[mid]) {
|
|
@@ -114,14 +117,15 @@ export function build_source_to_generated_map(source_map, post_processing_change
|
|
|
114
117
|
* @param {number} source_line - 1-based line number in source
|
|
115
118
|
* @param {number} source_column - 0-based column number in source
|
|
116
119
|
* @param {SourceToGeneratedMap} source_to_gen_map - Lookup map
|
|
117
|
-
* @returns {{line: number, column: number}
|
|
120
|
+
* @returns {{line: number, column: number}} Generated position
|
|
118
121
|
*/
|
|
119
122
|
export function get_generated_position(source_line, source_column, source_to_gen_map) {
|
|
120
123
|
const key = `${source_line}:${source_column}`;
|
|
121
124
|
const positions = source_to_gen_map.get(key);
|
|
122
125
|
|
|
123
126
|
if (!positions || positions.length === 0) {
|
|
124
|
-
|
|
127
|
+
// No mapping found in source map - this shouldn't happen since all tokens should have mappings
|
|
128
|
+
throw new Error(`No source map entry for position "${source_line}:${source_column}"`);
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
// If multiple generated positions map to same source, return the first
|
|
@@ -256,10 +256,10 @@ export function bindChecked(maybe_tracked) {
|
|
|
256
256
|
throw not_tracked_type_error('bindChecked()');
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
|
|
259
|
+
var tracked = /** @type {Tracked} */ (maybe_tracked);
|
|
260
260
|
|
|
261
261
|
return (input) => {
|
|
262
|
-
|
|
262
|
+
var clear_event = on(input, 'change', () => {
|
|
263
263
|
set(tracked, input.checked);
|
|
264
264
|
});
|
|
265
265
|
|
|
@@ -281,10 +281,10 @@ export function bindIndeterminate(maybe_tracked) {
|
|
|
281
281
|
throw not_tracked_type_error('bindIndeterminate()');
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
|
|
284
|
+
var tracked = /** @type {Tracked} */ (maybe_tracked);
|
|
285
285
|
|
|
286
286
|
return (input) => {
|
|
287
|
-
|
|
287
|
+
var clear_event = on(input, 'change', () => {
|
|
288
288
|
set(tracked, input.indeterminate);
|
|
289
289
|
});
|
|
290
290
|
|
|
@@ -367,7 +367,7 @@ function bind_element_size(maybe_tracked, type) {
|
|
|
367
367
|
);
|
|
368
368
|
|
|
369
369
|
effect(() => {
|
|
370
|
-
|
|
370
|
+
set(tracked, element[type]);
|
|
371
371
|
return unsubscribe;
|
|
372
372
|
});
|
|
373
373
|
};
|
|
@@ -474,10 +474,10 @@ export function bind_content_editable(maybe_tracked, property) {
|
|
|
474
474
|
throw not_tracked_type_error(`bind${property.charAt(0).toUpperCase() + property.slice(1)}()`);
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
var tracked = /** @type {Tracked} */ (maybe_tracked);
|
|
478
478
|
|
|
479
479
|
return (element) => {
|
|
480
|
-
|
|
480
|
+
var clear_event = on(element, 'input', () => {
|
|
481
481
|
set(tracked, element[property]);
|
|
482
482
|
});
|
|
483
483
|
|
|
@@ -533,12 +533,22 @@ export function bindFiles(maybe_tracked) {
|
|
|
533
533
|
throw not_tracked_type_error('bindFiles()');
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
-
|
|
536
|
+
var tracked = /** @type {Tracked} */ (maybe_tracked);
|
|
537
537
|
|
|
538
538
|
return (input) => {
|
|
539
|
-
|
|
539
|
+
var clear_event = on(input, 'change', () => {
|
|
540
540
|
set(tracked, input.files);
|
|
541
541
|
});
|
|
542
|
+
|
|
543
|
+
effect(() => {
|
|
544
|
+
var value = get(tracked);
|
|
545
|
+
|
|
546
|
+
if (value !== input.files && value instanceof FileList) {
|
|
547
|
+
input.files = value;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
return clear_event;
|
|
542
552
|
};
|
|
543
553
|
}
|
|
544
554
|
|
|
@@ -552,7 +562,7 @@ export function bindNode(maybe_tracked) {
|
|
|
552
562
|
throw not_tracked_type_error('bindNode()');
|
|
553
563
|
}
|
|
554
564
|
|
|
555
|
-
|
|
565
|
+
var tracked = /** @type {Tracked} */ (maybe_tracked);
|
|
556
566
|
|
|
557
567
|
/** @param {HTMLElement} node */
|
|
558
568
|
return (node) => {
|
|
@@ -369,8 +369,10 @@ export function destroy_block(block, remove_dom = true) {
|
|
|
369
369
|
|
|
370
370
|
if ((remove_dom && (f & (BRANCH_BLOCK | ROOT_BLOCK)) !== 0) || (f & HEAD_BLOCK) !== 0) {
|
|
371
371
|
var s = block.s;
|
|
372
|
-
|
|
373
|
-
|
|
372
|
+
if (s !== null) {
|
|
373
|
+
remove_block_dom(s.start, s.end);
|
|
374
|
+
removed = true;
|
|
375
|
+
}
|
|
374
376
|
}
|
|
375
377
|
|
|
376
378
|
destroy_block_children(block, remove_dom && !removed);
|
|
@@ -284,4 +284,29 @@ describe('basic client > components & composition', () => {
|
|
|
284
284
|
render(App);
|
|
285
285
|
}).not.toThrow();
|
|
286
286
|
});
|
|
287
|
+
|
|
288
|
+
it('handles component without any output', () => {
|
|
289
|
+
component Noop() {
|
|
290
|
+
// No output
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
component Op() {
|
|
294
|
+
<div>{'Some HTML content'}</div>
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
component App() {
|
|
298
|
+
let Content = track(() => Noop);
|
|
299
|
+
<@Content />
|
|
300
|
+
|
|
301
|
+
<button onClick={() => @Content = Op}>{'Show Op'}</button>
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
render(App);
|
|
305
|
+
|
|
306
|
+
const button = container.querySelector('button');
|
|
307
|
+
|
|
308
|
+
expect(() => {
|
|
309
|
+
button.click();
|
|
310
|
+
}).not.toThrow();
|
|
311
|
+
});
|
|
287
312
|
});
|
|
@@ -81,7 +81,7 @@ describe('composite > props', () => {
|
|
|
81
81
|
logs.push(@count);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
<button onClick={() => @count = @count + 1}>{'+'}</button>
|
|
84
|
+
<button onClick={() => (@count = @count + 1)}>{'+'}</button>
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
component App() {
|
|
@@ -122,7 +122,7 @@ describe('composite > props', () => {
|
|
|
122
122
|
|
|
123
123
|
component Toggle(props) {
|
|
124
124
|
const [pressed, rest] = trackSplit(props, ['pressed']);
|
|
125
|
-
const onClick = () => @pressed = !@pressed;
|
|
125
|
+
const onClick = () => (@pressed = !@pressed);
|
|
126
126
|
<Button {...@rest} class={@pressed ? 'on' : 'off'} {onClick}>{'button 1'}</Button>
|
|
127
127
|
<Button class={@pressed ? 'on' : 'off'} {onClick}>{'button 2'}</Button>
|
|
128
128
|
}
|