diff --git a/.copier-answers.yaml b/.copier-answers.yaml index f472638..3b639ce 100644 --- a/.copier-answers.yaml +++ b/.copier-answers.yaml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 9b579a3 +_commit: 9498b78 _src_path: https://github.com/python-project-templates/base.git add_docs: true add_extension: cppjswasm diff --git a/js/build.mjs b/js/build.mjs index 4877fa3..551bf34 100644 --- a/js/build.mjs +++ b/js/build.mjs @@ -1,92 +1,42 @@ -import { NodeModulesExternal } from "@finos/perspective-esbuild-plugin/external.js"; -import { build } from "@finos/perspective-esbuild-plugin/build.js"; -import { transform } from "lightningcss"; +import { bundle } from "./tools/bundle.mjs"; +import { bundle_css } from "./tools/css.mjs"; +import { node_modules_external } from "./tools/externals.mjs"; import { getarg } from "./tools/getarg.mjs"; + +import { transform } from "lightningcss"; import fs from "fs"; import cpy from "cpy"; -const DEBUG = getarg("--debug"); - -const COMMON_DEFINE = { - global: "window", - "process.env.DEBUG": `${DEBUG}`, -}; - -const BUILD = [ +const BUNDLES = [ { - define: COMMON_DEFINE, entryPoints: ["src/ts/index.ts"], - plugins: [NodeModulesExternal()], - format: "esm", - loader: { - ".css": "text", - ".html": "text", - }, + plugins: [node_modules_external()], outfile: "dist/esm/index.js", }, { - define: COMMON_DEFINE, entryPoints: ["src/ts/index.ts"], - plugins: [], - format: "esm", - loader: { - ".css": "text", - ".html": "text", - }, outfile: "dist/cdn/index.js", }, ]; -async function compile_css() { - const process_path = (path) => { - const outpath = path.replace("src/css", "dist/css"); - fs.mkdirSync(outpath, { recursive: true }); - - fs.readdirSync(path, { withFileTypes: true }).forEach((entry) => { - const input = `${path}/${entry.name}`; - const output = `${outpath}/${entry.name}`; - - if (entry.isDirectory()) { - process_path(input); - } else if (entry.isFile() && entry.name.endsWith(".css")) { - const source = fs.readFileSync(input); - const { code } = transform({ - filename: entry.name, - code: source, - minify: !DEBUG, - sourceMap: false, - }); - fs.writeFileSync(output, code); - } - }); - }; +async function build() { + // Bundle css + await bundle_css(); - process_path("src/css"); -} - -async function copy_html() { + // Copy HTML fs.mkdirSync("dist/html", { recursive: true }); cpy("src/html/*", "dist/html"); - // also copy to top level cpy("src/html/*", "dist/"); -} -async function copy_img() { + // Copy images fs.mkdirSync("dist/img", { recursive: true }); cpy("src/img/*", "dist/img"); -} -async function copy_to_python() { + await Promise.all(BUNDLES.map(bundle)).catch(() => process.exit(1)); + + // Copy from dist to python fs.mkdirSync("../python_template_cppjswasm/extension", { recursive: true }); cpy("dist/**/*", "../python_template_cppjswasm/extension"); } -async function build_all() { - await compile_css(); - await copy_html(); - await copy_img(); - await Promise.all(BUILD.map(build)).catch(() => process.exit(1)); - await copy_to_python(); -} - -build_all(); +build(); diff --git a/js/package.json b/js/package.json index 8c1a4ca..f6420a4 100644 --- a/js/package.json +++ b/js/package.json @@ -48,10 +48,9 @@ }, "dependencies": {}, "devDependencies": { - "@finos/perspective-esbuild-plugin": "^3.2.1", - "@playwright/test": "^1.59.0", + "@playwright/test": "^1.59.1", "cpy": "^13.2.1", - "esbuild": "^0.27.2", + "esbuild": "^0.27.3", "lightningcss": "^1.29.3", "http-server": "^14.1.1", "nodemon": "^3.1.10", diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index f4bf28d..aa803af 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -8,17 +8,14 @@ importers: .: devDependencies: - '@finos/perspective-esbuild-plugin': - specifier: ^3.2.1 - version: 3.2.1 '@playwright/test': - specifier: ^1.59.0 + specifier: ^1.59.1 version: 1.59.1 cpy: specifier: ^13.2.1 version: 13.2.1 esbuild: - specifier: ^0.27.2 + specifier: ^0.27.3 version: 0.27.3 http-server: specifier: ^14.1.1 @@ -197,9 +194,6 @@ packages: cpu: [x64] os: [win32] - '@finos/perspective-esbuild-plugin@3.2.1': - resolution: {integrity: sha512-6EqacFm5rGA0EtzD2p9RuxtVLEci9AEC4NPiOCpqFdgxxX8Msv8LlL7Ml3opxyaEeKpndVsLFdOD5U+PNaaa+A==} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -294,10 +288,6 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -430,10 +420,6 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -780,38 +766,12 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - nodemon@3.1.11: resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==} engines: {node: '>=10'} @@ -1083,11 +1043,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1096,9 +1051,6 @@ packages: resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} hasBin: true - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -1141,17 +1093,11 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -1172,9 +1118,6 @@ packages: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - snapshots: '@esbuild/aix-ppc64@0.27.3': @@ -1255,13 +1198,6 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@finos/perspective-esbuild-plugin@3.2.1': - dependencies: - node-fetch: 2.7.0 - tar: 6.2.1 - transitivePeerDependencies: - - encoding - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1373,8 +1309,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chownr@2.0.0: {} - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -1594,10 +1528,6 @@ snapshots: dependencies: is-callable: 1.2.7 - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - fsevents@2.3.2: optional: true @@ -1937,27 +1867,10 @@ snapshots: minimist@1.2.8: {} - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - mkdirp@1.0.4: {} - ms@2.1.3: {} nice-try@1.0.5: {} - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - nodemon@3.1.11: dependencies: chokidar: 3.6.0 @@ -2269,23 +2182,12 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - tar@6.2.1: - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 touch@3.1.1: {} - tr46@0.0.3: {} - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -2343,17 +2245,10 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - webidl-conversions@3.0.1: {} - whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -2398,5 +2293,3 @@ snapshots: which@1.3.1: dependencies: isexe: 2.0.0 - - yallist@4.0.0: {} diff --git a/js/tools/bundle.mjs b/js/tools/bundle.mjs new file mode 100644 index 0000000..b417871 --- /dev/null +++ b/js/tools/bundle.mjs @@ -0,0 +1,59 @@ +import { getarg } from "./getarg.mjs"; +import esbuild from "esbuild"; + +const DEBUG = getarg("--debug"); + +const CUTOFF_PERCENT = 0.02; + +const COMMON_DEFINE = { + global: "window", + "process.env.DEBUG": `${DEBUG}`, +}; + +const COMMON_LOADER = { + ".css": "text", + ".html": "text", + ".jsx": "jsx", + ".png": "file", + ".ttf": "file", + ".wasm": "file", +}; + +const DEFAULT_BUILD = { + target: ["es2022"], + bundle: true, + format: "esm", + minify: DEBUG, + sourcemap: true, + metafile: true, + entryNames: "[name]", + chunkNames: "[name]", + assetNames: "[name]", + define: COMMON_DEFINE, + loader: COMMON_LOADER, + plugins: [], +}; + +export const bundle = async (config) => { + const result = await esbuild.build({ + ...DEFAULT_BUILD, + ...config, + }); + + if (result.metafile) { + for (const output of Object.keys(result.metafile.outputs)) { + const { inputs, bytes } = result.metafile.outputs[output]; + for (const input of Object.keys(inputs)) { + if (inputs[input].bytesInOutput / bytes < CUTOFF_PERCENT) { + delete inputs[input]; + } + } + } + + const text = await esbuild.analyzeMetafile(result.metafile, { + color: true, + }); + + console.log(text); + } +}; diff --git a/js/tools/css.mjs b/js/tools/css.mjs new file mode 100644 index 0000000..4ac3594 --- /dev/null +++ b/js/tools/css.mjs @@ -0,0 +1,52 @@ +import { getarg } from "./getarg.mjs"; + +import { bundleAsync } from "lightningcss"; +import fs from "fs"; +import path from "path"; + +const DEBUG = getarg("--debug"); + +const DEFAULT_RESOLVER = { + resolve(specifier, originatingFile) { + if (/^https?:\/\//.test(specifier)) { + return specifier; + } + + if (specifier.startsWith("perspective-viewer-")) { + const viewerCssDir = path.resolve( + "node_modules/@perspective-dev/viewer/dist/css", + ); + const normalized = specifier.replace(/^perspective-viewer-/, ""); + const normalizedPath = path.join(viewerCssDir, normalized); + if (fs.existsSync(normalizedPath)) { + return normalizedPath; + } + return path.join(viewerCssDir, specifier); + } + return path.resolve(path.dirname(originatingFile), specifier); + }, +}; + +const bundle_one = async (file, resolver) => { + const { code } = await bundleAsync({ + filename: path.resolve(file), + minify: !DEBUG, + sourceMap: false, + resolver: resolver || DEFAULT_RESOLVER, + }); + const outName = path.basename(file); + fs.mkdirSync("./dist", { recursive: true }); + fs.writeFileSync(path.join("./dist", outName), code); +}; + +export const bundle_css = async (root = "src/css/index.css", resolver = null) => { + const resolved = path.resolve(root); + if (fs.statSync(resolved).isDirectory()) { + const files = fs.readdirSync(resolved).filter((f) => f.endsWith(".css")); + for (const file of files) { + await bundle_one(path.join(root, file), resolver); + } + } else { + await bundle_one(root, resolver); + } +} diff --git a/js/tools/externals.mjs b/js/tools/externals.mjs new file mode 100644 index 0000000..0ee4835 --- /dev/null +++ b/js/tools/externals.mjs @@ -0,0 +1,18 @@ +export const node_modules_external = (whitelist) => { + function setup(build) { + build.onResolve({ filter: /^[A-Za-z0-9\@]/ }, (args) => { + if (!whitelist || !args.path.startsWith(whitelist)) { + return { + path: args.path, + external: true, + namespace: "skip-node-modules", + }; + } + }); + } + + return { + name: "node_modules_external", + setup, + }; +}; diff --git a/js/tools/getarg.mjs b/js/tools/getarg.mjs index 30793a7..9cfe88c 100644 --- a/js/tools/getarg.mjs +++ b/js/tools/getarg.mjs @@ -20,4 +20,4 @@ export const getarg = (flag, ...args) => { }) .join(" "); } - }; \ No newline at end of file + }; diff --git a/js/tools/html.mjs b/js/tools/html.mjs new file mode 100644 index 0000000..e69de29