Optimizer Rules

The Optimizer can break up large applications into lots of small lazy-loadable chunks. In addition, the Optimizer can lazy-load function closure, which lexically captures variables. However, there are limits to what can be achieved, and therefore the Optimizer comes with a set of rules. Not all valid Javascript is valid Optimizer code. This section describes the rules that developer needs to follow for successful Optimizer transformation.

The $ is not only a marker for the Optimizer but also a marker for the developer to follow these rules.

NOTE: There are plans for a linter that will be able to enforce these rules eagerly.

Imports

RULE: If a function that is being extracted by Optimizer refers to a top-level symbol, that symbol must either be imported or exported.

import { importedFn } from '...';
export exportedFn = () => {...};

const salutation = "Hello";

someApi$(() => {
  importedFn(); // OK
  exportedFn(); // OK
  salutation; // Error: salutation not imported/exported
})

The reason for the above rule becomes obvious when the output is examined.

import { importedFn } from '...';
export exportedFn = () => { ... };

const salutation = "Hello";

someApi(qrl('./chunk-a.js', 'someApi_1'));

chunk-a.js:

import { importedFn } from '...';
import { exportedFn } from './originalFile';

export const someApi_1 = () => {
  importedFn(); // OK
  exportedFn(); // OK
  salutation; // Error: no way to get reference to this.
};

Closures

RULE: If a function lexically captures a variable (or parameter), that variable must be (1) a const and (2) the value must be serializable.

function somefn() {
  let count = 0;
  list.foreach((item) => {
    count++;
    const currentCount = count;
    someApi$(() => {
      item; // OK (assuming serializable)
      count; // ERROR: count not const
      currentCount; // OK (assuming serializable)
    });
  });
}

Again looking at the generated code reveals why these rules must be so:

function somefn() {
  let count = 0;
  list.foreach((item) => {
    count++;
    const currentCount = count;
    someApi$(qrl('./chunk-a.js', '_1', [
      item,
      count,
      currentCount,
    ]));
  });
}

chunk-a.js:

export _1 = () => {
  const [item, count, currentCount] = useLexicalScope();

  item; // OK (assuming serializable)
  count; // ERROR: count not const
  currentCount; // OK (assuming serializable)
};

See serialization for discussion of what is serializable.