-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathcomponent.js
124 lines (116 loc) · 3.95 KB
/
component.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
const fs = require("fs");
const glob = require("glob");
const nunjucks = require("nunjucks");
const path = require("path");
const { getComponentsDir } = require("../_helpers/get-components-dir");
const config = require("../../eleventy.config");
exports.Component = class Component {
/**
* Initialize a new component.
*
* @param {string} name Name of the component as the directory in which the
* component lives.
* @param {object} props Properties to render.
* @param {object} cfg Eleventy config object. (defaults to the project
* config)
*/
constructor(name, props = {}, cfg = config) {
this.load(name, cfg);
if (!this.template) throw `Template file does not exist: ${name}`;
this.props = props;
}
/**
* Load the appropriate component files.
*
* @param {string} name Name of the component as the directory in which the
* component lives.
* @param {object} config Eleventy config object.
*/
load(name, config) {
const dir = getComponentsDir(config);
// Load transformer as the default export.
const transformer = glob.sync(path.join(dir, name, "*.transformer.js"))[0];
if (transformer) {
// Delete the imported file from the require cache. This brings in
// updates if the dev server is already running.
delete require.cache[require.resolve(transformer)];
const module = require(transformer);
if (typeof module === "function") this.transformer = module;
}
// Load the template content.
const template = glob.sync(path.join(dir, name, "*.template.njk"))[0];
if (template) this.template = fs.readFileSync(template).toString();
}
/**
* If the component has a transformer, transform the props. (NOTE: This should
* only be called from the render() function.)
*/
transform() {
if (!this.transformer) return this.props;
this.props = this.transformer(this.props);
}
/**
* Render the component, returning the HTML string.
*/
render() {
this.transform();
return nunjucks.renderString(this.template, this.props);
}
/**
* Whether or not the component should have paired tags or a single tag.
*/
isPaired() {
return this.template.includes("{{ children");
}
};
/**
* Sets the props for a component and then renders the component.
*
* @param {object} component A component object (from class above).
* @param {object} props The properties to set on the component.
*/
const renderComponent = (component, props) => {
try {
component.props = props;
return component.render();
} catch (error) {
throw console.error(`ERROR: `, error);
}
};
/**
* Provides a method for adding NJK components.
*
* @param {object} eleventyConfig Eleventy's configuration object
*/
exports.default = (eleventyConfig) => {
// Grab all the directories in the components dir. Note that this only grabs
// top-level comps at this time.
let components = glob.sync(path.join(getComponentsDir(config), "*"), {
ignore: ["**/*.css"],
});
// Loop through each ...
components = components.map((compDir) => {
// Extract the name of the directory, which is expected to match the name of
// the component and its interior files.
const name = path.basename(compDir);
// Instantiate the component object.
const component = new this.Component(name, {});
// Swap hyphens for underscores in shortcode name.
const shortcodeName = name.replace(/\-/gi, "_");
// If the component is paired ...
if (component.isPaired()) {
// ... Render the component, setting the children prop appropriately.
eleventyConfig.addPairedNunjucksShortcode(
shortcodeName,
(children, props) => {
return renderComponent(component, { ...props, children: children });
}
);
} else {
// Otherwise, render an unpaired component and pass the props directly.
eleventyConfig.addNunjucksShortcode(shortcodeName, (props) => {
return renderComponent(component, props);
});
}
});
};