diff --git a/README.md b/README.md index 371f4cc..cc6569b 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ If you are familiar with Blade, React, Vue or similar, you will find the syntax | **propsSlot** | `String` | `'props'` | Used to retrieve props passed to slot via `$slots.slotName.props`. | | **parserOptions** | `Object` | `{recognizeSelfClosing: true}` | Pass options to `posthtml-parser`. | | **expressions** | `Object` | `{}` | Pass options to `posthtml-expressions`. | -| **plugins** | `Array` | `[]` | PostHTML plugins to apply to every parsed component. | +| **plugins** | `Array\|Object` | `[]` | PostHTML plugins to [apply](#plugins) to every parsed component. | | **matcher** | `Object` | `[{tag: options.tagPrefix}]` | Array of objects used to match tags. | | **attrsParserRules** | `Object` | `{}` | Additional rules for attributes parser plugin. | | **strict** | `Boolean` | `true` | Toggle exception throwing. | @@ -842,6 +842,35 @@ Result: You can add custom rules to define how attributes are parsed - we use [posthtml-attrs-parser](https://github.com/posthtml/posthtml-attrs-parser) to handle them. + +### Plugins + +You can specify whether plugins are applied before or after the `posthtml-expressions` plugin: + +```js +const options = { + plugins: { + before: [/* ... */], + after: [/* ... */], + } +}; +``` + +For backwards compatibility, passing an array will default to applying plugins after the `posthtml-expressions` plugin. The following options are equivalent: + +```js +const options = { + plugins: [/* ... */] +}; + +const options = { + plugins: { + before: [/* ... */], + after: [], + } +}; +``` + ### Advanced attributes configurations If default configurations for valid attributes are not right for you, you may configure them as explained below. diff --git a/src/index.d.ts b/src/index.d.ts index f791b7c..79942ed 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -154,7 +154,7 @@ export type PostHTMLComponents = { * * @default [] */ - plugins?: Array<() => void>; + plugins?: Array<() => void>|Record<'before','after', Array<() => void>>; /** * Array of objects used to match tags. diff --git a/src/index.js b/src/index.js index ab0e89d..a767e6a 100644 --- a/src/index.js +++ b/src/index.js @@ -140,10 +140,21 @@ module.exports = (options = {}) => tree => { options.props = {...options.expressions.locals}; options.aware = {}; + if (Array.isArray(options.plugins)) { + options.plugins = { + before: [], + after: options.plugins + }; + } + const pushedContent = {}; log('Start of processing..', 'init', 'success'); + if (options.plugins.before.length > 0) { + tree = applyPluginsToTree(tree, options.plugins.before); + } + return processStacks( processPushes( processTree(options)( @@ -169,8 +180,8 @@ function processTree(options) { return (tree, messages) => { log(`Processing tree number ${processCounter}..`, 'processTree'); - if (options.plugins.length > 0) { - tree = applyPluginsToTree(tree, options.plugins); + if (options.plugins.after.length > 0) { + tree = applyPluginsToTree(tree, options.plugins.after); } match.call(tree, options.matcher, currentNode => { @@ -217,10 +228,15 @@ function processTree(options) { options.expressions.locals = attributes; options.expressions.locals.$slots = filledSlots; // const plugins = [...options.plugins, expressions(options.expressions)]; + + if (options.plugins.before.length > 0) { + nextNode = applyPluginsToTree(nextNode, options.plugins.before); + } + nextNode = expressions(options.expressions)(nextNode); - if (options.plugins.length > 0) { - nextNode = applyPluginsToTree(nextNode, options.plugins); + if (options.plugins.after.length > 0) { + nextNode = applyPluginsToTree(nextNode, options.plugins.after); } // Process tag diff --git a/test/test-plugins.js b/test/test-plugins.js index 4f6a98f..5a50716 100644 --- a/test/test-plugins.js +++ b/test/test-plugins.js @@ -1,5 +1,8 @@ import { test, expect } from 'vitest'; import { process } from './process.js'; +import { walk } from 'posthtml/lib/api'; + +const plugin = tree => walk.call(tree, node => typeof node === 'string' && node === "{{title}}"? "{{replaced}}": node); test('posthtml-include', async () => { process( @@ -22,6 +25,40 @@ test('posthtml-include', async () => { }); }); +test('Run plugin before expressions', async () => { + process( + ``, + { + root: './test/templates', + tag: 'component', + attribute: 'src', + plugins: { before: [plugin], after: []}, + expressions: { + strict: false, + missingLocal: '{local}', + } + } + ) + .then(html => { + expect(html).toBe(`

{{replaced}}

Default body
`); + }); +}); + +test('Run plugin after expressions', async () => { + process( + ``, + { + root: './test/templates', + tag: 'component', + attribute: 'src', + plugins: { before: [], after: [plugin]}, + } + ) + .then(html => { + expect(html).toBe(`

Default title

Default body
`); + }); +}); + // test.skip('Must work with posthtml-extend syntax', async () => { // const actual = `My Content`; // const expected = `Extend Layout
My Content
`;