@moonwave99/goffre 0.0.4 → 0.0.6

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 (2) hide show
  1. package/lib/goffre.js +253 -229
  2. package/package.json +1 -1
package/lib/goffre.js CHANGED
@@ -17,281 +17,305 @@ const DEFAULT_DATA_PATH = path.join(process.cwd(), "data");
17
17
  const DEFAULT_VIEWS_PATH = path.join(process.cwd(), "src", "views");
18
18
  const DEFAULT_BUILD_PATH = path.join(process.cwd(), "dist");
19
19
  const MAX_SLUG_LOG_LENGTH = 40;
20
+ const DEFAULT_BLOCK_SEPARATOR = "<!-- block -->";
20
21
 
21
22
  function log() {
22
- console.log.apply(
23
- null,
24
- ["[goffre]", ...arguments].map((x) => chalk.cyan(x))
25
- );
23
+ console.log.apply(
24
+ null,
25
+ ["[goffre]", ...arguments].map((x) => chalk.cyan(x)),
26
+ );
26
27
  }
27
28
 
28
29
  function getEnv() {
29
- return {
30
- mode: process.env.MODE || "dev",
31
- };
30
+ return {
31
+ mode: process.env.MODE || "dev",
32
+ };
32
33
  }
33
34
 
34
35
  function stringify(token) {
35
- if (token instanceof Date) {
36
- return token.toISOString().split("T")[0];
37
- }
38
- return token;
36
+ if (token instanceof Date) {
37
+ return token.toISOString().split("T")[0];
38
+ }
39
+ return token;
39
40
  }
40
41
 
41
42
  export function getSlug(slug, params) {
42
- return slug
43
- .split("/")
44
- .reduce(
45
- (memo, x) =>
46
- !x.startsWith(":")
47
- ? [...memo, x]
48
- : [
49
- ...memo,
50
- slugify(stringify(params[x.slice(1)]), {
51
- lower: true,
52
- strict: true,
53
- }),
54
- ],
55
- []
56
- )
57
- .join("/");
43
+ return slug
44
+ .split("/")
45
+ .reduce(
46
+ (memo, x) =>
47
+ !x.startsWith(":")
48
+ ? [...memo, x]
49
+ : [
50
+ ...memo,
51
+ slugify(stringify(params[x.slice(1)]), {
52
+ lower: true,
53
+ strict: true,
54
+ }),
55
+ ],
56
+ [],
57
+ )
58
+ .join("/");
58
59
  }
59
60
 
60
- export function getTemplate({ page, templates, defaultTemplate = "_default" }) {
61
- if (page.template) {
62
- return page.template;
63
- }
64
- if (templates.find((x) => x.startsWith(page.slug))) {
65
- return page.slug;
66
- }
67
- return defaultTemplate;
61
+ export function getTemplate({
62
+ page,
63
+ templates = [],
64
+ defaultTemplate = "_default",
65
+ }) {
66
+ if (
67
+ templates.find((x) => path.basename(x, ".handlebars") === page.template)
68
+ ) {
69
+ return page.template;
70
+ }
71
+ if (templates.find((x) => x.startsWith(page.slug))) {
72
+ return page.slug;
73
+ }
74
+ return defaultTemplate;
68
75
  }
69
76
 
70
- function renderPage({ app, templates, buildPath, maxSlugLogLength, ...page }) {
71
- return new Promise((resolve, reject) => {
72
- const template = getTemplate({ page, templates });
77
+ function renderPage({
78
+ app,
79
+ templates,
80
+ buildPath,
81
+ maxSlugLogLength,
82
+ blockSeparator,
83
+ ...page
84
+ }) {
85
+ return new Promise((resolve, reject) => {
86
+ const template = getTemplate({ page, templates });
87
+
88
+ switch (app.locals.options.logLevel) {
89
+ case "silent":
90
+ break;
91
+ case "verbose":
92
+ log(
93
+ `Generating ${chalk.yellow(
94
+ page.slug.padEnd(maxSlugLogLength, " "),
95
+ )} with template ${chalk.green(template)}...`,
96
+ );
97
+ break;
98
+ case "normal":
99
+ default:
100
+ log(`Generating ${chalk.yellow(page.slug)}...`);
101
+ }
73
102
 
74
- switch (app.locals.options.logLevel) {
75
- case "silent":
76
- break;
77
- case "verbose":
78
- log(
79
- `Generating ${chalk.yellow(
80
- page.slug.padEnd(maxSlugLogLength, " ")
81
- )} with template ${chalk.green(template)}...`
82
- );
83
- break;
84
- case "normal":
85
- default:
86
- log(`Generating ${chalk.yellow(page.slug)}...`);
103
+ app.render(
104
+ template,
105
+ {
106
+ ...page,
107
+ layout: typeof page.layout === "undefined" ? "main" : page.layout,
108
+ content: page.content ? marked.parse(page.content) : null,
109
+ blocks: getPageBlocks(page.content, blockSeparator),
110
+ },
111
+ async (error, html) => {
112
+ if (error) {
113
+ reject(error);
114
+ return;
87
115
  }
116
+ const outputFileName = `${page.slug}${page.extname || ".html"}`;
117
+ await outputFile(path.join(buildPath, outputFileName), html);
118
+ resolve({
119
+ ...page,
120
+ outputFileName,
121
+ });
122
+ },
123
+ );
124
+ });
125
+ }
88
126
 
89
- app.render(
90
- template,
91
- {
92
- ...page,
93
- layout:
94
- typeof page.layout === "undefined" ? "main" : page.layout,
95
- content: page.content ? marked.parse(page.content) : null,
96
- },
97
- async (error, html) => {
98
- if (error) {
99
- reject(error);
100
- return;
101
- }
102
- const outputFileName = `${page.slug}${page.extname || ".html"}`;
103
- await outputFile(path.join(buildPath, outputFileName), html);
104
- resolve({
105
- ...page,
106
- outputFileName,
107
- });
108
- }
109
- );
110
- });
127
+ function getPageBlocks(content = "", separator) {
128
+ if (!content.includes(separator)) {
129
+ return [];
130
+ }
131
+ const blocks = content.split(separator).map((x) => marked.parse(x));
132
+ return blocks.filter(Boolean);
111
133
  }
112
134
 
113
135
  export async function load({ dataPath } = {}) {
114
- return {
115
- json: await loadJSON(dataPath || DEFAULT_DATA_PATH),
116
- pages: await loadMarkdown(dataPath || DEFAULT_DATA_PATH),
117
- };
136
+ return {
137
+ json: await loadJSON(dataPath || DEFAULT_DATA_PATH),
138
+ pages: await loadMarkdown(dataPath || DEFAULT_DATA_PATH),
139
+ };
118
140
  }
119
141
 
120
142
  export async function loadJSON(cwd) {
121
- const files = await globby("**/*.json", { cwd });
122
- return files.reduce(
123
- (memo, x) => ({
124
- ...memo,
125
- [path.basename(x, ".json")]: require(path.join(cwd, x)),
126
- }),
127
- {}
128
- );
143
+ const files = await globby("**/*.json", { cwd });
144
+ return files.reduce(
145
+ (memo, x) => ({
146
+ ...memo,
147
+ [path.basename(x, ".json")]: require(path.join(cwd, x)),
148
+ }),
149
+ {},
150
+ );
129
151
  }
130
152
 
131
153
  function excerpt(file) {
132
- file.excerpt = file.content.split("\n")[1];
154
+ file.excerpt = file.content.split("\n")[1];
133
155
  }
134
156
 
135
157
  export async function loadMarkdown(cwd) {
136
- const files = await globby("**/*.md", { cwd });
137
- return Promise.all(
138
- files.map(async (fileName) => {
139
- const fullPath = path.join(cwd, fileName);
140
- const contents = await readFile(fullPath, "utf-8");
141
- const parsed = matter(contents, { excerpt });
142
- const outputFileName = fileName.replace(".md", "");
143
- const slug = !parsed.data.slug
144
- ? outputFileName
145
- : getSlug(parsed.data.slug, parsed.data);
146
- return {
147
- ...parsed.data,
148
- excerpt: parsed.excerpt,
149
- slug,
150
- description: parsed.data.description || parsed.excerpt,
151
- content: parsed.content,
152
- };
153
- })
154
- );
158
+ const files = await globby("**/*.md", { cwd });
159
+ return Promise.all(
160
+ files.map(async (fileName) => {
161
+ const fullPath = path.join(cwd, fileName);
162
+ const contents = await readFile(fullPath, "utf-8");
163
+ const parsed = matter(contents, { excerpt });
164
+ const outputFileName = fileName.replace(".md", "");
165
+ const slug = !parsed.data.slug
166
+ ? outputFileName
167
+ : getSlug(parsed.data.slug, parsed.data);
168
+ return {
169
+ ...parsed.data,
170
+ excerpt: parsed.excerpt,
171
+ slug,
172
+ description: parsed.data.description || parsed.excerpt,
173
+ content: parsed.content,
174
+ };
175
+ }),
176
+ );
155
177
  }
156
178
 
157
179
  export const getSorter =
158
- ({ sortBy, order }) =>
159
- (a, b) => {
160
- let output;
161
- if (a[sortBy] instanceof Date) {
162
- output = new Date(a[sortBy]) - new Date(b[sortBy]);
163
- } else {
164
- output = a[sortBy] - b[sortBy];
165
- }
166
- return order === "desc" ? -output : output;
167
- };
180
+ ({ sortBy, order }) =>
181
+ (a, b) => {
182
+ let output;
183
+ if (a[sortBy] instanceof Date) {
184
+ output = new Date(a[sortBy]) - new Date(b[sortBy]);
185
+ } else {
186
+ output = a[sortBy] - b[sortBy];
187
+ }
188
+ return order === "desc" ? -output : output;
189
+ };
168
190
 
169
191
  export async function render({
170
- pages,
171
- viewsPath = DEFAULT_VIEWS_PATH,
172
- buildPath = DEFAULT_BUILD_PATH,
173
- domain,
174
- uglyUrls = false,
175
- logLevel = "normal",
176
- locals = {},
177
- markdown = {},
178
- handlebars = {},
179
- sitemap = {},
180
- env = {},
192
+ pages,
193
+ viewsPath = DEFAULT_VIEWS_PATH,
194
+ buildPath = DEFAULT_BUILD_PATH,
195
+ blockSeparator = DEFAULT_BLOCK_SEPARATOR,
196
+ domain,
197
+ uglyUrls = false,
198
+ logLevel = "normal",
199
+ locals = {},
200
+ markdown = {},
201
+ handlebars = {},
202
+ sitemap = {},
203
+ env = {},
181
204
  }) {
182
- const extname = handlebars.extname || ".handlebars";
183
- const app = express();
184
- app.engine(
185
- extname,
186
- engine({
187
- ...handlebars,
188
- helpers: {
189
- ...defaultHelpers,
190
- ...handlebars.helpers,
191
- },
192
- })
193
- );
194
- app.set("view engine", "handlebars");
195
- app.set("layoutsDir", path.join(viewsPath, "layouts"));
196
- app.set("views", viewsPath);
205
+ const extname = handlebars.extname || ".handlebars";
206
+ const app = express();
207
+ app.engine(
208
+ extname,
209
+ engine({
210
+ ...handlebars,
211
+ helpers: {
212
+ ...defaultHelpers,
213
+ ...handlebars.helpers,
214
+ },
215
+ }),
216
+ );
217
+ app.set("view engine", "handlebars");
218
+ app.set("layoutsDir", path.join(viewsPath, "layouts"));
219
+ app.set("views", viewsPath);
197
220
 
198
- const templates = await globby(`**/*${extname}`, {
199
- cwd: viewsPath,
200
- });
221
+ const templates = await globby(`**/*${extname}`, {
222
+ cwd: viewsPath,
223
+ });
201
224
 
202
- app.locals = {
203
- ...app.locals,
204
- ...locals,
205
- options: {
206
- domain,
207
- uglyUrls,
208
- logLevel,
209
- },
210
- env: { ...getEnv(), ...env },
211
- };
225
+ app.locals = {
226
+ ...app.locals,
227
+ ...locals,
228
+ options: {
229
+ domain,
230
+ uglyUrls,
231
+ logLevel,
232
+ },
233
+ env: { ...getEnv(), ...env },
234
+ };
212
235
 
213
- marked.use(markdown);
236
+ marked.use(markdown);
214
237
 
215
- switch (logLevel) {
216
- case "silent":
217
- break;
218
- case "verbose":
219
- case "normal":
220
- default:
221
- log(`Start generation...`);
222
- }
238
+ switch (logLevel) {
239
+ case "silent":
240
+ break;
241
+ case "verbose":
242
+ case "normal":
243
+ default:
244
+ log(`Start generation...`);
245
+ }
223
246
 
224
- const results = await Promise.all(
225
- pages.map((x) =>
226
- renderPage({
227
- ...x,
228
- buildPath,
229
- app,
230
- templates,
231
- maxSlugLogLength: Math.min(
232
- Math.max.call(null, ...pages.map((x) => x.slug.length)),
233
- MAX_SLUG_LOG_LENGTH
234
- ),
235
- })
236
- )
237
- );
247
+ const results = await Promise.all(
248
+ pages.map((x) =>
249
+ renderPage({
250
+ ...x,
251
+ buildPath,
252
+ app,
253
+ templates,
254
+ blockSeparator,
255
+ maxSlugLogLength: Math.min(
256
+ Math.max.call(null, ...pages.map((x) => x.slug.length)),
257
+ MAX_SLUG_LOG_LENGTH,
258
+ ),
259
+ }),
260
+ ),
261
+ );
238
262
 
239
- switch (logLevel) {
240
- case "silent":
241
- break;
242
- case "verbose":
243
- case "normal":
244
- default:
245
- log(`Generated ${results.length} pages`);
246
- }
263
+ switch (logLevel) {
264
+ case "silent":
265
+ break;
266
+ case "verbose":
267
+ case "normal":
268
+ default:
269
+ log(`Generated ${results.length} pages`);
270
+ }
247
271
 
248
- if (sitemap.generate) {
249
- renderPage({
250
- slug: "sitemap",
251
- template: sitemap.template || "sitemap",
252
- extname: ".xml",
253
- layout: null,
254
- pages: results,
255
- buildPath,
256
- app,
257
- templates,
258
- });
259
- }
272
+ if (sitemap.generate) {
273
+ renderPage({
274
+ slug: "sitemap",
275
+ template: sitemap.template || "sitemap",
276
+ extname: ".xml",
277
+ layout: null,
278
+ pages: results,
279
+ buildPath,
280
+ app,
281
+ templates,
282
+ });
283
+ }
260
284
 
261
- return results;
285
+ return results;
262
286
  }
263
287
 
264
288
  export function paginate({
265
- collection,
266
- size = 10,
267
- sortBy = "slug",
268
- order = "asc",
289
+ collection,
290
+ size = 10,
291
+ sortBy = "slug",
292
+ order = "asc",
269
293
  }) {
270
- const total = Math.ceil(collection.length / size);
271
- return collection
272
- .sort(getSorter({ sortBy, order }))
273
- .reduce((memo, x, index) => {
274
- if (index % size === 0) {
275
- const page = Math.floor(index / size) + 1;
276
- return [
277
- ...memo,
278
- {
279
- pagination: {
280
- page,
281
- prev: page > 1 ? page - 1 : null,
282
- next: page < total ? page + 1 : null,
283
- total,
284
- },
285
- items: [x],
286
- },
287
- ];
288
- }
289
- return [
290
- ...memo.slice(0, -1),
291
- {
292
- ...memo[memo.length - 1],
293
- items: [...memo[memo.length - 1].items, x],
294
- },
295
- ];
296
- }, []);
294
+ const total = Math.ceil(collection.length / size);
295
+ return collection
296
+ .sort(getSorter({ sortBy, order }))
297
+ .reduce((memo, x, index) => {
298
+ if (index % size === 0) {
299
+ const page = Math.floor(index / size) + 1;
300
+ return [
301
+ ...memo,
302
+ {
303
+ pagination: {
304
+ page,
305
+ prev: page > 1 ? page - 1 : null,
306
+ next: page < total ? page + 1 : null,
307
+ total,
308
+ },
309
+ items: [x],
310
+ },
311
+ ];
312
+ }
313
+ return [
314
+ ...memo.slice(0, -1),
315
+ {
316
+ ...memo[memo.length - 1],
317
+ items: [...memo[memo.length - 1].items, x],
318
+ },
319
+ ];
320
+ }, []);
297
321
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moonwave99/goffre",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Mini static site generator",
5
5
  "author": {
6
6
  "name": "Diego Caponera",