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