LiTScript shares configuration with the TypeScript compiler wherever possible. Additionally, it needs some settings to control what files are included in the documentation, what format is outputted, and so on.
These settings are defined in a configuration file called litsconfig.json
which resides in the project root folder along with the usual tsconfig.json
file. The same settings can also be specified as command line switches. These
switches have the same names as the corresponding properties in the JSON file.
An example command line could look like this (each settings is described in the Options section):
lits --baseDir . --outDir temp --updateToc
If a setting is not defined in the configuration or on command line, the default value takes effect. These are defined in the Defaults section. The precedence order for settings is thus:
litsconfig.json
fileimport * as fs from 'fs'
import * as ts from 'typescript'
import * as path from 'path'
import * as fm from './templates/front-matter'
The names of the configuration files are constants.
export const tsconfig = "tsconfig.json"
export const litsconfig = "litsconfig.json"
The available settings are defined in the Options
interface. Options
are case-insensitive both in JSON and command line. Keys are converted to
lowercase before comparing them.
export interface Options {
We refer to the project root folder shortly as base directory. LiTScript expects to find the configuration files there.
baseDir: string
The output directory is stored in the outDir
property. A typical value
for this is the docs/
folder under the base directory. From there the
documentation files can be easily published to GitHub Pages.
outDir: string
The output format is either markdown
or html
. If markdown output is
chosen the settings in the front matter are not used.
outputFormat: 'markdown' | 'html'
The processed TypeScript files are defined in tsconfig.json
, so they
need not to be specified here. Other types of input files you need to add
to the files
property. It contains a list of glob patterns that are
relative to the base directory.
As an example, to include all files with the .md
extension under
directory instructions/
, add pattern instructions/**.md
to the list.
The double asterisk wild card will find all the files, no matter how
deep they are in the directory tree.
files: string[]
Sometimes you want to omit some files from the documentation. For
example, you may want to skip TypeScript files that don't contain
documentation. To achieve this you can list files to be excluded in the
exclude
array as glob patterns. This array is checked after the input
files have been collected.
Note that all the TypeScript files in the project are compiled, only the
documentation output is skipped. You can put other types of files in the
exclude list, but usually it is simpler just to omit them from the
files
array.
exclude: string[]
Set the following flag to suppress output of status messages.
silent: boolean
The name of the table of contents a.k.a. TOC file is specified in the
tocFile
property. TOC file has to be placed in the outDir
folder as
the paths defined in it are relative to that directory.
tocFile: string
LiTScript can automatically add new pages to the TOC file. To enable
this, set the updateToc
property to true
. If you want to omit some
files from TOC, you can add glob patterns that match their names to the
excludeFromToc
array.
updateToc: boolean
excludeFromToc: string[]
LiTScript can also create module dependency graph and save it in a JSON file. The name of the file is defined below. If undefined, the file is not produced.
dependencyGraph: string
If you want to skip the bundling phase for HTML output, unset the following flag.
bundle: boolean
In watch mode LiTScript compiler runs in background and automatically regenerates documentation for changed files.
watch: boolean
LiTScript contains also a development web server that supports live
reloading of changed files. When the serve
setting is on, the web
server is started. Note that the watch mode automatically turned on along
with the serve mode.
You can customize the network options by changing the serveOptions
object below.
serve: boolean
serveOptions: ServeOptions
Backend module is loaded by the LiTScript development server or by the
included node.js server when application is deployed. The output
directory of the backend module is specified by the backendOutDir
property.
backendModule: string
backendOutDir: string
The deployment mode controls whether debugging information needed for development is included with the generated JavaScript files.
deployMode: 'dev' | 'prod'
Front matter is a template-specific configuration object that is defined in the template package. Front matter is settings for the default template can be found here.
frontMatter: fm.FrontMatter
}
The network options used in serve mode are defined below.
export interface ServeOptions {
Host name or IP address. Typically 127.0.0.1
or localhost
.
host: string
Listened port number. If omitted, port 8000 is used.
port: number
}
The settings currently in effect can be read from this global variable.
var options: Options
Default values defined below are used for the settings that are not overridden in the configuration file or with the command line.
export const defaults: Options = {
baseDir: ".",
outDir: "./docs",
outputFormat: 'html',
files: ["**.md"],
exclude: [ ".git", "node_modules" ],
silent: false,
tocFile: "toc.json",
updateToc: false,
excludeFromToc: [],
dependencyGraph: "dependencies.json",
bundle: true,
watch: false,
serve: false,
serveOptions: {
host: "127.0.0.1",
port: 8000
},
backendModule: "",
backendOutDir: "./backend",
deployMode: 'dev',
frontMatter: fm.defaults
}
Other modules can access the effective settings using the function below.
export function getOptions(): Options {
return options
}
The following function returns the path to the given file relative to the base directory.
export function getBaseRelativePath(filePath: string) {
return path.relative(path.resolve(options.baseDir), filePath)
}
To set the options directly from outside the module, we provide this
helper function. Note that we use the Partial
export function setOptions(opts: Partial<Options>) {
options = opts as Options
mergeOptions(defaults, options)
}
The configuration file is read from JSON file into the global options
object. After that, default values are merged to the object.
export function readOptionsFromFile(baseDir: string = "./") {
let litsfile = path.resolve(baseDir, litsconfig)
if (!fs.existsSync(litsfile))
options = defaults
else {
let cont = fs.readFileSync(litsfile, 'utf8')
options = JSON.parse(cont)
mergeOptions(defaults, options)
}
}
The following function recursively merges object properties. It only sets
properties of the target
object which are undefined. So, it does not
override any properties already set.
export function mergeOptions(source: object, target: object) {
for (const key in source)
if (source.hasOwnProperty(key)) {
let val = source[key]
if (target.hasOwnProperty(key)) {
let valType = typeof val
if (typeof target[key] !== valType)
throw new SyntaxError(
`Invalid value for option "${key}". Expected ${valType}.`)
if (valType === 'object' && !(val instanceof Array))
mergeOptions(val, target[key])
}
else {
if (val === undefined)
throw new Error(`Mandatory option "${key}" missing.`)
target[key] = val
}
}
}
We store the compiler options read from tsconfig.json
in a global variable.
This variable can be read and written using the functions below.
var compilerOptions: ts.CompilerOptions
export function getCompilerOptions(): ts.CompilerOptions {
return compilerOptions
}
export function setCompilerOptions(opts: ts.CompilerOptions) {
compilerOptions = opts
}
This helper function finds the field in the options
object that corresponds
to the specified command line switch. The search is case-insensitive. If no
match is found, a CommandLineError
exception is thrown.
function getOptionKey(option: string, optObj: object): string {
for (const key in optObj)
if (optObj.hasOwnProperty(key) &&
key.toLowerCase() === option.toLowerCase())
return key
throw new CommandLineError("Unknown option: --" + option)
}
Command line parsing is done generically using the options
object as the
schema. Command line switches have the same names as the fields of the object.
Depending on the type of the field the parsing works a bit differently.
Strings are inputted as-is, booleans don't need an argument at all, and other
types are read in as JSON.
export function parseCommandLine(args: string[], optObj: object) {
let i = 0
while (i < args.length) {
let opt = args[i++]
if (opt[0] != '-' || opt[1] != '-')
throw new CommandLineError("Invalid command line argument: " + opt)
let key = getOptionKey(opt.slice(2), optObj)
let optype = typeof optObj[key]
if (optype === 'boolean')
optObj[key] = true
else if (i == args.length)
throw new CommandLineError("Value missing for option " + opt)
else {
let value = args[i++]
optObj[key] = optype === 'string' ? value : JSON.parse(value)
}
}
}
Outputting command line options works also generically based on the options object.
export function printCommandLineOptions(optObj: object) {
for (const key in optObj)
if (optObj.hasOwnProperty(key))
console.log(`--${key} \x1b[90m${JSON.stringify(optObj[key],
undefined, 4)}\x1b[0m`)
}
To distinguish command line errors from other types of errors, we define a
new class extending the Error
class.
export class CommandLineError extends Error {
constructor(message: string) {
super(message)
}
}