mikel-press 0.0.1 → 0.1.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.
- package/index.d.ts +50 -0
- package/index.js +88 -80
- package/package.json +3 -7
- package/cli.js +0 -47
package/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
interface MikelTemplateOptions {
|
|
2
|
+
functions: {[key: string]: (args: any) => string};
|
|
3
|
+
helpers: {[key: string]: (args: any) => string};
|
|
4
|
+
partials: {[key: string]: string};
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface FrontmatterOptions {
|
|
8
|
+
separator?: string;
|
|
9
|
+
parse?: (front: string) => any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface FrontmatterResult {
|
|
13
|
+
body: string;
|
|
14
|
+
attributes: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface VirtualPageOptions {
|
|
18
|
+
content?: string;
|
|
19
|
+
file?: string;
|
|
20
|
+
extname?: string;
|
|
21
|
+
basename?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface VirtualPage {
|
|
25
|
+
content: string,
|
|
26
|
+
attributes: any,
|
|
27
|
+
name: string,
|
|
28
|
+
extname: string,
|
|
29
|
+
basename: string,
|
|
30
|
+
url: string,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface SiteConfig {
|
|
34
|
+
source: string;
|
|
35
|
+
destination: string;
|
|
36
|
+
layout: string;
|
|
37
|
+
layoutContent: string;
|
|
38
|
+
dataDir: string;
|
|
39
|
+
pagesDir: string;
|
|
40
|
+
assetsDir: string;
|
|
41
|
+
frontmatter: Partial<FrontmatterOptions>;
|
|
42
|
+
mikel: Partial<MikelTemplateOptions>;
|
|
43
|
+
plugins: any[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare module "mikel-press" {
|
|
47
|
+
export function frontmatter(str: string, options?: FrontmatterOptions): FrontmatterResult;
|
|
48
|
+
export function createVirtualPage(options: VirtualPageOptions): VirtualPage;
|
|
49
|
+
export function run(config: Partial<SiteConfig>): void;
|
|
50
|
+
}
|
package/index.js
CHANGED
|
@@ -2,74 +2,27 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import mikel from "mikel";
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
11
|
-
|
|
5
|
+
// @description tiny front-matter parser
|
|
6
|
+
const frontmatter = (str = "", options = {}) => {
|
|
7
|
+
let body = (str || "").trim(), attributes = {};
|
|
8
|
+
if (!!options && typeof options === "object") {
|
|
9
|
+
const matches = Array.from(body.matchAll(new RegExp("^(" + (options.separator || "---") + " *)", "gm")));
|
|
10
|
+
if (matches?.length === 2 && matches[0].index === 0) {
|
|
11
|
+
const front = body.substring(0 + matches[0][1].length, matches[1].index).trim();
|
|
12
|
+
body = body.substring(matches[1].index + matches[1][1].length).trim();
|
|
13
|
+
attributes = typeof options.parse === "function" ? options.parse(front) : front;
|
|
12
14
|
}
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return {body, attributes};
|
|
27
|
-
},
|
|
28
|
-
// @description read a data folder
|
|
29
|
-
readData: folder => {
|
|
30
|
-
const files = fs.readdirSync(folder, "utf8")
|
|
31
|
-
.filter(file => path.extname(file) === ".json")
|
|
32
|
-
.map(file => path.join(folder, file))
|
|
33
|
-
.map(file => {
|
|
34
|
-
return [path.basename(file, ".json"), JSON.parse(fs.readFileSync(file, "utf8"))];
|
|
35
|
-
});
|
|
36
|
-
return Object.fromEntries(files);
|
|
37
|
-
},
|
|
38
|
-
// @description get pages from input folder
|
|
39
|
-
readPages: (folder, type = ".html", fm = null, parse = null) => {
|
|
40
|
-
return fs.readdirSync(folder, "utf8")
|
|
41
|
-
.filter(file => path.extname(file) === type)
|
|
42
|
-
.map(file => {
|
|
43
|
-
const content = fs.readFileSync(path.join(folder, file), "utf8");
|
|
44
|
-
const {body, attributes} = utils.frontmatter(content, fm);
|
|
45
|
-
return {
|
|
46
|
-
name: file,
|
|
47
|
-
basename: path.basename(file, type),
|
|
48
|
-
extname: path.extname(file),
|
|
49
|
-
url: attributes?.permalink || path.join("/", path.basename(file, type) + ".html"),
|
|
50
|
-
data: attributes || {},
|
|
51
|
-
content: typeof parse === "function" ? parse(body) : body,
|
|
52
|
-
};
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
// @description get assets
|
|
56
|
-
readAssets: (folder, fm = null) => {
|
|
57
|
-
const assetPaths = fs.readdirSync(folder, "utf8");
|
|
58
|
-
return Object.fromEntries(assetPaths.map(file => {
|
|
59
|
-
const content = fs.readFileSync(path.join(folder, file), "utf8");
|
|
60
|
-
const {body, attributes} = utils.frontmatter(content, fm);
|
|
61
|
-
const asset = {
|
|
62
|
-
name: file,
|
|
63
|
-
basename: path.basename(file, path.extname(file)),
|
|
64
|
-
extname: path.extname(file),
|
|
65
|
-
url: attributes?.permalink || path.join("/", file),
|
|
66
|
-
data: attributes || {},
|
|
67
|
-
content: body,
|
|
68
|
-
};
|
|
69
|
-
const assetName = asset.basename.replaceAll(".", "_").replaceAll("-", "_");
|
|
70
|
-
return [assetName, asset];
|
|
71
|
-
}));
|
|
72
|
-
},
|
|
15
|
+
}
|
|
16
|
+
return {body, attributes};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// @description utility to save a file to disk
|
|
20
|
+
const saveFile = (filePath, fileContent) => {
|
|
21
|
+
const folder = path.dirname(filePath);
|
|
22
|
+
if (!fs.existsSync(folder)) {
|
|
23
|
+
fs.mkdirSync(folder, {recursive: true});
|
|
24
|
+
}
|
|
25
|
+
fs.writeFileSync(filePath, fileContent, "utf8");
|
|
73
26
|
};
|
|
74
27
|
|
|
75
28
|
// @description returns the layout content from the given options
|
|
@@ -84,20 +37,76 @@ const getLayoutContent = config => {
|
|
|
84
37
|
content = config.layoutContent || config.templateContent;
|
|
85
38
|
}
|
|
86
39
|
// parse with frontmatter
|
|
87
|
-
const {body, attributes} =
|
|
40
|
+
const {body, attributes} = frontmatter(content, config.frontmatter);
|
|
88
41
|
return {
|
|
89
42
|
content: body,
|
|
90
43
|
data: attributes || {},
|
|
91
44
|
};
|
|
92
45
|
};
|
|
93
46
|
|
|
47
|
+
// @description create a virtual page object from the given options
|
|
48
|
+
const createVirtualPage = (options = {}) => {
|
|
49
|
+
const content = options.content || fs.readFileSync(options.file, "utf8");
|
|
50
|
+
const extname = options.extname || path.extname(options.file || "") || ".html";
|
|
51
|
+
const basename = options.basename || path.basename(options.file || "", extname) || "virtual";
|
|
52
|
+
const {body, attributes} = frontmatter(content, options.frontmatter);
|
|
53
|
+
return {
|
|
54
|
+
name: basename + extname,
|
|
55
|
+
basename: basename,
|
|
56
|
+
extname: extname,
|
|
57
|
+
url: options.url || attributes?.permalink || path.join("/", basename + extname),
|
|
58
|
+
data: attributes || {}, // DEPRECATED
|
|
59
|
+
attributes: attributes || {},
|
|
60
|
+
content: typeof options.parse === "function" ? options.parse(body) : body,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// @description get pages from input folder
|
|
65
|
+
const readPages = (folder, extensions = ".html", fm = null, parse = null) => {
|
|
66
|
+
const extensionsList = new Set([extensions].flat());
|
|
67
|
+
return fs.readdirSync(folder, "utf8")
|
|
68
|
+
.filter(file => extensionsList.has(path.extname(file)))
|
|
69
|
+
.map(file => {
|
|
70
|
+
return createVirtualPage({
|
|
71
|
+
file: path.join(folder, file),
|
|
72
|
+
frontmatter: fm,
|
|
73
|
+
parse: parse,
|
|
74
|
+
extname: ".html",
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// @description get assets
|
|
80
|
+
const readAssets = (folder, fm = null) => {
|
|
81
|
+
const assetPaths = fs.readdirSync(folder, "utf8");
|
|
82
|
+
return Object.fromEntries(assetPaths.map(file => {
|
|
83
|
+
const asset = createVirtualPage({
|
|
84
|
+
file: path.join(folder, file),
|
|
85
|
+
frontmatter: fm,
|
|
86
|
+
});
|
|
87
|
+
const assetName = asset.basename.replaceAll(".", "_").replaceAll("-", "_");
|
|
88
|
+
return [assetName, asset];
|
|
89
|
+
}));
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// @description read a data folder
|
|
93
|
+
const readData = folder => {
|
|
94
|
+
const files = fs.readdirSync(folder, "utf8")
|
|
95
|
+
.filter(file => path.extname(file) === ".json")
|
|
96
|
+
.map(file => path.join(folder, file))
|
|
97
|
+
.map(file => {
|
|
98
|
+
return [path.basename(file, ".json"), JSON.parse(fs.readFileSync(file, "utf8"))];
|
|
99
|
+
});
|
|
100
|
+
return Object.fromEntries(files);
|
|
101
|
+
};
|
|
102
|
+
|
|
94
103
|
// @description plugins
|
|
95
104
|
const plugins = {
|
|
96
105
|
// plugin to read and include posts in markdown
|
|
97
|
-
posts: (
|
|
106
|
+
posts: (options = {}) => {
|
|
98
107
|
return context => {
|
|
99
108
|
context.hooks.beforeEmit.add(() => {
|
|
100
|
-
const posts =
|
|
109
|
+
const posts = readPages(path.join(context.source, options?.dir || "posts"), [".md"], context.site.frontmatter, options?.parser);
|
|
101
110
|
context.site.posts = posts; // posts will be accesible in site.posts
|
|
102
111
|
context.site.pages = [...context.site.pages, ...posts]; // posts will be included as pages also
|
|
103
112
|
});
|
|
@@ -113,7 +122,6 @@ const run = (config = {}) => {
|
|
|
113
122
|
site: config || {},
|
|
114
123
|
source: path.resolve(process.cwd(), config?.source || "."),
|
|
115
124
|
destination: path.resolve(process.cwd(), config?.destination || "./www"),
|
|
116
|
-
compiler: null,
|
|
117
125
|
layout: getLayoutContent(config),
|
|
118
126
|
hooks: Object.freeze(Object.fromEntries(hooks.map(name => {
|
|
119
127
|
return [name, new Set()];
|
|
@@ -126,31 +134,31 @@ const run = (config = {}) => {
|
|
|
126
134
|
}
|
|
127
135
|
dispatch("initialize", []);
|
|
128
136
|
// 2. initialize mikel instance
|
|
129
|
-
|
|
137
|
+
const compiler = mikel.create(context.layout.content, config?.mikel || {});
|
|
130
138
|
dispatch("compiler", [context.compiler]);
|
|
131
139
|
// 3. read stuff
|
|
132
|
-
context.site.data =
|
|
133
|
-
context.site.pages =
|
|
134
|
-
context.site.assets =
|
|
140
|
+
context.site.data = readData(path.join(context.source, config?.dataDir || "data"));
|
|
141
|
+
context.site.pages = readPages(path.join(context.source, config?.pagesDir || "pages"), ".html", config?.frontmatter, c => c);
|
|
142
|
+
context.site.assets = readAssets(path.join(context.source, config?.assetsDir || "assets"), config?.frontmatter);
|
|
135
143
|
dispatch("beforeEmit", []);
|
|
136
144
|
// 4. save pages
|
|
137
145
|
context.site.pages.forEach(page => {
|
|
138
|
-
|
|
139
|
-
const content =
|
|
146
|
+
compiler.addPartial("content", page.content); // register page content as partial
|
|
147
|
+
const content = compiler({
|
|
140
148
|
site: context.site,
|
|
141
149
|
layout: context.layout,
|
|
142
150
|
page: page,
|
|
143
151
|
});
|
|
144
152
|
dispatch("emitPage", [page, content]);
|
|
145
|
-
|
|
153
|
+
saveFile(path.join(context.destination, page.url), content);
|
|
146
154
|
});
|
|
147
155
|
// 5. save assets
|
|
148
156
|
Object.values(context.site.assets).forEach(asset => {
|
|
149
157
|
dispatch("emitAsset", [asset]);
|
|
150
|
-
|
|
158
|
+
saveFile(path.join(context.destination, asset.url), asset.content);
|
|
151
159
|
});
|
|
152
160
|
dispatch("done", []);
|
|
153
161
|
};
|
|
154
162
|
|
|
155
163
|
// export
|
|
156
|
-
export default {
|
|
164
|
+
export default {run, createVirtualPage, frontmatter, plugins};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikel-press",
|
|
3
3
|
"description": "A minimal static site generator based on mikel templating",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Josemi Juanes",
|
|
@@ -12,16 +12,12 @@
|
|
|
12
12
|
"bugs": "https://github.com/jmjuanes/mikel/issues",
|
|
13
13
|
"main": "index.js",
|
|
14
14
|
"module": "index.js",
|
|
15
|
+
"types": "index.d.ts",
|
|
15
16
|
"exports": {
|
|
16
17
|
".": "./index.js",
|
|
17
18
|
"./index.js": "./index.js",
|
|
18
|
-
"./cli.js": "./cli.js",
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
|
-
"bin": {
|
|
22
|
-
"mikelpress": "./cli.js",
|
|
23
|
-
"mikel-press": "./cli.js"
|
|
24
|
-
},
|
|
25
21
|
"scripts": {
|
|
26
22
|
"test": "node test.js"
|
|
27
23
|
},
|
|
@@ -39,6 +35,6 @@
|
|
|
39
35
|
"files": [
|
|
40
36
|
"README.md",
|
|
41
37
|
"index.js",
|
|
42
|
-
"
|
|
38
|
+
"index.d.ts"
|
|
43
39
|
]
|
|
44
40
|
}
|
package/cli.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import {parseArgs} from "node:util";
|
|
5
|
-
import mikelPress from "./index.js";
|
|
6
|
-
|
|
7
|
-
// get the configuration file from the provided path
|
|
8
|
-
const resolveConfig = value => {
|
|
9
|
-
const configPath = path.resolve(process.cwd(), value || "./press.config.js");
|
|
10
|
-
return import(configPath).then(config => {
|
|
11
|
-
return config?.default || {};
|
|
12
|
-
});
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// available commands
|
|
16
|
-
const commands = {
|
|
17
|
-
build: {
|
|
18
|
-
description: "Generate the static site with the provided configuration.",
|
|
19
|
-
execute: async values => {
|
|
20
|
-
const config = await resolveConfig(values.config);
|
|
21
|
-
return mikelPress.run(config);
|
|
22
|
-
},
|
|
23
|
-
options: {
|
|
24
|
-
config: {
|
|
25
|
-
type: "string",
|
|
26
|
-
short: "c",
|
|
27
|
-
default: "press.config.js",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const main = async (args = []) => {
|
|
34
|
-
const [commandName, ...otherArguments] = args;
|
|
35
|
-
if (typeof commands[commandName] !== "undefined") {
|
|
36
|
-
const {values} = parseArgs({
|
|
37
|
-
args: otherArguments,
|
|
38
|
-
options: commands[commandName].options || {},
|
|
39
|
-
});
|
|
40
|
-
return commands[commandName].execute(values);
|
|
41
|
-
}
|
|
42
|
-
// if this command is not available, print help [TODO]
|
|
43
|
-
// return commands.help.execute();
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// run
|
|
47
|
-
main(process.argv.slice(2));
|