onlybuild 1.0.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.
@@ -0,0 +1,25 @@
1
+ # Contributing
2
+
3
+ ## Reporting Issues
4
+
5
+ If you have found a bug in `onlybuild` please consider the following when submitting an issue:
6
+
7
+ - **Search existing [GitHub Issues](https://github.com/neogeek/onlybuild/issues)** - The bug you are experiencing might have already been reported or even fixed in an unreleased version of `onlybuild` so be sure to review both [open](https://github.com/neogeek/onlybuild/issues?state=open) and [closed](https://github.com/neogeek/onlybuild/issues?state=closed) issues.
8
+ - **Create a reproducible test case** - To make it easier for maintainers to validate the issue please include a [gist](https://gist.github.com/) containing files used to reproduce the issue.
9
+ - **Include relevant information** - Include clear steps to reproduce the issue.
10
+
11
+ ## Pull Requests
12
+
13
+ Before submitting a pull request, be sure that the following requirements are met:
14
+
15
+ - Additional tests have been added to validate the changes.
16
+ - Relevant documentation has been added.
17
+ - The build passes.
18
+
19
+ ## Code Style
20
+
21
+ Make sure to install and enable the [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin in VS Code.
22
+
23
+ ## License
24
+
25
+ By contributing to `onlybuild` you agree that your contributions fall under the same license, [The MIT License (MIT)](./LICENSE).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Scott Doxey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,239 @@
1
+ # onlybuild
2
+
3
+ > A zero-config cli for building static websites.
4
+
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
+ [![Publish](https://github.com/neogeek/onlybuild/actions/workflows/publish.workflow.yml/badge.svg)](https://github.com/neogeek/onlybuild/actions/workflows/publish.workflow.yml)
7
+ [![NPM version](https://img.shields.io/npm/v/onlybuild)](https://www.npmjs.org/package/onlybuild)
8
+ [![Join the chat at https://discord.gg/nNtFsfd](https://img.shields.io/badge/discord-join%20chat-7289DA.svg)](https://discord.gg/nNtFsfd)
9
+
10
+ ## Features
11
+
12
+ - 🪄 Can be used without installing it via `npx onlybuild`
13
+ - 🐣 Small footprint when installed
14
+ - ⏱️ Fast build times, [see benchmark data](#Benchmark)
15
+ - 🧰 Use with any framework or no framework at all
16
+ - 📦 Easily deploy built files to any static file service, ex: Google Cloud Bucket, AWS S3, GitHub Pages
17
+ - 📖 Easily import data from local JSON files or fetch remote API data
18
+
19
+ ## Social
20
+
21
+ - Star [this repo on GitHub](https://github.com/neogeek/onlybuild) for updates
22
+ - Follow me on [Bluesky](https://bsky.app/profile/scottdoxey.com) or [Twitter](https://twitter.com/neogeek)
23
+ - Join the [Discord](https://discord.gg/nNtFsfd)
24
+ - Follow me on [GitHub](https://github.com/neogeek/)
25
+
26
+ ## Table of Contents
27
+
28
+ - [Install](#install)
29
+ - [Quick Start Guide](#quick-start-guide)
30
+ - [Getting Started](#getting-started)
31
+ - [File Structure](#file-structure)
32
+ - [Ignore Files](#ignore-files)
33
+ - [Formatting Files](#formatting-files)
34
+ - [Examples](#examples)
35
+ - [Benchmark](#benchmark)
36
+ - [Testing](#testing)
37
+ - [Contributing](#contributing)
38
+ - [Community Roadmap](#community-roadmap)
39
+ - [License](#license)
40
+
41
+ ## Install
42
+
43
+ ### Globally
44
+
45
+ ```bash
46
+ $ npm install onlybuild -g
47
+ ```
48
+
49
+ ```bash
50
+ $ onlybuild
51
+ ```
52
+
53
+ ### NPX
54
+
55
+ ```bash
56
+ $ npx onlybuild
57
+ ```
58
+
59
+ ### Local
60
+
61
+ ```bash
62
+ $ npm install onlybuild --save-dev
63
+ ```
64
+
65
+ ```json
66
+ {
67
+ ...
68
+ "scripts": {
69
+ "build": "onlybuild"
70
+ },
71
+ ...
72
+ }
73
+ ```
74
+
75
+ ```bash
76
+ $ npm run build
77
+ ```
78
+
79
+ ## Quick Start Guide
80
+
81
+ Create a new file `index.mjs` with the following contents:
82
+
83
+ ```javascript
84
+ export default '<h1>Hello, world!</h1>';
85
+ ```
86
+
87
+ Run `npx onlybuild` from the directory the `index.mjs` file is in.
88
+
89
+ That's it! You will now have a `build/` directory with an `index.html` file in it.
90
+
91
+ ## Getting Started
92
+
93
+ ### Simple Example
94
+
95
+ You can have the default export return a string.
96
+
97
+ ```javascript
98
+ export default '<h1>Hello, world!</h1>';
99
+ ```
100
+
101
+ ### Method Example
102
+
103
+ You can have the default export generate a string at runtime via a method.
104
+
105
+ ```javascript
106
+ const renderPage = () => '<h1>Hello, world!</h1>';
107
+
108
+ export default renderPage();
109
+ ```
110
+
111
+ ### Asynchronous Example
112
+
113
+ You can return asynchronously if you need to read a local file or call an external API.
114
+
115
+ ```javascript
116
+ import { readFile } from 'node:fs/promises';
117
+
118
+ const renderPage = async () => await readFile('index.html', 'utf8');
119
+
120
+ export default renderPage();
121
+ ```
122
+
123
+ ```javascript
124
+ const comments = await fetch(
125
+ 'https://jsonplaceholder.typicode.com/posts/1/comments'
126
+ ).then(response => response.json());
127
+
128
+ export default `<div>${comments
129
+ .map(post => `<section><h2>${comments.name}</h2><p>${post.body}</section>`)
130
+ .join('\n')}</div>`;
131
+ ```
132
+
133
+ ### Parse Markdown
134
+
135
+ You can run the contents through other libraries, for example, converting a Markdown file into HTML before returning it using libraries like [Marked](https://github.com/markedjs/marked).
136
+
137
+ ```javascript
138
+ import { readFile } from 'node:fs/promises';
139
+
140
+ import { marked } from 'marked';
141
+
142
+ export default marked.parse(await readFile('index.md', 'utf8'));
143
+ ```
144
+
145
+ ### <code>&#96;html&#96;</code> String Template Utility
146
+
147
+ 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.
148
+
149
+ ```javascript
150
+ import { html } from 'onlybuild';
151
+
152
+ export default html`<h1>Hello, world!</h1>`;
153
+ ```
154
+
155
+ Install the [lit-html](https://marketplace.visualstudio.com/items?itemName=bierner.lit-html) plugin in VS Code to help format the HTML on save.
156
+
157
+ ## File Structure
158
+
159
+ When you run `npx onlybuild` the default export of all `.mjs` file will be captured and written to files in a `build/` directory.
160
+
161
+ 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
+
163
+ ## Ignore Files
164
+
165
+ 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
+
167
+ ```
168
+ *.md
169
+
170
+ screenshot.png
171
+
172
+ LICENSE
173
+ ```
174
+
175
+ ## Formatting Files
176
+
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.
178
+
179
+ ```bash
180
+ $ npx prettier --write "build/**/*.html"
181
+ ```
182
+
183
+ 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
+
185
+ ```bash
186
+ $ npx prettier --write --ignore-path .prettierignore "build/**/*.html"
187
+ ```
188
+
189
+ ## Examples
190
+
191
+ 1. [Hello, world!](./examples/hello-world/) - A simple Hello, world example.
192
+ 1. [Markdown](./examples/markdown/) - Get the contents of a local Markdown file and convert the contents to HTML.
193
+ 1. [External API](./examples/external-api/) - Output data fetched from an external API.
194
+ 1. [<code>&#96;html&#96;</code> String Template](./examples/html-string-template/) - Use the <code>&#96;html&#96;</code> string template utility to add syntax highlighting to HTML.
195
+ 1. [Includes](./examples/includes/) - An example that uses reusable includes for building multiple pages.
196
+
197
+ ## Benchmark
198
+
199
+ > [!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.
201
+
202
+ Times shown are in seconds. Lower is better.
203
+
204
+ | Markdown Files | 250 | 500 | 1000 | 2000 | 4000 |
205
+ | ---------------------------------------------- | -------: | -------: | -------: | -------: | -------: |
206
+ | onlybuild | `0.321` | `0.390` | `0.554` | `0.892` | `1.717` |
207
+ | [Hugo](https://gohugo.io/) v0.101.0 | `0.071` | `0.110` | `0.171` | `0.352` | `0.684` |
208
+ | [Eleventy](https://www.11ty.dev/) 1.0.1 | `0.584` | `0.683` | `0.914` | `1.250` | `1.938` |
209
+ | [Astro](https://astro.build/) 1.0.1 | `2.270` | `3.172` | `5.098` | `9.791` | `22.907` |
210
+ | [Gatsby](https://www.gatsbyjs.com/) 4.19.0-cli | `14.462` | `15.722` | `17.967` | `22.356` | `29.059` |
211
+
212
+ See more benchmark data at <https://www.zachleat.com/web/build-benchmark/>
213
+
214
+ To run the benchmarks locally you have to install `bc`. This can be done on macOS by using `brew install bc`.
215
+
216
+ Once you have `bc` installed, install NPM packages for the main repo, then navigate to the `tests/benchmarks` directory, install NPM packages there as well, and then run `./bin/run.sh` to start the benchmark tests.
217
+
218
+ ## Testing
219
+
220
+ Run all tests via `npm test`.
221
+
222
+ - Tests are authored using the native [Node.js test runner](https://nodejs.org/api/test.html).
223
+ - Tests are run automatically via GitHub Actions on each new PR.
224
+ - For you add a new feature or fix a bug, please include the benchmark output in the PR along with your device stats.
225
+
226
+ ## Contributing
227
+
228
+ Be sure to review the [Contributing Guidelines](./CONTRIBUTING.md) before logging an issue or making a pull request.
229
+
230
+ ## Community Roadmap
231
+
232
+ The goal of this project is to keep the features it offers to a minimum, allowing you, the developer, to forge your own path. If you have feature requests or bugs, please create an issue and tag them with the appropriate tag. If an issue already exists, vote for it with 👍.
233
+
234
+ - [Feature Requests](https://github.com/neogeek/onlybuild/labels/enhancement)
235
+ - [Bugs](https://github.com/neogeek/onlybuild/labels/bug)
236
+
237
+ ## License
238
+
239
+ [The MIT License (MIT)](./LICENSE)
package/bin/index.js ADDED
@@ -0,0 +1,35 @@
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/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "onlybuild",
3
+ "description": "A zero-config cli for building static websites.",
4
+ "version": "1.0.0",
5
+ "engines": {
6
+ "node": ">=20.x"
7
+ },
8
+ "type": "module",
9
+ "bin": {
10
+ "onlybuild": "./bin/index.js"
11
+ },
12
+ "main": "./src/index.js",
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "globby": "14.0.1"
16
+ },
17
+ "scripts": {
18
+ "test": "node --test"
19
+ },
20
+ "keywords": [
21
+ "javascript",
22
+ "build",
23
+ "static"
24
+ ],
25
+ "authors": [
26
+ {
27
+ "name": "Scott Doxey",
28
+ "email": "hello@scottdoxey.com",
29
+ "homepage": "http://scottdoxey.com/"
30
+ }
31
+ ],
32
+ "homepage": "https://github.com/neogeek/onlybuild",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git://github.com/neogeek/onlybuild.git"
36
+ }
37
+ }
package/src/build.js ADDED
@@ -0,0 +1,27 @@
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 ADDED
@@ -0,0 +1,16 @@
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 ADDED
@@ -0,0 +1,14 @@
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
+ };
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ export { html } from './html.js';