Skip to content

Commit

Permalink
feat: add resolve option (#392)
Browse files Browse the repository at this point in the history
  • Loading branch information
underfin committed Dec 4, 2023
1 parent de6c324 commit cd52449
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 34 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 11 additions & 2 deletions crates/rolldown/src/bundler/bundler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,19 @@ impl Bundler<OsFileSystem> {
}

impl<T: FileSystem + Default + 'static> Bundler<T> {
pub fn with_plugins_and_fs(input_options: InputOptions, plugins: Vec<BoxPlugin>, fs: T) -> Self {
pub fn with_plugins_and_fs(
mut input_options: InputOptions,
plugins: Vec<BoxPlugin>,
fs: T,
) -> Self {
rolldown_tracing::try_init_tracing();
Self {
resolver: Resolver::with_cwd_and_fs(input_options.cwd.clone(), false, fs.share()).into(),
resolver: Resolver::with_cwd_and_fs(
input_options.cwd.clone(),
std::mem::take(&mut input_options.resolve),
fs.share(),
)
.into(),
plugin_driver: Arc::new(PluginDriver::new(plugins)),
input_options: Arc::new(input_options),
fs,
Expand Down
2 changes: 2 additions & 0 deletions crates/rolldown/src/bundler/options/input_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub struct InputOptions {
pub cwd: PathBuf,
pub external: External,
pub treeshake: bool,
pub resolve: Option<rolldown_resolver::ResolverOptions>,
}

impl Default for InputOptions {
Expand All @@ -80,6 +81,7 @@ impl Default for InputOptions {
cwd: std::env::current_dir().unwrap(),
external: External::default(),
treeshake: true,
resolve: None,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown/tests/common/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl Fixture {
cwd: fixture_path.to_path_buf(),
external: test_config.input.external.map(|e| External::ArrayString(e)).unwrap_or_default(),
treeshake: test_config.input.treeshake.unwrap_or(true),
resolve: None,
});

if fixture_path.join("dist").is_dir() {
Expand Down
27 changes: 14 additions & 13 deletions crates/rolldown_binding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ workspace = true
crate-type = ["cdylib"]

[dependencies]
async-trait = { workspace = true }
derivative = { workspace = true }
futures = { workspace = true }
napi = { version = "2.11.1", features = ["full"] }
napi-derive = { version = "2.11.0" }
rolldown = { path = "../rolldown" }
rolldown_error = { path = "../rolldown_error" }
rolldown_tracing = { path = "../rolldown_tracing" }
rolldown_fs = { path = "../rolldown_fs" }
rustc-hash = { workspace = true }
scoped-tls = { workspace = true }
serde = { workspace = true }
tracing = { workspace = true }
async-trait = { workspace = true }
derivative = { workspace = true }
futures = { workspace = true }
napi = { version = "2.11.1", features = ["full"] }
napi-derive = { version = "2.11.0" }
rolldown = { path = "../rolldown" }
rolldown_error = { path = "../rolldown_error" }
rolldown_tracing = { path = "../rolldown_tracing" }
rolldown_fs = { path = "../rolldown_fs" }
rolldown_resolver = { path = "../rolldown_resolver" }
rustc-hash = { workspace = true }
scoped-tls = { workspace = true }
serde = { workspace = true }
tracing = { workspace = true }

[target.'cfg(not(target_os = "linux"))'.dependencies]
mimalloc-rust = "0.2"
Expand Down
12 changes: 12 additions & 0 deletions crates/rolldown_binding/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,22 @@ export interface InputItem {
name?: string
import: string
}
export interface ResolveOptions {
alias?: Record<string, Array<string>>
aliasFields?: Array<Array<string>>
conditionNames?: Array<string>
exportsFields?: Array<Array<string>>
extensions?: Array<string>
mainFields?: Array<string>
mainFiles?: Array<string>
modules?: Array<string>
symlinks?: boolean
}
export interface InputOptions {
external?: (source: string, importer?: string, isResolved: boolean) => boolean
input: Array<InputItem>
plugins: Array<PluginOptions>
resolve?: ResolveOptions
cwd: string
}
export interface OutputOptions {
Expand Down
37 changes: 36 additions & 1 deletion crates/rolldown_binding/src/options/input_options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{fmt::Debug, path::PathBuf};
use std::{collections::HashMap, fmt::Debug, path::PathBuf};
mod plugin;
mod plugin_adapter;
use crate::utils::{napi_error_ext::NapiErrorExt, JsCallback};
Expand Down Expand Up @@ -28,6 +28,39 @@ impl From<InputItem> for rolldown::InputItem {

pub type ExternalFn = JsCallback<(String, Option<String>, bool), bool>;

#[napi(object)]
#[derive(Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct ResolveOptions {
pub alias: Option<HashMap<String, Vec<String>>>,
pub alias_fields: Option<Vec<Vec<String>>>,
pub condition_names: Option<Vec<String>>,
pub exports_fields: Option<Vec<Vec<String>>>,
pub extensions: Option<Vec<String>>,
pub main_fields: Option<Vec<String>>,
pub main_files: Option<Vec<String>>,
pub modules: Option<Vec<String>>,
pub symlinks: Option<bool>,
}

impl From<ResolveOptions> for rolldown_resolver::ResolverOptions {
fn from(value: ResolveOptions) -> Self {
Self {
alias: value
.alias
.map(|alias| alias.into_iter().map(|(key, value)| (key, value)).collect::<Vec<_>>()),
alias_fields: value.alias_fields,
condition_names: value.condition_names,
exports_fields: value.exports_fields,
extensions: value.extensions,
main_fields: value.main_fields,
main_files: value.main_files,
modules: value.modules,
symlinks: value.symlinks,
}
}
}

#[napi(object)]
#[derive(Deserialize, Default, Derivative)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -57,6 +90,7 @@ pub struct InputOptions {
// onwarn?: WarningHandlerWithDefault;
// perf?: boolean;
pub plugins: Vec<PluginOptions>,
pub resolve: Option<ResolveOptions>,
// preserveEntrySignatures?: PreserveEntrySignaturesOption;
// /** @deprecated Use the "preserveModules" output option instead. */
// preserveModules?: boolean;
Expand Down Expand Up @@ -105,6 +139,7 @@ impl From<InputOptions>
cwd,
external,
treeshake: false,
resolve: value.resolve.map(Into::into),
}),
value.plugins.into_iter().map(JsAdapterPlugin::new_boxed).collect::<napi::Result<Vec<_>>>(),
)
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown_binding_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub fn bundle(file_list: Vec<FileItem>) -> Vec<AssetItem> {
cwd: "/".into(),
external: External::ArrayString(vec![]),
treeshake: true,
resolve: None,
},
vec![],
memory_fs,
Expand Down
2 changes: 2 additions & 0 deletions crates/rolldown_resolver/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod resolver;
mod resolver_options;

pub use crate::resolver::{ResolveRet, Resolver};
pub use crate::resolver_options::ResolverOptions;
21 changes: 6 additions & 15 deletions crates/rolldown_resolver/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use rolldown_fs::FileSystem;
use std::path::PathBuf;
use sugar_path::{AsPath, SugarPathBuf};

use oxc_resolver::{Resolution, ResolveOptions, ResolverGeneric};
use oxc_resolver::{Resolution, ResolverGeneric};

use crate::ResolverOptions;

#[derive(Debug)]
pub struct Resolver<T: FileSystem + Default> {
Expand All @@ -13,20 +15,9 @@ pub struct Resolver<T: FileSystem + Default> {
}

impl<F: FileSystem + Default> Resolver<F> {
pub fn with_cwd_and_fs(cwd: PathBuf, preserve_symlinks: bool, fs: F) -> Self {
let resolve_options = ResolveOptions {
symlinks: !preserve_symlinks,
extensions: vec![
".js".to_string(),
".jsx".to_string(),
".ts".to_string(),
".tsx".to_string(),
],
prefer_relative: false,
..Default::default()
};

let inner_resolver = ResolverGeneric::new_with_file_system(fs, resolve_options);
pub fn with_cwd_and_fs(cwd: PathBuf, resolver_options: Option<ResolverOptions>, fs: F) -> Self {
let option = resolver_options.map_or_else(oxc_resolver::ResolveOptions::default, Into::into);
let inner_resolver = ResolverGeneric::new_with_file_system(fs, option);
Self { cwd, inner: inner_resolver }
}

Expand Down
92 changes: 92 additions & 0 deletions crates/rolldown_resolver/src/resolver_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#[derive(Debug)]
pub struct ResolverOptions {
/// Create aliases to import or require certain modules more easily.
/// A trailing $ can also be added to the given object's keys to signify an exact match.
pub alias: Option<Vec<(String, Vec<String>)>>,

/// A list of alias fields in description files.
/// Specify a field, such as `browser`, to be parsed according to [this specification](https://github.com/defunctzombie/package-browser-field-spec).
/// Can be a path to json object such as `["path", "to", "exports"]`.
///
/// Default `[]`
pub alias_fields: Option<Vec<Vec<String>>>,

/// Condition names for exports field which defines entry points of a package.
/// The key order in the exports field is significant. During condition matching, earlier entries have higher priority and take precedence over later entries.
///
/// Default `[]`
pub condition_names: Option<Vec<String>>,

/// A list of exports fields in description files.
/// Can be a path to json object such as `["path", "to", "exports"]`.
///
/// Default `[["exports"]]`.
pub exports_fields: Option<Vec<Vec<String>>>,
/// Attempt to resolve these extensions in order.
/// If multiple files share the same name but have different extensions,
/// will resolve the one with the extension listed first in the array and skip the rest.
///
/// Default `[".js", ".json", ".node"]`
pub extensions: Option<Vec<String>>,

/// A list of main fields in description files
///
/// Default `["main"]`.
pub main_fields: Option<Vec<String>>,

/// The filename to be used while resolving directories.
///
/// Default `["index"]`
pub main_files: Option<Vec<String>>,

/// A list of directories to resolve modules from, can be absolute path or folder name.
///
/// Default `["node_modules"]`
pub modules: Option<Vec<String>>,
/// Whether to resolve symlinks to their symlinked location.
/// When enabled, symlinked resources are resolved to their real path, not their symlinked location.
/// Note that this may cause module resolution to fail when using tools that symlink packages (like npm link).
///
/// Default `true`
pub symlinks: Option<bool>,
}

impl From<ResolverOptions> for oxc_resolver::ResolveOptions {
fn from(value: ResolverOptions) -> Self {
Self {
alias: value
.alias
.map(|alias| {
alias
.into_iter()
.map(|(key, value)| {
(key, value.into_iter().map(oxc_resolver::AliasValue::Path).collect::<Vec<_>>())
})
.collect::<Vec<_>>()
})
.unwrap_or_default(),
alias_fields: value.alias_fields.unwrap_or_default(),
condition_names: value.condition_names.unwrap_or_default(),
exports_fields: value.exports_fields.unwrap_or_else(|| vec![vec!["exports".into()]]),
extensions: value
.extensions
.unwrap_or_else(|| vec![".js".into(), ".json".into(), ".node".into()]),
main_fields: value.main_fields.unwrap_or_else(|| vec!["main".into()]),
main_files: value.main_files.unwrap_or_else(|| vec!["index".into()]),
modules: value.modules.unwrap_or_else(|| vec!["node_modules".into()]),
symlinks: value.symlinks.unwrap_or(true),
tsconfig: None,
description_files: vec!["package.json".into()],
enforce_extension: oxc_resolver::EnforceExtension::Auto,
extension_alias: vec![],
fallback: vec![],
fully_specified: false,
resolve_to_context: false,
prefer_relative: false,
prefer_absolute: false,
restrictions: vec![],
roots: vec![],
builtin_modules: false,
}
}
}
5 changes: 3 additions & 2 deletions packages/node/src/options/input-options-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { NormalizedInputOptions } from '../rollup-types'
import { InputOptions as BindingInputOptions } from '@rolldown/node-binding'
import path from 'path'
import { createBuildPluginAdapter } from './create-build-plugin-adapter'
import { InputOptions } from './input-options'
import { InputOptions, RolldownNormalizedInputOptions } from './input-options'

export function createInputOptionsAdapter(
options: NormalizedInputOptions,
options: RolldownNormalizedInputOptions,
inputOptions: InputOptions,
): BindingInputOptions {
return {
Expand All @@ -15,6 +15,7 @@ export function createInputOptionsAdapter(
),
cwd: process.cwd(),
external: inputOptions.external ? options.external : undefined,
resolve: options.resolve,
}
}

Expand Down
28 changes: 27 additions & 1 deletion packages/node/src/options/input-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,34 @@ import {
Plugin,
} from '../rollup-types'
import { ensureArray, normalizePluginOption } from '../utils'
import { ResolveOptions } from '@rolldown/node-binding'

// TODO export compat plugin type
export type RolldownPlugin = Plugin
export interface InputOptions {
input?: RollupInputOptions['input']
plugins?: RolldownPlugin[]
external?: RollupInputOptions['external']
resolve?: RolldownResolveOptions
}

export type RolldownResolveOptions = Omit<ResolveOptions, 'alias'> & {
alias: Record<string, string>
}

export type RolldownNormalizedInputOptions = NormalizedInputOptions & {
resolve?: ResolveOptions
}

export async function normalizeInputOptions(
config: InputOptions,
): Promise<NormalizedInputOptions> {
): Promise<RolldownNormalizedInputOptions> {
// @ts-expect-error
return {
input: getInput(config),
plugins: await normalizePluginOption(config.plugins),
external: getIdMatcher(config.external),
resolve: getResolve(config.resolve),
}
}

Expand Down Expand Up @@ -65,3 +76,18 @@ const getIdMatcher = <T extends Array<any>>(
// Rollup here convert `undefined` to function, it is bad for performance. So it will convert to `undefined` at adapter.
return () => false
}

function getResolve(
resolve?: RolldownResolveOptions,
): RolldownNormalizedInputOptions['resolve'] {
if (resolve) {
return {
...resolve,
alias: resolve.alias
? Object.fromEntries(
Object.entries(resolve.alias).map(([key, value]) => [key, [value]]),
)
: undefined,
}
}
}

0 comments on commit cd52449

Please sign in to comment.