From 0dbd604b181056fe93af069377a8ceb0c1391543 Mon Sep 17 00:00:00 2001 From: Gordon Smith Date: Fri, 27 Jun 2025 13:23:55 +0100 Subject: [PATCH] fix: Improve type-leaks test Was missing some node deps still in devDeps Signed-off-by: Gordon Smith --- eslint.config.js | 2 +- package-lock.json | 110 +++---- package.json | 1 + packages/comms/esbuild.js | 2 +- packages/comms/src/clienttools/eclMeta.ts | 4 +- packages/comms/src/clienttools/eclcc.ts | 8 +- packages/comms/src/ecl/scope.ts | 4 - packages/comms/src/index.node.ts | 2 +- packages/esbuild-plugins/src/build.ts | 128 +++++--- packages/markdown-it-plugins/esbuild.js | 18 +- packages/markdown-it-plugins/package.json | 6 +- packages/util/esbuild.js | 4 - tests/package.json | 26 ++ tests/type-leaks/type-dependencies.spec.ts | 335 ++++++++++++++++----- vitest.workspace.ts | 1 + 15 files changed, 445 insertions(+), 206 deletions(-) delete mode 100644 packages/util/esbuild.js create mode 100644 tests/package.json diff --git a/eslint.config.js b/eslint.config.js index e0d73857b0..47cff8ab6d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -52,7 +52,7 @@ export default [ max: 1 } ], - "no-console": [1, { + "no-console": ["error", { "allow": ["info", "warn", "error"] }], "func-call-spacing": ["error", "never"], diff --git a/package-lock.json b/package-lock.json index 09282fc05d..123d3ce8b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "demos/*" ], "devDependencies": { + "@types/esbuild-copy-static-files": "0.1.4", "@typescript-eslint/eslint-plugin": "8.29.0", "@typescript-eslint/parser": "8.29.0", "@vitest/browser": "3.2.4", @@ -793,7 +794,6 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", - "extraneous": true, "inBundle": true, "license": "MIT", "engines": { @@ -5616,7 +5616,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", - "dev": true, "license": "MIT", "dependencies": { "@shikijs/engine-javascript": "2.5.0", @@ -5631,7 +5630,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", - "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0", @@ -5643,7 +5641,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", - "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0", @@ -5654,7 +5651,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", - "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0" @@ -5664,7 +5660,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", - "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0" @@ -5685,7 +5680,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", - "dev": true, "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -5696,7 +5690,6 @@ "version": "10.0.2", "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, "license": "MIT" }, "node_modules/@sigstore/bundle": { @@ -5845,8 +5838,8 @@ "version": "0.5.17", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -5985,15 +5978,15 @@ "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==", - "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/command-line-usage": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/command-line-usage/-/command-line-usage-5.0.4.tgz", "integrity": "sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==", - "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/d3-array": { "version": "1.2.12", @@ -6158,6 +6151,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/esbuild-copy-static-files": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/esbuild-copy-static-files/-/esbuild-copy-static-files-0.1.4.tgz", + "integrity": "sha512-Xv4zewzzit6lccvS9sPcte2pe1xLDaKHm3KcjUEJuf1DV06oy2d0/DgjoGCHyM1bSMOrf1cFtB9pQmXJeVnJ0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "esbuild": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -6211,7 +6215,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "*" @@ -6298,7 +6301,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "*" @@ -6427,7 +6429,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, "license": "MIT" }, "node_modules/@types/web-bluetooth": { @@ -6651,7 +6652,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, "license": "ISC" }, "node_modules/@vitejs/plugin-react": { @@ -7638,8 +7638,8 @@ "version": "19.0.1", "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-19.0.1.tgz", "integrity": "sha512-APmMLzS4qbTivLrPdQXexGM4JRr+0g62QDaobzEvip/FdQIrv2qLy0mD5Qdmw4buydtVJgbFeKR8f59I6PPGDg==", - "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/helpers": "^0.5.11", "@types/command-line-args": "^5.2.3", @@ -7659,8 +7659,8 @@ "version": "20.19.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -7669,8 +7669,8 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/aproba": { "version": "2.0.0", @@ -7699,8 +7699,8 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12.17" } @@ -8292,7 +8292,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -8320,7 +8319,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -8337,8 +8335,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.1.2" }, @@ -8353,7 +8351,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -8364,7 +8361,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -8678,7 +8674,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -8689,8 +8684,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-back": "^6.2.2", "find-replace": "^5.0.2", @@ -8713,8 +8708,8 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-back": "^6.2.2", "chalk-template": "^0.4.0", @@ -9997,7 +9992,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -10045,7 +10039,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, "license": "MIT", "dependencies": { "dequal": "^2.0.0" @@ -10419,7 +10412,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", - "dev": true, "license": "MIT" }, "node_modules/emojis-list": { @@ -11374,8 +11366,8 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=14" }, @@ -11445,8 +11437,8 @@ "version": "24.12.23", "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.12.23.tgz", "integrity": "sha512-dLVCAISd5mhls514keQzmEG6QHmUUsNuWsb4tFafIUwvvgDjXhtfAYSKOzt5SWOy+qByV5pbsDZ+Vb7HUOBEdA==", - "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/flatted": { "version": "3.3.3", @@ -12339,7 +12331,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12424,7 +12415,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -12448,7 +12438,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -12560,7 +12549,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -13887,7 +13875,7 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/json-bignum/-/json-bignum-0.0.3.tgz", "integrity": "sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==", - "dev": true, + "peer": true, "engines": { "node": ">=0.8" } @@ -14573,8 +14561,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.ismatch": { "version": "4.4.0", @@ -14826,7 +14814,6 @@ "version": "13.2.0", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -15117,7 +15104,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -15138,7 +15124,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -15155,7 +15140,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -15177,7 +15161,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -15194,7 +15177,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -16314,7 +16296,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex-xs": "^1.0.0", @@ -17215,7 +17196,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -17732,7 +17712,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", - "dev": true, "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" @@ -17742,7 +17721,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "dev": true, "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" @@ -17752,7 +17730,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "dev": true, "license": "MIT" }, "node_modules/regexp.prototype.flags": { @@ -18501,7 +18478,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", - "dev": true, "license": "MIT", "dependencies": { "@shikijs/core": "2.5.0", @@ -18810,7 +18786,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -19107,7 +19082,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", @@ -19263,7 +19237,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -19301,8 +19274,8 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-back": "^6.2.2", "wordwrapjs": "^5.1.0" @@ -19748,7 +19721,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -20120,8 +20092,8 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12.17" } @@ -20211,7 +20183,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -20225,7 +20196,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -20239,7 +20209,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -20253,7 +20222,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -20269,7 +20237,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -20454,7 +20421,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -20469,7 +20435,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -21898,8 +21863,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12.17" } @@ -22222,7 +22187,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -22832,15 +22796,15 @@ }, "devDependencies": { "@hpcc-js/esbuild-plugins": "^1.4.2", - "apache-arrow": "19.0.1", "d3-dsv": "3.0.1", "d3-fetch": "3.0.1", "dotenv": "16.4.7", - "shiki": "2.5.0", "tsx": "4.19.3" }, "peerDependencies": { - "markdown-it": "14.1.0" + "apache-arrow": "19.0.1", + "markdown-it": "14.1.0", + "shiki": "2.5.0" } }, "packages/markdown-it-plugins/node_modules/commander": { diff --git a/package.json b/package.json index 70b7eedc4c..7d5d347f69 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "update-major": "run-p update-major-workspaces update-major root" }, "devDependencies": { + "@types/esbuild-copy-static-files": "0.1.4", "@typescript-eslint/eslint-plugin": "8.29.0", "@typescript-eslint/parser": "8.29.0", "@vitest/browser": "3.2.4", diff --git a/packages/comms/esbuild.js b/packages/comms/esbuild.js index 8b09d8988e..3ae5d79330 100644 --- a/packages/comms/esbuild.js +++ b/packages/comms/esbuild.js @@ -2,6 +2,6 @@ import { nodeTpl, nodeBoth } from "@hpcc-js/esbuild-plugins"; // config --- await Promise.all([ - nodeBoth("src/index.node.ts", "dist/node/index"), + nodeBoth("src/index.node.ts", "dist/node/index", { packages: "bundle" }), nodeTpl("utils/index.ts", "lib-esm/index") ]); diff --git a/packages/comms/src/clienttools/eclMeta.ts b/packages/comms/src/clienttools/eclMeta.ts index 6d85d28751..582d663fbd 100644 --- a/packages/comms/src/clienttools/eclMeta.ts +++ b/packages/comms/src/clienttools/eclMeta.ts @@ -1,5 +1,5 @@ -import * as fs from "fs"; -import * as path from "path"; +import * as fs from "node:fs"; +import * as path from "node:path"; import { Dictionary, DictionaryNoCase, find, SAXStackParser, scopedLogger, XMLNode } from "@hpcc-js/util"; import { ClientTools, locateClientTools } from "./eclcc.ts"; diff --git a/packages/comms/src/clienttools/eclcc.ts b/packages/comms/src/clienttools/eclcc.ts index 12ea12497b..6d2192a9a8 100644 --- a/packages/comms/src/clienttools/eclcc.ts +++ b/packages/comms/src/clienttools/eclcc.ts @@ -1,7 +1,7 @@ -import * as cp from "child_process"; -import * as fs from "fs"; -import * as os from "os"; -import * as path from "path"; +import * as cp from "node:child_process"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; import * as tmp from "tmp"; import { exists, scopedLogger, xml2json, XMLNode } from "@hpcc-js/util"; diff --git a/packages/comms/src/ecl/scope.ts b/packages/comms/src/ecl/scope.ts index 87508aaa9f..e34cae9ebd 100644 --- a/packages/comms/src/ecl/scope.ts +++ b/packages/comms/src/ecl/scope.ts @@ -1,11 +1,7 @@ import { StateObject, StringAnyMap } from "@hpcc-js/util"; -// import { utcFormat, utcParse } from "d3-time-format"; import { WsWorkunits } from "../services/wsWorkunits.ts"; import { Workunit } from "./workunit.ts"; -// const formatter = utcFormat("%Y-%m-%dT%H:%M:%S.%LZ"); -// const parser = utcParse("%Y-%m-%dT%H:%M:%S.%LZ"); - export interface AttributeEx extends WsWorkunits.Property { FormattedEnd?: string; } diff --git a/packages/comms/src/index.node.ts b/packages/comms/src/index.node.ts index 24a1beb204..391fecbd8f 100644 --- a/packages/comms/src/index.node.ts +++ b/packages/comms/src/index.node.ts @@ -6,7 +6,7 @@ root.DOMParser = DOMParser; // fetch polyfill --- import fetch, { Headers, Request, Response, } from "node-fetch"; -import * as https from "https"; +import * as https from "node:https"; import { Agent, setGlobalDispatcher } from "undici"; if (typeof root.fetch === "undefined") { diff --git a/packages/esbuild-plugins/src/build.ts b/packages/esbuild-plugins/src/build.ts index 4405575f99..afd9564598 100644 --- a/packages/esbuild-plugins/src/build.ts +++ b/packages/esbuild-plugins/src/build.ts @@ -4,19 +4,21 @@ import * as path from "path"; import * as esbuild from "esbuild"; import type { BuildOptions, Format, Loader, Plugin } from "esbuild"; import { umdWrapper } from "esbuild-plugin-umd-wrapper"; +import * as copyStaticFiles from "esbuild-copy-static-files"; import { inlineCSS } from "./inline-css.ts"; import { rebuildLogger } from "./rebuild-logger.ts"; -//@ts-ignore -import _copyStaticFiles from "esbuild-copy-static-files"; -export const copyStaticFiles: Plugin = _copyStaticFiles; +export { copyStaticFiles }; export const pkg = JSON.parse(readFileSync(path.join(process.cwd(), "./package.json"), "utf8")); export const NODE_MJS = pkg.type === "module" ? "js" : "mjs"; export const NODE_CJS = pkg.type === "module" ? "cjs" : "js"; -export async function buildWatch(input: string, format: Format | "umd" = "esm", external: string[] = [], config: BuildOptions, isDevelopment: boolean = process.argv.includes("--development"), isWatch: boolean = process.argv.includes("--watch")): Promise { +export async function buildWatch(inputs: string[] | Record | { in: string, out: string }[], config: BuildOptions): Promise { + const isDevelopment = process.argv.includes("--development"); + const isWatch = process.argv.includes("--watch"); const isProduction = !isDevelopment; + if (isProduction && existsSync(path.join(process.cwd(), "../../package.json"))) { const rootPkg = JSON.parse(readFileSync(path.join(process.cwd(), "../../package.json"), "utf8")); writeFileSync(path.join(process.cwd(), "src/__package__.ts"), `\ @@ -26,20 +28,38 @@ export const BUILD_VERSION = "${rootPkg.version}"; `, "utf8"); } - const ctx = await esbuild.context({ - entryPoints: [input], - format: format as Format, + config = { + entryPoints: inputs, + format: "esm", bundle: true, minify: isProduction, sourcemap: true, - external, + external: [ + ...config.external ?? [] + ], ...config, + loader: { + ...config.loader + }, + outExtension: { + ...config.outExtension + }, + banner: { + ...config.banner + }, + footer: { + ...config.footer + }, plugins: [ ...(isWatch ? [rebuildLogger(config)] : []), - ...(config.plugins ?? []), + ...config.plugins ?? [], inlineCSS() + ], + nodePaths: [ + ...config.nodePaths ?? [] ] - }); + }; + const ctx = await esbuild.context(config); if (isWatch) { await ctx.watch(); @@ -68,35 +88,54 @@ export type TplOptions = { supported?: Record; alias?: Record; define?: { [key: string]: string }; + packages?: "bundle" | "external" | "auto"; }; -export function browserTpl(input: string, output: string, { format = "esm", globalName, libraryName, keepNames, external = [], plugins = [], alias = {}, define = {}, loader = {} }: TplOptions = {}) { - return buildWatch(input, format, external, { - outfile: `${output}.${format === "esm" ? "js" : `${format}.js`}`, +function autoExternal(external?: string[]): string[] { + return [ + ...pkg.dependencies ? Object.keys(pkg.dependencies) : [], ...pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : [], + ...external ?? [] + ]; +} + +export function browserTpl(input: string, output: string, options: TplOptions = {}) { + options.format = options.format ?? "esm"; + + return buildWatch([input], { + format: options.format === "umd" ? "esm" : options.format, + external: options.external ?? [], + outfile: `${output}.${options.format === "esm" ? "js" : `${options.format}.js`}`, platform: "browser", target: "es2022", - globalName, - keepNames, - plugins: format === "umd" ? [umdWrapper({ libraryName }), ...plugins] : [...plugins], - alias, - define, - loader - } as BuildOptions); + globalName: options.globalName, + keepNames: options.keepNames, + plugins: options.format === "umd" ? [umdWrapper({ libraryName: options.libraryName }), ...options.plugins ?? []] : [...options.plugins ?? []], + alias: options.alias, + define: options.define, + loader: options.loader, + }); } -export function nodeTpl(input: string, output: string, { format = "esm", external = [], supported = {} }: TplOptions = {}) { - return buildWatch(input, format, external, { - outfile: `${output}.${format === "esm" ? NODE_MJS : NODE_CJS}`, +export function nodeTpl(input: string, output: string, options: TplOptions = {}) { + options.packages = options.packages ?? "external"; + if (options.packages === "auto") { + options.external = autoExternal(options.external); + } + + return buildWatch([input], { + format: options.format === "umd" ? "esm" : options.format, + outfile: `${output}.${options.format === "esm" ? NODE_MJS : NODE_CJS}`, platform: "node", - target: "node20", - packages: "external", - supported + target: "node22", + packages: options.packages === "auto" ? "bundle" : options.packages, }); } -export function neutralTpl(input: string, output: string, { format = "esm", globalName, libraryName, keepNames, external = [] }: TplOptions = {}) { +export function neutralTpl(input: string, output: string, options: TplOptions = {}) { + options.format = options.format ?? "esm"; + let postfix = ""; - switch (format) { + switch (options.format) { case "iife": postfix = "iife.js"; break; @@ -110,35 +149,36 @@ export function neutralTpl(input: string, output: string, { format = "esm", glob postfix = "umd.js"; break; default: - throw new Error(`Unknown format: ${format}`); + throw new Error(`Unknown format: ${options.format}`); } - return buildWatch(input, format, external, { - outfile: `${output}.${format === "esm" ? "js" : `${format}.js`}`, + return buildWatch([input], { + format: options.format === "umd" ? "esm" : options.format, + outfile: `${output}.${options.format === "esm" ? "js" : `${options.format}.js`}`, platform: "neutral", target: "es2022", - globalName, - keepNames, - plugins: format === "umd" ? [umdWrapper({ libraryName })] : [] as Plugin[] - } as BuildOptions); + globalName: options.globalName, + keepNames: options.keepNames, + plugins: options.format === "umd" ? [umdWrapper({ libraryName: options.libraryName })] : [] as Plugin[] + }); } -export function browserBoth(input: string, output: string, globalName?: string, libraryName?: string, external: string[] = []) { +export function browserBoth(input: string, output: string, options: TplOptions = {}) { return Promise.all([ - browserTpl(input, output, { format: "esm", globalName, libraryName, external }), - browserTpl(input, output, { format: "umd", globalName, libraryName, external }) + browserTpl(input, output, { format: "esm", ...options }), + browserTpl(input, output, { format: "umd", ...options }) ]); } -export function nodeBoth(input: string, output: string, external: string[] = []) { +export function nodeBoth(input: string, output: string, options: TplOptions = {}) { return Promise.all([ - nodeTpl(input, output, { format: "esm", external }), - nodeTpl(input, output, { format: "cjs", external }) + nodeTpl(input, output, { format: "esm", ...options }), + nodeTpl(input, output, { format: "cjs", ...options }) ]); } -export function bothTpl(input: string, output: string, globalName?: string, libraryName?: string, external: string[] = []) { +export function bothTpl(input: string, output: string, options: TplOptions = {}) { return Promise.all([ - browserBoth(input, output, globalName, libraryName, external), - nodeTpl(input, output, { format: "cjs", external }) + browserBoth(input, output, { ...options }), + nodeTpl(input, output, { format: "cjs", ...options }) ]); } diff --git a/packages/markdown-it-plugins/esbuild.js b/packages/markdown-it-plugins/esbuild.js index 29c017bfda..4f4163979e 100644 --- a/packages/markdown-it-plugins/esbuild.js +++ b/packages/markdown-it-plugins/esbuild.js @@ -1,7 +1,21 @@ import { nodeTpl } from "@hpcc-js/esbuild-plugins"; +import pkg from "./package.json" with { type: "json" }; // config --- await Promise.all([ - nodeTpl("src/loader.ts", "dist/loader.node", { supported: { "dynamic-import": true } }), - nodeTpl("src/ecl-lang/index.ts", "dist/ecl-lang"), + nodeTpl("src/loader.ts", "dist/loader.node", { + packages: "bundle", + supported: { "dynamic-import": true }, + external: [ + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}) + ] + }), + nodeTpl("src/ecl-lang/index.ts", "dist/ecl-lang", { + packages: "bundle", + external: [ + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}) + ] + }), ]); diff --git a/packages/markdown-it-plugins/package.json b/packages/markdown-it-plugins/package.json index 3d0b281815..8652aad4e4 100644 --- a/packages/markdown-it-plugins/package.json +++ b/packages/markdown-it-plugins/package.json @@ -58,15 +58,15 @@ "@types/markdown-it": "14.1.2" }, "peerDependencies": { - "markdown-it": "14.1.0" + "apache-arrow": "19.0.1", + "markdown-it": "14.1.0", + "shiki": "2.5.0" }, "devDependencies": { "@hpcc-js/esbuild-plugins": "^1.4.2", - "apache-arrow": "19.0.1", "d3-dsv": "3.0.1", "d3-fetch": "3.0.1", "dotenv": "16.4.7", - "shiki": "2.5.0", "tsx": "4.19.3" }, "repository": { diff --git a/packages/util/esbuild.js b/packages/util/esbuild.js deleted file mode 100644 index 5fd6c47051..0000000000 --- a/packages/util/esbuild.js +++ /dev/null @@ -1,4 +0,0 @@ -import { neutralTpl } from "@hpcc-js/esbuild-plugins"; - -// config --- -await neutralTpl("src/index.ts", "dist/index"); diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 0000000000..f7a1c57f6b --- /dev/null +++ b/tests/package.json @@ -0,0 +1,26 @@ +{ + "name": "hpcc-js-tests", + "version": "1.0.0", + "description": "Test orchestrator for HPCC Visualization Framework - runs all test suites", + "private": true, + "type": "module", + "scripts": { + "test": "npm run test-all", + "test-all": "npm run test-node-cjs && npm run test-node-esm && npm run test-browser-esm && npm run test-browser-umd && npm run test-type-leaks", + "test-node": "npm run test-node-cjs && npm run test-node-esm", + "test-browser": "npm run test-browser-esm && npm run test-browser-umd", + "test-node-cjs": "cd node-cjs && npm install && npm test", + "test-node-esm": "cd node-esm && npm install && npm test", + "test-browser-esm": "cd browser-esm && npm install && npm test", + "test-browser-umd": "cd browser-umd && npm install && npm test", + "test-type-leaks": "cd type-leaks && npm install && npm test", + "install-all": "npm run install-node && npm run install-browser && npm run install-type-leaks", + "install-node": "cd node-cjs && npm install && cd ../node-esm && npm install", + "install-browser": "cd browser-esm && npm install && cd ../browser-umd && npm install", + "install-type-leaks": "cd type-leaks && npm install", + "clean": "npm run clean-node && npm run clean-browser && npm run clean-type-leaks", + "clean-node": "cd node-cjs && rm -rf node_modules package-lock.json && cd ../node-esm && rm -rf node_modules package-lock.json", + "clean-browser": "cd browser-esm && rm -rf node_modules package-lock.json && cd ../browser-umd && rm -rf node_modules package-lock.json", + "clean-type-leaks": "cd type-leaks && rm -rf node_modules package-lock.json" + } +} \ No newline at end of file diff --git a/tests/type-leaks/type-dependencies.spec.ts b/tests/type-leaks/type-dependencies.spec.ts index 493aac93f6..29fb57d753 100644 --- a/tests/type-leaks/type-dependencies.spec.ts +++ b/tests/type-leaks/type-dependencies.spec.ts @@ -1,10 +1,13 @@ import { describe, it, expect } from "vitest"; import { existsSync, readFileSync, readdirSync, statSync } from "fs"; -import { join, resolve } from "path"; +import { join, resolve, dirname } from "path"; +import { fileURLToPath } from "url"; import { glob } from "glob"; import { parse } from "@typescript-eslint/typescript-estree"; -const PACKAGES_DIR = resolve("../../packages"); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const PACKAGES_DIR = resolve(__dirname, "../../packages"); interface PackageInfo { name: string; @@ -88,6 +91,19 @@ function extractTypeReferences(filePath: string): Set { } } + // Export declarations (for re-exports like `export * from "package"`) + if (node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration") { + if (node.source && node.source.value) { + const exportSource = node.source.value; + if (!exportSource.startsWith(".") && !exportSource.startsWith("/")) { + const packageName = extractPackageName(exportSource); + if (packageName) { + typeReferences.add(packageName); + } + } + } + } + // Dynamic imports if (node.type === "ImportExpression" && node.source?.value) { const importSource = node.source.value; @@ -117,20 +133,26 @@ function extractTypeReferences(filePath: string): Set { // Ignore parse errors for now - we'll still catch obvious import statements console.warn(`Failed to parse ${filePath}:`, error); - // Fallback: use regex to find import statements + // Fallback: use regex to find import and export statements const content = readFileSync(filePath, "utf8"); - const importRegex = /(?:import|from|require\()\s*["\']([^"\']+)["\']/g; - let match; - - while ((match = importRegex.exec(content)) !== null) { - const importSource = match[1]; - if (!importSource.startsWith(".") && !importSource.startsWith("/")) { - const packageName = extractPackageName(importSource); - if (packageName) { - typeReferences.add(packageName); + const patterns = [ + IMPORT_EXPORT_PATTERNS.general, + IMPORT_EXPORT_PATTERNS.exportAll, + IMPORT_EXPORT_PATTERNS.exportNamed + ]; + + patterns.forEach(pattern => { + let match; + while ((match = pattern.exec(content)) !== null) { + const importSource = match[1]; + if (!importSource.startsWith(".") && !importSource.startsWith("/")) { + const packageName = extractPackageName(importSource); + if (packageName) { + typeReferences.add(packageName); + } } } - } + }); } return typeReferences; @@ -154,95 +176,272 @@ function extractPackageName(importPath: string): string | null { return null; } +/** + * Check if a string is a valid NPM package name + */ +function isValidPackageName(name: string): boolean { + // NPM package names must: + // - Be lowercase + // - Not contain URL-unsafe characters + // - Not contain spaces or other problematic characters + // - Be reasonable length + return /^[a-z0-9@/_.-]+$/.test(name) && + name.length <= 214 && + name.length > 0 && + !name.includes("..") && // No relative paths + !name.startsWith(".") && // No hidden files + !name.startsWith("-") && // No leading dash + !name.endsWith("-"); // No trailing dash +} + +/** + * Common regex patterns for extracting import/export statements + */ +const IMPORT_EXPORT_PATTERNS = { + // ES6 imports: import ... from "package" (not template literals) + esImport: /\bimport\s+[^'"]*\s+from\s+["']([^"'${}]+)["']/g, + // CommonJS require: require("package") (not template literals, not property access) + commonjsRequire: /(? { + const runtimeReferences = new Set(); + + try { + const content = readFileSync(filePath, "utf8"); + + // More precise regex patterns to match import/require statements in bundled files + const patterns = [ + IMPORT_EXPORT_PATTERNS.esImport, + IMPORT_EXPORT_PATTERNS.commonjsRequire, + IMPORT_EXPORT_PATTERNS.dynamicImport + ]; + + // AMD/UMD define dependencies need special handling + const defineRegex = IMPORT_EXPORT_PATTERNS.amdDefine; + let defineMatch; + while ((defineMatch = defineRegex.exec(content)) !== null) { + const depList = defineMatch[1]; + // More precise regex to match only valid quoted dependency strings + const deps = depList.match(/["']([a-zA-Z0-9@/_-][a-zA-Z0-9@/_.-]*)["']/g) || []; + for (const quotedDep of deps) { + const dep = quotedDep.slice(1, -1); // Remove quotes + if (dep && !dep.startsWith(".") && !dep.startsWith("/") && + !dep.includes("${") && !dep.includes("'") && !dep.includes('"') && + !dep.includes(" ") && !dep.includes(":") && !dep.includes("=") && // Exclude config-like strings + dep.length < 50 && dep.length > 0 && // Avoid false positives from long code snippets + /^[a-zA-Z0-9@/_-][a-zA-Z0-9@/_.-]*$/.test(dep)) { // Only valid package name characters + const packageName = extractPackageName(dep); + if (packageName && packageName !== "exports" && packageName !== "module" && + !["value", "dojo.parser", "right", "w"].includes(packageName)) { + runtimeReferences.add(packageName); + } + } + } + } + + // Process other patterns with better filtering to avoid false positives + for (const pattern of patterns) { + let match; + while ((match = pattern.exec(content)) !== null) { + const importSource = match[1]; + if (importSource && !importSource.startsWith(".") && !importSource.startsWith("/") && + !importSource.includes("${") && importSource.length < 100 && + // Filter out obvious false positives from CSS-like syntax or object literals + !/^,/.test(importSource) && // Don't start with comma + !/:$/.test(importSource) && // Don't end with colon + !/=$/.test(importSource) && // Don't end with equals + !/^[^a-zA-Z]/.test(importSource) && // Must start with letter + !/\s/.test(importSource)) { // No spaces + const packageName = extractPackageName(importSource); + if (packageName && packageName !== "exports" && packageName !== "module" && + // Additional filtering for known false positives + !packageName.startsWith(",") && !packageName.endsWith(":") && !packageName.endsWith("=") && + packageName.length > 1 && isValidPackageName(packageName)) { + runtimeReferences.add(packageName); + } + } + } + } + } catch (error) { + console.warn(`Failed to read bundled file ${filePath}:`, error); + } + + return runtimeReferences; +} + +describe("Package Dependencies", () => { + const packages = getPackages(); + + it("should have valid package.json files", () => { + expect(packages.length).toBeGreaterThan(0); + packages.forEach(pkg => { + expect(pkg.name).toBeTruthy(); + expect(pkg.packageJson).toBeTruthy(); + }); + }); + + it("should not have type dependencies that are not declared as dependencies or devDependencies", () => { + const errors: string[] = []; + + packages.forEach(pkg => { + const srcDir = join(pkg.path, "src"); + if (!existsSync(srcDir)) return; + + const tsFiles = glob.sync("**/*.ts", { + cwd: srcDir, + absolute: true, + ignore: ["**/*.d.ts", "**/*.spec.ts", "**/*.test.ts"] + }); + + const allTypeReferences = new Set(); + tsFiles.forEach(file => { + const refs = extractTypeReferences(file); + refs.forEach(ref => allTypeReferences.add(ref)); + }); + + allTypeReferences.forEach(ref => { + if (!pkg.dependencies.has(ref) && + !pkg.devDependencies.has(ref) && + !pkg.peerDependencies.has(ref)) { + errors.push(`${pkg.name}: Type reference '${ref}' not found in dependencies, devDependencies, or peerDependencies`); + } + }); + }); + + if (errors.length > 0) { + throw new Error(`Type dependency violations found:\n${errors.join("\n")}`); + } + }); + + it("should not reference third-party runtime dependencies unless they are in dependencies", () => { + const errors: string[] = []; + + // Known build-time/dev-only packages that should be ignored + const knownBuildTimeDeps = new Set([ + "dojo", "dgrid", "dojo.parser", "function", "some", + // False positives from bundled optional dependencies + "d3-time", // Bundled in @hpcc-js/comms but not actually required + "pnpapi", // Optional Yarn PnP dependency in bundled code + "utf-8-validate" // Optional WebSocket dependency in bundled code + ]); + + packages.forEach(pkg => { + // Check for bundled files in common output directories + const bundleDirs = ["dist"]; + const bundledFiles: string[] = []; + + bundleDirs.forEach(dir => { + const bundleDir = join(pkg.path, dir); + if (existsSync(bundleDir)) { + const jsFiles = glob.sync("**/*.{js,mjs,cjs}", { + cwd: bundleDir, + absolute: true, + ignore: ["**/*.min.js", "**/*.map", "**/*.d.ts"] + }); + bundledFiles.push(...jsFiles); + } + }); + + if (bundledFiles.length === 0) return; + + const allRuntimeReferences = new Set(); + bundledFiles.forEach(file => { + const refs = extractRuntimeReferences(file); + refs.forEach(ref => allRuntimeReferences.add(ref)); + }); + + // Filter out built-in modules, internal @hpcc-js packages, and known build-time deps + const externalReferences = Array.from(allRuntimeReferences).filter(ref => + !isBuiltInModule(ref) && + !knownBuildTimeDeps.has(ref) + ); + + externalReferences.forEach(ref => { + if (!pkg.dependencies.has(ref) && !pkg.peerDependencies.has(ref)) { + errors.push(`${pkg.name}: Runtime reference '${ref}' not found in dependencies or peerDependencies (found in bundled files)`); + } + }); + }); + + if (errors.length > 0) { + throw new Error(`Runtime dependency violations found:\n${errors.join("\n")}`); + } + }); +}); + describe("Package Type Dependencies", () => { const packages = getPackages(); for (const pkg of packages) { describe(`${pkg.name}`, () => { it("should not reference third-party types unless they are in dependencies", async () => { - // Get all TypeScript files in the package - const tsFiles = ["types/index.d.ts", "types/index.browser.d.ts", "types/index.node.d.ts"]; + // Get all TypeScript declaration files in the package + const typesDir = join(pkg.path, "types"); + if (!existsSync(typesDir)) return; + + const tsFiles = glob.sync("**/index*.d.ts", { + cwd: typesDir, + absolute: true, + ignore: ["**/*.spec.d.ts", "**/*.test.d.ts"] + }); const allTypeReferences = new Set(); - // Extract type references from all TypeScript files + // Extract type references from all TypeScript declaration files for (const tsFile of tsFiles) { - const filePath = join(pkg.path, tsFile); - if (existsSync(filePath)) { - const typeRefs = extractTypeReferences(filePath); - typeRefs.forEach(ref => allTypeReferences.add(ref)); - } + const typeRefs = extractTypeReferences(tsFile); + typeRefs.forEach(ref => allTypeReferences.add(ref)); } // Check each type reference const violations: string[] = []; for (const typeRef of allTypeReferences) { - // Skip if it's a built-in module - if (isBuiltInModule(typeRef)) { - continue; - } - - // Skip if it's an internal @hpcc-js package - if (typeRef.startsWith("@hpcc-js/")) { - continue; - } - // Skip if it's in dependencies if (pkg.dependencies.has(typeRef)) { continue; } - // Skip if it's in dependencies + // Skip if it's in peerDependencies if (pkg.peerDependencies.has(typeRef)) { continue; } - // Skip if it's a @types/ package and the base package is in dependencies - if (typeRef.startsWith("@types/")) { - const basePackage = typeRef.replace("@types/", ""); - if (pkg.dependencies.has(basePackage)) { + // Skip if it's a regular package but we have its @types/ equivalent in dependencies + if (!typeRef.startsWith("@types/")) { + const typesPackage = `@types/${typeRef}`; + if (pkg.dependencies.has(typesPackage) || pkg.peerDependencies.has(typesPackage)) { continue; } } - // Skip vitest-related packages (test runner) - if (typeRef === "vitest" || typeRef.startsWith("@vitest/")) { - continue; - } - - // Skip some common dev-only packages that might be referenced in source - const devOnlyPackages = new Set([ - "vitepress", "vite", "typescript", "esbuild", - "@vitejs/plugin-react", "rollup" - ]); - - if (devOnlyPackages.has(typeRef)) { - continue; - } - // This is a violation - third-party type used without dependency violations.push(typeRef); } @@ -250,6 +449,7 @@ describe("Package Type Dependencies", () => { if (violations.length > 0) { console.error(`\n${pkg.name} type violations:`); console.error(`Dependencies: ${Array.from(pkg.dependencies).join(", ") || "(none)"}`); + console.error(`PeerDependencies: ${Array.from(pkg.peerDependencies).join(", ") || "(none)"}`); console.error(`Violations: ${violations.join(", ")}`); } @@ -258,3 +458,4 @@ describe("Package Type Dependencies", () => { }); } }); + diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 77de4a367b..6e5ded6153 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -12,6 +12,7 @@ export const nodeConfig = defineConfig({ "**/components/**", "**/demos/**", "**/src/**", + "tests/**", ], environment: "node", setupFiles: []