-
-
Notifications
You must be signed in to change notification settings - Fork 59
feat: support more param for build command #1258
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,6 @@ module.exports = { | |
logger: { | ||
warn: () => {}, | ||
override: () => {}, | ||
debug: () => {}, | ||
}, | ||
}; |
This file was deleted.
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,162 @@ | ||||||||
import path from 'node:path'; | ||||||||
import util from 'node:util'; | ||||||||
import { loadEnv, type RsbuildEntry } from '@rsbuild/core'; | ||||||||
import { loadConfig } from '../config'; | ||||||||
import type { | ||||||||
EcmaScriptVersion, | ||||||||
RsbuildConfigOutputTarget, | ||||||||
RslibConfig, | ||||||||
Syntax, | ||||||||
} from '../types'; | ||||||||
import { getAbsolutePath } from '../utils/helper'; | ||||||||
import { logger } from '../utils/logger'; | ||||||||
import type { CommonOptions } from './commands'; | ||||||||
import { onBeforeRestart } from './restart'; | ||||||||
|
||||||||
const getEnvDir = (cwd: string, envDir?: string) => { | ||||||||
if (envDir) { | ||||||||
return path.isAbsolute(envDir) ? envDir : path.resolve(cwd, envDir); | ||||||||
} | ||||||||
return cwd; | ||||||||
}; | ||||||||
|
||||||||
export const parseEntryOption = ( | ||||||||
entries?: string[], | ||||||||
): Record<string, string> | undefined => { | ||||||||
if (!entries || entries.length === 0) { | ||||||||
return undefined; | ||||||||
} | ||||||||
|
||||||||
const parsed: Record<string, string> = {}; | ||||||||
let unnamedIndex = 0; | ||||||||
|
||||||||
for (const rawEntry of entries) { | ||||||||
const value = rawEntry?.trim(); | ||||||||
if (!value) { | ||||||||
continue; | ||||||||
} | ||||||||
|
||||||||
const equalIndex = value.indexOf('='); | ||||||||
if (equalIndex > -1) { | ||||||||
const name = value.slice(0, equalIndex).trim(); | ||||||||
const entryPath = value.slice(equalIndex + 1).trim(); | ||||||||
if (name && entryPath) { | ||||||||
parsed[name] = entryPath; | ||||||||
continue; | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
unnamedIndex += 1; | ||||||||
const key = unnamedIndex === 1 ? 'index' : `entry${unnamedIndex}`; | ||||||||
parsed[key] = value; | ||||||||
} | ||||||||
|
||||||||
return Object.keys(parsed).length === 0 ? undefined : parsed; | ||||||||
}; | ||||||||
|
||||||||
export const parseSyntaxOption = (syntax?: string): Syntax | undefined => { | ||||||||
if (!syntax) { | ||||||||
return undefined; | ||||||||
} | ||||||||
|
||||||||
const trimmed = syntax.trim(); | ||||||||
if (!trimmed) { | ||||||||
return undefined; | ||||||||
} | ||||||||
|
||||||||
if (trimmed.startsWith('[')) { | ||||||||
try { | ||||||||
const parsed = JSON.parse(trimmed); | ||||||||
if (Array.isArray(parsed)) { | ||||||||
return parsed; | ||||||||
} | ||||||||
} catch (e) { | ||||||||
const reason = e instanceof Error ? e.message : String(e); | ||||||||
throw new Error( | ||||||||
`Failed to parse --syntax option "${trimmed}" as JSON array: ${reason}`, | ||||||||
); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
return trimmed as EcmaScriptVersion; | ||||||||
}; | ||||||||
|
||||||||
const applyCliOptions = ( | ||||||||
config: RslibConfig, | ||||||||
options: CommonOptions, | ||||||||
root: string, | ||||||||
): void => { | ||||||||
if (options.root) config.root = root; | ||||||||
if (options.logLevel) config.logLevel = options.logLevel; | ||||||||
|
||||||||
for (const lib of config.lib) { | ||||||||
if (options.format !== undefined) lib.format = options.format; | ||||||||
if (options.bundle !== undefined) lib.bundle = options.bundle; | ||||||||
if (options.dts !== undefined) lib.dts = options.dts; | ||||||||
if (options.autoExtension !== undefined) | ||||||||
lib.autoExtension = options.autoExtension; | ||||||||
if (options.autoExternal !== undefined) | ||||||||
lib.autoExternal = options.autoExternal; | ||||||||
if (options.tsconfig !== undefined) { | ||||||||
lib.source ||= {}; | ||||||||
lib.source.tsconfigPath = options.tsconfig; | ||||||||
} | ||||||||
const entry = parseEntryOption(options.entry); | ||||||||
if (entry !== undefined) { | ||||||||
lib.source ||= {}; | ||||||||
lib.source.entry = entry as RsbuildEntry; | ||||||||
} | ||||||||
const syntax = parseSyntaxOption(options.syntax); | ||||||||
if (syntax !== undefined) lib.syntax = syntax; | ||||||||
const output = lib.output ?? {}; | ||||||||
if (options.target !== undefined) | ||||||||
output.target = options.target as RsbuildConfigOutputTarget; | ||||||||
if (options.minify !== undefined) output.minify = options.minify; | ||||||||
if (options.clean !== undefined) output.cleanDistPath = options.clean; | ||||||||
const externals = options.external?.filter(Boolean) ?? []; | ||||||||
if (externals.length > 0) output.externals = externals; | ||||||||
if (options.distPath) { | ||||||||
output.distPath ??= {}; | ||||||||
output.distPath.root = options.distPath; | ||||||||
} | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assignment to
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||
} | ||||||||
}; | ||||||||
|
||||||||
export async function initConfig(options: CommonOptions): Promise<{ | ||||||||
config: RslibConfig; | ||||||||
configFilePath: string; | ||||||||
watchFiles: string[]; | ||||||||
}> { | ||||||||
const cwd = process.cwd(); | ||||||||
const root = options.root ? getAbsolutePath(cwd, options.root) : cwd; | ||||||||
const envs = loadEnv({ | ||||||||
cwd: getEnvDir(root, options.envDir), | ||||||||
mode: options.envMode, | ||||||||
}); | ||||||||
|
||||||||
onBeforeRestart(envs.cleanup); | ||||||||
|
||||||||
const { content: config, filePath: configFilePath } = await loadConfig({ | ||||||||
cwd: root, | ||||||||
path: options.config, | ||||||||
envMode: options.envMode, | ||||||||
loader: options.configLoader, | ||||||||
}); | ||||||||
|
||||||||
config.source ||= {}; | ||||||||
config.source.define = { | ||||||||
...envs.publicVars, | ||||||||
...config.source.define, | ||||||||
}; | ||||||||
|
||||||||
applyCliOptions(config, options, root); | ||||||||
|
||||||||
logger.debug('Rslib config used to generate Rsbuild environments:'); | ||||||||
logger.debug(`\n${util.inspect(config, { depth: null, colors: true })}`); | ||||||||
|
||||||||
return { | ||||||||
config, | ||||||||
configFilePath, | ||||||||
watchFiles: [configFilePath, ...envs.filePaths], | ||||||||
}; | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { describe, expect, test } from '@rstest/core'; | ||
import { parseEntryOption, parseSyntaxOption } from '../src/cli/initConfig'; | ||
|
||
describe('parseEntryOption', () => { | ||
test('returns undefined when entries are missing or empty', () => { | ||
expect(parseEntryOption()).toBeUndefined(); | ||
expect(parseEntryOption([])).toBeUndefined(); | ||
expect(parseEntryOption(['', ' '])).toBeUndefined(); | ||
}); | ||
|
||
test('parses named and positional entries with trimming', () => { | ||
const result = parseEntryOption([ | ||
' main = ./src/main.ts ', | ||
' ./src/utils.ts ', | ||
'entry=./src/entry.ts', | ||
'./src/extra.ts', | ||
]); | ||
|
||
expect(result).toEqual({ | ||
main: './src/main.ts', | ||
index: './src/utils.ts', | ||
entry: './src/entry.ts', | ||
entry2: './src/extra.ts', | ||
}); | ||
}); | ||
}); | ||
|
||
describe('parseSyntaxOption', () => { | ||
test('returns undefined for missing or whitespace values', () => { | ||
expect(parseSyntaxOption()).toBeUndefined(); | ||
expect(parseSyntaxOption('')).toBeUndefined(); | ||
expect(parseSyntaxOption(' ')).toBeUndefined(); | ||
}); | ||
|
||
test('returns the trimmed ECMAScript version when not a JSON array', () => { | ||
expect(parseSyntaxOption(' es2020 ')).toBe('es2020'); | ||
}); | ||
|
||
test('parses JSON array syntax', () => { | ||
expect(parseSyntaxOption('["chrome 120", "firefox 115"]')).toEqual([ | ||
'chrome 120', | ||
'firefox 115', | ||
]); | ||
}); | ||
|
||
test('throws descriptive error when JSON parsing fails', () => { | ||
const parseInvalidSyntax = () => parseSyntaxOption('[invalid'); | ||
expect(parseInvalidSyntax).toThrowError( | ||
/Failed to parse --syntax option "\[inv.*JSON array/, | ||
); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assignment of
options.autoExtension
tolib.autoExtension
should maintain type consistency. Based on the test snapshot showingautoExtension: 'false'
as a string, consider ensuring boolean values are properly handled or document the expected type behavior.Copilot uses AI. Check for mistakes.