Introduction
In a module, import
produces a GET
to a server and processes the results in a specific way. While the browser is aware that an import
is taking place, the server isn’t. Letting the server know the response will be processed as an import
opens up many possibilities.
Commonly an import
request is made for a static file with something like - import { Foo } from "/bar.mjs";
. If the response contains something like export class Foo { }
, import is happy.
Because the request is a standard GET
, we can include query parameters:
import { Class1, Class2 } from "/ModuleService.mjs?one=Class1&two=Class2";
In this case, ModuleService
represents an entry point into a process that dynamically builds the response. We can intercept the request in the request pipeline to do this. Again, as long as the response contains something like export Class1 { } export Class2 { }
, import is happy.
This approach allows us to do things like manage small discrete modules in our source, but avoid having to import each required module individually. We can use this for things like implementing primitive forms of (pseudo) dependency injection.
import { Dependency1, Dependency2, Dependency3 }
from "/diService.mjs?\
asm1.ns1=Dependency1&\
asm2.ns2=Dependency2,Dependency3";
export class OperationalClass {
#dependency1;
#dependency2;
#dependency3;
constructor() {
this.#dependency1 = new Dependency1();
this.#dependency2 = new Dependency2();
this.#dependency3 = new Dependency3();
}
doStuff(){
const x = this.#dependency1.invokeFunction();
}
}
When an import of OperationalClass
is requested, the server can parse the imports, and package all four modules/classes into a single file. This is only one possible use of this approach.
Problem
A problem with this is that while the browser knows it is expecting a module when it requests an import
, the server doesn’t explicitly know it needs to return a response in the form of an import response. We can use tricks like flags in the query string to indicate an import and inspect the query string for these flags in the request processing pipeline, but this is cumbersome and error prone. The imbalance between what the browser knows and what the server knows is limiting and problematic.
Solution
A better approach is to indicate an import
is being requested in the request header. This provides simple and explicit information the request pipeline can use to route the request.
Proposal
Proposed: When an import
produces its GET
request, it adds request header information to indicate an “import response” is being requested.
Details
This proposal does not specify the specific headers, but identifies the need for a “module import” header group, and two header items:
- Import Indicator - this item is used as the primary filter by the request pipeline.
If the indicator is not present or “false” the request is not an import request. If the indicator is present or “true” the request is an import request. Optionally, the indicator can be valued for use as a switching parameter. - Import Data - this item is a string or set of Key Value Pair that contains information used in processing the request. (e.g., the query string values in the example).
Implementation
The proposal can be implemented in two stages.
The first stage simply adds a single header item to the request. This is (essentially) a flag - without value or with a default value. This should be a low impact effort, as it creates no changes visible to the developer, requires little code to implement, and has the only effect of creating a new header entry. Since no data is being managed, the import
statement is unchanged - data can still be sent in the query string. This alone provides significant value.
Stage two would alter the import statement to accept parameters. The details need to be discussed, but a simple version might be:
import { Item }
from "/target"
with [parameters];
Any information provided in the with
parameters would be written to the Import Data
header item. The example might look something like:
import { Dependency1, Dependency2, Dependency3 }
from "/diService.mjs"
with [
["asm1.ns1", "Dependency1"],
["asm2.ns2", "Dependency2, Dependency3"]
];