Search code examples
javascripttypescriptduktape

How to use typescript modules with Duktape


I am trying to use JS as a scripting language in a game engine, with TypeScript on top. As runtime I use Duktape. I am a complete noob regarding JavaScript and currently I am trying to wrap my head around how I can enable a workflow where people can #include (require etc.) other files.

For testing purposes I started with two simple TypeScript files:

Foo.ts

declare function Print(text : string) : void;

export module Foo 
{
    export function GetFoo(): string 
    {
        return "Foo"
    }

    Print("Foo: " + GetFoo())
}

Bar.ts

import { Foo } from "./Foo"

declare function Print(text: string): void;

Print("Bar: " + Foo.GetFoo())

Now I put both through TypeScript's Transpile function (from typescriptServices.js) and get:

Foo.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Foo;
(function (Foo) {
    function GetFoo() {
        return "Foo";
    }
    Foo.GetFoo = GetFoo;
    Print("Foo: " + GetFoo());
})(Foo = exports.Foo || (exports.Foo = {}));

Bar.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Foo_1 = require("./Foo");
Print("Bar: " + Foo_1.Foo.GetFoo());

I then want to execute Bar.js through Duktape. I read this so my understanding is that there are multiple ways to make including other files work, but this one seems to make specifically the 'require' function work, so it sounds about right for the JS code that TypeScript generated for me. So I create a Duktape context, I call duk_module_duktape_init on it and I register a 'modSearch' function etc. BUT before it actually even gets to the 'require' part of Bar.js, Duktape already fails with the message "ReferenceError: identifier 'exports' undefined".

As mentioned before, I am really new to JS and TS and may have the wrong idea about this whole endeavor, so feel free to suggest entirely different approaches. All I care about is that users can split up code into multiple files, and somehow reference those files in such a way that tools such as VS Code can figure out those references and provide their functionality to develop code. I do not care whether I end up using 'require' or 'import' and whether I can use all TS features or just some sub-set.

Long story short, what am I doing wrong and how can I make this work?


Solution

  • Late last night I figured out what my problem was, I'll post it here in case anyone ever runs into a similar problem.

    The problem is that TypeScript generated code to set the '__esModule' property for both files. Only Foo.ts exported anything and was used in a 'require' call, but for Bar.ts the transpiled JS code also wants to access the 'exports' object.

    In Duktape, when you use duk_module_duktape_init to enable support for the 'require' function, it will generate an 'exports' object but ONLY once you go through an actual 'require()' call and only for the module to be loaded of course.

    The problem here is that already the top-level file (which is not loaded through a require-call) tries to access an 'exports' object which does not exist for this file.

    The solution is quite simply to create a dummy object, ie. by prepending 'var exports = {}' to the top-level script file.