Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: auto build bin field #282

Merged
merged 29 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1a9d0f0
Add support for bin entry
devjiwonchoi Nov 11, 2023
db35afd
Merge branch 'main' of github.com:devjiwonchoi/bunchee into bin
devjiwonchoi Nov 11, 2023
71508af
TODO: output
devjiwonchoi Nov 16, 2023
03e9471
Merge branch 'main' of github.com:devjiwonchoi/bunchee into bin
devjiwonchoi Nov 17, 2023
a188064
add handling output config
devjiwonchoi Nov 17, 2023
0dbbde0
fix: bin path is already dist path
devjiwonchoi Nov 17, 2023
7e4670d
Update integration.test.ts
devjiwonchoi Nov 17, 2023
ab6edaf
test: add multi-path test
devjiwonchoi Nov 18, 2023
1233b35
fix: add bin as not string case
devjiwonchoi Nov 18, 2023
6b37808
Merge branch 'bin' of github.com:devjiwonchoi/bunchee into bin
devjiwonchoi Nov 18, 2023
2248913
fix: remove hello world console
devjiwonchoi Nov 18, 2023
ab2972a
refac: build-config
devjiwonchoi Nov 18, 2023
2b735bf
refac: exports
devjiwonchoi Nov 18, 2023
6886437
refac: exports
devjiwonchoi Nov 18, 2023
de72835
refac: move bin related codes from exports to build config
devjiwonchoi Nov 18, 2023
290b03e
chore: remove unnecessary pkg from getExportTypeDist
devjiwonchoi Nov 18, 2023
6486090
refac: dts and else outputs as one
devjiwonchoi Nov 18, 2023
80cf0ae
lint fix
devjiwonchoi Nov 18, 2023
fe0e2b2
fix: resolve type any
devjiwonchoi Nov 18, 2023
e17f328
refac: skip loop if no filename
devjiwonchoi Nov 18, 2023
dc8816b
test: add multi out dir
devjiwonchoi Nov 18, 2023
1503cef
add tests
devjiwonchoi Nov 18, 2023
4e28e3f
chore: modify comments
devjiwonchoi Nov 20, 2023
c7d5e95
test: remove nested path case
devjiwonchoi Nov 22, 2023
aaf6c03
chore: merge no entry cases
devjiwonchoi Nov 22, 2023
5d0b1c7
Merge branch 'main' into bin
huozhi Dec 3, 2023
69b306b
reverts as assertion
huozhi Dec 3, 2023
e8ba49c
create bin export paths map and refine tests
huozhi Dec 3, 2023
c60c483
new line
huozhi Dec 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# NPM
node_modules/
package-lock.json
node_modules
dist

# Output
dist/
out/

# Misc
.DS_Store
*.log
.yalc
yalc.lock
test/**/*.js.map
.DS_Store
test/**/*.js.map
121 changes: 105 additions & 16 deletions src/build-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
import type { InputOptions, OutputOptions, Plugin } from 'rollup'
import { type TypescriptOptions } from './typescript'

import { resolve, dirname } from 'path'
import { resolve, dirname, extname } from 'path'
import { wasm } from '@rollup/plugin-wasm'
import { swc } from 'rollup-plugin-swc3'
import commonjs from '@rollup/plugin-commonjs'
Expand All @@ -37,6 +37,7 @@ import {
import {
availableExportConventions,
availableESExtensionsRegex,
dtsExtentions,
} from './constants'

const swcMinifyOptions = {
Expand Down Expand Up @@ -89,9 +90,7 @@ async function buildInputConfig(
target: jscTarget,
minify: shouldMinify,
} = options
const hasSpecifiedTsTarget = Boolean(
tsCompilerOptions.target && tsConfigPath,
)
const hasSpecifiedTsTarget = Boolean(tsCompilerOptions.target && tsConfigPath)

const swcParserConfig = {
syntax: useTypescript ? 'typescript' : 'ecmascript',
Expand Down Expand Up @@ -141,10 +140,7 @@ async function buildInputConfig(
jsx: tsCompilerOptions.jsx || 'react',
}

const typesPlugins = [
...commonPlugins,
inlineCss({ skip: true }),
]
const typesPlugins = [...commonPlugins, inlineCss({ skip: true })]

if (useTypescript) {
const mergedOptions = {
Expand All @@ -159,7 +155,9 @@ async function buildInputConfig(
delete mergedOptions.tsBuildInfoFile
}

const dtsPlugin = (require('rollup-plugin-dts') as typeof import('rollup-plugin-dts')).default({
const dtsPlugin = (
require('rollup-plugin-dts') as typeof import('rollup-plugin-dts')
).default({
tsconfig: undefined,
compilerOptions: mergedOptions,
})
Expand Down Expand Up @@ -373,6 +371,58 @@ export async function buildEntryConfig(
configs.push(...buildConfigs)
})

if (pkg.bin) {
const isSinglePathBin = typeof pkg.bin === 'string'
const binDistPaths = isSinglePathBin
? [pkg.bin as string]
: Object.values(pkg.bin)

for (const binDistPath of binDistPaths) {
huozhi marked this conversation as resolved.
Show resolved Hide resolved
// assuming in `./a/b/c.js` the `a` as the dist folder name
// convert `./a/b/c.js` to `./b/c.js` as if the entry of the exports
const startsWithDotSlash = binDistPath.startsWith('./')

const binPathWithoutDist = binDistPath
.split('/')
// [ '.', 'a', 'b', 'c.js'] => [ 'b', 'c.js']
// [ 'a', 'b', 'c.js'] => [ 'b', 'c.js']
.slice(startsWithDotSlash ? 2 : 1)
// [ 'b', 'c.js'] => 'b/c.js'
.join('/')

// 'b/c.js' => './b/c' as if the key of the exports
const assumedSourcePathFromDistPath = `./${binPathWithoutDist.replace(
extname(binPathWithoutDist),
'',
)}`

const source = await getSourcePathFromExportPath(
cwd,
assumedSourcePathFromDistPath,
'',
)

if (!source) continue

const binEntryPath = await resolveSourceFile(cwd, source)
const binEntryConfig = buildConfig(
binEntryPath,
pkg,
exportPaths,
bundleConfig,
{
source: binEntryPath,
name: binDistPath,
export: {},
},
cwd,
tsOptions,
dts,
)
configs.push(binEntryConfig)
}
}

return (await Promise.all(configs)).filter(nonNullable)
}

Expand All @@ -397,22 +447,52 @@ async function buildConfig(
tsOptions,
dts,
)
const outputExports = getExportConditionDist(pkg, exportCondition, cwd)
const outputExports: Array<string | { format: 'cjs' | 'esm'; file: string }> =
dts
? getExportTypeDist(exportCondition, cwd)
: getExportConditionDist(pkg, exportCondition, cwd)

if (pkg.bin) {
huozhi marked this conversation as resolved.
Show resolved Hide resolved
const isSinglePathBin = typeof pkg.bin === 'string'
const binDistPaths = isSinglePathBin
? [pkg.bin as string]
: Object.values(pkg.bin)

for (const binDistPath of binDistPaths) {
const ext = extname(binDistPath).slice(1) as 'js' | 'cjs' | 'mjs'

if (dts) {
const dtsExt = dtsExtentions[ext]
const filename = filenameWithoutExtension(binDistPath)
if (!filename) continue

const binTypeFile = `${filename}${dtsExt}`
outputExports.push(binTypeFile)
} else {
// ESM by default, CJS if the file extension is .cjs
const isCJS = ext === 'cjs'
huozhi marked this conversation as resolved.
Show resolved Hide resolved

outputExports.push({
format: isCJS ? 'cjs' : 'esm',
file: binDistPath,
})
}
}
}

let outputConfigs = []

// Generate dts job - single config
if (dts) {
const typeOutputExports = getExportTypeDist(exportCondition, cwd)
outputConfigs = typeOutputExports.map((v) =>
outputConfigs = outputExports.map((v) =>
buildOutputConfigs(
pkg,
exportPaths,
{
...bundleConfig,
format: 'es',
useTypescript,
file: v,
file: v as string,
huozhi marked this conversation as resolved.
Show resolved Hide resolved
},
exportCondition,
cwd,
Expand All @@ -423,13 +503,18 @@ async function buildConfig(
} else {
// multi outputs with specified format
outputConfigs = outputExports.map((exportDist) => {
const { file, format } = exportDist as {
format: 'cjs' | 'esm'
file: string
}

return buildOutputConfigs(
pkg,
exportPaths,
{
...bundleConfig,
file: exportDist.file,
format: exportDist.format,
file,
format,
useTypescript,
},
exportCondition,
Expand All @@ -440,7 +525,11 @@ async function buildConfig(
})
// CLI output option is always prioritized
if (file) {
const fallbackFormat = outputExports[0]?.format
const firstOutput = outputExports[0] as {
format: 'cjs' | 'esm'
file: string
}
const fallbackFormat = firstOutput?.format
outputConfigs = [
buildOutputConfigs(
pkg,
Expand Down
3 changes: 2 additions & 1 deletion src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ async function bundle(
)
// const exportPathsLength = Object.keys(exportPaths).length
const isMultiEntries = hasMultiEntryExport(exportPaths) // exportPathsLength > 1
const hasBin = Boolean(pkg.bin)

const tsConfig = await resolveTsConfig(cwd)
const hasTsConfig = Boolean(tsConfig?.tsConfigPath)
Expand Down Expand Up @@ -135,7 +136,7 @@ async function bundle(
? (await fileExists(entryPath)) && (await fs.stat(entryPath)).isFile()
: false

if (!hasSpecifiedEntryFile && !isMultiEntries) {
if (!hasSpecifiedEntryFile && !isMultiEntries && !hasBin) {
huozhi marked this conversation as resolved.
Show resolved Hide resolved
const err = new Error(`Entry file \`${entryPath}\` is not existed`)
err.name = 'NOT_EXISTED'
return Promise.reject(err)
Expand Down
6 changes: 6 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ export const availableESExtensionsRegex = /\.(m|c)?[jt]sx?$/
export const dtsExtensionRegex = /\.d\.(m|c)?ts$/

export const SRC = 'src'

export const dtsExtentions = {
js: '.d.ts',
cjs: '.d.cts',
mjs: '.d.mts',
}
11 changes: 4 additions & 7 deletions src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
ParsedExportCondition,
} from './types'
import { exit, filenameWithoutExtension, hasCjsExtension } from './utils'
import { dtsExtentions } from './constants'

export function getTypings(pkg: PackageMetadata) {
return pkg.types || pkg.typings
Expand Down Expand Up @@ -235,12 +236,7 @@ export const getExportTypeDist = (
existed.add(typeFile)
continue
}
const ext = extname(filePath).slice(1)
const dtsExtentions: Record<string, string> = {
js: '.d.ts',
cjs: '.d.cts',
mjs: '.d.mts',
}
const ext = extname(filePath).slice(1) as 'js' | 'cjs' | 'mjs'
const typeFile = getDistPath(
`${filenameWithoutExtension(filePath) || ''}${dtsExtentions[ext]}`,
cwd,
Expand All @@ -250,6 +246,7 @@ export const getExportTypeDist = (
}
existed.add(typeFile)
}

return Array.from(existed)
}

Expand Down Expand Up @@ -314,7 +311,7 @@ export function getExportConditionDist(
dist.push({ format, file: distFile })
}

if (dist.length === 0) {
if (dist.length === 0 && !pkg.bin) {
// TODO: Deprecate this warning and behavior in v3
console.error(
`Doesn't fin any exports in ${pkg.name}, using default dist path dist/index.js`,
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type BundleConfig = {
type PackageMetadata = {
name?: string
main?: string
bin?: string | Record<string, string>
module?: string
type?: 'commonjs' | 'module'
dependencies?: Record<string, string>
Expand Down
5 changes: 5 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ export async function getSourcePathFromExportPath(
return
}

export async function getSourcePathFromOutputPath(
cwd: string,
outputPath: string,
) {}

// Unlike path.basename, forcedly removing extension
export function filenameWithoutExtension(file: string | undefined) {
return file
Expand Down
Loading