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.
Files changed (4) hide show
  1. package/index.d.ts +50 -0
  2. package/index.js +88 -80
  3. package/package.json +3 -7
  4. 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
- // 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});
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
- 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
- },
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} = utils.frontmatter(content, config.frontmatter);
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: (parser = null) => {
106
+ posts: (options = {}) => {
98
107
  return context => {
99
108
  context.hooks.beforeEmit.add(() => {
100
- const posts = utils.readPages(path.join(context.source, "posts"), ".md", context.site.frontmatter, parser);
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
- context.compiler = mikel.create(context.layout.content, config?.mikel || {});
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 = 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);
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
- context.compiler.addPartial("content", page.content); // register page content as partial
139
- const content = context.compiler({
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
- utils.saveFile(path.join(context.destination, page.url), content);
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
- utils.saveFile(path.join(context.destination, asset.url), asset.content);
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 {utils, plugins, run};
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.1",
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
- "cli.js"
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));