Currently, the only way to share code between scripts is to use shouts, whispers, or global functions. The JavaScript ecosystem has a solution for structuring code and sharing it across projects, and that is to use ES Modules. Introduced in ES6, ES Modules add special syntax to JavaScript to represent "imports" and "exports". Imports are functions and data that is imported from other scripts, while exports are functions and data that are made available to other scripts.
We want scripts to support imports and exports just like ES Modules. This includes the following features:
[x] Scripts can use import statements.
[x] import * as lib from "source"
[x] import lib from "source"
[x] import { export1, export2, export3 as alias3 } from "source"
[x] import "source"
[x] Scripts can use export statements.
[x] export function abc() {}
[x] export class abc {}
[x] export const abc = 'def'
[x] export let abc = 'def'
[x] export default 'def'
[x] export { export1, export2, export3 as alias3 }
[x] export * from "source"
[x] export * as name1 from "source"
[x] export { name1, name2 } from "source"
[x] export { default } from "source"
[x] Modules can be imported from other scripts. For now, the paths look like this: system.name.tag
Relative imports are supported using the . and : symbols. : goes up a system level, while . references the current level.
[x] Modules can be imported from scripts that the runtime provides. There is currently only one possible import: casualos.
[x] Modules can be imported from scripts that exist outside the runtime. Used for things like NPM libraries. For now, all these imports are a HTTP URL: https://esm.run/d3@7.8.3 to load d3 from jsdelivr.
[x] Modules can be resolved using @onResolveModule.
[x] Scripts can resolve modules using import.meta.resolve().
[x] Scripts can use dynamic imports using await import(module).
[x] Scripts can pair import.meta.resolve() and await import().
There are some expected limitations of this system:
Live bindings
In the native ES Module spec, exported variables are views to the actual variables that are inside the module. References to imported variables are effectively the same as doing a getter function call.
However, in this initial implementation, exported variables are shallow copies of the actual variable value.
This limitation exists because the Transpiler would have to rewrite all references to an imported variable to ensure that it is retrieving the most up-to-date value from the module that it was exported from. Doing such is possible, but is a lot of extra work.
Circular dependencies
In the native ES Module spec, circular dependencies are supported by compiling each module and resolving the bindings for variables before actually executing the code. This means that circular dependencies work effectively the same as if the modules were defined in the same file. Functions could then be hoisted and would at the very least be defined.
In this initial implementation, circular dependencies are not supported. Once again, they could be theoretically supported if the Transpiler was written to support them, but it would be a lot of extra work.
Currently, the only way to share code between scripts is to use shouts, whispers, or global functions. The JavaScript ecosystem has a solution for structuring code and sharing it across projects, and that is to use ES Modules. Introduced in ES6, ES Modules add special syntax to JavaScript to represent "imports" and "exports". Imports are functions and data that is imported from other scripts, while exports are functions and data that are made available to other scripts.
We want scripts to support imports and exports just like ES Modules. This includes the following features:
import
statements.import * as lib from "source"
import lib from "source"
import { export1, export2, export3 as alias3 } from "source"
import "source"
export
statements.export function abc() {}
export class abc {}
export const abc = 'def'
export let abc = 'def'
export default 'def'
export { export1, export2, export3 as alias3 }
export * from "source"
export * as name1 from "source"
export { name1, name2 } from "source"
export { default } from "source"
system.name.tag
.
and:
symbols.:
goes up a system level, while.
references the current level.casualos
.https://esm.run/d3@7.8.3
to load d3 from jsdelivr.@onResolveModule
.import.meta.resolve()
.await import(module)
.import.meta.resolve()
andawait import()
.There are some expected limitations of this system: