basecampjs 0.0.2 → 0.0.4

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 (2) hide show
  1. package/index.js +63 -13
  2. package/package.json +4 -2
package/index.js CHANGED
@@ -11,6 +11,8 @@ import matter from "gray-matter";
11
11
  import MarkdownIt from "markdown-it";
12
12
  import nunjucks from "nunjucks";
13
13
  import { Liquid } from "liquidjs";
14
+ import { minify as minifyCss } from "csso";
15
+ import { minify as minifyHtml } from "html-minifier-terser";
14
16
 
15
17
  const cwd = process.cwd();
16
18
  const md = new MarkdownIt({ html: true, linkify: true, typographer: true });
@@ -21,6 +23,8 @@ const defaultConfig = {
21
23
  outDir: "dist",
22
24
  templateEngine: "nunjucks",
23
25
  markdown: true,
26
+ minifyCSS: false,
27
+ minifyHTML: false,
24
28
  integrations: { nunjucks: true, liquid: false, vue: false, alpine: false }
25
29
  };
26
30
 
@@ -69,6 +73,45 @@ async function copyPublic(publicDir, outDir) {
69
73
  }
70
74
  }
71
75
 
76
+ async function minifyCSSFiles(outDir) {
77
+ const files = await walkFiles(outDir);
78
+ const cssFiles = files.filter((file) => extname(file).toLowerCase() === ".css");
79
+
80
+ await Promise.all(cssFiles.map(async (file) => {
81
+ try {
82
+ const css = await readFile(file, "utf8");
83
+ const { css: minified } = minifyCss(css);
84
+ await writeFile(file, minified, "utf8");
85
+ } catch (err) {
86
+ console.error(kolor.red(`Failed to minify CSS ${relative(outDir, file)}: ${err.message}`));
87
+ }
88
+ }));
89
+ }
90
+
91
+ async function minifyHTMLFiles(outDir, config) {
92
+ const files = await walkFiles(outDir);
93
+ const htmlFiles = files.filter((file) => extname(file).toLowerCase() === ".html");
94
+
95
+ await Promise.all(htmlFiles.map(async (file) => {
96
+ try {
97
+ const html = await readFile(file, "utf8");
98
+ const minified = await minifyHtml(html, {
99
+ collapseWhitespace: true,
100
+ removeComments: true,
101
+ minifyCSS: !!config.minifyCSS,
102
+ minifyJS: true,
103
+ keepClosingSlash: true,
104
+ removeRedundantAttributes: true,
105
+ removeScriptTypeAttributes: true,
106
+ removeStyleLinkTypeAttributes: true
107
+ });
108
+ await writeFile(file, minified, "utf8");
109
+ } catch (err) {
110
+ console.error(kolor.red(`Failed to minify HTML ${relative(outDir, file)}: ${err.message}`));
111
+ }
112
+ }));
113
+ }
114
+
72
115
  async function walkFiles(dir) {
73
116
  const results = [];
74
117
  if (!existsSync(dir)) return results;
@@ -111,15 +154,11 @@ function toUrlPath(outRel) {
111
154
  return `/${normalized}`;
112
155
  }
113
156
 
114
- function pageContext(frontmatter, html, config, relPath, data, url = "/") {
157
+ function pageContext(frontmatter, html, config, relPath, data, path = "/") {
115
158
  return {
116
159
  site: { name: config.siteName, config },
117
- page: { ...frontmatter, content: html, source: relPath, url },
118
- frontmatter,
119
- content: html,
120
- data,
121
- collections: data,
122
- ...data
160
+ page: { ...frontmatter, content: html, source: relPath, path },
161
+ collections: data
123
162
  };
124
163
  }
125
164
 
@@ -133,8 +172,9 @@ async function renderWithLayout(layoutName, html, ctx, env, liquidEnv) {
133
172
  const ext = extname(layoutName).toLowerCase();
134
173
  const layoutCtx = {
135
174
  ...ctx,
175
+ frontmatter: ctx.page || {},
136
176
  content: html,
137
- title: ctx.frontmatter?.title ?? ctx.page?.title ?? ctx.site?.name
177
+ title: ctx.page?.title ?? ctx.site?.name
138
178
  };
139
179
 
140
180
  if (ext === ".njk") {
@@ -154,14 +194,14 @@ async function renderPage(filePath, { pagesDir, layoutsDir, outDir, env, liquidE
154
194
  const ext = extname(filePath).toLowerCase();
155
195
  const outRel = rel.replace(/\.liquid(\.html)?$/i, ".html").replace(ext, ".html");
156
196
  const outPath = join(outDir, outRel);
157
- const url = toUrlPath(outRel);
197
+ const path = toUrlPath(outRel);
158
198
  await ensureDir(dirname(outPath));
159
199
 
160
200
  if (ext === ".md") {
161
201
  const raw = await readFile(filePath, "utf8");
162
202
  const parsed = matter(raw);
163
203
  const html = md.render(parsed.content);
164
- const ctx = pageContext(parsed.data, html, config, rel, data, url);
204
+ const ctx = pageContext(parsed.data, html, config, rel, data, path);
165
205
  const rendered = await renderWithLayout(parsed.data.layout, html, ctx, env, liquidEnv);
166
206
  await writeFile(outPath, rendered, "utf8");
167
207
  return;
@@ -170,7 +210,7 @@ async function renderPage(filePath, { pagesDir, layoutsDir, outDir, env, liquidE
170
210
  if (ext === ".njk") {
171
211
  const raw = await readFile(filePath, "utf8");
172
212
  const parsed = matter(raw);
173
- const ctx = pageContext(parsed.data, parsed.content, config, rel, data, url);
213
+ const ctx = pageContext(parsed.data, parsed.content, config, rel, data, path);
174
214
  const templateName = rel.replace(/\\/g, "/");
175
215
  let pageHtml = env.renderString(parsed.content, ctx, { path: templateName });
176
216
  if (shouldRenderMarkdown(parsed.data, config, false)) {
@@ -184,7 +224,7 @@ async function renderPage(filePath, { pagesDir, layoutsDir, outDir, env, liquidE
184
224
  if (ext === ".liquid" || filePath.toLowerCase().endsWith(".liquid.html")) {
185
225
  const raw = await readFile(filePath, "utf8");
186
226
  const parsed = matter(raw);
187
- const ctx = pageContext(parsed.data, parsed.content, config, rel, data, url);
227
+ const ctx = pageContext(parsed.data, parsed.content, config, rel, data, path);
188
228
  let pageHtml = await liquidEnv.parseAndRender(parsed.content, ctx);
189
229
  if (shouldRenderMarkdown(parsed.data, config, false)) {
190
230
  pageHtml = md.render(pageHtml);
@@ -197,7 +237,7 @@ async function renderPage(filePath, { pagesDir, layoutsDir, outDir, env, liquidE
197
237
  if (ext === ".html") {
198
238
  const raw = await readFile(filePath, "utf8");
199
239
  const parsed = matter(raw);
200
- const ctx = pageContext(parsed.data, parsed.content, config, rel, data, url);
240
+ const ctx = pageContext(parsed.data, parsed.content, config, rel, data, path);
201
241
  let pageHtml = parsed.content;
202
242
  if (shouldRenderMarkdown(parsed.data, config, false)) {
203
243
  pageHtml = md.render(pageHtml);
@@ -233,6 +273,16 @@ async function build(cwdArg = cwd) {
233
273
 
234
274
  await Promise.all(files.map((file) => renderPage(file, { pagesDir, layoutsDir, outDir, env, liquidEnv, config, data })));
235
275
 
276
+ if (config.minifyCSS) {
277
+ await minifyCSSFiles(outDir);
278
+ console.log(kolor.green("CSS minified"));
279
+ }
280
+
281
+ if (config.minifyHTML) {
282
+ await minifyHTMLFiles(outDir, config);
283
+ console.log(kolor.green("HTML minified"));
284
+ }
285
+
236
286
  console.log(kolor.green(`Built ${files.length} page(s) → ${relative(cwdArg, outDir)}`));
237
287
  }
238
288
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "basecampjs",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
- "description": "BasecampJS engine for Campsite static site generator.",
5
+ "description": "BasecampJS engine for CampsiteJS static site generator.",
6
6
  "bin": {
7
7
  "campsite": "./index.js"
8
8
  },
@@ -11,6 +11,8 @@
11
11
  ".": "./index.js"
12
12
  },
13
13
  "dependencies": {
14
+ "csso": "^5.0.5",
15
+ "html-minifier-terser": "^7.2.0",
14
16
  "chokidar": "^3.6.0",
15
17
  "gray-matter": "^4.0.3",
16
18
  "kolorist": "^1.8.0",