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: rolldown cli first implementation #610

Merged
merged 19 commits into from Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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,
},
],
}