Files and Modules

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

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.jsonfile from npm:

  • description: A text based description of the package.
  • keywords: An array of keywords that describe the package.
  • author: Author of the package
  • license: License of the package
  • dependencies: An object of key-values describing the package dependencies.

lv files

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
}

Imports

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:

  • Relative Imports
  • Global Imports

Relative 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

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

Import namespaces

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.

Javascript imports

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) })
  }
}

Importing Live Elements from javascript

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)
})