Skip to content

Commit 05582e6

Browse files
author
Damir
committed
Allow to check if slot is defined via slots locals
1 parent 6f83229 commit 05582e6

File tree

4 files changed

+72
-11
lines changed

4 files changed

+72
-11
lines changed

src/index.js

+39-8
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const merge = require('deepmerge');
1212
const findPathFromTagName = require('./find-path');
1313

1414
// Used for slot without name
15-
const defaultSlotName = '___default__slot__name___';
15+
const defaultSlotName = '__default-slot';
1616

1717
const defaultSlotType = 'replace';
1818

@@ -53,6 +53,8 @@ function processNodes(tree, options, messages) {
5353

5454
options.expressions.locals = attributes;
5555

56+
options.expressions.locals.slots = getFilledSlotNames(options.slotTagName, node.content);
57+
5658
const plugins = [...options.plugins, expressions(options.expressions)];
5759

5860
const layoutTree = processNodes(applyPluginsToTree(html, plugins), options, messages);
@@ -235,26 +237,55 @@ function getSlots(tag, content = []) {
235237
// so slot name can be shorthands like <slot content> => <slot name="content">
236238
if (!node.attrs.name) {
237239
node.attrs.name = Object.keys({...node.attrs}).find(name => !Object.keys(slotTypes).includes(name) && name !== 'type' && name !== defaultSlotType);
238-
}
239240

240-
if (!node.attrs.name) {
241-
node.attrs.name = defaultSlotName;
241+
if (!node.attrs.name) {
242+
node.attrs.name = defaultSlotName;
243+
}
242244
}
243245

244246
const {name} = node.attrs;
245247

246-
if (Array.isArray(slots[name])) {
247-
slots[name].push(node);
248-
} else {
249-
slots[name] = [node];
248+
if (!Array.isArray(slots[name])) {
249+
slots[name] = [];
250250
}
251251

252+
slots[name].push(node);
253+
252254
return node;
253255
});
254256

255257
return slots;
256258
}
257259

260+
/**
261+
* Get all filled slots names so we can
262+
* pass as locals to check if slot is filled
263+
* @param {String} tag
264+
* @param {Object} content
265+
* @return {Object}
266+
*/
267+
function getFilledSlotNames(tag, content = []) {
268+
const slotNames = [];
269+
270+
match.call(content, {tag}, node => {
271+
let name = node.attrs && node.attrs.name;
272+
273+
if (!name) {
274+
name = Object.keys({...node.attrs}).find(name => !Object.keys(slotTypes).includes(name) && name !== 'type' && name !== defaultSlotType);
275+
276+
if (!name) {
277+
name = defaultSlotName;
278+
}
279+
}
280+
281+
slotNames[name] = true;
282+
283+
return node;
284+
});
285+
286+
return slotNames;
287+
}
288+
258289
/**
259290
* Apply plugins to tree
260291
* @param {Object} tree

test/templates/layouts/base.html

+7-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
<html><head><title>Base Layout</title></head><body><main><slot name="content"></slot></main><footer><slot name="footer">footer content</slot></footer></body></html>
1+
<html>
2+
<head><title>Base Layout</title></head>
3+
<body>
4+
<main><slot name="content"></slot></main>
5+
<footer><slot name="footer">footer content</slot></footer>
6+
</body>
7+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<html>
2+
<head><title>Slot Condition Layout</title></head>
3+
<body>
4+
<main><slot name="content"></slot></main>
5+
<if condition="slots.footer"><footer><slot name="footer">footer content</slot></footer></if>
6+
<else><p>There is not footer defined.</p></else>
7+
</body>
8+
</html>

test/test-slots.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ const posthtml = require('posthtml');
66
const clean = html => html.replace(/(\n|\t)/g, '').trim();
77

88
test('Must process with slots', async t => {
9-
const actual = `<component src="layouts/base.html"><slot name="content">Content</slot><slot name="footer">Footer</slot></component>`;
10-
const expected = `<html><head><title>Base Layout</title></head><body><main>Content</main><footer>Footer</footer></body></html>`;
9+
const actual = `<component src="layouts/base.html"><slot name="content"><h1>Content</h1></slot><slot name="footer">Footer</slot></component>`;
10+
const expected = `<html><head><title>Base Layout</title></head><body><main><h1>Content</h1></main><footer>Footer</footer></body></html>`;
1111

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

@@ -85,3 +85,19 @@ test('Must process with default slot without defining slot tag', async t => {
8585

8686
t.is(html, expected);
8787
});
88+
89+
test('Must process slots conditional rendering by using slot name', async t => {
90+
let actual = `<component src="layouts/slot-condition.html"><slot name="content"><h1>Content</h1></slot></component>`;
91+
let expected = `<html><head><title>Slot Condition Layout</title></head><body><main><h1>Content</h1></main><p>There is not footer defined.</p></body></html>`;
92+
93+
let html = await posthtml([plugin({root: './test/templates'})]).process(actual).then(result => clean(result.html));
94+
95+
t.is(html, expected);
96+
97+
actual = `<component src="layouts/slot-condition.html"><slot name="content"><h1>Content</h1></slot><slot name="footer">There is now footer defined</slot></component>`;
98+
expected = `<html><head><title>Slot Condition Layout</title></head><body><main><h1>Content</h1></main><footer>There is now footer defined</footer></body></html>`;
99+
100+
html = await posthtml([plugin({root: './test/templates', strict: false})]).process(actual).then(result => clean(result.html));
101+
102+
t.is(html, expected);
103+
});

0 commit comments

Comments
 (0)