Open bo-carey opened 1 year ago
I have been wanting to do the same. I am currently trying out the esbuild plugin named esbuild-plugin-umd-wrapper
with the following added configuration to tsup.config.cjs
:
esbuildPlugins: [
umdWrapper({
libraryName: 'library-name',
})
],
// Adding 'umd' to trigger the umdWrapper plugin
format: ['cjs', 'esm', 'iife', 'umd'],
At first sight it appears to work as expected
I get an error when I specify 'umd' as a format @weyert do you mind linking to your full tsup.config.js ?
Or, to others, is there another way to create a UMD package with tsup ?
is there another way to create a UMD package with tsup?
@dhowe My org has been generating our bundles as esm
and converting them to amd
/umd
after. Maybe that could be a workaround for you 🤷♂️
thanks @bo-carey
generating our bundles as
esm
and converting them toamd
/umd
after
with what tooling ?
with what tooling ?
@dhowe Thankfully our library consumers use ember so they have access to transforming esm -> amd via the ember-cli. Otherwise, there are some small libraries out there to transform it, such as dcodeIO/esm2umd
.
The following worked in my case:
tsup.config.cjs
import { defineConfig } from 'tsup'
import umdWrapper from 'esbuild-plugin-umd-wrapper'
import { dependencies } from './package.json'
import { createUmdWrapper } from './build-plugins/umdWrapperPlugin.cjs'
const externalDependencies = Object.keys(dependencies)
const sdkClientVersion = '1.0.0'
const clientName = `ClientName`
const isDevelopmentMode = process.env._DEV === 'true'
/** @type {import('tsup').Options} */
const baseConfig = {
entry: {
appstore: 'src/index.ts',
},
outDir: 'dist',
outExtension({ format, options }) {
const ext = format === 'esm' ? 'mjs' : 'js'
const outputExtension = options.minify ? `${format}.min.${ext}` : `${format}.${ext}`
return {
js: `.${outputExtension}`,
}
},
platform: 'browser',
format: ['cjs', 'esm'],
noExternal: externalDependencies,
target: ['chrome90', 'edge90', 'firefox90', 'opera98', 'safari15'],
name: '@company/client',
globalName: clientName,
legacyOutput: false,
bundle: true,
esbuildPlugins: [],
banner: { js: `/* Client SDK version ${sdkClientVersion} */\n` },
define: {
__VERSION__: `'${sdkClientVersion}'`,
},
minify: false,
splitting: false,
sourcemap: true,
dts: false,
clean: true,
onSuccess: 'tsc --project tsconfig.build.json --emitDeclarationOnly --declaration',
watch: isDevelopmentMode,
metafile: isDevelopmentMode,
}
export default defineConfig([
{
...baseConfig,
esbuildPlugins: [],
minify: false,
},
{
...baseConfig,
esbuildPlugins: [],
minify: true,
},
{
...baseConfig,
format: ['umd'],
minify: false,
plugins: [createUmdWrapper({ libraryName: clientName, external: [] })],
},
{
...baseConfig,
minify: true,
format: ['umd'],
plugins: [createUmdWrapper({ libraryName: clientName, external: [] })],
},
{
...baseConfig,
entry: {
marketplace: 'src/index.ts',
},
minify: false,
target: 'es5',
format: ['umd'],
outputExtension: {
js: `browser.js`,
},
outDir: 'dist',
esbuildPlugins: [umdWrapper({ libraryName: clientName, external: 'inherit' })],
},
])
umdWrapperPlugin.cjs
import * as path from 'node:path'
import * as fs from 'node:fs'
/*
Plugin is based on the `esbuild-plugin-umd-wrapper``, found at:
https://github.com/inqnuam/esbuild-plugin-umd-wrapper
*/
// eslint-disable-next-line no-unused-vars
const createWrapperWithLib = ({ depsKeys, depsValKey, amdLoader, lib, defineDeps, globalDeps, requireDeps }) => {
return `(function (g, f) {
if ("object" == typeof exports && "object" == typeof module) {
module.exports = f(${requireDeps});
} else if ("function" == typeof ${amdLoader} && ${amdLoader}.amd) {
${amdLoader}("${lib}", ${defineDeps}, f);
} else if ("object" == typeof exports) {
exports["${lib}"] = f(${requireDeps});
} else {
g["${lib}"] = f(${globalDeps});
}
}(this, (${depsKeys}) => {
var exports = {};
var module = { exports };\n\n`
}
export const alphabet = [
'__da',
'__db',
'__dc',
'__dd',
'__de',
'__df',
'__dg',
'__dh',
'__di',
'__dj',
'__dk',
'__dl',
'__dm',
'__dn',
'__do',
'__dp',
'__dq',
'__dr',
'__ds',
'__dt',
'__du',
'__dv',
'__dw',
'__dx',
'__dy',
'__dz',
]
function getUmdBanner(opts) {
const external = opts.external ?? []
const defineDeps = external?.length ? `['${external.join("', '")}']` : '[]'
const globalDeps = external?.map(x => `g["${x}"]`).join(', ') ?? ''
const requireDeps = external?.map(x => `require('${x}')`).join(', ') ?? ''
let deps = []
if (external) {
deps = external.map((x, i) => {
return {
key: alphabet[i],
val: x,
}
})
}
const depsKeys = deps.map(x => x.key).join(', ')
const depsValKey = deps.map(x => `"${x.val}": ${x.key}`).join(', ')
const options = {
depsKeys,
depsValKey,
amdLoader: 'define',
defineDeps,
globalDeps,
requireDeps,
lib: opts.libraryName,
}
const result = createWrapperWithLib(options)
return result
}
export const umdFooter = `if (typeof module.exports == "object" && typeof exports == "object") {
var __cp = (to, from, except, desc) => {
if ((from && typeof from === "object") || typeof from === "function") {
for (let key of Object.getOwnPropertyNames(from)) {
if (!Object.prototype.hasOwnProperty.call(to, key) && key !== except)
Object.defineProperty(to, key, {
get: () => from[key],
enumerable: !(desc = Object.getOwnPropertyDescriptor(from, key)) || desc.enumerable,
});
}
}
return to;
};
module.exports = __cp(module.exports, exports);
}
return module.exports;
}))\n\n\n`
export const umdWrapperSetup = build => {
const { initialOptions } = build
const external = initialOptions.external
const content = getUmdBanner(external)
if (initialOptions.footer) {
if (initialOptions.footer.js) {
initialOptions.footer.js += umdFooter
} else {
initialOptions.footer.js = umdFooter
}
} else {
initialOptions.footer = {
js: umdFooter,
}
}
if (initialOptions.banner) {
if (initialOptions.banner.js) {
initialOptions.banner.js += content
} else {
initialOptions.banner.js = content
}
} else {
initialOptions.banner = {
js: content,
}
}
}
export const createUmdWrapper = opts => {
let pluginExternalDependencies = []
return {
name: 'add-umd-wrapper',
esbuildOptions(options) {
options.format = 'cjs'
pluginExternalDependencies = []
return options
},
buildEnd(result) {
try {
result.writtenFiles.forEach(file => {
const filePath = path.join(process.cwd(), file.name)
if (file.name.endsWith('.js')) {
const fileName = path.basename(file.name)
const umdBanner = getUmdBanner({ ...opts, external: pluginExternalDependencies })
// eslint-disable-next-line security/detect-non-literal-fs-filename
const content = fs.readFileSync(filePath, 'utf-8')
const patchedFileContents = content.replace(`//# sourceMappingURL=${fileName}.map`, '')
const scriptContent = `\n\n\n${patchedFileContents}\n\n\n`
const wrappedContent = `${umdBanner}${scriptContent}${umdFooter}\n\n//# sourceMappingURL=${fileName}.map\n`
const newContent = `/* umd */\n${wrappedContent}`
// eslint-disable-next-line security/detect-non-literal-fs-filename
fs.writeFileSync(filePath, newContent, 'utf8')
}
})
} catch (err) {
// eslint-disable-next-line no-console
console.error(err)
}
},
}
}
thanks @weyert
Hello! Are there any plans or existing solutions to format as
UMD
orAMD
?Upvote & Fund