Skip to content

Commit 9f03894

Browse files
authored
Merge pull request #263 from wzrdjstr/feature/plugin-run-order
Allow plugins to run before or after expressions plugin
2 parents e1a2361 + 049bd96 commit 9f03894

4 files changed

Lines changed: 88 additions & 6 deletions

File tree

README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ If you are familiar with Blade, React, Vue or similar, you will find the syntax
5555
| **propsSlot** | `String` | `'props'` | Used to retrieve props passed to slot via `$slots.slotName.props`. |
5656
| **parserOptions** | `Object` | `{recognizeSelfClosing: true}` | Pass options to `posthtml-parser`. |
5757
| **expressions** | `Object` | `{}` | Pass options to `posthtml-expressions`. |
58-
| **plugins** | `Array` | `[]` | PostHTML plugins to apply to every parsed component. |
58+
| **plugins** | `Array\|Object` | `[]` | PostHTML plugins to [apply](#plugins) to every parsed component. |
5959
| **matcher** | `Object` | `[{tag: options.tagPrefix}]` | Array of objects used to match tags. |
6060
| **attrsParserRules** | `Object` | `{}` | Additional rules for attributes parser plugin. |
6161
| **strict** | `Boolean` | `true` | Toggle exception throwing. |
@@ -842,6 +842,35 @@ Result:
842842

843843
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.
844844

845+
846+
### Plugins
847+
848+
You can specify whether plugins are applied before or after the `posthtml-expressions` plugin:
849+
850+
```js
851+
const options = {
852+
plugins: {
853+
before: [/* ... */],
854+
after: [/* ... */],
855+
}
856+
};
857+
```
858+
859+
For backwards compatibility, passing an array will default to applying plugins after the `posthtml-expressions` plugin. The following options are equivalent:
860+
861+
```js
862+
const options = {
863+
plugins: [/* ... */]
864+
};
865+
866+
const options = {
867+
plugins: {
868+
before: [/* ... */],
869+
after: [],
870+
}
871+
};
872+
```
873+
845874
### Advanced attributes configurations
846875

847876
If default configurations for valid attributes are not right for you, you may configure them as explained below.

src/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export type PostHTMLComponents = {
154154
*
155155
* @default []
156156
*/
157-
plugins?: Array<() => void>;
157+
plugins?: Array<() => void>|Record<'before','after', Array<() => void>>;
158158

159159
/**
160160
* Array of objects used to match tags.

src/index.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,21 @@ module.exports = (options = {}) => tree => {
140140
options.props = {...options.expressions.locals};
141141
options.aware = {};
142142

143+
if (Array.isArray(options.plugins)) {
144+
options.plugins = {
145+
before: [],
146+
after: options.plugins
147+
};
148+
}
149+
143150
const pushedContent = {};
144151

145152
log('Start of processing..', 'init', 'success');
146153

154+
if (options.plugins.before.length > 0) {
155+
tree = applyPluginsToTree(tree, options.plugins.before);
156+
}
157+
147158
return processStacks(
148159
processPushes(
149160
processTree(options)(
@@ -169,8 +180,8 @@ function processTree(options) {
169180
return (tree, messages) => {
170181
log(`Processing tree number ${processCounter}..`, 'processTree');
171182

172-
if (options.plugins.length > 0) {
173-
tree = applyPluginsToTree(tree, options.plugins);
183+
if (options.plugins.after.length > 0) {
184+
tree = applyPluginsToTree(tree, options.plugins.after);
174185
}
175186

176187
match.call(tree, options.matcher, currentNode => {
@@ -217,10 +228,15 @@ function processTree(options) {
217228
options.expressions.locals = attributes;
218229
options.expressions.locals.$slots = filledSlots;
219230
// const plugins = [...options.plugins, expressions(options.expressions)];
231+
232+
if (options.plugins.before.length > 0) {
233+
nextNode = applyPluginsToTree(nextNode, options.plugins.before);
234+
}
235+
220236
nextNode = expressions(options.expressions)(nextNode);
221237

222-
if (options.plugins.length > 0) {
223-
nextNode = applyPluginsToTree(nextNode, options.plugins);
238+
if (options.plugins.after.length > 0) {
239+
nextNode = applyPluginsToTree(nextNode, options.plugins.after);
224240
}
225241

226242
// Process <yield> tag

test/test-plugins.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { test, expect } from 'vitest';
22
import { process } from './process.js';
3+
import { walk } from 'posthtml/lib/api';
4+
5+
const plugin = tree => walk.call(tree, node => typeof node === 'string' && node === "{{title}}"? "{{replaced}}": node);
36

47
test('posthtml-include', async () => {
58
process(
@@ -22,6 +25,40 @@ test('posthtml-include', async () => {
2225
});
2326
});
2427

28+
test('Run plugin before expressions', async () => {
29+
process(
30+
`<component src="components/component-locals.html"></component>`,
31+
{
32+
root: './test/templates',
33+
tag: 'component',
34+
attribute: 'src',
35+
plugins: { before: [plugin], after: []},
36+
expressions: {
37+
strict: false,
38+
missingLocal: '{local}',
39+
}
40+
}
41+
)
42+
.then(html => {
43+
expect(html).toBe(`<div><h1>{{replaced}}</h1></div><div>Default body</div>`);
44+
});
45+
});
46+
47+
test('Run plugin after expressions', async () => {
48+
process(
49+
`<component src="components/component-locals.html"></component>`,
50+
{
51+
root: './test/templates',
52+
tag: 'component',
53+
attribute: 'src',
54+
plugins: { before: [], after: [plugin]},
55+
}
56+
)
57+
.then(html => {
58+
expect(html).toBe(`<div><h1>Default title</h1></div><div>Default body</div>`);
59+
});
60+
});
61+
2562
// test.skip('Must work with posthtml-extend syntax', async () => {
2663
// const actual = `<extends src="layouts/extend.html"><block name="content">My Content</block></extends>`;
2764
// const expected = `<html><head><title>Extend Layout</title></head><body><main>My Content</main><footer>footer content</footer></body></html>`;

0 commit comments

Comments
 (0)