- We run
node --loader=presm ./dist/calc_runner.mjs. - The presm hook loads the project configuration[1].
- It sees that the path is "owned" by the hook (inside of output prefix).
- It runs the chain of operations neceesary to generate this resource.
HOW DOES IT KNOW?
- Known / stable mapping back and forth, e.g. glob-based or for
http
dist/_http/<base64-of-url>.*. That way the mapping back would be deterministic. - We could even go as far as making no glob assumptions at the expense
of nice destination filenames.
dist/_fs/<src-relative-path>.*. - Build index on startup for the files reachable from the resource providers.
- Require unique basenames to make file-provider enough to get full mapping.
- Where does the final name come from?
- fs-loader: src/calc_runner.ts ~> dist/calc_runner.ts
- ts-pre: dist/calc_runner.ts ~> dist_calc_runner.mjs
- Output filename generation should be reversible, end-to-end.
Example:
dist/_fs0/calc.ts.mjs. This could be obfuscated to "look better". May be better or worse.dist/<reversible-string>.<extension>. - Alternatively, we could give up on individual output files and configure "bundles". Even worse. Way too slow and complex. In this case:
- File loader from src/calc_runer.ts -> { source: ....'0, contentType: 'text/typescript' }
- typescript preprocessor -> { source: ....'1, contentType: 'text/javascript' } -> Rewrite specifiers (?)
- JavaScript postprocessors (babel in this case) -> { source: ....'2, contentType: 'text/javascript' } -> OR rewrite specifiers here (?)
- Presm builds up an index of potential transpiled paths to disk paths
- Therefore
foo.ts.mjscan be mapped back tofoo.tsand transpilation can start from that file - See example
- Known / stable mapping back and forth, e.g. glob-based or for
http
- It passes control to node.js with the final contents. -> { format: 'module', source: ....'2 }
- Node.js sees
import ./calc.mjsin the generated code. - Node calls resolve on
("./calc.mjs", "file://///dist/calc_runner.mjs"). - Loader resolves (somehow?) to
file://///dist/calc.mjs. - Start back at (3).
Q: Should it be .mjs or .js on the compiled side?
Q: How to deal with valid TS that uses import ./foo.js
to referenece .ts files?
- Where to write generated code.
- Which resources to load and how.
- (pre) Additional source file types to support.
- (post) Which transforms to apply to what kind of resource.
{
"outputPrefix": "./dist",
"resourceProviders": [
{ "type": "@presm/file", "base": "./src" },
"@presm/https",
],
// "Convert one format into another format"
"preProcessors": [
"@presm/typescript", // assumption: defaults to tsconfig.json
"@presm/json",
],
// "Transform within the same format"
"postProcessors": [
{
"type": "@presm/babel",
"options": {
"presets": ["env"],
},
},
],
}- In most cases we have resolved a path before it gets loaded.
- BUT not in all cases..? E.g. dynamic import..?
- Sometimes we didn't rewrite the specifier, that's another case.
import * as ns from './foo/bar.json';
// This is hard (impossible?) to rewrite.
import('./foo/' + locale + '.json').then(dynNS => {
dynNS === ns;
});
// If we want to support this, we'll likely have to do the same
// kind of globbing magic that tools like rollup/webpack do.Example Filesystem:
/src
/calc.ts
/res
/calc.yaml
Example resourceProviders in Project Config
resourceProviders:
{ "type": "@presm/file", "base": "./src" },
// This would "own" all files in src/ directory
// and therefore build an index for all files and subdirectories in this directoryExample preProcessors in Project Config
"preProcessors": [
"@presm/typescript", // transforms .ts -> [.mjs | .cjs]
"@presm/yaml", // transforms .yaml -> [.json | .mjs]
],Therefore presm would build the following many to one mapping of:
transpiled resource → project file in disk
index (including nested directories):
/dist/src/calc.ts.mjs -> /src/calc.ts
/dist/src/calc.ts.cjs -> /src/calc.ts
/dist/src/res/calc.yaml.json -> /src/res/calc.yaml
/dist/src/res/calc.yaml.mjs -> /src/res/calc.yaml
- We run
node --loader=presm ./src/calc_runner.ts. - The presm hook loads the project configuration
- The loader resolves to the equivalent dist path
(
file://///dist/calc_runner.mjs). - Everything else is as above.
$ node --loader=presm
> const {calc} = await import('presm-example-calc');
> calc(2, 3);
5
>
- The loader resolves
('presm-example-calc', 'file:///<repl>')
- It finds a
package.jsonwith the rightname. - It finds an exports entry with
./dist/calc.mjs. - It needs to load it on-the-fly.
- Put source paths into package.json of the project
(e.g.
"cli": "./src/calc_runner.ts") and generate apackage.jsonin dist that updates the paths to their compiled equivalents. - Potentially this breaks TypeScript (I think it doesn't like
TypeScript files in
package.json#main). - Breaks
npm publishfrom project directory. This is the same problem thatpikahas. - Allows to generate "good"
exportsfields.
// ./package.json
{
"exports": {
".": "./src/foo.ts"
},
}
// ./dist/package.json
{
"exports": {
".": {
"typings": "./foo.d.ts",
"require": "./foo.cjs",
"import": "./foo.mjs",
},
},
}For all files:
- Load all
resourceProviders,preProcessors, andpostProcessors - Expose a custom
resolvehook that likely comes from aresourceProvider - Expose a dummy
getFormathook to eliminate formatting problems for unsupported formats:
- NOTE: Supported formats are
builtin,commonjs,dynamic,json,module,wasm
- Get the source (
string) of the file via aresourceProviderthat matches its URL (e.g.file:) - stop iteratingresourceProviderswhen found - For all
preProcessors, input and outputsource - For all
postProcessors, input and outputsource - Return
sourceinobjectfor node to run