mikel-press 0.28.0 → 0.30.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 +29 -0
- package/index.js +120 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -162,6 +162,20 @@ This plugin loads additional files (aka assets) and includes them in the build f
|
|
|
162
162
|
|
|
163
163
|
This plugin processes and parses the frontmatter in each file. The parsed frontmatter content will be available in `page.attributes` field.
|
|
164
164
|
|
|
165
|
+
### `press.TransformPlugin(options)`
|
|
166
|
+
|
|
167
|
+
A generic transform plugin that will execute the provided `options.transform` function with the `context` and the current `node` object. Example:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
press.TransformPlugin({
|
|
171
|
+
transform: node => {
|
|
172
|
+
if (node.label === press.LABEL_PAGE && node.content && path.extname(node.source) === ".md") {
|
|
173
|
+
node.content = `{{#markdown}}\n\n${node.content}\n\n{{/markdown}}\n`;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
165
179
|
### `press.ContentPagePlugin()`
|
|
166
180
|
|
|
167
181
|
This plugin processes each page using the mikel templating.
|
|
@@ -173,6 +187,21 @@ This plugin copies static files from the source to the destination.
|
|
|
173
187
|
Options:
|
|
174
188
|
- `options.patterns` (array): List of file patterns to copy. Each pattern should have `from` and `to`.
|
|
175
189
|
|
|
190
|
+
### `press.RedirectsPlugin(options)`
|
|
191
|
+
|
|
192
|
+
The RedirectsPlugin lets you define URL redirects. For each redirect rule, the plugin generates a small HTML file that forwards visitors to the target URL using both HTML and JavaScript fallbacks.
|
|
193
|
+
|
|
194
|
+
Options:
|
|
195
|
+
- `options.redirects` (array): list of redirections. Each redirection should have `from` and `to`.
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
press.RedirectsPlugin({
|
|
199
|
+
redirects: [
|
|
200
|
+
{ from: "/socials/github.html", to: "https://github.com/jmjuanes" },
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
176
205
|
## API
|
|
177
206
|
|
|
178
207
|
**mikel-press** exposes a single function that triggers the build with the given configuration object provided as an argument.
|
package/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
|
|
4
|
+
// @description internal method to get the first node that ends with the provided string
|
|
5
|
+
const getNodeFromSource = (nodes = [], endingStr = "") => {
|
|
6
|
+
return (nodes || []).find(node => (node.source || "").endsWith(endingStr || ""));
|
|
7
|
+
};
|
|
8
|
+
|
|
4
9
|
// @description get all plugins of the given type
|
|
5
10
|
const getPlugins = (context, name) => {
|
|
6
11
|
return context.plugins.filter(plugin => typeof plugin[name] === "function");
|
|
@@ -11,6 +16,26 @@ const applyLayout = page => {
|
|
|
11
16
|
return `{{>>layout:${page.attributes.layout}}}\n\n${page.content}\n\n{{/layout:${page.attributes.layout}}}\n`;
|
|
12
17
|
};
|
|
13
18
|
|
|
19
|
+
// @description generate the content for the redirection page
|
|
20
|
+
const generateRedirectHTML = (to, status) => {
|
|
21
|
+
const content = [
|
|
22
|
+
`<!DOCTYPE html>`,
|
|
23
|
+
`<html lang="en">`,
|
|
24
|
+
`<head>`,
|
|
25
|
+
` <meta charset="utf-8" />`,
|
|
26
|
+
` <title>Redirecting...</title>`,
|
|
27
|
+
` <meta http-equiv="refresh" content="0; url=${to}" />`,
|
|
28
|
+
` <link rel="canonical" href="${to}" />`,
|
|
29
|
+
` <script>window.location.replace("${to}");</script>`,
|
|
30
|
+
`</head>`,
|
|
31
|
+
`<body>`,
|
|
32
|
+
` <p>Redirectig to <a href="${to}">${to}</a>...</p>`,
|
|
33
|
+
`</body>`,
|
|
34
|
+
`</html>`,
|
|
35
|
+
];
|
|
36
|
+
return content.join("\n");
|
|
37
|
+
};
|
|
38
|
+
|
|
14
39
|
// @description press main function
|
|
15
40
|
// @param {Object} config - configuration object
|
|
16
41
|
// @param {String} config.source - source folder
|
|
@@ -39,7 +64,28 @@ press.createContext = (config = {}) => {
|
|
|
39
64
|
...plugins,
|
|
40
65
|
],
|
|
41
66
|
nodes: [],
|
|
67
|
+
actions: {},
|
|
68
|
+
});
|
|
69
|
+
// register helpers and funcions
|
|
70
|
+
context.template.addFunction("getPageUrl", params => {
|
|
71
|
+
return getNodeFromSource(params?.variables?.root?.site?.pages || [], params.args[0])?.url || "";
|
|
72
|
+
});
|
|
73
|
+
context.template.addFunction("getAssetUrl", params => {
|
|
74
|
+
return getNodeFromSource(params?.variables?.root?.site?.assets || [], params.args[0])?.url || "";
|
|
75
|
+
});
|
|
76
|
+
context.template.addHelper("pages", params => {
|
|
77
|
+
const draft = params?.options?.draft ?? params?.opt?.draft;
|
|
78
|
+
const collection = params?.opt?.collection || params?.options?.collection || null;
|
|
79
|
+
const items = (params.data?.site?.pages || []).filter(page => {
|
|
80
|
+
if (typeof draft === "boolean" && draft !== !!page?.attributes?.draft) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return !collection || page.attributes?.collection === collection;
|
|
84
|
+
});
|
|
85
|
+
const limit = Math.min(items.length, params.options?.limit || params.opt?.limit || items.length);
|
|
86
|
+
return items.slice(0, limit).reverse().map((c, i) => params.fn(c, {index: i})).join("");
|
|
42
87
|
});
|
|
88
|
+
// initialize plugins
|
|
43
89
|
getPlugins(context, "init").forEach(plugin => {
|
|
44
90
|
return plugin.init(context);
|
|
45
91
|
});
|
|
@@ -59,7 +105,15 @@ press.createContext = (config = {}) => {
|
|
|
59
105
|
|
|
60
106
|
// @description build the provided context
|
|
61
107
|
press.buildContext = (context, nodesToBuild = null) => {
|
|
62
|
-
const nodes = Array.isArray(nodesToBuild) ? nodesToBuild : context.nodes;
|
|
108
|
+
const nodes = (Array.isArray(nodesToBuild) ? nodesToBuild : context.nodes).slice();
|
|
109
|
+
const createNode = (nodeLabel, nodeObject = {}) => {
|
|
110
|
+
nodes.push({ label: nodeLabel, content: "", ...nodeObject });
|
|
111
|
+
};
|
|
112
|
+
// 0. assign actions to context
|
|
113
|
+
Object.assign(context.actions, {
|
|
114
|
+
createPage: pageObject => createNode(press.LABEL_PAGE, pageObject),
|
|
115
|
+
createAsset: assetObject => createNode(press.LABEL_ASSET, assetObject),
|
|
116
|
+
});
|
|
63
117
|
// 1. transform nodes
|
|
64
118
|
getPlugins(context, "transform").forEach(plugin => {
|
|
65
119
|
// special hook to initialize the transform plugin
|
|
@@ -108,6 +162,10 @@ press.watchContext = (context, options = {}) => {
|
|
|
108
162
|
|
|
109
163
|
// @description general utilities
|
|
110
164
|
press.utils = {
|
|
165
|
+
// @description normalize a path
|
|
166
|
+
normalizePath: (rawPath) => {
|
|
167
|
+
return path.normalize("/" + rawPath);
|
|
168
|
+
},
|
|
111
169
|
// @description read a file from disk
|
|
112
170
|
// @param {String} file path to the file to read
|
|
113
171
|
read: (file, encoding = "utf8") => {
|
|
@@ -140,6 +198,26 @@ press.utils = {
|
|
|
140
198
|
.filter(file => (extensions === "*" || extensions.includes(path.extname(file))) && !exclude.includes(file))
|
|
141
199
|
.filter(file => fs.statSync(path.join(folder, file)).isFile());
|
|
142
200
|
},
|
|
201
|
+
// @description walk through the given folder and get all files
|
|
202
|
+
// @params {String} folder folder to walk through
|
|
203
|
+
// @params {Array|String} extensions extensions to include. Default: "*"
|
|
204
|
+
walkdir: (folder, extensions = "*", exclude = []) => {
|
|
205
|
+
const walkSync = (currentFolder, files = []) => {
|
|
206
|
+
const fullFolderPath = path.join(folder, currentFolder);
|
|
207
|
+
fs.readdirSync(fullFolderPath).forEach(file => {
|
|
208
|
+
const filePath = path.join(currentFolder, file);
|
|
209
|
+
const fullFilePath = path.join(fullFolderPath, file);
|
|
210
|
+
if (fs.statSync(fullFilePath).isDirectory()) {
|
|
211
|
+
return walkSync(filePath, files);
|
|
212
|
+
}
|
|
213
|
+
if (extensions === "*" || extensions.includes(path.extname(file))) {
|
|
214
|
+
files.push(filePath);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return files;
|
|
218
|
+
};
|
|
219
|
+
return walkSync("./", []);
|
|
220
|
+
},
|
|
143
221
|
// @description watch for file changes
|
|
144
222
|
// @param {String} filePath path to the file to watch
|
|
145
223
|
// @param {Function} listener method to listen for file changes
|
|
@@ -190,7 +268,7 @@ press.SourcePlugin = (options = {}) => {
|
|
|
190
268
|
source: path.join(folder, file),
|
|
191
269
|
label: options.label || press.LABEL_PAGE,
|
|
192
270
|
path: path.join(options?.basePath || ".", file),
|
|
193
|
-
url:
|
|
271
|
+
url: press.utils.normalizePath(path.join(options?.basePath || ".", file)),
|
|
194
272
|
};
|
|
195
273
|
});
|
|
196
274
|
},
|
|
@@ -247,6 +325,14 @@ press.LayoutsPlugin = (options = {}) => {
|
|
|
247
325
|
});
|
|
248
326
|
};
|
|
249
327
|
|
|
328
|
+
// @description generic transform plugin
|
|
329
|
+
press.TransformPlugin = (options = {}) => {
|
|
330
|
+
const transformFn = typeof options?.transform === "function" ? options.transform : options;
|
|
331
|
+
return {
|
|
332
|
+
transform: (context, node) => transformFn(node, context),
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
|
|
250
336
|
// @description frontmatter plugin
|
|
251
337
|
press.FrontmatterPlugin = () => {
|
|
252
338
|
return {
|
|
@@ -258,7 +344,7 @@ press.FrontmatterPlugin = () => {
|
|
|
258
344
|
node.title = node.attributes?.title || node.path;
|
|
259
345
|
if (node.attributes.permalink) {
|
|
260
346
|
node.path = node.attributes.permalink;
|
|
261
|
-
node.url =
|
|
347
|
+
node.url = press.utils.normalizePath(node.path);
|
|
262
348
|
}
|
|
263
349
|
}
|
|
264
350
|
},
|
|
@@ -326,19 +412,37 @@ press.UsePlugin = mikelPlugin => {
|
|
|
326
412
|
};
|
|
327
413
|
|
|
328
414
|
// @description copy plugin
|
|
329
|
-
press.CopyAssetsPlugin = (options = {}) => {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
415
|
+
press.CopyAssetsPlugin = (options = {}) => ({
|
|
416
|
+
name: "CopyAssetsPlugin",
|
|
417
|
+
load: () => {
|
|
418
|
+
const filesToCopy = (options?.patterns || []).filter(item => {
|
|
419
|
+
return item.from && fs.existsSync(path.resolve(item.from));
|
|
420
|
+
});
|
|
421
|
+
return filesToCopy.map(item => {
|
|
422
|
+
const filePath = path.join(options?.basePath || ".", item.to || path.basename(item.from));
|
|
423
|
+
return {
|
|
424
|
+
source: path.resolve(item.from),
|
|
425
|
+
path: filePath,
|
|
426
|
+
url: press.utils.normalizePath(filePath),
|
|
427
|
+
label: options?.label || press.LABEL_ASSET,
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// @description redirections plugin
|
|
434
|
+
press.RedirectsPlugin = (options = {}) => ({
|
|
435
|
+
name: "RedirectsPlugin",
|
|
436
|
+
load: () => {
|
|
437
|
+
return (options.redirects || []).map(redirection => ({
|
|
438
|
+
source: redirection.from,
|
|
439
|
+
path: path.join(options?.basePath || ".", redirection.from),
|
|
440
|
+
url: press.utils.normalizePath(path.join(options?.basePath || ".", redirection.from)),
|
|
441
|
+
label: press.LABEL_ASSET,
|
|
442
|
+
content: generateRedirectHTML(redirection.to),
|
|
443
|
+
}));
|
|
444
|
+
},
|
|
445
|
+
});
|
|
342
446
|
|
|
343
447
|
// export press generator
|
|
344
448
|
export default press;
|