@growing-web/esm-pack-core
Version:
esm pack build core.
262 lines (242 loc) • 6.14 kB
text/typescript
import nodePolyfills from 'rollup-plugin-node-polyfills'
import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'
import esbuild from 'rollup-plugin-esbuild'
import rollupJSONPlugin from '@rollup/plugin-json'
import path from 'pathe'
import fs from 'fs-extra'
import { rollup } from 'rollup'
import _ from 'lodash'
import { APP_NAME } from './constants'
import { rollupPluginWrapTargets } from './plugins/rollup-plugin-wrap-exports'
import { rollupPluginNodeProcessPolyfill } from './plugins/rollup-plugin-node-process-polyfill'
import { isDynamicEntry } from './resolvePackage'
import { readPackageJSON } from 'pkg-types'
import peerDepsExternal from 'rollup-plugin-peer-deps-external'
export * from './resolvePackage'
process.setMaxListeners(0)
export async function build({
buildFiles,
outputPath,
sourcePath,
sourcemap = true,
}: {
buildFiles: string[]
outputPath: string
sourcePath: string
sourcemap?: boolean
}) {
const inputMap: Record<string, string> = {}
const pkg = await readPackageJSON(sourcePath)
const devBuildFiles: string[] = []
await Promise.all(
buildFiles.map(async (file) => {
const dynamicEntry = await isDynamicEntry(
fs.readFileSync(file, { encoding: 'utf8' }),
)
if (dynamicEntry) {
devBuildFiles.push(file)
}
}),
)
for (const file of buildFiles) {
let n = file.split('/').pop()
if (!n) {
continue
}
const extIndex = n.lastIndexOf('.')
if (extIndex !== -1) n = n.slice(0, extIndex)
if (inputMap[n]) {
const _n = n
let i = 1
while (inputMap[n]) n = _n + ++i
}
inputMap[n] = file
}
await Promise.all([
doBuildMultipleEntry({
input: inputMap,
outputPath,
sourcePath,
env: 'production',
name: pkg.name,
sourcemap,
}),
Promise.all(
devBuildFiles.map((input) =>
doBuildSingleEntry({
input,
outputPath,
sourcePath,
env: 'development',
name: pkg.name,
sourcemap,
}),
),
),
])
}
export async function doBuildMultipleEntry({
input,
sourcePath,
outputPath,
env,
name,
sourcemap,
}: {
input: Record<string, string>
outputPath: string
sourcePath: string
env: string
name?: string
sourcemap: boolean
}) {
const inputKeys = Object.keys(input)
const bundle = await rollup({
input: input,
treeshake: { moduleSideEffects: true },
onwarn: onWarning,
external: (id) =>
!inputKeys.includes(path.join(sourcePath, id)) && !needExternal(id),
plugins: createRollupPlugins(name, env),
})
fs.ensureDirSync(outputPath)
const { output } = await bundle.generate({
dir: outputPath,
format: 'esm',
exports: 'named',
sourcemap,
// preserveModules: true,
entryFileNames: (chunk) => {
let id = chunk.facadeModuleId
if (id?.startsWith(`${APP_NAME}:`)) {
id = id.replace(`${APP_NAME}:`, '')
}
id = id?.replace(sourcePath, '') ?? ''
id = id.replace(/^\//, '')
if (id.endsWith('.js') || id.endsWith('.mjs')) {
return id
}
return `${id}.js`
},
chunkFileNames: '[hash].js',
})
await Promise.all(
output.map((chunk) => {
if (chunk.type === 'chunk') {
const map = chunk.map?.toString()
const encoding = {
encoding: 'utf8',
}
const filename = chunk.fileName
return Promise.all([
fs.outputFile(path.join(outputPath, filename), chunk.code, encoding),
map &&
fs.outputFile(
path.join(outputPath, `${filename}.map`),
map,
encoding,
),
])
}
return () => {}
}),
)
}
export async function doBuildSingleEntry({
input,
sourcePath,
outputPath,
env,
name,
sourcemap,
devPrefix = 'dev.',
}: {
input: string
outputPath: string
sourcePath: string
env: string
name?: string
sourcemap: boolean
devPrefix?: string
}) {
try {
const bundle = await rollup({
input: input,
treeshake: { moduleSideEffects: true },
onwarn: onWarning,
external: (id) => path.join(id) !== path.join(input) && !needExternal(id),
plugins: createRollupPlugins(name, env),
})
fs.ensureDirSync(outputPath)
let file = path.join(outputPath, path.relative(sourcePath, input))
const basename = path.basename(input)
if (basename === 'package.json') {
file = path.join(outputPath, 'package.json.js')
}
if (env === 'development') {
file = file.replace(basename, `${devPrefix}${basename}`)
}
await bundle.write({
file,
exports: 'named',
sourcemap,
})
} catch (error: any) {
throw new Error(error)
}
}
function createRollupPlugins(name: string | undefined, env: string) {
return [
peerDepsExternal(),
resolve({
preferBuiltins: false,
browser: true,
extensions: ['.mjs', '.cjs', '.js', '.json'],
}),
rollupJSONPlugin({
preferConst: true,
indent: ' ',
compact: false,
namedExports: true,
}),
commonjs({
extensions: ['.js', '.cjs'],
esmExternals: true,
requireReturnsDefault: 'auto',
}),
rollupPluginWrapTargets(false, name),
esbuild({
minify: true,
define: {
'process.env.NODE_ENV': JSON.stringify(env),
'process.env.VUE_ENV': JSON.stringify('browser'),
},
}),
rollupPluginNodeProcessPolyfill({
NODE_ENV: env,
}),
(nodePolyfills as any)(),
// rollupUrlReplacePlugin(),
].filter(Boolean)
}
function needExternal(id: string) {
return (
id.startsWith(`${APP_NAME}:`) ||
id.startsWith(`node:`) ||
id[0] === '.' ||
path.isAbsolute(id) ||
['process', 'Buffer', 'module'].includes(id) ||
path.basename(id) === 'package.json'
)
}
function onWarning(warning, handler) {
if (
['THIS_IS_UNDEFINED', 'UNRESOLVED_IMPORT', 'SOURCEMAP_ERROR'].includes(
warning.code,
)
) {
return
}
handler(warning)
}