mikel-press 0.29.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.
Files changed (3) hide show
  1. package/README.md +15 -0
  2. package/index.js +102 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -187,6 +187,21 @@ This plugin copies static files from the source to the destination.
187
187
  Options:
188
188
  - `options.patterns` (array): List of file patterns to copy. Each pattern should have `from` and `to`.
189
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
+
190
205
  ## API
191
206
 
192
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
@@ -41,6 +66,26 @@ press.createContext = (config = {}) => {
41
66
  nodes: [],
42
67
  actions: {},
43
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("");
87
+ });
88
+ // initialize plugins
44
89
  getPlugins(context, "init").forEach(plugin => {
45
90
  return plugin.init(context);
46
91
  });
@@ -117,6 +162,10 @@ press.watchContext = (context, options = {}) => {
117
162
 
118
163
  // @description general utilities
119
164
  press.utils = {
165
+ // @description normalize a path
166
+ normalizePath: (rawPath) => {
167
+ return path.normalize("/" + rawPath);
168
+ },
120
169
  // @description read a file from disk
121
170
  // @param {String} file path to the file to read
122
171
  read: (file, encoding = "utf8") => {
@@ -149,6 +198,26 @@ press.utils = {
149
198
  .filter(file => (extensions === "*" || extensions.includes(path.extname(file))) && !exclude.includes(file))
150
199
  .filter(file => fs.statSync(path.join(folder, file)).isFile());
151
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
+ },
152
221
  // @description watch for file changes
153
222
  // @param {String} filePath path to the file to watch
154
223
  // @param {Function} listener method to listen for file changes
@@ -199,7 +268,7 @@ press.SourcePlugin = (options = {}) => {
199
268
  source: path.join(folder, file),
200
269
  label: options.label || press.LABEL_PAGE,
201
270
  path: path.join(options?.basePath || ".", file),
202
- url: path.normalize("/" + path.join(options?.basePath || ".", file)),
271
+ url: press.utils.normalizePath(path.join(options?.basePath || ".", file)),
203
272
  };
204
273
  });
205
274
  },
@@ -275,7 +344,7 @@ press.FrontmatterPlugin = () => {
275
344
  node.title = node.attributes?.title || node.path;
276
345
  if (node.attributes.permalink) {
277
346
  node.path = node.attributes.permalink;
278
- node.url = path.normalize("/" + node.path);
347
+ node.url = press.utils.normalizePath(node.path);
279
348
  }
280
349
  }
281
350
  },
@@ -343,19 +412,37 @@ press.UsePlugin = mikelPlugin => {
343
412
  };
344
413
 
345
414
  // @description copy plugin
346
- press.CopyAssetsPlugin = (options = {}) => {
347
- return {
348
- load: () => {
349
- return (options?.patterns || [])
350
- .filter(item => item.from && fs.existsSync(path.resolve(item.from)))
351
- .map(item => ({
352
- source: path.resolve(item.from),
353
- path: path.join(options?.basePath || ".", item.to || path.basename(item.from)),
354
- label: options?.label || press.LABEL_ASSET,
355
- }));
356
- },
357
- };
358
- };
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
+ });
359
446
 
360
447
  // export press generator
361
448
  export default press;
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.29.0",
4
+ "version": "0.30.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {