mikel-press 0.0.1
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/README.md +52 -0
- package/cli.js +47 -0
- package/index.js +156 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# mikel-press
|
|
2
|
+
|
|
3
|
+
**mikel-press** is a tiny static site generator inspired by Jekyll that uses **mikel** as its templating engine. It provides a simple and efficient way to build static websites with customizable layouts and content.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
To install **mikel-press**, ensure you have [Node.js](https://nodejs.org) installed on your system. Then, add this package as a dependency to your project using **yarn**:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
$ yarn add --dev mikel-press
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or **npm**:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
$ npm install --dev mikel-press
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Building the Site
|
|
22
|
+
|
|
23
|
+
Run the `build` command to generate your static website:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
$ mikel-press build
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The generated files will be available in the `www` directory by default.
|
|
30
|
+
|
|
31
|
+
### (WIP) Starting a Development Server
|
|
32
|
+
|
|
33
|
+
To preview your site locally, use the `serve` command:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
mikel-press serve
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This will start a local server at `http://localhost:4000`.
|
|
40
|
+
|
|
41
|
+
## (WIP) Configuration
|
|
42
|
+
|
|
43
|
+
Create a file called `press.config.js` with the configuration.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Contributing
|
|
47
|
+
|
|
48
|
+
Contributions are welcome! If you have ideas or find a bug, feel free to open an issue or submit a pull request.
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
This project is licensed under the MIT License.
|
package/cli.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
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));
|
package/index.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import mikel from "mikel";
|
|
4
|
+
|
|
5
|
+
// global utils
|
|
6
|
+
const utils = {
|
|
7
|
+
// @description save file
|
|
8
|
+
saveFile: (filePath, fileContent) => {
|
|
9
|
+
const folder = path.dirname(filePath);
|
|
10
|
+
if (!fs.existsSync(folder)) {
|
|
11
|
+
fs.mkdirSync(folder, {recursive: true});
|
|
12
|
+
}
|
|
13
|
+
fs.writeFileSync(filePath, fileContent, "utf8");
|
|
14
|
+
},
|
|
15
|
+
// @description tiny front-matter parser
|
|
16
|
+
frontmatter: (str = "", parser = null) => {
|
|
17
|
+
let body = (str || "").trim(), attributes = {};
|
|
18
|
+
if (!!parser) {
|
|
19
|
+
const matches = Array.from(body.matchAll(/^(--- *)/gm))
|
|
20
|
+
if (matches?.length === 2 && matches[0].index === 0) {
|
|
21
|
+
const front = body.substring(0 + matches[0][1].length, matches[1].index).trim();
|
|
22
|
+
body = body.substring(matches[1].index + matches[1][1].length).trim();
|
|
23
|
+
attributes = typeof parser === "function" ? parser(front) : front;
|
|
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
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// @description returns the layout content from the given options
|
|
76
|
+
const getLayoutContent = config => {
|
|
77
|
+
let content = "";
|
|
78
|
+
// using options.template to specify the the absolute path to the template file
|
|
79
|
+
if (typeof config?.layout === "string" || typeof config?.template === "string") {
|
|
80
|
+
content = fs.readFileSync(config.layout || config.template, "utf8");
|
|
81
|
+
}
|
|
82
|
+
// using templateContent to specify the string content of the template
|
|
83
|
+
if (typeof config?.layoutContent === "string" || typeof config?.templateContent === "string") {
|
|
84
|
+
content = config.layoutContent || config.templateContent;
|
|
85
|
+
}
|
|
86
|
+
// parse with frontmatter
|
|
87
|
+
const {body, attributes} = utils.frontmatter(content, config.frontmatter);
|
|
88
|
+
return {
|
|
89
|
+
content: body,
|
|
90
|
+
data: attributes || {},
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// @description plugins
|
|
95
|
+
const plugins = {
|
|
96
|
+
// plugin to read and include posts in markdown
|
|
97
|
+
posts: (parser = null) => {
|
|
98
|
+
return context => {
|
|
99
|
+
context.hooks.beforeEmit.add(() => {
|
|
100
|
+
const posts = utils.readPages(path.join(context.source, "posts"), ".md", context.site.frontmatter, parser);
|
|
101
|
+
context.site.posts = posts; // posts will be accesible in site.posts
|
|
102
|
+
context.site.pages = [...context.site.pages, ...posts]; // posts will be included as pages also
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// @description run mikel press with the provided configuration
|
|
109
|
+
const run = (config = {}) => {
|
|
110
|
+
// 0. initialize context object
|
|
111
|
+
const hooks = ["initialize", "compiler", "beforeEmit", "emitPage", "emitAsset", "done"];
|
|
112
|
+
const context = {
|
|
113
|
+
site: config || {},
|
|
114
|
+
source: path.resolve(process.cwd(), config?.source || "."),
|
|
115
|
+
destination: path.resolve(process.cwd(), config?.destination || "./www"),
|
|
116
|
+
compiler: null,
|
|
117
|
+
layout: getLayoutContent(config),
|
|
118
|
+
hooks: Object.freeze(Object.fromEntries(hooks.map(name => {
|
|
119
|
+
return [name, new Set()];
|
|
120
|
+
}))),
|
|
121
|
+
};
|
|
122
|
+
const dispatch = (name, args) => Array.from(context.hooks[name]).forEach(fn => fn.apply(null, args));
|
|
123
|
+
// 1. execute plugins
|
|
124
|
+
if (config?.plugins && Array.isArray(config?.plugins)) {
|
|
125
|
+
config.plugins.forEach(plugin => plugin(context));
|
|
126
|
+
}
|
|
127
|
+
dispatch("initialize", []);
|
|
128
|
+
// 2. initialize mikel instance
|
|
129
|
+
context.compiler = mikel.create(context.layout.content, config?.mikel || {});
|
|
130
|
+
dispatch("compiler", [context.compiler]);
|
|
131
|
+
// 3. read stuff
|
|
132
|
+
context.site.data = utils.readData(path.join(context.source, config?.dataDir || "data"));
|
|
133
|
+
context.site.pages = utils.readPages(path.join(context.source, config?.pagesDir || "pages"), ".html", config?.frontmatter ?? true, c => c);
|
|
134
|
+
context.site.assets = utils.readAssets(path.join(context.source, config?.assetsDir || "assets"), config?.frontmatter ?? true);
|
|
135
|
+
dispatch("beforeEmit", []);
|
|
136
|
+
// 4. save pages
|
|
137
|
+
context.site.pages.forEach(page => {
|
|
138
|
+
context.compiler.addPartial("content", page.content); // register page content as partial
|
|
139
|
+
const content = context.compiler({
|
|
140
|
+
site: context.site,
|
|
141
|
+
layout: context.layout,
|
|
142
|
+
page: page,
|
|
143
|
+
});
|
|
144
|
+
dispatch("emitPage", [page, content]);
|
|
145
|
+
utils.saveFile(path.join(context.destination, page.url), content);
|
|
146
|
+
});
|
|
147
|
+
// 5. save assets
|
|
148
|
+
Object.values(context.site.assets).forEach(asset => {
|
|
149
|
+
dispatch("emitAsset", [asset]);
|
|
150
|
+
utils.saveFile(path.join(context.destination, asset.url), asset.content);
|
|
151
|
+
});
|
|
152
|
+
dispatch("done", []);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// export
|
|
156
|
+
export default {utils, plugins, run};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mikel-press",
|
|
3
|
+
"description": "A minimal static site generator based on mikel templating",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Josemi Juanes",
|
|
8
|
+
"email": "hello@josemi.xyz"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": "https://github.com/jmjuanes/mikel",
|
|
12
|
+
"bugs": "https://github.com/jmjuanes/mikel/issues",
|
|
13
|
+
"main": "index.js",
|
|
14
|
+
"module": "index.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./index.js",
|
|
17
|
+
"./index.js": "./index.js",
|
|
18
|
+
"./cli.js": "./cli.js",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"mikelpress": "./cli.js",
|
|
23
|
+
"mikel-press": "./cli.js"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "node test.js"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"static",
|
|
30
|
+
"site",
|
|
31
|
+
"generator",
|
|
32
|
+
"mikel",
|
|
33
|
+
"template",
|
|
34
|
+
"templating"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"mikel": "^0.17.0"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"README.md",
|
|
41
|
+
"index.js",
|
|
42
|
+
"cli.js"
|
|
43
|
+
]
|
|
44
|
+
}
|