mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
fix: bundle dev plugins whose dependencies only declare main or touch node builtins
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// @vitest-environment node
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
@@ -46,6 +47,23 @@ describe('dev plugins', () => {
|
||||
expect(await getEnabledPluginFrameOrigins()).toContain('https://video.example.com');
|
||||
});
|
||||
|
||||
it('bundles src/ with packages that only declare main and touch Node builtins', async () => {
|
||||
// Shaped like asn1js (no "exports") and asmcrypto (require('crypto')
|
||||
// behind a runtime check), which the S/MIME plugin pulls in.
|
||||
await devPlugin('deps', { ...BASE, id: 'deps' }, {
|
||||
'src/index.js': "import { value } from 'legacy-pkg';\nexport default value;",
|
||||
'node_modules/legacy-pkg/package.json': JSON.stringify({ name: 'legacy-pkg', main: 'lib/main.js' }),
|
||||
'node_modules/legacy-pkg/lib/main.js':
|
||||
"exports.value = typeof process !== 'undefined' && process.versions?.node ? require('crypto') : 42;",
|
||||
});
|
||||
const { listDevPlugins, readDevBundle } = await import('../plugin-dev');
|
||||
|
||||
const [entry] = await listDevPlugins();
|
||||
|
||||
expect(entry?.plugin.id).toBe('deps');
|
||||
expect(await readDevBundle(entry)).not.toContain('esbuild failed');
|
||||
});
|
||||
|
||||
it('refuses an entrypoint outside the plugin folder', async () => {
|
||||
// The bundle route serves the file without a session.
|
||||
await writeFile(path.join(dir, 'secret.js'), 'TOP SECRET');
|
||||
|
||||
@@ -2,6 +2,7 @@ import { readFile, readdir, stat } from 'node:fs/promises';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { createHash } from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import { builtinModules } from 'node:module';
|
||||
import { logger } from '@/lib/logger';
|
||||
import type { ServerPlugin } from './plugin-registry';
|
||||
import { checkPluginManifest } from '@/lib/extension-package';
|
||||
@@ -93,6 +94,9 @@ async function bundleEntrypoint(bundlePath: string): Promise<string> {
|
||||
// (`new Function('module', 'exports', 'require', 'React', ...)`).
|
||||
format: 'cjs',
|
||||
platform: 'neutral',
|
||||
// Neutral leaves mainFields empty, so packages that only declare "main"
|
||||
// or "module" (asn1js, pkijs, ...) would not resolve.
|
||||
mainFields: ['browser', 'module', 'main'],
|
||||
write: false,
|
||||
logLevel: 'silent',
|
||||
sourcemap: 'inline',
|
||||
@@ -103,6 +107,10 @@ async function bundleEntrypoint(bundlePath: string): Promise<string> {
|
||||
external: [
|
||||
'react', 'react-dom', 'react-dom/client', 'react/jsx-runtime',
|
||||
'@plugin-host',
|
||||
// Browser libraries reach for Node builtins behind runtime checks that
|
||||
// never pass in the sandbox (asmcrypto's require('crypto')). Leave them
|
||||
// out instead of failing the whole bundle.
|
||||
...builtinModules, ...builtinModules.map((m) => `node:${m}`),
|
||||
],
|
||||
});
|
||||
const out = result.outputFiles?.[0]?.text;
|
||||
|
||||
Reference in New Issue
Block a user