Skip to content

Commit

Permalink
feat: support resolveDynamicImport hook (#981)
Browse files Browse the repository at this point in the history
  • Loading branch information
underfin authored Apr 28, 2024
1 parent bd952da commit 37ef30d
Show file tree
Hide file tree
Showing 18 changed files with 180 additions and 15 deletions.
21 changes: 19 additions & 2 deletions crates/rolldown/src/utils/resolve_id.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::path::Path;

use rolldown_common::ModuleType;
use rolldown_common::{ImportKind, ModuleType};
use rolldown_error::BuildResult;
use rolldown_plugin::{HookResolveIdArgs, HookResolveIdExtraOptions, SharedPluginDriver};
use rolldown_plugin::{
HookResolveDynamicImportArgs, HookResolveIdArgs, HookResolveIdExtraOptions, SharedPluginDriver,
};

use crate::{types::resolved_request_info::ResolvedRequestInfo, SharedResolver};

Expand All @@ -24,6 +26,21 @@ pub async fn resolve_id(
_preserve_symlinks: bool,
) -> anyhow::Result<BuildResult<ResolvedRequestInfo>> {
let import_kind = options.kind;
if import_kind == ImportKind::DynamicImport {
if let Some(r) = plugin_driver
.resolve_dynamic_import(&HookResolveDynamicImportArgs {
importer: importer.map(std::convert::AsRef::as_ref),
source: request,
})
.await?
{
return Ok(Ok(ResolvedRequestInfo {
path: r.id.into(),
module_type: ModuleType::Unknown,
is_external: matches!(r.external, Some(true)),
}));
}
}
// Run plugin resolve_id first, if it is None use internal resolver as fallback
if let Some(r) = plugin_driver
.resolve_id(&HookResolveIdArgs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ pub struct BindingPluginOptions {
>,
>,

#[serde(skip_deserializing)]
#[napi(
ts_type = "(specifier: string, importer: Nullable<string>) => MaybePromise<VoidNullable<BindingHookResolveIdOutput>>"
)]
pub resolve_dynamic_import:
Option<MaybeAsyncJsCallback<(String, Option<String>), Option<BindingHookResolveIdOutput>>>,

#[serde(skip_deserializing)]
#[napi(ts_type = "(id: string) => MaybePromise<VoidNullable<BindingHookLoadOutput>>")]
pub load: Option<MaybeAsyncJsCallback<String, Option<BindingHookLoadOutput>>>,
Expand Down
16 changes: 16 additions & 0 deletions crates/rolldown_binding/src/options/plugin/js_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ impl Plugin for JsPlugin {
}
}

async fn resolve_dynamic_import(
&self,
_ctx: &rolldown_plugin::SharedPluginContext,
args: &rolldown_plugin::HookResolveDynamicImportArgs,
) -> rolldown_plugin::HookResolveIdReturn {
if let Some(cb) = &self.resolve_dynamic_import {
Ok(
cb.await_call((args.source.to_string(), args.importer.map(str::to_string)))
.await?
.map(Into::into),
)
} else {
Ok(None)
}
}

async fn load(
&self,
_ctx: &rolldown_plugin::SharedPluginContext,
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown_plugin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub use crate::{
types::hook_render_chunk_args::HookRenderChunkArgs,
types::hook_render_chunk_output::HookRenderChunkOutput,
types::hook_render_error::HookRenderErrorArgs,
types::hook_resolve_dynamic_import_args::HookResolveDynamicImportArgs,
types::hook_resolve_id_args::HookResolveIdArgs,
types::hook_resolve_id_extra_options::HookResolveIdExtraOptions,
types::hook_resolve_id_output::HookResolveIdOutput,
Expand Down
12 changes: 10 additions & 2 deletions crates/rolldown_plugin/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::{any::Any, borrow::Cow, fmt::Debug, sync::Arc};
use super::plugin_context::SharedPluginContext;
use crate::{
types::hook_render_error::HookRenderErrorArgs, HookBuildEndArgs, HookLoadArgs, HookLoadOutput,
HookRenderChunkArgs, HookRenderChunkOutput, HookResolveIdArgs, HookResolveIdOutput,
HookTransformArgs,
HookRenderChunkArgs, HookRenderChunkOutput, HookResolveDynamicImportArgs, HookResolveIdArgs,
HookResolveIdOutput, HookTransformArgs,
};
use anyhow::Result;
use rolldown_common::{ModuleInfo, Output};
Expand Down Expand Up @@ -35,6 +35,14 @@ pub trait Plugin: Any + Debug + Send + Sync + 'static {
Ok(None)
}

async fn resolve_dynamic_import(
&self,
_ctx: &SharedPluginContext,
_args: &HookResolveDynamicImportArgs,
) -> HookResolveIdReturn {
Ok(None)
}

async fn load(&self, _ctx: &SharedPluginContext, _args: &HookLoadArgs) -> HookLoadReturn {
Ok(None)
}
Expand Down
16 changes: 14 additions & 2 deletions crates/rolldown_plugin/src/plugin_driver/build_hooks.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::sync::Arc;

use crate::{
HookBuildEndArgs, HookLoadArgs, HookLoadReturn, HookNoopReturn, HookResolveIdArgs,
HookResolveIdReturn, HookTransformArgs, PluginDriver,
HookBuildEndArgs, HookLoadArgs, HookLoadReturn, HookNoopReturn, HookResolveDynamicImportArgs,
HookResolveIdArgs, HookResolveIdReturn, HookTransformArgs, PluginDriver,
};
use anyhow::Result;
use rolldown_common::ModuleInfo;
Expand Down Expand Up @@ -38,6 +38,18 @@ impl PluginDriver {
Ok(None)
}

pub async fn resolve_dynamic_import(
&self,
args: &HookResolveDynamicImportArgs<'_>,
) -> HookResolveIdReturn {
for (plugin, ctx) in &self.plugins {
if let Some(r) = plugin.resolve_dynamic_import(ctx, args).await? {
return Ok(Some(r));
}
}
Ok(None)
}

pub async fn load(&self, args: &HookLoadArgs<'_>) -> HookLoadReturn {
for (plugin, ctx) in &self.plugins {
if let Some(r) = plugin.load(ctx, args).await? {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[derive(Debug)]
pub struct HookResolveDynamicImportArgs<'a> {
pub importer: Option<&'a str>,
pub source: &'a str,
}
1 change: 1 addition & 0 deletions crates/rolldown_plugin/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod hook_load_output;
pub mod hook_render_chunk_args;
pub mod hook_render_chunk_output;
pub mod hook_render_error;
pub mod hook_resolve_dynamic_import_args;
pub mod hook_resolve_id_args;
pub mod hook_resolve_id_extra_options;
pub mod hook_resolve_id_output;
Expand Down
1 change: 1 addition & 0 deletions packages/rolldown/src/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export interface BindingPluginOptions {
name: string
buildStart?: (ctx: BindingPluginContext) => MaybePromise<VoidNullable>
resolveId?: (specifier: string, importer: Nullable<string>, options: BindingHookResolveIdExtraOptions) => MaybePromise<VoidNullable<BindingHookResolveIdOutput>>
resolveDynamicImport?: (specifier: string, importer: Nullable<string>) => MaybePromise<VoidNullable<BindingHookResolveIdOutput>>
load?: (id: string) => MaybePromise<VoidNullable<BindingHookLoadOutput>>
transform?: (id: string, code: string) => MaybePromise<VoidNullable<BindingHookLoadOutput>>
moduleParsed?: (ctx: BindingPluginContext, module: BindingModuleInfo) => MaybePromise<VoidNullable>
Expand Down
22 changes: 22 additions & 0 deletions packages/rolldown/src/plugin/bindingify-build-hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,28 @@ export function bindingifyResolveId(
}
}

export function bindingifyResolveDynamicImport(
hook?: Plugin['resolveDynamicImport'],
): BindingPluginOptions['resolveDynamicImport'] {
if (!hook) {
return undefined
}
const [handler, _optionsIgnoredSofar] = normalizeHook(hook)

return async (specifier, importer) => {
const ret = await handler.call(null, specifier, importer ?? undefined)
if (ret == false || ret == null) {
return
}
if (typeof ret === 'string') {
return {
id: ret,
}
}
return ret
}
}

export function bindingifyTransform(
hook?: Plugin['transform'],
): BindingPluginOptions['transform'] {
Expand Down
4 changes: 4 additions & 0 deletions packages/rolldown/src/plugin/bindingify-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
bindingifyBuildStart,
bindingifyLoad,
bindingifyModuleParsed,
bindingifyResolveDynamicImport,
bindingifyResolveId,
bindingifyTransform,
} from './bindingify-build-hooks'
Expand All @@ -30,6 +31,9 @@ export function bindingifyPlugin(
name: plugin.name ?? 'unknown',
buildStart: bindingifyBuildStart(options, plugin.buildStart),
resolveId: bindingifyResolveId(plugin.resolveId),
resolveDynamicImport: bindingifyResolveDynamicImport(
plugin.resolveDynamicImport,
),
buildEnd: bindingifyBuildEnd(plugin.buildEnd),
transform: bindingifyTransform(plugin.transform),
moduleParsed: bindingifyModuleParsed(plugin.moduleParsed),
Expand Down
27 changes: 18 additions & 9 deletions packages/rolldown/src/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ export type Hook<Handler extends AnyFn, HookOptions extends AnyObj = AnyObj> =
| FormalHook<Handler, HookOptions>
| Handler

export type ResolveIdResult =
| string
| NullValue
| false
| {
id: string
external?: boolean
}

export interface Plugin {
name?: string

Expand All @@ -42,15 +51,15 @@ export interface Plugin {
source: string,
importer: string | undefined,
extraOptions: BindingHookResolveIdExtraOptions,
) => MaybePromise<
| string
| NullValue
| false
| {
id: string
external?: boolean
}
>
) => MaybePromise<ResolveIdResult>
>

resolveDynamicImport?: Hook<
(
this: null,
source: string,
importer: string | undefined,
) => MaybePromise<ResolveIdResult>
>

load?: Hook<
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineTest } from '@tests'
import { expect, vi } from 'vitest'
import path from 'node:path'

const entry = path.join(__dirname, './main.js')

const resolveDynamicImport = vi.fn()

export default defineTest({
config: {
input: entry,
plugins: [
{
name: 'test-plugin',
resolveDynamicImport: function (id, importer) {
resolveDynamicImport()
if (id === 'foo') {
expect(importer).toStrictEqual(entry)
return {
id: path.join(__dirname, './foo.js'),
}
}
},
},
],
},
afterTest: (output) => {
expect(resolveDynamicImport).toHaveBeenCalledTimes(1)
},
})
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import('foo')
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineTest } from '@tests'
import { expect, vi } from 'vitest'
import path from 'node:path'

const entry = path.join(__dirname, './main.js')

const resolveId = vi.fn()

export default defineTest({
config: {
input: entry,
plugins: [
{
name: 'test-plugin',
resolveId: function (id, importer, options) {
resolveId()
if (id === 'foo') {
expect(importer).toStrictEqual(entry)
return {
id: path.join(__dirname, './foo.js'),
}
}
},
},
],
},
afterTest: () => {
expect(resolveId).toHaveBeenCalledTimes(2)
},
})
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import('foo')

0 comments on commit 37ef30d

Please sign in to comment.