Skip to content

Latest commit

 

History

History
225 lines (160 loc) · 5.96 KB

File metadata and controls

225 lines (160 loc) · 5.96 KB

Back to README · API Reference →

Getting Started

This guide walks you from zero to running JavaScript code from a Delphi application for the first time.


Contents

  1. Prerequisites
  2. Getting the DLL
  3. Integrating into a Delphi Project
  4. First Run
  5. Running the Examples

Prerequisites

For building the DLL from source

Component Requirement Note
Windows 10 / Server 2016 x64 Tier 1 target
Visual Studio 2022 ≥ 17.4 or 2026 Workload «Desktop development with C++»
ClangCL VS component VC.Llvm.Clang + VC.Llvm.ClangToolset — required with Node.js 24+
Python 3.14+ Must be on PATH
Rust / cargo stable x86_64-pc-windows-msvc Required with Node.js 26 (Temporal API)
NASM Not required Build uses openssl-no-asm

Installing Rust if not already installed:

winget install Rustlang.Rustup
rustup target add x86_64-pc-windows-msvc

For using the DLL in a Delphi project

Component Requirement
RAD Studio 12 (Delphi 12 Athens) or newer
FPC ≥ 3.2 Win64 (alternative)
npm Any current version — only needed for examples using INodeNpm

Getting the DLL

Option A — build from source (recommended)

# 1. Clone the repository
git clone <url> D:\projects\externals\libnode
cd D:\projects\externals\libnode

# 2. Build Node.js static libraries (30–60 min on first build)
.\scripts\build_node_static.bat

# 3. Build and verify the DLL
.\scripts\BuildHostDll.ps1

After a successful build: build\dll\Release\DelphiLibNodeJS.dll (~100 MB).

For more details on building, see Build from Source.

Option B — use a pre-built DLL

Copy delphiLibNodeJS.dll to the directory containing your .exe.


Integrating into a Delphi Project

Step 1 — copy files

delphiLibNodeJS.dll     → alongside your .exe
source\DelphiLibNodeJS.pas  → into the Delphi search path

Step 2 — add unit to uses clause

uses
  Windows, SysUtils,
  delphiLibNodeJS;

Step 3 — configure search path

In project options: Search path → add $(ProjectDir)\..\..\delphi (or the path to delphiLibNodeJS.pas).


First Run

A minimal example — execute JavaScript and print the result:

program HelloNodeJS;

{$APPTYPE CONSOLE}
uses
  SysUtils, delphiLibNodeJS;

var
  Factory : INodeFactory;
  Runtime : INodeRuntime;
  Log     : INodeLog;
  Cfg     : TDNRuntimeConfig;
  Rec     : TDNLogRecord;
begin
  OleCheck(DN_CreateFactory(nil, Factory));

  FillChar(Cfg, SizeOf(Cfg), 0);
  Cfg.size := SizeOf(Cfg);
  OleCheck(Factory.CreateRuntime(@Cfg, Runtime));

  Log := Runtime as INodeLog;
  OleCheck(Runtime.Start);

  Runtime.RunScriptText(
    'const v = process.versions.node;' +
    'console.log("[hello] Node.js " + v + " running inside Delphi!");',
    nil);

  Runtime.Stop;
  Runtime.Join(5000, nil);

  // Read the log
  while Log.Poll(@Rec) = S_OK do
    Writeln(string(Rec.message_utf8));

  // Runtime, Log, Factory are released automatically (Delphi ARC)
end.

Expected output:

[hello] Node.js 26.x.x running inside Delphi!

Important: always set size := SizeOf(...) on configuration structures before passing them — this is a mandatory API convention.


Error Handling Template

var
  Factory : INodeFactory;
  Runtime : INodeRuntime;
  Log     : INodeLog;
  Cfg     : TDNRuntimeConfig;
begin
  OleCheck(DN_CreateFactory(nil, Factory));
  try
    FillChar(Cfg, SizeOf(Cfg), 0);
    Cfg.size := SizeOf(Cfg);
    OleCheck(Factory.CreateRuntime(@Cfg, Runtime));
    try
      Log := Runtime as INodeLog;
      OleCheck(Runtime.Start);
      // ... work with Runtime ...
    finally
      Log := nil;          // important: derived interfaces first
      Runtime.Stop;
      Runtime.Join(5000, nil);
      Runtime := nil;
    end;
  finally
    Factory := nil;
  end;
end;

Release order: Log → Npm → Runtime → Factory. Violating this order may cause an ACCESS_VIOLATION during finalization.


Limitations and Known Issues

Bootstrap and ESM Entry Points

Bootstrap injection (setting up globalThis.bridge, __dn_host_log, uncaughtException/unhandledRejection handlers) is not performed for inline ESM entry points — that is, when a string of the form data:text/javascript,... is passed in DN_RUNTIME_CONFIG.resource_name.

Consequence: globalThis.bridge and __dn_host_log will be undefined in such entry points. Bridge calls (bridge.ns.method()) will throw an error.

Workaround: Use a file-based ESM (file: URL) or a CJS entry point. Bootstrap is correctly injected for:

  • file-based CJS (.cjs, .js in a CJS package),
  • file-based ESM (file:///.../entry.mjs),
  • inline CJS (data:text/javascript,require(...) with CJS semantics).

This is a V1 limitation. Bootstrap support for inline ESM is planned for a future release.


Running the Examples

The repository includes 20 ready-to-run examples (00–19):

# Build all examples
.\examples\BuildExamples.ps1

# Run a few to verify
.\examples\00_basic\Win64\Release\ExBasic.exe                  # DLL smoke test
.\examples\02_bidirectional\Win64\Release\ExBidirectional.exe  # bidirectional bridge
.\examples\03_pipeline\Win64\Release\ExPipeline.exe            # ESM + top-level await
.\examples\07_bridge\Win64\Release\ExBridge.exe                # INodeBridge Direction A+B
.\examples\11_embedded_npm\Win64\Release\ExEmbeddedNpm.exe     # embedded npm (Mode C)

Full descriptions of all examples — see Examples.


See Also