diff --git a/packages/shims/package.json b/packages/shims/package.json index a257b5b88..120ae5f53 100644 --- a/packages/shims/package.json +++ b/packages/shims/package.json @@ -26,7 +26,7 @@ "changelog:validate": "../../scripts/validate-changelog.sh @ocap/shims", "clean": "rimraf --glob ./dist './*.tsbuildinfo'", "publish:preview": "yarn npm publish --tag preview", - "test": "vitest run --config vitest.config.ts --passWithNoTests", + "test": "yarn build && vitest run --config vitest.config.ts --passWithNoTests", "test:clean": "yarn test --no-cache --coverage.clean", "test:dev": "yarn test --coverage false", "test:verbose": "yarn test --reporter verbose", @@ -42,6 +42,7 @@ "@metamask/auto-changelog": "^3.4.4", "deepmerge": "^4.3.1", "rimraf": "^6.0.1", + "vite": "^5.3.5", "vitest": "^2.0.5" }, "engines": { diff --git a/packages/shims/scripts/bundle.js b/packages/shims/scripts/bundle.js index 45157d98d..b27a9f6d7 100644 --- a/packages/shims/scripts/bundle.js +++ b/packages/shims/scripts/bundle.js @@ -2,41 +2,47 @@ import 'ses'; import '@endo/lockdown/commit.js'; import bundleSource from '@endo/bundle-source'; -import { copyFile, mkdir, writeFile } from 'fs/promises'; +import { createReadStream, createWriteStream } from 'fs'; +import { mkdir } from 'fs/promises'; import path from 'path'; import { rimraf } from 'rimraf'; +import { Readable } from 'stream'; import { fileURLToPath } from 'url'; console.log('Bundling shims...'); -const rootDir = path.resolve(import.meta.dirname, '..'); +const rootDir = fileURLToPath(new URL('..', import.meta.url)); const src = path.resolve(rootDir, 'src'); const dist = path.resolve(rootDir, 'dist'); -const fileNames = { - endoify: 'endoify.mjs', - eventualSend: 'eventual-send.mjs', - applyLockdown: 'apply-lockdown.mjs', -}; await mkdir(dist, { recursive: true }); -await rimraf(`${dist}/*`); - -for (const fileName of [fileNames.endoify, fileNames.applyLockdown]) { - await copyFile(path.resolve(src, fileName), path.resolve(dist, fileName)); -} - -const eventualSendSourcePath = fileURLToPath( - import.meta.resolve('@endo/eventual-send/shim.js'), -); +await rimraf(`${dist}/*`, { glob: true }); + +/** + * Bundles the target file as endoScript and returns the content as a readable stream. + * + * @param {string} specifier - Import path to the file to bundle, e.g. `'@endo/eventual-send/shim.js'`. + * @returns {Promise} The bundled file contents as a Readable stream. + */ +const createEndoBundleReadStream = async (specifier) => { + const filePath = fileURLToPath(import.meta.resolve(specifier)); + const { source: bundle } = await bundleSource(filePath, { + format: 'endoScript', + }); + return Readable.from(bundle); +}; -const { source: eventualSendBundleSource } = await bundleSource( - eventualSendSourcePath, - { format: 'endoScript' }, -); +const sources = { + ses: createReadStream( + path.resolve(rootDir, '../../node_modules/ses/dist/ses.mjs'), + ), + eventualSend: await createEndoBundleReadStream('@endo/eventual-send/shim.js'), + shim: createReadStream(path.resolve(src, 'endoify.mjs')), +}; -await writeFile( - path.resolve(dist, fileNames.eventualSend), - eventualSendBundleSource, -); +const target = createWriteStream(path.resolve(dist, 'endoify.mjs')); -console.log('Success!'); +sources.ses.pipe(target, { end: false }); +sources.ses.on('end', () => sources.eventualSend.pipe(target, { end: false })); +sources.eventualSend.on('end', () => sources.shim.pipe(target, { end: true })); +sources.shim.on('end', () => console.log('Success!')); diff --git a/packages/shims/src/endoify.mjs b/packages/shims/src/endoify.mjs index 824aa9c32..40124f6f0 100644 --- a/packages/shims/src/endoify.mjs +++ b/packages/shims/src/endoify.mjs @@ -1,5 +1,6 @@ -import './ses.mjs'; -import './eventual-send.mjs'; +/* eslint-disable import-x/unambiguous */ +// @inline './ses.mjs'; +// @inline './eventual-send.mjs'; lockdown({ consoleTaming: 'unsafe', @@ -9,3 +10,5 @@ lockdown({ domainTaming: 'unsafe', overrideTaming: 'severe', }); + +/* eslint-enable import-x/unambiguous */ diff --git a/packages/shims/src/endoify.test.ts b/packages/shims/src/endoify.test.ts new file mode 100644 index 000000000..de8618c6c --- /dev/null +++ b/packages/shims/src/endoify.test.ts @@ -0,0 +1,12 @@ +import { HandledPromise } from '@endo/eventual-send'; +import { describe, expect, it } from 'vitest'; + +describe('endoified', () => { + it('calls lockdown', () => { + expect(Object.isFrozen(Array.prototype)).toBe(true); // Due to `lockdown()`, and therefore `ses` + }); + + it('loads eventual-send', () => { + expect(typeof HandledPromise).not.toBe('undefined'); // Due to eventual send + }); +}); diff --git a/packages/shims/src/vitest-environment-endoified.ts b/packages/shims/src/vitest-environment-endoified.ts new file mode 100644 index 000000000..63a817387 --- /dev/null +++ b/packages/shims/src/vitest-environment-endoified.ts @@ -0,0 +1,25 @@ +import type { Context } from 'node:vm'; +import type { Environment } from 'vitest/environments'; + +export default { + name: 'endoified', + transformMode: 'ssr', + async setupVM() { + const vm = await import('node:vm'); + return { + getVmContext(): Context { + return vm.createContext({ + setTimeout, + clearTimeout, + }); + }, + // eslint-disable-next-line no-empty-function + teardown(): void {}, + }; + }, + async setup() { + throw new Error( + 'endoified environment requires vitest option --pool=vmThreads or --pool=vmForks', + ); + }, +} as Environment; diff --git a/packages/shims/vitest.config.ts b/packages/shims/vitest.config.ts index 2709902e1..4d7480d27 100644 --- a/packages/shims/vitest.config.ts +++ b/packages/shims/vitest.config.ts @@ -1,9 +1,23 @@ // eslint-disable-next-line spaced-comment /// +import { defineConfig, mergeConfig } from 'vite'; + import { getDefaultConfig } from '../../vitest.config.packages.js'; -const config = getDefaultConfig(); +const defaultConfig = getDefaultConfig(); + +const config = mergeConfig( + defaultConfig, + defineConfig({ + test: { + pool: 'vmThreads', + environment: './vitest-environment-endoified.ts', + setupFiles: '../dist/endoify.mjs', + }, + }), +); + // @ts-expect-error We can and will delete this. delete config.test.coverage.thresholds; export default config; diff --git a/yarn.lock b/yarn.lock index d5df286f8..7dfab2953 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1324,6 +1324,7 @@ __metadata: deepmerge: "npm:^4.3.1" rimraf: "npm:^6.0.1" ses: "npm:^1.7.0" + vite: "npm:^5.3.5" vitest: "npm:^2.0.5" languageName: unknown linkType: soft