"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractElementTags = exports.convertChildren = void 0; const block_1 = require("./block"); const common_1 = require("./common"); const mustache_1 = require("./mustache"); const text_1 = require("./text"); const attr_1 = require("./attr"); const const_1 = require("./const"); const sort_1 = require("../sort"); const __1 = require("../.."); /* eslint-disable complexity -- X */ /** Convert for Fragment or Element or ... */ function* convertChildren( /* eslint-enable complexity -- X */ fragment, parent, ctx) { for (const child of fragment.children) { if (child.type === "Comment") { yield convertComment(child, parent, ctx); continue; } if (child.type === "Text") { if (!child.data && child.start === child.end) { continue; } yield (0, text_1.convertText)(child, parent, ctx); continue; } if (child.type === "Element") { if (child.name.includes(":")) { yield convertSpecialElement(child, parent, ctx); } else { yield convertHTMLElement(child, parent, ctx); } continue; } if (child.type === "InlineComponent") { if (child.name.includes(":")) { yield convertSpecialElement(child, parent, ctx); } else { yield convertComponentElement(child, parent, ctx); } continue; } if (child.type === "Slot") { yield convertSlotElement(child, parent, ctx); continue; } if (child.type === "MustacheTag") { yield (0, mustache_1.convertMustacheTag)(child, parent, ctx); continue; } if (child.type === "RawMustacheTag") { yield (0, mustache_1.convertRawMustacheTag)(child, parent, ctx); continue; } if (child.type === "IfBlock") { // {#if expr} {/if} yield (0, block_1.convertIfBlock)(child, parent, ctx); continue; } if (child.type === "EachBlock") { // {#each expr as item, index (key)} {/each} yield (0, block_1.convertEachBlock)(child, parent, ctx); continue; } if (child.type === "AwaitBlock") { // {#await promise} {:then number} {:catch error} {/await} yield (0, block_1.convertAwaitBlock)(child, parent, ctx); continue; } if (child.type === "KeyBlock") { // {#key expression}...{/key} yield (0, block_1.convertKeyBlock)(child, parent, ctx); continue; } if (child.type === "Window") { yield convertWindowElement(child, parent, ctx); continue; } if (child.type === "Body") { yield convertBodyElement(child, parent, ctx); continue; } if (child.type === "Head") { yield convertHeadElement(child, parent, ctx); continue; } if (child.type === "Title") { yield convertTitleElement(child, parent, ctx); continue; } if (child.type === "Options") { yield convertOptionsElement(child, parent, ctx); continue; } if (child.type === "SlotTemplate") { yield convertSlotTemplateElement(child, parent, ctx); continue; } if (child.type === "DebugTag") { yield (0, mustache_1.convertDebugTag)(child, parent, ctx); continue; } if (child.type === "ConstTag") { yield (0, const_1.convertConstTag)(child, parent, ctx); continue; } if (child.type === "Document") { yield convertDocumentElement(child, parent, ctx); continue; } throw new Error(`Unknown type:${child.type}`); } } exports.convertChildren = convertChildren; /** Extract `let:` directives. */ function extractLetDirectives(fragment) { const letDirectives = []; const attributes = []; for (const attr of fragment.attributes) { if (attr.type === "Let") { letDirectives.push(attr); } else { attributes.push(attr); } } return { letDirectives, attributes }; } /** Check if children needs a scope. */ function needScopeByChildren(fragment) { for (const child of fragment.children) { if (child.type === "ConstTag") { return true; } } return false; } /** Convert for HTML Comment */ function convertComment(node, parent, ctx) { const comment = Object.assign({ type: "SvelteHTMLComment", value: node.data, parent }, ctx.getConvertLocation(node)); ctx.addToken("HTMLComment", node); return comment; } /** Convert for HTMLElement */ function convertHTMLElement(node, parent, ctx) { var _a, _b; const locs = ctx.getConvertLocation(node); const element = Object.assign({ type: "SvelteElement", kind: "html", name: null, startTag: { type: "SvelteStartTag", attributes: [], selfClosing: false, parent: null, range: [locs.range[0], null], loc: { start: { line: locs.loc.start.line, column: locs.loc.start.column, }, end: null, }, }, children: [], endTag: null, parent }, locs); ctx.elements.set(element, node); element.startTag.parent = element; const elementName = node.name; const { letDirectives, attributes } = extractLetDirectives(node); const letParams = []; if (letDirectives.length) { ctx.letDirCollections.beginExtract(); element.startTag.attributes.push(...(0, attr_1.convertAttributes)(letDirectives, element.startTag, ctx)); letParams.push(...ctx.letDirCollections.extract().getLetParams()); } if (!letParams.length && !needScopeByChildren(node)) { element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx)); element.children.push(...convertChildren(node, element, ctx)); } else { ctx.scriptLet.nestBlock(element, letParams); element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx)); (0, sort_1.sortNodes)(element.startTag.attributes); element.children.push(...convertChildren(node, element, ctx)); ctx.scriptLet.closeScope(); } extractElementTags(element, ctx, { buildNameNode: (openTokenRange) => { ctx.addToken("HTMLIdentifier", openTokenRange); const name = Object.assign({ type: "SvelteName", name: elementName, parent: element }, ctx.getConvertLocation(openTokenRange)); return name; }, }); if (element.name.name === "script" || element.name.name === "style" || (element.name.name === "template" && ctx.findBlock(element))) { // Restore the block-like element. for (const child of element.children) { if (child.type === "SvelteText") { child.value = ctx.code.slice(...child.range); } } if (element.name.name === "script") { ctx.stripScriptCode(element.startTag.range[1], (_b = (_a = element.endTag) === null || _a === void 0 ? void 0 : _a.range[0]) !== null && _b !== void 0 ? _b : element.range[1]); } } if (element.startTag.selfClosing && element.name.name.endsWith("-")) { // Restore the self-closing block. const selfClosingBlock = /^[a-z]-+$/iu.test(element.name.name) && ctx.findSelfClosingBlock(element); if (selfClosingBlock) { element.name.name = selfClosingBlock.originalTag; } } return element; } /** Convert for Special element. e.g. */ function convertSpecialElement(node, parent, ctx) { const locs = ctx.getConvertLocation(node); const element = Object.assign({ type: "SvelteElement", kind: "special", name: null, startTag: { type: "SvelteStartTag", attributes: [], selfClosing: false, parent: null, range: [locs.range[0], null], loc: { start: { line: locs.loc.start.line, column: locs.loc.start.column, }, end: null, }, }, children: [], endTag: null, parent }, locs); ctx.elements.set(element, node); element.startTag.parent = element; const elementName = node.name; const { letDirectives, attributes } = extractLetDirectives(node); const letParams = []; if (letDirectives.length) { ctx.letDirCollections.beginExtract(); element.startTag.attributes.push(...(0, attr_1.convertAttributes)(letDirectives, element.startTag, ctx)); letParams.push(...ctx.letDirCollections.extract().getLetParams()); } if (!letParams.length && !needScopeByChildren(node)) { element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx)); element.children.push(...convertChildren(node, element, ctx)); } else { ctx.scriptLet.nestBlock(element, letParams); element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx)); (0, sort_1.sortNodes)(element.startTag.attributes); element.children.push(...convertChildren(node, element, ctx)); ctx.scriptLet.closeScope(); } const thisExpression = (node.type === "InlineComponent" && elementName === "svelte:component" && node.expression) || (node.type === "Element" && elementName === "svelte:element" && node.tag); if (thisExpression) { processThisAttribute(node, thisExpression, element, ctx); } extractElementTags(element, ctx, { buildNameNode: (openTokenRange) => { ctx.addToken("HTMLIdentifier", openTokenRange); const name = Object.assign({ type: "SvelteName", name: elementName, parent: element }, ctx.getConvertLocation(openTokenRange)); return name; }, }); return element; } /** process `this=` */ function processThisAttribute(node, thisValue, element, ctx) { let thisNode; if (typeof thisValue === "string") { // this="..." const startIndex = findStartIndexOfThis(node, ctx); const eqIndex = ctx.code.indexOf("=", startIndex + 4 /* t,h,i,s */); const valueStartIndex = (0, common_1.indexOf)(ctx.code, (c) => Boolean(c.trim()), eqIndex + 1); const quote = ctx.code.startsWith(thisValue, valueStartIndex) ? null : ctx.code[valueStartIndex]; const literalStartIndex = quote ? valueStartIndex + quote.length : valueStartIndex; const literalEndIndex = literalStartIndex + thisValue.length; const endIndex = quote ? literalEndIndex + quote.length : literalEndIndex; const thisAttr = Object.assign({ type: "SvelteAttribute", key: null, boolean: false, value: [], parent: element.startTag }, ctx.getConvertLocation({ start: startIndex, end: endIndex })); thisAttr.key = Object.assign({ type: "SvelteName", name: "this", parent: thisAttr }, ctx.getConvertLocation({ start: startIndex, end: eqIndex })); thisAttr.value.push(Object.assign({ type: "SvelteLiteral", value: thisValue, parent: thisAttr }, ctx.getConvertLocation({ start: literalStartIndex, end: literalEndIndex, }))); // this ctx.addToken("HTMLIdentifier", { start: startIndex, end: startIndex + 4, }); // = ctx.addToken("Punctuator", { start: eqIndex, end: eqIndex + 1, }); if (quote) { // " ctx.addToken("Punctuator", { start: valueStartIndex, end: literalStartIndex, }); } ctx.addToken("HTMLText", { start: literalStartIndex, end: literalEndIndex, }); if (quote) { // " ctx.addToken("Punctuator", { start: literalEndIndex, end: endIndex, }); } thisNode = thisAttr; } else { // this={...} const eqIndex = ctx.code.lastIndexOf("=", (0, common_1.getWithLoc)(thisValue).start); const startIndex = ctx.code.lastIndexOf("this", eqIndex); const closeIndex = ctx.code.indexOf("}", (0, common_1.getWithLoc)(thisValue).end); const endIndex = (0, common_1.indexOf)(ctx.code, (c) => c === ">" || !c.trim(), closeIndex); const thisDir = Object.assign({ type: "SvelteSpecialDirective", kind: "this", key: null, expression: null, parent: element.startTag }, ctx.getConvertLocation({ start: startIndex, end: endIndex })); thisDir.key = Object.assign({ type: "SvelteSpecialDirectiveKey", parent: thisDir }, ctx.getConvertLocation({ start: startIndex, end: eqIndex })); // this ctx.addToken("HTMLIdentifier", { start: startIndex, end: startIndex + 4, }); // = ctx.addToken("Punctuator", { start: eqIndex, end: eqIndex + 1, }); ctx.scriptLet.addExpression(thisValue, thisDir, null, (es) => { thisDir.expression = es; }); thisNode = thisDir; } const targetIndex = element.startTag.attributes.findIndex((attr) => thisNode.range[1] <= attr.range[0]); if (targetIndex === -1) { element.startTag.attributes.push(thisNode); } else { element.startTag.attributes.splice(targetIndex, 0, thisNode); } } /** Find the start index of `this` */ function findStartIndexOfThis(node, ctx) { var _a, _b, _d, _e; // Get the end index of `svelte:element` const startIndex = ctx.code.indexOf(node.name, node.start) + node.name.length; const sortedAttrs = [...node.attributes].sort((a, b) => a.start - b.start); // Find the start index of `this` from the end index of `svelte:element`. // However, it only seeks to the start index of the first attribute (or the end index of element node). let thisIndex = (0, common_1.indexOf)(ctx.code, (_c, index) => ctx.code.startsWith("this", index), startIndex, (_b = (_a = sortedAttrs[0]) === null || _a === void 0 ? void 0 : _a.start) !== null && _b !== void 0 ? _b : node.end); while (thisIndex < 0) { if (sortedAttrs.length === 0) throw new __1.ParseError("Cannot resolved `this` attribute.", thisIndex, ctx); // Step3: Find the start index of `this` from the end index of attribute. // However, it only seeks to the start index of the first attribute (or the end index of element node). const nextStartIndex = sortedAttrs.shift().end; thisIndex = (0, common_1.indexOf)(ctx.code, (_c, index) => ctx.code.startsWith("this", index), nextStartIndex, (_e = (_d = sortedAttrs[0]) === null || _d === void 0 ? void 0 : _d.start) !== null && _e !== void 0 ? _e : node.end); } return thisIndex; } /** Convert for ComponentElement */ function convertComponentElement(node, parent, ctx) { const locs = ctx.getConvertLocation(node); const element = Object.assign({ type: "SvelteElement", kind: "component", name: null, startTag: { type: "SvelteStartTag", attributes: [], selfClosing: false, parent: null, range: [locs.range[0], null], loc: { start: { line: locs.loc.start.line, column: locs.loc.start.column, }, end: null, }, }, children: [], endTag: null, parent }, locs); ctx.elements.set(element, node); element.startTag.parent = element; const elementName = node.name; const { letDirectives, attributes } = extractLetDirectives(node); const letParams = []; if (letDirectives.length) { ctx.letDirCollections.beginExtract(); element.startTag.attributes.push(...(0, attr_1.convertAttributes)(letDirectives, element.startTag, ctx)); letParams.push(...ctx.letDirCollections.extract().getLetParams()); } if (!letParams.length && !needScopeByChildren(node)) { element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx)); element.children.push(...convertChildren(node, element, ctx)); } else { ctx.scriptLet.nestBlock(element, letParams); element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx)); (0, sort_1.sortNodes)(element.startTag.attributes); element.children.push(...convertChildren(node, element, ctx)); ctx.scriptLet.closeScope(); } extractElementTags(element, ctx, { buildNameNode: (openTokenRange) => { const chains = elementName.split("."); const id = chains.shift(); const idRange = { start: openTokenRange.start, end: openTokenRange.start + id.length, }; // ctx.addToken("Identifier", idRange) const identifier = Object.assign({ type: "Identifier", name: id, // @ts-expect-error -- ignore parent: element }, ctx.getConvertLocation(idRange)); let object = identifier; // eslint-disable-next-line func-style -- var let esCallback = (es) => { element.name = es; }; let start = idRange.end + 1; for (const name of chains) { const range = { start, end: start + name.length }; ctx.addToken("HTMLIdentifier", range); const mem = Object.assign({ type: "SvelteMemberExpressionName", object, property: Object.assign({ type: "SvelteName", name, parent: null }, ctx.getConvertLocation(range)), parent: element }, ctx.getConvertLocation({ start: openTokenRange.start, end: range.end, })); mem.property.parent = mem; object.parent = mem; object = mem; start = range.end + 1; if (mem.object === identifier) { esCallback = (es) => { mem.object = es; }; } } ctx.scriptLet.addExpression(identifier, identifier.parent, null, esCallback); return object; }, }); return element; } /** Convert for Slot */ function convertSlotElement(node, parent, ctx) { // Slot translates to SvelteHTMLElement. const element = convertHTMLElement(node, parent, ctx); ctx.slots.add(element); return element; } /** Convert for window element. e.g. */ function convertWindowElement(node, parent, ctx) { return convertSpecialElement(node, parent, ctx); } /** Convert for document element. e.g. */ function convertDocumentElement(node, parent, ctx) { return convertSpecialElement(node, parent, ctx); } /** Convert for body element. e.g. */ function convertBodyElement(node, parent, ctx) { return convertSpecialElement(node, parent, ctx); } /** Convert for head element. e.g. */ function convertHeadElement(node, parent, ctx) { return convertSpecialElement(node, parent, ctx); } /** Convert for title element. e.g. */ function convertTitleElement(node, parent, ctx) { return convertHTMLElement(node, parent, ctx); } /** Convert for options element. e.g. <svelte:options> */ function convertOptionsElement(node, parent, ctx) { return convertSpecialElement(node, parent, ctx); } /** Convert for <svelte:fragment> element. */ function convertSlotTemplateElement(node, parent, ctx) { return convertSpecialElement(node, parent, ctx); } /** Extract element tag and tokens */ function extractElementTags(element, ctx, options) { var _a, _b; const startTagNameEnd = (0, common_1.indexOf)(ctx.code, (c) => c === "/" || c === ">" || !c.trim(), element.range[0] + 1); const openTokenRange = { start: element.range[0] + 1, end: startTagNameEnd, }; element.name = options.buildNameNode(openTokenRange); const startTagEnd = ctx.code.indexOf(">", (_b = (_a = element.startTag.attributes[element.startTag.attributes.length - 1]) === null || _a === void 0 ? void 0 : _a.range[1]) !== null && _b !== void 0 ? _b : openTokenRange.end) + 1; element.startTag.range[1] = startTagEnd; element.startTag.loc.end = ctx.getLocFromIndex(startTagEnd); if (ctx.code[element.range[1] - 1] !== ">") { // Have not end tag return; } if (ctx.code[element.range[1] - 2] === "/") { // self close element.startTag.selfClosing = true; return; } const endTagOpen = ctx.code.lastIndexOf("<", element.range[1] - 1); if (endTagOpen <= startTagEnd - 1) { // void element return; } const endTagNameStart = endTagOpen + 2; const endTagNameEnd = (0, common_1.indexOf)(ctx.code, (c) => c === ">" || !c.trim(), endTagNameStart); const endTagClose = ctx.code.indexOf(">", endTagNameEnd); element.endTag = Object.assign({ type: "SvelteEndTag", parent: element }, ctx.getConvertLocation({ start: endTagOpen, end: endTagClose + 1 })); ctx.addToken("HTMLIdentifier", { start: endTagNameStart, end: endTagNameEnd, }); } exports.extractElementTags = extractElementTags;