Lightning Web Runtime :: Compiler
Table of contents
Introduction
The Lightning Web Runtime Compiler is a pluggable Rollup-based bundler for off-core LWC-based applications.
The Web Runtime Compiler extends the LWC/Rollup bundling functionality with the ability to inject container services, which can then participate in the compilation lifecycle phase.
Background
Definitions
Lightning Web Runtime: A LWC application runtime.
container: Initializes and builds the set of container services which make up an application.
container service: A service plugin, which can be (1) addressed and (2) hook into various container lifecycle phases.
specifier: A string import value (eg: import('app/home')
, import('lodash')
).
pivot: A way to specify when a particular component/module may be loaded relative to some context (eg: 'mobile' vs 'desktop', locale, etc).
Rollup plugins
Along with custom plugins, these Rollup plugins are automatically included and used by the Compiler:
- @lwc/rollup-plugin
- rollup-plugin-node-resolve
- rollup-plugin-commonjs
- If
minify
mode is on: terser is used
APIs
compile()
The Web Runtime Compiler exposes one main function:
async function compile(input):Promise<output>
It takes in a configuration object, and returns a Promise
to its output.
Compiler input
-
-
name
(string, required): component name. -
namespace
(string, required): component namespace. -
files
({ string: string, ...}, required): key/value pairs where each key is a file path and its value is a string containing the file contents. -
baseDir
(string, optional, default =""
): a directory prefix which contains contains the components being compiled. -
lwcOptions
(object, optional): pass in a customized LWC Rollup plugin config object, which gets merged with a default object. The following options are available (all are optional):rootDir
,modules
,exclude
,stylesheetConfig
, andexperimentalDynamicComponent
. -
outputConfig
(object[], optional):-
compat
(boolean, optional, default =false
):true
indicates browser compatibility mode. -
minify
(boolean, optional, default =false
):true
if the bundle should be minified. -
sourcemap
(boolean, optional, default =false
):true
if a source map should be generated. -
env
(object, optional, default ={ NODE_ENV: 'development' }
):-
NODE_ENV
(string, required) = node environment string (eg: 'production', 'development', etc).
-
-
-
-
Rollup options:
-
input
(string, optional, default =${namespace}/${name}
): the input path for the bundle, similar to Rollup'sinput
. -
external
(string[], optional, default =['lwc']
): list of dependencies that should not be included in the bundled output, similar to Rollup'sexternal
. -
format
(string, optional, default = 'amd'): type of output ('amd', 'cjs', 'esm', 'iife', 'umd') similar to Rollup'soutput.format
. -
formatConfig
(object, optional, default ={ id: namespace + '/' + name, define: undefined }
):-
amd
(object):-
define
(string): function to use in place ofdefine
, similar to Rollup'soutput.amd.define
-
-
-
-
Web Runtime options:
-
plugins
(Plugin[], optional, default =[]
): array of Rollup plugins to use. -
inlineConfig
(object[], optional, default =[]
): describes the exclusion patterns for each module descriptor; array of objects with the following properites:-
descriptor
(string, required): specifier which should have all dependencies inlined, EXCEPT for those in theexclude
array. Globs are supported. -
exclude
(string[], optional, default =[]
): array of specifiers which should be excluded from being inlined. Globs are supported. Globstars can be used to recursively match all modules in scoped namespaces or nested namespaces. Excluded specifiers that are not listed inexternal
orexposedModules
will be inlined.
-
-
exposedModules
(string[], optional, default:[]
): array of specifiers that are supported by the Web Runtime container.
-
Compiler output
The Compiler's output uses the same format as the LWC compiler.
- output (object):
-
success
(boolean):true
if the Compiler encountered no fatal errors. -
diagnostics
(CompilerDiagnostic[]): array of error and warning information. -
result
:-
code
(string): the resulting bundled code. -
map
(* | null): the generated source map, if requested. -
outputConfig
(object |undefined
): same object passed in as[Compiler input](#compiler-input).outputConfig
.
-
-
version
(string): the version of the Compiler which bundled this code.
-
Examples
Simple application
Creating a bundle for an LWC application. Components are stored in ./src.
All dependencies except for lwc
will be inlined in the bundle.
import { compile } from '@webruntime/compiler';
import * as path from 'path';
const options = {
name: 'main',
namespace: 'any',
external: ['lwc'],
baseDir: path.join(__dirname, 'src'),
files: {
'any/main': `
import { createElement } from 'lwc';
import App from 'x/app';
const element = createElement('x-app', { is: App });
document.body.appendChild(element);
`,
'x/app': `
export default 'Hello World';
`,
},
};
const { success, diagnostics } = await compile(options);
if (!success) {
for (let diagnostic of diagnostics) {
console.error(`${diagnostic.level}: ${diagnostic.message}`);
}
}
// Compiled Result:
// define('any/main', ['lwc'], function (lwc) { 'use strict';\n" +
// '\n' +
// " var App = 'Hello World';\n" +
// '\n' +
// " const element = lwc.createElement('x-app', {\n" +
// ' is: App\n' +
// ' });\n' +
// ' document.body.appendChild(element);\n' +
// '\n' +
// '});\n',
Simple application with inline config
Create a bundle for an LWC application without inlining any dependencies. Components are stored in ./src.
All dependencies except for lwc
will be defined separetly in the bundle.
import { compile } from '@webruntime/compiler';
import * as path from 'path';
const options = {
name: 'main',
namespace: 'any',
external: ['lwc'],
baseDir: path.join(__dirname, 'src'),
files: {
'any/main': `
import { createElement } from 'lwc';
import App from 'x/app';
const element = createElement('x-app', { is: App });
document.body.appendChild(element);
`,
'x/app': `
export default 'Hello World';
`,
},
inlineConfig: [
{
descriptor: '*/*',
exclude: ['*/*'],
},
],
};
const { success, diagnostics } = await compile(options);
if (!success) {
for (let diagnostic of diagnostics) {
console.error(`${diagnostic.level}: ${diagnostic.message}`);
}
}
// Compiled Result:
// define('any/main', ['lwc', 'x/app'], function (lwc, App) { 'use strict';\n" +
// '\n' +
// " App = App && Object.prototype.hasOwnProperty.call(App, 'default') ? App['default'] : App;\n" +
// '\n' +
// " const element = lwc.createElement('x-app', {\n" +
// ' is: App\n' +
// ' });\n' +
// ' document.body.appendChild(element);\n' +
// '\n' +
// '});\n',
LWC
Creating a bundle for a LWC stored in the files
object:
import { compile } from '@webruntime/compiler';
const options = {
name: 'lwc',
namespace: 'my',
external: ['lwc'],
files: {
'my/lwc.js': `
import { LightningElement } from 'lwc';
export default class Lwc extends LightningElement {}
`,
'my/lwc.html': '<template>lwc</template>',
'my/lwc.css': ':host { font-size: 16px; }',
},
};
const { success, code } = await compile(options);
console.log('The bundle is: ', success ? code : 'a failure');
// Output:
// The bundle is: define('my/lwc', ['lwc'], function (lwc) { ... });
JavaScript library
Create a non-LWC bundle. The JavaScript entry point must live in the files
object:
import { compile } from '@webruntime/compiler';
import * as fs from 'fs';
import * as path from 'path';
const filePath = path.join(__dirname, 'src', 'x', 'file.js');
const fileContents = fs.readFileSync(filePath).toString();
export const options = {
name: 'file',
namespace: '',
files: {
'file.js': fileContents,
},
};
const { code = '' } = await compile(options);
export default code;
Full Compiler config object
{
// LWC
name: 'cmp',
input: 'x',
files: {
'logger.js': 'export const logger = console.log;'
},
baseDir: path.join(__dirname, 'apps'),
lwcOptions: {
stylesheetConfig: {
customProperties: {
resolution: {
type: 'module',
name: '@salesforce/cssvars/customProperties',
},
},
},
experimentalDynamicComponent: {
loader: '',
strictSpecifier: true,
},
},
outputConfig: {
compat: true,
minify: true,
sourcemap: true,
env: { NODE_ENV: 'production' }
},
// Rollup
external: [
'lwc',
'force/lds',
'wire-service'
],
format: 'amd',
formatConfig: {
amd: { define: 'loader.addModule' }
},
// Web Runtime
plugins: [
{
plugin: 'src/rollup/global-rollup.js',
config: {
globals: {
'lodash': '_',
},
},
},
],
inlineConfig: [
{ descriptor: 'view/*', exclude: ['view/faq', 'wire-service'] },
{ descriptor: 'x/foo', exclude: ['@salesforce/**'] }
],
}
Container config mappings
Compiler configuration options are gathered from different areas, including:
- the calling service, possibly via a Rollup plugin (
service
) - the Container config object (
containerConfig
)
name: service
namespace: service
files: service
baseDir: containerConfig.moduleDir
stylesheetConfig: service or containerConfig.compilerConfig.lwcOptions
experimentalDynamicComponent: service or containerConfig.compilerConfig.lwcOptions
outputConfig: containerConfig.compilerConfig.outputConfig[mode]
external: containerConfig.externals, containerConfig.preloadModules, compilerConfig.external and exclusionsFrom(config.bundle)
format: service or container defaults
formatConfig: containerConfig.compilerConfig.formatConfig
plugins: service or container defaults
inlineConfig: containerConfig.bundle