diff --git a/.ls-lint.yml b/.ls-lint.yml index 9e98363830..66034d6b95 100644 --- a/.ls-lint.yml +++ b/.ls-lint.yml @@ -9,10 +9,10 @@ ignore: - temp - rollup # rollup submodule - '**/dist' + - '**/*.config.{js,mjs,cjs,ts,cts,mts}' + - '**/*.test.{js,mjs,cjs,ts,cts,mts}' - packages/rollup-tests/test # Copied from rollup repo - 'crates/rolldown/tests/fixtures/**/_test.js' # `_test.js` is a special file, which will be treat as the entry file while executing the build output of fixture. - - packages/rolldown/build.config.ts # convention name for using unbuild - - packages/rolldown/vitest.config.ts - web/docs/.vitepress/cache # cache files generated by vitepress # FIXME: should not ignore following folders - web diff --git a/cspell.json b/cspell.json index 83b108f9d3..4cf7409e99 100644 --- a/cspell.json +++ b/cspell.json @@ -99,6 +99,8 @@ "tinybench", "transpiling", "treeshake", + "consola", + "citty", "underfin", "UNKEYED", "vite", diff --git a/examples/basic-vue/build.js b/examples/basic-vue/rolldown.config.js similarity index 52% rename from examples/basic-vue/build.js rename to examples/basic-vue/rolldown.config.js index e24eae5a3b..e66dccfe5c 100644 --- a/examples/basic-vue/build.js +++ b/examples/basic-vue/rolldown.config.js @@ -1,9 +1,6 @@ -import { performance } from 'node:perf_hooks' -import * as rolldown from 'rolldown' +import { defineConfig } from 'rolldown' -const start = performance.now() - -const build = await rolldown.rolldown({ +export default defineConfig({ input: './index.js', resolve: { // This needs to be explicitly set for now because oxc resolver doesn't @@ -12,7 +9,3 @@ const build = await rolldown.rolldown({ conditionNames: ['import'], }, }) - -await build.write() - -console.log(`bundled in ${(performance.now() - start).toFixed(2)}ms`) diff --git a/packages/rolldown/bin/cli.js b/packages/rolldown/bin/cli.js new file mode 100755 index 0000000000..4a3bf36f02 --- /dev/null +++ b/packages/rolldown/bin/cli.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import('../dist/cli.mjs') diff --git a/packages/rolldown/build.config.ts b/packages/rolldown/build.config.ts index 8f19abb8b2..10c5bdf771 100644 --- a/packages/rolldown/build.config.ts +++ b/packages/rolldown/build.config.ts @@ -5,13 +5,21 @@ import nodePath from 'node:path' import { globSync } from 'glob' export default defineBuildConfig({ - entries: ['./src/index'], + entries: [ + './src/index', + { + builder: 'rollup', + input: './src/cli/index', + name: 'cli', + }, + ], clean: true, declaration: true, // generate .d.ts files externals: [/rolldown-binding\..*\.node/, /@rolldown\/binding-.*/], rollup: { emitCJS: true, cjsBridge: true, + inlineDependencies: true, }, hooks: { 'build:done'(_ctx) { @@ -25,8 +33,8 @@ export default defineBuildConfig({ // Move the binary file to dist binaryFiles.forEach((file) => { const fileName = nodePath.basename(file) - console.log('Copying', file, 'to ./dist') - nodeFs.copyFileSync(file, `./dist/${fileName}`) + console.log('Copying', file, 'to ./dist/shared') + nodeFs.copyFileSync(file, `./dist/shared/${fileName}`) }) }, }, diff --git a/packages/rolldown/package.json b/packages/rolldown/package.json index f819199f64..6705ace02d 100644 --- a/packages/rolldown/package.json +++ b/packages/rolldown/package.json @@ -13,9 +13,14 @@ "rolldown" ], "files": [ + "bin", + "cli", "dist", "!dist/*.node" ], + "bin": { + "rolldown": "./bin/cli.js" + }, "main": "./dist/index.cjs", "types": "./dist/index.d.ts", "exports": { @@ -63,7 +68,9 @@ }, "devDependencies": { "@napi-rs/cli": "^3.0.0-alpha.43", + "citty": "^0.1.6", "colorette": "^2.0.20", + "consola": "^3.2.3", "glob": "^10.3.10", "rollup": "^4.12.1", "type-fest": "^4.12.0", diff --git a/packages/rolldown/src/cli/commands/bundle.ts b/packages/rolldown/src/cli/commands/bundle.ts new file mode 100644 index 0000000000..6eb47a91e2 --- /dev/null +++ b/packages/rolldown/src/cli/commands/bundle.ts @@ -0,0 +1,26 @@ +import { performance } from 'node:perf_hooks' +import consola from 'consola' +import { colors } from 'consola/utils' +import { RolldownOptions, rolldown } from '../../index' +import { RolldownConfigExport } from '../../types/rolldown-config-export' +import { arraify } from '../../utils' + +export async function bundle(configExport: RolldownConfigExport) { + const options = arraify(configExport) + + for (const option of options) { + await bundleInner(option) + } +} + +async function bundleInner(options: RolldownOptions) { + const start = performance.now() + + const build = await rolldown(options) + + const _output = await build.write(options?.output) + + consola.log( + `Finished in ${colors.bold((performance.now() - start).toFixed(2))} ms`, + ) +} diff --git a/packages/rolldown/src/cli/errors.ts b/packages/rolldown/src/cli/errors.ts new file mode 100644 index 0000000000..94a1363bf1 --- /dev/null +++ b/packages/rolldown/src/cli/errors.ts @@ -0,0 +1,2 @@ +export const ERR_UNSUPPORTED_CONFIG_FORMAT = + "Unsupported config format. please use '.js', '.mjs' and '.ts' format" diff --git a/packages/rolldown/src/cli/index.ts b/packages/rolldown/src/cli/index.ts new file mode 100644 index 0000000000..f9e6f4a012 --- /dev/null +++ b/packages/rolldown/src/cli/index.ts @@ -0,0 +1,52 @@ +import { defineCommand, runMain, showUsage } from 'citty' +import consola from 'consola' +import process from 'node:process' +import path from 'node:path' +import pkgJson from '../../package.json' assert { type: 'json' } +import { loadConfig } from './utils.js' +import { bundle } from './commands/bundle' + +const main = defineCommand({ + meta: { + name: 'rolldown', + version: pkgJson.version, + description: pkgJson.description, + }, + args: { + config: { + type: 'string', + alias: 'c', + description: + 'Use this config file (if argument is used but value is unspecified, defaults to rolldown.config.js)', + }, + help: { + type: 'boolean', + alias: 'h', + description: 'Show this help message', + }, + }, + async run(ctx) { + if (ctx.args.help) { + await showUsage(ctx.cmd) + return + } + const cwd = process.cwd() + let configPath + if (ctx.args.config) { + configPath = path.resolve(cwd, ctx.args.config) + } else { + configPath = path.resolve(cwd, 'rolldown.config.js') + } + + const config = await loadConfig(configPath) + + if (!config) { + consola.error(`No configuration found at ${configPath}`) + process.exit(1) + } + + await bundle(config) + }, +}) + +runMain(main) diff --git a/packages/rolldown/src/cli/utils.ts b/packages/rolldown/src/cli/utils.ts new file mode 100644 index 0000000000..94209cec50 --- /dev/null +++ b/packages/rolldown/src/cli/utils.ts @@ -0,0 +1,27 @@ +import path from 'node:path' +import { ERR_UNSUPPORTED_CONFIG_FORMAT } from './errors.js' +import { RolldownConfigExport } from '../types/rolldown-config-export.js' + +/** + * @typedef {import('../rollup').RollupOptions} RollupOptions + */ + +/** + * Load a rolldown configuration file + */ +export async function loadConfig( + configPath: string, +): Promise { + if (!isSupportedFormat(configPath)) { + throw new Error(ERR_UNSUPPORTED_CONFIG_FORMAT) + } + return import(configPath).then((config) => config.default) +} + +/** + * Check whether the configuration file is supported + */ +function isSupportedFormat(configPath: string): boolean { + const ext = path.extname(configPath) + return /\.(js|mjs)$/.test(ext) +} diff --git a/packages/rolldown/src/index.ts b/packages/rolldown/src/index.ts index 44d83b62ce..ed5a476604 100644 --- a/packages/rolldown/src/index.ts +++ b/packages/rolldown/src/index.ts @@ -1,19 +1,19 @@ import { RolldownOutput } from './objects/rolldown-output' import type { InputOptions, RolldownPlugin } from './options/input-options' import type { OutputOptions } from './options/output-options' +import type { RolldownOptions } from './types/rolldown-options' +import { defineConfig } from './utils/define-config' export { rolldown, experimental_scan } from './rolldown' -interface RollupOptions extends InputOptions { - // This is included for compatibility with config files but ignored by rollup.rollup - output?: OutputOptions | OutputOptions[] -} +export { defineConfig } -// export types from rolldown export type { - RollupOptions, + RolldownOptions, + RolldownOptions as RollupOptions, + RolldownOutput, + RolldownOutput as RollupOutput, InputOptions, OutputOptions, RolldownPlugin as Plugin, - RolldownOutput as RollupOutput, } diff --git a/packages/rolldown/src/rolldown-build.ts b/packages/rolldown/src/rolldown-build.ts index 3c1b0b199b..6af24ba33f 100644 --- a/packages/rolldown/src/rolldown-build.ts +++ b/packages/rolldown/src/rolldown-build.ts @@ -10,6 +10,7 @@ export class RolldownBuild { #bundler?: Bundler constructor(inputOptions: InputOptions) { + // TODO: Check if `inputOptions.output` is set. If so, throw an warning that it is ignored. this.#inputOptions = inputOptions } diff --git a/packages/rolldown/src/types/rolldown-config-export.ts b/packages/rolldown/src/types/rolldown-config-export.ts new file mode 100644 index 0000000000..46b211e92d --- /dev/null +++ b/packages/rolldown/src/types/rolldown-config-export.ts @@ -0,0 +1,3 @@ +import { RolldownOptions } from './rolldown-options' + +export type RolldownConfigExport = RolldownOptions | RolldownOptions[] diff --git a/packages/rolldown/src/types/rolldown-options.ts b/packages/rolldown/src/types/rolldown-options.ts new file mode 100644 index 0000000000..ea8ea3ad6e --- /dev/null +++ b/packages/rolldown/src/types/rolldown-options.ts @@ -0,0 +1,7 @@ +import { InputOptions } from '../options/input-options' +import { OutputOptions } from '../options/output-options' + +export interface RolldownOptions extends InputOptions { + // This is included for compatibility with config files but ignored by `rolldown.rolldown` + output?: OutputOptions +} diff --git a/packages/rolldown/src/utils/define-config.ts b/packages/rolldown/src/utils/define-config.ts new file mode 100644 index 0000000000..b79aae5eb0 --- /dev/null +++ b/packages/rolldown/src/utils/define-config.ts @@ -0,0 +1,7 @@ +import { RolldownConfigExport } from '../types/rolldown-config-export' + +export function defineConfig( + config: RolldownConfigExport, +): RolldownConfigExport { + return config +} diff --git a/packages/rolldown/test/cli/config.test.ts b/packages/rolldown/test/cli/config.test.ts new file mode 100644 index 0000000000..53e9cbd837 --- /dev/null +++ b/packages/rolldown/test/cli/config.test.ts @@ -0,0 +1,47 @@ +import { describe, test, assert } from 'vitest' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { loadConfig } from '../../src/cli/utils' +import { ERR_UNSUPPORTED_CONFIG_FORMAT } from '../../src/cli/errors' +import { expect } from 'vitest' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +describe('loadConfig', () => { + const RE_ERR = RegExp(ERR_UNSUPPORTED_CONFIG_FORMAT) + + test('js', async () => { + const config = await loadConfig( + path.resolve(__dirname, 'fixtures/rolldown.config.js'), + ) + assert.deepEqual(config, { input: 'src/index.js' }) + }) + + test('mjs', async () => { + const config = await loadConfig( + path.resolve(__dirname, 'fixtures/rolldown.config.mjs'), + ) + assert.deepEqual(config, [ + { input: 'src/app1/index.js' }, + { input: 'src/app2/index.js' }, + ]) + }) + + test('cjs', async () => { + await expect( + loadConfig(path.resolve(__dirname, 'fixtures/rolldown.config.cjs')), + ).rejects.toThrowError(RE_ERR) + }) + + test('other format', async () => { + await expect( + loadConfig(path.resolve(__dirname, 'fixtures/rolldown.config.json')), + ).rejects.toThrowError(RE_ERR) + }) + + test('not found file', async () => { + await expect( + loadConfig(path.join(__dirname, 'fixtures/rollup.config.js')), + ).rejects.toThrowError(Error) + }) +}) diff --git a/packages/rolldown/test/cli/fixtures/basic/rolldown.config.js b/packages/rolldown/test/cli/fixtures/basic/rolldown.config.js new file mode 100644 index 0000000000..11af42931d --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/basic/rolldown.config.js @@ -0,0 +1,24 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +export default { + // input: 'src/index.js', + input: path.resolve(__dirname, 'src/index.js'), + output: [ + { + // dir: 'build', + // file: 'build/bundle.js', + dir: path.resolve(__dirname, 'build'), + file: path.resolve(__dirname, 'build/bundle.js'), + }, + ], + resolve: { + conditionNames: ['import'], + alias: { + // modules: 'src/modules', + modules: path.resolve(__dirname, 'src/modules'), + }, + }, +} diff --git a/packages/rolldown/test/cli/fixtures/basic/src/calc.js b/packages/rolldown/test/cli/fixtures/basic/src/calc.js new file mode 100644 index 0000000000..241be4f120 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/basic/src/calc.js @@ -0,0 +1,7 @@ +export function add(a, b) { + return a + b +} + +export function sub(a, b) { + return a - b +} diff --git a/packages/rolldown/test/cli/fixtures/basic/src/index.js b/packages/rolldown/test/cli/fixtures/basic/src/index.js new file mode 100644 index 0000000000..4d646891e0 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/basic/src/index.js @@ -0,0 +1,7 @@ +import { add, sub } from './calc' +import { say } from 'modules/say' + +console.log(add(1, 2)) // 3 +console.log(sub(1, 2)) // -1 + +say('hello world!') diff --git a/packages/rolldown/test/cli/fixtures/basic/src/modules/say.js b/packages/rolldown/test/cli/fixtures/basic/src/modules/say.js new file mode 100644 index 0000000000..0870291b8c --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/basic/src/modules/say.js @@ -0,0 +1,3 @@ +export function say(msg) { + console.log(msg) +} diff --git a/packages/rolldown/test/cli/fixtures/rolldown.config.cjs b/packages/rolldown/test/cli/fixtures/rolldown.config.cjs new file mode 100644 index 0000000000..3eb9167425 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/rolldown.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + input: 'src/index.js', +} diff --git a/packages/rolldown/test/cli/fixtures/rolldown.config.js b/packages/rolldown/test/cli/fixtures/rolldown.config.js new file mode 100644 index 0000000000..a5bd3ab982 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/rolldown.config.js @@ -0,0 +1,3 @@ +export default { + input: 'src/index.js', +} diff --git a/packages/rolldown/test/cli/fixtures/rolldown.config.json b/packages/rolldown/test/cli/fixtures/rolldown.config.json new file mode 100644 index 0000000000..c6e431fd73 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/rolldown.config.json @@ -0,0 +1,3 @@ +{ + "input": "src/index.js" +} diff --git a/packages/rolldown/test/cli/fixtures/rolldown.config.mjs b/packages/rolldown/test/cli/fixtures/rolldown.config.mjs new file mode 100644 index 0000000000..2e3d9d01f8 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/rolldown.config.mjs @@ -0,0 +1,8 @@ +export default [ + { + input: 'src/app1/index.js', + }, + { + input: 'src/app2/index.js', + }, +] diff --git a/packages/rolldown/test/cli/fixtures/rolldown.config.ts b/packages/rolldown/test/cli/fixtures/rolldown.config.ts new file mode 100644 index 0000000000..921b8033e0 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/rolldown.config.ts @@ -0,0 +1,11 @@ +import { transform } from './transform' + +export default { + input: 'src/index.ts', + plugins: [ + { + name: 'test-plugin', + transform, + }, + ], +} diff --git a/packages/rolldown/test/cli/fixtures/transform.ts b/packages/rolldown/test/cli/fixtures/transform.ts new file mode 100644 index 0000000000..dbc6033659 --- /dev/null +++ b/packages/rolldown/test/cli/fixtures/transform.ts @@ -0,0 +1,4 @@ +export function transform(code: string, id: string) { + console.log('transform', id, code) + return code +} diff --git a/packages/rolldown/vitest.config.mts b/packages/rolldown/vitest.config.mts index 25d00a4c7e..3f7edd9ec0 100644 --- a/packages/rolldown/vitest.config.mts +++ b/packages/rolldown/vitest.config.mts @@ -2,7 +2,7 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - include: ['./test/runner.ts'], + include: ['./test/runner.ts', './test/*.test.ts'], testTimeout: 20000, }, esbuild: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a9ee7b330..0501fa954a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,9 +96,15 @@ importers: '@napi-rs/cli': specifier: ^3.0.0-alpha.43 version: 3.0.0-alpha.43 + citty: + specifier: ^0.1.6 + version: 0.1.6 colorette: specifier: ^2.0.20 version: 2.0.20 + consola: + specifier: ^3.2.3 + version: 3.2.3 glob: specifier: ^10.3.10 version: 10.3.10