diff --git a/README.md b/README.md index a499d31..b52f409 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ import { defineConfig } from '@hypernym/bundler' export default defineConfig({ entries: [ { input: './src/index.ts' }, - { declaration: './src/types/index.ts' }, + { dts: './src/types/index.ts' }, { input: './src/utils/index.ts', output: './dist/utils/utils.min.mjs', @@ -99,11 +99,34 @@ Set a custom config path via the CLI command: npx hyperbundler --config hyper.config.ts ``` -## Options +## Formats + +During transformation, file formats are automatically resolved and in most cases there is no need for additional configuration. + +`Hyperbundler` module environment for generated files defaults to `esm`, which means the outputs will have a `.mjs` extension unless otherwise specified. For TypeScript declarations, the appropriate extension will be `.d.mts`. + +Formats can also be explicitly specified for each entry, if necessary. + +### Inputs + +Default transformation behaviour for all `chunk` entries: + +- `./srcDir/file.js` resolves to `./outDir/file.mjs` +- `./srcDir/file.mjs` resolves to `./outDir/file.mjs` +- `./srcDir/file.cjs` resolves to `./outDir/file.cjs` +- `./srcDir/file.ts` resolves to `./outDir/file.mjs` +- `./srcDir/file.mts` resolves to `./outDir/file.mjs` +- `./srcDir/file.cts` resolves to `./outDir/file.cjs` -All options are documented with descriptions and examples so auto-completion will be offered as you type. +### Declarations -Simply hover over the property and see what it does in the `quickinfo`. +Default transformation behaviour for all `dts` entries: + +- `./srcDir/file.ts` resolves to `./outDir/file.d.mts` + +## Options + +All options are documented with descriptions and examples so auto-completion will be offered as you type. Simply hover over the property and see what it does in the `quickinfo`. ### entries @@ -121,7 +144,7 @@ import { defineConfig } from '@hypernym/bundler' export default defineConfig({ entries: [ { input: './src/index.ts' }, // => './dist/index.mjs' - { declaration: './src/types.ts' }, // => './dist/types.d.ts' + { dts: './src/types.ts' }, // => './dist/types.d.mts' // ... ], }) @@ -140,9 +163,7 @@ import { defineConfig } from '@hypernym/bundler' export default defineConfig({ entries: [ - { - input: './src/index.ts', // => './dist/index.mjs' - }, + { input: './src/index.ts' }, // => './dist/index.mjs' ], }) ``` @@ -160,9 +181,19 @@ import { defineConfig } from '@hypernym/bundler' export default defineConfig({ entries: [ - { - { declaration: './src/types.ts' }, // => './dist/types.d.ts' - }, + { declaration: './src/types.ts' }, // => './dist/types.d.mts' + ], +}) +``` + +Also, it is possible to use `dts` alias. + +```ts +import { defineConfig } from '@hypernym/bundler' + +export default defineConfig({ + entries: [ + { dts: './src/types.ts' }, // => './dist/types.d.mts' ], }) ``` @@ -373,7 +404,7 @@ export default defineConfig({ ### build:entry:start -- Type: `(options: BuildEntryOptions, stats: BuildStats) => void | Promise` +- Type: `(entry: BuildEntryOptions, stats: BuildEntryStats) => void | Promise` - Default: `undefined` Called on each entry just before the build process. @@ -388,12 +419,12 @@ import { plugin1, plugin2 } from './src/utils/plugins.js' export default defineConfig({ hooks: { - 'build:entry:start': async (options, stats) => { + 'build:entry:start': async (entry, stats) => { // adds custom plugins for a specific entry only - if (options.input?.includes('./src/index.ts')) { - options.defaultPlugins = [ + if (entry.input?.includes('./src/index.ts')) { + entry.defaultPlugins = [ plugin1(), // adds a custom plugin before the default bundler plugins - ...options.defaultPlugins, // list of default bundler plugins + ...entry.defaultPlugins, // list of default bundler plugins plugin2(), // adds a custom plugin after the default bundler plugins ] } @@ -404,7 +435,7 @@ export default defineConfig({ ### build:entry:end -- Type: `(options: BuildEntryOptions, stats: BuildStats) => void | Promise` +- Type: `(entry: BuildEntryOptions, stats: BuildEntryStats) => void | Promise` - Default: `undefined` Called on each entry right after the build process is completed. @@ -416,7 +447,7 @@ import { defineConfig } from '@hypernym/bundler' export default defineConfig({ hooks: { - 'build:entry:end': async (options, stats) => { + 'build:entry:end': async (entry, stats) => { // ... }, }, diff --git a/bundler.config.ts b/bundler.config.ts index 2649103..69de906 100644 --- a/bundler.config.ts +++ b/bundler.config.ts @@ -4,12 +4,19 @@ import { version } from './package.json' export default defineConfig({ entries: [ { input: './src/index.ts' }, - { declaration: './src/types/index.ts' }, + { dts: './src/types/index.ts' }, + { + input: './src/index.ts', + output: './dist/index.cjs', + }, + { + dts: './src/types/index.ts', + output: './dist/types/index.d.cts', + }, { input: './src/bin/index.ts', transformers: { replace: { - preventAssignment: true, __version__: version, }, }, diff --git a/src/bin/build.ts b/src/bin/build.ts index 1131f1c..ec4182f 100644 --- a/src/bin/build.ts +++ b/src/bin/build.ts @@ -149,6 +149,7 @@ export async function build( } const fileStats = { + cwd, path: `${parseOutput(_entry.output)}/${parseInput(copyInput)}`, size: totalSize, buildTime: Date.now() - entryStart, @@ -166,17 +167,17 @@ export async function build( if (entry.input) { const logFilter = getLogFilter(entry.logFilter || []) - const _output = getOutputPath(outDir, entry.input) + const _output = entry.output || getOutputPath(outDir, entry.input) let _format: ModuleFormat = 'esm' if (_output.endsWith('.cjs')) _format = 'cjs' const buildLogs: BuildLogs[] = [] const _entry = { input: entry.input, - output: entry.output || _output, + output: _output, externals: entry.externals || options.externals, format: entry.format || _format, - transformers: entry.transformers, + ...entry, defaultPlugins: [ esbuildPlugin({ minify: !isUndefined(entry.minify) @@ -185,15 +186,6 @@ export async function build( ...entry.transformers?.esbuild, }), ], - plugins: entry.plugins, - banner: entry.banner, - footer: entry.footer, - intro: entry.intro, - outro: entry.outro, - paths: entry.paths, - name: entry.name, - globals: entry.globals, - extend: entry.extend, } if (!entry.plugins) { @@ -225,7 +217,16 @@ export async function build( ) } - await hooks?.['build:entry:start']?.(_entry, buildStats) + const fileStats = { + cwd, + path: _entry.output, + size: 0, + buildTime: entryStart, + format: _entry.format, + logs: buildLogs, + } + + await hooks?.['build:entry:start']?.(_entry, fileStats) const _build = await rollup({ input: resolve(cwd, _entry.input), @@ -249,43 +250,30 @@ export async function build( }) const stats = await stat(resolve(cwd, _entry.output)) - const file = { - path: _entry.output, - size: stats.size, - buildTime: Date.now() - entryStart, - format: _entry.format, - logs: buildLogs, - } + fileStats.size = stats.size + fileStats.buildTime = Date.now() - entryStart + fileStats.logs = buildLogs - buildStats.files.push(file) + buildStats.files.push(fileStats) buildStats.size = buildStats.size + stats.size - logModuleStats(file, longestOutput) + logModuleStats(fileStats, longestOutput) - await hooks?.['build:entry:end']?.(_entry, buildStats) + await hooks?.['build:entry:end']?.(_entry, fileStats) } - if (entry.declaration) { + if (entry.dts || entry.declaration) { const logFilter = getLogFilter(entry.logFilter || []) - - const _output = getOutputPath(outDir, entry.declaration, true) - let _format: ModuleFormat = 'esm' - if (_output.endsWith('.d.cts')) _format = 'cjs' - const buildLogs: BuildLogs[] = [] + const dts = entry.dts! || entry.declaration! + const _entry = { - input: entry.declaration, - output: entry.output || _output, + dts, + output: entry.output || getOutputPath(outDir, dts, true), externals: entry.externals || options.externals, - format: entry.format || _format, - transformers: entry.transformers, + format: entry.format || 'esm', + ...entry, defaultPlugins: [dtsPlugin(entry.transformers?.dts)], - plugins: entry.plugins, - banner: entry.banner, - footer: entry.footer, - intro: entry.intro, - outro: entry.outro, - paths: entry.paths, } if (!entry.plugins) { @@ -294,10 +282,19 @@ export async function build( ) } - await hooks?.['build:entry:start']?.(_entry, buildStats) + const fileStats = { + cwd, + path: _entry.output, + size: 0, + buildTime: entryStart, + format: 'dts', + logs: buildLogs, + } + + await hooks?.['build:entry:start']?.(_entry, fileStats) const _build = await rollup({ - input: resolve(cwd, _entry.input), + input: resolve(cwd, _entry.dts), external: _entry.externals, plugins: _entry.plugins || _entry.defaultPlugins, onLog: (level, log) => { @@ -315,36 +312,28 @@ export async function build( }) const stats = await stat(resolve(cwd, _entry.output)) - const fileStats = { - path: _entry.output, - size: stats.size, - buildTime: Date.now() - entryStart, - format: 'dts', - logs: buildLogs, - } + fileStats.size = stats.size + fileStats.buildTime = Date.now() - entryStart + fileStats.logs = buildLogs buildStats.files.push(fileStats) buildStats.size = buildStats.size + stats.size logModuleStats(fileStats, longestOutput) - await hooks?.['build:entry:end']?.(_entry, buildStats) + await hooks?.['build:entry:end']?.(_entry, fileStats) } if (entry.template && entry.output) { const buildLogs: BuildLogs[] = [] - const _entry = { - template: entry.template, - output: entry.output, - } - - await write(_entry.output, _entry.template) + await write(entry.output, entry.template) - const stats = await stat(resolve(cwd, _entry.output)) + const stats = await stat(resolve(cwd, entry.output)) const fileStats = { - path: _entry.output, + cwd, + path: entry.output, size: stats.size, buildTime: Date.now() - entryStart, format: 'tmp', diff --git a/src/types/build.ts b/src/types/build.ts index c3492af..e8fd229 100644 --- a/src/types/build.ts +++ b/src/types/build.ts @@ -1,5 +1,5 @@ import type { Plugin, LogLevel, RollupLog } from 'rollup' -import type { EntryBase } from './entries' +import type { EntryBase, EntryChunk, EntryDeclaration } from './entries' import type { TransformersChunk, TransformersDeclaration } from './transformers' export interface BuildLogs { @@ -7,6 +7,33 @@ export interface BuildLogs { log: RollupLog } +export interface BuildEntryStats { + /** + * The root path of the project. + */ + cwd: string + /** + * Module output path. + */ + path: string + /** + * Module size. + */ + size: number + /** + * Build time of individual module. + */ + buildTime: number + /** + * Module format. + */ + format: string + /** + * List of warnings from build plugins. + */ + logs: BuildLogs[] +} + export interface BuildStats { /** * The root path of the project. @@ -23,31 +50,28 @@ export interface BuildStats { /** * List of generated bundle modules. */ - files: { - /** - * Module output path. - */ - path: string - /** - * Module size. - */ - size: number - /** - * Build time of individual module. - */ - buildTime: number - /** - * Module format. - */ - format: string - /** - * List of warnings from build plugins. - */ - logs: BuildLogs[] - }[] + files: BuildEntryStats[] } -export interface BuildEntryOptions extends EntryBase { +type PickEntryChunkOptions = + | 'input' + | 'name' + | 'globals' + | 'extend' + | 'minify' + | 'transformers' +type PickEntryDtsOptions = 'declaration' | 'dts' | 'transformers' + +export interface BuildEntryOptions + extends EntryBase, + Pick, + Pick { + /** + * Specifies the path of the transformed file. + * + * @default undefined + */ + output?: string /** * Specifies options for default plugins. * diff --git a/src/types/entries.ts b/src/types/entries.ts index 02a4103..ba0fc41 100644 --- a/src/types/entries.ts +++ b/src/types/entries.ts @@ -66,11 +66,34 @@ export interface EntryBase { export interface EntryChunk extends EntryBase { /** * Specifies the path of the build source. + * + * @example + * + * ```ts + * export default defineConfig({ + * entries: [ + * { input: './src/index.ts' }, // => './dist/index.mjs' + * ] + * }) + * ``` */ input?: string /** * Specifies the path of the transformed file. * + * @example + * + * ```ts + * export default defineConfig({ + * entries: [ + * { + * input: './src/index.ts', + * output: './out/index.js', // => './out/index.js' + * }, + * ] + * }) + * ``` + * * @default undefined */ output?: string @@ -117,6 +140,7 @@ export interface EntryChunk extends EntryBase { */ minify?: boolean declaration?: never + dts?: never copy?: never template?: never } @@ -124,11 +148,50 @@ export interface EntryChunk extends EntryBase { export interface EntryDeclaration extends EntryBase { /** * Specifies the path of the TypeScript `declaration` build source. + * + * @example + * + * ```ts + * export default defineConfig({ + * entries: [ + * { dts: './src/types.ts' }, // => './dist/types.d.mts' + * ] + * }) + * ``` + */ + dts?: string + /** + * Specifies the path of the TypeScript `declaration` build source. + * + * Also, it is possible to use `dts` alias. + * + * @example + * + * ```ts + * export default defineConfig({ + * entries: [ + * { declaration: './src/types.ts' }, // => './dist/types.d.mts' + * ] + * }) + * ``` */ declaration?: string /** * Specifies the path of the TypeScript transformed `declaration` file. * + * @example + * + * ```ts + * export default defineConfig({ + * entries: [ + * { + * dts: './src/types.ts', + * output: './out/types.d.ts', // => './out/types.d.ts' + * }, + * ] + * }) + * ``` + * * @default undefined */ output?: string @@ -203,6 +266,7 @@ export interface EntryCopy { copy?: CopyOptions input?: never declaration?: never + dts?: never template?: never name?: never globals?: never @@ -236,6 +300,7 @@ export interface EntryTemplate { output: string input?: never declaration?: never + dts?: never copy?: never name?: never globals?: never diff --git a/src/types/hooks.ts b/src/types/hooks.ts index 7225df6..80684ea 100644 --- a/src/types/hooks.ts +++ b/src/types/hooks.ts @@ -1,5 +1,5 @@ import type { Options } from './options' -import type { BuildStats, BuildEntryOptions } from './build' +import type { BuildEntryStats, BuildStats, BuildEntryOptions } from './build' export interface HooksOptions { /** @@ -48,7 +48,7 @@ export interface HooksOptions { * ```ts * export default defineConfig({ * hooks: { - * 'build:entry:start': async (options, stats) => { + * 'build:entry:start': async (entry, stats) => { * // ... * } * } @@ -58,8 +58,8 @@ export interface HooksOptions { * @default undefined */ 'build:entry:start'?: ( - options: BuildEntryOptions, - stats: BuildStats, + entry: BuildEntryOptions, + stats: BuildEntryStats, ) => void | Promise /** * Called on each entry right after the build process is completed. @@ -69,7 +69,7 @@ export interface HooksOptions { * ```ts * export default defineConfig({ * hooks: { - * 'build:entry:end': async (options, stats) => { + * 'build:entry:end': async (entry, stats) => { * // ... * } * } @@ -79,8 +79,8 @@ export interface HooksOptions { * @default undefined */ 'build:entry:end'?: ( - options: BuildEntryOptions, - stats: BuildStats, + entry: BuildEntryOptions, + stats: BuildEntryStats, ) => void | Promise /** * Called right after building is complete. diff --git a/src/types/options.ts b/src/types/options.ts index c3444ca..1d2bddd 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -14,7 +14,7 @@ export interface Options { * export default defineConfig({ * entries: [ * { input: './src/index.ts' }, // => './dist/index.mjs' - * { declaration: './src/types.ts' }, // => './dist/types.d.ts' + * { dts: './src/types.ts' }, // => './dist/types.d.mts' * // ... * ] * }) diff --git a/src/utils/get-longest-output.ts b/src/utils/get-longest-output.ts index f9edcbc..16d19cc 100644 --- a/src/utils/get-longest-output.ts +++ b/src/utils/get-longest-output.ts @@ -8,24 +8,20 @@ export function getLongestOutput( const outputs: string[] = [] for (const entry of entries) { - if ('copy' in entry && entry.copy) { - const out = entry.copy.output - outputs.push(out) - } + if (entry.copy) outputs.push(entry.copy.output) - if ('input' in entry && entry.input) { + if (entry.input) { const out = entry.output || getOutputPath(outDir, entry.input) outputs.push(out) } - if ('declaration' in entry && entry.declaration) { - const out = entry.output || getOutputPath(outDir, entry.declaration, true) + if (entry.declaration || entry.dts) { + const dts = entry.declaration! || entry.dts! + const out = entry.output || getOutputPath(outDir, dts, true) outputs.push(out) } - if ('template' in entry && entry.template) { - outputs.push(entry.output) - } + if (entry.template) outputs.push(entry.output) } return Math.max(...outputs.map((v) => v.length)) diff --git a/src/utils/get-output-path.ts b/src/utils/get-output-path.ts index 49046ae..678e85c 100644 --- a/src/utils/get-output-path.ts +++ b/src/utils/get-output-path.ts @@ -1,20 +1,19 @@ export function getOutputPath( outDir: string, input: string, - types: boolean = false, + dts?: boolean, ): string { const _input = input.startsWith('./') ? input.slice(2) : input let output = _input.replace(_input.split('/')[0], outDir) - const ts = types ? 'd.ts' : 'mjs' - const mts = types ? 'd.mts' : 'mjs' - const cts = types ? 'd.cts' : 'cjs' + const ext = dts ? 'd.mts' : 'mjs' + const cts = dts ? 'd.cts' : 'cjs' - if (output.endsWith('.ts')) output = `${output.slice(0, -2)}${ts}` - if (output.endsWith('.mts')) output = `${output.slice(0, -3)}${mts}` - if (output.endsWith('.cts')) output = `${output.slice(0, -3)}${cts}` + if (output.endsWith('.js')) output = `${output.slice(0, -2)}${ext}` + else if (output.endsWith('.ts')) output = `${output.slice(0, -2)}${ext}` + else if (output.endsWith('.mts')) output = `${output.slice(0, -3)}${ext}` + else if (output.endsWith('.cts')) output = `${output.slice(0, -3)}${cts}` if (outDir.startsWith('./') || outDir.startsWith('../')) return output - - return `./${output}` + else return `./${output}` }