mikel-press 0.22.2 → 0.23.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 +7 -3
- package/index.js +67 -32
- package/package.json +1 -4
package/README.md
CHANGED
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
To install **mikel-press**, ensure you have [Node.js](https://nodejs.org) installed on your system. Then, add this package as a dependency to your project using **yarn**:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
$ yarn add --dev mikel-press
|
|
13
|
+
$ yarn add --dev mikel mikel-press
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Or **npm**:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
$ npm install --dev mikel-press
|
|
19
|
+
$ npm install --dev mikel mikel-press
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Directory structure
|
|
@@ -52,18 +52,22 @@ A basic **mikel-press** directory structure looks like this:
|
|
|
52
52
|
| `source` | The path to the directory containing the site folders. | `"."` |
|
|
53
53
|
| `destination` | The output directory where the generated static site will be saved. | `"www"` |
|
|
54
54
|
| `extensions` | List of file extensions to process. | `[".html"]` |
|
|
55
|
-
| `
|
|
55
|
+
| `template` | An instance of `mikel.create` to compile templates. | - |
|
|
56
56
|
| `plugins` | A list of plugins used to extend the functionality of **mikel-press**. | `[]` |
|
|
57
57
|
| `*` | Any other properties passed in config will be available as `site.*` inside each page template. | - |
|
|
58
58
|
|
|
59
59
|
Here is an example configuration object:
|
|
60
60
|
|
|
61
61
|
```javascript
|
|
62
|
+
import mikel from "mikel";
|
|
62
63
|
import press from "mikel-press";
|
|
63
64
|
|
|
64
65
|
press({
|
|
65
66
|
source: ".",
|
|
66
67
|
destination: "./www",
|
|
68
|
+
template: mikel.create("", {
|
|
69
|
+
// define your custom helpers and functions here
|
|
70
|
+
}),
|
|
67
71
|
title: "Hello world",
|
|
68
72
|
description: "My awesome site",
|
|
69
73
|
plugins: [
|
package/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
// @description get all plugins of the given type
|
|
5
|
+
const getPlugins = (context, name) => {
|
|
6
|
+
return context.plugins.filter(plugin => typeof plugin[name] === "function");
|
|
7
|
+
};
|
|
4
8
|
|
|
5
9
|
// @description press main function
|
|
6
10
|
// @param {Object} config - configuration object
|
|
@@ -8,28 +12,35 @@ import mikel from "mikel";
|
|
|
8
12
|
// @param {String} config.destination - destination folder to save the files
|
|
9
13
|
// @param {Array} config.plugins - list of plugins to apply
|
|
10
14
|
const press = (config = {}) => {
|
|
11
|
-
const
|
|
15
|
+
const context = press.createContext(config);
|
|
16
|
+
press.buildContext(context, context.nodes);
|
|
17
|
+
if (config.watch === true) {
|
|
18
|
+
press.watchContext(context);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// @description create a context object
|
|
23
|
+
press.createContext = (config = {}) => {
|
|
24
|
+
const {source, destination, plugins, extensions, exclude, template, watch, ...otherConfig} = config;
|
|
12
25
|
const context = Object.freeze({
|
|
13
26
|
config: otherConfig,
|
|
14
27
|
source: path.resolve(source || "."),
|
|
15
28
|
destination: path.resolve(destination || "./www"),
|
|
16
29
|
extensions: extensions || [".html"],
|
|
17
30
|
exclude: exclude || ["node_modules", ".git", ".gitignore", ".github"],
|
|
18
|
-
template:
|
|
31
|
+
template: template,
|
|
19
32
|
plugins: [
|
|
20
33
|
press.SourcePlugin({folder: ".", label: press.LABEL_PAGE}),
|
|
21
34
|
...plugins,
|
|
22
35
|
],
|
|
23
36
|
nodes: [],
|
|
24
37
|
});
|
|
25
|
-
|
|
26
|
-
// 0. initialize
|
|
27
|
-
getPlugins("init").forEach(plugin => {
|
|
38
|
+
getPlugins(context, "init").forEach(plugin => {
|
|
28
39
|
return plugin.init(context);
|
|
29
40
|
});
|
|
30
|
-
//
|
|
41
|
+
// load nodes into context
|
|
31
42
|
const nodesPaths = new Set(); // prevent adding duplicated nodes
|
|
32
|
-
getPlugins("load").forEach(plugin => {
|
|
43
|
+
getPlugins(context, "load").forEach(plugin => {
|
|
33
44
|
[plugin.load(context) || []].flat().forEach(node => {
|
|
34
45
|
if (nodesPaths.has(node.source)) {
|
|
35
46
|
throw new Error(`File ${node.source} has been already processed by another plugin`);
|
|
@@ -38,29 +49,35 @@ const press = (config = {}) => {
|
|
|
38
49
|
nodesPaths.add(node.source);
|
|
39
50
|
});
|
|
40
51
|
});
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
return context;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// @description build the provided context
|
|
56
|
+
press.buildContext = (context, nodesToBuild = null) => {
|
|
57
|
+
const nodes = Array.isArray(nodesToBuild) ? nodesToBuild : context.nodes;
|
|
58
|
+
// 1. transform nodes
|
|
59
|
+
getPlugins(context, "transform").forEach(plugin => {
|
|
43
60
|
// special hook to initialize the transform plugin
|
|
44
61
|
if (typeof plugin.beforeTransform === "function") {
|
|
45
62
|
plugin.beforeTransform(context);
|
|
46
63
|
}
|
|
47
64
|
// run the transform in all nodes
|
|
48
|
-
|
|
65
|
+
nodes.forEach((node, _, allNodes) => {
|
|
49
66
|
return plugin.transform(context, node, allNodes);
|
|
50
67
|
});
|
|
51
68
|
});
|
|
52
|
-
//
|
|
53
|
-
const shouldEmitPlugins = getPlugins("shouldEmit");
|
|
54
|
-
const filteredNodes =
|
|
69
|
+
// 2. filter nodes and get only the ones that are going to be emitted
|
|
70
|
+
const shouldEmitPlugins = getPlugins(context, "shouldEmit");
|
|
71
|
+
const filteredNodes = nodes.filter((node, _, allNodes) => {
|
|
55
72
|
return shouldEmitPlugins.every(plugin => {
|
|
56
73
|
return !!plugin.shouldEmit(context, node, allNodes);
|
|
57
74
|
});
|
|
58
75
|
});
|
|
59
|
-
//
|
|
60
|
-
getPlugins("beforeEmit").forEach(plugin => {
|
|
76
|
+
// 3. before emit
|
|
77
|
+
getPlugins(context, "beforeEmit").forEach(plugin => {
|
|
61
78
|
return plugin.beforeEmit(context);
|
|
62
79
|
});
|
|
63
|
-
//
|
|
80
|
+
// 4. emit each node
|
|
64
81
|
filteredNodes.forEach(node => {
|
|
65
82
|
// 1. if node has been processed (aka node.content is an string), write the file
|
|
66
83
|
if (typeof node.content === "string") {
|
|
@@ -73,6 +90,17 @@ const press = (config = {}) => {
|
|
|
73
90
|
});
|
|
74
91
|
};
|
|
75
92
|
|
|
93
|
+
// @description start a watch on the current context
|
|
94
|
+
press.watchContext = (context, options = {}) => {
|
|
95
|
+
const labelsToWatch = options.labels || [press.LABEL_PAGE, press.LABEL_PARTIAL, press.LABEL_DATA];
|
|
96
|
+
const nodesToRebuild = context.nodes.filter(node => labelsToWatch.includes(node.label));
|
|
97
|
+
const rebuild = () => press.buildContext(context, nodesToRebuild);
|
|
98
|
+
// create a watch for each registered node in the context
|
|
99
|
+
nodesToRebuild.forEach(node => {
|
|
100
|
+
press.utils.watch(node.source, rebuild);
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
76
104
|
// @description general utilities
|
|
77
105
|
press.utils = {
|
|
78
106
|
// @description read a file from disk
|
|
@@ -107,6 +135,19 @@ press.utils = {
|
|
|
107
135
|
.filter(file => (extensions === "*" || extensions.includes(path.extname(file))) && !exclude.includes(file))
|
|
108
136
|
.filter(file => fs.statSync(path.join(folder, file)).isFile());
|
|
109
137
|
},
|
|
138
|
+
// @description watch for file changes
|
|
139
|
+
// @param {String} filePath path to the file to watch
|
|
140
|
+
// @param {Function} listener method to listen for file changes
|
|
141
|
+
watch: (filePath, listener) => {
|
|
142
|
+
let lastModifiedTime = null;
|
|
143
|
+
fs.watch(filePath, "utf8", () => {
|
|
144
|
+
const modifiedTime = fs.statSync(filePath).mtimeMs;
|
|
145
|
+
if (lastModifiedTime !== modifiedTime) {
|
|
146
|
+
lastModifiedTime = modifiedTime;
|
|
147
|
+
return listener(filePath);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
},
|
|
110
151
|
// @description frontmatter parser
|
|
111
152
|
// @params {String} content content to parse
|
|
112
153
|
// @params {Function} parser parser function to use
|
|
@@ -130,7 +171,7 @@ press.LABEL_PARTIAL = "asset/partial";
|
|
|
130
171
|
|
|
131
172
|
// @description source plugin
|
|
132
173
|
press.SourcePlugin = (options = {}) => {
|
|
133
|
-
const shouldEmit = options?.
|
|
174
|
+
const shouldEmit = options?.emit ?? true, shouldRead = options.read ?? true;
|
|
134
175
|
const processedNodes = new Set();
|
|
135
176
|
return {
|
|
136
177
|
name: "SourcePlugin",
|
|
@@ -145,10 +186,14 @@ press.SourcePlugin = (options = {}) => {
|
|
|
145
186
|
label: options.label || press.LABEL_PAGE,
|
|
146
187
|
path: path.join(options?.basePath || ".", file),
|
|
147
188
|
url: path.normalize("/" + path.join(options?.basePath || ".", file)),
|
|
148
|
-
content: press.utils.read(path.join(folder, file)),
|
|
149
189
|
};
|
|
150
190
|
});
|
|
151
191
|
},
|
|
192
|
+
transform: (context, node) => {
|
|
193
|
+
if (processedNodes.has(node.source) && shouldRead) {
|
|
194
|
+
node.content = press.utils.read(node.source);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
152
197
|
shouldEmit: (context, node) => {
|
|
153
198
|
return !processedNodes.has(node.source) || shouldEmit;
|
|
154
199
|
},
|
|
@@ -157,27 +202,17 @@ press.SourcePlugin = (options = {}) => {
|
|
|
157
202
|
|
|
158
203
|
// @description data plugin
|
|
159
204
|
press.DataPlugin = (options = {}) => {
|
|
160
|
-
return press.SourcePlugin({folder: "./data",
|
|
205
|
+
return press.SourcePlugin({folder: "./data", emit: false, extensions: [".json"], label: press.LABEL_DATA, ...options});
|
|
161
206
|
};
|
|
162
207
|
|
|
163
208
|
// @description partials plugin
|
|
164
209
|
press.PartialsPlugin = (options = {}) => {
|
|
165
|
-
return press.SourcePlugin({folder: "./partials",
|
|
210
|
+
return press.SourcePlugin({folder: "./partials", emit: false, extensions: [".html"], label: press.LABEL_PARTIAL, ...options});
|
|
166
211
|
};
|
|
167
212
|
|
|
168
213
|
// @description assets plugin
|
|
169
214
|
press.AssetsPlugin = (options = {}) => {
|
|
170
|
-
return {
|
|
171
|
-
name: "AssetsPlugin",
|
|
172
|
-
load: context => {
|
|
173
|
-
const folder = path.join(context.source, options?.folder || "./assets");
|
|
174
|
-
return press.utils.readdir(folder, options?.extensions || "*", options?.exclude || context.exclude).map(file => ({
|
|
175
|
-
source: path.join(folder, file),
|
|
176
|
-
label: options.label || press.LABEL_ASSET,
|
|
177
|
-
path: path.join(options?.basePath || ".", file),
|
|
178
|
-
}));
|
|
179
|
-
},
|
|
180
|
-
};
|
|
215
|
+
return press.SourcePlugin({folder: "./assets", read: false, extensions: "*", label: press.LABEL_ASSET, ...options});
|
|
181
216
|
};
|
|
182
217
|
|
|
183
218
|
// @description frontmatter plugin
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikel-press",
|
|
3
3
|
"description": "A tiny and fast static site generator based on mikel templating",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.23.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
@@ -18,9 +18,6 @@
|
|
|
18
18
|
"engines": {
|
|
19
19
|
"node": ">=20"
|
|
20
20
|
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"mikel": "^0.22.2"
|
|
23
|
-
},
|
|
24
21
|
"files": [
|
|
25
22
|
"README.md",
|
|
26
23
|
"index.js"
|