otro generador de sitios estáticos? en serio?

This commit is contained in:
Cat /dev/Nulo 2023-08-04 15:33:29 -03:00
commit 827d1dfc04
7 changed files with 481 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules/

75
gen.js Executable file
View file

@ -0,0 +1,75 @@
import { lstat, mkdir, readdir } from "fs/promises";
import { dirname, join, relative, resolve } from "path";
import { cwd } from "process";
import js from "./transformers/js.js";
import copy from "./transformers/copy.js";
const sitegenConfigPath = join(cwd(), "sitegen.config.js");
/** @type {any} */
let config = {};
try {
await lstat(sitegenConfigPath);
config = await import(sitegenConfigPath);
} catch {}
const inputDirPath = "./src";
const outputDirPath = join(cwd(), "_site");
const transformers = {
".s.js": js,
".s.jsx": js,
".png": copy,
".jpg": copy,
".jpeg": copy,
".avif": copy,
".webp": copy,
".woff": copy,
".woff2": copy,
...(config?.transformers || {}),
};
let built = 0;
/**
* @param {string} path
*/
async function buildFile(path) {
const transformerEnt = Object.entries(transformers).find(([ext]) =>
path.endsWith(ext)
);
if (!transformerEnt) return;
const outputPath = join(outputDirPath, relative(inputDirPath, path));
await mkdir(dirname(outputPath), { recursive: true });
const transformer = transformerEnt[1];
try {
await transformer.build(path, outputPath);
} catch (error) {
console.error(`While building ${path}`);
throw error;
}
built++;
}
/**
* @param {string} dir
* @returns {Promise<void>}
*/
async function buildDir(dir) {
const children = await readdir(dir, { withFileTypes: true });
await Promise.all(
children.map((child) => {
const path = join(dir, child.name);
if (resolve(path) === resolve(outputDirPath)) return;
if (resolve(path) === resolve("node_modules")) return;
if (resolve(path) === resolve(".git")) return;
if (child.isDirectory()) return buildDir(path);
else if (child.isFile()) return buildFile(path);
})
);
}
console.time("build");
await buildDir(inputDirPath);
console.timeEnd("build");
console.info(`Built ${built} files into ${outputDirPath}.`);

26
package.json Normal file
View file

@ -0,0 +1,26 @@
{
"name": "sitegen",
"type": "module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"sitegen": "./gen.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"esbuild": "^0.18.17",
"nanoid": "^4.0.2",
"vhtml": "^2.2.0"
},
"devDependencies": {
"@tsconfig/node18": "^18.2.0",
"@types/node": "^20.4.6",
"typescript": "^5.1.6"
}
}

281
pnpm-lock.yaml Normal file
View file

@ -0,0 +1,281 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
esbuild:
specifier: ^0.18.17
version: 0.18.17
nanoid:
specifier: ^4.0.2
version: 4.0.2
vhtml:
specifier: ^2.2.0
version: 2.2.0
devDependencies:
'@tsconfig/node18':
specifier: ^18.2.0
version: 18.2.0
'@types/node':
specifier: ^20.4.6
version: 20.4.6
typescript:
specifier: ^5.1.6
version: 5.1.6
packages:
/@esbuild/android-arm64@0.18.17:
resolution: {integrity: sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: false
optional: true
/@esbuild/android-arm@0.18.17:
resolution: {integrity: sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: false
optional: true
/@esbuild/android-x64@0.18.17:
resolution: {integrity: sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: false
optional: true
/@esbuild/darwin-arm64@0.18.17:
resolution: {integrity: sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@esbuild/darwin-x64@0.18.17:
resolution: {integrity: sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@esbuild/freebsd-arm64@0.18.17:
resolution: {integrity: sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: false
optional: true
/@esbuild/freebsd-x64@0.18.17:
resolution: {integrity: sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-arm64@0.18.17:
resolution: {integrity: sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-arm@0.18.17:
resolution: {integrity: sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-ia32@0.18.17:
resolution: {integrity: sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-loong64@0.18.17:
resolution: {integrity: sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-mips64el@0.18.17:
resolution: {integrity: sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-ppc64@0.18.17:
resolution: {integrity: sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-riscv64@0.18.17:
resolution: {integrity: sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-s390x@0.18.17:
resolution: {integrity: sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/linux-x64@0.18.17:
resolution: {integrity: sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@esbuild/netbsd-x64@0.18.17:
resolution: {integrity: sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: false
optional: true
/@esbuild/openbsd-x64@0.18.17:
resolution: {integrity: sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: false
optional: true
/@esbuild/sunos-x64@0.18.17:
resolution: {integrity: sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: false
optional: true
/@esbuild/win32-arm64@0.18.17:
resolution: {integrity: sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@esbuild/win32-ia32@0.18.17:
resolution: {integrity: sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@esbuild/win32-x64@0.18.17:
resolution: {integrity: sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@tsconfig/node18@18.2.0:
resolution: {integrity: sha512-yhxwIlFVSVcMym3O31HoMnRXpoenmpIxcj4Yoes2DUpe+xCJnA7ECQP1Vw889V0jTt/2nzvpLQ/UuMYCd3JPIg==}
dev: true
/@types/node@20.4.6:
resolution: {integrity: sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==}
dev: true
/esbuild@0.18.17:
resolution: {integrity: sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.18.17
'@esbuild/android-arm64': 0.18.17
'@esbuild/android-x64': 0.18.17
'@esbuild/darwin-arm64': 0.18.17
'@esbuild/darwin-x64': 0.18.17
'@esbuild/freebsd-arm64': 0.18.17
'@esbuild/freebsd-x64': 0.18.17
'@esbuild/linux-arm': 0.18.17
'@esbuild/linux-arm64': 0.18.17
'@esbuild/linux-ia32': 0.18.17
'@esbuild/linux-loong64': 0.18.17
'@esbuild/linux-mips64el': 0.18.17
'@esbuild/linux-ppc64': 0.18.17
'@esbuild/linux-riscv64': 0.18.17
'@esbuild/linux-s390x': 0.18.17
'@esbuild/linux-x64': 0.18.17
'@esbuild/netbsd-x64': 0.18.17
'@esbuild/openbsd-x64': 0.18.17
'@esbuild/sunos-x64': 0.18.17
'@esbuild/win32-arm64': 0.18.17
'@esbuild/win32-ia32': 0.18.17
'@esbuild/win32-x64': 0.18.17
dev: false
/nanoid@4.0.2:
resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==}
engines: {node: ^14 || ^16 || >=18}
hasBin: true
dev: false
/typescript@5.1.6:
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
/vhtml@2.2.0:
resolution: {integrity: sha512-TPXrXrxBOslRUVnlVkiAqhoXneiertIg86bdvzionrUYhEuiROvyPZNiiP6GIIJ2Q7oPNVyEtIx8gMAZZE9lCQ==}
dev: false

7
transformers/copy.js Normal file
View file

@ -0,0 +1,7 @@
import { copyFile, constants } from "fs/promises";
export default class CopyTransformer {
static async build(inputPath, outputPath) {
await copyFile(inputPath, outputPath, constants.COPYFILE_FICLONE);
}
}

83
transformers/js.js Normal file
View file

@ -0,0 +1,83 @@
import { nanoid } from "nanoid";
import { build } from "esbuild";
import { mkdir, readFile, rm, writeFile } from "fs/promises";
import { cwd } from "process";
import { basename, dirname, join } from "path";
import h from "vhtml";
globalThis.vhtml = h;
export default class JsTransformer {
/**
* @param {string} inputPath
* @param {string} outputPath
*/
static async build(inputPath, outputPath) {
outputPath = join(
dirname(outputPath),
basename(outputPath, ".s.jsx") + ".html"
);
const outfile = join(cwd(), `${nanoid()}.mjs`);
await mkdir(dirname(outfile), { recursive: true });
try {
await build({
entryPoints: [join(cwd(), inputPath)],
outfile,
format: "esm",
loader: {
".json": "json",
},
bundle: true,
platform: "node",
plugins: [
{
name: "svg-jsx",
setup(build) {
build.onLoad({ filter: /\.svg$/ }, async (args) => {
const f = await readFile(args.path, "utf-8");
const js = `
export default (props) => {
let s = \`${f}\`;
if (props.class)
s = s.replace("<svg", \`<svg class="\${props.class}"\`);
return (
<div
class={props.containerClass}
dangerouslySetInnerHTML={{ __html: s }}
></div>
);
};
`;
return { contents: js, loader: "jsx" };
});
},
},
{
name: "relative-import-path",
setup(build) {
build.onResolve({ filter: /./ }, (args) => {
if (args.path.endsWith(".json") || args.path.endsWith(".svg"))
return { path: join(args.resolveDir, args.path) };
else if (args.path.startsWith("."))
return {
path: join(args.resolveDir, args.path),
external: true,
};
else return { path: args.path, external: !!args.importer };
});
},
},
],
jsxFactory: "h",
banner: { js: `const h = globalThis.vhtml;` },
});
const file = await import(outfile);
const html = "<!doctype html>" + (await file.render());
await writeFile(outputPath, html);
} finally {
}
await rm(outfile);
}
}

8
tsconfig.json Normal file
View file

@ -0,0 +1,8 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noEmit": true
}
}