onlybuild 1.0.0 → 1.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## [v1.2.0](https://github.com/neogeek/onlybuild/tree/v1.2.0) - (2024-05-07)
4
+
5
+ [Full Changelog](https://github.com/neogeek/onlybuild/compare/v1.1.0...v1.2.0)
6
+
7
+ - [feat] Added .env support. [#10](https://github.com/neogeek/onlybuild/pull/10)
8
+ - [hotfix] Added a prepare build target. [#9](https://github.com/neogeek/onlybuild/pull/9)
9
+ - [hotfix] Fix permissions on bin file after build. [#8](https://github.com/neogeek/onlybuild/pull/8)
10
+ - [feat] Converted project to typescript. [#7](https://github.com/neogeek/onlybuild/pull/7)
11
+ - [hotfix] Updated packages. [#6](https://github.com/neogeek/onlybuild/pull/6)
12
+ - [hotfix] Disable gitignore in globby. [#3](https://github.com/neogeek/onlybuild/pull/3)
13
+
14
+ ## [v1.1.0](https://github.com/neogeek/onlybuild/tree/v1.1.0) - (2024-04-26)
15
+
16
+ [Full Changelog](https://github.com/neogeek/onlybuild/compare/v1.0.0...v1.1.0)
17
+
18
+ - [feat] Added input arg and version, help and out flags. [#2](https://github.com/neogeek/onlybuild/pull/2)
19
+ - [feat] Broke out build and copy methods into smaller ones. [#1](https://github.com/neogeek/onlybuild/pull/1)
20
+
21
+ ## [v1.0.0](https://github.com/neogeek/onlybuild/tree/v1.0.0) - (2024-04-23)
22
+
23
+ - Initial release! 🎉
24
+
25
+ _This changelog was generated with **[generate-local-changelog](https://github.com/neogeek/generate-local-changelog)**_
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  [![Test](https://github.com/neogeek/onlybuild/actions/workflows/test.workflow.yml/badge.svg)](https://github.com/neogeek/onlybuild/actions/workflows/test.workflow.yml)
6
6
  [![Publish](https://github.com/neogeek/onlybuild/actions/workflows/publish.workflow.yml/badge.svg)](https://github.com/neogeek/onlybuild/actions/workflows/publish.workflow.yml)
7
+ [![Documentation](https://doxdox.org/images/badge-flat.svg)](https://doxdox.org/neogeek/onlybuild)
7
8
  [![NPM version](https://img.shields.io/npm/v/onlybuild)](https://www.npmjs.org/package/onlybuild)
8
9
  [![Join the chat at https://discord.gg/nNtFsfd](https://img.shields.io/badge/discord-join%20chat-7289DA.svg)](https://discord.gg/nNtFsfd)
9
10
 
@@ -26,11 +27,14 @@
26
27
  ## Table of Contents
27
28
 
28
29
  - [Install](#install)
30
+ - [Usage](#usage)
29
31
  - [Quick Start Guide](#quick-start-guide)
30
32
  - [Getting Started](#getting-started)
31
33
  - [File Structure](#file-structure)
32
34
  - [Ignore Files](#ignore-files)
33
35
  - [Formatting Files](#formatting-files)
36
+ - [Watching For Changes](#watching-for-changes)
37
+ - [Local Server](#local-server)
34
38
  - [Examples](#examples)
35
39
  - [Benchmark](#benchmark)
36
40
  - [Testing](#testing)
@@ -76,6 +80,19 @@ $ npm install onlybuild --save-dev
76
80
  $ npm run build
77
81
  ```
78
82
 
83
+ ## Usage
84
+
85
+ ```bash
86
+ Usage: onlybuild <path> [options]
87
+
88
+ Options:
89
+
90
+ -h, --help Display this help message.
91
+ -v, --version Display the current installed version.
92
+ -o, --out Sets build directory. Default path is build/
93
+ -i, --ignore Sets ignore file path. Default path is .onlyignore
94
+ ```
95
+
79
96
  ## Quick Start Guide
80
97
 
81
98
  Create a new file `index.mjs` with the following contents:
@@ -142,6 +159,31 @@ import { marked } from 'marked';
142
159
  export default marked.parse(await readFile('index.md', 'utf8'));
143
160
  ```
144
161
 
162
+ ### Parse Markdown w/ Syntax Highlighting
163
+
164
+ This Markdown example parses code blocks and adds CSS classes before rendering the page to HTML.
165
+
166
+ ```javascript
167
+ import { readFile } from 'node:fs/promises';
168
+
169
+ import { Marked } from 'marked';
170
+
171
+ import { markedHighlight } from 'marked-highlight';
172
+ import hljs from 'highlight.js';
173
+
174
+ const marked = new Marked(
175
+ markedHighlight({
176
+ langPrefix: 'hljs language-',
177
+ highlight(code, lang, info) {
178
+ const language = hljs.getLanguage(lang) ? lang : 'plaintext';
179
+ return hljs.highlight(code, { language }).value;
180
+ }
181
+ })
182
+ );
183
+
184
+ export default marked.parse(await readFile('index.md', 'utf8'));
185
+ ```
186
+
145
187
  ### <code>&#96;html&#96;</code> String Template Utility
146
188
 
147
189
  The `onlybuild` library includes an optional <code>&#96;html&#96;</code> string template utility that can be used to add syntax highlighting and formatting to HTML, making it easier to author HTML in JavaScript.
@@ -156,14 +198,62 @@ Install the [lit-html](https://marketplace.visualstudio.com/items?itemName=biern
156
198
 
157
199
  ## File Structure
158
200
 
159
- When you run `npx onlybuild` the default export of all `.mjs` file will be captured and written to files in a `build/` directory.
201
+ When you run `npx onlybuild`, all `.mjs` files with a `export default` that returns a string will be captured and written to the `build/` directory. All other files will be copied with the same file structure to the `build` directory unless included in the `.onlyignore` file or in a default ignored directory, indicated by a leading `_` character.
160
202
 
161
203
  If the name of your `.mjs` file is `index.mjs` the output will be saved to `index.html`, but if it's name is `something-else.mjs` the output will be saved to `something-else/index.mjs`.
162
204
 
205
+ See the example file structure below for a more comprehensive example that includes building files and copying static files.
206
+
207
+ <table>
208
+ <tr>
209
+ <th>
210
+ Files
211
+ </v>
212
+ <th>
213
+ Build Output
214
+ </th>
215
+ <tr>
216
+ <td>
217
+
218
+ ```
219
+ ├── _includes
220
+ │ └── head.mjs
221
+ ├── about
222
+ │ └── index.mjs
223
+ ├── blog
224
+ │ └── hello-world.mjs
225
+ ├── css
226
+ │ └── styles.css
227
+ ├── images
228
+ │ └── icon.png
229
+ └── index.mjs
230
+ ```
231
+
232
+ </td>
233
+ <td>
234
+
235
+ ```
236
+ ├── index.html
237
+ ├── css
238
+ │ └── styles.css
239
+ ├── images
240
+ │ └── icon.png
241
+ ├── about
242
+ │ └── index.html
243
+ └── blog
244
+ └── hello-world
245
+ └── index.html
246
+
247
+ ```
248
+
249
+ </td></tr></table>
250
+
163
251
  ## Ignore Files
164
252
 
165
253
  If you want to ignore files from being generated into static files or copied into the `build.` directory you can add them to an ignore file called `.onlyignore`, which has a syntax similar to [`.gitignore`](https://git-scm.com/docs/gitignore) files.
166
254
 
255
+ As stated in the previous section, any files in a directory with a leading `_` character will be automatically ignored. Example: `_includes` or `_data`.
256
+
167
257
  ```
168
258
  *.md
169
259
 
@@ -174,16 +264,60 @@ LICENSE
174
264
 
175
265
  ## Formatting Files
176
266
 
177
- Originally, [Prettier](https://prettier.io/) was included in the build step, but it caused the build time to balloon to 3x its current time. Because of this, it was removed and is recommended to run after the build step, or not at all if it's not needed.
267
+ If you want to reformat the HTML files in the build directory, you can use [Prettier](https://prettier.io/) after the build completes.
178
268
 
179
- ```bash
180
- $ npx prettier --write "build/**/*.html"
269
+ ```json
270
+ {
271
+ ...
272
+ "scripts": {
273
+ "build": "onlybuild",
274
+ "format": "npx prettier --write \"build/**/*.html\""
275
+ },
276
+ ...
277
+ }
181
278
  ```
182
279
 
183
280
  If your `build/` directory is in `.gitignore` (which it probably should be) you will need to ignore the `.gitignore` file by setting the `--ignore-path` flag to something else. The file you set it to doesn't need to exist.
184
281
 
185
- ```bash
186
- $ npx prettier --write --ignore-path .prettierignore "build/**/*.html"
282
+ ```json
283
+ {
284
+ ...
285
+ "scripts": {
286
+ "build": "onlybuild",
287
+ "format": "npx prettier --write --ignore-path .prettierignore \"build/**/*.html\""
288
+ },
289
+ ...
290
+ }
291
+ ```
292
+
293
+ ## Watching For Changes
294
+
295
+ If you want to automatically rebuild the project when files are updated you can use [nodemon](https://nodemon.io/).
296
+
297
+ ```json
298
+ {
299
+ ...
300
+ "scripts": {
301
+ "build": "onlybuild",
302
+ "watch": "npx nodemon --ext mjs,md,css --ignore ./build -x \"npm run build\""
303
+ },
304
+ ...
305
+ }
306
+ ```
307
+
308
+ ## Local Server
309
+
310
+ Serving the files once the build is complete is easy using the NPM package [http-server](https://github.com/http-party/http-server).
311
+
312
+ ```json
313
+ {
314
+ ...
315
+ "scripts": {
316
+ "build": "onlybuild",
317
+ "serve": "npx http-server build"
318
+ },
319
+ ...
320
+ }
187
321
  ```
188
322
 
189
323
  ## Examples
@@ -197,13 +331,13 @@ $ npx prettier --write --ignore-path .prettierignore "build/**/*.html"
197
331
  ## Benchmark
198
332
 
199
333
  > [!NOTE]
200
- > Each run (for onlybuild only) was repeated 5 times and the average time was selected. This result set was generated on a MacBook Air (M1, 2020), macOS Sonoma 14.4.1, 8 GB memory.
334
+ > Each run (for `onlybuild` only) was repeated 5 times and the lowest/fastest time was selected. This result set was generated on a MacBook Air (M1, 2020), macOS Sonoma 14.4.1, 8 GB memory.
201
335
 
202
336
  Times shown are in seconds. Lower is better.
203
337
 
204
338
  | Markdown Files | 250 | 500 | 1000 | 2000 | 4000 |
205
339
  | ---------------------------------------------- | -------: | -------: | -------: | -------: | -------: |
206
- | onlybuild | `0.321` | `0.390` | `0.554` | `0.892` | `1.717` |
340
+ | onlybuild | `0.296` | `0.356` | `0.512` | `0.770` | `1.309` |
207
341
  | [Hugo](https://gohugo.io/) v0.101.0 | `0.071` | `0.110` | `0.171` | `0.352` | `0.684` |
208
342
  | [Eleventy](https://www.11ty.dev/) 1.0.1 | `0.584` | `0.683` | `0.914` | `1.250` | `1.938` |
209
343
  | [Astro](https://astro.build/) 1.0.1 | `2.270` | `3.172` | `5.098` | `9.791` | `22.907` |
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import 'dotenv/config';
3
+ export {};
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import 'dotenv/config';
6
+ import { globby } from 'globby';
7
+ import parseCmdArgs from 'parse-cmd-args';
8
+ import { buildFiles } from '../src/build.js';
9
+ import { copyFiles } from '../src/copy.js';
10
+ const args = parseCmdArgs(null, {
11
+ requireUserInput: false
12
+ });
13
+ if (args.flags['--version'] || args.flags['-v']) {
14
+ process.stdout.write(`${JSON.parse(await readFile(join(dirname(fileURLToPath(import.meta.url)), '../package.json'), 'utf8')).version}\n`);
15
+ process.exit();
16
+ }
17
+ else if (args.flags['--help'] || args.flags['-h']) {
18
+ process.stdout.write(`Usage: onlybuild <path> [options]
19
+
20
+ Options:
21
+
22
+ -h, --help Display this help message.
23
+ -v, --version Display the current installed version.
24
+ -o, --out Sets build directory. Default path is build/
25
+ -i, --ignore Sets ignore file path. Default path is .onlyignore
26
+ `);
27
+ process.exit();
28
+ }
29
+ const [buildDir = 'build/'] = [args.flags['--out'], args.flags['-o']]
30
+ .filter(flag => typeof flag === 'string')
31
+ .map(String);
32
+ const [ignoreFile = '.onlyignore'] = [args.flags['--ignore'], args.flags['-i']]
33
+ .filter(flag => typeof flag === 'string')
34
+ .map(String);
35
+ await buildFiles(await globby(['**/*.mjs', '!_*/**/*', '!node_modules/', `!${buildDir}`], {
36
+ gitignore: false,
37
+ ignoreFiles: [ignoreFile],
38
+ cwd: args.inputs[0]
39
+ }), buildDir);
40
+ await copyFiles(await globby([
41
+ '**/*',
42
+ '!**/*.mjs',
43
+ '!_*/**/*',
44
+ '!package.json',
45
+ '!package-lock.json',
46
+ '!node_modules/',
47
+ `!${buildDir}`
48
+ ], {
49
+ gitignore: false,
50
+ ignoreFiles: [ignoreFile],
51
+ cwd: args.inputs[0]
52
+ }), buildDir);
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Write the file to the build directory while creating the directory, recursively, if it doesn't exist.
3
+ *
4
+ * @param {string} path
5
+ * @param {string} buildDir
6
+ * @param {string} contents
7
+ */
8
+ export declare const writeFileAndMakeDir: (path: string, buildDir: string, contents: string) => Promise<void>;
9
+ /**
10
+ * Import the default export of a JavaScript file and check that the default export is a string before writing the contents to a build file.
11
+ *
12
+ * @param {string} path
13
+ * @param {string} buildDir
14
+ */
15
+ export declare const buildFile: (path: string, buildDir: string) => Promise<void>;
16
+ /**
17
+ * Iterate over all the file paths and write them to the build directory.
18
+ *
19
+ * @param {string[]} paths
20
+ * @param {string} buildDir
21
+ */
22
+ export declare const buildFiles: (paths: string[], buildDir: string) => Promise<void>;
@@ -0,0 +1,38 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { basename, dirname, join, resolve } from 'node:path';
3
+ /**
4
+ * Write the file to the build directory while creating the directory, recursively, if it doesn't exist.
5
+ *
6
+ * @param {string} path
7
+ * @param {string} buildDir
8
+ * @param {string} contents
9
+ */
10
+ export const writeFileAndMakeDir = async (path, buildDir, contents) => {
11
+ const filename = basename(path, '.mjs');
12
+ const directory = filename === 'index'
13
+ ? join(buildDir, dirname(path))
14
+ : join(buildDir, dirname(path), filename);
15
+ await mkdir(directory, { recursive: true });
16
+ await writeFile(join(directory, 'index.html'), contents);
17
+ };
18
+ /**
19
+ * Import the default export of a JavaScript file and check that the default export is a string before writing the contents to a build file.
20
+ *
21
+ * @param {string} path
22
+ * @param {string} buildDir
23
+ */
24
+ export const buildFile = async (path, buildDir) => {
25
+ const contents = (await import(resolve(path))).default;
26
+ if (typeof contents === 'string') {
27
+ await writeFileAndMakeDir(path, buildDir, contents);
28
+ }
29
+ };
30
+ /**
31
+ * Iterate over all the file paths and write them to the build directory.
32
+ *
33
+ * @param {string[]} paths
34
+ * @param {string} buildDir
35
+ */
36
+ export const buildFiles = async (paths, buildDir) => {
37
+ await Promise.all(paths.map(async (path) => await buildFile(path, buildDir)));
38
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copy the file to the build directory while creating the directory, recursively, if it doesn't exist.
3
+ *
4
+ * @param {string} path
5
+ * @param {string} buildDir
6
+ */
7
+ export declare const copyFileAndMakeDir: (path: string, buildDir: string) => Promise<void>;
8
+ /**
9
+ * Iterate over all the file paths and copy them to the build directory.
10
+ *
11
+ * @param {string[]} paths
12
+ * @param {string} buildDir
13
+ */
14
+ export declare const copyFiles: (paths: string[], buildDir: string) => Promise<void>;
@@ -0,0 +1,21 @@
1
+ import { copyFile, mkdir } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ /**
4
+ * Copy the file to the build directory while creating the directory, recursively, if it doesn't exist.
5
+ *
6
+ * @param {string} path
7
+ * @param {string} buildDir
8
+ */
9
+ export const copyFileAndMakeDir = async (path, buildDir) => {
10
+ await mkdir(join(buildDir, dirname(path)), { recursive: true });
11
+ await copyFile(path, join(buildDir, path));
12
+ };
13
+ /**
14
+ * Iterate over all the file paths and copy them to the build directory.
15
+ *
16
+ * @param {string[]} paths
17
+ * @param {string} buildDir
18
+ */
19
+ export const copyFiles = async (paths, buildDir) => {
20
+ await Promise.all(paths.map(async (path) => await copyFileAndMakeDir(path, buildDir)));
21
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * String template utility that adds syntax highlighting and formatting in text editors.
3
+ *
4
+ * @param {TemplateStringsArray} strings
5
+ * @param {any[]} values
6
+ */
7
+ export declare const html: (strings: TemplateStringsArray, ...values: any[]) => string;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * String template utility that adds syntax highlighting and formatting in text editors.
3
+ *
4
+ * @param {TemplateStringsArray} strings
5
+ * @param {any[]} values
6
+ */
7
+ export const html = (strings, ...values) => {
8
+ const processedValues = values.map(value => Array.isArray(value) ? value.join('') : value);
9
+ return strings.reduce((prev, curr, i) => `${prev}${curr}${processedValues[i] || ''}`, '');
10
+ };
@@ -0,0 +1 @@
1
+ export { html } from './html.js';
package/package.json CHANGED
@@ -1,21 +1,35 @@
1
1
  {
2
2
  "name": "onlybuild",
3
3
  "description": "A zero-config cli for building static websites.",
4
- "version": "1.0.0",
4
+ "version": "1.2.0",
5
5
  "engines": {
6
6
  "node": ">=20.x"
7
7
  },
8
8
  "type": "module",
9
9
  "bin": {
10
- "onlybuild": "./bin/index.js"
10
+ "onlybuild": "dist/bin/index.js"
11
11
  },
12
- "main": "./src/index.js",
12
+ "exports": {
13
+ ".": "./dist/src/index.js",
14
+ "./build": "./dist/src/build.js",
15
+ "./copy": "./dist/src/copy.js"
16
+ },
17
+ "types": "./dist/src/index.d.ts",
13
18
  "license": "MIT",
14
19
  "dependencies": {
15
- "globby": "14.0.1"
20
+ "dotenv": "16.4.5",
21
+ "globby": "14.0.1",
22
+ "parse-cmd-args": "5.0.2"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "20.12.10",
26
+ "tsx": "4.9.3",
27
+ "typescript": "5.4.5"
16
28
  },
17
29
  "scripts": {
18
- "test": "node --test"
30
+ "test": "node --import tsx --test ./src/*.test.ts",
31
+ "build": "rm -rf dist/ && tsc && chmod +x ./dist/bin/index.js",
32
+ "prepare": "npm run build"
19
33
  },
20
34
  "keywords": [
21
35
  "javascript",
package/bin/index.js DELETED
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { globby } from 'globby';
4
-
5
- import { buildFiles } from '../src/build.js';
6
- import { copyFiles } from '../src/copy.js';
7
-
8
- await buildFiles(
9
- await globby(['**/*.mjs', '!_*/**/*', '!node_modules/', '!build/'], {
10
- gitignore: true,
11
- ignoreFiles: ['.onlyignore']
12
- }),
13
- 'build/'
14
- );
15
-
16
- await copyFiles(
17
- await globby(
18
- [
19
- '**/*',
20
- '!**/*.mjs',
21
- '!_*/**/*',
22
- '!package.json',
23
- '!package-lock.json',
24
- '!node_modules/',
25
- '!build/'
26
- ],
27
- {
28
- gitignore: true,
29
- ignoreFiles: ['.onlyignore']
30
- }
31
- ),
32
- 'build/'
33
- );
34
-
35
- export {};
package/src/build.js DELETED
@@ -1,27 +0,0 @@
1
- import { writeFile, mkdir } from 'node:fs/promises';
2
- import { basename, dirname, join, resolve } from 'node:path';
3
-
4
- /**
5
- * @param {string[]} paths
6
- * @param {string} buildDir
7
- */
8
- export const buildFiles = async (paths, buildDir) => {
9
- await Promise.all(
10
- paths.map(async path => {
11
- const filename = basename(path, '.mjs');
12
-
13
- const directory =
14
- filename === 'index'
15
- ? join(buildDir, dirname(path))
16
- : join(buildDir, dirname(path), filename);
17
-
18
- const html = (await import(resolve(path))).default;
19
-
20
- if (typeof html === 'string') {
21
- await mkdir(directory, { recursive: true });
22
-
23
- await writeFile(join(directory, `index.html`), html);
24
- }
25
- })
26
- );
27
- };
package/src/copy.js DELETED
@@ -1,16 +0,0 @@
1
- import { copyFile, mkdir } from 'node:fs/promises';
2
- import { dirname, join } from 'node:path';
3
-
4
- /**
5
- * @param {string[]} paths
6
- * @param {string} buildDir
7
- */
8
- export const copyFiles = async (paths, buildDir) => {
9
- await Promise.all(
10
- paths.map(async path => {
11
- await mkdir(join(buildDir, dirname(path)), { recursive: true });
12
-
13
- await copyFile(path, join(buildDir, path));
14
- })
15
- );
16
- };
package/src/html.js DELETED
@@ -1,14 +0,0 @@
1
- /**
2
- * @param {TemplateStringsArray} strings
3
- * @param {any[]} values
4
- */
5
- export const html = (strings, ...values) => {
6
- const processedValues = values.map(value =>
7
- Array.isArray(value) ? value.join('') : value
8
- );
9
-
10
- return strings.reduce(
11
- (prev, curr, i) => `${prev}${curr}${processedValues[i] || ''}`,
12
- ''
13
- );
14
- };
File without changes