Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5fd2729

Browse files
authored
feat(node): support ts config file (#3134)
<!-- Thank you for contributing! --> ### Description Follow rolldown-vite to support load ts config file. # build ts config file - polyfill the `__dirname/__filename` related stuff - external node_modules bare import - write outfile to same directory, avid resolve node_modules package has different. Note: the `vite` write it to nearest node_modules, it needs to add more unnecessary logic. # execute - using `dynamic import` to execute it and then delete outfile. <!-- Please insert your description here and provide especially info about the "what" this PR is solving -->
1 parent af456e1 commit 5fd2729

22 files changed

Lines changed: 217 additions & 82 deletions

File tree

packages/rolldown/src/cli/arguments/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from './utils'
99
import { parseArgs } from 'node:util'
1010
import { normalizeCliOptions, type NormalizedCliOptions } from './normalize'
11-
import { logger } from '../utils'
11+
import { logger } from '../logger'
1212
import type { Schema } from './types'
1313

1414
export const flattenedSchema: Record<string, Schema> = flattenSchema(

packages/rolldown/src/cli/arguments/normalize.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @description This file is used for normalize the options.
33
* In CLI, the input options and output options are mixed together. We need to tell them apart.
44
*/
5-
import { logger } from '../utils'
5+
import { logger } from '../logger'
66
import { setNestedProperty } from './utils'
77
import { CliOptions, cliOptionsSchema } from './schema'
88
import { inputCliOptionsSchema } from '../../options/input-options-schema'

packages/rolldown/src/cli/commands/bundle.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@ import path from 'node:path'
22
import { performance } from 'node:perf_hooks'
33
import { onExit } from 'signal-exit'
44
import { colors } from '../colors'
5-
import { ensureConfig, logger } from '../utils'
5+
import { logger } from '../logger'
66
import { NormalizedCliOptions } from '../arguments/normalize'
77
import { arraify } from '../../utils/misc'
88
import { rolldown } from '../../api/rolldown'
99
import { watch as rolldownWatch } from '../../api/watch'
1010
import type { RolldownOptions, RolldownOutput, RollupOutput } from '../..'
11+
import { loadConfig } from '../load-config'
1112

1213
export async function bundleWithConfig(
1314
configPath: string,
1415
cliOptions: NormalizedCliOptions,
1516
): Promise<void> {
16-
const config = await ensureConfig(configPath)
17+
const config = await loadConfig(configPath)
1718

1819
if (!config) {
1920
logger.error(`No configuration found at ${config}`)
2021
process.exit(1)
2122
}
2223

24+
// TODO: Could add more validation/diagnostics here to emit a nice error message
2325
const configList = arraify(config)
2426
const operation = cliOptions.watch ? watchInner : bundleInner
2527
for (const config of configList) {

packages/rolldown/src/cli/commands/help.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { logger } from '../utils'
1+
import { logger } from '../logger'
22
import {
33
version,
44
description,

packages/rolldown/src/cli/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import process from 'node:process'
22
import { bundleWithCliOptions, bundleWithConfig } from './commands/bundle'
3-
import { logger } from './utils'
3+
import { logger } from './logger'
44
import { parseCliArguments } from './arguments'
55
import { showHelp } from './commands/help'
66
import { version } from '../../package.json'
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import path from 'node:path'
2+
import fs from 'node:fs'
3+
import { ConfigExport } from '../types/config-export'
4+
import { pathToFileURL } from 'node:url'
5+
import { rolldown } from '../api/rolldown'
6+
import { RolldownOutputChunk } from '../types/rolldown-output'
7+
8+
export async function loadTsConfig(configFile: string): Promise<ConfigExport> {
9+
const file = await bundleTsConfig(configFile)
10+
try {
11+
return (await import(pathToFileURL(file).href)).default
12+
} finally {
13+
fs.unlink(file, () => {}) // Ignore errors
14+
}
15+
}
16+
17+
async function bundleTsConfig(configFile: string): Promise<string> {
18+
const dirnameVarName = 'injected_original_dirname'
19+
const filenameVarName = 'injected_original_filename'
20+
const importMetaUrlVarName = 'injected_original_import_meta_url'
21+
22+
const bundle = await rolldown({
23+
input: configFile,
24+
platform: 'node',
25+
resolve: {
26+
mainFields: ['main'],
27+
},
28+
define: {
29+
__dirname: dirnameVarName,
30+
__filename: filenameVarName,
31+
'import.meta.url': importMetaUrlVarName,
32+
'import.meta.dirname': dirnameVarName,
33+
'import.meta.filename': filenameVarName,
34+
},
35+
treeshake: false,
36+
external: [/^[\w@][^:]/], // external bare imports
37+
plugins: [
38+
{
39+
name: 'inject-file-scope-variables',
40+
transform: {
41+
filter: { id: /\.[cm]?[jt]s$/ },
42+
async handler(code, id) {
43+
const injectValues =
44+
`const ${dirnameVarName} = ${JSON.stringify(path.dirname(id))};` +
45+
`const ${filenameVarName} = ${JSON.stringify(id)};` +
46+
`const ${importMetaUrlVarName} = ${JSON.stringify(
47+
pathToFileURL(id).href,
48+
)};`
49+
return { code: injectValues + code, map: null }
50+
},
51+
},
52+
},
53+
],
54+
})
55+
const result = await bundle.write({
56+
dir: path.dirname(configFile),
57+
format: 'esm',
58+
sourcemap: 'inline',
59+
entryFileNames: 'rolldown.config.[hash].js',
60+
})
61+
62+
return result.output.find(
63+
(chunk): chunk is RolldownOutputChunk =>
64+
chunk.type === 'chunk' && chunk.isEntry,
65+
)!.fileName
66+
}
67+
68+
const SUPPORTED_JS_CONFIG_FORMATS = ['.js', '.mjs', '.cjs']
69+
const SUPPORTED_TS_CONFIG_FORMATS = ['.ts', '.mts', '.cts']
70+
const SUPPORTED_CONFIG_FORMATS = [
71+
...SUPPORTED_JS_CONFIG_FORMATS,
72+
...SUPPORTED_TS_CONFIG_FORMATS,
73+
]
74+
75+
export async function loadConfig(configPath: string): Promise<ConfigExport> {
76+
const ext = path.extname(configPath)
77+
78+
try {
79+
if (
80+
SUPPORTED_JS_CONFIG_FORMATS.includes(ext) ||
81+
(process.env.NODE_OPTIONS?.includes('--import=tsx') &&
82+
SUPPORTED_TS_CONFIG_FORMATS.includes(ext))
83+
) {
84+
return (await import(pathToFileURL(configPath).href)).default
85+
} else if (SUPPORTED_TS_CONFIG_FORMATS.includes(ext)) {
86+
return await loadTsConfig(configPath)
87+
} else {
88+
throw new Error(
89+
`Unsupported config format. Expected: \`${SUPPORTED_CONFIG_FORMATS.join(',')}\` but got \`${ext}\``,
90+
)
91+
}
92+
} catch (err) {
93+
throw new Error('Error happened while loading config.', { cause: err })
94+
}
95+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { type ConsolaInstance, createConsola } from 'consola'
2+
3+
/**
4+
* Console logger
5+
*/
6+
export const logger: Record<string, any> | ConsolaInstance = process.env
7+
.ROLLDOWN_TEST
8+
? createTestingLogger()
9+
: createConsola({
10+
formatOptions: {
11+
date: false,
12+
},
13+
})
14+
15+
function createTestingLogger() {
16+
const types = [
17+
'silent',
18+
'fatal',
19+
'error',
20+
'warn',
21+
'log',
22+
'info',
23+
'success',
24+
'fail',
25+
'ready',
26+
'start',
27+
'box',
28+
'debug',
29+
'trace',
30+
'verbose',
31+
]
32+
const ret: Record<string, any> = Object.create(null)
33+
for (const type of types) {
34+
ret[type] = console.log
35+
}
36+
return ret
37+
}

packages/rolldown/src/cli/utils.ts

Lines changed: 0 additions & 69 deletions
This file was deleted.

packages/rolldown/tests/cli/__snapshots__/cli-e2e.test.ts.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,24 @@ exports[`cli options for bundling > should handle single string options 1`] = `
159159
"
160160
`;
161161
162+
exports[`config > no package.json > should allow loading cts config 1`] = `
163+
"<DIR>/index.js chunk │ size: 0.11 kB
164+
165+
"
166+
`;
167+
168+
exports[`config > no package.json > should allow loading mts config 1`] = `
169+
"<DIR>/index.js chunk │ size: 0.11 kB
170+
171+
"
172+
`;
173+
174+
exports[`config > no package.json > should allow loading ts config 1`] = `
175+
"<DIR>/index.js chunk │ size: 0.11 kB
176+
177+
"
178+
`;
179+
162180
exports[`config > no package.json > should allow loading ts config with tsx 1`] = `
163181
"<DIR>/index.js chunk │ size: 0.11 kB
164182

packages/rolldown/tests/cli/cli-e2e.test.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,29 @@ describe('config', () => {
112112
expect(err).not.toBeUndefined()
113113
}
114114
})
115-
it('should throw friendly error message for ts', async () => {
115+
it('should allow loading ts config', async () => {
116116
const cwd = cliFixturesDir('ext-ts')
117-
try {
118-
const _ = await $({ cwd })`rolldown -c rolldown.config.ts`
119-
} catch (err) {
120-
expect(err).toBeInstanceOf(Error)
121-
expect((err as Error).message).toContain('Unsupported config format.')
122-
}
117+
const status = await $({
118+
cwd,
119+
})`rolldown -c rolldown.config.ts`
120+
expect(status.exitCode).toBe(0)
121+
expect(cleanStdout(status.stdout)).toMatchSnapshot()
122+
})
123+
it('should allow loading cts config', async () => {
124+
const cwd = cliFixturesDir('ext-cts')
125+
const status = await $({
126+
cwd,
127+
})`rolldown -c rolldown.config.cts`
128+
expect(status.exitCode).toBe(0)
129+
expect(cleanStdout(status.stdout)).toMatchSnapshot()
130+
})
131+
it('should allow loading mts config', async () => {
132+
const cwd = cliFixturesDir('ext-mts')
133+
const status = await $({
134+
cwd,
135+
})`rolldown -c rolldown.config.mts`
136+
expect(status.exitCode).toBe(0)
137+
expect(cleanStdout(status.stdout)).toMatchSnapshot()
123138
})
124139
it('should allow loading ts config with tsx', async () => {
125140
const cwd = cliFixturesDir('ext-ts')

0 commit comments

Comments
 (0)