Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/generators/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default class Generator {
hasJs: boolean;
computations: Computation[];
templateProperties: Record<string, Node>;
slots: Set<string>;

code: MagicString;

Expand Down Expand Up @@ -76,6 +77,7 @@ export default class Generator {
this.events = new Set();
this.transitions = new Set();
this.importedComponents = new Map();
this.slots = new Set();

this.bindingGroups = [];
this.indirectDependencies = new Map();
Expand Down
5 changes: 4 additions & 1 deletion src/generators/dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default function dom(
generator.stylesheet.warnOnUnusedSelectors(options.onwarn);

parsed.html.children.forEach((node: Node) => {
visit(generator, block, state, node, []);
visit(generator, block, state, node, [], []);
});

const builder = new CodeBuilder();
Expand Down Expand Up @@ -181,6 +181,7 @@ export default function dom(
this._root = options._root || this;
this._yield = options._yield;
this._bind = options._bind;
${generator.slots.size && `this._slotted = options.slots || {};`}

${generator.stylesheet.hasStyles &&
options.css !== false &&
Expand All @@ -200,6 +201,8 @@ export default function dom(
`}
`}

${generator.slots.size && `this.slots = {};`}

this._fragment = @create_main_fragment( this._state, this );

if ( options.target ) {
Expand Down
13 changes: 13 additions & 0 deletions src/generators/dom/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { DomGenerator } from './index';
import Block from './Block';
import { Node } from '../../interfaces';

export interface State {
name?: string;
namespace: string;
Expand All @@ -11,3 +15,12 @@ export interface State {
usesComponent?: boolean;
selectBindingDependencies?: string[];
}

export type Visitor = (
generator: DomGenerator,
block: Block,
state: State,
node: Node,
elementStack: Node[],
componentStack: Node[]
) => void;
42 changes: 30 additions & 12 deletions src/generators/dom/preprocess.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Block from './Block';
import { trimStart, trimEnd } from '../../utils/trim';
import { assign } from '../../shared/index.js';
import getStaticAttributeValue from '../shared/getStaticAttributeValue';
import { DomGenerator } from './index';
import { Node } from '../../interfaces';
import { State } from './interfaces';
Expand Down Expand Up @@ -41,6 +42,7 @@ const preprocessors = {
state: State,
node: Node,
elementStack: Node[],
componentStack: Node[],
stripWhitespace: boolean
) => {
const dependencies = block.findDependencies(node.expression);
Expand All @@ -57,6 +59,7 @@ const preprocessors = {
state: State,
node: Node,
elementStack: Node[],
componentStack: Node[],
stripWhitespace: boolean
) => {
const dependencies = block.findDependencies(node.expression);
Expand All @@ -74,6 +77,7 @@ const preprocessors = {
state: State,
node: Node,
elementStack: Node[],
componentStack: Node[],
stripWhitespace: boolean
) => {
node._state = getChildState(state);
Expand All @@ -94,6 +98,7 @@ const preprocessors = {
node: Node,
inEachBlock: boolean,
elementStack: Node[],
componentStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) => {
Expand All @@ -113,7 +118,7 @@ const preprocessors = {
node._state = getChildState(state);

blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node, inEachBlock, elementStack, stripWhitespace, nextSibling);
preprocessChildren(generator, node._block, node._state, node, inEachBlock, elementStack, componentStack, stripWhitespace, nextSibling);

if (node._block.dependencies.size > 0) {
dynamic = true;
Expand All @@ -140,6 +145,7 @@ const preprocessors = {
node.else,
inEachBlock,
elementStack,
componentStack,
stripWhitespace,
nextSibling
);
Expand Down Expand Up @@ -169,6 +175,7 @@ const preprocessors = {
node: Node,
inEachBlock: boolean,
elementStack: Node[],
componentStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) => {
Expand Down Expand Up @@ -221,7 +228,7 @@ const preprocessors = {
});

generator.blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node, true, elementStack, stripWhitespace, nextSibling);
preprocessChildren(generator, node._block, node._state, node, true, elementStack, componentStack, stripWhitespace, nextSibling);
block.addDependencies(node._block.dependencies);
node._block.hasUpdateMethod = node._block.dependencies.size > 0;

Expand All @@ -240,6 +247,7 @@ const preprocessors = {
node.else,
inEachBlock,
elementStack,
componentStack,
stripWhitespace,
nextSibling
);
Expand All @@ -254,6 +262,7 @@ const preprocessors = {
node: Node,
inEachBlock: boolean,
elementStack: Node[],
componentStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) => {
Expand Down Expand Up @@ -326,10 +335,23 @@ const preprocessors = {
generator.components.has(node.name) || node.name === ':Self';

if (isComponent) {
const name = block.getUniqueName(
(node.name === ':Self' ? generator.name : node.name).toLowerCase()
);

node._state = getChildState(state, {
name,
parentNode: `${name}._slotted.default`,
isYield: true
});
} else {
const slot = getStaticAttributeValue(node, 'slot');
if (slot) {
// TODO validate slots — no nesting, no dynamic names...
const component = componentStack[componentStack.length - 1];
component._slots.add(slot);
}

const name = block.getUniqueName(
node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
);
Expand All @@ -355,17 +377,12 @@ const preprocessors = {
(node.name === ':Self' ? generator.name : node.name).toLowerCase()
);

node._block = block.child({
name: generator.getUniqueName(`create_${name}_yield_fragment`),
});
if (node.children) node._slots = new Set(['default']); // TODO only include default if there are unslotted children

generator.blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node, inEachBlock, elementStack, stripWhitespace, nextSibling);
block.addDependencies(node._block.dependencies);
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
preprocessChildren(generator, block, node._state, node, inEachBlock, elementStack, componentStack.concat(node), stripWhitespace, nextSibling);
} else {
if (node.name === 'pre' || node.name === 'textarea') stripWhitespace = false;
preprocessChildren(generator, block, node._state, node, inEachBlock, elementStack.concat(node), stripWhitespace, nextSibling);
preprocessChildren(generator, block, node._state, node, inEachBlock, elementStack.concat(node), componentStack, stripWhitespace, nextSibling);
}
}
},
Expand All @@ -378,6 +395,7 @@ function preprocessChildren(
node: Node,
inEachBlock: boolean,
elementStack: Node[],
componentStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) {
Expand Down Expand Up @@ -407,7 +425,7 @@ function preprocessChildren(

cleaned.forEach((child: Node, i: number) => {
const preprocessor = preprocessors[child.type];
if (preprocessor) preprocessor(generator, block, state, child, inEachBlock, elementStack, stripWhitespace, cleaned[i + 1] || nextSibling);
if (preprocessor) preprocessor(generator, block, state, child, inEachBlock, elementStack, componentStack, stripWhitespace, cleaned[i + 1] || nextSibling);

if (lastChild) {
lastChild.next = child;
Expand Down Expand Up @@ -471,7 +489,7 @@ export default function preprocess(
};

generator.blocks.push(block);
preprocessChildren(generator, block, state, node, false, [], true, null);
preprocessChildren(generator, block, state, node, false, [], [], true, null);
block.hasUpdateMethod = true;

return { block, state };
Expand Down
5 changes: 3 additions & 2 deletions src/generators/dom/visit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ export default function visit(
block: Block,
state: State,
node: Node,
elementStack: Node[]
elementStack: Node[],
componentStack: Node[]
) {
const visitor = visitors[node.type];
visitor(generator, block, state, node, elementStack);
visitor(generator, block, state, node, elementStack, componentStack);
}
37 changes: 6 additions & 31 deletions src/generators/dom/visitors/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,47 +43,22 @@ export default function visitComponent(
block: Block,
state: State,
node: Node,
elementStack: Node[]
elementStack: Node[],
componentStack: Node[]
) {
generator.hasComponents = true;

const name = block.getUniqueName(
(node.name === ':Self' ? generator.name : node.name).toLowerCase()
);
const name = node._state.name;

const componentInitProperties = [`_root: #component._root`];

// Component has children, put them in a separate {{yield}} block
if (node.children.length > 0) {
const params = block.params.join(', ');

const childBlock = node._block;
const slots = Array.from(node._slots).map(name => `${name}: @createFragment()`);
componentInitProperties.push(`slots: { ${slots.join(', ')} }`);

node.children.forEach((child: Node) => {
visit(generator, childBlock, node._state, child, elementStack);
visit(generator, block, node._state, child, elementStack, componentStack.concat(node));
});

const yield_fragment = block.getUniqueName(`${name}_yield_fragment`);

block.builders.init.addLine(
`var ${yield_fragment} = ${childBlock.name}( ${params}, #component );`
);

block.builders.create.addLine(`${yield_fragment}.create();`);

block.builders.claim.addLine(
`${yield_fragment}.claim( ${state.parentNodes} );`
);

if (childBlock.hasUpdateMethod) {
block.builders.update.addLine(
`${yield_fragment}.update( changed, ${params} );`
);
}

block.builders.destroy.addLine(`${yield_fragment}.destroy();`);

componentInitProperties.push(`_yield: ${yield_fragment}`);
}

const allContexts = new Set();
Expand Down
7 changes: 4 additions & 3 deletions src/generators/dom/visitors/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export default function visitEachBlock(
block: Block,
state: State,
node: Node,
elementStack: Node[]
elementStack: Node[],
componentStack: Node[]
) {
const each_block = generator.getUniqueName(`each_block`);
const create_each_block = node._block.name;
Expand Down Expand Up @@ -125,12 +126,12 @@ export default function visitEachBlock(
}

node.children.forEach((child: Node) => {
visit(generator, node._block, node._state, child, elementStack);
visit(generator, node._block, node._state, child, elementStack, componentStack);
});

if (node.else) {
node.else.children.forEach((child: Node) => {
visit(generator, node.else._block, node.else._state, child, elementStack);
visit(generator, node.else._block, node.else._state, child, elementStack, componentStack);
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/generators/dom/visitors/Element/Attribute.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import attributeLookup from './lookup';
import deindent from '../../../../utils/deindent';
import { stringify } from '../../../../utils/stringify';
import getStaticAttributeValue from './getStaticAttributeValue';
import getStaticAttributeValue from '../../../shared/getStaticAttributeValue';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
Expand Down
2 changes: 1 addition & 1 deletion src/generators/dom/visitors/Element/Binding.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import deindent from '../../../../utils/deindent';
import flattenReference from '../../../../utils/flattenReference';
import getStaticAttributeValue from './getStaticAttributeValue';
import getStaticAttributeValue from '../../../shared/getStaticAttributeValue';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
Expand Down
Loading