Skip to content

Building

David Benson edited this page Oct 7, 2026 · 9 revisions

This page describes the public build in etc/build: what each Ant target reads and writes, the Closure Compiler settings, the bundles it produces, how to add a source file, and which parts of the shipped files the public build cannot regenerate. It is for developers who change the source and want the change in production mode, in a .war, or in a fork.

Do you need to build?

The minified bundles in src/main/webapp/js are committed, so you can run the editor straight from a clone (see Getting started). Development mode (?dev=1) loads the sources directly and never needs a build.

Build when you want to:

  • see your source changes in production mode (the default URL without ?dev=1),
  • produce build/draw.war for a servlet container,
  • ship a fork that runs from minified bundles, for example on GitHub Pages.

Requirements

Tool Version Notes
JDK 8, 11 or 17 The release workflow .github/workflows/war.yml builds with Zulu JDK 8 and the docker-drawio image with JDK 11; JDK 17 works as well
Apache Ant 1.10 ant must be on the PATH
Google Closure Compiler v20220502 Committed as etc/build/compiler.jar, nothing to download

No Node.js, npm or network access is needed.

Running the build

All targets run from etc/build:

cd etc/build
ant          # default target "all": merge, app, atlas, integrate
ant app      # merge + app: the editor, viewer and extension bundles
ant war      # app + javac, then packages build/draw.war
ant clean    # removes compiled classes, build/ and intermediate files

A full ant run takes well under a minute. The outputs overwrite the committed files in src/main/webapp/js, so git status shows them as modified afterwards. Some differ from the committed copies even without source changes, notably viewer.min.js and viewer-static.min.js, see Output bundles.

Build files

File Purpose
etc/build/build.xml All targets, with a comment block per target
etc/build/build.properties Paths: jscompiler, src.dir (Java sources), war.dir (src/main/webapp), grapheditor.dir, javac.dir (WEB-INF/classes), build.dir (build/), war.name (draw.war)
etc/build/build.properties.local Optional, not committed. Loaded before build.properties, so any property set here wins
etc/build/compiler.jar Closure Compiler

Targets

The dependency chain is merge → app → atlas → integrate → all. javac stands alone, and war depends on app and javac.

Target Reads Writes
merge shapes/**/*.js, stencils/**/*.js js/shapes-14-6-5.min.js. Deletes the build/ directory at the end, including a previously built draw.war
app styles/default.xml, resources/dia.txt, VERSION, mxgraph/mxClient.js, the sources in js/grapheditor, js/diagramly and its subfolders, js/orgchart, the vendored libraries js/app.min.js, js/viewer.min.js, js/viewer-static.min.js, js/extensions.min.js, js/orgchart.min.js; leaves etc/build/base.min.js and etc/build/base-viewer.min.js for atlas
atlas base.min.js, base-viewer.min.js, extensions.min.js, connect/common/js/mxReader.js, js/diagramly/Simple.js js/atlas.min.js, js/atlas-viewer.min.js (intermediate files for integrate)
integrate atlas.min.js, shapes and stencils bundles, etc/integrate/Integrate.js js/integrate.min.js
all Runs integrate, then deletes atlas.min.js and atlas-viewer.min.js
javac src/main/server/java, the jars in WEB-INF/lib WEB-INF/classes (plus a copy of log4j.properties)
war Everything under src/main/webapp after app and javac build/draw.war
clean Deletes WEB-INF/classes, build/, etc/build/base.min.js, etc/build/base-viewer.min.js

What app does, in order:

  1. Writes a small configuration snippet for the viewer bundles that defaults PROXY_URL, STYLE_PATH, SHAPES_PATH, STENCIL_PATH, DRAW_MATH_URL, GRAPH_IMAGE_PATH, mxImageBasePath and mxBasePath to https://viewer.diagrams.net/... unless the page has set them, and sets mxLoadStylesheets = false.
  2. Generates Graph-Stylesheet.js from styles/default.xml (assigned to Graph.prototype.defaultThemes) and Graph-Resources.js from resources/dia.txt (passed to mxResources.parse).
  3. Compiles the intermediate bundles grapheditor.min.js (Editor.js, EditorUi.js, Sidebar.js, Graph.js, InlineToolbar.js, Format.js, Shapes.js, Actions.js, Menus.js, Toolbar.js, Dialogs.js), sidebar.min.js (every Sidebar-*.js palette) and client.min.js (deflate/base64.js, diagramly/Init.js, grapheditor/Init.js, mxgraph/mxClient.js).
  4. Builds the viewer bundles, then extensions.min.js and orgchart.min.js, then app.min.js, replacing @DRAWIO-VERSION@ with the contents of VERSION.
  5. Deletes its temporary files.

Closure Compiler settings

Every <jscomp> task uses the same options:

Option Value Effect
compilationLevel simple Renames local variables and removes whitespace and comments. Properties and global names are kept, which is what lets plugins and PostConfig.js override prototypes by name
debug false
forceRecompile true Always recompiles
warning ${jscomp.warning}, default quiet Prints only errors and the per-task summary. Use ant -Djscomp.warning=default (or verbose) to see warnings
languageIn / languageOut Not set The Ant task's defaults for this compiler version apply

With the defaults, modern syntax such as let, const, arrow functions, class, async and ?? is accepted and transpiled to ES5, and the compiler injects its $jscomp runtime helpers (visible at the top of the compiled code in app.min.js). Syntax newer than v20220502 understands, such as class fields, fails with JSC_PARSE_ERROR and stops the build. The code base itself is written in ES5 style; see Coding conventions.

Vendored libraries that are already minified (pako, DOMPurify, rough.js and so on) are added with <concat> and are never passed through the compiler.

Output bundles

File Contents Loaded by
js/app.min.js spin.js, DOMPurify, CryptoJS AES, pako, rough.js, perfect-freehand, client.min.js, grapheditor.min.js, sidebar.min.js, then the draw.io layer: files and storage clients, dialogs, Editor.js, EditorUi.js, ConfigEditor.js, sync, App.js, Menus.js, Pages.js, Minimal.js, Simple.js and more bootstrap.js in production mode
js/shapes-14-6-5.min.js Every .js file under shapes/ (and under stencils/, if any) App.main before the UI is created; also contained in viewer-static.min.js and integrate.min.js
js/stencils.min.js All stencil libraries in a compact encoded form. The stencil XML sources are not in this repository, so the public build keeps the committed file App.main; Devel.js in development mode; also contained in integrate.min.js and, when built from this repository, in the viewer bundles
js/extensions.min.js Extensions.js (the Lucidchart importer), the VSDX importer, EMF conversion, the GraphML codec, the Miro importer, then JSZip, the binary Visio converter, the ELK layout bundle with ElkLayout.js, the Mermaid bundle, orgchart.min.js, the libavoid router with libavoid-routing.js and LibavoidRouting.js App.main before the UI is created
js/orgchart.min.js The org chart layout libraries plus mxOrgChartLayout.js On demand (also contained in extensions.min.js)
js/viewer.min.js Viewer configuration snippet, spin.js, DOMPurify, pako, rough.js, client.min.js, a subset of the editor (Editor, EditorUi, Graph, Shapes, Actions, DrawioFile, Pages and others), the English UI strings, the default stylesheet and GraphViewer.js; ends by calling window.onDrawioViewerLoad if defined, otherwise GraphViewer.processElements() Pages that embed diagrams, see Embedding
js/viewer-static.min.js viewer.min.js plus shapes-14-6-5.min.js, mxStencilRegistry.allowEval = false and a service worker registration that only runs where Editor.enableServiceWorker is true The HTML that the editor exports or generates for embedding (unless DRAWIO_VIEWER_URL names another script), and pages that should not load shape files at runtime
js/integrate.min.js The full editor with the extensions, shapes and stencils, plus Integrate.js, which sets editor defaults for running inside another page and calls window.mxIntegrateCallback if defined (about 20 MB) Applications that load the editor into their own page instead of an iframe

The two viewer bundles differ from the committed files by design. The committed copies are built where the stencil XML is available, so they do not contain the stencil bundle and fetch stencil XML at runtime from STENCIL_PATH, which defaults to https://viewer.diagrams.net/stencils. The public build has no stencil XML, so it appends js/stencils.min.js to both viewer bundles (about 6.8 MB instead of 2.9 MB for viewer.min.js), and a viewer built this way renders all built-in stencils without requests to other hosts.

Files the build does not produce

These files are committed and used as they are. Do not edit the generated ones: your changes would be lost at the next update, and they cannot be rebuilt from this repository.

Files Origin
The bundles in js/elk/, js/mermaid/, js/plantuml/, js/gliffy/ and js/vsd/, js/libavoid-js/libavoid.min.js, and the MathJax bundle in math4/es5/ Generated, built outside this repository
js/stencils.min.js Generated from stencil XML that is not in this repository
mxgraph/mxClient.js Generated from mxgraph/src, see the next section
js/libavoid-js/libavoid-routing.js Hand-written source of the routing core, also used by drawio-mcp; see js/libavoid-js/README.md
js/deflate/pako.min.js, js/sanitizer/purify.min.js, js/cryptojs/aes.min.js, js/spin/spin.min.js, js/rough/rough.min.js, js/freehand/perfect-freehand.js, js/jszip/jszip.min.js, js/jquery/, js/simplepeer/, js/dropbox/, js/onedrive/OneDrive.js, js/orgchart/*.min.js Third-party libraries, copied in by hand. etc/dependencies/package.json records their versions so that dependency security alerts cover them

Updating a vendored library means replacing the file; there is no package manager. Libraries must be plain scripts that define globals (UMD or IIFE builds, not ES modules). etc/dependencies/dompurify-smoke.js is a smoke test for DOMPurify updates that runs the old and new versions against draw.io's sanitiser configuration; its header explains how to run it.

mxGraph: mxgraph/src and mxgraph/mxClient.js

There are two copies of the graph library:

  • mxgraph/src/ holds the sources. mxgraph/src/mxClient.js is a loader that writes a <script> tag for every source file (mxClient.include(...)). Development mode uses it.
  • mxgraph/mxClient.js is a single minified file with those includes inlined and the version filled in. The app target compiles this file into client.min.js.

The public build does not regenerate mxgraph/mxClient.js. A change under mxgraph/src therefore shows up with ?dev=1 but not in any bundle you build. Keep changes to graph behaviour in the editor layers instead: override the mx* prototype methods from js/grapheditor, js/diagramly or a plugin, which is how draw.io customises mxGraph itself (see Architecture).

Adding a source file

A new file has to be registered twice: once for development mode and once for the build. Order matters in both places, because files only see what was loaded before them.

  1. Development mode. Add an mxscript(...) line to js/diagramly/Devel.js after the files it depends on:

    mxscript(drawDevUrl + 'js/diagramly/MyFeature.js');

    Files under mxgraph/src are listed in mxgraph/src/mxClient.js instead, but see the limitation above.

  2. Build. Add a <file> element to the matching <jscomp> block in etc/build/build.xml:

    Kind of file Block(s) in the app target
    js/grapheditor/*.js The grapheditor.min.js block; also the viewer block (.tmp1.js, step 7) if the viewer needs it
    js/diagramly/sidebar/Sidebar-*.js The sidebar.min.js block
    js/diagramly/*.js for the editor The final application block (step 10) that becomes app.min.js, and the base.min.js block before it, which feeds atlas and integrate; also the viewer block if the viewer needs it
    Import and export code The extensions.min.js block
    A pre-minified third-party library A <filelist> in the <concat> that builds app.min.js (and base.min.js, and base-viewer.min.js if the viewer needs it), not a <jscomp> block

    For example, for a file the editor needs after App.js:

    <sources dir="${war.dir}/js/diagramly">
        <file name="App.js" />
        <file name="MyFeature.js" />
        <file name="Menus.js" />
        ...
  3. Run ant app and test both /?dev=1 and /.

Shape files are different: every .js file under shapes/ is picked up by merge automatically, and development mode loads them on demand through the mxStencilRegistry.libraries table in js/diagramly/Editor.js. See Shapes and stencils. A new sidebar palette also needs registering in the sidebar code, see Extending the editor.

The .war

ant war runs app and javac and zips the whole of src/main/webapp into build/draw.war. The archive contains:

  • every static file of the web application, including the development sources, templates, resources and images,
  • WEB-INF/web.xml, which maps the servlets,
  • WEB-INF/classes with the compiled servlets from src/main/server/java/com/mxgraph/online,
  • WEB-INF/lib with the servlet API and the libraries the servlets use,
  • placeholder files such as WEB-INF/google_client_id, which the OAuth servlets read their credentials from.

Note that war does not run atlas or integrate, so the archive contains whatever integrate.min.js is in js/.

The servlets use the javax.servlet API. Which endpoints they provide, and which endpoints the client calls that are not in the .war, is listed in Self-hosting.

For each release tag (v*) the war.yml workflow runs ant war and attaches draw.war to the GitHub release. The Docker image builds the same way from a clone of this repository.

Known limitations of the public build

  • No stencil XML. If any .xml file exists under src/main/webapp/stencils, merge tries to convert the stencil XML with a helper class that is not in this repository and fails with Could not find Xml2Js. Ship custom stencils as a custom library or as JavaScript shapes instead, see Shapes and stencils.
  • mxGraph sources are not rebuilt, see above.
  • The service worker is not regenerated. service-worker.js lists the precached files with checksums of the released bundles, and nothing in the public build updates it. See Self-hosting.
  • /embed.js is mapped in WEB-INF/web.xml to a servlet class that is not part of this repository, so requests to it fail in the .war.
  • Bundles built elsewhere (ELK, Mermaid, PlantUML, Gliffy, binary Visio, libavoid, MathJax) cannot be rebuilt from this repository; updated copies arrive with draw.io releases.

See also

Clone this wiki locally