mikel-press 0.20.0 → 0.20.2

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 +12 -4
  2. package/index.js +73 -38
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -121,7 +121,7 @@ Options:
121
121
  - `options.extensions` (array): Defines the file extensions that should be processed. If not provided, it will use `config.extensions`.
122
122
  - `options.basePath` (string): Specifies the base path for the output files.
123
123
 
124
- ### `press.PartialsLoaderPlugin(options)`
124
+ ### `press.PartialsPlugin(options)`
125
125
 
126
126
  An alias of `press.SourcePlugin` that will read all files in the `partials` folder and process them as a partials. The **mikel** tag `{{>file}}` can be used to include the partial in `partials/file.html`.
127
127
 
@@ -129,13 +129,21 @@ This plugin accepts the following options:
129
129
  - `options.folder` (string): To change the directory to load the partials files. Default is `./partials`.
130
130
  - `options.extensions` (array): Defines the file extensions that should be processed. If not provided, it will use `config.extensions`.
131
131
 
132
- ### `press.DataLoaderPlugin(options)`
132
+ ### `press.DataPlugin(options)`
133
133
 
134
- This plugin loads JSON files from the specified directory and makes them available in the site context.
134
+ This plugin loads JSON files from the specified directory and makes them available in the site context. This plugin accepts the following options:
135
135
 
136
- Options:
137
136
  - `options.folder` (string): Specifies a custom source directory for data files. If not provided, `./data` is used.
138
137
 
138
+ ### `press.AssetsPlugin(options)`
139
+
140
+ This plugin loads additional files (aka assets) and includes them in the build folder. This plugin accepts the following options:
141
+
142
+ - `options.folder` (string): Specifies a custom source directory for assets files. If not provided, `./assets` is used.
143
+ - `options.extensions` (array): Defines the file extensions that should be processed. If not provided, it will use `"*"`.
144
+ - `options.exclude` (array): Defines the list of file names to exclude.
145
+ - `options.basePath` (string): Allows to specify a base path for the output files.
146
+
139
147
  ### `press.FrontmatterPlugin()`
140
148
 
141
149
  This plugin processes and parses the frontmatter in each file. The parsed frontmatter content will be available in `page.attributes` field.
package/index.js CHANGED
@@ -8,15 +8,16 @@ import mikel from "mikel";
8
8
  // @param {String} config.destination - destination folder to save the files
9
9
  // @param {Array} config.plugins - list of plugins to apply
10
10
  const press = (config = {}) => {
11
- const {source, destination, plugins, extensions, mikelOptions, ...otherConfig} = config;
11
+ const {source, destination, plugins, extensions, exclude, mikelOptions, ...otherConfig} = config;
12
12
  const context = Object.freeze({
13
13
  config: otherConfig,
14
14
  source: path.resolve(source || "."),
15
15
  destination: path.resolve(destination || "./www"),
16
16
  extensions: extensions || [".html"],
17
+ exclude: exclude || ["node_modules", ".git", ".gitignore", ".github"],
17
18
  template: mikel.create("{{>content}}", mikelOptions || {}),
18
19
  plugins: [
19
- SourcePlugin({folder: ".", label: press.LABEL_PAGE}),
20
+ press.SourcePlugin({folder: ".", label: press.LABEL_PAGE}),
20
21
  ...plugins,
21
22
  ],
22
23
  nodes: [],
@@ -34,9 +35,13 @@ const press = (config = {}) => {
34
35
  });
35
36
  });
36
37
  // 2. transform nodes
37
- const transformPlugins = getPlugins("transform");
38
- context.nodes.forEach((node, _, allNodes) => {
39
- transformPlugins.forEach(plugin => {
38
+ getPlugins("transform").forEach(plugin => {
39
+ // special hook to initialize the transform plugin
40
+ if (typeof plugin.beforeTransform === "function") {
41
+ plugin.beforeTransform(context);
42
+ }
43
+ // run the transform in all nodes
44
+ context.nodes.forEach((node, _, allNodes) => {
40
45
  return plugin.transform(context, node, allNodes);
41
46
  });
42
47
  });
@@ -52,11 +57,15 @@ const press = (config = {}) => {
52
57
  return plugin.beforeEmit(context);
53
58
  });
54
59
  // 5. emit each node
55
- const emitPlugins = getPlugins("emit");
56
- filteredNodes.forEach((node, _, allNodes) => {
57
- emitPlugins.forEach(plugin => {
58
- return plugin.emit(context, node, allNodes);
59
- });
60
+ filteredNodes.forEach(node => {
61
+ // 1. if node has been processed (aka node.content is an string), write the file
62
+ if (typeof node.content === "string") {
63
+ press.utils.write(path.join(context.destination, node.path), node.content);
64
+ }
65
+ // 2. if node has not been processed, just copy the file
66
+ else if (fs.existsSync(node.source)) {
67
+ press.utils.copy(node.source, path.join(context.destination, node.path));
68
+ }
60
69
  });
61
70
  };
62
71
 
@@ -86,12 +95,12 @@ press.utils = {
86
95
  fs.copyFileSync(source, target);
87
96
  },
88
97
  // @description get all files from the given folder and the given extensions
89
- readdir: (folder, extensions = "*") => {
98
+ readdir: (folder, extensions = "*", exclude = []) => {
90
99
  if (!fs.existsSync(folder) || !fs.statSync(folder).isDirectory()) {
91
100
  return [];
92
101
  }
93
102
  return fs.readdirSync(folder, "utf8")
94
- .filter(file => extensions === "*" || extensions.includes(path.extname(file)))
103
+ .filter(file => (extensions === "*" || extensions.includes(path.extname(file))) && !exclude.includes(file))
95
104
  .filter(file => fs.statSync(path.join(folder, file)).isFile());
96
105
  },
97
106
  // @description frontmatter parser
@@ -117,27 +126,54 @@ press.LABEL_PARTIAL = "asset/partial";
117
126
 
118
127
  // @description source plugin
119
128
  press.SourcePlugin = (options = {}) => {
129
+ const shouldEmit = options?.shouldEmit ?? true;
130
+ const processedNodes = new Set();
120
131
  return {
121
132
  name: "SourcePlugin",
122
133
  load: context => {
123
134
  const folder = path.join(context.source, options?.folder || ".");
124
- return press.utils.readdir(folder, options?.extensions || context.extensions).map(file => ({
125
- source: path.join(folder, file),
126
- label: options.label || press.LABEL_PAGE,
127
- path: path.join(options?.basePath || ".", file),
128
- url: path.normalize("/" + path.join(options?.basePath || ".", file)),
129
- content: press.utils.read(path.join(folder, file)),
130
- }));
135
+ const extensions = options?.extensions || context.extensions;
136
+ const exclude = options?.exclude || context.exclude;
137
+ return press.utils.readdir(folder, extensions, exclude).map(file => {
138
+ processedNodes.add(path.join(folder, file)); // register this node
139
+ return {
140
+ source: path.join(folder, file),
141
+ label: options.label || press.LABEL_PAGE,
142
+ path: path.join(options?.basePath || ".", file),
143
+ url: path.normalize("/" + path.join(options?.basePath || ".", file)),
144
+ content: press.utils.read(path.join(folder, file)),
145
+ };
146
+ });
147
+ },
148
+ shouldEmit: (context, node) => {
149
+ return !processedNodes.has(node.source) || shouldEmit;
131
150
  },
132
151
  };
133
152
  };
134
153
 
135
- // @description loader plugins
136
- press.DataLoaderPlugin = (options = {}) => {
137
- return SourcePlugin({folder: "./data", extensions: [".json"], label: press.LABEL_DATA, ...options});
154
+ // @description data plugin
155
+ press.DataPlugin = (options = {}) => {
156
+ return press.SourcePlugin({folder: "./data", shouldEmit: false, extensions: [".json"], label: press.LABEL_DATA, ...options});
157
+ };
158
+
159
+ // @description partials plugin
160
+ press.PartialsPlugin = (options = {}) => {
161
+ return press.SourcePlugin({folder: "./partials", shouldEmit: false, extensions: [".html"], label: press.LABEL_PARTIAL, ...options});
138
162
  };
139
- press.PartialsLoaderPlugin = (options = {}) => {
140
- return SourcePlugin({folder: "./partials", extensions: [".html"], label: press.LABEL_PARTIAL, ...options});
163
+
164
+ // @description assets plugin
165
+ press.AssetsPlugin = (options = {}) => {
166
+ return {
167
+ name: "AssetsPlugin",
168
+ load: context => {
169
+ const folder = path.join(context.source, options?.folder || "./assets");
170
+ return press.utils.readdir(folder, options?.extensions || "*", options?.exclude || context.exclude).map(file => ({
171
+ source: path.join(folder, file),
172
+ label: options.label || press.LABEL_ASSET,
173
+ path: path.join(options?.basePath || ".", file),
174
+ }));
175
+ },
176
+ };
141
177
  };
142
178
 
143
179
  // @description frontmatter plugin
@@ -147,7 +183,7 @@ press.FrontmatterPlugin = () => {
147
183
  transform: (_, node) => {
148
184
  if (typeof node.content === "string") {
149
185
  const result = press.utils.frontmatter(node.content, JSON.parse);
150
- node.content = result.content;
186
+ node.content = result.body || "";
151
187
  node.attributes = result.attributes || {};
152
188
  node.title = node.attributes?.title || node.path;
153
189
  if (node.attributes.permalink) {
@@ -163,10 +199,7 @@ press.FrontmatterPlugin = () => {
163
199
  press.ContentPagePlugin = (siteData = {}) => {
164
200
  return {
165
201
  name: "ContentPagePlugin",
166
- shouldEmit: (context, node) => {
167
- return ![press.LABEL_ASSET, press.LABEL_DATA, press.LABEL_PARTIAL].includes(node.label);
168
- },
169
- beforeEmit: context => {
202
+ beforeTransform: context => {
170
203
  const getNodes = label => context.nodes.filter(n => n.label === label);
171
204
  // 1. prepare site data
172
205
  Object.assign(siteData, context.config, {
@@ -180,19 +213,17 @@ press.ContentPagePlugin = (siteData = {}) => {
180
213
  // 2. register partials into template
181
214
  siteData.partials.forEach(partial => {
182
215
  context.template.addPartial(path.basename(partial.path), {
183
- body: partial.content,
216
+ body: partial.content || "",
184
217
  attributes: partial.attributes || {},
185
218
  });
186
219
  });
187
220
  },
188
- emit: (context, node) => {
221
+ transform: (context, node) => {
189
222
  if (node.label === press.LABEL_PAGE && typeof node.content === "string") {
190
223
  context.template.use(ctx => {
191
224
  ctx.tokens = mikel.tokenize(node.content || "");
192
225
  });
193
- // compile and write the template
194
- const result = context.template({site: siteData, page: node});
195
- press.utils.write(path.join(context.destination, node.path), result);
226
+ node.content = context.template({site: siteData, page: node});
196
227
  }
197
228
  },
198
229
  };
@@ -202,10 +233,14 @@ press.ContentPagePlugin = (siteData = {}) => {
202
233
  press.CopyAssetsPlugin = (options = {}) => {
203
234
  return {
204
235
  name: "CopyAssetsPlugin",
205
- beforeEmit: context => {
206
- (options.patterns || [])
207
- .filter(item => !!item.from && !!item.to && fs.existsSync(item.from))
208
- .forEach(item => press.utils.copy(item.from, path.join(context.destination, item.to)));
236
+ load: () => {
237
+ return (options?.patterns || [])
238
+ .filter(item => item.from && fs.existsSync(path.resolve(item.from)))
239
+ .map(item => ({
240
+ source: path.resolve(item.from),
241
+ path: path.join(options?.basePath || ".", item.to || path.basename(item.from)),
242
+ label: options?.label || press.LABEL_ASSET,
243
+ }));
209
244
  },
210
245
  };
211
246
  };
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.20.0",
4
+ "version": "0.20.2",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {
@@ -19,7 +19,7 @@
19
19
  "node": ">=20"
20
20
  },
21
21
  "dependencies": {
22
- "mikel": "^0.20.0"
22
+ "mikel": "^0.20.2"
23
23
  },
24
24
  "files": [
25
25
  "README.md",