Skip to content

Commit

Permalink
feat: support OutputOptions#externalLiveBindings (#1979)
Browse files Browse the repository at this point in the history
<!-- Thank you for contributing! -->

### Description

<!-- Please insert your description here and provide especially info
about the "what" this PR is solving -->
  • Loading branch information
hyf0 authored Aug 14, 2024
1 parent 6d4ef7e commit cc41c94
Show file tree
Hide file tree
Showing 16 changed files with 115 additions and 5 deletions.
13 changes: 10 additions & 3 deletions crates/rolldown/src/utils/chunk/render_chunk_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,21 @@ pub fn render_chunk_exports(

match export_mode {
Some(OutputExports::Named) => {
format!(
"Object.defineProperty(exports, '{exported_name}', {{
if !options.external_live_bindings
&& export_ref
.is_created_by_import_from_external(&link_output.module_table.modules)
{
format!("exports['{exported_name}'] = {canonical_name}")
} else {
format!(
"Object.defineProperty(exports, '{exported_name}', {{
enumerable: true,
get: function () {{
return {canonical_name};
}}
}});"
)
)
}
}
Some(OutputExports::Default) => {
if matches!(options.format, OutputFormat::Cjs) {
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown/src/utils/normalize_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ pub fn normalize_options(mut raw_options: crate::BundlerOptions) -> NormalizeOpt
inject: raw_options.inject.unwrap_or_default(),
oxc_inject_global_variables_config,
extend: raw_options.extend.unwrap_or(false),
external_live_bindings: raw_options.external_live_bindings.unwrap_or(true),
};

NormalizeOptionsReturn { options: normalized, resolve_options: raw_resolve }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"config": {
"external": ["node:fs"],
"format": "cjs",
"externalLiveBindings": false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
source: crates/rolldown_testing/src/integration_test.rs
---
# Assets

## main.cjs

```js
"use strict";
const { read, readFile, readFileSync, readSync } = __toESM(require("node:fs"));
//#region main.js
const nonExternal = "nonExternal";
//#endregion
Object.defineProperty(exports, 'nonExternal', {
enumerable: true,
get: function () {
return nonExternal;
}
});
exports['read'] = read
exports['readFile'] = readFile
exports['readFileSync'] = readFileSync
exports['readSync'] = readSync
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { read } from 'node:fs'
import { readSync } from 'node:fs'
export { readSync }
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { readFile } from 'node:fs';
import { readFileSync } from 'node:fs';

export { read, readSync } from './indirect'

export { readFileSync };

export const nonExternal = 'nonExternal';
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,10 @@ expression: "snapshot_outputs.join(\"\\n\")"
- main-!~{000}~.mjs => main-91SdaZrf.mjs
- share-!~{002}~.mjs => share-Ak_TT_yj.mjs

# tests/rolldown/function/external_live_bindings

- main-!~{000}~.cjs => main-gvGF4Vpz.cjs

# tests/rolldown/function/format/app/multiple_entry_modules

- cube-!~{002}~.mjs => cube-UC9SFjeM.mjs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub struct BindingOutputOptions {
#[napi(ts_type = "'default' | 'named' | 'none' | 'auto'")]
pub exports: Option<String>,
pub extend: Option<bool>,
// externalLiveBindings: boolean;
pub external_live_bindings: Option<bool>,
// footer: () => string | Promise<string>;
#[derivative(Debug = "ignore")]
#[serde(skip_deserializing)]
Expand Down Expand Up @@ -91,6 +91,7 @@ pub struct BindingOutputOptions {
// strict: boolean;
// systemNullSetters: boolean;
// validate: boolean;

// --- Enhanced options
pub minify: Option<bool>,
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ pub fn normalize_binding_options(
inject: input_options
.inject
.map(|inner| inner.into_iter().map(normalize_binding_inject_import).collect()),
external_live_bindings: output_options.external_live_bindings,
};

#[cfg(not(target_family = "wasm"))]
Expand Down
1 change: 1 addition & 0 deletions crates/rolldown_common/src/inner_bundler_options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ pub struct BundlerOptions {
pub define: Option<FxIndexMap<String, String>>,
pub extend: Option<bool>,
pub inject: Option<Vec<InjectImport>>,
pub external_live_bindings: Option<bool>,
}

#[cfg(feature = "deserialize_bundler_options")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use super::{
};
use crate::{EsModuleFlag, InjectImport, InputItem, ModuleType};

#[allow(clippy::struct_excessive_bools)] // Using raw booleans is more clear in this case
#[derive(Debug)]
pub struct NormalizedBundlerOptions {
// --- Input
Expand Down Expand Up @@ -53,4 +54,5 @@ pub struct NormalizedBundlerOptions {
pub define: Vec<(/* Target to be replaced */ String, /* Replacement */ String)>,
pub inject: Vec<InjectImport>,
pub oxc_inject_global_variables_config: InjectGlobalVariablesConfig,
pub external_live_bindings: bool,
}
41 changes: 40 additions & 1 deletion crates/rolldown_common/src/types/symbol_ref.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use oxc::semantic::SymbolId;
use rustc_hash::FxHashSet;

use crate::ModuleIdx;
use crate::{IndexModules, Module, ModuleIdx, Specifier};

/// Crossing module ref between symbols
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -14,3 +15,41 @@ impl From<(ModuleIdx, SymbolId)> for SymbolRef {
Self { owner: value.0, symbol: value.1 }
}
}

impl SymbolRef {
pub fn is_created_by_import_from_external(&self, modules: &IndexModules) -> bool {
self.inner_is_created_by_import_from_external(modules, &mut FxHashSet::default())
}
fn inner_is_created_by_import_from_external(
self,
modules: &IndexModules,
visited: &mut FxHashSet<SymbolRef>,
) -> bool {
let is_not_inserted_before = visited.insert(self);
if !is_not_inserted_before {
// We are in a cycle
return false;
}

let Module::Ecma(owner) = &modules[self.owner] else { return false };

let Some(named_import) = owner.named_imports.get(&self) else {
return false;
};

let rec = &owner.import_records[named_import.record_id];

match &modules[rec.resolved_module] {
Module::Ecma(ecma) => {
let Specifier::Literal(imported) = &named_import.imported else {
return false;
};
let Some(named_export) = ecma.named_exports.get(imported) else {
return false;
};
named_export.referenced.is_created_by_import_from_external(modules)
}
Module::External(_) => true,
}
}
}
6 changes: 6 additions & 0 deletions crates/rolldown_testing/_config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@
"type": "string"
}
},
"externalLiveBindings": {
"type": [
"boolean",
"null"
]
},
"footer": {
"type": [
"string",
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 @@ -237,6 +237,7 @@ export interface BindingOutputOptions {
esModule?: boolean | 'if-default-prop'
exports?: 'default' | 'named' | 'none' | 'auto'
extend?: boolean
externalLiveBindings?: boolean
footer?: (chunk: RenderedChunk) => MaybePromise<VoidNullable<string>>
format?: 'es' | 'cjs' | 'iife'
globals?: Record<string, string>
Expand Down
1 change: 1 addition & 0 deletions packages/rolldown/src/options/bindingify-output-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function bindingifyOutputOptions(
// TODO(sapphi-red): support parallel plugins
plugins: [],
minify: outputOptions.minify,
externalLiveBindings: outputOptions.externalLiveBindings,
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/rolldown/src/options/output-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const outputOptionsSchema = z.strictObject({
minify: z.boolean().optional(),
name: z.string().optional(),
globals: z.record(z.string()).optional(),
externalLiveBindings: z.boolean().optional(),
})

export const outputCliOptionsSchema = outputOptionsSchema
Expand Down

0 comments on commit cc41c94

Please sign in to comment.