Skip to content

Commit

Permalink
Slot is fill and fill is slot
Browse files Browse the repository at this point in the history
  • Loading branch information
thewebartisan7 committed Oct 19, 2022
1 parent 96ff607 commit fc4283c
Show file tree
Hide file tree
Showing 17 changed files with 73 additions and 74 deletions.
2 changes: 1 addition & 1 deletion examples/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ <h1 class="display-1 fw-bold mb-4">Build the web with PostHTML</h1>
</div>
</div>
</footer>
<div class="modal fade custom-modal" id="modalWithComponents" data-bs-backdrop="true" data-bs-keyboard="true" aria-labelledby="modalWithComponents" tabindex="-1" aria-hidden="true" aria-modal="true" role="dialog">
<div class="modal fade" id="modalWithComponents" data-bs-backdrop="true" data-bs-keyboard="true" aria-labelledby="modalWithComponents" tabindex="-1" aria-hidden="true" aria-modal="true" role="dialog">
<div class="modal-dialog modal-lg modal-fullscreen-lg-down modal-dialog-centered modal-dialog-custom">
<div class="modal-content modal-content-custom">
<div class="modal-header">
Expand Down
6 changes: 3 additions & 3 deletions examples/src/components/modal/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,18 @@
<yield></yield>
<if condition="$slots.header?.filled">
<x-modal.header>
<!-- Alternative way to <fill:header></fill:header> -->
<!-- Alternative way to <slot:header></slot:header> -->
{{{ $slots.header?.source }}}
</x-modal.header>
</if>
<if condition="$slots.body?.filled">
<x-modal.body>
<fill:body></fill:body>
<slot:body></slot:body>
</x-modal.body>
</if>
<if condition="$slots.footer?.filled">
<x-modal.footer close="{{ $slots.footer?.locals.close }}">
<fill:footer></fill:footer>
<slot:footer></slot:footer>
</x-modal.footer>
</if>
</div><!-- /.modal-content -->
Expand Down
17 changes: 8 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ module.exports = (options = {}) => tree => {
* @return {Object} PostHTML tree
*/
function processTree(options) {
// rename to pushes and slots
const slotContent = {};
const filledSlots = {};

let processCounter = 0;

Expand Down Expand Up @@ -127,15 +126,15 @@ function processTree(options) {
let nextNode = parser(readFileSync(componentPath, 'utf8'));

// Set filled slots
setFilledSlots(currentNode, slotContent, options);
setFilledSlots(currentNode, filledSlots, options);

// Reset previous locals with passed global and keep aware locals
options.expressions.locals = {...options.locals, ...options.aware};

const {attributes, locals} = processLocals(currentNode, nextNode, slotContent, options);
const {attributes, locals} = processLocals(currentNode, nextNode, filledSlots, options);

options.expressions.locals = attributes;
options.expressions.locals.$slots = slotContent;
options.expressions.locals.$slots = filledSlots;
// const plugins = [...options.plugins, expressions(options.expressions)];
nextNode = expressions(options.expressions)(nextNode);

Expand All @@ -145,11 +144,11 @@ function processTree(options) {
return currentNode.content || nextNode.content || '';
});

// Process <slot> tags
processSlotContent(nextNode, slotContent, options);

// Process <fill> tags
processFillContent(nextNode, slotContent, options);
processFillContent(nextNode, filledSlots, options);

// Process <slot> tags
processSlotContent(nextNode, filledSlots, options);

// Remove component tag and replace content with <yield>
currentNode.tag = false;
Expand Down
72 changes: 36 additions & 36 deletions src/slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ const {render} = require('posthtml-render');
* Set filled slots
*
* @param {Object} currentNode PostHTML tree
* @param {Object} slots
* @param {Object} filledSlots
* @param {Object} options Plugin options
* @return {void}
*/
function setFilledSlots(currentNode, slots, {slot}) {
match.call(currentNode, {tag: slot}, fillNode => {
function setFilledSlots(currentNode, filledSlots, {fill}) {
match.call(currentNode, {tag: fill}, fillNode => {
if (!fillNode.attrs) {
fillNode.attrs = {};
}

const name = fillNode.tag.split(':')[1];

/** @var {Object} locals */
/** @var {Object} locals - NOT YET TESTED */
const locals = Object.fromEntries(Object.entries(fillNode.attrs).filter(([attributeName]) => ![name, 'type'].includes(attributeName)));

if (locals) {
Expand All @@ -30,7 +30,7 @@ function setFilledSlots(currentNode, slots, {slot}) {
});
}

slots[name] = {
filledSlots[name] = {
filled: true,
rendered: false,
tag: fillNode.tag,
Expand All @@ -45,68 +45,68 @@ function setFilledSlots(currentNode, slots, {slot}) {
}

/**
* Process <slot> tag
* Process <fill> tag
*
* @param {Object} tree PostHTML tree
* @param {Object} content Content pushed by stack name
* @param {Object} filledSlots Filled slots content
* @param {Object} options Plugin options
* @return {void}
*/
function processSlotContent(tree, content, {slot}) {
match.call(tree, {tag: slot}, slotNode => {
const name = slotNode.tag.split(':')[1];
function processFillContent(tree, filledSlots, {fill}) {
match.call(tree, {tag: fill}, fillNode => {
const name = fillNode.tag.split(':')[1];

if (!content[name]) {
content[name] = {};
if (!filledSlots[name]) {
filledSlots[name] = {};
}

content[name].tag = slotNode.tag;
content[name].attrs = slotNode.attrs;
content[name].content = slotNode.content;
content[name].source = render(slotNode.content);
content[name].rendered = false;
filledSlots[name].tag = fillNode.tag;
filledSlots[name].attrs = fillNode.attrs;
filledSlots[name].content = fillNode.content;
filledSlots[name].source = render(fillNode.content);
filledSlots[name].rendered = false;

slotNode.tag = false;
slotNode.content = null;
fillNode.tag = false;
fillNode.content = null;

return slotNode;
return fillNode;
});
}

/**
* Process <fill> tag
* Process <slot> tag
*
* @param {Object} tree PostHTML tree
* @param {Object} content Content pushed by stack name
* @param {Object} filledSlots Filled slots content
* @param {Object} options Plugin options
* @return {void}
*/
function processFillContent(tree, content, {fill}) {
match.call(tree, {tag: fill}, fillNode => {
const name = fillNode.tag.split(':')[1];
function processSlotContent(tree, filledSlots, {slot}) {
match.call(tree, {tag: slot}, slotNode => {
const name = slotNode.tag.split(':')[1];

fillNode.tag = false;
slotNode.tag = false;

if (content[name]?.rendered) {
fillNode.content = null;
} else if (fillNode.content && content[name]?.attrs && (typeof content[name]?.attrs.append !== 'undefined' || typeof content[name]?.attrs.prepend !== 'undefined')) {
fillNode.content = typeof content[name]?.attrs.append === 'undefined' ? content[name]?.content.concat(fillNode.content) : fillNode.content.concat(content[name]?.content);
if (filledSlots[name]?.rendered) {
slotNode.content = null;
} else if (slotNode.content && filledSlots[name]?.attrs && (typeof filledSlots[name]?.attrs.append !== 'undefined' || typeof filledSlots[name]?.attrs.prepend !== 'undefined')) {
slotNode.content = typeof filledSlots[name]?.attrs.append === 'undefined' ? filledSlots[name]?.content.concat(slotNode.content) : slotNode.content.concat(filledSlots[name]?.content);
} else {
fillNode.content = content[name]?.content;
slotNode.content = filledSlots[name]?.content;
}

// Set rendered to true so a slot can be output only once,
// when not present "aware" attribute
if (content[name] && (!content[name]?.attrs || typeof content[name].attrs.aware === 'undefined')) {
content[name].rendered = true;
if (filledSlots[name] && (!filledSlots[name]?.attrs || typeof filledSlots[name].attrs.aware === 'undefined')) {
filledSlots[name].rendered = true;
}

return fillNode;
return slotNode;
});
}

module.exports = {
setFilledSlots,
processSlotContent,
processFillContent
processFillContent,
processSlotContent
};
2 changes: 1 addition & 1 deletion test/templates/components/component-append-prepend.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div><fill:title>Title</fill:title></div><div><fill:body>Body</fill:body></div>
<div><slot:title>Title</slot:title></div><div><slot:body>Body</slot:body></div>
2 changes: 1 addition & 1 deletion test/templates/components/component-locals.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
body: 'Default body'
}
</script>
<div><h1>{{title}}</h1></div><div><fill:body>{{body}}</fill:body></div>
<div><h1>{{title}}</h1></div><div><slot:body>{{body}}</slot:body></div>
2 changes: 1 addition & 1 deletion test/templates/components/component-multiple-slot.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div><fill:title></fill:title></div><div><fill:body></fill:body></div><div><fill:title></fill:title></div>
<div><slot:title></slot:title></div><div><slot:body></slot:body></div><div><slot:title></slot:title></div>
2 changes: 1 addition & 1 deletion test/templates/components/component.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div><fill:title></fill:title></div><div><fill:body></fill:body></div>
<div><slot:title></slot:title></div><div><slot:body></slot:body></div>
4 changes: 2 additions & 2 deletions test/templates/components/nested-one-slot.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="nested-one">
<div class="nested-one-before"><fill:before></fill:before></div>
<div class="nested-one-before"><slot:before></slot:before></div>
<div class="nested-one-yield"><yield></yield></div>
<div class="nested-one-after"><fill:after></fill:after></div>
<div class="nested-one-after"><slot:after></slot:after></div>
</div>
4 changes: 2 additions & 2 deletions test/templates/components/nested-two-slot.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="nested-two">
<div class="nested-two-before"><fill:before2></fill:before2></div>
<div class="nested-two-before"><slot:before2></slot:before2></div>
<div class="nested-two-yield"><yield></yield></div>
<div class="nested-two-after"><fill:after2></fill:after2></div>
<div class="nested-two-after"><slot:after2></slot:after2></div>
</div>
2 changes: 1 addition & 1 deletion test/templates/components/script-locals.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
</script>
{{ slotsAccessibleViaScript }}
<div>{{ $slots }}</div>
<div><h1>{{title}}</h1></div><div><fill:filled></fill:filled><fill:notfilled></fill:notfilled></div>
<div><h1>{{title}}</h1></div><div><slot:filled></slot:filled><slot:notfilled></slot:notfilled></div>
2 changes: 1 addition & 1 deletion test/templates/layouts/base-locals.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
title: 'Default title'
}
</script>
<html><head><title>{{ title }}</title></head><body><main><yield></yield></main><footer><fill:footer></fill:footer></footer></body></html>
<html><head><title>{{ title }}</title></head><body><main><yield></yield></main><footer><slot:footer></slot:footer></footer></body></html>
4 changes: 2 additions & 2 deletions test/templates/layouts/base.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<html>
<head><title>Base Layout</title></head>
<body>
<header><fill:header></fill:header></header>
<header><slot:header></slot:header></header>
<main><yield></yield></main>
<footer><fill:footer>footer content</fill:footer></footer>
<footer><slot:footer>footer content</slot:footer></footer>
</body>
</html>
2 changes: 1 addition & 1 deletion test/templates/layouts/slot-condition.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<head><title>Slot Condition Layout</title></head>
<body>
<main><yield></yield></main>
<if condition="$slots.footer?.filled"><footer><fill:footer></fill:footer></footer></if>
<if condition="$slots.footer?.filled"><footer><slot:footer></slot:footer></footer></if>
<else><p>There is not footer defined.</p></else>
</body>
</html>
8 changes: 4 additions & 4 deletions test/test-locals.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const posthtml = require('posthtml');
const clean = html => html.replace(/(\n|\t)/g, '').trim();

test('Must process layout with locals', async t => {
const actual = `<component src="layouts/base-locals.html" locals='{ "title": "My Page" }'><div>Content</div><slot:footer>Footer</slot:footer></component>`;
const actual = `<component src="layouts/base-locals.html" locals='{ "title": "My Page" }'><div>Content</div><fill:footer>Footer</fill:footer></component>`;
const expected = `<html><head><title>My Page</title></head><body><main><div>Content</div></main><footer>Footer</footer></body></html>`;

const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand All @@ -15,7 +15,7 @@ test('Must process layout with locals', async t => {
});

test('Must process attributes as locals', async t => {
const actual = `<component src="components/component-locals.html" title="My Component Title" body="Content"><slot:body prepend><span>Body</span></slot:body></component>`;
const actual = `<component src="components/component-locals.html" title="My Component Title" body="Content"><fill:body prepend><span>Body</span></fill:body></component>`;
const expected = `<div><h1>My Component Title</h1></div><div><span>Body</span>Content</div>`;

const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand Down Expand Up @@ -56,8 +56,8 @@ test('Must process parent and child locals via component', async t => {
});

test('Must has access to $slots in script locals', async t => {
const actual = `<x-script-locals><slot:filled>filled slot content...</slot:filled></x-script-locals>`;
const expected = `{"filled":{"filled":true,"rendered":false,"tag":"slot:filled","attrs":{},"content":["filled slot content..."],"source":"filled slot content...","locals":{}}}<div>{"filled":{"filled":true,"rendered":false,"tag":"slot:filled","attrs":{},"content":["filled slot content..."],"source":"filled slot content...","locals":{}}}</div><div><h1>Default title</h1></div><div>filled slot content...</div>`;
const actual = `<x-script-locals><fill:filled>filled slot content...</fill:filled></x-script-locals>`;
const expected = `{"filled":{"filled":true,"rendered":false,"tag":"fill:filled","attrs":{},"content":["filled slot content..."],"source":"filled slot content...","locals":{}}}<div>{"filled":{"filled":true,"rendered":false,"tag":"fill:filled","attrs":{},"content":["filled slot content..."],"source":"filled slot content...","locals":{}}}</div><div><h1>Default title</h1></div><div>filled slot content...</div>`;

const html = await posthtml([plugin({root: './test/templates/components'})]).process(actual).then(result => clean(result.html));

Expand Down
2 changes: 1 addition & 1 deletion test/test-nested.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test('Must process all nested component to html', async t => {
});

test('Must process all nested component with slots to html', async t => {
const actual = `<x-nested-one-slot><x-nested-two-slot>yield content<slot:before2>nested-two before</slot:before2><slot:after2>nested-two after</slot:after2></x-nested-two-slot><slot:before>nested-one before</slot:before><slot:after>nested-one after</slot:after></x-nested-one-slot>`;
const actual = `<x-nested-one-slot><x-nested-two-slot>yield content<fill:before2>nested-two before</fill:before2><fill:after2>nested-two after</fill:after2></x-nested-two-slot><fill:before>nested-one before</fill:before><fill:after>nested-one after</fill:after></x-nested-one-slot>`;
const expected = `<div class="nested-one"><div class="nested-one-before">nested-one before</div><div class="nested-one-yield"><div class="nested-two"><div class="nested-two-before">nested-two before</div><div class="nested-two-yield">yield content</div><div class="nested-two-after">nested-two after</div></div></div><div class="nested-one-after">nested-one after</div></div>`;

const html = await posthtml([plugin({root: './test/templates/components'})]).process(actual).then(result => clean(result.html));
Expand Down
14 changes: 7 additions & 7 deletions test/test-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const posthtml = require('posthtml');
const clean = html => html.replace(/(\n|\t)/g, '').trim();

test('Must process with slots', async t => {
const actual = `<component src="layouts/base.html"><div>Main content</div><slot:header><h1>My header</h1></slot:header><slot:footer>My footer</slot:footer></component>`;
const actual = `<component src="layouts/base.html"><div>Main content</div><fill:header><h1>My header</h1></fill:header><fill:footer>My footer</fill:footer></component>`;
const expected = `<html><head><title>Base Layout</title></head><body><header><h1>My header</h1></header><main><div>Main content</div></main><footer>My footer</footer></body></html>`;

const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand All @@ -15,7 +15,7 @@ test('Must process with slots', async t => {
});

test('Must process the same component multiple times', async t => {
const actual = `<component src="components/component.html"><slot:title>Title</slot:title><slot:body>Body</slot:body></component><component src="components/component.html"><slot:title>Title 2</slot:title><slot:body>Body 2</slot:body></component>`;
const actual = `<component src="components/component.html"><fill:title>Title</fill:title><fill:body>Body</fill:body></component><component src="components/component.html"><fill:title>Title 2</fill:title><fill:body>Body 2</fill:body></component>`;
const expected = `<div>Title</div><div>Body</div><div>Title 2</div><div>Body 2</div>`;

const html = await posthtml([plugin({root: './test/templates/', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand All @@ -25,15 +25,15 @@ test('Must process the same component multiple times', async t => {

test('Must process multiple time a slot with aware attribute', async t => {
// With aware
let actual = `<component src="components/component-multiple-slot.html"><slot:title aware>Title</slot:title><slot:body>Body</slot:body></component>`;
let actual = `<component src="components/component-multiple-slot.html"><fill:title aware>Title</fill:title><fill:body>Body</fill:body></component>`;
let expected = `<div>Title</div><div>Body</div><div>Title</div>`;

let html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));

t.is(html, expected);

// Without aware
actual = `<component src="components/component-multiple-slot.html"><slot:title>Title</slot:title><slot:body>Body</slot:body></component>`;
actual = `<component src="components/component-multiple-slot.html"><fill:title>Title</fill:title><fill:body>Body</fill:body></component>`;
expected = `<div>Title</div><div>Body</div><div></div>`;

html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand All @@ -42,7 +42,7 @@ test('Must process multiple time a slot with aware attribute', async t => {
});

test('Must process append and prepend content to slot', async t => {
const actual = `<component src="components/component-append-prepend.html"><slot:title append><span>Append</span></slot:title><slot:body prepend><span>Prepend</span></slot:body></component>`;
const actual = `<component src="components/component-append-prepend.html"><fill:title append><span>Append</span></fill:title><fill:body prepend><span>Prepend</span></fill:body></component>`;
const expected = `<div>Title<span>Append</span></div><div><span>Prepend</span>Body</div>`;

const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand All @@ -58,7 +58,7 @@ test('Must process slots conditional rendering by using slot name', async t => {

t.is(html, expected);

actual = `<component src="layouts/slot-condition.html"><h1>Content</h1><slot:footer>There is now footer defined</slot:footer></component>`;
actual = `<component src="layouts/slot-condition.html"><h1>Content</h1><fill:footer>There is now footer defined</fill:footer></component>`;
expected = `<html><head><title>Slot Condition Layout</title></head><body><main><h1>Content</h1></main><footer>There is now footer defined</footer></body></html>`;

html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand All @@ -67,7 +67,7 @@ test('Must process slots conditional rendering by using slot name', async t => {
});

test('Must render slots using $slots locals', async t => {
const actual = `<component src="layouts/base-render-slots-locals.html"><div>Main content</div><slot:header><h1>My header</h1></slot:header><slot:footer>My footer</slot:footer></component>`;
const actual = `<component src="layouts/base-render-slots-locals.html"><div>Main content</div><fill:header><h1>My header</h1></fill:header><fill:footer>My footer</fill:footer></component>`;
const expected = `<html><head><title>Base Render Slots Locals Layout</title></head><body><header><h1>My header</h1></header><main><div>Main content</div></main><footer>My footer</footer></body></html>`;

const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html));
Expand Down

0 comments on commit fc4283c

Please sign in to comment.