Now.js Framework Documentation

Now.js Framework Documentation

Project bundles — keeping project-only code out of Now/

EN 06 Nov 2026 02:55

Project bundles — keeping project-only code out of Now/

Overview

The Now/ folder (sources in Now/js, Now/css and bundles in Now/dist) and vite.config.js are
identical in every project. They are changed upstream (nowjs → adminframework) and distributed to
every project; no file that belongs to a single project may live there.

Code that only one project uses and that needs its own bundle (a designer, a timeline, ...) lives in

js/components/<name>/
├── build.json      ← tells vite how to build it
├── entry.js        ← bundle entry point (imports the other files and the CSS)
├── *.js · *.css    ← bundle sources
js/dist/            ← build output of every bundle in this project

vite.config.js does not need editing — when BUILD_TARGET=<name> is not a Now.js bundle, vite reads
js/components/<name>/build.json and builds into js/dist/.

build.json

{
  "entry": "entry.js",
  "name": "now.timeline.min.js",
  "css": "now.timeline.min.css",
  "libName": "NowTimeline"
}
Key Meaning
entry Entry file, relative to the bundle folder (default entry.js)
name JS file name in js/dist/
css CSS file name in js/dist/ (produced when the entry imports CSS)
libName IIFE global name — the entry's export default ends up on window[libName]

The folder name must not be one of the Now.js bundles (core, table, graph, serviceworker,
queue, eventcalendar, editor, formbuilder, syntaxhighlighter); those names always win.

Build command

Add a script to that project's package.json (this file may differ between projects):

"build:timeline": "cross-env BUILD_TARGET=timeline vite build"

and append && npm run build:timeline to "build" to have it built with everything else.

The core build empties Now/dist (emptyOutDir) every time; js/dist is never emptied, so project
bundles survive a core build and a fresh distribution of Now/.

Loading a bundle

index.php is a shared file that is the same in every project, so it does not load project bundles —
the module that needs one loads it from its own admin.js, passing on admin.js's ?v= (the service
worker serves .js files without a query cache-first, so a rebuilt file would not be picked up).

const docDesignerBundle = (() => {
  const src = document.currentScript?.src || '';
  const base = src.replace(/modules\/docdesigner\/admin\.js.*$/, '');
  if (!base) return Promise.resolve();
  const query = src.includes('?') ? src.slice(src.indexOf('?')) : '';

  const css = document.createElement('link');
  css.rel = 'stylesheet';
  css.href = `${base}js/dist/now.docdesigner.min.css${query}`;
  document.head.appendChild(css);

  return new Promise((resolve, reject) => {
    const js = document.createElement('script');
    js.src = `${base}js/dist/now.docdesigner.min.js${query}`;
    js.onload = () => resolve();
    js.onerror = () => reject(new Error('Failed to load js/dist/now.docdesigner.min.js'));
    document.head.appendChild(js);
  });
})();

async function initDocDesigner() {
  await docDesignerBundle; // the page may render before the script has loaded
  // ...
}

Bundles in use

Project Bundle Output
project_manager js/components/timeline/ (entry imports sources from modules/project/timeline/) js/dist/now.timeline.min.*
form js/components/docdesigner/ js/dist/now.docdesigner.min.*
gcms.in.th js/components/designer/ js/dist/gcms.designer.min.*
bluprint.in.th js/components/migration/ (uses @e965/xlsx) js/dist/now.migration.min.js