From ebf91aef1d4bde5373ff689daa80c6bf9fe3167b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 24 Apr 2023 10:56:45 -0300 Subject: build tools --- packages/web-util/build.mjs | 34 ++++++- packages/web-util/package.json | 5 + packages/web-util/src/index.build.ts | 188 +++++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 packages/web-util/src/index.build.ts (limited to 'packages/web-util') diff --git a/packages/web-util/build.mjs b/packages/web-util/build.mjs index 6e20b8b92..8e74f69c7 100755 --- a/packages/web-util/build.mjs +++ b/packages/web-util/build.mjs @@ -65,6 +65,35 @@ const buildConfigBase = { }, } +/** + * Build time libraries, under node runtime + */ +const buildConfigBuild = { + ...buildConfigBase, + entryPoints: ["src/index.build.ts"], + outExtension: { + '.js': '.mjs' + }, + format: 'esm', + platform: 'node', + external: ["esbuild"], + // https://github.com/evanw/esbuild/issues/1921 + // How to fix "Dynamic require of "os" is not supported" + // esbuild cannot convert external "static" commonjs require statements to static esm imports + banner: { + js: ` + import { fileURLToPath } from 'url'; + import { createRequire as topLevelCreateRequire } from 'module'; + const require = topLevelCreateRequire(import.meta.url); + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); +` + }, +}; + +/** + * Development libraries, under node runtime + */ const buildConfigNode = { ...buildConfigBase, entryPoints: ["src/index.node.ts", "src/cli.ts"], @@ -76,6 +105,9 @@ const buildConfigNode = { external: ["preact"], }; +/** + * Support libraries, under browser runtime + */ const buildConfigBrowser = { ...buildConfigBase, entryPoints: ["src/tests/mock.ts", "src/tests/swr.ts", "src/index.browser.ts", "src/live-reload.ts", 'src/stories.tsx'], @@ -89,7 +121,7 @@ const buildConfigBrowser = { jsxFragment: 'Fragment', }; -[buildConfigNode, buildConfigBrowser].forEach((config) => { +[buildConfigNode, buildConfigBrowser, buildConfigBuild].forEach((config) => { esbuild .build(config) .catch((e) => { diff --git a/packages/web-util/package.json b/packages/web-util/package.json index f89d0bb73..77f71c7bb 100644 --- a/packages/web-util/package.json +++ b/packages/web-util/package.json @@ -21,6 +21,10 @@ "types": "./lib/index.browser.js", "default": "./lib/index.browser.mjs" }, + "./lib/index.build": { + "types": "./lib/index.build.js", + "default": "./lib/index.build.mjs" + }, "./lib/index.node": { "types": "./lib/index.node.js", "default": "./lib/index.node.cjs" @@ -48,6 +52,7 @@ "rimraf": "^3.0.2", "swr": "2.0.3", "tslib": "^2.4.0", + "sass": "1.56.1", "typescript": "^4.9.4", "ws": "7.4.5" }, diff --git a/packages/web-util/src/index.build.ts b/packages/web-util/src/index.build.ts new file mode 100644 index 000000000..02e6886ab --- /dev/null +++ b/packages/web-util/src/index.build.ts @@ -0,0 +1,188 @@ +import esbuild from "esbuild"; +import path from "path"; +import fs from "fs"; +import sass from "sass"; +import { PluginBuild } from "esbuild"; + +// this should give us the current directory where +// the project is being built +const BASE = process.cwd(); +const distProd = path.join(BASE, "dist", "prod"); +const distDev = path.join(BASE, "dist", "dev"); +const sourceDir = path.join(BASE, "src"); + +const preact = path.join( + BASE, + "node_modules", + "preact", + "compat", + "dist", + "compat.module.js", +); + +// https://preactjs.com/guide/v8/switching-to-preact/#how-to-alias-preact-compat +const preactCompatPlugin: esbuild.Plugin = { + name: "preact-compat", + setup(build) { + build.onResolve({ filter: /^(react-dom|react)$/ }, (args) => { + return { + path: preact, + }; + }); + }, +}; + +export function getFilesInSource(regex: RegExp): string[] { + return getFilesInDirectory(sourceDir, regex); +} + +function getFilesInDirectory(startPath: string, regex: RegExp): string[] { + if (!fs.existsSync(startPath)) { + return []; + } + const files = fs.readdirSync(startPath); + const result = files + .flatMap((file) => { + const filename = path.join(startPath, file); + + const stat = fs.lstatSync(filename); + if (stat.isDirectory()) { + return getFilesInDirectory(filename, regex); + } + if (regex.test(filename)) { + return [filename]; + } + return []; + }) + .filter((x) => !!x); + + return result; +} + +let GIT_ROOT = BASE; +while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") { + GIT_ROOT = path.join(GIT_ROOT, "../"); +} +if (GIT_ROOT === "/") { + // eslint-disable-next-line no-undef + console.log("not found"); + // eslint-disable-next-line no-undef + process.exit(1); +} +const GIT_HASH = GIT_ROOT === "/" ? undefined : git_hash(); + +const buf = fs.readFileSync(path.join(BASE, "package.json")); +let _package = JSON.parse(buf.toString("utf-8")); + +function git_hash() { + const rev = fs + .readFileSync(path.join(GIT_ROOT, ".git", "HEAD")) + .toString() + .trim() + .split(/.*[: ]/) + .slice(-1)[0]; + if (rev.indexOf("/") === -1) { + return rev; + } else { + return fs.readFileSync(path.join(GIT_ROOT, ".git", rev)).toString().trim(); + } +} + +// FIXME: Put this into some helper library. +function copyFilesPlugin(files: Array) { + return { + name: "copy-files", + setup(build: PluginBuild) { + const outDir = build.initialOptions.outdir; + if (outDir === undefined) + throw Error("esbuild build options does not specify outdir"); + build.onEnd(() => { + for (const file of files) { + const name = path.parse(file).base; + fs.copyFileSync(file, path.join(outDir, name)); + } + }); + }, + }; +} + +const DEFAULT_SASS_FILTER = /\.(s[ac]ss|css)$/; + +const buildSassPlugin: esbuild.Plugin = { + name: "custom-build-sass", + setup(build) { + build.onLoad({ filter: DEFAULT_SASS_FILTER }, ({ path: file }) => { + const resolveDir = path.dirname(file); + const { css: contents } = sass.compile(file, { loadPaths: ["./"] }); + + return { + resolveDir, + loader: "css", + contents, + }; + }); + }, +}; + +const buildConfig: esbuild.BuildOptions = { + bundle: true, + minify: false, + loader: { + ".svg": "file", + ".png": "dataurl", + ".jpeg": "dataurl", + ".ttf": "file", + ".woff": "file", + ".woff2": "file", + ".eot": "file", + }, + target: ["es6"], + format: "esm", + platform: "browser", + sourcemap: true, + jsxFactory: "h", + jsxFragment: "Fragment", + define: { + __VERSION__: `"${_package.version}"`, + __GIT_HASH__: `"${GIT_HASH}"`, + }, + plugins: [ + preactCompatPlugin, + copyFilesPlugin(["./src/index.html"]), + buildSassPlugin, + ], +}; + +/** + * Build sources for prod environment + */ +export function buildProd(entryPoints: string[]) { + return esbuild.build({ + ...buildConfig, + entryPoints, + outdir: distProd, + }); +} + +/** + * Build sources for dev environment + */ +function buildDev(entryPoints: string[]): Promise { + return esbuild.build({ + ...buildConfig, + entryPoints, + outdir: distDev, + }); +} + +/** + * Do startup for development environment + */ +export function initializeDev( + entryPoints: string[], +): () => Promise { + buildConfig.inject = [ + "./node_modules/@gnu-taler/web-util/lib/live-reload.mjs", + ]; + return () => buildDev(entryPoints); +} -- cgit v1.2.3