tc39 / proposal-module-declarations

JavaScript Module Declarations
https://tc39.es/proposal-module-declarations
MIT License
373 stars 7 forks source link

Private vs public module fragments #4

Closed devongovett closed 1 year ago

devongovett commented 3 years ago

In the current proposal, my understanding is that module fragments are always publicly accessible outside the file they are bundled in. For example you can do import './module.js#fragment' to import something from the #fragment module defined in module.js.

I think it may be useful to annotate which module fragments are accessible from outside the file they are defined in. For example:

// Only accessible within this file
module '#private' {
  export function doSomethingPrivate() {}
}

// Accessible from outside the file.
export module '#public' {
  import {doSomethingPrivate} from '#private';

  export function publicFunction() {
    doSomethingPrivate();
  }
}

This could be especially useful for library bundling usecases where you may have several input modules and wish to produce a single output file but only some of the input modules should be accessible. Node's exports field in package.json allows something like this on the file level, but for bundled libraries, something similar could be useful.

Bundlers like Rollup and Parcel are often used to bundle libraries like this to prevent unintended access to library internals. In a world where bundlers move to using module fragments, I think we'd want a way to preserve these semantics. If private module fragments were possible, we could simply wrap all modules in fragments and concatenate them, and re-export the entry module.

Thoughts?

ljharb commented 3 years ago

I would hope they’re all inaccessible by default, like any other expression.

littledan commented 3 years ago

On a pedantic/formal level, module fragments are not expressions; they are declarations which declare entries in the host's module map, not runtime language values. This led me to initially think that the export keyword wouldn't make sense.

At the same time, at a high level, it does make sense to me to think of module fragments as things which can be made available outside of the module, or kept local to the module. Multiple people have independently made this request for "private" module fragments, and there have also been use cases presented for module fragments which are exported (e.g., https://github.com/tc39/proposal-js-module-blocks/issues/34).

The semantics of exported vs non-exported module fragments would be a little different from normal module exports. Normal exports are JavaScript values which would be on the module namespace object, but these are entries in the module map. The module fragments case would be more like: all of the fragments go in the module map just the same, but when trying to import a module fragment, there would be an extra check: if a module fragment was not declared with export, and the referrer has a different URL (ignoring the fragment), then the import would be rejected.