-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Nested import declarations #646
Conversation
Previously, CreateImportBinding was only supported for module Environment Records. Supporting the CreateImportBinding method for declarative Environment Records will be important for nested |ImportDeclaration|s, since the declarative Environment Record for the nested block containing the |ImportDeclaration| will need to allow indirect bindings, too. Because declarative Environment Records may now have indirect bindings, the GetBindingValue method of declarative Environment Records now fully incorporates the logic of the GetBindingValue method of module Environment Records, and so the module-specific method has been removed.
…ion. This change means |ImportDeclaration|s are no longer grammatically restricted to the top level of a module. It also means |ImportDeclaration|s are no longer grammatically restricted to |Module|s, and could potentially appear in |Script|s, which is a problem to be addressed in a follow-up commit.
Note that this change allows functions and blocks to define bindings for |ImportDeclaration|s, which was not previously possible. After this change, the [[ImportEntries]] field of Module Record is no longer used anywhere in this specification.
The top-level ImportEntries of a module are now extracted as part of the CreateLexicalBindings operation, based on the top-level LexicallyScopedDeclarations of the module, which now include |ImportDeclaration| productions in addition to other lexically scoped declarations like function/generator declarations and let/const variable declarations. Though it is possible that another specification might depend on this field, the only relevant specification that I'm aware of is the WHATWG Loader spec (https://whatwg.github.io/loader), and while it refers to the Source Text Module Record in many places, it never makes use of the [[ImportEntries]] field. Moreover, I think we should remove this field now rather than allowing it to persist, because any hypothetical auxiliary specification that relies on [[ImportEntries]] is likely to be treating top-level import declarations differently from nested import declarations, and thus runs the risk of mishandling nested import declarations.
…ings. Unless this note is wrong, we do not have to worry about import declarations in eval strings, which is good news because it would be impossible to determine statically what module identifiers are requested by a dynamic string of code.
This change means nested modules will be evaluated at the beginning of their enclosing blocks, if they have not already been evaluated.
As a personal note, my Monday evening flight from JFK to SEA for the July TC39 meeting was canceled, but I'll be there by this afternoon! |
On the subject of the impact on lazy/deferred parsing... You were proposing a solution that involved tokenizing the script to find the nested import statements. In my experience, it isn't possible to tokenize JS accurately without tracking expressions. OTOH, I don't see recognizing import statements in a lazy parse as a deal-breaker. The work required (for Chakra, anyway) would be on the order of the work we already do to recognize early errors, for instance. |
For a feature like this I think there should be a tc39 repo for the proposal (rather than putting everything in a PR and tracking all issues in this one). |
@bterlson ok, agreed, I will create a new repository once I've updated the proposal to accommodate the feedback from yesterday |
I like this a lot (or something like it) because it makes it possible to // parent export class Parent { // child import Parent from 'parent.js' export class Child extends Parent {
} // That is, with this it is possible to import 'parent' from another file On Thu, Jul 28, 2016 at 12:37 PM, Ben Newman [email protected]
|
As others have mentioned, I'd love to see this developed more fully in its own proposal repo. But I wonder about how feasible the nested import is. For top-level imports, the loader can be reasonably efficient about pulling in all dependencies upfront. But for nested imports, having them return synchronously means an immediate blocking HTTP request before the method can complete, and that doesn't seem feasible. Maybe it could be allowed and "just work" in async methods, though? |
@shicks That's right, though I would never propose a synchronous HTTP request. Instead, the idea was that there must be a deadline for the asynchronous part of module fetching, and that deadline would come before evaluation of the original entry point module (potentially long before any nested The essence of the follow-up proposal is overturning the requirement of synchronous evaluation in favor of making all An alternative approach for asynchronous imports would be an imperative API that takes a module identifier and returns a Credit to @dherman, @caridy, @wycats, @samth, and others for their input into designing this static asynchronous syntax. More details to come (in a separate proposal)! |
Very cool! The Is the idea with top-level await that you could resolve a circular dependency explicitly by writing both as top-level imports but adding |
Do you have some quick sample code for https://git.io/es-next? @benjamn Using: import { check as checkClient } from "./client.js";
import { check as checkServer } from "./server.js";
import { check as checkBoth } from "./both.js"; For now. |
@hemanth that is the code this proposal wants to replace and is presented as bad code. The correct way is the first example in https://github.com/benjamn/reify/blob/master/PROPOSAL.md#isolated-scopes |
@Kovensky It makes sense, isolated scopes, rings the bell. describe("fancy feature #5", () => {
import { strictEqual } from "assert";
it("should work on the client", () => {
import { check } from "./client.js";
strictEqual(check(), "client ok");
});
it("should work on the client", () => {
import { check } from "./server.js";
strictEqual(check(), "server ok");
});
it("should work on both client and server", () => {
import { check } from "./both.js";
strictEqual(check(), "both ok");
});
}); |
I would have to see a specific "async" import proposal to understand if it As the class hierarchy requires "hard" dependencies (the extend expression On Fri, Jul 29, 2016 at 2:10 PM, Stephen Hicks [email protected]
|
@benjamn Closing this as we're not anywhere close to accepting this PR. |
(wrong @benjamn, i think) |
@ljharb yes thanks |
I opened an issue here: tc39/proposals#70 to get some clarity around the intent of this proposal and what the differences are in between this and the functional |
From what I could gather, the purpose of this is to restrict the scope of imported names and make it easier for dead code elimination to remove imports. The import still executes synchronously but at the top of the enclosing block. The dynamic EDIT: this makes it sound like a top-level await, but it is not the same. A top-level await can cause a wait at any point in the body, but the hoisted imports always "await" and finish executing before the body starts executing at all, barring cycles. |
Stage: 0
Champion: Ben Newman (Meteor Development Group)
Specification: Work in progress; see attached commits.
Summary
This proposal argues for relaxing the current restriction that
import
declarations may appear only at the top level of a module.Specifically, this proposal would allow
import
declarations that areimport
idioms;import
declarations.At this time, no changes are proposed regarding the placement or semantics of
export
declarations. In the opinion of the author, keeping all
export
declarations at the toplevel remains important for many of the static properties of the ECMAScript module system.
Read the full proposal here.