Skip to content

Commit

Permalink
feat: rolldown cli first implementation (#610)
Browse files Browse the repository at this point in the history
* feat: rolldown cli first implementation

* fix: switch to pnpm

* chore: add node test files to ls-lint ignore

* fix: add config loading

* refactor: lazy jiti instance

* refactor: error management

* fix: move to packages/rolldown

* fix: define return type of load config

* fix: add more test case

* fix: add config path normalization

* fix: WIP building ...

* chore: add ls lint

* chore: add test case file to ls-lint ignore

* chore: revert git ignore

* fix: lint errors

* fix: spell

* fix: build implementation and test fixtures

* fix: add cli dir to dist files

* refactor to ts

---------

Co-authored-by: Yunfei <[email protected]>
  • Loading branch information
kazupon and hyf0 committed Mar 21, 2024
1 parent 78c4c38 commit c82e59d
Show file tree
Hide file tree
Showing 28 changed files with 285 additions and 22 deletions.
4 changes: 2 additions & 2 deletions .ls-lint.yml
Expand Up @@ -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
2 changes: 2 additions & 0 deletions cspell.json
Expand Up @@ -99,6 +99,8 @@
"tinybench",
"transpiling",
"treeshake",
"consola",
"citty",
"underfin",
"UNKEYED",
"vite",
Expand Down
@@ -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
Expand All @@ -12,7 +9,3 @@ const build = await rolldown.rolldown({
conditionNames: ['import'],
},
})

await build.write()

console.log(`bundled in ${(performance.now() - start).toFixed(2)}ms`)
2 changes: 2 additions & 0 deletions packages/rolldown/bin/cli.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
import('../dist/cli.mjs')
14 changes: 11 additions & 3 deletions packages/rolldown/build.config.ts
Expand Up @@ -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) {
Expand All @@ -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}`)
})
},
},
Expand Down
7 changes: 7 additions & 0 deletions packages/rolldown/package.json
Expand Up @@ -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": {
Expand Down Expand Up @@ -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",
Expand Down
26 changes: 26 additions & 0 deletions 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`,
)
}
2 changes: 2 additions & 0 deletions 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"
52 changes: 52 additions & 0 deletions 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)
27 changes: 27 additions & 0 deletions 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<RolldownConfigExport | undefined> {
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)
}
14 changes: 7 additions & 7 deletions 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,
}
1 change: 1 addition & 0 deletions packages/rolldown/src/rolldown-build.ts
Expand Up @@ -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
}

Expand Down
3 changes: 3 additions & 0 deletions packages/rolldown/src/types/rolldown-config-export.ts
@@ -0,0 +1,3 @@
import { RolldownOptions } from './rolldown-options'

export type RolldownConfigExport = RolldownOptions | RolldownOptions[]
7 changes: 7 additions & 0 deletions 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
}
7 changes: 7 additions & 0 deletions 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
}
47 changes: 47 additions & 0 deletions 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)
})
})
24 changes: 24 additions & 0 deletions 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'),
},
},
}
7 changes: 7 additions & 0 deletions 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
}
7 changes: 7 additions & 0 deletions 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!')
3 changes: 3 additions & 0 deletions packages/rolldown/test/cli/fixtures/basic/src/modules/say.js
@@ -0,0 +1,3 @@
export function say(msg) {
console.log(msg)
}
3 changes: 3 additions & 0 deletions packages/rolldown/test/cli/fixtures/rolldown.config.cjs
@@ -0,0 +1,3 @@
module.exports = {
input: 'src/index.js',
}
3 changes: 3 additions & 0 deletions packages/rolldown/test/cli/fixtures/rolldown.config.js
@@ -0,0 +1,3 @@
export default {
input: 'src/index.js',
}
3 changes: 3 additions & 0 deletions packages/rolldown/test/cli/fixtures/rolldown.config.json
@@ -0,0 +1,3 @@
{
"input": "src/index.js"
}
8 changes: 8 additions & 0 deletions packages/rolldown/test/cli/fixtures/rolldown.config.mjs
@@ -0,0 +1,8 @@
export default [
{
input: 'src/app1/index.js',
},
{
input: 'src/app2/index.js',
},
]
11 changes: 11 additions & 0 deletions 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,
},
],
}

0 comments on commit c82e59d

Please sign in to comment.