Live Elements files end with the .lv
extension. Files are grouped into modules, and modules are grouped into packages. The following is an example of a Live Elements package:
|-- live.package.json
|-- main.lv
|-- module1
| |-- submodule1
| | |-- Example1.lv
| | |-- Example2.lv
| |-- submodule2
| | |-- Example3.lv
|-- module2
| |-- Example4.lv
All the files ending in the .lv
extension are files inside the module. At the root of this structure, live.package.json
describes the package.
live.package.json
is placed at the root of every package, and needs to provide the name of the package, together with a version:
{
"name" : "package_name",
"version" : "1.0.0"
}
Additional values can be added, but are not required, especially since they are provided inside the package.json
file from npm:
description
: A text based description of the package.keywords
: An array of keywords that describe the package.author
: Author of the packagelicense
: License of the packagedependencies
: An object of key-values describing the package dependencies.lv files contain live elements code, and can export either components or component objects (instances). A component exported from a file is automatically available to all other files inside the same module. For example, in module module1/submodule1
, if Example1.lv
would export the following component:
component Example1{
}
This component would automatically be available in Example2.lv
:
// Example2.lv
component Example2{
Object x: Example1{}
}
All components declared at the root of each file are exported automatically. We can declare multiple components and instances inside a file. In Example1
we could have the following exports:
component Example1{}
component Example1Second{}
instance exampleInstance Example1{}
exampleInstance
is an object of type Example1
and is made available as part of the submodule
module. Now, all of these components are automatically available in Example2
:
component Example2{
Object x: Example1{}
Object y: Example1Second{}
Object z: exampleInstance
}
Files within the same module are visible to each other by default. If you want use functionality from other modules, you have to import those modules.
There are 2 types of imports:
Relatve imports are imports that are part of the same package. A module inside a package can import another by using the import
keyword, followed by the path to that module from the root of the package. Path segments are separated by the dot (.
) notation, and relative imports also start with a dot. Let's revisit the structure from the begining of this article:
|-- live.package.json
|-- main.lv
|-- module1
| |-- submodule1
| | |-- Example1.lv
| | |-- Example2.lv
| |-- submodule2
| | |-- Example3.lv
|-- module2
| |-- Example4.lv
If, from Example1.lv
, we were to import module2
, we would use the following notation:
import .module2
The starting dot signifies it's a relative import from the root of the package. If we would want to import submodule2
from Example1.lv
, we would write:
import .module1.submodule2
From the root of the package, submodule2
is inside module1
folder, so the path to would be module1.submodule2
.
Importing the actual root of the package is done using a single dot(.
) after the import
, since there is no other path to import:
import . // imports the module at the root of the package
Global imports do not start the import path using a dot(.
):
import pacakge1
import package1.module1
import package2
Global imports are searched through the search paths of the compiler. In case of node based applications, the compiler looks inside the node_modules
folder.
In this file structure:
|-- live.package.json
|-- main.lv
|-- module1
| |-- submodule1
| | |-- Example1.lv
|-- node_modules
| |-- package1
| | |-- module1
| | | |-- GlobalModuleFile.lv
Importing package1.module1
from either main.lv
, or Example1.lv
will make the compiler look for the node_modules
folder in the root of the package, then search for package1/module1
inside that folder:
// In main.lv
import pacakge1.module1 // will resolve to node_modules/package1/module1
Imports, by default will import all the exported components or component instances from a module inside the global namespace of the importing file. If we want to use a namespace to import them to, we can use the import as
statement:
import .module2 as m2
import .module1.submodule2 as s2
m2.SomeComponent{
s2.OtherComponent{}
}
Import namespaces help when there are conflicts, like 2 components from different modules having the same name.
Since Live Elements is build on top of javascript, there are a few ways to import javascript modules:
import {readFile, readdir} from 'fs'
import fs from 'fs'
Not all javascript import expressions are supported.import * as name from '...'
for example doesn't work, even though this would be perfectly valid in javascript. If you need other types of imports, you can either create an intermediate javascript file that exports the required modules, or use the dynamic jsimport
function:
Application{
run: () => {
import('...').then((module) => { console.log(module) })
}
}
The lvimport
function from live-elements-core/lvimport.mjs
imports a single lv file. The function is asynchronous, as it needs to compile the file into javascript first, then load the module via the javascript import
function.
The following Example.lv
file exports 2 components:
// Example.lv
component A{}
component B{}
Using lvimport
we can access them as such:
// importlv_example.mjs
import lvimport from 'live-elements-core/lvimport.mjs'
lvimport('Example.lv').then(exampleModule => {
const A = exampleModule.A
const B = exampleModule.B
console.log(A) // [class A extends BaseElement]
console.log(B) // [class B extends BaseElement]
}).catch(err => {
console.log(err)
})