Coder Social home page Coder Social logo

rolldown / rolldown Goto Github PK

View Code? Open in Web Editor NEW
6.9K 42.0 393.0 17.84 MB

Fast Rust bundler for JavaScript with Rollup-compatible API.

Home Page: https://rolldown.rs

License: MIT License

Rust 59.46% JavaScript 29.78% Just 0.31% TypeScript 9.56% HTML 0.04% Vue 0.63% CSS 0.23%

rolldown's Introduction

Rolldown logo

MIT licensed NPM version NPM Unpacked Size (with version) Build Status Code Coverage CodSpeed Badge Discord chat

NPM Unpacked Size darwin-arm64 NPM Unpacked Size darwin-x64 NPM Unpacked Size linux-x64-gnu NPM Unpacked Size win32-x64 NPM Unpacked Size wasm32-wasi

🚧 Work in Progress

Rolldown is currently in active development and not usable for production yet.

Rolldown

Rolldown is a JavaScript bundler written in Rust intended to serve as the future bundler used in Vite. It provides Rollup-compatible APIs and plugin interface, but will be more similar to esbuild in scope.

For more information, please check out the documentation at rolldown.rs.

Contributing

We would love to have more contributors involved!

To get started, please read our Contributing Guide.

Credits

The Rolldown project is heavily inspired by:

And supported by:

  • napi-rs for Node.js add-ons in Rust via Node-API.
  • oxc for the underlying parser, resolver, and sourcemap support.

Licenses

This project is licensed under the MIT License.

This project also partially contains code derived or copied from the following projects:

Licenses of these projects are listed in THIRD-PARTY-LICENSE

rolldown's People

Contributors

adriencaccia avatar boshen avatar brooooooklyn avatar cecil0o0 avatar cunzaizhuyi avatar demivan avatar dunqing avatar erkelost avatar houyunlu avatar hyf0 avatar iwanabethatguy avatar joinmouse avatar kazupon avatar liulinboyi avatar milesj avatar patak-dev avatar pengbouestc avatar renovate[bot] avatar sapphi-red avatar shonya3 avatar suyanhanx avatar sxzz avatar talkor avatar trivikr avatar underfin avatar woai3c avatar yyx990803 avatar zackshen avatar zhusjfaker avatar zscumt123 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rolldown's Issues

render module with source mutation instead of ast mutaition

Description

Module render implementation has two ways.

Module Render With Ast Mutation

Replaced the ast nodes then codegen ast.eg Babel transform, esbuild bundling.

Advantages

The chunk render can be implemented by concat the modules ast, and also chunk minify/format can be implemented by one ast parse. The cold start performance is very fast.

Drawbacks

Do ast mutation causes the ast to be dirty at rebuild, we must parse the code again and cause rebuild performance regression.

Other the developer experience is bad because we need to manually create ast nodes, and the code snip is huge and difficult to review.

Module Render With Source Mutation

Replace the original source using the ast span position. eg Rollup using MagicString, webpack using ReplaceSource.

Drawbacks

The chunk render can be implemented by concat the modules mutation source and the minify/format needs to be parse again.

Rolldown main branch implement

  • Module render with Ast Mutation

Here has walk ast tree twice, one is scan ast to launch module graph, one is walk ast tress to run ast mutation

  • Chunk render with concat modules ast codegen result.

Launch stage

Should be finished at early stage, it will block tree shaking codegen and other things.

Launch Source Mutation Step

Add source Mutation action abstract and scan the mutation actions with walking ast tree once.

runtime module

Runtime Module

Summary

The bundler will attach some runtime code to keep the bundler code running successfully.

Interop Runtime Module

Summary

Because the ecosystem package has lots of commnjs packages. The rollup provider @rollup/plugin-commonjs transforms commonjs to esm, but it is complex becuase commonjs can't static analyzer at some edge cases. So rolldown will treat commonjs as first-class citizens and support it by following esbuild implementation.

The esbuild introduce __toESM __commonJS __toCommonJS __esm for commonjs/esm interop.

__commonJS

If a module is commonjs and it is imported by esm. The module will wrap a __commonJS closure and return a require() function, the require function will return module.exports. See a example

// index.js
import value from './commonjs.js'
value;
// ./commonjs.js
module.exports = 1

It will compiled to

var require_commonjs = __commonJS({
  "./commonjs.js"(exports, module) {
    module.exports = 1;
  }
});

var import_util = __toESM(require_commonjs());
console.log((0, import_util.default)());

__toESM

Converts the module from CommonJS to ESM. The interop esbuild following webpack implementation to compat node.js mode & babel mode.

  • In an ".mjs" file or package.json has "type: module", use node.js compat mode.
  • detect __esModule to compat babel mode.

__toCommonJS

Converts the module from ESM to CommonJS. It will add __esModule to support babel compat mode.

__esm

This is for lazily-initialized ESM code.

How to inject runtime module

Esbuild

The esbuild will generate a runtime file as normal module, collect the usage symbols and link it at module graph, so unused symbols will be deleted at tree shaking.
But the implementation will generate a runtime chunk at multiple entries and it is smaller.

Webpack

The webpack use lots of RuntimeGlobals and sets their dependency information manual. You can see here for detailed at
web-infra-dev/rspack#1049.

Rolldown

Rolldown will fllowing the esbuild implementation because it is simple and clear for runtime architecture.

Implementation Step

  • #16
  • generate runtime file at module graph
  • link runtime symbols

Pass `test_tests__esbuild__default__export_forms_common_js__test_config_json `

generated code

// entry.js
(init_commonjs(), __toCommonJS(commonjs_ns))
(init_c(), __toCommonJS(c_ns))
(init_d(), __toCommonJS(d_ns))
(init_e(), __toCommonJS(e_ns))
(init_f(), __toCommonJS(f_ns))
(init_g(), __toCommonJS(g_ns))
(init_h(), __toCommonJS(h_ns))

error

(init_d(), __toCommonJS(d_ns))
^

TypeError: (init_c(...) , __toCommonJS(...)) is not a function

reason

Js treat

(init_commonjs(), __toCommonJS(commonjs_ns))
(init_c(), __toCommonJS(c_ns))

as

(init_commonjs(), __toCommonJS(commonjs_ns))(init_c(), __toCommonJS(c_ns))

while we want to emit code like

(init_commonjs(), __toCommonJS(commonjs_ns));
(init_c(), __toCommonJS(c_ns));

support commonjs

Summary

As description of #35, we will following esbuild bundle commonjs strategy.

Linker

  • iter the import records of all modules to identify which module should be wrapped.
  • create binding symbol for esm module import cjs module

Module codegen

Wrap module to lazy execute

  • commonjs module
module.exports = 1
// compiled
var require_a = __commonJS({
  "a.js"(exports, module) {
    module.exports = 1;
  }
});
  • the esm module which imported by commonjs
export const a = 1;
// compiled
var a; 
var init_b = __esm({
  "b.js"() {
    a = 1;
  }
});

Commonjs module codegen

  1. wrapped module with __commonJS
  2. rewrite require call to init module expr call
  • require esm module
require('esm');
// compiled
var require_a = __commonJS({
  "a.js"(exports, module) {
     (init_esm(), __toCommonJS(esm_exports));
  }
});
  • require cjs module
require('cjs');
// compiled
var require_a = __commonJS({
 "a.js"(exports, module) {
   require_cjs();
 }
});

Esm module codegen

  • rewrite import cjs module
import cjs from './cjs'
console.log(cjs)
// compiled
var import_cjs = __toESM(require_cjs());
console.log(import_cjs.default)
  • rewrite import wrapped esm module
import esm from './esm'
console.log(esm)
// compiled
init_esm()
console.log(esm)

Wrapped esm module codegen

  • the import declaration is same as esm module
  • wrapped module with __esm
  • hoisted named declaration function
export function a(){}
// compiled
function a(){}
var b_ns = {
   get a () => a
}
var init_b = __esm({
  "b.js"() {
  }
});
  • hoisted default declaration function
export default function a(){}
// compiled
function a(){}
var b_ns = {
   get default () => a
}
var init_b = __esm({
  "b.js"() {
  }
});
  • hoisted top-level symbol definition, including namespace definition
export default  1
export const a = 1
// compiled
var a, b_default;
var b_ns = {
   get default () => b_default,
   get a() => a
}
var init_b = __esm({
  "b.js"() {
      b_default = 1
      a = 1
  }
});

Implemention

  • scan module kind (cjs/esm)
  • scan wrapped module at linker, create symbols for esm module import cjs module, including ast codegen
  • runtime symbol deconflict
  • runtime symbol mark used at linker
  • hoisted function using magic string move api
  • #42
  • add cjs_compat mode option
  • using magic string ident api to ident wrap module function
  • #55
  • reexport with commonjs
  • export * from commonjs at entry
  • #74
  • #86

support mix esm and cjs module

If esm module mixed cjs require syntax, it can worked becuase treat it as esm.

  • Using mix esm import and cjs require
  • Using mix esm export and cjs require

So here only has cjs exports syntax will be as cjs module.

If cjs module mixed esm syntax

`test_tests__esbuild__default__common_js_from_es6__test_config_json` has different compiled output compared to esbuild

https://github.com/evanw/esbuild/blob/4e11b50fe3178ed0a78c077df78788d66304d379/internal/bundler_tests/bundler_default_test.go#L110-L135

rolldown

import { __esm, __toCommonJS } from "./_rolldown_runtime.mjs";

// bar.js
function bar$1() {
	return 'bar'
}
var bar_ns = {
  get bar() { return bar$1 }
};
var init_bar = __esm({
'bar.js'() {

}
});
// foo.js
function foo$1() {
	return 'foo'
}
var foo_ns = {
  get foo() { return foo$1 }
};
var init_foo = __esm({
'foo.js'() {

}
});
// entry.js
const {foo} = (init_foo(), __toCommonJS(foo_ns))
console.log(foo(), bar())
const {bar} = (init_bar(), __toCommonJS(bar_ns)) // This should not be hoisted

esbuild

https://github.com/evanw/esbuild/blob/4e11b50fe3178ed0a78c077df78788d66304d379/internal/bundler_tests/snapshots/snapshots_default.txt#L802-L834

// foo.js
var foo_exports = {};
__export(foo_exports, {
  foo: () => foo
});
function foo() {
  return "foo";
}
var init_foo = __esm({
  "foo.js"() {
  }
});

// bar.js
var bar_exports = {};
__export(bar_exports, {
  bar: () => bar
});
function bar() {
  return "bar";
}
var init_bar = __esm({
  "bar.js"() {
  }
});

// entry.js
var { foo: foo2 } = (init_foo(), __toCommonJS(foo_exports));
console.log(foo2(), bar2());
var { bar: bar2 } = (init_bar(), __toCommonJS(bar_exports));

problems need to investigate

  • different orders
  • different output for the same module

Setup warm cache for CI to speed it up

See how it's being configured and used in oxc:

The main idea is that the cache is only saved on the main branch, and PR branches only pull the cache but never save them. This setup will always hand us a warm cache everywhere due to the 10G cache constraint. You may find the cache storage limit page helpful https://github.com/web-infra-dev/oxc/actions/caches

[Tracking]: code splitting

The issue is to tracking todos about code splitting for milestone 1

Correctness

  • Prevent exports of entry chunk/module being renamed

This won't happens.

Enhancements

Refactor

Misc

basic code splitting

Summary

The following will trigger code splitting.

  • entries
  • shared module
  • dynamic import
  • manual chunks option

Split Modules to chunk by entries

  1. Mark an entry that has a corresponding bit flag.
  2. Mark the entry bit flag of the module by iter entries. The entry bits flag of the module may be imported by multiple entries bit flag.
  3. Generate chunks for entries, and add the corresponding bit flag to the chunk.
  4. Iter modules to compare the entry bit flag of the module with the entry bit flag of already generated chunks.
  • If a chunk has an equal entry bit flag of the module and connect the module with the chunk.
  • Else generate a new chunk for the entry bit flag of the module and connect the module with the chunk.

Cross chunks symbol imports and exports

Dynamic import module generate a chunk

Scanned the dynamic import module added to entries.

Step implementation

  • split Modules to chunk by entries
  • cross chunks symbol imports and exports
  • dynamic import as entries
  • manual chunk object
  • manual chunk function

Milestone 1: replace esbuild with rolldown in vite

POC 1

POC 2

  • support format: cjs
  • node binding
    • plugin & plugin driver
  • hash support
  • complete supporting for  external module
  • chunk hash

  • module graph
  • esm syntax ast scan & linking imports/exports
  • module render with ast mutaition and ast codegen (symbol deconflict)
  • chunk render with concat module ast codegen result
  • #17
  • glob import
  • ci release build
  • simple benchmark
  • more esbuild/rollup test migrate
  • options

How to apply __esmMin or __esm runtime symbol

Esbuil provide __esmMin which using production and __esm which using development, it is controlled by MinifyIdentifiers option. But it is need discuss how to control at rolldown. Here has two ways to control it.

  • Add similar option to do it, maybe MinifyIdentifiers
  • Add mode which value be production or development

Should not generate namespace binding for `ExportsKind::CommonJs`

Founded in #126. See https://github.com/rolldown-rs/rolldown/blob/c18181625c3aae63bf2fe7bda0dd66ff33a283d5/crates/rolldown/tests/esbuild/splitting/shared-commonjs-into-es6/artifacts.snap for example.

Some modules try to import the namespace binding of a cjs module, which is not exist. This make test_tests__esbuild__splitting__shared_commonjs_into_es6__test_config_json failed. To make the test works, #126 make the cjs module generate namespace binding.

This is a temp workaround. I will investigate why some modules try to import the namespace binding of a cjs module.

Fix tests that are executed failed

Those tests are failed to be executed, founded in #119.

  • test_tests__esbuild__default__arrow_fn_scope__test_config_json
  • #135
  • test_tests__esbuild__default__const_with_let__test_config_json
  • #136
  • test_tests__esbuild__default__forbid_const_assign_when_bundling__test_config_json
  • test_tests__esbuild__default__forbid_const_assign_when_lowering_using__test_config_json
  • test_tests__esbuild__default__import_missing_common_js__test_config_json
  • test_tests__esbuild__default__import_then_catch__test_config_json
  • test_tests__esbuild__default__nested_require_without_call__test_config_json
  • test_tests__esbuild__default__preserve_key_comment__test_config_json
  • test_tests__esbuild__default__require_property_access_common_js__test_config_json
  • test_tests__esbuild__default__require_resolve__test_config_json
  • test_tests__esbuild__default__require_without_call__test_config_json
  • test_tests__esbuild__default__this_inside_function__test_config_json
  • test_tests__esbuild__default__var_relocating_bundle__test_config_json
  • test_tests__esbuild__import_star__import_namespace_undefined_property_side_effect_free_file__test_config_json
  • test_tests__esbuild__import_star__import_of_export_star__test_config_json
  • test_tests__esbuild__import_star__import_of_export_star_of_import__test_config_json
  • test_tests__esbuild__splitting__shared_commonjs_into_es6__test_config_json

Enhancements

Not bug.

  • test_tests__esbuild__default__dynamic_import_with_template_iife__test_config_json

[Tracking]: prettify the build output

"prettify the build output" is not the primary goal currently, the issue is only for recording.

  • #440
  • Empty wrapper should be in a single line
  • Merge import specifiers that import from the same module
  • remove empty statements
  • Remove meaningless lines of modules.

symbol deconflicting for nested scopes

Vite PreBundling used esbuild api

Scan Deps

  • esbuild.context
context.cancel()
context.rebuild()
context.dispose()
  • esbuild plugin
 build.onLoad({filter, namespace}, ({path , importer, pluginData}) => {}) => { loader, contents, pluginData} // using loader ts
 build.onResolve({filter, namespace}, ({path , importer})  => {} ) => { path, namespace, external }

Bundling Deps

  • esbuild.context
context.cancel()
context.rebuild(({metafile}) => {}) // metafile.outputs
context.dispose() 
  • esbuild plugin
 build.onEnd()
 build.onResolve({filter, namespace}, ({ path , importer, kind}) => {} ) => { path, namespace, external }
  • define
  • platform 'node' : 'browser'
  • banner
  • tsconfig
  • metafile
  • sourcemap
  • logLevel
  • outdir
  • supported dynamic-import. import-meta

Rolldown task

  • plugin end
  • esbuild plugin adapter or just rewrite it to rollup plugin

Need to discuss should implement the following api and how to do it at rolldown.

  • esbuild.context
  • banner
  • define
  • platform
  • tsconfig
  • metafile
  • plugin pluginData
  • esbuild loader ts(astro files using)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.