Exportera TypeScript-klass till webbläsarfönster och Node.js utan bundler (browserify eller webpack)


Jag har följande exempelklass i TypeScript:

export default class Greeter {
    greeting: string;

    constructor(message: string) {
        this.greeting = message;
    }

    greet() {
        return "Hello, " + this.greeting;
    }
}

Det använder följande tsconfig.json:

{
    "compilerOptions": {
        "module": "umd",
        "target": "es5",
        "noImplicitAny": true,
        "removeComments": false,
        "preserveConstEnums": true,
        "suppressImplicitAnyIndexErrors": true
    },
    "files" :[
        "example.ts"
    ]
}

Genom att använda transpileraren genererar den följande JavaScript:

(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var Greeter = (function () {
        function Greeter(message) {
            this.greeting = message;
        }
        Greeter.prototype.greet = function () {
            return "Hello, " + this.greeting;
        };
        return Greeter;
    }());
    exports.Greeter = Greeter;
});

Jag har testat många saker med olika moduler (umd, commonjs), men jag kan inte få klassen att fungera i Node.js och webbläsaren samtidigt med samma kod (använder require som fungerar med Angular, React och i webbläsarens Window-objekt). Jag försöker uppnå något liknande:

// With Node.js, Angular, React etc

let Greeter = require("Greeter");
let instanceGreeter = new Greeter("My Name");
instanceGreeter.greet();

// And that works in the browser too with the same codebase

var instanceGreeter = new Greeter("My Browser Name");
instanceGreeter.greet();

Är detta möjligt genom att följa någon standard eller måste jag exportera det med min egen kod (utan att använda modulalternativet)?

Svar #1

Efter mycket forskning kunde jag inte hitta något som matchade mina behov utan ett trick. Jag slutade med att skapa 2 tsconfig.json-filer (en för varje typ t.ex. module och window) som kommer att generera 2 JavaScript-filer. En är avsedd att användas i webbläsaren utan att använda någon typ av bundler som Webpack, Browserify etc. och den andra fungerar (eller borde åtminstone) med import, require etc.

Nackdelen med detta tillvägagångssätt är att du behöver ta bort nyckelordet export default i din klass om du vill kompilera till versionen av fönstret t.ex :

// If willing to export to window remove `export default` e.g 
// class Greeter {
// instead of 
// export default class Greeter {
export default class Greeter {
    greeting: string;

    constructor(message: string) {
        this.greeting = message;
    }

    greet() {
        return "Hello, " + this.greeting;
    }
}   

Eftersom det bara kan finnas en tsconfig.json-fil i varje katalog, skapade jag mappen build-instructions med 2 mappar inuti, nämligen commonjs och window:

build-instructions
├───commonjs
|   └── tsconfig.json
├───window
    └── tsconfig.json

Filen commonjs/tsconfig.json (som fungerar för moduler som require, import etc) ser ut så här:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "outDir":"../../build"
    },
    "files" :[
        "../yourtypescriptfile.ts"
    ]
}

Och fönstret (som fungerar i webbläsaren) ser ut så här:

{
    "compilerOptions": {
        "module": "none",
        "target": "es5",
        "outFile":"../../build/yourlibrary.window.js"
    },
    "files" :[
        "../yourtypescriptfile.ts"
    ]
}

När du kör npm-kommandot i projektets rotmapp (för att transpila TypeScript) med:

For CommonJS
tsc --p ./path-to-folder/commonjs

For Window
tsc --p ./path-to-folder/window

Båda kommer att skapa en JavaScript-fil kompatibel med alla typer, för webbläsaren med ett resultat som:

var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
}());

Och för modulen (beroende på vilken modul du använder som umd, commonjs eller amd):

define(["require", "exports"], function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    // If willing to export to window remove `export default` e.g 
    // class Greeter {
    // instead of 
    // export default class Greeter {
    var Greeter = (function () {
        function Greeter(message) {
            this.greeting = message;
        }
        Greeter.prototype.greet = function () {
            return "Hello, " + this.greeting;
        };
        return Greeter;
    }());
    exports.default = Greeter;
});