@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.
- package/lib/goffre.js +253 -229
- 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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
return {
|
|
31
|
+
mode: process.env.MODE || "dev",
|
|
32
|
+
};
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
function stringify(token) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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({
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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({
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
154
|
+
file.excerpt = file.content.split("\n")[1];
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
export async function loadMarkdown(cwd) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
221
|
+
const templates = await globby(`**/*${extname}`, {
|
|
222
|
+
cwd: viewsPath,
|
|
223
|
+
});
|
|
201
224
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
236
|
+
marked.use(markdown);
|
|
214
237
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
285
|
+
return results;
|
|
262
286
|
}
|
|
263
287
|
|
|
264
288
|
export function paginate({
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
289
|
+
collection,
|
|
290
|
+
size = 10,
|
|
291
|
+
sortBy = "slug",
|
|
292
|
+
order = "asc",
|
|
269
293
|
}) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
}
|