TypeScript编译为单个文件

114

我正在使用TS 1.7,并尝试将我的项目编译成一个大文件,以便我可以在我的html文件中包含它。

我的项目结构如下:

-build // Build directory
-src // source root
--main.ts // my "Main" file that uses the imports my outer files
--subDirectories with more ts files.
-package.json
-tsconfig.json

我的 tsconfig 文件如下:

 {
  "compilerOptions": {
    "module":"amd",
    "target": "ES5",
    "removeComments": true,
    "preserveConstEnums": true,
    "outDir": "./build",
    "outFile":"./build/build.js",
    "sourceRoot": "./src/",
    "rootDir": "./src/",
    "sourceMap": true
  }
}
当我构建我的项目时,我期望build.js文件是从我的源代码编译而成的一个大文件。 但是这个build.js文件是空的,所有我的文件都被编译成了js文件。
我的每个TS文件看起来都有点像这样。
import {blabla} from "../../bla/blas";

export default class bar implements someThing {
    private variable : string;
}

我做错了什么?


我最喜欢的实现方式是使用 esbuild。它是用 Go 编写的,因此比 TSC 快得多,比 webpack 快 100 倍。请参见 https://esbuild.github.io/。它支持 outFile 和 outDir 选项。 - José Mancharo
5个回答

82

选项1 - 如果您不使用模块

如果您的代码只包含常规的Typescript,没有模块(import/export),则只需将参数outfile添加到您的tsconfig.json中即可。

// tsconfig.json
{
    "compilerOptions": {
        "lib": ["es5", "es6", "dom"],
        "rootDir": "./src/",
        "outFile": "./build/build.js"
    }
}

但是这个选项有一些限制。

如果出现以下错误:

Cannot compile modules using option 'outFile' unless the '--module' flag is 'amd' or 'system'

请尝试下面的“选项2”。

选项2 - 使用模块加载器

如果您正在使用模块(导入/导出),则需要使用模块加载器在浏览器中运行编译后的脚本。

当您将代码编译为单个文件时(使用 outFile),TypeScript 原生支持编译成 amdsystem 模块加载器。

在 tsconfig 中,您需要设置 moduleamdsystem

// tsconfig.json
{
    "compilerOptions": {
        "module": "AMD",
        "lib": ["es5", "es6", "dom"],
        "rootDir": "./src/",
        "outFile": "./build/build.js"
    }
}

如果您选择使用AMD,您需要使用require.js运行时。AMD需要一个AMD加载器,require.js是最受欢迎的选项(https://requirejs.org/)。

如果您选择使用System,则需要使用SystemJS模块加载器(https://github.com/systemjs/systemjs)。

选项3-使用模块捆绑器/构建工具

可能最好的选择是使用模块捆绑器/构建工具,如Webpack。

Webpack将把所有TypeScript文件编译成一个单独的JavaScript捆绑包。

因此,您将使用webpack来进行编译,而不是tsc

首先安装webpack,webpack-clits-loader:

npm install --save-dev webpack webpack-cli typescript ts-loader
如果您正在使用Webpack与TypeScript,则最好使用commonjsmodule
// tsconfig.json
{
    "compilerOptions": {
        "module": "commonjs",
        "lib": ["es5", "es6", "dom"],
        "rootDir": "src"
    }
}

Webpack webpack.config.js 示例:

//webpack.config.js
const path = require('path');

module.exports = {
  mode: "development",
  devtool: "inline-source-map",
  entry: {
    main: "./src/YourEntryFile.ts",
  },
  output: {
    path: path.resolve(__dirname, './build'),
    filename: "[name]-bundle.js" // <--- Will be compiled to this single file
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
  },
  module: {
    rules: [
      { 
        test: /\.tsx?$/,
        loader: "ts-loader"
      }
    ]
  }
};

现在,编译代码时,不要使用tsc命令,而应该使用webpack命令。

package.json示例:

{
  "name": "yourProject",
  "version": "0.1.0",
  "private": true,
  "description": "",
  "scripts": {
    "build": "webpack" // <---- compile ts to a single file
  },
  "devDependencies": {
    "ts-loader": "^8.0.2",
    "typescript": "^3.9.7",
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12"
  }
}

Webpack 的 TypeScript 文档

最后,使用以下命令编译所有文件(最好是在监视模式下):

npx webpack -w

4
你的解决方案不起作用,我收到了“除非'--module'标志为 'amd'或 'system',否则无法使用选项 'outFile' 编译模块”的错误信息。 - Alkis Mavridis
@AlkisMavridis 我也一样。你找到解决方法了吗? - 1valdis
2
@AlkisMavridis,如果你在使用模块,我已经更新了我的答案,并提供了其他将多个文件编译成单个文件的选项。 - Daniel Barral
1
不要忘记使用 npm i webpack-cli 安装 CLI,并使用 npx webpack -w 命令以整洁的方式运行 Webpack 的观察模式。 - Philippe Fanaro
最后一个文件是哪个,在哪里将“scripts.build”放入“webpack”中? - JohnF
1
@JohnF 那个文件是“package.json”,位于根目录下。我已更新答案。https://docs.npmjs.com/creating-a-package-json-file - Daniel Barral

56

这将在TypeScript 1.8中实现。使用该版本时,当 moduleamdsystem时,outFile选项将起作用。

目前该功能已在TypeScript的开发版本中可用。要安装,请运行:

$ npm install -g typescript@next

对于之前的版本,即使不明显,模块(module)输出文件(outFile)选项也不能一起使用。

您可以查看此问题以获取更多详细信息。


如果您想在低于1.8的版本中输出单个文件,则不能在tsconfig.json中使用模块(module)选项。相反,您必须使用模块关键字(module keyword)创建名称空间。

您的tsconfig.json文件应该像这样:

{
  "compilerOptions": {
    "target": "ES5",
    "removeComments": true,
    "preserveConstEnums": true,
    "outFile": "./build/build.js",
    "sourceRoot": "./src/",
    "rootDir": "./src/",
    "sourceMap": true
  }
}

你的TS文件应该看起来像这样:

module SomeModule {
  export class RaceTrack {
    constructor(private host: Element) {
      host.appendChild(document.createElement("canvas"));
    }
  }
}

你将不再使用import语句,而是必须通过命名空间引用导入。

window.addEventListener("load", (ev: Event) => {
  var racetrack = new SomeModule.RaceTrack(document.getElementById("content"));
});

1
你指出的问题似乎已经解决了,而且 https://github.com/Microsoft/TypeScript/wiki/Compiler-Options 建议使用 --module 标志来配合 --outFile 标志使用。 - David Limkys
确实,但似乎没有起作用。 :( 也许在 GitHub 上提出问题? - toskv
我刚刚注意到这个。看起来我提到的问题的修复将在1.8版本中可用。也许现在可以使用typescript@next获取最新版本?https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes - toskv
3
我已经编辑了答案,包括@next部分,这样更清晰明了。 :) - toskv
62
使用 TypeScript 3.x 是否有更好的方法来实现这个? - vitaly-t
显示剩余9条评论

25

最简单的方式

您可以使用一个名为 ncc 的自定义工具来完成此操作,该工具由 Vercel 开发和维护。以下是他们在 create-next-app 中如何使用它:

ncc build ./index.ts -w -o dist/

使用 yarn 安装:

yarn add @vercel/ncc

或者使用npm:

npm install @vercel/ncc

这会创建一个可以直接在浏览器中运行的代码吗? 我的浏览器显示“__dirname”未定义。 - rambi
我不确定,但根据你分享的内容,答案似乎是否定的。__dirname是一个特定于node.js的环境变量。 - Paul Razvan Berg
ncc 适用于 Node.js 项目,例如如果您想要将数据库种子脚本从 TypeScript 转换为 JavaScript。通常这些脚本以 "手写" 的方式用 JavaScript 编写,但使用 ncc,您可以重用已经在您的应用程序中编写的 TypeScript 代码。 - Eric Burel
1
@rambi,你可以通过一个小技巧在浏览器中运行它。在你的HTML文件中,在ncc输出之前的某个地方添加var __dirname = "", module = {}。最好在单独的脚本标签中完成这个操作。 - Matthew Ratzloff

1

我也需要将多个带有导入和导出的 TypeScript 文件编译成单个 JS 文件,并找到了解决方案。

  1. 使用此 tsconfig 生成一个带有 System 模块的单个 JS 文件。此 tsconfig 应放置在您的 TypeScript 文件所在的位置。运行 tsc 以生成单个 js 文件
{
  "compilerOptions" : {
      "module" : "System",
      "declaration" : false,
      "baseUrl" : "./",
      "outFile" : "../libs/bundle.js",
      "alwaysStrict" : true,
      "removeComments" : true,
  },
  "include": [
      "./**/*.ts"
  ],
  "exclude": [
      "./out.d.ts"
  ]
}

  • 在您的index.html文件中,您需要包含system.js捆绑JS文件(位于https://github.com/systemjs/systemjs/blob/main/dist/system.js)。此外,您还需要将systemjs额外命名支持(位于https://github.com/systemjs/systemjs/blob/main/dist/extras/named-register.js)附加到system.js中。

  • 在您的index.html文件中包含编译后的bundle.js文件。

  • 包含一个脚本,导入注册表中找到的所有命名导入。

  • (步骤2-4如下所示)

    <html>
       <head>
        <script src = "libs/system.js"></script>
        <script src = "libs/bundle.js"></script>
        <script>
          Object.keys(System.registerRegistry).forEach((key) => System.import(key))
        </script>
      </head>
    </html>
    
    

    0

    当我需要类似打包我的Lambda函数的东西时,我找到了这个问题。问题是,我只想将我的模块捆绑成单个文件,并从我的自己的代码中包含它们的导入,而不是node_modules。

    最终我使用了esbuild解决了我的问题:

    esbuild ./src/lambda/** --outdir=./dist/lambda --bundle --platform=node --target=node18 --packages=external
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接