Convert src/compiler/compile/render_dom/wrappers/Fragment.ts to JavaScript

pull/8569/head
Simon Holthausen 3 years ago
parent 57f5f5e3a4
commit f921b755a5

@ -3,6 +3,7 @@ import { b, x } from 'code-red';
import EventHandler from './Element/EventHandler.js'; import EventHandler from './Element/EventHandler.js';
import add_event_handlers from './shared/add_event_handlers.js'; import add_event_handlers from './shared/add_event_handlers.js';
import add_actions from './shared/add_actions.js'; import add_actions from './shared/add_actions.js';
const associated_events = { const associated_events = {
fullscreenElement: ['fullscreenchange'], fullscreenElement: ['fullscreenchange'],
visibilityState: ['visibilitychange'] visibilityState: ['visibilitychange']

@ -419,7 +419,7 @@ export default class EachBlockWrapper extends Wrapper {
* initial_mount_node: import('estree').Identifier; * initial_mount_node: import('estree').Identifier;
* update_anchor_node: import('estree').Identifier; * update_anchor_node: import('estree').Identifier;
* update_mount_node: import('estree').Identifier; * update_mount_node: import('estree').Identifier;
* }} * }} params
*/ */
render_keyed({ render_keyed({
block, block,

@ -1,170 +1,161 @@
import Wrapper from './shared/Wrapper'; import AwaitBlock from './AwaitBlock.js';
import AwaitBlock from './AwaitBlock'; import Body from './Body.js';
import Body from './Body'; import DebugTag from './DebugTag.js';
import DebugTag from './DebugTag'; import Document from './Document.js';
import Document from './Document'; import EachBlock from './EachBlock.js';
import EachBlock from './EachBlock'; import Element from './Element.js';
import Element from './Element'; import Head from './Head.js';
import Head from './Head'; import IfBlock from './IfBlock.js';
import IfBlock from './IfBlock'; import KeyBlock from './KeyBlock.js';
import KeyBlock from './KeyBlock'; import InlineComponent from './InlineComponent/index.js';
import InlineComponent from './InlineComponent/index'; import MustacheTag from './MustacheTag.js';
import MustacheTag from './MustacheTag'; import RawMustacheTag from './RawMustacheTag.js';
import RawMustacheTag from './RawMustacheTag'; import Slot from './Slot.js';
import Slot from './Slot'; import SlotTemplate from './SlotTemplate.js';
import SlotTemplate from './SlotTemplate'; import Text from './Text.js';
import Text from './Text'; import Comment from './Comment.js';
import Comment from './Comment'; import Title from './Title.js';
import Title from './Title'; import Window from './Window.js';
import Window from './Window'; import { trim_start, trim_end } from '../../../utils/trim.js';
import { INode } from '../../nodes/interfaces'; import { link } from '../../../utils/link.js';
import Renderer from '../Renderer'; import { regex_starts_with_whitespace } from '../../../utils/patterns.js';
import Block from '../Block';
import { trim_start, trim_end } from '../../../utils/trim';
import { link } from '../../../utils/link';
import { Identifier } from 'estree';
import { regex_starts_with_whitespace } from '../../../utils/patterns';
const wrappers = { const wrappers = {
AwaitBlock, AwaitBlock,
Body, Body,
Comment, Comment,
DebugTag, DebugTag,
Document, Document,
EachBlock, EachBlock,
Element, Element,
Head, Head,
IfBlock, IfBlock,
InlineComponent, InlineComponent,
KeyBlock, KeyBlock,
MustacheTag, MustacheTag,
Options: null, Options: null,
RawMustacheTag, RawMustacheTag,
Slot, Slot,
SlotTemplate, SlotTemplate,
Text, Text,
Title, Title,
Window Window
}; };
function trimmable_at(child: INode, next_sibling: Wrapper): boolean { /**
// Whitespace is trimmable if one of the following is true: * @param {import('../../nodes/interfaces.js').INode} child
// The child and its sibling share a common nearest each block (not at an each block boundary) * @param {import('./shared/Wrapper.js').default} next_sibling
// The next sibling's previous node is an each block * @returns {boolean}
return ( */
next_sibling.node.find_nearest(/EachBlock/) === child.find_nearest(/EachBlock/) || function trimmable_at(child, next_sibling) {
next_sibling.node.prev.type === 'EachBlock' // Whitespace is trimmable if one of the following is true:
); // The child and its sibling share a common nearest each block (not at an each block boundary)
// The next sibling's previous node is an each block
return (next_sibling.node.find_nearest(/EachBlock/) === child.find_nearest(/EachBlock/) ||
next_sibling.node.prev.type === 'EachBlock');
} }
export default class FragmentWrapper { export default class FragmentWrapper {
nodes: Wrapper[];
constructor(
renderer: Renderer,
block: Block,
nodes: INode[],
parent: Wrapper,
strip_whitespace: boolean,
next_sibling: Wrapper
) {
this.nodes = [];
let last_child: Wrapper;
let window_wrapper: Window | undefined;
let i = nodes.length;
while (i--) {
const child = nodes[i];
if (!child.type) {
throw new Error('missing type');
}
if (!(child.type in wrappers)) {
throw new Error(`TODO implement ${child.type}`);
}
// special case — this is an easy way to remove whitespace surrounding
// <svelte:window/>. lil hacky but it works
if (child.type === 'Window') {
window_wrapper = new Window(renderer, block, parent, child);
continue;
}
if (child.type === 'Text') {
let { data } = child;
// We want to remove trailing whitespace inside an element/component/block,
// *unless* there is no whitespace between this node and its next sibling
if (this.nodes.length === 0) {
const should_trim = next_sibling
? next_sibling.node.type === 'Text' &&
regex_starts_with_whitespace.test(next_sibling.node.data) &&
trimmable_at(child, next_sibling)
: !child.has_ancestor('EachBlock');
if (should_trim && !child.keep_space()) { /** @type {import('./shared/Wrapper.js').default[]} */
data = trim_end(data); nodes;
if (!data) continue;
} /**
} * @param {import('../Renderer.js').default} renderer
* @param {import('../Block.js').default} block
// glue text nodes (which could e.g. be separated by comments) together * @param {import('../../nodes/interfaces.js').INode[]} nodes
if (last_child && last_child.node.type === 'Text') { * @param {import('./shared/Wrapper.js').default} parent
(last_child as Text).data = data + (last_child as Text).data; * @param {boolean} strip_whitespace
continue; * @param {import('./shared/Wrapper.js').default} next_sibling
} */
constructor(renderer, block, nodes, parent, strip_whitespace, next_sibling) {
const wrapper = new Text(renderer, block, parent, child, data); this.nodes = [];
if (wrapper.skip) continue;
/** @type {import('./shared/Wrapper.js').default} */
this.nodes.unshift(wrapper); let last_child;
link(last_child, (last_child = wrapper)); /** @type {import('./Window.js').default | undefined} */
} else { let window_wrapper;
const Wrapper = wrappers[child.type]; let i = nodes.length;
if (!Wrapper || (child.type === 'Comment' && !renderer.options.preserveComments)) continue; while (i--) {
const child = nodes[i];
const wrapper = new Wrapper( if (!child.type) {
renderer, throw new Error('missing type');
block, }
parent, if (!(child.type in wrappers)) {
child, throw new Error(`TODO implement ${child.type}`);
strip_whitespace, }
last_child || next_sibling // special case — this is an easy way to remove whitespace surrounding
); // <svelte:window/>. lil hacky but it works
this.nodes.unshift(wrapper); if (child.type === 'Window') {
window_wrapper = new Window(renderer, block, parent, child);
link(last_child, (last_child = wrapper)); continue;
} }
} if (child.type === 'Text') {
let { data } = child;
if (strip_whitespace) { // We want to remove trailing whitespace inside an element/component/block,
const first = this.nodes[0] as Text; // *unless* there is no whitespace between this node and its next sibling
if (this.nodes.length === 0) {
const should_trim = next_sibling
? next_sibling.node.type === 'Text' &&
regex_starts_with_whitespace.test(next_sibling.node.data) &&
trimmable_at(child, next_sibling)
: !child.has_ancestor('EachBlock');
if (should_trim && !child.keep_space()) {
data = trim_end(data);
if (!data)
continue;
}
}
// glue text nodes (which could e.g. be separated by comments) together
if (last_child && last_child.node.type === 'Text') {
( /** @type {import('./Text.js').default} */(last_child)).data = data + ( /** @type {import('./Text.js').default} */(last_child)).data;
continue;
}
const wrapper = new Text(renderer, block, parent, child, data);
if (wrapper.skip)
continue;
this.nodes.unshift(wrapper);
link(last_child, (last_child = wrapper));
}
else {
const Wrapper = wrappers[child.type];
if (!Wrapper || (child.type === 'Comment' && !renderer.options.preserveComments))
continue;
const wrapper = new Wrapper(renderer, block, parent, child, strip_whitespace, last_child || next_sibling);
this.nodes.unshift(wrapper);
link(last_child, (last_child = wrapper));
}
}
if (strip_whitespace) {
const first = /** @type {import('./Text.js').default} */ (this.nodes[0]);
if (first && first.node.type === 'Text' && !first.node.keep_space()) {
first.data = trim_start(first.data);
if (!first.data) {
first.var = null;
this.nodes.shift();
if (this.nodes[0]) {
this.nodes[0].prev = null;
}
}
}
}
if (window_wrapper) {
this.nodes.unshift(window_wrapper);
link(last_child, window_wrapper);
}
}
/**
* @param {import('../Block.js').default} block
* @param {import('estree').Identifier} parent_node
* @param {import('estree').Identifier} parent_nodes
*/
render(block, parent_node, parent_nodes) {
for (let i = 0; i < this.nodes.length; i += 1) {
this.nodes[i].render(block, parent_node, parent_nodes);
}
}
}
if (first && first.node.type === 'Text' && !first.node.keep_space()) {
first.data = trim_start(first.data);
if (!first.data) {
first.var = null;
this.nodes.shift();
if (this.nodes[0]) {
this.nodes[0].prev = null;
}
}
}
}
if (window_wrapper) {
this.nodes.unshift(window_wrapper);
link(last_child, window_wrapper);
}
}
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
for (let i = 0; i < this.nodes.length; i += 1) {
this.nodes[i].render(block, parent_node, parent_nodes);
}
}
}

Loading…
Cancel
Save