问题 TypeScript是否为NodeJS模块访问提供显式公共API?


从我想要做的节点应用程序中:

var typeScript = require('typescript'); 

typeScript.compile('...')

我希望将编译器实现到构建系统但不能访问公共API(typescript.compile等),这是不可能的。

这是我想要做的更完整的示例,不过下面是针对LiveScript的,而不是TypeScript,在为Brunch.io构建系统编写的插件中使用:

LiveScript = require 'LiveScript'
sysPath = require 'path'

module.exports = class LiveScriptCompiler
  brunchPlugin: yes
  type: 'javascript'
  extension: 'ls'

  constructor: (@config) ->
    null

  compile: (data, path, callback) ->
    try
      result = LiveScript.compile data, bare: yes
    catch err
      error = err
    finally
      callback error, result

  include: [
    (sysPath.join __dirname, '..', 'vendor', 'prelude-browser-0.6.0.js')
  ]

好奇,如果有人找到了解决方法吗?

更新

我最终实现了我自己的解决方案,解决了上面和其他地方列出的各种问题。请参见 https://github.com/damassi/TypeScript-Watcher 了解更多信息和使用方法。


13011
2017-10-03 21:40


起源



答案:


这个有点hacky但它​​会工作。

就在昨天我才这样想,我正在查看他们的代码。如果从源代码中检查bin / typscript.js(这是一个非常大的文件,有近21k行代码),你会看到它创建了TypeScript.TypeScriptCompiler,然后你会发现这个DOES暴露了一种编译方式。

var compiler = new TypeScript.TypeScriptCompiler(outfile, errorfile, 
    new TypeScript.NullLogger(), settings);

现在,您需要一种简单的方法来公开它。要做到这一点,你将不得不修改他们的代码,这就是为什么这是hacky。为此,您可以通过添加以下内容来修改typescript.js:

module.exports = exports = TypeScript;

就在文件的末尾。

然后,您可以在模块的根目录中创建一个index.js文件(注意:在所有这些的本地范围内安装模块:“npm install typescript”),它会公开该对象。

exports.TypeScript = require("bin/typescript");

准备好了!现在您可以调用它并使用它编译代码。您可以在tsc.js文件中查看如何使用API​​进行编译。

我提前为可怕的代码道歉:

var fs = require("fs");
var TypeScript = require("typescript");
var path = "test.ts";
var pathout = "test.js";
var content = fs.readFileSync(path, "utf-8");
var fd = fs.openSync(pathout, 'w'); 
var outFile = { 
    Write: function (str) { 
        fs.writeSync(fd, str); 
    }, 
    WriteLine: function (str) {
    console.log(fd, str); 
        fs.writeSync(fd, str + '\r\n'); 
    }, 
    Close: function () { 
        fs.closeSync(fd); 
        fd = null; 
    } 
};
var createFile = function (path) { 
    function mkdirRecursiveSync(path) { 
        var stats = fs.statSync(path); 
        if(stats.isFile()) { 
            throw "\"" + path + "\" exists but isn't a directory."; 
        } else { 
            if(stats.isDirectory()) { 
                return; 
            } else { 
                mkdirRecursiveSync(_path.dirname(path)); 
                fs.mkdirSync(path, 509); 
            } 
        } 
    } 
    mkdirRecursiveSync(_path.dirname(path));
    console.log(path) 
    var fd = fs.openSync(path, 'w'); 
    return { 
        Write: function (str) { 
            fs.writeSync(fd, str); 
        }, 
        WriteLine: function (str) { 
            fs.writeSync(fd, str + '\r\n'); 
        }, 
        Close: function () { 
            fs.closeSync(fd); 
            fd = null; 
        } 
    }; 
};
var stderr = { 
    Write: function (str) { 
        process.stderr.write(str); 
    }, 
    WriteLine: function (str) { 
        process.stderr.write(str + '\n'); 
    }, 
    Close: function () { 
    } 
}
var compiler = new TypeScript.TypeScriptCompiler(outFile, outFile);
compiler.setErrorOutput(stderr);
compiler.addUnit(content, path);
compiler.typeCheck();
compiler.emit(false, createFile);
outFile.Close();

出于某种原因,编写代码的人真的是C#的粉丝并继续使用名为WriteLine,Close和Write的方法,这些方法实际上只是包装器。您可以获得必须添加此功能的开销,但您必须修改模块中的大量代码,这是不值得的。我认为最好有一个类来扩展(或者如果你还在JS上,继承原型)并让它为你做,让它干。

非常好的是,如果你想翻译500个TypeScript文件并将它们全部放入一个.js文件中,你可以调用compiler.addUnit(anothercontent,anotherpath); 500次,然后看到它都进入一个文件:)

专注于更好的事情:如果你检查tsc.js代码,你会发现一个批处理编译器类。如果你想在构建过程中使用它,那么使用类似它的更强大的东西可能会更好。它提供观看文件等。

浏览过代码后,我想我只会向开发团队提交一张票,要求他们提供更清晰的API。

注意:此处的所有文件读取都以同步方式完成。就性能而言,这是糟糕的,非常糟糕的。我不确切地知道你打算做什么,但是如果可能的话,我不能建议你找到一种让异步的方法。


8
2017-10-03 22:52



很好的答案。期待你的榜样。 - cnp
完成!你可以玩它,只需更改文件名(它是test.js和test.ts)并给它其他文件名,它将工作。它现在的方式,错误输出将被释放到process.stderr,但你可以用它做任何你想做的事情。您还可以将处理函数添加为compiler.setErrorCallback,这样您就可以生成构建错误报告! - Mamsaac


答案:


这个有点hacky但它​​会工作。

就在昨天我才这样想,我正在查看他们的代码。如果从源代码中检查bin / typscript.js(这是一个非常大的文件,有近21k行代码),你会看到它创建了TypeScript.TypeScriptCompiler,然后你会发现这个DOES暴露了一种编译方式。

var compiler = new TypeScript.TypeScriptCompiler(outfile, errorfile, 
    new TypeScript.NullLogger(), settings);

现在,您需要一种简单的方法来公开它。要做到这一点,你将不得不修改他们的代码,这就是为什么这是hacky。为此,您可以通过添加以下内容来修改typescript.js:

module.exports = exports = TypeScript;

就在文件的末尾。

然后,您可以在模块的根目录中创建一个index.js文件(注意:在所有这些的本地范围内安装模块:“npm install typescript”),它会公开该对象。

exports.TypeScript = require("bin/typescript");

准备好了!现在您可以调用它并使用它编译代码。您可以在tsc.js文件中查看如何使用API​​进行编译。

我提前为可怕的代码道歉:

var fs = require("fs");
var TypeScript = require("typescript");
var path = "test.ts";
var pathout = "test.js";
var content = fs.readFileSync(path, "utf-8");
var fd = fs.openSync(pathout, 'w'); 
var outFile = { 
    Write: function (str) { 
        fs.writeSync(fd, str); 
    }, 
    WriteLine: function (str) {
    console.log(fd, str); 
        fs.writeSync(fd, str + '\r\n'); 
    }, 
    Close: function () { 
        fs.closeSync(fd); 
        fd = null; 
    } 
};
var createFile = function (path) { 
    function mkdirRecursiveSync(path) { 
        var stats = fs.statSync(path); 
        if(stats.isFile()) { 
            throw "\"" + path + "\" exists but isn't a directory."; 
        } else { 
            if(stats.isDirectory()) { 
                return; 
            } else { 
                mkdirRecursiveSync(_path.dirname(path)); 
                fs.mkdirSync(path, 509); 
            } 
        } 
    } 
    mkdirRecursiveSync(_path.dirname(path));
    console.log(path) 
    var fd = fs.openSync(path, 'w'); 
    return { 
        Write: function (str) { 
            fs.writeSync(fd, str); 
        }, 
        WriteLine: function (str) { 
            fs.writeSync(fd, str + '\r\n'); 
        }, 
        Close: function () { 
            fs.closeSync(fd); 
            fd = null; 
        } 
    }; 
};
var stderr = { 
    Write: function (str) { 
        process.stderr.write(str); 
    }, 
    WriteLine: function (str) { 
        process.stderr.write(str + '\n'); 
    }, 
    Close: function () { 
    } 
}
var compiler = new TypeScript.TypeScriptCompiler(outFile, outFile);
compiler.setErrorOutput(stderr);
compiler.addUnit(content, path);
compiler.typeCheck();
compiler.emit(false, createFile);
outFile.Close();

出于某种原因,编写代码的人真的是C#的粉丝并继续使用名为WriteLine,Close和Write的方法,这些方法实际上只是包装器。您可以获得必须添加此功能的开销,但您必须修改模块中的大量代码,这是不值得的。我认为最好有一个类来扩展(或者如果你还在JS上,继承原型)并让它为你做,让它干。

非常好的是,如果你想翻译500个TypeScript文件并将它们全部放入一个.js文件中,你可以调用compiler.addUnit(anothercontent,anotherpath); 500次,然后看到它都进入一个文件:)

专注于更好的事情:如果你检查tsc.js代码,你会发现一个批处理编译器类。如果你想在构建过程中使用它,那么使用类似它的更强大的东西可能会更好。它提供观看文件等。

浏览过代码后,我想我只会向开发团队提交一张票,要求他们提供更清晰的API。

注意:此处的所有文件读取都以同步方式完成。就性能而言,这是糟糕的,非常糟糕的。我不确切地知道你打算做什么,但是如果可能的话,我不能建议你找到一种让异步的方法。


8
2017-10-03 22:52



很好的答案。期待你的榜样。 - cnp
完成!你可以玩它,只需更改文件名(它是test.js和test.ts)并给它其他文件名,它将工作。它现在的方式,错误输出将被释放到process.stderr,但你可以用它做任何你想做的事情。您还可以将处理函数添加为compiler.setErrorCallback,这样您就可以生成构建错误报告! - Mamsaac


目前,仅通过require和调用compile来实现编译是不可能的。如果你可以看看harness.ts有一个编译器模块提供了一个相当简单的方法,但我建议你从外部调用tsc。

///<reference path='node.d.ts'/>
import exec = module('child_process');

var child = exec.exec('tsc foo.ts',
  function (error, stdout, stderr) {
    if (error !== null) {
      console.log('exec error: ' + error);
    }
});

我相信这会成功。


2
2017-10-03 22:01



真棒。会做。 - cnp
你可能想删除这个答案 - Olivier Refalo


检查 这个github项目 通过niutech,它可以动态地将TypeScript代码转换为JS代码 在浏览器中,但我想可以很容易地修改它以在node.js中工作。


1
2017-10-07 06:41



谢谢你提到我的项目! - niutech


好需要 如果您只想执行/访问打字稿文件,可以帮助您实现这一目标。

它允许你需要()打字稿文件 - 不需要预编译 - 以及一堆其他文件格式(coffeescript,clojurescript,yaml,xml等)

require('better-require')();
var myModule = require('./mymodule.ts');

披露:我写的更好 - 要求。


1
2017-11-01 15:06



很棒的工具 - 我现在试着尝试将它修补到Brunch.io汇编程序中。 - cnp
谢谢!随意拉请求;)这是非常简单的。 - Olivier Lalonde


你可以尝试一下 https://github.com/sinclairzx81/typescript.api。这个项目执行require()扩展,但也有一些手动编译ts源的功能。应该可以用它创建一个自动构建系统。

注意它是基于typescript 0.9编译器构建的,所以你可能会或者可能没有成功编译0.8.3源代码,给出了语言的各种更新。


1
2018-05-13 11:03