Imports

J2 gets by with one keyword and a search path: import splices another source file into your program, and a package is nothing more than a directory of source files you can put on that path.

The import statement

import "path" names a source file whose contents become part of the program, as if written at the point of the import. Imports appear at the top level, one per line, conventionally at the top of the file.

With geometry.j2:

func area(w, h) = w * h
func perimeter(w, h) = 2 * (w + h)

a neighboring file uses it like this:

import "geometry.j2"

print(area(3, 4))         # 12
print(perimeter(3, 4))    # 14

Because imports are textual, everything the imported file defines is visible: functions, classes, and top-level bindings alike. There are no namespaces and no selective imports; files share one program scope, so name your definitions accordingly.

The search path

An absolute path is used as written. A relative path is tried in three places, in order:

  1. next to the importing file,
  2. in the importing file's lib/ subdirectory,
  3. in each directory listed in the J2_PATH environment variable, colon separated.

J2_PATH is how shared code escapes a single project: keep your utilities in one directory and export it once in your shell profile.

$ J2_PATH=$HOME/j2/lib j2 run report.j2

Transitive imports and cycles

Imported files may import other files, and the same file imported twice, directly or through a diamond, is included only once. Cycles are therefore harmless: if a.j2 and b.j2 import each other, the second inclusion is a no-op and the program proceeds.

Project layout

A working convention for a program that has outgrown one file:

report/
  report.j2        entry point: imports, wiring, top-level flow
  parse.j2         one concern per file
  render.j2
  lib/             utilities shared by the files above
    tables.j2

The entry point imports its parts; the parts import what they share from lib/. A "package" you hand to someone else is simply this directory, and installing it means putting it somewhere on J2_PATH. There is no package manager, no manifest, and no registry; a directory of readable source is the unit of distribution.

The standard library needs no imports

The built-in functions and the standard modules (math, text, json, fs, and the rest) are available in every file without any import. The Standard Library page lists all of them. import exists for your code, not the language's.