You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

posthtml-component

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

posthtml-component - npm Package Compare versions

Comparing version

to
1.0.0

src/valid-attributes.js

2

package.json
{
"name": "posthtml-component",
"version": "1.0.0-beta.16",
"version": "1.0.0",
"description": "PostHTML Components Blade-like with slots, attributes as props and custom tag",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -20,6 +20,6 @@ [![NPM][npm]][npm-url]

This PostHTML plugin provides an HTML-friendly syntax for write components in your templates.
If you are familiar with Blade, you will find similar syntax as this plugin is inspired by it.
If you are familiar with Blade, React, Vue or similar, you will find familiar syntax as this plugin is inspired by them.
See below a basic example, as code is worth a thousand words.
> This plugin is still in early stage of development and the current API may change.
**See also the first [PostHTML Bootstrap UI](https://github.com/thewebartisan7/posthtml-bootstrap-ui) using this plugin and check also the [starter template here](https://github.com/thewebartisan7/posthtml-bootstrap-ui-starter).**

@@ -73,7 +73,7 @@ ## Basic example

You may notice that our `src/button.html` component has a `type` and `class` attribute, and when we use the component in `src/index.html` we add type and class attribute.
The result is that `type` is override, and `class` is merged.
You may notice that the `src/button.html` component has a `type` and `class` attribute, and when we use the component in `src/index.html` we pass `type` and `class` attribute.
The result is that `type` is override, and `class` is merged.
By default `class` and `style` attributes are merged, while all others attribute are override.
You can also override class and style attribute by prepending `override:` to the class attribute. Example:
By default `class` and `style` attributes are merged, while all others attribute are override.
You can also override `class` and `style` attributes by prepending `override:` to the class attribute. Example:

@@ -87,39 +87,46 @@ ```html

All attributes you pass to the component will be added to the first node of your component or to the node with an attribute names `attributes`,
and only if they are not defined as `props` via `<script props>`. More details on this in [Attributes](#attributes) section.
All attributes you pass to the component will be added to the first node of your component or to the node with an attribute names `attributes`,
and only if they are not defined as `props` via `<script props>` or if they are not in the following file
[valid-attributes.js](https://github.com/thewebartisan7/posthtml-components/blob/main/src/valid-attributes.js).
You can also manage valid attributes via options.
More details on this in [Attributes](#attributes) section.
The `<yield>` tag is where your content will be injected.
In next section you can find all available options and then examples for each features.
In next section you can find all available options and then examples for each feature.
See also the `docs-src` folder where you can find more examples. You can run `npm run build` to compile them.
See also the `docs-src` folder where you can find more examples.
You can run `npm run build` to compile them.
## Options
| Option | Default | Description |
|:------------------------:|:--------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------|
| **root** | `'./'` | String value as root path for components lookup. |
| **folders** | `['']` | Array of additional multi folders path from `options.root` or any defined namespaces root, fallback or custom. |
| **tagPrefix** | `x-` | String for tag prefix. The plugin will use RegExp with this string. |
| **tag** | `false` | String or boolean value for component tag. Use this with `options.attribute`. Boolean only false. |
| **attribute** | `src` | String value for component attribute for set path. |
| **namespaces** | `[]` | Array of namespace's root path, fallback path and custom path for override. |
| **namespaceSeparator** | `::` | String value for namespace separator to be used with tag name. Example `<x-namespace::button>` |
| **fileExtension** | `html` | String value for file extension of the components used for retrieve x-tag file. |
| **yield** | `yield` | String value for `<yield>` tag name. Where main content of component is injected. |
| **slot** | `slot` | String value for `<slot>` tag name. Used with RegExp by appending `:` (example `<slot:slot-name>`). |
| **fill** | `fill` | String value for `<fill>` tag name. Used with RegExp by appending `:` (example `<fill:slot-name>`). |
| **slotSeparator** | `:` | String value used for separate `<slot>` and `<fill>` tag from their names. |
| **push** | `push` | String value for `<push>` tag name. |
| **stack** | `stack` | String value for `<stack>` tag name. |
| **propsScriptAttribute** | `props` | String value used as attribute in `<script props>` parsed by the plugin to retrieve props of the component. |
| **propsContext** | `props` | String value used as object name inside the script to process props before passed to the component. |
| **propsAttribute** | `props` | String value for props attribute to define props as JSON. |
| **propsSlot** | `props` | String value used to retrieve the props passed to slot via `$slots.slotName.props`. |
| **expressions** | `{}` | Object to configure `posthtml-expressions`. You can pre-set locals or customize the delimiters for example. |
| **plugins** | `[]` | PostHTML plugins to apply for every parsed components. |
| **matcher** | `[{tag: options.tagPrefix}]` | Array of object used to match the tags. |
| **attrsParserRules** | `{}` | Additional rules for attributes parser plugin. |
| **strict** | `true` | Boolean value for enable or disable throw an exception. |
| **mergeCustomizer** | `function` | Function callback passed to lodash `mergeWith` for merge `options.expressions.locals` and props passed via attribute `props`. |
| **utilities** | `{merge: _.mergeWith, template: _.template}` | Object of utilities methods to be passed to `<script props>`. By default lodash `mergeWith` and `template`. |
| Option | Default | Description |
|:------------------------:|:---------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------|
| **root** | `'./'` | String value as root path for components lookup. |
| **folders** | `['']` | Array of additional multi folders path from `options.root` or any defined namespaces root, fallback or custom. |
| **tagPrefix** | `x-` | String for tag prefix. The plugin will use RegExp with this string. |
| **tag** | `false` | String or boolean value for component tag. Use this with `options.attribute`. Boolean only false. |
| **attribute** | `src` | String value for component attribute for set path. |
| **namespaces** | `[]` | Array of namespace's root path, fallback path and custom path for override. |
| **namespaceSeparator** | `::` | String value for namespace separator to be used with tag name. Example `<x-namespace::button>` |
| **fileExtension** | `html` | String value for file extension of the components used for retrieve x-tag file. |
| **yield** | `yield` | String value for `<yield>` tag name. Where main content of component is injected. |
| **slot** | `slot` | String value for `<slot>` tag name. Used with RegExp by appending `:` (example `<slot:slot-name>`). |
| **fill** | `fill` | String value for `<fill>` tag name. Used with RegExp by appending `:` (example `<fill:slot-name>`). |
| **slotSeparator** | `:` | String value used for separate `<slot>` and `<fill>` tag from their names. |
| **push** | `push` | String value for `<push>` tag name. |
| **stack** | `stack` | String value for `<stack>` tag name. |
| **propsScriptAttribute** | `props` | String value used as attribute in `<script props>` parsed by the plugin to retrieve props of the component. |
| **propsContext** | `props` | String value used as object name inside the script to process process before passed to the component. |
| **propsAttribute** | `props` | String value for props attribute to define props as JSON. |
| **propsSlot** | `props` | String value used to retrieve the props passed to slot via `$slots.slotName.props`. |
| **expressions** | `{}` | Object to configure `posthtml-expressions`. You can pre-set locals or customize the delimiters for example. |
| **plugins** | `[]` | PostHTML plugins to apply for every parsed components. |
| **matcher** | `[{tag: options.tagPrefix}]` | Array of object used to match the tags. |
| **attrsParserRules** | `{}` | Additional rules for attributes parser plugin. |
| **strict** | `true` | Boolean value for enable or disable throw an exception. |
| **mergeCustomizer** | `function` | Function callback passed to lodash `mergeWith` for merge `options.expressions.locals` and props passed via attribute `props`. |
| **utilities** | `{merge: _.mergeWith, template: _.template}` | Object of utilities methods to be passed to `<script props>`. By default lodash `mergeWith` and `template`. |
| **elementAttributes** | `{}` | An object with tag name and a function modifier of valid-attributes.js. |
| **safelistAttributes** | `['data-*']` | An array of attributes name to be added to default valid attributes. |
| **blacklistAttributes** | `[]` | An array of attributes name to be removed from default valid attributes. |

@@ -219,3 +226,3 @@ ## Features

For example let's suppose your main root is `./src` and then you have several folders where you have your components, for example `./src/components` and `./src/layouts`.
You can setup the plugin like below:
You can set up the plugin like below:

@@ -236,3 +243,3 @@ ```js

With namespaces you can define a top level root path to your components like shown in below example.
With namespaces, you can define a top level root path to your components like shown in below example.
It can be useful for handle custom theme, where you define a specific top level root, with fallback root when component it's not found,

@@ -354,3 +361,3 @@ and a custom root for override, something like a child theme.

By default the content is replaced, but you can also prepend or append the content, or keep the default content by not filling the slot.
By default, the content is replaced, but you can also prepend or append the content, or keep the default content by not filling the slot.

@@ -495,3 +502,3 @@ Add some default content in the component:

By default the content is pushed in the stack in the given order.
By default, the content is pushed in the stack in the given order.
If you would like to prepend content onto the beginning of a stack, you should use the `prepend` attribute:

@@ -563,7 +570,4 @@

If you don't add the props in `<script props>` inside your component, all props will be added as attributes to the first node of your component or to the node with attribute `attributes`.
More details on this in the next section.
In the `<script props>` you have access to passed props via object `props`, and you can add any logic you need inside it.
So in the `<script props>` you have access to passed props via object `props`, and you can add any logic you need inside it.
Create the component:

@@ -612,3 +616,3 @@

You can also notice how the `class` attribute is merged with `class` attribute of the first node. Let's see in next section more about this.
You can also notice how the `class` attribute is merged with `class` attribute of the first node. In the next section you will know more about this.

@@ -687,6 +691,11 @@ You can change how attributes are merged with global props defined via options by passing a callback function used by lodash method [mergeWith](https://lodash.com/docs/4.17.15#mergeWith).

Your can pass any attributes to your components and this will be added to the first node of your component, or to the node with an attribute named `attributes`.
You can pass any attributes to your components and this will be added to the first node of your component,
or to the node with an attribute named `attributes`. If you are familiar with VueJS this is the same as so called
[fallthrough attribute](https://vuejs.org/guide/components/attrs.html), or with Laravel Blade is
[component-attributes](https://laravel.com/docs/10.x/blade#component-attributes).
By default `class` and `style` are merged with existing `class` and `style` attribute.
All others attributes are override by default.
Only attribute not defined as `props` will be used.
Only attributes defined in [valid-attributes.js](https://github.com/thewebartisan7/posthtml-components/blob/main/src/valid-attributes.js)
or not defined as `props` in the `<script props>`.

@@ -723,4 +732,2 @@ As already seen in basic example:

If you are familiar with Laravel Blade, this is also how Blade handle this.
As said early, `class` and `style` are merged by default, if you want to override them, just prepend `override:` to the attribute name:

@@ -740,3 +747,3 @@

If you want to use another node for add such attributes, then you can add the attribute `attributes` like shown below.
If you want to use another node and not the first one, then you can add the attribute `attributes` like shown below.

@@ -772,5 +779,52 @@ ```html

### Advanced attributes configurations
If default configurations for valid attributes are not right for you, then you can configure them as explained below.
```js
// index.js
const { readFileSync, writeFileSync } = require('fs')
const posthtml = require('posthtml')
const components = require('posthtml-components')
const options = {
root: './src',
// Add attributes to specific tag or override defaults
elementAttributes: {
DIV: (defaultAttributes) => {
/* Add new one */
defaultAttributes.push('custom-attribute-name');
return defaultAttributes;
},
DIV: (defaultAttributes) => {
/* Override all */
defaultAttributes = ['custom-attribute-name', 'another-one'];
return defaultAttributes;
},
},
// Add attributes to all tags, use '*' as wildcard for attribute name that starts with
safelistAttributes: [
'custom-attribute-name',
'attribute-name-start-with-*'
],
// Remove attributes from all tags that support it
blacklistAttributes: [
'role'
]
}
posthtml(components(options))
.process(readFileSync('src/index.html', 'utf8'))
.then((result) => writeFileSync('dist/index.html', result.html, 'utf8'))
```
## Examples
You can work with `<slot>` and `<fill>` or you can create component for each "block" of your component, and you can also support both of them.
You can work with `<slot>` and `<fill>` or you can create component for each block of your component, and you can also support both of them.
You can find an example of this inside `docs-src/components/modal`. Below is a short explanation about the both approach.

@@ -777,0 +831,0 @@

@@ -47,3 +47,3 @@ 'use strict';

if (!componentPath) {
throw new Error(`[components] For the tag ${tag} was not found any template in defined root path ${options.folders.join(', ')}`);
throw new Error(`[components] <${tag}> could not find ${fileNameFromTag} in the defined root paths (${options.folders.join(', ')})`);
}

@@ -67,3 +67,3 @@

if (!namespaceOption) {
throw new Error(`[components] Unknown component namespace ${namespace}.`);
throw new Error(`[components] Unknown component namespace: ${namespace}.`);
}

@@ -89,3 +89,3 @@

if (!componentPath && options.strict) {
throw new Error(`[components] For the tag ${tag} was not found any template in the defined namespace's base path ${namespaceOption.root}.`);
throw new Error(`[components] <${tag}> could not find ${fileNameFromTag} in the defined namespace base path ${namespaceOption.root}`);
}

@@ -92,0 +92,0 @@

@@ -31,2 +31,3 @@ 'use strict';

const assign = require('lodash/assign');
const isPlainObject = require('lodash/isPlainObject');

@@ -65,2 +66,3 @@ /* eslint-disable complexity */

has,
isPlainObject,
isObject: isObjectLike,

@@ -76,2 +78,9 @@ isArray,

};
// Additional element attributes, in case already exist in valid-attributes.js it will replace all attributes
// It should be an object with key as tag name and as value a function modifier which receive
// the default attributes and return an array of attributes. Example:
// { TAG: (attributes) => { attributes[] = 'attribute-name'; return attributes; } }
options.elementAttributes = isPlainObject(options.elementAttributes) ? options.elementAttributes : {};
options.safelistAttributes = Array.isArray(options.safelistAttributes) ? options.safelistAttributes : [];
options.blacklistAttributes = Array.isArray(options.blacklistAttributes) ? options.blacklistAttributes : [];

@@ -267,2 +276,5 @@ // Merge customizer callback passed to lodash mergeWith

// Delete attribute used as path
delete currentNode.attrs[options.attribute];
return componentPath;

@@ -269,0 +281,0 @@ }

@@ -6,5 +6,7 @@ 'use strict';

const styleToObject = require('style-to-object');
const omit = require('lodash/omit');
const validAttributes = require('./valid-attributes');
const keys = require('lodash/keys');
const union = require('lodash/union');
const pick = require('lodash/pick');
const difference = require('lodash/difference');
const each = require('lodash/each');

@@ -15,2 +17,3 @@ const has = require('lodash/has');

const isObject = require('lodash/isObject');
const isEmpty = require('lodash/isEmpty');

@@ -48,4 +51,40 @@ /**

const mainNodeAttributes = omit(attributes, union(keys(props), [options.attribute], keys(aware), keys(options.props), ['$slots']));
// Merge elementAttributes and blacklistAttributes with options provided
validAttributes.blacklistAttributes = union(validAttributes.blacklistAttributes, options.blacklistAttributes);
validAttributes.safelistAttributes = union(validAttributes.safelistAttributes, options.safelistAttributes);
// Merge or override elementAttributes from options provided
if (!isEmpty(options.elementAttributes)) {
each(options.elementAttributes, (modifier, tagName) => {
if (typeof modifier === 'function' && isString(tagName)) {
tagName = tagName.toUpperCase();
const attributes = modifier(validAttributes.elementAttributes[tagName]);
if (Array.isArray(attributes)) {
validAttributes.elementAttributes[tagName] = attributes;
}
}
});
}
// Attributes to be excluded
const excludeAttributes = union(validAttributes.blacklistAttributes, keys(props), keys(aware), keys(options.props), ['$slots']);
// All valid HTML attributes for the main element
const allValidElementAttributes = isString(mainNode.tag) && has(validAttributes.elementAttributes, mainNode.tag.toUpperCase()) ? validAttributes.elementAttributes[mainNode.tag.toUpperCase()] : [];
// Valid HTML attributes without the excluded
const validElementAttributes = difference(allValidElementAttributes, excludeAttributes);
// Add override attributes
validElementAttributes.push('override:style');
validElementAttributes.push('override:class');
// Pick valid attributes from passed
const mainNodeAttributes = pick(attributes, validElementAttributes);
// Get additional specified attributes
each(attributes, (value, attr) => {
each(validAttributes.safelistAttributes, additionalAttr => {
if (additionalAttr === attr || (additionalAttr.endsWith('*') && attr.startsWith(additionalAttr.replace('*', '')))) {
mainNodeAttributes[attr] = value;
}
});
});
each(mainNodeAttributes, (value, key) => {

@@ -52,0 +91,0 @@ if (['class', 'style'].includes(key)) {

@@ -5,2 +5,3 @@ 'use strict';

const {render} = require('posthtml-render');
const get = require('lodash/get');

@@ -17,4 +18,8 @@ /**

match.call(tree, {tag: push}, pushNode => {
if (get(pushNode, 'attrs.name') === '') {
throw new Error(`[components] <${push}> tag requires a value for the "name" attribute.`);
}
if (!pushNode.attrs || !pushNode.attrs.name) {
throw new Error(`[components] Push <${push}> tag must have an attribute "name".`);
throw new Error(`[components] <${push}> tag requires a "name" attribute.`);
}

@@ -21,0 +26,0 @@