poops 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/lib/markup/engines/liquid.js +10 -0
- package/lib/markup/engines/nunjucks.js +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -615,6 +615,34 @@ Both engines support the same feature set (collections, pagination, search index
|
|
|
615
615
|
|
|
616
616
|
Both engines process `.html` and `.md` files in addition to their native extension.
|
|
617
617
|
|
|
618
|
+
#### Templates from an npm package
|
|
619
|
+
|
|
620
|
+
Layouts and partials can live in an installed package, so a shared theme ships as a dependency instead of copied files. Reference it by package name — any include/extend name containing a `/` is resolved from `node_modules`:
|
|
621
|
+
|
|
622
|
+
```nunjucks
|
|
623
|
+
{% extends "my-theme/layout.html" %}
|
|
624
|
+
{% block content %}
|
|
625
|
+
<h1>{{ page.title }}</h1>
|
|
626
|
+
{% endblock %}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Or from front matter, so the page carries no template syntax:
|
|
630
|
+
|
|
631
|
+
```yaml
|
|
632
|
+
---
|
|
633
|
+
layout: my-theme/layout
|
|
634
|
+
---
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
A theme package must:
|
|
638
|
+
|
|
639
|
+
- **Not restrict subpaths with `exports`** — or map its templates explicitly, e.g. `"exports": { "./*": "./*" }`. Otherwise Node blocks resolving the `.html` files by path.
|
|
640
|
+
- **Reference its own partials relatively** — `{% import "./nav.html" as nav %}`, not the bare name. A bare name (no `/`) is always searched in the consumer's project only, never the package.
|
|
641
|
+
|
|
642
|
+
Bundled filters (`toc`, `breadcrumb`, `og`, `canonical`, …) are engine-global, so package templates use them with no extra wiring.
|
|
643
|
+
|
|
644
|
+
Liquid resolves package templates the same way — `node_modules` is on its include roots, so `{% layout "my-theme/layout.liquid" %}` and `{% render "my-theme/partial.liquid" %}` resolve by package name too (a Liquid theme ships `.liquid` files). The `exports`/relative-partial rules above apply the same, except containment is by include root rather than the `/` name gate.
|
|
645
|
+
|
|
618
646
|
#### Custom Engines
|
|
619
647
|
|
|
620
648
|
The `engine` option also accepts a module specifier — an npm package name or a path relative to your project root. The module's default export must be an engine class:
|
|
@@ -34,6 +34,16 @@ export default class LiquidEngine {
|
|
|
34
34
|
}
|
|
35
35
|
} catch { /* ignore */ }
|
|
36
36
|
|
|
37
|
+
// Resolve package templates ({% render 'pkg/layout.liquid' %}) from the
|
|
38
|
+
// consumer's node_modules — parity with the Nunjucks engine. Every ancestor
|
|
39
|
+
// node_modules on the path is a root, so hoisted/scoped/pnpm installs all
|
|
40
|
+
// resolve, and liquidjs's containment guard passes without weakening it.
|
|
41
|
+
// Appended last so project templates always shadow a same-named package one.
|
|
42
|
+
for (let dir = templatesDir, prev = null; dir !== prev; prev = dir, dir = path.dirname(dir)) {
|
|
43
|
+
const nm = path.join(dir, 'node_modules')
|
|
44
|
+
if (fs.existsSync(nm)) roots.push(nm)
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
// Dependency index for incremental rebuilds, fed by the recording parse
|
|
38
48
|
// cache below. Persists across compiles; each page render overwrites its
|
|
39
49
|
// own entry.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
2
3
|
import { globSync } from 'glob'
|
|
3
4
|
import nunjucks from 'nunjucks'
|
|
4
5
|
import path from 'node:path'
|
|
@@ -15,6 +16,10 @@ class RelativeLoader extends nunjucks.Loader {
|
|
|
15
16
|
constructor(templatesDir, includePaths, onTouch) {
|
|
16
17
|
super()
|
|
17
18
|
this.templatesDir = templatesDir
|
|
19
|
+
// Resolve package templates ({% extends 'pkg/layout.html' %}) from the
|
|
20
|
+
// consumer project's node_modules. Anchored at templatesDir so resolution
|
|
21
|
+
// walks up to the project root — also lets a fixture project be used in tests.
|
|
22
|
+
this.require = createRequire(path.join(templatesDir, 'package.json'))
|
|
18
23
|
// Copy, don't alias+mutate: the caller reuses this array to build the
|
|
19
24
|
// markup glob exclusions, so pushing '_*' here would leak into it.
|
|
20
25
|
this.includePaths = [...(includePaths || []), '_*']
|
|
@@ -48,6 +53,14 @@ class RelativeLoader extends nunjucks.Loader {
|
|
|
48
53
|
pattern = `${this.includePaths[0]}/${pattern}`
|
|
49
54
|
}
|
|
50
55
|
fullPath = globSync(toPosix(path.join(this.templatesDir, pattern)))[0]
|
|
56
|
+
|
|
57
|
+
// Not in the project tree — try a package template installed in the
|
|
58
|
+
// consumer's node_modules, addressed as 'pkg/template.html'. The '/'
|
|
59
|
+
// gate keeps bare local names (navtree.html) project-only, so this
|
|
60
|
+
// never fires on the common path.
|
|
61
|
+
if (!fullPath && name.includes('/')) {
|
|
62
|
+
try { fullPath = this.require.resolve(name) } catch { /* stays not-found below */ }
|
|
63
|
+
}
|
|
51
64
|
}
|
|
52
65
|
if (!fullPath || !fs.existsSync(fullPath)) {
|
|
53
66
|
log({ tag: 'markup', error: true, text: 'Template not found:', link: name })
|