From 6340cc5454f637a97fb7329d2494c1dfc3fb1735 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 5 May 2023 08:34:21 -0300 Subject: add postcss, fix export names --- packages/web-util/build.mjs | 127 +++++++++++++++---------- packages/web-util/package.json | 34 ++++--- packages/web-util/src/cli.ts | 1 - packages/web-util/src/index.browser.ts | 1 - packages/web-util/src/index.build.ts | 141 ++++++++++++++++++++-------- packages/web-util/src/index.testing.ts | 3 + packages/web-util/src/index.ts | 1 - packages/web-util/src/serve.ts | 167 ++++++++++++++++----------------- packages/web-util/src/tests/index.ts | 2 - 9 files changed, 281 insertions(+), 196 deletions(-) create mode 100644 packages/web-util/src/index.testing.ts delete mode 100644 packages/web-util/src/index.ts delete mode 100644 packages/web-util/src/tests/index.ts (limited to 'packages/web-util') diff --git a/packages/web-util/build.mjs b/packages/web-util/build.mjs index 8e74f69c7..0b015f22c 100755 --- a/packages/web-util/build.mjs +++ b/packages/web-util/build.mjs @@ -15,34 +15,38 @@ GNU Taler; see the file COPYING. If not, see */ -import esbuild from 'esbuild' -import path from "path" -import fs from "fs" +import esbuild from "esbuild"; +import path from "path"; +import fs from "fs"; // eslint-disable-next-line no-undef -const BASE = process.cwd() +const BASE = process.cwd(); -let GIT_ROOT = BASE -while (!fs.existsSync(path.join(GIT_ROOT, '.git')) && GIT_ROOT !== '/') { - GIT_ROOT = path.join(GIT_ROOT, '../') +let GIT_ROOT = BASE; +while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") { + GIT_ROOT = path.join(GIT_ROOT, "../"); } -if (GIT_ROOT === '/') { +if (GIT_ROOT === "/") { // eslint-disable-next-line no-undef - console.log("not found") + console.log("not found"); // eslint-disable-next-line no-undef process.exit(1); } -const GIT_HASH = GIT_ROOT === '/' ? undefined : git_hash() +const GIT_HASH = GIT_ROOT === "/" ? undefined : git_hash(); - -let _package = JSON.parse(fs.readFileSync(path.join(BASE, 'package.json'))); +let _package = JSON.parse(fs.readFileSync(path.join(BASE, "package.json"))); function git_hash() { - const rev = fs.readFileSync(path.join(GIT_ROOT, '.git', 'HEAD')).toString().trim().split(/.*[: ]/).slice(-1)[0]; - if (rev.indexOf('/') === -1) { + 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(); + return fs.readFileSync(path.join(GIT_ROOT, ".git", rev)).toString().trim(); } } @@ -50,20 +54,18 @@ const buildConfigBase = { outdir: "lib", bundle: true, minify: false, - target: [ - 'es6' - ], + target: ["es2021"], loader: { - '.key': 'text', - '.crt': 'text', - '.html': 'text', + ".key": "text", + ".crt": "text", + ".html": "text", }, sourcemap: true, define: { - '__VERSION__': `"${_package.version}"`, - '__GIT_HASH__': `"${GIT_HASH}"`, + __VERSION__: `"${_package.version}"`, + __GIT_HASH__: `"${GIT_HASH}"`, }, -} +}; /** * Build time libraries, under node runtime @@ -72,36 +74,52 @@ const buildConfigBuild = { ...buildConfigBase, entryPoints: ["src/index.build.ts"], outExtension: { - '.js': '.mjs' + ".js": ".mjs", }, - format: 'esm', - platform: 'node', + 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: { + 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 buildConfigTesting = { + ...buildConfigBase, + entryPoints: ["src/index.testing.ts"], + outExtension: { + ".js": ".mjs", + }, + format: "esm", + platform: "browser", + external: ["preact", "@gnu-taler/taler-util", "jed", "swr", "axios"], + jsxFactory: "h", + jsxFragment: "Fragment", +}; + +/** + * Testing libraries, under node runtime + */ const buildConfigNode = { ...buildConfigBase, entryPoints: ["src/index.node.ts", "src/cli.ts"], outExtension: { - '.js': '.cjs' + ".js": ".cjs", }, - format: 'cjs', - platform: 'node', + format: "cjs", + platform: "node", external: ["preact"], }; @@ -110,26 +128,33 @@ const buildConfigNode = { */ const buildConfigBrowser = { ...buildConfigBase, - entryPoints: ["src/tests/mock.ts", "src/tests/swr.ts", "src/index.browser.ts", "src/live-reload.ts", 'src/stories.tsx'], + entryPoints: [ + "src/tests/mock.ts", + "src/tests/swr.ts", + "src/index.browser.ts", + "src/live-reload.ts", + "src/stories.tsx", + ], outExtension: { - '.js': '.mjs' + ".js": ".mjs", }, - format: 'esm', - platform: 'browser', - external: ["preact", "@gnu-taler/taler-util", "jed","swr","axios"], - jsxFactory: 'h', - jsxFragment: 'Fragment', + format: "esm", + platform: "browser", + external: ["preact", "@gnu-taler/taler-util", "jed", "swr", "axios"], + jsxFactory: "h", + jsxFragment: "Fragment", }; -[buildConfigNode, buildConfigBrowser, buildConfigBuild].forEach((config) => { - esbuild - .build(config) - .catch((e) => { - // eslint-disable-next-line no-undef - console.log(e) - // eslint-disable-next-line no-undef - process.exit(1) - }); - -}) - +[ + buildConfigNode, + buildConfigBrowser, + buildConfigBuild, + buildConfigTesting, +].forEach((config) => { + esbuild.build(config).catch((e) => { + // eslint-disable-next-line no-undef + console.log(e); + // eslint-disable-next-line no-undef + process.exit(1); + }); +}); diff --git a/packages/web-util/package.json b/packages/web-util/package.json index 77f71c7bb..eda3f0874 100644 --- a/packages/web-util/package.json +++ b/packages/web-util/package.json @@ -9,25 +9,21 @@ "license": "AGPL-3.0-or-later", "private": false, "exports": { - "./lib/tests/swr": { - "types": "./lib/tests/swr.js", - "default": "./lib/tests/swr.mjs" - }, - "./lib/tests/mock": { - "types": "./lib/tests/mock.js", - "default": "./lib/tests/mock.mjs" - }, - "./lib/index.browser": { + "./browser": { "types": "./lib/index.browser.js", "default": "./lib/index.browser.mjs" }, - "./lib/index.build": { + "./build": { "types": "./lib/index.build.js", "default": "./lib/index.build.mjs" }, - "./lib/index.node": { + "./node": { "types": "./lib/index.node.js", "default": "./lib/index.node.cjs" + }, + "./testing": { + "types": "./lib/index.testing.js", + "default": "./lib/index.testing.mjs" } }, "scripts": { @@ -42,21 +38,31 @@ "@types/node": "^18.11.17", "@types/web": "^0.0.82", "@types/ws": "^8.5.3", + "autoprefixer": "^10.4.14", "axios": "^1.2.2", "chokidar": "^3.5.3", "esbuild": "^0.17.7", "express": "^4.18.2", + "postcss": "^8.4.23", + "postcss-load-config": "^4.0.1", "preact": "10.11.3", "preact-render-to-string": "^5.2.6", "prettier": "^2.5.1", "rimraf": "^3.0.2", + "sass": "1.56.1", "swr": "2.0.3", "tslib": "^2.4.0", - "sass": "1.56.1", "typescript": "^4.9.4", "ws": "7.4.5" }, "dependencies": { - "@types/chrome": "0.0.197" + "@babel/core": "7.18.9", + "@babel/helper-compilation-targets": "7.18.9", + "@linaria/babel-preset": "3.0.0-beta.23", + "@linaria/core": "3.0.0-beta.22", + "@linaria/esbuild": "3.0.0-beta.22", + "@linaria/react": "3.0.0-beta.22", + "@types/chrome": "0.0.197", + "tailwindcss": "^3.3.2" } -} \ No newline at end of file +} diff --git a/packages/web-util/src/cli.ts b/packages/web-util/src/cli.ts index b02947f7a..05a22bc8a 100644 --- a/packages/web-util/src/cli.ts +++ b/packages/web-util/src/cli.ts @@ -36,7 +36,6 @@ walletCli return serve({ folder: args.serve.folder || "./dist", port: args.serve.port || 8000, - development: args.serve.development, }); }); diff --git a/packages/web-util/src/index.browser.ts b/packages/web-util/src/index.browser.ts index c7ba8435f..2a537b405 100644 --- a/packages/web-util/src/index.browser.ts +++ b/packages/web-util/src/index.browser.ts @@ -5,5 +5,4 @@ export * from "./utils/http-impl.sw.js"; export * from "./utils/observable.js"; export * from "./context/index.js"; export * from "./components/index.js"; -export * as tests from "./tests/index.js"; export { renderStories, parseGroupImport } from "./stories.js"; diff --git a/packages/web-util/src/index.build.ts b/packages/web-util/src/index.build.ts index 02e6886ab..19bb7fdfb 100644 --- a/packages/web-util/src/index.build.ts +++ b/packages/web-util/src/index.build.ts @@ -1,15 +1,14 @@ -import esbuild from "esbuild"; -import path from "path"; +import esbuild, { PluginBuild } from "esbuild"; +// import linaria from "@linaria/esbuild"; import fs from "fs"; +import path from "path"; +import postcss from "postcss"; import sass from "sass"; -import { PluginBuild } from "esbuild"; +import postcssrc from "postcss-load-config"; // 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, @@ -32,11 +31,10 @@ const preactCompatPlugin: esbuild.Plugin = { }, }; -export function getFilesInSource(regex: RegExp): string[] { - return getFilesInDirectory(sourceDir, regex); -} - -function getFilesInDirectory(startPath: string, regex: RegExp): string[] { +export function getFilesInDirectory( + startPath: string, + regex?: RegExp, +): string[] { if (!fs.existsSync(startPath)) { return []; } @@ -49,7 +47,7 @@ function getFilesInDirectory(startPath: string, regex: RegExp): string[] { if (stat.isDirectory()) { return getFilesInDirectory(filename, regex); } - if (regex.test(filename)) { + if (!regex || regex.test(filename)) { return [filename]; } return []; @@ -108,7 +106,7 @@ function copyFilesPlugin(files: Array) { const DEFAULT_SASS_FILTER = /\.(s[ac]ss|css)$/; -const buildSassPlugin: esbuild.Plugin = { +const sassPlugin: esbuild.Plugin = { name: "custom-build-sass", setup(build) { build.onLoad({ filter: DEFAULT_SASS_FILTER }, ({ path: file }) => { @@ -124,7 +122,42 @@ const buildSassPlugin: esbuild.Plugin = { }, }; -const buildConfig: esbuild.BuildOptions = { +const postCssPlugin: esbuild.Plugin = { + name: "custom-build-postcss", + setup(build) { + build.onLoad({ filter: DEFAULT_SASS_FILTER }, async ({ path: file }) => { + const resolveDir = path.dirname(file); + const sourceBuffer = fs.readFileSync(file); + const source = sourceBuffer.toString("utf-8"); + const postCssConfig = await postcssrc(); + postCssConfig.options.from = file; + + const { css: contents } = await postcss(postCssConfig.plugins).process( + source, + postCssConfig.options, + ); + + return { + resolveDir, + loader: "css", + contents, + }; + }); + }, +}; + +function linariaPlugin() { + // const linariaCssPlugin: esbuild.Plugin = linaria.default({ + // babelOptions: { + // babelrc: false, + // configFile: "./babel.config-linaria.json", + // }, + // sourceMap: true, + // }); + // return linariaCssPlugin; +} + +const defaultEsBuildConfig: esbuild.BuildOptions = { bundle: true, minify: false, loader: { @@ -146,43 +179,71 @@ const buildConfig: esbuild.BuildOptions = { __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, - }); +export interface BuildParams { + source: { + assets: string[]; + js: string[]; + }; + destination: string; + css: "sass" | "postcss"; // | "linaria"; +} + +export function computeConfig(params: BuildParams) { + const plugins: Array = [ + preactCompatPlugin, + copyFilesPlugin(params.source.assets), + ]; + + switch (params.css) { + case "sass": { + plugins.push(sassPlugin); + break; + } + case "postcss": { + plugins.push(postCssPlugin); + break; + } + + // case "linaria": { + // plugins.push(linariaPlugin()); + // break; + // } + + default: { + const cssType: never = params.css; + throw Error(`not supported: ${cssType}`); + } + } + return { + ...defaultEsBuildConfig, + entryPoints: params.source.js, + outdir: params.destination, + plugins, + }; } /** - * Build sources for dev environment + * Build sources for prod environment */ -function buildDev(entryPoints: string[]): Promise { - return esbuild.build({ - ...buildConfig, - entryPoints, - outdir: distDev, - }); +export function build(config: BuildParams) { + return esbuild.build(computeConfig(config)); } +const LIVE_RELOAD_SCRIPT = + "./node_modules/@gnu-taler/web-util/lib/live-reload.mjs"; + /** * Do startup for development environment */ export function initializeDev( - entryPoints: string[], + config: BuildParams, ): () => Promise { - buildConfig.inject = [ - "./node_modules/@gnu-taler/web-util/lib/live-reload.mjs", - ]; - return () => buildDev(entryPoints); + function buildDevelopment() { + const result = computeConfig(config); + result.inject = [LIVE_RELOAD_SCRIPT]; + return esbuild.build(result); + } + return buildDevelopment; } diff --git a/packages/web-util/src/index.testing.ts b/packages/web-util/src/index.testing.ts new file mode 100644 index 000000000..2349debc5 --- /dev/null +++ b/packages/web-util/src/index.testing.ts @@ -0,0 +1,3 @@ +export * from "./tests/hook.js"; +export * from "./tests/swr.js"; +export * from "./tests/mock.js"; diff --git a/packages/web-util/src/index.ts b/packages/web-util/src/index.ts deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/web-util/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/web-util/src/serve.ts b/packages/web-util/src/serve.ts index f37ef90ce..93d8082fe 100644 --- a/packages/web-util/src/serve.ts +++ b/packages/web-util/src/serve.ts @@ -21,7 +21,6 @@ const logger = new Logger("serve.ts"); const PATHS = { WS: "/ws", - NOTIFY: "/notify", EXAMPLE: "/examples", APP: "/app", }; @@ -30,101 +29,97 @@ export async function serve(opts: { folder: string; port: number; source?: string; - development?: boolean; examplesLocationJs?: string; examplesLocationCss?: string; - onUpdate?: () => Promise; + onSourceUpdate?: () => Promise; }): Promise { const app = express(); app.use(PATHS.APP, express.static(opts.folder)); - const servers = [ - http.createServer(app), - https.createServer(httpServerOptions, app), - ]; - logger.info(` ${PATHS.APP}: application`); - logger.info(` ${PATHS.EXAMPLE}: examples`); - logger.info(` ${PATHS.WS}: websocket`); - logger.info(` ${PATHS.NOTIFY}: broadcast`); - - if (opts.development) { - const wss = new WebSocket.Server({ noServer: true }); - - wss.on("connection", function connection(ws) { - ws.send("welcome"); - }); - - servers.forEach(function addWSHandler(server) { - server.on("upgrade", function upgrade(request, socket, head) { - const { pathname } = parse(request.url || ""); - if (pathname === PATHS.WS) { - wss.handleUpgrade(request, socket, head, function done(ws) { - wss.emit("connection", ws, request); - }); - } else { - socket.destroy(); - } - }); - }); - const sendToAllClients = function (data: object): void { - wss.clients.forEach(function each(client) { - if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify(data)); - } - }); - }; - const watchingFolder = opts.source ?? opts.folder; - logger.info(`watching ${watchingFolder} for change`); - - chokidar.watch(watchingFolder).on("change", (path, stats) => { - logger.info(`changed ${path}`); - - if (opts.onUpdate) { - sendToAllClients({ type: "file-updated-start", data: { path } }); - opts - .onUpdate() - .then((result) => { - sendToAllClients({ - type: "file-updated-done", - data: { path, result }, - }); - }) - .catch((error) => { - sendToAllClients({ - type: "file-updated-failed", - data: { path, error }, - }); - }); + const httpServer = http.createServer(app); + const httpPort = opts.port; + const httpsServer = https.createServer(httpServerOptions, app); + const httpsPort = opts.port + 1; + const servers = [httpServer, httpsServer]; + + logger.info(`Dev server. Endpoints:`); + logger.info(` ${PATHS.APP}: where root application can be tested`); + logger.info(` ${PATHS.EXAMPLE}: where examples can be found and browse`); + logger.info(` ${PATHS.WS}: websocket for live reloading`); + + const wss = new WebSocket.Server({ noServer: true }); + + wss.on("connection", function connection(ws) { + ws.send("welcome"); + }); + + servers.forEach(function addWSHandler(server) { + server.on("upgrade", function upgrade(request, socket, head) { + const { pathname } = parse(request.url || ""); + if (pathname === PATHS.WS) { + wss.handleUpgrade(request, socket, head, function done(ws) { + wss.emit("connection", ws, request); + }); } else { - sendToAllClients({ type: "file-change", data: { path } }); + socket.destroy(); } }); + }); - if (opts.onUpdate) opts.onUpdate(); - - app.get(PATHS.EXAMPLE, function (req: any, res: any) { - res.set("Content-Type", "text/html"); - res.send( - storiesHtml - .replace( - "__EXAMPLES_JS_FILE_LOCATION__", - opts.examplesLocationJs ?? `.${PATHS.APP}/stories.js`, - ) - .replace( - "__EXAMPLES_CSS_FILE_LOCATION__", - opts.examplesLocationCss ?? `.${PATHS.APP}/stories.css`, - ), - ); - }); - - app.get(PATHS.NOTIFY, function (req: any, res: any) { - res.send("ok"); - }); - - servers.forEach(function startServer(server, index) { - logger.info(`serving ${opts.folder} on ${opts.port + index}`); - server.listen(opts.port + index); + const sendToAllClients = function (data: object): void { + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(JSON.stringify(data)); + } }); - } + }; + const watchingFolder = opts.source ?? opts.folder; + logger.info(`watching ${watchingFolder} for changes`); + + chokidar.watch(watchingFolder).on("change", (path, stats) => { + logger.info(`changed: ${path}`); + + if (opts.onSourceUpdate) { + sendToAllClients({ type: "file-updated-start", data: { path } }); + opts + .onSourceUpdate() + .then((result) => { + sendToAllClients({ + type: "file-updated-done", + data: { path, result }, + }); + }) + .catch((error) => { + sendToAllClients({ + type: "file-updated-failed", + data: { path, error }, + }); + }); + } else { + sendToAllClients({ type: "file-change", data: { path } }); + } + }); + + if (opts.onSourceUpdate) opts.onSourceUpdate(); + + app.get(PATHS.EXAMPLE, function (req: any, res: any) { + res.set("Content-Type", "text/html"); + res.send( + storiesHtml + .replace( + "__EXAMPLES_JS_FILE_LOCATION__", + opts.examplesLocationJs ?? `.${PATHS.APP}/stories.js`, + ) + .replace( + "__EXAMPLES_CSS_FILE_LOCATION__", + opts.examplesLocationCss ?? `.${PATHS.APP}/stories.css`, + ), + ); + }); + + logger.info(`Serving ${opts.folder} on ${httpPort}: plain HTTP`); + httpServer.listen(httpPort); + logger.info(`Serving ${opts.folder} on ${httpsPort}: HTTP + TLS`); + httpsServer.listen(httpsPort); } diff --git a/packages/web-util/src/tests/index.ts b/packages/web-util/src/tests/index.ts deleted file mode 100644 index 2c0d929f8..000000000 --- a/packages/web-util/src/tests/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./hook.js"; -// export * from "./axios.js" -- cgit v1.2.3