ripple 0.2.173 → 0.2.174
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/segments.js +161 -42
- 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.174",
|
|
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.174"
|
|
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
|
}
|
|
@@ -1,19 +1,31 @@
|
|
|
1
|
+
/** @typedef {import('@volar/language-core').CodeMapping} VolarCodeMapping */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} CustomMappingData
|
|
5
|
+
* @property {[number]} generatedLengths
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {VolarCodeMapping & {
|
|
10
|
+
* data: VolarCodeMapping['data'] & {
|
|
11
|
+
* customData: CustomMappingData
|
|
12
|
+
* }
|
|
13
|
+
* }} CodeMapping
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** @typedef {{code: string, mappings: CodeMapping[]}} MappingsResult */
|
|
17
|
+
|
|
1
18
|
import { walk } from 'zimmerframe';
|
|
2
19
|
import { build_source_to_generated_map, get_generated_position } from '../../source-map-utils.js';
|
|
3
20
|
|
|
21
|
+
/** @type {VolarCodeMapping['data']} */
|
|
4
22
|
export const mapping_data = {
|
|
5
23
|
verification: true,
|
|
6
24
|
completion: true,
|
|
7
25
|
semantic: true,
|
|
8
26
|
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,
|
|
27
|
+
structure: true,
|
|
28
|
+
format: false,
|
|
17
29
|
};
|
|
18
30
|
|
|
19
31
|
/**
|
|
@@ -28,14 +40,24 @@ export const mapping_data = {
|
|
|
28
40
|
* @param {object} esrap_source_map - Esrap source map for accurate position lookup
|
|
29
41
|
* @param {PostProcessingChanges } post_processing_changes - Optional post-processing changes
|
|
30
42
|
* @param {number[]} line_offsets - Pre-computed line offsets array for generated code
|
|
31
|
-
* @returns {
|
|
43
|
+
* @returns {MappingsResult}
|
|
32
44
|
*/
|
|
33
|
-
export function convert_source_map_to_mappings(
|
|
34
|
-
|
|
45
|
+
export function convert_source_map_to_mappings(
|
|
46
|
+
ast,
|
|
47
|
+
source,
|
|
48
|
+
generated_code,
|
|
49
|
+
esrap_source_map,
|
|
50
|
+
post_processing_changes,
|
|
51
|
+
line_offsets,
|
|
52
|
+
) {
|
|
53
|
+
/** @type {CodeMapping[]} */
|
|
35
54
|
const mappings = [];
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Converts line/column positions to byte offsets
|
|
58
|
+
* @param {string} text
|
|
59
|
+
* @returns {number[]}
|
|
60
|
+
*/
|
|
39
61
|
const build_line_offsets = (text) => {
|
|
40
62
|
const offsets = [0]; // Line 1 starts at offset 0
|
|
41
63
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -73,7 +95,7 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
73
95
|
const adjusted_source_map = build_source_to_generated_map(
|
|
74
96
|
esrap_source_map,
|
|
75
97
|
post_processing_changes,
|
|
76
|
-
line_offsets
|
|
98
|
+
line_offsets,
|
|
77
99
|
);
|
|
78
100
|
|
|
79
101
|
// Collect text tokens from AST nodes
|
|
@@ -96,11 +118,19 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
96
118
|
if (node.name && node.loc) {
|
|
97
119
|
// Check if this identifier has tracked_shorthand metadata (e.g., TrackedMap -> #Map)
|
|
98
120
|
if (node.metadata?.tracked_shorthand) {
|
|
99
|
-
tokens.push({
|
|
121
|
+
tokens.push({
|
|
122
|
+
source: node.metadata.tracked_shorthand,
|
|
123
|
+
generated: node.name,
|
|
124
|
+
loc: node.loc,
|
|
125
|
+
});
|
|
100
126
|
} else if (node.metadata?.is_capitalized) {
|
|
101
127
|
// This identifier was capitalized during transformation
|
|
102
128
|
// Map the original lowercase name to the capitalized generated name
|
|
103
|
-
tokens.push({
|
|
129
|
+
tokens.push({
|
|
130
|
+
source: node.metadata.original_name,
|
|
131
|
+
generated: node.name,
|
|
132
|
+
loc: node.loc,
|
|
133
|
+
});
|
|
104
134
|
} else {
|
|
105
135
|
// No transformation - source and generated names are the same
|
|
106
136
|
tokens.push({ source: node.name, generated: node.name, loc: node.loc });
|
|
@@ -111,7 +141,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
111
141
|
// JSXIdentifiers can also be capitalized (for dynamic components)
|
|
112
142
|
if (node.loc && node.name) {
|
|
113
143
|
if (node.metadata?.is_capitalized) {
|
|
114
|
-
tokens.push({
|
|
144
|
+
tokens.push({
|
|
145
|
+
source: node.metadata.original_name,
|
|
146
|
+
generated: node.name,
|
|
147
|
+
loc: node.loc,
|
|
148
|
+
});
|
|
115
149
|
} else {
|
|
116
150
|
tokens.push({ source: node.name, generated: node.name, loc: node.loc });
|
|
117
151
|
}
|
|
@@ -130,7 +164,7 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
130
164
|
source: '',
|
|
131
165
|
generated: '',
|
|
132
166
|
loc: node.loc,
|
|
133
|
-
is_import_statement: true
|
|
167
|
+
is_import_statement: true,
|
|
134
168
|
});
|
|
135
169
|
}
|
|
136
170
|
|
|
@@ -152,7 +186,10 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
152
186
|
visit(node.local);
|
|
153
187
|
}
|
|
154
188
|
return;
|
|
155
|
-
} else if (
|
|
189
|
+
} else if (
|
|
190
|
+
node.type === 'ImportDefaultSpecifier' ||
|
|
191
|
+
node.type === 'ImportNamespaceSpecifier'
|
|
192
|
+
) {
|
|
156
193
|
// Just visit local
|
|
157
194
|
if (node.local) {
|
|
158
195
|
visit(node.local);
|
|
@@ -246,17 +283,32 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
246
283
|
}
|
|
247
284
|
|
|
248
285
|
// 3. Push closing tag name (not visited by AST walker)
|
|
249
|
-
if (
|
|
286
|
+
if (
|
|
287
|
+
!node.openingElement?.selfClosing &&
|
|
288
|
+
node.closingElement?.name?.type === 'JSXIdentifier'
|
|
289
|
+
) {
|
|
250
290
|
const closingNameNode = node.closingElement.name;
|
|
251
291
|
if (closingNameNode.metadata?.is_capitalized) {
|
|
252
|
-
tokens.push({
|
|
292
|
+
tokens.push({
|
|
293
|
+
source: closingNameNode.metadata.original_name,
|
|
294
|
+
generated: closingNameNode.name,
|
|
295
|
+
loc: closingNameNode.loc,
|
|
296
|
+
});
|
|
253
297
|
} else {
|
|
254
|
-
tokens.push({
|
|
298
|
+
tokens.push({
|
|
299
|
+
source: closingNameNode.name,
|
|
300
|
+
generated: closingNameNode.name,
|
|
301
|
+
loc: closingNameNode.loc,
|
|
302
|
+
});
|
|
255
303
|
}
|
|
256
304
|
}
|
|
257
305
|
|
|
258
306
|
return;
|
|
259
|
-
} else if (
|
|
307
|
+
} else if (
|
|
308
|
+
node.type === 'FunctionDeclaration' ||
|
|
309
|
+
node.type === 'FunctionExpression' ||
|
|
310
|
+
node.type === 'ArrowFunctionExpression'
|
|
311
|
+
) {
|
|
260
312
|
// Add function/component keyword token
|
|
261
313
|
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') {
|
|
262
314
|
const source_keyword = node.metadata?.was_component ? 'component' : 'function';
|
|
@@ -266,8 +318,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
266
318
|
generated: 'function',
|
|
267
319
|
loc: {
|
|
268
320
|
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
269
|
-
end: {
|
|
270
|
-
|
|
321
|
+
end: {
|
|
322
|
+
line: node.loc.start.line,
|
|
323
|
+
column: node.loc.start.column + source_keyword.length,
|
|
324
|
+
},
|
|
325
|
+
},
|
|
271
326
|
});
|
|
272
327
|
}
|
|
273
328
|
|
|
@@ -654,7 +709,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
654
709
|
}
|
|
655
710
|
}
|
|
656
711
|
return;
|
|
657
|
-
} else if (
|
|
712
|
+
} else if (
|
|
713
|
+
node.type === 'JSXClosingElement' ||
|
|
714
|
+
node.type === 'JSXClosingFragment' ||
|
|
715
|
+
node.type === 'JSXOpeningFragment'
|
|
716
|
+
) {
|
|
658
717
|
// These are handled by their parent nodes
|
|
659
718
|
return;
|
|
660
719
|
} else if (node.type === 'JSXMemberExpression') {
|
|
@@ -736,7 +795,10 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
736
795
|
}
|
|
737
796
|
// Skip typeAnnotation
|
|
738
797
|
return;
|
|
739
|
-
} else if (
|
|
798
|
+
} else if (
|
|
799
|
+
node.type === 'TSTypeParameterInstantiation' ||
|
|
800
|
+
node.type === 'TSTypeParameterDeclaration'
|
|
801
|
+
) {
|
|
740
802
|
// Generic type parameters - visit to collect type variable names
|
|
741
803
|
if (node.params) {
|
|
742
804
|
for (const param of node.params) {
|
|
@@ -881,7 +943,10 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
881
943
|
visit(node.typeAnnotation);
|
|
882
944
|
}
|
|
883
945
|
return;
|
|
884
|
-
} else if (
|
|
946
|
+
} else if (
|
|
947
|
+
node.type === 'TSCallSignatureDeclaration' ||
|
|
948
|
+
node.type === 'TSConstructSignatureDeclaration'
|
|
949
|
+
) {
|
|
885
950
|
// Call or construct signature
|
|
886
951
|
if (node.typeParameters) {
|
|
887
952
|
visit(node.typeParameters);
|
|
@@ -1078,7 +1143,22 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1078
1143
|
visit(node.typeAnnotation);
|
|
1079
1144
|
}
|
|
1080
1145
|
return;
|
|
1081
|
-
} else if (
|
|
1146
|
+
} else if (
|
|
1147
|
+
node.type === 'TSAnyKeyword' ||
|
|
1148
|
+
node.type === 'TSUnknownKeyword' ||
|
|
1149
|
+
node.type === 'TSNumberKeyword' ||
|
|
1150
|
+
node.type === 'TSObjectKeyword' ||
|
|
1151
|
+
node.type === 'TSBooleanKeyword' ||
|
|
1152
|
+
node.type === 'TSBigIntKeyword' ||
|
|
1153
|
+
node.type === 'TSStringKeyword' ||
|
|
1154
|
+
node.type === 'TSSymbolKeyword' ||
|
|
1155
|
+
node.type === 'TSVoidKeyword' ||
|
|
1156
|
+
node.type === 'TSUndefinedKeyword' ||
|
|
1157
|
+
node.type === 'TSNullKeyword' ||
|
|
1158
|
+
node.type === 'TSNeverKeyword' ||
|
|
1159
|
+
node.type === 'TSThisType' ||
|
|
1160
|
+
node.type === 'TSIntrinsicKeyword'
|
|
1161
|
+
) {
|
|
1082
1162
|
// Primitive type keywords - leaf nodes, no children
|
|
1083
1163
|
return;
|
|
1084
1164
|
} else if (node.type === 'TSDeclareFunction') {
|
|
@@ -1120,23 +1200,40 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1120
1200
|
}
|
|
1121
1201
|
|
|
1122
1202
|
throw new Error(`Unhandled AST node type in mapping walker: ${node.type}`);
|
|
1123
|
-
}
|
|
1203
|
+
},
|
|
1124
1204
|
});
|
|
1125
1205
|
|
|
1126
1206
|
// Process each token in order
|
|
1127
1207
|
// All tokens now have .loc property - no need for fallback logic
|
|
1128
1208
|
for (const token of tokens) {
|
|
1129
1209
|
const source_text = token.source;
|
|
1210
|
+
const gen_text = token.generated;
|
|
1130
1211
|
|
|
1131
1212
|
// Handle import statement full-statement mapping
|
|
1132
1213
|
if (token.is_import_statement) {
|
|
1133
1214
|
// Get source position from start
|
|
1134
|
-
const source_start = loc_to_offset(
|
|
1135
|
-
|
|
1215
|
+
const source_start = loc_to_offset(
|
|
1216
|
+
token.loc.start.line,
|
|
1217
|
+
token.loc.start.column,
|
|
1218
|
+
source_line_offsets,
|
|
1219
|
+
);
|
|
1220
|
+
const source_end = loc_to_offset(
|
|
1221
|
+
token.loc.end.line,
|
|
1222
|
+
token.loc.end.column,
|
|
1223
|
+
source_line_offsets,
|
|
1224
|
+
);
|
|
1136
1225
|
|
|
1137
1226
|
// Get generated positions using source map
|
|
1138
|
-
const gen_start_pos = get_generated_position(
|
|
1139
|
-
|
|
1227
|
+
const gen_start_pos = get_generated_position(
|
|
1228
|
+
token.loc.start.line,
|
|
1229
|
+
token.loc.start.column,
|
|
1230
|
+
adjusted_source_map,
|
|
1231
|
+
);
|
|
1232
|
+
const gen_end_pos = get_generated_position(
|
|
1233
|
+
token.loc.end.line,
|
|
1234
|
+
token.loc.end.column,
|
|
1235
|
+
adjusted_source_map,
|
|
1236
|
+
);
|
|
1140
1237
|
|
|
1141
1238
|
if (source_start !== null && source_end !== null && gen_start_pos && gen_end_pos) {
|
|
1142
1239
|
// Convert generated line:col to byte offsets
|
|
@@ -1152,7 +1249,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1152
1249
|
lengths: [Math.min(source_length, gen_length)],
|
|
1153
1250
|
data: {
|
|
1154
1251
|
// only verification (diagnostics) to avoid duplicate hover/completion
|
|
1155
|
-
verification: true
|
|
1252
|
+
verification: true,
|
|
1253
|
+
|
|
1254
|
+
customData: {
|
|
1255
|
+
generatedLengths: [gen_text.length],
|
|
1256
|
+
},
|
|
1156
1257
|
},
|
|
1157
1258
|
});
|
|
1158
1259
|
}
|
|
@@ -1160,25 +1261,42 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1160
1261
|
}
|
|
1161
1262
|
|
|
1162
1263
|
// Use .loc to get the exact source position
|
|
1163
|
-
const source_pos = loc_to_offset(
|
|
1264
|
+
const source_pos = loc_to_offset(
|
|
1265
|
+
token.loc.start.line,
|
|
1266
|
+
token.loc.start.column,
|
|
1267
|
+
source_line_offsets,
|
|
1268
|
+
);
|
|
1164
1269
|
|
|
1165
1270
|
// Get generated position using source map
|
|
1166
|
-
const gen_line_col = get_generated_position(
|
|
1271
|
+
const gen_line_col = get_generated_position(
|
|
1272
|
+
token.loc.start.line,
|
|
1273
|
+
token.loc.start.column,
|
|
1274
|
+
adjusted_source_map,
|
|
1275
|
+
);
|
|
1167
1276
|
let gen_pos = null;
|
|
1168
1277
|
if (gen_line_col) {
|
|
1169
1278
|
// Convert generated line:col to byte offset
|
|
1170
1279
|
gen_pos = gen_loc_to_offset(gen_line_col.line, gen_line_col.column);
|
|
1171
1280
|
} else {
|
|
1172
1281
|
// No mapping found in source map - this shouldn't happen since all tokens should have mappings
|
|
1173
|
-
console.warn(
|
|
1282
|
+
console.warn(
|
|
1283
|
+
`[segments.js] No source map entry for token "${source_text}" at ${token.loc.start.line}:${token.loc.start.column}`,
|
|
1284
|
+
);
|
|
1174
1285
|
}
|
|
1175
1286
|
|
|
1176
1287
|
if (source_pos !== null && gen_pos !== null) {
|
|
1288
|
+
// !IMPORTANT: don't set generatedLengths, otherwise Volar will use that vs our source
|
|
1289
|
+
// We're adding it to our custom metadata instead as we need it for patching positions
|
|
1177
1290
|
mappings.push({
|
|
1178
1291
|
sourceOffsets: [source_pos],
|
|
1179
1292
|
generatedOffsets: [gen_pos],
|
|
1180
1293
|
lengths: [source_text.length],
|
|
1181
|
-
data:
|
|
1294
|
+
data: {
|
|
1295
|
+
...mapping_data,
|
|
1296
|
+
customData: {
|
|
1297
|
+
generatedLengths: [gen_text.length],
|
|
1298
|
+
},
|
|
1299
|
+
},
|
|
1182
1300
|
});
|
|
1183
1301
|
}
|
|
1184
1302
|
}
|
|
@@ -1195,9 +1313,10 @@ export function convert_source_map_to_mappings(ast, source, generated_code, esra
|
|
|
1195
1313
|
lengths: [1],
|
|
1196
1314
|
data: {
|
|
1197
1315
|
...mapping_data,
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1316
|
+
customData: {
|
|
1317
|
+
generatedLengths: [1],
|
|
1318
|
+
},
|
|
1319
|
+
},
|
|
1201
1320
|
});
|
|
1202
1321
|
}
|
|
1203
1322
|
|
|
@@ -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
|
}
|