diff --git a/.changeset/spotty-cows-sip.md b/.changeset/spotty-cows-sip.md new file mode 100644 index 0000000..dde1f1d --- /dev/null +++ b/.changeset/spotty-cows-sip.md @@ -0,0 +1,5 @@ +--- +"quickbundle": minor +--- + +Add cross-compilation feature. diff --git a/examples/multiple-standalones/package.json b/examples/multiple-standalones/package.json index 217ed67..12a74b9 100644 --- a/examples/multiple-standalones/package.json +++ b/examples/multiple-standalones/package.json @@ -8,7 +8,9 @@ "standaloneB": "./dist/standaloneB.js" }, "scripts": { - "build": "quickbundle compile", + "build": "pnpm build:local && pnpm build:cross", + "build:cross": "quickbundle compile --target node-v21.7.3-linux-arm64", + "build:local": "quickbundle compile", "watch": "pnpm build" }, "dependencies": { diff --git a/examples/multiple-standalones/src/index.ts b/examples/multiple-standalones/src/index.ts index 3fbe610..201243b 100644 --- a/examples/multiple-standalones/src/index.ts +++ b/examples/multiple-standalones/src/index.ts @@ -1,8 +1,21 @@ #!/usr/bin/env node +import process from "node:process"; + import ora from "ora"; -console.info("A standalone program"); +console.info("Hello world\n"); + +console.debug( + "Debug information", + JSON.stringify( + { + embeddedNodeVersion: process.version, + }, + null, + 2, + ), +); const spinner = ora("Fake processing").start(); @@ -12,5 +25,4 @@ const sleep = async (duration = 3000) => { void sleep().then(() => { spinner.stop(); - console.info("End processing"); }); diff --git a/examples/single-standalone/package.json b/examples/single-standalone/package.json index 146a3bb..b2191db 100644 --- a/examples/single-standalone/package.json +++ b/examples/single-standalone/package.json @@ -5,7 +5,9 @@ "source": "./src/index.ts", "bin": "./dist/index.cjs", "scripts": { - "build": "quickbundle compile", + "build": "pnpm build:local && pnpm build:cross", + "build:cross": "quickbundle compile --target node-v21.7.3-linux-arm64", + "build:local": "quickbundle compile", "watch": "pnpm build" }, "dependencies": { diff --git a/examples/single-standalone/src/index.ts b/examples/single-standalone/src/index.ts index 3fbe610..201243b 100644 --- a/examples/single-standalone/src/index.ts +++ b/examples/single-standalone/src/index.ts @@ -1,8 +1,21 @@ #!/usr/bin/env node +import process from "node:process"; + import ora from "ora"; -console.info("A standalone program"); +console.info("Hello world\n"); + +console.debug( + "Debug information", + JSON.stringify( + { + embeddedNodeVersion: process.version, + }, + null, + 2, + ), +); const spinner = ora("Fake processing").start(); @@ -12,5 +25,4 @@ const sleep = async (duration = 3000) => { void sleep().then(() => { spinner.stop(); - console.info("End processing"); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f62ad3..cc222cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,6 +119,9 @@ importers: '@swc/core': specifier: ^1.9.1 version: 1.9.1 + decompress: + specifier: ^4.2.1 + version: 4.2.1 gzip-size: specifier: ^7.0.0 version: 7.0.0 @@ -141,6 +144,9 @@ importers: specifier: ^4.7.0 || ^5.0.0 version: 5.6.3 devDependencies: + '@types/decompress': + specifier: 4.2.7 + version: 4.2.7 '@types/node': specifier: 22.9.0 version: 22.9.0 @@ -1322,6 +1328,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/decompress@4.2.7': + resolution: {integrity: sha512-9z+8yjKr5Wn73Pt17/ldnmQToaFHZxK0N1GHysuk/JIPT8RIdQeoInM01wWPgypRcvb6VH1drjuFpQ4zmY437g==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1637,10 +1646,16 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + bl@1.2.3: + resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1656,9 +1671,24 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-alloc-unsafe@1.1.0: + resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==} + + buffer-alloc@1.2.0: + resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-fill@1.0.0: + resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -1757,6 +1787,9 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + comment-parser@1.4.1: resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} engines: {node: '>= 12.0.0'} @@ -1793,6 +1826,9 @@ packages: core-js-compat@3.39.0: resolution: {integrity: sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cosmiconfig-typescript-loader@5.0.0: resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} engines: {node: '>=v16'} @@ -1862,6 +1898,26 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decompress-tar@4.1.1: + resolution: {integrity: sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==} + engines: {node: '>=4'} + + decompress-tarbz2@4.1.1: + resolution: {integrity: sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==} + engines: {node: '>=4'} + + decompress-targz@4.1.1: + resolution: {integrity: sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==} + engines: {node: '>=4'} + + decompress-unzip@4.0.1: + resolution: {integrity: sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==} + engines: {node: '>=4'} + + decompress@4.2.1: + resolution: {integrity: sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==} + engines: {node: '>=4'} + deep-equal@2.2.3: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} engines: {node: '>= 0.4'} @@ -1934,6 +1990,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + enhanced-resolve@5.17.1: resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} @@ -2292,6 +2351,9 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.4.2: resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} peerDependencies: @@ -2304,6 +2366,18 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-type@3.9.0: + resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==} + engines: {node: '>=0.10.0'} + + file-type@5.2.0: + resolution: {integrity: sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==} + engines: {node: '>=4'} + + file-type@6.2.0: + resolution: {integrity: sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==} + engines: {node: '>=4'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2334,6 +2408,9 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -2376,6 +2453,10 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} + get-stream@2.3.1: + resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==} + engines: {node: '>=0.10.0'} + get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -2478,6 +2559,9 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2614,6 +2698,9 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + is-natural-number@4.0.1: + resolution: {integrity: sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==} + is-negative-zero@2.0.3: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} @@ -2649,6 +2736,10 @@ packages: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} + is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -2692,6 +2783,9 @@ packages: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -2876,6 +2970,10 @@ packages: magic-string@0.30.12: resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} + make-dir@1.3.0: + resolution: {integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==} + engines: {node: '>=4'} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -3119,6 +3217,9 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} @@ -3225,6 +3326,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3236,10 +3340,26 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + pinkie-promise@2.0.1: + resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} + engines: {node: '>=0.10.0'} + + pinkie@2.0.4: + resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} + engines: {node: '>=0.10.0'} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -3266,6 +3386,9 @@ packages: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + promise-inflight@1.0.1: resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} peerDependencies: @@ -3311,6 +3434,9 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3447,6 +3573,9 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -3464,6 +3593,10 @@ packages: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} + seek-bzip@1.0.6: + resolution: {integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==} + hasBin: true + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3597,6 +3730,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -3615,6 +3751,9 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-dirs@2.1.0: + resolution: {integrity: sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3643,6 +3782,10 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + tar-stream@1.6.2: + resolution: {integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==} + engines: {node: '>= 0.8.0'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -3670,6 +3813,9 @@ packages: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} + to-buffer@1.1.1: + resolution: {integrity: sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3775,6 +3921,9 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} @@ -3928,6 +4077,13 @@ packages: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -3951,6 +4107,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -5489,6 +5648,10 @@ snapshots: dependencies: '@types/ms': 0.7.34 + '@types/decompress@4.2.7': + dependencies: + '@types/node': 22.9.0 + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.6 @@ -5867,10 +6030,17 @@ snapshots: balanced-match@1.0.2: {} + base64-js@1.5.1: {} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 + bl@1.2.3: + dependencies: + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -5891,8 +6061,24 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) + buffer-alloc-unsafe@1.1.0: {} + + buffer-alloc@1.2.0: + dependencies: + buffer-alloc-unsafe: 1.1.0 + buffer-fill: 1.0.0 + + buffer-crc32@0.2.13: {} + + buffer-fill@1.0.0: {} + buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + builtin-modules@3.3.0: {} bytes@3.1.2: {} @@ -5978,6 +6164,8 @@ snapshots: colorette@2.0.20: {} + commander@2.20.3: {} + comment-parser@1.4.1: {} commondir@1.0.1: {} @@ -6017,6 +6205,8 @@ snapshots: dependencies: browserslist: 4.24.2 + core-util-is@1.0.3: {} + cosmiconfig-typescript-loader@5.0.0(@types/node@22.9.0)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3): dependencies: '@types/node': 22.9.0 @@ -6083,6 +6273,44 @@ snapshots: dependencies: character-entities: 2.0.2 + decompress-tar@4.1.1: + dependencies: + file-type: 5.2.0 + is-stream: 1.1.0 + tar-stream: 1.6.2 + + decompress-tarbz2@4.1.1: + dependencies: + decompress-tar: 4.1.1 + file-type: 6.2.0 + is-stream: 1.1.0 + seek-bzip: 1.0.6 + unbzip2-stream: 1.4.3 + + decompress-targz@4.1.1: + dependencies: + decompress-tar: 4.1.1 + file-type: 5.2.0 + is-stream: 1.1.0 + + decompress-unzip@4.0.1: + dependencies: + file-type: 3.9.0 + get-stream: 2.3.1 + pify: 2.3.0 + yauzl: 2.10.0 + + decompress@4.2.1: + dependencies: + decompress-tar: 4.1.1 + decompress-tarbz2: 4.1.1 + decompress-targz: 4.1.1 + decompress-unzip: 4.0.1 + graceful-fs: 4.2.11 + make-dir: 1.3.0 + pify: 2.3.0 + strip-dirs: 2.1.0 + deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.1 @@ -6160,6 +6388,10 @@ snapshots: emoji-regex@9.2.2: {} + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 @@ -6756,6 +6988,10 @@ snapshots: dependencies: reusify: 1.0.4 + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + fdir@6.4.2(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -6764,6 +7000,12 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-type@3.9.0: {} + + file-type@5.2.0: {} + + file-type@6.2.0: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6800,6 +7042,8 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + fs-constants@1.0.0: {} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -6842,6 +7086,11 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-stream@2.3.1: + dependencies: + object-assign: 4.1.1 + pinkie-promise: 2.0.1 + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 @@ -6944,6 +7193,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} import-fresh@3.3.0: @@ -7062,6 +7313,8 @@ snapshots: is-module@1.0.0: {} + is-natural-number@4.0.1: {} + is-negative-zero@2.0.3: {} is-number-object@1.0.7: @@ -7089,6 +7342,8 @@ snapshots: dependencies: call-bind: 1.0.7 + is-stream@1.1.0: {} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -7126,6 +7381,8 @@ snapshots: is-windows@1.0.2: {} + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} @@ -7299,6 +7556,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + make-dir@1.3.0: + dependencies: + pify: 3.0.0 + make-dir@3.1.0: dependencies: semver: 6.3.1 @@ -7730,6 +7991,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + once@1.4.0: + dependencies: + wrappy: 1.0.2 + onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -7854,14 +8119,26 @@ snapshots: path-type@4.0.0: {} + pend@1.2.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.2: {} + pify@2.3.0: {} + + pify@3.0.0: {} + pify@4.0.1: {} + pinkie-promise@2.0.1: + dependencies: + pinkie: 2.0.4 + + pinkie@2.0.4: {} + possible-typed-array-names@1.0.0: {} prelude-ls@1.2.1: {} @@ -7876,6 +8153,8 @@ snapshots: proc-log@4.2.0: {} + process-nextick-args@2.0.1: {} + promise-inflight@1.0.1: {} promise-retry@2.0.1: @@ -7919,6 +8198,16 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -8099,6 +8388,8 @@ snapshots: has-symbols: 1.0.3 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-regex-test@1.0.3: @@ -8119,6 +8410,10 @@ snapshots: refa: 0.12.1 regexp-ast-analysis: 0.7.1 + seek-bzip@1.0.6: + dependencies: + commander: 2.20.3 + semver@6.3.1: {} semver@7.6.3: {} @@ -8281,6 +8576,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -8300,6 +8599,10 @@ snapshots: strip-bom@3.0.0: {} + strip-dirs@2.1.0: + dependencies: + is-natural-number: 4.0.1 + strip-json-comments@3.1.1: {} supports-color@5.5.0: @@ -8322,6 +8625,16 @@ snapshots: tapable@2.2.1: {} + tar-stream@1.6.2: + dependencies: + bl: 1.2.3 + buffer-alloc: 1.2.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + readable-stream: 2.3.8 + to-buffer: 1.1.1 + xtend: 4.0.2 + term-size@2.2.1: {} termost@0.18.0: @@ -8348,6 +8661,8 @@ snapshots: dependencies: os-tmpdir: 1.0.2 + to-buffer@1.1.1: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -8462,6 +8777,11 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + unbzip2-stream@1.4.3: + dependencies: + buffer: 5.7.1 + through: 2.3.8 + undici-types@6.19.8: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -8696,6 +9016,10 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.0 + wrappy@1.0.2: {} + + xtend@4.0.2: {} + y18n@5.0.8: {} yallist@2.1.2: {} @@ -8716,6 +9040,11 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + yocto-queue@0.1.0: {} yocto-queue@1.1.1: {} diff --git a/quickbundle/README.md b/quickbundle/README.md index d6bb61c..9aec919 100644 --- a/quickbundle/README.md +++ b/quickbundle/README.md @@ -11,7 +11,7 @@ Quickbundle allows you to bundle a library in a **quick**, **fast** and **easy** way: - Fast build and watch mode powered by Rollup[^1] and SWC[^2]. -- Compile mode to create standalone binaries for systems that do not have Node.js installed[^3]. +- Compile mode to compile and cross compile standalone binaries for systems that do not have Node.js installed[^3]. - Zero configuration: define the build artifacts in your `package.json`, and you're all set! - Support of `cjs` & `esm` module formats output. - Support of several loaders including JavaScript, TypeScript, JSX, JSON, and Images. @@ -113,7 +113,7 @@ yarn add quickbundle "name": "lib", // Package name "source": "./src/index.ts", // Source code entry point. Make sure that it starts with `#!/usr/bin/env node` pragme to make the binary portable for consumers who would like to use it by installing the package instead of using the generated standalone executable. "bin": { - "your-binary-name": "./dist/index.cjs", // Binary information to get the executable name from the key and, from the value, the bundled file to generate from the source code and inject into the executable. The generated executable will be located in the same folder as the bundled file and, dependending on the current operating system running the `compile` command, the executable will be named either `your-binary-name.exe` on Windows or `your-binary-name` on Linux and macOS. + "your-binary-name": "./dist/index.cjs", // Binary information to get the executable name from the key and, from the value, the bundled file to generate from the source code and inject into the executable. The generated executable will be located in the same folder as the bundled file and, by default, dependending on the current operating system running the `compile` command, the executable will be named either `your-binary-name.exe` on Windows or `your-binary-name` on Linux and macOS. For cross-compilation output, check the `Patterns` section. }, // "bin": "./dist/index.cjs", // Or, if the binary name follows the package name, you can define a string-based `bin` value. "scripts": { @@ -171,6 +171,24 @@ quickbundle watch --source-maps Enabling source map generation is needed only if a build is [obfuscated (minified)](#optimize-the-build-output) for debugging-easing purposes. It generally pairs with the [`minification` flag](#optimize-the-build-output). +### Cross compilation to other platforms + +By default, the `compile` command embeds the runtime at the origin of its execution which means it generates executables compatible only with machines running the same operating system, processor architecture, and Node.js version. + +However, Quickbundle provides the ability to target a different operating system, processor architecture, or Node.js version (also known as cross-compilation): + +```bash +quickbundle compile --target node-v23.1.0-darwin-arm64 # Embeds Node v23 runtime built for macOS ARM64 target +quickbundle compile --target node-v23.1.0-darwin-x64 # Embeds Node v23 runtime built for macOS X64 target +quickbundle compile --target node-v23.1.0-linux-arm64 # Embeds Node v23 runtime built for Linux ARM64 target +quickbundle compile --target node-v23.1.0-linux-x64 # Embeds Node v23 runtime built for Linux X64 target +quickbundle compile --target node-v23.1.0-win-arm64 # Embeds Node v23 runtime built for Windows ARM64 target +quickbundle compile --target node-v23.1.0-win-x64 # Embeds Node v23 runtime built for Windows X64 target +``` + +> [!note] +> The accepted target input follows the `node-vx.y.z-(darwin|linux|win)-(arm64|x64|x86)` format (for an exhaustive view, check available filenames in [https://nodejs.org/download/release/vx.y.z](https://nodejs.org/download/release/latest/)). +
## ☑️ Roadmap diff --git a/quickbundle/package.json b/quickbundle/package.json index ff3a125..70fb63a 100644 --- a/quickbundle/package.json +++ b/quickbundle/package.json @@ -58,6 +58,7 @@ "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-url": "^8.0.2", "@swc/core": "^1.9.1", + "decompress": "^4.2.1", "gzip-size": "^7.0.0", "rollup": "^4.24.4", "rollup-plugin-dts": "^6.1.1", @@ -66,6 +67,7 @@ "termost": "^1.2.0" }, "devDependencies": { + "@types/decompress": "4.2.7", "@types/node": "22.9.0" } } diff --git a/quickbundle/src/bundler/config.ts b/quickbundle/src/bundler/config.ts index 735448c..aa6be0d 100644 --- a/quickbundle/src/bundler/config.ts +++ b/quickbundle/src/bundler/config.ts @@ -1,4 +1,3 @@ -import { join } from "node:path"; import { createRequire } from "node:module"; import { swc } from "rollup-plugin-swc3"; @@ -10,11 +9,14 @@ import { nodeResolve } from "@rollup/plugin-node-resolve"; import json from "@rollup/plugin-json"; import commonjs from "@rollup/plugin-commonjs"; -import { CWD } from "../constants"; +import { resolveFromExternalDirectory } from "../helpers"; import { isRecord } from "./helpers"; const require = createRequire(import.meta.url); -const PKG = require(join(CWD, "./package.json")) as PackageJson; + +const PKG = require( + resolveFromExternalDirectory("package.json"), +) as PackageJson; type PackageJson = { name?: string; diff --git a/quickbundle/src/commands/compile.ts b/quickbundle/src/commands/compile.ts index 423c9ee..5745d9e 100644 --- a/quickbundle/src/commands/compile.ts +++ b/quickbundle/src/commands/compile.ts @@ -1,37 +1,47 @@ -import { basename, dirname, resolve } from "node:path"; +import process from "node:process"; +import { basename, dirname, join, resolve } from "node:path"; import os from "node:os"; -import { rm, writeFile } from "node:fs/promises"; import { helpers } from "termost"; import type { Termost } from "termost"; +import { + copyFile, + createRegExpMatcher, + download, + removePath, + resolveFromInternalDirectory, + unzip, + writeFile, +} from "../helpers"; import { createConfiguration } from "../bundler/config"; import type { Configuration } from "../bundler/config"; import { build } from "../bundler/build"; type CompileCommandContext = { config: Configuration; - osType: "linux" | "macos" | "windows"; + osType: OsType; + targetInput: string; }; +const TEMPORARY_PATH = resolveFromInternalDirectory("dist", "tmp"); +const TEMPORARY_DOWNLOAD_PATH = join(TEMPORARY_PATH, "zip"); +const TEMPORARY_RUNTIME_PATH = join(TEMPORARY_PATH, "runtime"); + export const createCompileCommand = (program: Termost) => { return program .command({ name: "compile", description: "Compiles the source code into a self-contained executable", }) - .task({ - key: "osType", - label: "Get context", - handler() { - const type = os.type(); - - return type === "Windows_NT" - ? "windows" - : type === "Darwin" - ? "macos" - : "linux"; + .option({ + key: "targetInput", + name: { + long: "target", + short: "t", }, + description: "Set a different cross-compilation target", + defaultValue: "local", }) .task({ key: "config", @@ -44,6 +54,51 @@ export const createCompileCommand = (program: Termost) => { }); }, }) + .task({ + key: "osType", + label({ targetInput }) { + return `Get \`${targetInput}\` runtime`; + }, + async handler({ targetInput }) { + if (targetInput === "local") { + await copyFile(process.execPath, TEMPORARY_RUNTIME_PATH); + + return getOsType(os.type()); + } + + const matchedRuntimeParts = matchRuntimeParts(targetInput); + + if (!matchedRuntimeParts) { + throw new Error( + "Invalid `runtime` flag input. The accepted targets are the one listed in https://nodejs.org/download/release/ with the following format `node-vx.y.z-(darwin|linux|win)-(arm64|x64|x86)`.", + ); + } + + const osType = getOsType(matchedRuntimeParts.os); + const extension = osType === "windows" ? "zip" : "tar.gz"; + + await download( + `https://nodejs.org/download/release/${matchedRuntimeParts.version}/${targetInput}.${extension}`, + TEMPORARY_DOWNLOAD_PATH, + ); + + await unzip( + { + path: TEMPORARY_DOWNLOAD_PATH, + targetedArchivePath: + osType === "windows" + ? join(targetInput, "node.exe") + : join(targetInput, "bin", "node"), + }, + { + directoryPath: dirname(TEMPORARY_RUNTIME_PATH), + filename: basename(TEMPORARY_RUNTIME_PATH), + }, + ); + + return osType; + }, + }) .task({ label: "Build", async handler({ config }) { @@ -75,6 +130,30 @@ export const createCompileCommand = (program: Termost) => { }); }; +type OsType = "linux" | "macos" | "windows"; + +const getOsType = (input: string): OsType => { + switch (input) { + case "Windows_NT": + case "win": + return "windows"; + case "Darwin": + case "darwin": + return "macos"; + case "Linux": + case "linux": + return "linux"; + default: + throw new Error(`Unsupported operating system \`${input}\``); + } +}; + +const matchRuntimeParts = createRegExpMatcher< + "architecture" | "os" | "version" +>( + /^node-(?v\d+\.\d+\.\d+)-(?darwin|linux|win)-(?arm64|x64|x86)$/, +); + const compile = async ({ bin, input, @@ -109,15 +188,12 @@ const compile = async ({ useCodeCache: false, useSnapshot: false, }), - "utf-8", ); - await Promise.all( - [ - `node --experimental-sea-config ${seaConfigFileName}`, - `node -e "require('fs').copyFileSync(process.execPath, '${executableFileName}')"`, - ].map(async (command) => helpers.exec(command)), - ); + await Promise.all([ + helpers.exec(`node --experimental-sea-config ${seaConfigFileName}`), + copyFile(TEMPORARY_RUNTIME_PATH, executableFileName), + ]); if (osType === "macos") { await helpers.exec(`codesign --remove-signature ${executableFileName}`); @@ -132,6 +208,8 @@ const compile = async ({ } await Promise.all( - [blobFileName, seaConfigFileName].map(async (file) => rm(file)), + [blobFileName, seaConfigFileName, TEMPORARY_PATH].map(async (path) => + removePath(path), + ), ); }; diff --git a/quickbundle/src/constants.ts b/quickbundle/src/constants.ts deleted file mode 100644 index 989f798..0000000 --- a/quickbundle/src/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -import process from "node:process"; - -export const CWD = process.cwd(); diff --git a/quickbundle/src/helpers.ts b/quickbundle/src/helpers.ts index 14e4875..dc1cb58 100644 --- a/quickbundle/src/helpers.ts +++ b/quickbundle/src/helpers.ts @@ -1,14 +1,19 @@ -import { dirname } from "node:path"; +import { finished } from "node:stream/promises"; +import { Readable } from "node:stream"; +import process from "node:process"; +import { dirname, join, resolve } from "node:path"; import { + copyFile as fsCopyFile, readFile as fsReadFile, writeFile as fsWriteFile, mkdir, + rename, + rm, } from "node:fs/promises"; -import { existsSync } from "node:fs"; +import { createWriteStream } from "node:fs"; import type { Termost } from "termost"; - -import { CWD } from "./constants"; +import decompress from "decompress"; /** * TS assertion not working properly with arrow function. @@ -25,24 +30,102 @@ export function assert(condition: unknown, message: string): asserts condition { } } -export const readFile = fsReadFile; +/** + * Resolve a relative path from the Quickbundle node modules directory. + * @param paths - Relative paths. + * @returns The resolved absolute path. + * @example + * resolveFromInternalDirectory("dist", "node"); + */ +export const resolveFromInternalDirectory = (...paths: string[]) => { + return resolve(import.meta.dirname, "../", ...paths); +}; -export const resolveModulePath = (path: string) => { - try { - return Boolean(require.resolve(path, { paths: [CWD] })); - } catch { - return false; - } +/** + * Resolve a relative path from the current working project directory. + * @param paths - Relative paths. + * @returns The resolved absolute path. + * @example + * resolveFromExternalDirectory("package.json"); + */ +export const resolveFromExternalDirectory = (...paths: string[]) => { + return resolve(process.cwd(), ...paths); +}; + +export const createRegExpMatcher = (regex: RegExp) => { + return (value: string) => { + return regex.exec(value)?.groups as Record | undefined; + }; +}; + +export const createDirectory = async (path: string) => { + await mkdir(path, { + recursive: true, + }); +}; + +export const copyFile = async (fromPath: string, toPath: string) => { + await createDirectory(dirname(toPath)); + await fsCopyFile(fromPath, toPath); +}; + +export const removePath = async (path: string) => { + await rm(path, { + force: true, + recursive: true, + }); +}; + +export const readFile = async (filePath: string) => { + return fsReadFile(filePath); }; export const writeFile = async (filePath: string, content: string) => { - const dir = dirname(filePath); + await createDirectory(dirname(filePath)); + await fsWriteFile(filePath, content, "utf8"); +}; + +export const download = async (url: string, filePath: string) => { + await createDirectory(dirname(filePath)); - if (!existsSync(dir)) { - await mkdir(dir, { recursive: true }); + const { body, ok, status, statusText } = await fetch(url); + + if (!ok) { + throw new Error( + `An error ocurred while downloading \`${url}\`. Received \`${status}\` status code with the following message \`${statusText}\`.`, + ); } - await fsWriteFile(filePath, content, "utf8"); + if (!body) { + throw new Error(`Empty body received while downloading \`${url}\`.`); + } + + await finished(Readable.fromWeb(body).pipe(createWriteStream(filePath))); +}; + +export const unzip = async ( + input: { + path: string; + targetedArchivePath: string; + }, + output: { + directoryPath: string; + filename: string; + }, +) => { + const { targetedArchivePath } = input; + const { directoryPath } = output; + + await decompress(input.path, directoryPath, { + filter(file) { + return file.path === targetedArchivePath; + }, + }); + + await rename( + join(directoryPath, targetedArchivePath), + join(directoryPath, output.filename), + ); }; export type CreateCommandContext = CustomContext & {