From d03a1cb83decb5d357753e7ffda82626826cfd83 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 27 Dec 2023 20:45:47 +0100 Subject: [PATCH] refactor: readable code and esm tests against miniflare --- package.json | 1 + pnpm-lock.yaml | 197 ++++++++++++++++++ src/plugin.ts | 95 +++++++-- .../@fixture/wasm/dist/index.d.ts | 15 ++ test/node_modules/@fixture/wasm/dist/index.js | 15 ++ .../@fixture/wasm/dist/index.wasm | Bin 96 -> 465 bytes .../node_modules/@fixture/wasm/dist/index.wat | 172 ++++++++++++++- .../@fixture/wasm/index.wasm.d.ts | 8 +- test/node_modules/@fixture/wasm/package.json | 2 +- test/plugin.test.ts | 35 +++- 10 files changed, 512 insertions(+), 28 deletions(-) create mode 100644 test/node_modules/@fixture/wasm/dist/index.d.ts create mode 100644 test/node_modules/@fixture/wasm/dist/index.js diff --git a/package.json b/package.json index 82d790b..bdcb000 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "eslint": "^8.56.0", "eslint-config-unjs": "^0.2.1", "jiti": "^1.21.0", + "miniflare": "^3.20231030.4", "prettier": "^3.1.1", "rollup": "^4.9.1", "typescript": "^5.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0e4549..c14b063 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,9 @@ devDependencies: jiti: specifier: ^1.21.0 version: 1.21.0 + miniflare: + specifier: ^3.20231030.4 + version: 3.20231030.4 prettier: specifier: ^3.1.1 version: 3.1.1 @@ -275,6 +278,51 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@cloudflare/workerd-darwin-64@1.20231030.0: + resolution: {integrity: sha512-J4PQ9utPxLya9yHdMMx3AZeC5M/6FxcoYw6jo9jbDDFTy+a4Gslqf4Im9We3aeOEdPXa3tgQHVQOSelJSZLhIw==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-darwin-arm64@1.20231030.0: + resolution: {integrity: sha512-WSJJjm11Del4hSneiNB7wTXGtBXI4QMCH9l5qf4iT5PAW8cESGcCmdHtWDWDtGAAGcvmLT04KNvmum92vRKKQQ==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-linux-64@1.20231030.0: + resolution: {integrity: sha512-2HUeRTvoCC17fxE0qdBeR7J9dO8j4A8ZbdcvY8pZxdk+zERU6+N03RTbk/dQMU488PwiDvcC3zZqS4gwLfVT8g==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-linux-arm64@1.20231030.0: + resolution: {integrity: sha512-4/GK5zHh+9JbUI6Z5xTCM0ZmpKKHk7vu9thmHjUxtz+o8Ne9DoD7DlDvXQWgMF6XGaTubDWyp3ttn+Qv8jDFuQ==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/workerd-windows-64@1.20231030.0: + resolution: {integrity: sha512-fb/Jgj8Yqy3PO1jLhk7mTrHMkR8jklpbQFud6rL/aMAn5d6MQbaSrYOCjzkKGp0Zng8D2LIzSl+Fc0C9Sggxjg==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/aix-ppc64@0.19.10: resolution: {integrity: sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q==} engines: {node: '>=12'} @@ -519,6 +567,11 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@fastify/busboy@2.1.0: + resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==} + engines: {node: '>=14'} + dev: true + /@humanwhocodes/config-array@0.11.13: resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} @@ -1197,6 +1250,12 @@ packages: is-shared-array-buffer: 1.0.2 dev: true + /as-table@1.0.55: + resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} + dependencies: + printable-characters: 1.0.42 + dev: true + /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true @@ -1276,6 +1335,10 @@ packages: update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + /builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -1341,6 +1404,15 @@ packages: resolution: {integrity: sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==} dev: true + /capnp-ts@0.7.0: + resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==} + dependencies: + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} @@ -1498,6 +1570,11 @@ packages: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1618,6 +1695,10 @@ packages: css-tree: 2.2.1 dev: true + /data-uri-to-buffer@2.0.2: + resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} + dev: true + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2315,6 +2396,11 @@ packages: strip-final-newline: 3.0.0 dev: true + /exit-hook@2.2.1: + resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} + engines: {node: '>=6'} + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -2464,6 +2550,13 @@ packages: hasown: 2.0.0 dev: true + /get-source@2.0.12: + resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} + dependencies: + data-uri-to-buffer: 2.0.2 + source-map: 0.6.1 + dev: true + /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -2515,6 +2608,10 @@ packages: is-glob: 4.0.3 dev: true + /glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -3140,6 +3237,29 @@ packages: engines: {node: '>=4'} dev: true + /miniflare@3.20231030.4: + resolution: {integrity: sha512-7MBz0ArLuDop1WJGZC6tFgN6c5MRyDOIlxbm3yp0TRBpvDS/KsTuWCQcCjsxN4QQ5zvL3JTkuIZbQzRRw/j6ow==} + engines: {node: '>=16.13'} + hasBin: true + dependencies: + acorn: 8.11.2 + acorn-walk: 8.3.1 + capnp-ts: 0.7.0 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + source-map-support: 0.5.21 + stoppable: 1.1.0 + undici: 5.28.2 + workerd: 1.20231030.0 + ws: 8.16.0 + youch: 3.3.3 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -3232,6 +3352,11 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true + /mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + dev: true + /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3858,6 +3983,10 @@ packages: react-is: 18.2.0 dev: true + /printable-characters@1.0.42: + resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + dev: true + /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -4129,6 +4258,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -4160,10 +4296,22 @@ packages: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true + /stacktracey@2.1.8: + resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} + dependencies: + as-table: 1.0.55 + get-source: 2.0.12 + dev: true + /std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} dev: true + /stoppable@1.1.0: + resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} + engines: {node: '>=4', npm: '>=6'} + dev: true + /string.prototype.trim@1.2.8: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} @@ -4347,6 +4495,10 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true + /tsutils@3.21.0(typescript@5.3.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -4483,6 +4635,13 @@ packages: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true + /undici@5.28.2: + resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==} + engines: {node: '>=14.0'} + dependencies: + '@fastify/busboy': 2.1.0 + dev: true + /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -4715,10 +4874,36 @@ packages: stackback: 0.0.2 dev: true + /workerd@1.20231030.0: + resolution: {integrity: sha512-+FSW+d31f8RrjHanFf/R9A+Z0csf3OtsvzdPmAKuwuZm/5HrBv83cvG9fFeTxl7/nI6irUUXIRF9xcj/NomQzQ==} + engines: {node: '>=16'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20231030.0 + '@cloudflare/workerd-darwin-arm64': 1.20231030.0 + '@cloudflare/workerd-linux-64': 1.20231030.0 + '@cloudflare/workerd-linux-arm64': 1.20231030.0 + '@cloudflare/workerd-windows-64': 1.20231030.0 + dev: true + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true @@ -4741,3 +4926,15 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} dev: true + + /youch@3.3.3: + resolution: {integrity: sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA==} + dependencies: + cookie: 0.5.0 + mustache: 4.2.0 + stacktracey: 2.1.8 + dev: true + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: true diff --git a/src/plugin.ts b/src/plugin.ts index 30ea320..1c3762f 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -5,7 +5,8 @@ import type { RenderedChunk, Plugin as RollupPlugin } from "rollup"; import { createUnplugin } from "unplugin"; import { sha1 } from "./_utils"; -const WASM_EXTERNAL_ID = "\0unwasm:external:"; +const UNWASM_EXTERNAL_PREFIX = "\0unwasm:external:"; +const UMWASM_HELPERS_ID = "\0unwasm:helpers"; export interface UnwasmPluginOptions { /** @@ -35,7 +36,10 @@ const unplugin = createUnplugin((opts) => { name: "unwasm", rollup: { async resolveId(id, importer) { - if (id.startsWith(WASM_EXTERNAL_ID)) { + if (id === UMWASM_HELPERS_ID) { + return id; + } + if (id.startsWith(UNWASM_EXTERNAL_PREFIX)) { return { id, external: true, @@ -43,12 +47,12 @@ const unplugin = createUnplugin((opts) => { } if (id.endsWith(".wasm")) { const r = await this.resolve(id, importer, { skipSelf: true }); - if (r?.id && r?.id !== id) { + if (r?.id && r.id !== id) { return { id: r.id.startsWith("file://") ? r.id.slice(7) : r.id, external: false, moduleSideEffects: false, - syntheticNamedExports: false, + syntheticNamedExports: true, }; } } @@ -66,6 +70,9 @@ const unplugin = createUnplugin((opts) => { }, }, async load(id) { + if (id === UMWASM_HELPERS_ID) { + return getPluginUtils(); + } if (!id.endsWith(".wasm") || !existsSync(id)) { return; } @@ -83,28 +90,64 @@ const unplugin = createUnplugin((opts) => { if (!asset) { return; } - let _dataStr: string; - if (opts.esmImport) { - _dataStr = `await import("${WASM_EXTERNAL_ID}${id}").then(r => r?.default || r)`; - } else { - const base64Str = asset.source.toString("base64"); - _dataStr = `(()=>{const d=atob("${base64Str}");const s=d.length;const b=new Uint8Array(s);for(let i=0;i Math.random, "Math.floor": () => Math.floor } }).then(r => r?.exports||r?.instance?.exports || r);`; - if (opts.lazy) { - _str = `(()=>{const e=async()=>{return ${_str}};let _p;const p=()=>{if(!_p)_p=e();return _p;};return {then:cb=>p().then(cb),catch:cb=>p().catch(cb)}})()`; - } + + const envCode: string = opts.esmImport + ? ` +async function _instantiate(imports) { + const _mod = await import("${UNWASM_EXTERNAL_PREFIX}${id}").then(r => r.default || r); + return WebAssembly.instantiate(_mod, imports) +} +` + : ` +import { base64ToUint8Array } from "${UMWASM_HELPERS_ID}"; + +function _instantiate(imports) { + const _mod = base64ToUint8Array("${asset.source.toString("base64")}") + return WebAssembly.instantiate(_mod, imports) +} + `; + + const code = `${envCode} +const _defaultImports = Object.create(null); + +// TODO: For testing only +Object.assign(_defaultImports, { env: { "seed": () => () => Date.now() * Math.random() } }) + +const instancePromises = new WeakMap(); +function instantiate(imports = _defaultImports) { + let p = instancePromises.get(imports); + if (!p) { + p = _instantiate(imports); + instancePromises.set(imports, p); + } + return p; +} + +const _instance = instantiate(); +const _exports = _instance.then(r => r?.instance?.exports || r?.exports || r); + +export default ${opts.lazy ? "" : "await "} _exports; + `; + return { - code: `export default ${_str};`, + code, map: { mappings: "" }, syntheticNamedExports: true, }; }, renderChunk(code: string, chunk: RenderedChunk) { + if (!opts.esmImport) { + return; + } + if ( - !chunk.moduleIds.some((id) => id.endsWith(".wasm")) || - !code.includes(WASM_EXTERNAL_ID) + !( + chunk.moduleIds.some((id) => id.endsWith(".wasm")) || + chunk.imports.some((id) => id.endsWith(".wasm")) + ) || + !code.includes(UNWASM_EXTERNAL_PREFIX) ) { + console.log(chunk); return; } const s = new MagicString(code); @@ -124,7 +167,7 @@ const unplugin = createUnplugin((opts) => { asset, }; }; - const ReplaceRE = new RegExp(`${WASM_EXTERNAL_ID}([^"']+)`, "g"); + const ReplaceRE = new RegExp(`${UNWASM_EXTERNAL_PREFIX}([^"']+)`, "g"); for (const match of code.matchAll(ReplaceRE)) { const resolved = resolveImport(match[1]); const index = match.index as number; @@ -147,6 +190,20 @@ const unplugin = createUnplugin((opts) => { }; }); +export function getPluginUtils() { + return ` +export function base64ToUint8Array(str) { + const data = atob(str); + const size = data.length; + const bytes = new Uint8Array(size); + for (let i = 0; i < size; i++) { + bytes[i] = data.charCodeAt(i); + } + return bytes; +} + `; +} + const rollup = unplugin.rollup as (opts: UnwasmPluginOptions) => RollupPlugin; export default { diff --git a/test/node_modules/@fixture/wasm/dist/index.d.ts b/test/node_modules/@fixture/wasm/dist/index.d.ts new file mode 100644 index 0000000..dc5b272 --- /dev/null +++ b/test/node_modules/@fixture/wasm/dist/index.d.ts @@ -0,0 +1,15 @@ +declare namespace __AdaptedExports { + /** Exported memory */ + export const memory: WebAssembly.Memory; + /** + * src/index.asc/rand + * @param min `f64` + * @param max `f64` + * @returns `f64` + */ + export function rand(min: number, max: number): number; +} +/** Instantiates the compiled WebAssembly module with the given imports. */ +export declare function instantiate(module: WebAssembly.Module, imports: { + env: unknown, +}): Promise; diff --git a/test/node_modules/@fixture/wasm/dist/index.js b/test/node_modules/@fixture/wasm/dist/index.js new file mode 100644 index 0000000..c793cf9 --- /dev/null +++ b/test/node_modules/@fixture/wasm/dist/index.js @@ -0,0 +1,15 @@ +export async function instantiate(module, imports = {}) { + const adaptedImports = { + env: Object.assign(Object.create(globalThis), imports.env || {}, { + seed() { + // ~lib/builtins/seed() => f64 + return (() => { + // @external.js + return Date.now() * Math.random(); + })(); + }, + }), + }; + const { exports } = await WebAssembly.instantiate(module, adaptedImports); + return exports; +} diff --git a/test/node_modules/@fixture/wasm/dist/index.wasm b/test/node_modules/@fixture/wasm/dist/index.wasm index 9e93f831782837730d982845ce54f233d56deb58..7646d1e28f83d8673e6c8f691fba1d3938ef6413 100644 GIT binary patch literal 465 zcmZ8e%}T>S5T4oHq}44ZJ&B$+q*u>gly*aX0N)^mSS7`Yf@>|%n6iohjNgb^tr z^*db_`o0H1bqxsx#x&eUM2)I$eWjS71RkR^Gg+D&kVUSdL67hRGFK=Q7Bo5@XnNak zZv{I9=5z#!9OlPFlOU8j)}U{t{`TPE?R)xup2TVThq!wj-an78Uh4k{gRiMF`Sp4E zsyFi>ycVbIF+bdD@!*m}@vhO?@LZfNk;^bOc#HD`ZvF&$n%9UdnZZ~-P(&_*Ze ztAGeNGf9QoR9Pmm0^CCy+pxiECft~m+h~DJEEK9M`}X2qwvQTK&Nma71L!R0fSQU< qY2fs7nN)WzBFMO|&ZFd`Ix@J0>K>8F1PU1a5+|o=;ZX^=!G8e724YhH delta 76 zcmcb}oS?*zSe(nqz`(%B%9y~wSi{W3$iTwNSis20$jS^9XA@zbm?)#fz?Peun_pDP b#K6hM$l%Dx&A`RS$iv_Q)CdHT4*c8znb`?& diff --git a/test/node_modules/@fixture/wasm/dist/index.wat b/test/node_modules/@fixture/wasm/dist/index.wat index 8fd055c..2c33661 100644 --- a/test/node_modules/@fixture/wasm/dist/index.wat +++ b/test/node_modules/@fixture/wasm/dist/index.wat @@ -1,5 +1,15 @@ (module (type $0 (func (result f64))) + (type $1 (func (param i64) (result i64))) + (type $2 (func (param i32) (result i32))) + (type $3 (func (param i64))) + (type $4 (func (param f64 f64) (result f64))) + (import "env" "seed" (func $~lib/builtins/seed (result f64))) + (global $~lib/math/random_seeded (mut i32) (i32.const 0)) + (global $~lib/math/random_state0_64 (mut i64) (i64.const 0)) + (global $~lib/math/random_state1_64 (mut i64) (i64.const 0)) + (global $~lib/math/random_state0_32 (mut i32) (i32.const 0)) + (global $~lib/math/random_state1_32 (mut i32) (i32.const 0)) (global $~lib/memory/__data_end i32 (i32.const 8)) (global $~lib/memory/__stack_pointer (mut i32) (i32.const 32776)) (global $~lib/memory/__heap_base i32 (i32.const 32776)) @@ -8,8 +18,166 @@ (elem $0 (i32.const 1)) (export "rand" (func $src/index.asc/rand)) (export "memory" (memory $0)) - (func $src/index.asc/rand (result f64) - f64.const 100 + (func $~lib/math/murmurHash3 (param $0 i64) (result i64) + local.get $0 + local.get $0 + i64.const 33 + i64.shr_u + i64.xor + local.set $0 + local.get $0 + i64.const -49064778989728563 + i64.mul + local.set $0 + local.get $0 + local.get $0 + i64.const 33 + i64.shr_u + i64.xor + local.set $0 + local.get $0 + i64.const -4265267296055464877 + i64.mul + local.set $0 + local.get $0 + local.get $0 + i64.const 33 + i64.shr_u + i64.xor + local.set $0 + local.get $0 + return + ) + (func $~lib/math/splitMix32 (param $0 i32) (result i32) + local.get $0 + i32.const 1831565813 + i32.add + local.set $0 + local.get $0 + local.get $0 + i32.const 15 + i32.shr_u + i32.xor + local.get $0 + i32.const 1 + i32.or + i32.mul + local.set $0 + local.get $0 + local.get $0 + local.get $0 + local.get $0 + i32.const 7 + i32.shr_u + i32.xor + local.get $0 + i32.const 61 + i32.or + i32.mul + i32.add + i32.xor + local.set $0 + local.get $0 + local.get $0 + i32.const 14 + i32.shr_u + i32.xor + return + ) + (func $~lib/math/NativeMath.seedRandom (param $0 i64) + local.get $0 + i64.const 0 + i64.eq + if + i64.const -7046029254386353131 + local.set $0 + end + local.get $0 + call $~lib/math/murmurHash3 + global.set $~lib/math/random_state0_64 + global.get $~lib/math/random_state0_64 + i64.const -1 + i64.xor + call $~lib/math/murmurHash3 + global.set $~lib/math/random_state1_64 + local.get $0 + i32.wrap_i64 + call $~lib/math/splitMix32 + global.set $~lib/math/random_state0_32 + global.get $~lib/math/random_state0_32 + call $~lib/math/splitMix32 + global.set $~lib/math/random_state1_32 + i32.const 1 + global.set $~lib/math/random_seeded + ) + (func $~lib/math/NativeMath.random (result f64) + (local $0 i64) + (local $1 i64) + (local $2 i64) + global.get $~lib/math/random_seeded + i32.eqz + if + call $~lib/builtins/seed + i64.reinterpret_f64 + call $~lib/math/NativeMath.seedRandom + end + global.get $~lib/math/random_state0_64 + local.set $0 + global.get $~lib/math/random_state1_64 + local.set $1 + local.get $1 + global.set $~lib/math/random_state0_64 + local.get $0 + local.get $0 + i64.const 23 + i64.shl + i64.xor + local.set $0 + local.get $0 + local.get $0 + i64.const 17 + i64.shr_u + i64.xor + local.set $0 + local.get $0 + local.get $1 + i64.xor + local.set $0 + local.get $0 + local.get $1 + i64.const 26 + i64.shr_u + i64.xor + local.set $0 + local.get $0 + global.set $~lib/math/random_state1_64 + local.get $1 + i64.const 12 + i64.shr_u + i64.const 4607182418800017408 + i64.or + local.set $2 + local.get $2 + f64.reinterpret_i64 + f64.const 1 + f64.sub + return + ) + (func $src/index.asc/rand (param $0 f64) (param $1 f64) (result f64) + (local $2 f64) + block $~lib/math/NativeMath.floor|inlined.0 (result f64) + call $~lib/math/NativeMath.random + local.get $1 + local.get $0 + f64.sub + f64.const 1 + f64.add + f64.mul + local.set $2 + local.get $2 + f64.floor + br $~lib/math/NativeMath.floor|inlined.0 + end return ) ) diff --git a/test/node_modules/@fixture/wasm/index.wasm.d.ts b/test/node_modules/@fixture/wasm/index.wasm.d.ts index 6899cee..e785915 100644 --- a/test/node_modules/@fixture/wasm/index.wasm.d.ts +++ b/test/node_modules/@fixture/wasm/index.wasm.d.ts @@ -1 +1,7 @@ -export declare function rand(min: number, max: number): number; +import type { instantiate} from './dist' + +type exports = ReturnType + +export default exports + +export const rand = exports.rand diff --git a/test/node_modules/@fixture/wasm/package.json b/test/node_modules/@fixture/wasm/package.json index 041a381..3d958ae 100644 --- a/test/node_modules/@fixture/wasm/package.json +++ b/test/node_modules/@fixture/wasm/package.json @@ -10,7 +10,7 @@ }, "scripts": { "asc": "pnpm --package=assemblyscript dlx asc", - "build": "pnpm asc ./src/index.asc.ts -o dist/index.wasm -t dist/index.wat -b dist/index.js", + "build": "pnpm asc ./src/index.asc.ts -o dist/index.wasm -t dist/index.wat -b raw", "test": "NODE_OPTIONS='--experimental-wasm-modules' node -e 'import(`./dist/index.wasm`).then(mod => console.log(mod.rand(10, 20)))'" } } diff --git a/test/plugin.test.ts b/test/plugin.test.ts index 76c84b9..cec1488 100644 --- a/test/plugin.test.ts +++ b/test/plugin.test.ts @@ -1,5 +1,6 @@ import { fileURLToPath } from "node:url"; import { existsSync } from "node:fs"; +import { rm } from "node:fs/promises"; import { it, describe, expect } from "vitest"; import { evalModule } from "mlly"; import { nodeResolve as rollupNodeResolve } from "@rollup/plugin-node-resolve"; @@ -10,13 +11,19 @@ const r = (p: string) => fileURLToPath(new URL(p, import.meta.url)); const entry = r("fixture/index.mjs"); +await rm(r(".tmp"), { recursive: true }).catch(() => {}); + describe("plugin:rollup-inline", () => { - it("builds", async () => { + it("works", async () => { const build = await rollup({ input: entry, plugins: [rollupNodeResolve({}), unwasm.rollup({})], }); - const { output } = await build.generate({ format: "esm" }); + const { output } = await build.write({ + format: "esm", + entryFileNames: "[name].mjs", + dir: r(".tmp/rollup-inline"), + }); const code = output[0].code; const mod = await evalModule(code, { url: entry }); expect(mod.rand(1, 1000)).toBeGreaterThan(0); @@ -24,19 +31,37 @@ describe("plugin:rollup-inline", () => { }); describe("plugin:rollup-esm", () => { - it("builds", async () => { + it("works", async () => { const build = await rollup({ input: entry, plugins: [rollupNodeResolve({}), unwasm.rollup({ esmImport: true })], }); const { output } = await build.write({ format: "esm", + entryFileNames: "[name].mjs", dir: r(".tmp/rollup-esm"), }); const code = output[0].code; - const esmImport = code.match(/import\('(.+wasm)'\)/)?.[1]; + const esmImport = code.match(/["'](.+wasm)["']/)?.[1]; expect(esmImport).match(/\.\/wasm\/index-[\da-f]+\.wasm/); expect(existsSync(r(`.tmp/rollup-esm/${esmImport}`))).toBe(true); - // TODO: Fix mlly to allow evaluating native .wasm + + const { Miniflare } = await import("miniflare"); + const mf = new Miniflare({ + modules: true, + modulesRules: [{ type: "CompiledWasm", include: ["**/*.wasm"] }], + scriptPath: r(".tmp/rollup-esm/_mf.mjs"), + script: ` + import * as _wasm from "./index.mjs"; + export default { + async fetch(request, env, ctx) { + return new Response("" + _wasm.rand(1, 1000)); + } + } + `, + }); + const res = await mf.dispatchFetch("http://localhost"); + expect(Number.parseInt(await res.text())).toBeGreaterThan(0); + await mf.dispose(); }); });