Prevously in rolldown/rolldown#10693, I was thinking unifying __share and __esm into the same thing, because:
__share and __esm both "wrap" the code
- They both delay the execuion timing to call-time
Reasoning with these things, it seems intuitive to think of __share and __esm are similar thing with minor differences.
Also with thinking #5, I initially think wrappers in wrappers(see below per chunk example) output just doesn't feel right. It makes the execution model more complicated, which isn't a good signal.
With __esm wrappers in __share wrappers, you need to think that before calling some initXXX, you should make sure calling __share_require(...) to execute the body code of __share so those __esm calls get executed.
But then I found that isn't it intuitive you need to execute the "chunk" code if your code relies on the chunk. So it does make the execution model more complicated than the current one describled in #5. But it's sound.
Conclusion
- The current direction is inlining "chunks" not "modules" in the chunks. So the output will look like
// Per-chunk entry-b.js in the below example
strictExecutionOrder would be enabled/required for using inlineCommonChunks
- Current direction gives the more optimal output
- Inside of
__share wrapper, modules are still scoped hoisted. This can't be done(or much limited) compared to module level approach
- With
strictExecutionOrder: true, multiple __share wrappers can always merge to one __share wrapper
Example
Input
// shared.js
globalThis.order ??= []
globalThis.order.push('shared')
export function mark(name) {
globalThis.order.push(name)
}
// config.js
import { mark } from './shared.js'
mark('config')
// client.js
import './config.js'
import { mark } from './shared.js'
mark('client')
// locale.js
import { mark } from './shared.js'
mark('locale')
// ui.js
import './locale.js'
import { mark } from './shared.js'
mark('ui')
// entry-a.js
import './config.js'
import './client.js'
console.log(globalThis.order.join(','))
// shared,config,client
// entry-b.js
import './config.js'
import './locale.js'
import './client.js'
import './ui.js'
console.log(globalThis.order.join(','))
// shared,config,locale,client,ui
// entry-c.js
import './locale.js'
import './ui.js'
console.log(globalThis.order.join(','))
// shared,locale,ui
COMMON-ABC.js = [shared.js]
COMMON-AB.js = [config.js, client.js]
COMMON-BC.js = [locale.js, ui.js]
entry-a.js -> COMMON-AB.js -> COMMON-ABC.js
entry-b.js -> COMMON-AB.js -> COMMON-ABC.js
-> COMMON-BC.js -> COMMON-ABC.js
entry-c.js -> COMMON-BC.js -> COMMON-ABC.js
// COMMON-ABC.js
function mark(name) {
globalThis.order.push(name)
}
const init_shared = __esmMin(() => {
globalThis.order ??= []
globalThis.order.push('shared')
})
export {
init_shared,
mark,
}
// Per-module entry-b.js
import {
init_shared,
mark,
} from './COMMON-ABC.js'
__share('config.js', module => {
const init_config = __esmMin(() => {
init_shared()
mark('config')
})
module.exports = { init_config }
})
__share('client.js', module => {
const init_client = __esmMin(() => {
__share_require('config.js').init_config()
mark('client')
})
module.exports = { init_client }
})
__share('locale.js', module => {
const init_locale = __esmMin(() => {
init_shared()
mark('locale')
})
module.exports = { init_locale }
})
__share('ui.js', module => {
const init_ui = __esmMin(() => {
__share_require('locale.js').init_locale()
mark('ui')
})
module.exports = { init_ui }
})
const config = __share_require('config.js')
const locale = __share_require('locale.js')
const client = __share_require('client.js')
const ui = __share_require('ui.js')
config.init_config()
locale.init_locale()
client.init_client()
ui.init_ui()
// Per-chunk entry-b.js
import {
init_shared,
mark,
} from './COMMON-ABC.js'
__share('COMMON-AB.js', module => {
const init_config = __esmMin(() => {
init_shared()
mark('config')
})
const init_client = __esmMin(() => {
init_config()
mark('client')
})
module.exports = {
init_config,
init_client,
}
})
__share('COMMON-BC.js', module => {
const init_locale = __esmMin(() => {
init_shared()
mark('locale')
})
const init_ui = __esmMin(() => {
init_locale()
mark('ui')
})
module.exports = {
init_locale,
init_ui,
}
})
const commonAb = __share_require('COMMON-AB.js')
const commonBc = __share_require('COMMON-BC.js')
commonAb.init_config()
commonBc.init_locale()
commonAb.init_client()
commonBc.init_ui()
Prevously in rolldown/rolldown#10693, I was thinking unifying
__shareand__esminto the same thing, because:__shareand__esmboth "wrap" the codeReasoning with these things, it seems intuitive to think of
__shareand__esmare similar thing with minor differences.Also with thinking #5, I initially think wrappers in wrappers(see below per chunk example) output just doesn't feel right. It makes the execution model more complicated, which isn't a good signal.
With
__esmwrappers in__sharewrappers, you need to think that before calling someinitXXX, you should make sure calling__share_require(...)to execute the body code of__shareso those__esmcalls get executed.But then I found that isn't it intuitive you need to execute the "chunk" code if your code relies on the chunk. So it does make the execution model more complicated than the current one describled in #5. But it's sound.
Conclusion
// Per-chunk entry-b.jsin the below examplestrictExecutionOrderwould be enabled/required for usinginlineCommonChunks__sharewrapper, modules are still scoped hoisted. This can't be done(or much limited) compared to module level approachstrictExecutionOrder: true, multiple__sharewrappers can always merge to one__sharewrapperExample
Input