partial-tsx 0.0.2 → 0.0.3
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 +1 -1
- package/src/partial.ts +49 -9
package/package.json
CHANGED
package/src/partial.ts
CHANGED
|
@@ -278,6 +278,21 @@ const isLikelyStatementBlock = (code: string, index: number) => {
|
|
|
278
278
|
return /\bfunction(?:\s+[\w$]+)?\s*\([^)]*\)$/.test(before) || /\b(?:if|for|while|switch|with)\s*\([^)]*\)$/.test(before) || /\bcatch(?:\s*\([^)]*\))?$/.test(before) || /(?:^|[^\w$])(?:else|try|finally|do)$/.test(before) || before.endsWith("=>");
|
|
279
279
|
};
|
|
280
280
|
|
|
281
|
+
// `null` when `index` isn't a comment start, `-1` when the comment is unterminated, else the index just past it.
|
|
282
|
+
// The three tag/expression scanners below all need this and drifted apart once already — #1600 was one of them missing it.
|
|
283
|
+
const findCommentEnd = (code: string, index: number) => {
|
|
284
|
+
if (code[index] !== "/") return null;
|
|
285
|
+
if (code[index + 1] === "/") {
|
|
286
|
+
const end = code.indexOf("\n", index + 2);
|
|
287
|
+
return end === -1 ? -1 : end + 1;
|
|
288
|
+
}
|
|
289
|
+
if (code[index + 1] === "*") {
|
|
290
|
+
const end = code.indexOf("*/", index + 2);
|
|
291
|
+
return end === -1 ? -1 : end + 2;
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
};
|
|
295
|
+
|
|
281
296
|
const findJsxTagEnd = (code: string, start: number) => {
|
|
282
297
|
const stack: string[] = [];
|
|
283
298
|
let quote: string | null = null;
|
|
@@ -288,6 +303,20 @@ const findJsxTagEnd = (code: string, start: number) => {
|
|
|
288
303
|
else if (char === quote) quote = null;
|
|
289
304
|
continue;
|
|
290
305
|
}
|
|
306
|
+
// A regex ending in an escaped slash (`/\//`) puts a literal `//` in the source, so it must be consumed before the
|
|
307
|
+
// comment scan below mistakes it for one — same ordering `findJsxAttributeExpressionEnd` uses.
|
|
308
|
+
if (stack.length > 0 && isLikelyRegexLiteralStart(code, index)) {
|
|
309
|
+
index = findRegexLiteralEnd(code, index) - 1;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
// Comments are legal anywhere in a tag, including its own attribute list — not just inside an attribute's `{…}`.
|
|
313
|
+
// Skipping them keeps an apostrophe in `// framework's Link` from opening a string that swallows the rest of the tag (#1600).
|
|
314
|
+
const commentEnd = findCommentEnd(code, index);
|
|
315
|
+
if (commentEnd !== null) {
|
|
316
|
+
if (commentEnd === -1) return -1;
|
|
317
|
+
index = commentEnd - 1;
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
291
320
|
if (char === '"' || char === "'" || char === "`") {
|
|
292
321
|
quote = char;
|
|
293
322
|
continue;
|
|
@@ -307,14 +336,9 @@ const findJsxTagEnd = (code: string, start: number) => {
|
|
|
307
336
|
const findJsxAttributeExpressionEnd = (code: string, index: number) => {
|
|
308
337
|
let depth = 1;
|
|
309
338
|
while (index < code.length) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
index =
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
if (code[index] === "/" && code[index + 1] === "*") {
|
|
316
|
-
const close = code.indexOf("*/", index + 2);
|
|
317
|
-
index = close === -1 ? code.length : close + 2;
|
|
339
|
+
const commentEnd = findCommentEnd(code, index);
|
|
340
|
+
if (commentEnd !== null) {
|
|
341
|
+
index = commentEnd === -1 ? code.length : commentEnd;
|
|
318
342
|
continue;
|
|
319
343
|
}
|
|
320
344
|
if (isLikelyRegexLiteralStart(code, index)) {
|
|
@@ -349,6 +373,18 @@ const completeIncompleteJsxTag = (code: string, start: number) => {
|
|
|
349
373
|
let tag = "";
|
|
350
374
|
while (index < code.length) {
|
|
351
375
|
const char = code[index];
|
|
376
|
+
const commentEnd = findCommentEnd(code, index);
|
|
377
|
+
if (commentEnd !== null) {
|
|
378
|
+
// A still-streaming comment has to be dropped, not carried: emitting it would bury the `>` we synthesize below
|
|
379
|
+
// inside the comment. A closed one is kept verbatim — comments are legal in a JSX attribute list (#1600).
|
|
380
|
+
if (commentEnd === -1) {
|
|
381
|
+
index = code.length;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
tag += code.slice(index, commentEnd);
|
|
385
|
+
index = commentEnd;
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
352
388
|
if (char === "`") {
|
|
353
389
|
// A backtick is illegal as a JSX attribute value (`title=`x`` → "JSX value should be either an expression or a quoted JSX text"); template literals are only valid inside `{...}`. Drop the attribute.
|
|
354
390
|
tag = tag.replace(/\s*[\w$:-]+\s*=\s*$/, "");
|
|
@@ -396,7 +432,11 @@ const completeIncompleteJsxTag = (code: string, start: number) => {
|
|
|
396
432
|
if (/=\s*$/.test(tag)) tag = tag.replace(/\s*[\w$:-]+\s*=\s*$/, "");
|
|
397
433
|
// Otherwise a trailing name with no following space is still streaming (`valu` may become `value`); a trailing space commits it as a boolean attr.
|
|
398
434
|
else if (!/\s$/.test(tag)) tag = tag.replace(/\s+[A-Za-z_$][\w$:-]*$/, "");
|
|
399
|
-
|
|
435
|
+
// That trim can eat the newline terminating a trailing comment. The closer synthesized below must stay clear of it:
|
|
436
|
+
// after `//` it would be buried inside the comment, after `*/` it would fuse into a spurious self-closing `/>`.
|
|
437
|
+
if (/\/\/[^\n]*$|\*\/$/.test(tag)) tag += "\n";
|
|
438
|
+
// The `\/` strip below must not mistake a block comment's terminating slash for a stray self-closing one.
|
|
439
|
+
const completedTag = VOID_TAGS.has(tagName) ? `${tag.replace(/\s*(?<!\*)\/\s*$/, "")} />` : `${tag}>`;
|
|
400
440
|
return { tag: completedTag, tagName };
|
|
401
441
|
};
|
|
402
442
|
|