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 all 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ dist
.yalc
yalc.lock
test/**/*.js.map
.DS_Store
.DS_Store
63 changes: 62 additions & 1 deletion 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, join } from 'path'
import { wasm } from '@rollup/plugin-wasm'
import { swc } from 'rollup-plugin-swc3'
import commonjs from '@rollup/plugin-commonjs'
Expand Down Expand Up @@ -38,7 +38,9 @@ import {
import {
availableExportConventions,
availableESExtensionsRegex,
dtsExtensions,
} from './constants'
import { logger } from './logger'

const swcMinifyOptions = {
compress: true,
Expand Down Expand Up @@ -375,6 +377,65 @@ export async function buildEntryConfig(
configs.push(...buildConfigs)
})

const binaryExports = pkg.bin
if (binaryExports) {
// binDistPaths: [ [ 'bin1', './dist/bin1.js'], [ 'bin2', './dist/bin2.js'] ]
const binPairs = typeof binaryExports === 'string'
? [['bin', binaryExports]]
: Object.keys(binaryExports)
.map((key) => [join('bin', key), (binaryExports)[key]])

const isESModule = pkg.type === 'module'
const binExportPaths = binPairs.reduce((acc, [binName, binDistPath]) => {
const ext = extname(binDistPath).slice(1) as keyof typeof dtsExtensions
const isCjsExt = ext === 'cjs'
const isEsmExt = ext === 'mjs'

const exportType = isEsmExt
? 'import'
: isCjsExt
? 'require'
: isESModule
? 'import'
: 'require'

acc[binName] = {
[exportType]: binDistPath,
}
return acc
}, {} as ExportPaths)

for (const [binName] of binPairs) {
const source = await getSourcePathFromExportPath(
cwd,
binName,
'$binary'
)

if (!source) {
logger.warn(`Cannot find source file for ${binName}`)
continue
}

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

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

Expand Down
4 changes: 3 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,8 @@ async function bundle(
? (await fileExists(entryPath)) && (await fs.stat(entryPath)).isFile()
: false

if (!hasSpecifiedEntryFile && !isMultiEntries) {
const hasNoEntry = !hasSpecifiedEntryFile && !isMultiEntries && !hasBin
if (hasNoEntry) {
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 dtsExtensions = {
js: '.d.ts',
cjs: '.d.cts',
mjs: '.d.mts',
}
12 changes: 4 additions & 8 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 { dtsExtensions } from './constants'

export function getTypings(pkg: PackageMetadata) {
return pkg.types || pkg.typings
Expand Down Expand Up @@ -235,14 +236,9 @@ 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 keyof typeof dtsExtensions
const typeFile = getDistPath(
`${filenameWithoutExtension(filePath) || ''}${dtsExtentions[ext]}`,
`${filenameWithoutExtension(filePath) || ''}${dtsExtensions[ext]}`,
cwd,
)
if (existed.has(typeFile)) {
Expand Down Expand Up @@ -314,7 +310,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: 3 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function resolveSourceFile(cwd: string, filename: string) {
return path.resolve(cwd, SRC, filename)
}

async function findSourceEntryFile(
export async function findSourceEntryFile(
cwd: string,
exportPath: string,
exportTypeSuffix: string | null,
Expand Down Expand Up @@ -109,7 +109,8 @@ export async function getSourcePathFromExportPath(
if (exportPath === '.') exportPath = './index'

// Find convention-based source file for specific export types
if (availableExportConventions.includes(exportType)) {
// $binary represents `pkg.bin`
if (availableExportConventions.includes(exportType) && exportType !== '$binary') {
const filename = await findSourceEntryFile(
cwd,
exportPath,
Expand Down
64 changes: 59 additions & 5 deletions test/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import fs from 'fs/promises'
import fsSync from 'fs'
import { execSync, fork } from 'child_process'
import { resolve, join } from 'path'
import { stripANSIColor, existsFile, assertFilesContent } from './testing-utils'
Expand Down Expand Up @@ -228,10 +227,7 @@ const testCases: {
name: 'ts-incremental',
args: [],
async expected(dir) {
const distFiles = [
'./dist/index.js',
'./dist/index.d.ts',
]
const distFiles = ['./dist/index.js', './dist/index.d.ts']

for (const f of distFiles) {
expect(await existsFile(join(dir, f))).toBe(true)
Expand Down Expand Up @@ -312,6 +308,64 @@ const testCases: {
)
},
},
{
name: 'bin/single-path',
args: [],
async expected(dir) {
const distFiles = [
join(dir, './dist/bin.js'),
join(dir, './dist/bin.d.ts'),
]
for (const f of distFiles) {
expect(await existsFile(f)).toBe(true)
}
expect(await fs.readFile(distFiles[0], 'utf-8')).toContain(
'#!/usr/bin/env node',
)
},
},
{
name: 'bin/multi-path',
args: [],
async expected(dir) {
const distBinFiles = [
join(dir, './dist/bin/a.js'),
join(dir, './dist/bin/b.js'),
]
const distTypeFiles = [
join(dir, './dist/bin/a.d.ts'),
join(dir, './dist/bin/b.d.ts'),
]
const distFiles = distBinFiles.concat(distTypeFiles)

for (const distFile of distFiles) {
expect(await existsFile(distFile)).toBe(true)
}
for (const distScriptFile of distBinFiles) {
expect(await fs.readFile(distScriptFile, 'utf-8')).toContain(
'#!/usr/bin/env node',
)
}
},
},
{
name: 'bin/cts',
args: [],
async expected(dir) {
const distFiles = [
join(dir, './dist/bin/index.cjs'),
join(dir, './dist/bin/index.d.cts'),
]

for (const distFile of distFiles) {
expect(await existsFile(distFile)).toBe(true)
}

expect(await fs.readFile(distFiles[0], 'utf-8')).toContain(
'#!/usr/bin/env node',
)
}
},
{
name: 'esm-shims',
args: [],
Expand Down
3 changes: 3 additions & 0 deletions test/integration/bin/cts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"bin": "./dist/bin/index.cjs"
}
3 changes: 3 additions & 0 deletions test/integration/bin/cts/src/bin/index.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node
const path = require('path')
console.log(path.basename(__filename))
1 change: 1 addition & 0 deletions test/integration/bin/cts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
6 changes: 6 additions & 0 deletions test/integration/bin/multi-path/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"bin": {
"a": "./dist/bin/a.js",
"b": "./dist/bin/b.js"
}
}
2 changes: 2 additions & 0 deletions test/integration/bin/multi-path/src/bin/a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
console.log('a')
2 changes: 2 additions & 0 deletions test/integration/bin/multi-path/src/bin/b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
console.log('b')
1 change: 1 addition & 0 deletions test/integration/bin/multi-path/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
3 changes: 3 additions & 0 deletions test/integration/bin/single-path/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"bin": "./dist/bin.js"
}
2 changes: 2 additions & 0 deletions test/integration/bin/single-path/src/bin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
console.log('Hello, world!')
1 change: 1 addition & 0 deletions test/integration/bin/single-path/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}