Skip to content

Using transform settings from tsconfig loaded by resolve.tsconfigFilename is quite confusing #5839

@sapphi-red

Description

@sapphi-red

I was expecting that resolve.tsconfigFilename only affects resolution because the option is inside resolve. To make it worse, even when I set the related transform options, those were overridden without any warning, making the investigation difficult...

if let Some(ref experimental_decorator) = ts_config.compiler_options.experimental_decorators {
options.transform_options.decorator.legacy = *experimental_decorator;
}
if let Some(ref experimental_decorator) = ts_config.compiler_options.emit_decorator_metadata {
options.transform_options.decorator.emit_decorator_metadata = *experimental_decorator;
}
// FIXME:
// if user set `transform.typescript.only_remove_type_imports` to false in `rolldown.config.js`, but also set `verbatim_module_syntax` to true in `tsconfig.json`
// We will override the value either, but actually `rolldown.config.js` should have higher priority.
// This due to the type of `only_remove_type_imports` is `bool` we don't know if the `false` is set
// by user or by default value.
if let Some(ref verbatim_module_syntax) = ts_config.compiler_options.verbatim_module_syntax {
options.transform_options.typescript.only_remove_type_imports = *verbatim_module_syntax;
}

My proposal is to:

  • rename resolve.tsconfigFilename to a top-level tsconfig option
  • prefer the value set in the transform / resolve options if both values are set, also emit a warning
  • (not directly related) port the tsconfig & transform option merging code from
    let compiler_options = &tsconfig.compiler_options;
    // when both the normal options and tsconfig is set,
    // we want to prioritize the normal options
    if compiler_options.jsx.as_deref() == Some("preserve")
    && transform_options
    .jsx
    .as_ref()
    .is_none_or(|jsx| matches!(jsx, Either::Right(right) if right.runtime.is_none()))
    {
    transform_options.jsx = Some(Either::Left(String::from("preserve")));
    }
    if !matches!(&transform_options.jsx, Some(Either::Left(left)) if left == "preserve") {
    let mut jsx = if let Some(Either::Right(jsx)) = transform_options.jsx {
    jsx
    } else {
    JsxOptions::default()
    };
    if compiler_options.jsx_factory.is_some() && jsx.pragma.is_none() {
    jsx.pragma.clone_from(&compiler_options.jsx_factory);
    }
    if compiler_options.jsx_import_source.is_some() && jsx.import_source.is_none() {
    jsx.import_source.clone_from(&compiler_options.jsx_import_source);
    }
    if compiler_options.jsx_fragment_factory.is_some() && jsx.pragma_frag.is_none() {
    jsx.pragma_frag.clone_from(&compiler_options.jsx_fragment_factory);
    }
    if jsx.runtime.is_none() {
    match compiler_options.jsx.as_deref() {
    Some("react") => {
    jsx.runtime = Some(String::from("classic"));
    // this option should not be set when using classic runtime
    jsx.import_source = None;
    }
    Some("react-jsx") => {
    jsx.runtime = Some(String::from("automatic"));
    // these options should not be set when using automatic runtime
    jsx.pragma = None;
    jsx.pragma_frag = None;
    }
    Some("react-jsxdev") => jsx.development = Some(true),
    _ => {}
    }
    }
    transform_options.jsx = Some(Either::Right(jsx));
    }
    if transform_options.decorator.as_ref().is_none_or(|decorator| decorator.legacy.is_none()) {
    let mut decorator = transform_options.decorator.unwrap_or_default();
    if compiler_options.experimental_decorators.is_some() {
    decorator.legacy = compiler_options.experimental_decorators;
    }
    if compiler_options.emit_decorator_metadata.is_some() {
    decorator.emit_decorator_metadata = compiler_options.emit_decorator_metadata;
    }
    transform_options.decorator = Some(decorator);
    }
    // | preserveValueImports | importsNotUsedAsValues | verbatimModuleSyntax | onlyRemoveTypeImports |
    // | -------------------- | ---------------------- | -------------------- |---------------------- |
    // | false | remove | false | false |
    // | false | preserve, error | - | - |
    // | true | remove | - | - |
    // | true | preserve, error | true | true |
    let mut typescript = transform_options.typescript.unwrap_or_default();
    typescript.only_remove_type_imports = if compiler_options.verbatim_module_syntax.is_some() {
    compiler_options.verbatim_module_syntax
    } else if compiler_options.preserve_value_imports.is_some()
    || compiler_options.imports_not_used_as_values.is_some()
    {
    let preserve_value_imports = compiler_options.preserve_value_imports.unwrap_or(false);
    let imports_not_used_as_values =
    compiler_options.imports_not_used_as_values.as_deref().unwrap_or("remove");
    if !preserve_value_imports && imports_not_used_as_values == "remove" {
    Some(true)
    } else if preserve_value_imports
    && (imports_not_used_as_values == "preserve" || imports_not_used_as_values == "error")
    {
    Some(false)
    } else {
    // warnings.push(
    // `preserveValueImports=${preserveValueImports} + importsNotUsedAsValues=${importsNotUsedAsValues} is not supported by oxc.` +
    // 'Please migrate to the new verbatimModuleSyntax option.',
    // )
    Some(false)
    }
    } else {
    Some(false)
    };
    transform_options.typescript = Some(typescript);
    let disable_use_define_for_class_fields = !compiler_options
    .use_define_for_class_fields
    .unwrap_or_else(|| is_use_define_for_class_fields(compiler_options.target.as_deref()));
    let mut assumptions = transform_options.assumptions.unwrap_or_default();
    assumptions.set_public_class_fields = Some(disable_use_define_for_class_fields);
    transform_options.assumptions = Some(assumptions);
    to https://github.com/rolldown/rolldown/blob/0078e8415034198cf466b7647f81978f812ab5d9/crates/rolldown/src/bundler_builder.rs, which sets more options

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions