Open magicdawn opened 2 months ago
使用 tsup build cjs 时比较简单, 需要注意的是 bundle deps except electron
electron
可以使用
// tsup.config.ts export default defineConfig({ entry: ['src/index.ts'], format: 'cjs', platform: 'node', noExternal: /.*/, // bundle any dep esbuildOptions(options) { options.charset = 'utf8' options.external ||= [] options.external.push('electron') // externalize electron } })
注意 noExternal 正则, 与 options.external.push('electron')
noExternal
options.external.push('electron')
electron 支持 esm 了
// tsup.config.ts import { randomUUID } from 'crypto' import _ from 'lodash' import { builtinModules } from 'module' import path from 'path' import { defineConfig } from 'tsup' const { camelCase } = _ const env = process.env.NODE_ENV || 'development' const REPO_ROOT = path.join(import.meta.dirname, '../../') const nsp = randomUUID() const prefix = randomUUID() export default defineConfig({ entry: ['./src/index.ts'], format: 'esm', outExtension: () => ({ js: '.mjs', dts: '.d.mts' }), outDir: path.join(REPO_ROOT, `bundle/${env}/main/`), clean: true, platform: 'node', // TODO: get node version based on electron version // Using: Node.js v18.15.0 and Electron.js v25.3.0 target: 'node18', env: { NODE_ENV: env, }, // NOTE: 此处 external & noExternal 只是输入给 esbuild.options({ plugins: [externalPlugin] }) // 无法做到, bundle any dep, except `electron` // 直接使用 `esbuildOptions.external` 则 esbuild.onResolve / onLoad 逻辑都不会走 noExternal: [/.*/], esbuildOptions(options, context) { options.charset = 'utf8' options.external ||= [] // options.external.push('electron') }, esbuildPlugins: [ // esbuild doesn't transpile `require('foo')` into `import` statements if 'foo' is externalized // https://github.com/evanw/esbuild/issues/566#issuecomment-735551834 // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/optimizer/esbuildDepPlugin.ts#L300 { name: 'custom-external-plugin', setup(build) { const filter = new RegExp( ['electron', ...builtinModules.map((x) => [x, `node\\:${x}`]).flat()] .map((id) => `(?:^${id}$)`) .join('|'), ) // console.log(filter) build.onResolve({ filter }, (args) => { if (args.kind === 'require-call') { return { path: args.path, namespace: nsp, } } return { path: args.path, external: true, } }) build.onLoad({ filter: /.*/, namespace: nsp }, (args) => { const m = camelCase(args.path).replace(/[:/]/g, '_') return { contents: ` import ${m} from ${JSON.stringify(prefix + args.path)}; module.exports = ${m}; `, } }) build.onResolve({ filter: new RegExp(`^${prefix}`) }, (args) => { return { path: args.path.slice(prefix.length), external: true, } }) }, }, ], })
因为 esbuild 如果 external 了 builtin module 如 fs, require('fs') 将会得到保留, 到运行时就会报 dynamic require of fs is not supported. 需要一个 esbuild plugin, 从 vite 中摘的:
require('fs')
dynamic require of fs is not supported
import m from 'blabla:fs'; module.exports = fs
{path: 'fs', external: true}
最后生成的代码长这样
// bf7bd391-8f37-4e9a-8c3e-8266c1c54f72:electron import electron2 from "electron"; var require_electron = __commonJS({ "bf7bd391-8f37-4e9a-8c3e-8266c1c54f72:electron"(exports, module) { module.exports = electron2; } }); // bf7bd391-8f37-4e9a-8c3e-8266c1c54f72:fs import fs2 from "fs"; var require_fs = __commonJS({ "bf7bd391-8f37-4e9a-8c3e-8266c1c54f72:fs"(exports, module) { module.exports = fs2; } }); // ...
[!CAUTION] vite 中使用 namespace import, 如 import * as fs from 'fs'; module.exports = fs; 但实际 namespace import 结果会有兼容问题: fs-extra>graceful-fs 会定义 fs.xxx, 而 namespace import 结果是 freeze 的 有代码在调用 require('constants').hasOwnProperty, namespace import 结果上没有 Object.prototype 上的方法
[!CAUTION] vite 中使用 namespace import, 如 import * as fs from 'fs'; module.exports = fs; 但实际 namespace import 结果会有兼容问题:
import * as fs from 'fs'; module.exports = fs;
require('constants').hasOwnProperty
https://github.com/magicdawn/clash-config-manager/blob/main/packages/ui/vite.config.ts
even in 2024
main process
cjs
使用 tsup build cjs 时比较简单, 需要注意的是 bundle deps except
electron
可以使用
注意
noExternal
正则, 与options.external.push('electron')
esm
electron 支持 esm 了
因为 esbuild 如果 external 了 builtin module 如 fs,
require('fs')
将会得到保留, 到运行时就会报dynamic require of fs is not supported
. 需要一个 esbuild plugin, 从 vite 中摘的:import m from 'blabla:fs'; module.exports = fs
{path: 'fs', external: true}
最后生成的代码长这样