diff --git a/src/index.js b/src/index.js index e5170be..e2cf14f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ 'use strict'; -const {readFileSync} = require('fs'); +const {readFileSync, existsSync} = require('fs'); const path = require('path'); const {parser} = require('posthtml-parser'); const {match} = require('posthtml/lib/api'); @@ -64,7 +64,7 @@ module.exports = (options = {}) => tree => { } } - options.roots = Array.isArray(options.folders) ? options.folders : [options.folders]; + options.folders = Array.isArray(options.folders) ? options.folders : [options.folders]; options.namespaces = Array.isArray(options.namespaces) ? options.namespaces : [options.namespaces]; options.namespaces.forEach((namespace, index) => { options.namespaces[index].root = path.resolve(namespace.root); @@ -121,12 +121,17 @@ function processTree(options) { return currentNode; } - const componentPath = path.isAbsolute(componentFile) && componentFile !== currentNode.attrs[options.attribute] ? + const componentPath = path.isAbsolute(componentFile) && !currentNode.attrs[options.attribute] ? componentFile : path.join(options.root, componentFile); - if (!componentPath) { - return currentNode; + // Check if file exist only when not using x-tag + if (currentNode.attrs[options.attribute] && !existsSync(componentPath)) { + if (options.strict) { + throw new Error(`[components] The component was not found in ${componentPath}.`); + } else { + return currentNode; + } } // console.log(`${++processCounter}) Processing component ${componentPath}`); @@ -152,8 +157,8 @@ function processTree(options) { // Process tag const content = match.call(nextNode, {tag: options.yield}, nextNode => { - // Fill with current node content or default content or empty - return currentNode.content || nextNode.content || ''; + // Fill with current node content or default + return currentNode.content || nextNode.content; }); // Process tags diff --git a/test/templates/components/component-default-yield.html b/test/templates/components/component-default-yield.html new file mode 100644 index 0000000..94b0e19 --- /dev/null +++ b/test/templates/components/component-default-yield.html @@ -0,0 +1 @@ +
Default yield
diff --git a/test/templates/components/component-mapped-attributes2.html b/test/templates/components/component-mapped-attributes2.html new file mode 100644 index 0000000..d9d3b54 --- /dev/null +++ b/test/templates/components/component-mapped-attributes2.html @@ -0,0 +1,7 @@ + +
{{ title }} {{ body }}
diff --git a/test/templates/components/component-mapped-attributes3.html b/test/templates/components/component-mapped-attributes3.html new file mode 100644 index 0000000..3d8bf78 --- /dev/null +++ b/test/templates/components/component-mapped-attributes3.html @@ -0,0 +1,7 @@ + +
{{ title }} {{ body }}
diff --git a/test/templates/components/component-without-elements.html b/test/templates/components/component-without-elements.html new file mode 100644 index 0000000..a18e7b6 --- /dev/null +++ b/test/templates/components/component-without-elements.html @@ -0,0 +1 @@ +I am a component without any elements. Yeah, something uncommon but just for testing. {{ title }} diff --git a/test/test-attributes.js b/test/test-attributes.js new file mode 100644 index 0000000..ea7ba71 --- /dev/null +++ b/test/test-attributes.js @@ -0,0 +1,42 @@ +'use strict'; + +const test = require('ava'); +const plugin = require('../src'); +const posthtml = require('posthtml'); +const clean = html => html.replace(/(\n|\t)/g, '').trim(); + +test('Must merge and map attributes not locals to first node', async t => { + const actual = ``; + const expected = `
My Title Default body
`; + + const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html)); + + t.is(html, expected); +}); + +test('Must merge and map attributes not locals to node with attribute "attributes" without style', async t => { + const actual = ``; + const expected = `
My Title Default body
`; + + const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html)); + + t.is(html, expected); +}); + +test('Must merge and map attributes not locals to node with attribute "attributes" without class', async t => { + const actual = ``; + const expected = `
My Title Default body
`; + + const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html)); + + t.is(html, expected); +}); + +test('Must not map attributes for component without any elements', async t => { + const actual = `
`; + const expected = `
I am a component without any elements. Yeah, something uncommon but just for testing. My Title
`; + + const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html)); + + t.is(html, expected); +}); diff --git a/test/test-errors.js b/test/test-errors.js index acff2af..8af3a6a 100644 --- a/test/test-errors.js +++ b/test/test-errors.js @@ -24,12 +24,12 @@ test('Must return node as-is when namespace is empty with strict mode disabled', const actual = `
Submit
`; const expected = `
Submit
`; - const html = await posthtml([plugin({root: './test/templates', strict: false, namespaces: [{name: 'empty-namespace', root: './test/templates/empty-namespace'}]})]).process(actual).then(result => clean(result.html)); + const html = await posthtml([plugin({root: './test/templates', strict: false, namespaces: {name: 'empty-namespace', root: './test/templates/empty-namespace'}})]).process(actual).then(result => clean(result.html)); t.is(html, expected); }); -test('Must return node as-is when component is not found with strict mode disabled', async t => { +test('Must return node as-is when x-tag is not found with strict mode disabled', async t => { const actual = `
Submit
`; const expected = `
Submit
`; @@ -37,3 +37,24 @@ test('Must return node as-is when component is not found with strict mode disabl t.is(html, expected); }); + +test('Must return node as-is when component is not found with strict mode disabled', async t => { + const actual = `
Submit
`; + const expected = `
Submit
`; + + const html = await posthtml([plugin({tag: 'component', strict: false})]).process(actual).then(result => clean(result.html)); + + t.is(html, expected); +}); + +test('Must fail when component is not found with strict mode enabled', async t => { + const actual = `
Submit
`; + + await t.throwsAsync(async () => posthtml([plugin({root: './test/templates/empty-root', tag: 'component', strict: true})]).process(actual).then(result => clean(result.html))); +}); + +test('Must fail when component is not found in defined namespace with strict mode enabled', async t => { + const actual = `
Submit
`; + + await t.throwsAsync(async () => posthtml([plugin({root: './test/templates', strict: true, namespaces: [{name: 'empty-namespace', root: './test/templates/empty-namespace'}]})]).process(actual).then(result => clean(result.html))); +}); diff --git a/test/test-locals.js b/test/test-locals.js index 5d33e56..c923e41 100644 --- a/test/test-locals.js +++ b/test/test-locals.js @@ -23,15 +23,6 @@ test('Must process attributes as locals', async t => { t.is(html, expected); }); -test('Must process default attributes and map attributes not locals to first node', async t => { - const actual = ``; - const expected = `
My Title Default body
`; - - const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html)); - - t.is(html, expected); -}); - test('Must process component with locals as JSON and string', async t => { const actual = ` { t.is(html, expected); }); -// test('Must pass locals from parent to child via aware', async t => { -// const actual = ``; -// const expected = `PARENT:
aBoolean value: true type: boolean aString value: I am custom aString for PARENT via component (1) type: string aString2 value: I am not string 2 type: string anArray value: ["one","two","three"] type: object anObject value: {"one":"One","two":"Two","three":"Three"} type: object anArray2 value: ["one2","two2","three2"] type: object anObject2 value: {"one":"One2","two":"Two2","three":"Three2"} type: object
CHILD:
aBoolean value: true type: boolean aString value: I am custom aString for PARENT via component (1) type: string aString2 value: I am not string 2 type: string anArray value: ["one","two","three"] type: object anObject value: {"one":"One","two":"Two","three":"Three"} type: object anArray2 value: ["one2","two2","three2"] type: object anObject2 value: {"one":"One2","two":"Two2","three":"Three2"} type: object
`; -// -// const html = await posthtml([plugin({root: './test/templates/components'})]).process(actual).then(result => clean(result.html)); -// -// t.is(html, expected); -// }); +test('Must pass locals from parent to child via aware', async t => { + const actual = ``; + const expected = `PARENT:
aBoolean value: true type: boolean aString value: I am custom aString for PARENT via component (1) type: string aString2 value: I am not string 2 type: string anArray value: ["one","two","three"] type: object anObject value: {"one":"One","two":"Two","three":"Three"} type: object anArray2 value: ["one2","two2","three2"] type: object anObject2 value: {"one":"One2","two":"Two2","three":"Three2"} type: object
CHILD:
aBoolean value: true type: boolean aString value: I am custom aString for PARENT via component (1) type: string aString2 value: I am not string 2 type: string anArray value: ["one","two","three"] type: object anObject value: {"one":"One","two":"Two","three":"Three"} type: object anArray2 value: ["one2","two2","three2"] type: object anObject2 value: {"one":"One2","two":"Two2","three":"Three2"} type: object
`; + + const html = await posthtml([plugin({root: './test/templates/components'})]).process(actual).then(result => clean(result.html)); + + t.is(html, expected); +}); test('Must process default, merged and override props', async t => { const actual = ``; diff --git a/test/test-slots.js b/test/test-slots.js index da41d6c..4f91a08 100644 --- a/test/test-slots.js +++ b/test/test-slots.js @@ -14,6 +14,15 @@ test('Must process with slots', async t => { t.is(html, expected); }); +test('Must output default yield content when not provided', async t => { + const actual = ``; + const expected = `
Default yield
`; + + const html = await posthtml([plugin({root: './test/templates', tag: 'component'})]).process(actual).then(result => clean(result.html)); + + t.is(html, expected); +}); + test('Must process the same component multiple times', async t => { const actual = `TitleBodyTitle 2Body 2`; const expected = `
Title
Body
Title 2
Body 2
`; diff --git a/test/test-x-tag.js b/test/test-x-tag.js index 9b4881e..2d496d1 100644 --- a/test/test-x-tag.js +++ b/test/test-x-tag.js @@ -9,7 +9,7 @@ test('Must process component with x-tag', async t => { const actual = ``; const expected = `
Modal
`; - const html = await posthtml([plugin({root: './test/templates/components'})]).process(actual).then(result => clean(result.html)); + const html = await posthtml([plugin({root: './test/templates', folders: 'components'})]).process(actual).then(result => clean(result.html)); t.is(html, expected); });