diff --git a/.gitignore b/.gitignore index c6bba59..33ce24f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# virtual-star custom +build/ + +# node boilerplate # Logs logs *.log diff --git a/README.md b/README.md index ec4803c..d24b324 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # virtual-star Static site builder for a blog site, for deployment to neocities or via included static-server docker container. + +## Install + +- Clone this repository, and run `npm i`. +- Set up a `.env` file if desired. + +### Configure +In your `.env` file, or as environment vars: + +- `VS_DEBUG`: set to `true` to enable more logging, `false` otherwise (default: `false`). +- `VS_INPUT_DIR`: change the input directory (default: `content/`). +- `VS_OUTPUT_DIR`: change the output directory (default: `build/`). + +## Usage + +### Build +_Converts .md files in the input directory to .html files in the output directory._ + +- `npm run build` + +### Serve +_Statically serves files in the output directory_ + +- `npm run serve` diff --git a/build.js b/build.js new file mode 100644 index 0000000..2163b71 --- /dev/null +++ b/build.js @@ -0,0 +1,46 @@ +import fs from 'fs'; +import Showdown from 'showdown'; +import 'dotenv/config'; +import { ENV } from './lib/env.js'; + +const converter = new Showdown.Converter(); + +let inputDir = 'content/'; +if(ENV.getIsSet('VS_INPUT_DIR')) { + inputDir = ENV.getString('VS_INPUT_DIR'); +} + +let outputDir = 'build/'; +if(ENV.getIsSet('VS_OUTPUT_DIR')) { + outputDir = ENV.getString('VS_OUTPUT_DIR'); +} + +const fileEntities = fs.readdirSync(inputDir, {recursive: true, withFileTypes: true, encoding: 'utf8'}); + +fileEntities.forEach((fileEnt) => { + const filenameTokens = fileEnt.name.split('.'); + const extension = filenameTokens[filenameTokens.length - 1]; + + if(extension === 'md') { + const fullPath = fileEnt.parentPath + fileEnt.name; + + const content = fs.readFileSync(fullPath, {encoding: 'utf8'}); + + const converted = converter.makeHtml(content); + + let outputFileName = ""; + for(let i = 0; i < filenameTokens.length - 1; i++) { + outputFileName += filenameTokens[i]; + } + + outputFileName += '.html'; + + const trimmedPath = fileEnt.parentPath.replace(inputDir, ""); + + const outputPath = outputDir + trimmedPath + outputFileName; + + fs.writeFileSync(outputPath, converted); + + console.log(`Converted ${fullPath} -> ${outputPath}`); + } +}); \ No newline at end of file diff --git a/bundle.js b/bundle.js new file mode 100644 index 0000000..ae37d76 --- /dev/null +++ b/bundle.js @@ -0,0 +1 @@ +// No exports! \ No newline at end of file diff --git a/content/test.md b/content/test.md new file mode 100644 index 0000000..73e2de4 --- /dev/null +++ b/content/test.md @@ -0,0 +1 @@ +# Here's a test! \ No newline at end of file diff --git a/lib/env.js b/lib/env.js new file mode 100644 index 0000000..2499520 --- /dev/null +++ b/lib/env.js @@ -0,0 +1,17 @@ +export const ENV = { + getValue: function (key) { + return process.env[key]; + }, + getBoolean: function (key) { + return (this.getValue(key)?.toLowerCase?.() === 'true'); + }, + getNumber: function (key) { + return parseFloat(this.getValue(key)); + }, + getString: function (key) { + return this.getValue(key); + }, + getIsSet: function (key) { + return (this.getValue(key) !== undefined); + }, +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..dbaa7aa --- /dev/null +++ b/package-lock.json @@ -0,0 +1,164 @@ +{ + "name": "virtual-star", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "virtual-star", + "version": "0.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^16.4.7", + "serve-handler": "^6.1.6", + "showdown": "^2.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/showdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "license": "MIT", + "dependencies": { + "commander": "^9.0.0" + }, + "bin": { + "showdown": "bin/showdown.js" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.me/tiviesantos" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..496015d --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "virtual-star", + "version": "0.0.0", + "description": "Static blog generator and simple server for node", + "homepage": "https://github.com/walcutt/virtual-star#readme", + "bugs": { + "url": "https://github.com/walcutt/virtual-star/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/walcutt/virtual-star.git" + }, + "license": "ISC", + "author": "walcutt", + "type": "module", + "main": "bundle.js", + "scripts": { + "build": "node build.js", + "serve": "node serve.js" + }, + "dependencies": { + "dotenv": "^16.4.7", + "serve-handler": "^6.1.6", + "showdown": "^2.1.0" + } +} diff --git a/serve.js b/serve.js new file mode 100644 index 0000000..3ba020b --- /dev/null +++ b/serve.js @@ -0,0 +1,26 @@ +// based on the minimal demo from https://www.npmjs.com/package/serve +import handler from 'serve-handler'; +import http from 'http'; +import 'dotenv/config'; +import { ENV } from './lib/env.js'; + +const isDebugEnabled = ENV.getBoolean('VS_DEBUG'); +const hasOverrideDirectory = ENV.getIsSet('VS_OUTPUT_DIR'); +const overrideDirectory = ENV.getString('VS_OUTPUT_DIR'); +const defaultDirectory = 'build'; + +const options = { + public: hasOverrideDirectory ? overrideDirectory : defaultDirectory, + directoryListing: isDebugEnabled, +}; + +const server = http.createServer((req, res) => { + return handler(req, res, options); +}); + +server.listen(3000, () => { + console.log('Running on port 3000'); + if(isDebugEnabled) { + console.log(options); + } +}); \ No newline at end of file