告诉 typescript 编译 json 文件

Posted

技术标签:

【中文标题】告诉 typescript 编译 json 文件【英文标题】:tell typescript to compile json files 【发布时间】:2017-03-21 03:34:43 【问题描述】:

当我使用导入 json 文件时,打字稿编译器工作正常

const tasks = require('./tasks.json')

但是,当我运行tsc时,输出目录不包含tasks.json文件,导致运行时错误。

有没有办法告诉编译器它应该复制所有 json 文件,或者我应该手动将所有 json 文件复制/粘贴到 dist 目录中?

我的 tsc 编译器选项当前读取

  "compilerOptions": 
    "target": "es6",
    "module": "commonjs",
    "sourceMap": true,
    "noImplicitAny": true,
    "removeComments": false,
    "outDir": "./dist/",
    "sourceMap": true,
    "pretty": true,
    "noImplicitThis": true,
    "strictNullChecks": true,
    "sourceMap": true
  ,

谢谢!

【问题讨论】:

这似乎是 grunt/gulp 会做的事情,而不是编译器。 你不编译 json 你会有一个 gulp/grunt 任务将它移动到你的公共目录(wwwroot 等),我的建议只是将它保存在公共目录中,如果它只需要那里那么你就不需要移动任务了。 Gulp 或 grunt 可用于将 JSON 文件复制到您的 dist,但在运行时您仍然会遇到 require() 语句的问题(除非它实际上在节点中运行)。看我的回答。 【参考方案1】:

问题

对于想要复制所有 JSON 文件的人来说,在 TypeScript 中真的很难。即使使用"resolveJsonModule": truetsc 也只会复制.json 直接由import 引用的文件。

这里是一些示例代码,它想要做一个动态运行时require()。这只有在所有 JSON 文件都已复制到 dist/ 文件夹中时才有效,这是 tsc refuses 要做的。

// Works
import * as config from './config.default.json';

const env = process.env.NODE_ENV || 'development';

const envConfigFile = `./config.$env.json`;

// Does not work, because the file was not copied over
if (fs.existsSync(envConfigFile)) 
  const envConfig = require(envConfigFile);
  Object.assign(config, envConfig);

方案一:将 json 文件放在 src 树之外(推荐)

假设您有 /src//dist/ 文件夹,您可以将 JSON 文件保存在项目的 / 文件夹中。然后位于/src/config/load-config.ts 的脚本可以在运行时执行此操作:

const envConfig = require(`../../config.$env.json`);

// Or you could read manually without using require
const envConfigFile = path.join(__dirname, '..', '..', `config.$env.json`);
const envConfig = JSON.parse(fs.readFileSync(envConfigFile, 'utf-8'));

这是最简单的解决方案。您只需要确保必要的配置文件将在生产环境中到位。

其余的解决方案将处理您确实希望将配置文件保留在 src/ 文件夹中并让它们出现在您的 dist/ 文件夹中的情况。

解决方案 2:导入所有可能的文件

对于上面的例子,我们可以这样做:

import * as config from './config.default.json';
import * as testingConfig from './config.testing.json';
import * as stagingConfig from './config.staging.json';
import * as productionConfig from './config.production.json';

这应该会导致指定的 json 文件被复制到 dist/ 文件夹中,所以我们的 require() 现在应该可以工作了。

缺点:如果有人想添加一个新的.json 文件,那么他们还必须添加一个新的导入行。

方案3:单独复制json文件(推荐)

您可以在tsc 完成后添加cpy-clicopyfiles 步骤to your build process 将所有.json 文件复制到dist/ 文件夹中。

这假设您使用 npm run build 或类似的东西进行构建。

例如:

$ npm install --save-dev cpy-cli

// To copy just the json files, add this to package.json
"postbuild": "cpy --cwd=src --parents '**/*.json' ../dist/",

// Or to copy everything except TypeScript files
"postbuild": "cpy --cwd=src --parents '**/*' '!**/*.ts' ../dist/",

现在npm run build 应该运行tsc,然后运行cpy

缺点:需要额外的devDependency。您必须将此作为构建过程的一部分。

方案四:使用js文件代替json文件

或者,不要使用.json 文件。将它们移动到.js 文件中,并在您的tsconfig.json 中启用"allowJs": true。然后tsc 将为您复制文件。

您的新 .js 文件需要如下所示:module.exports = ... ;

我发现了这个想法recommended here。

注意:为了启用"allowJs": true,您可能需要添加"esModuleInterop": true"declaration": false,甚至可能需要添加"skipLibCheck": true。这取决于您现有的设置。

还有另一个问题(抱歉我没有测试这个):

如果tsc 的配置文件并非全部被其他文件静态引用,它们会转译它们吗?您的文件或其文件夹可能需要在 tsconfig.jsonfilesinclude 选项中明确引用。

解决方案5:使用ts文件而不是json文件

听起来很简单,但仍有一些问题需要考虑:

您的配置文件现在看起来像这样:const config = ... ; export default config;

请参阅上面关于 files / include 选项的说明。

如果您在运行时动态加载配置文件,请不要忘记它们已被转译为 .js 文件。所以不要去尝试require().ts文件,因为它们不会在那里!

如果有人想更改配置文件,他们应该进行全新的tsc 构建。他们可以修改dist 文件夹中的转译.js 文件,但应该避免这种情况,因为这些更改可能会被未来的构建覆盖。

测试

在尝试此操作时,请确保在构建之间清除您的dist/ 文件夹和tsconfig.tsbuildinfo 文件,以便正确测试该过程。 p>

tsc 并不总是清理dist/ 文件夹,有时它只是向其中添加新文件。因此,如果您不删除它们,早期实验遗留下来的旧文件可能会产生误导性结果!)

【讨论】:

解释得很漂亮! 谢谢,我采用了解决方案 1 :)【参考方案2】:

您始终可以使用打字稿代码获得项目的绝对路径。要做到这一点,只需通过 required 关键字而不是在 fs 模块的帮助下读取 JSON 文件。在文件路径中使用process.cwd() 访问打字稿项目目录:

import * as fs from 'fs';

const task: any = JSON.parse(fs.readFileSync(`$process.cwd()/tasks.json`).toString());

要使其正常工作,您可能需要将运行脚本更改为 node dist/src/index.js,在该路径中指定 dist 文件夹。

【讨论】:

【参考方案3】:

在tsconfig.json中,添加


  "compilerOptions": 
     "resolveJsonModule": true,
   ,
   "include": [
     "src/config/*.json"
  ]

请注意,它不会复制那些 json 文件,它们是 required。如果你需要动态 require 一些json 文件并需要将它们复制到dist,那么你需要从,例如,

return require("some.json") as YourType

return (await import("some.json")) as YourType

.

【讨论】:

抱歉,我的解决方案不完整。你能试试上面的看看吗?上述解决方案对我有用。 如果要包含所有子文件夹,可以使用"src/config/**/*.json" 这对我来说是最好的解决方案。注意:尽管父文件夹已经包含在我的包含中,但我必须明确地有一个等效的行 简单有效【参考方案4】:

您可以将其包含在您的构建脚本中 && ncp src/res build/res,将文件直接复制到您的 outDir

【讨论】:

【参考方案5】:

在 typescript 2.9+ 中,您可以直接使用 JSON 文件,它会自动复制到 dist 目录。

这是tsconfig.json,需要最少的配置:


    "compilerOptions": 
        "allowSyntheticDefaultImports": true,
        "esModuleInterop"             : true,
        "module"                      : "commonjs",
        "outDir"                      : "./dist",
        "resolveJsonModule"           : true,
        "target"                      : "es6"
    ,
    "exclude"        : [
        "node_modules"
    ]

然后就可以创建一个json文件了。


    "address": "127.0.0.1",
    "port"   : 8080

示例用法:

import config from './config.json';

class Main 

    public someMethod(): void 
        console.log(config.port);
    


new Main().someMethod();

如果您不使用 esModuleInterop 属性,您应该访问封装在默认字段中的 json 属性。 config.default.port.

【讨论】:

请注意"resolveJsonModule": true 只复制imported json 文件。它不会复制 all json 文件或 required json 文件。【参考方案6】:

当我使用导入 json 文件时,打字稿编译器工作正常

const tasks = require('./tasks.json')

只要您定义了全局 require() 函数,例如使用 node.d.ts,TypeScript 就不会抱怨这一点。使用香草设置,您实际上会get a compile error that require is not defined。

即使你告诉 TypeScript 有一个全局的 require 函数,它只是将它视为一个预期会返回某些内容的函数,它不会让编译器真正分析该函数需要什么 ("tasks.json") 和对该文件执行任何操作。这是Browserify 或Webpack 之类的工具的工作,它可以解析您的代码库以查找require 语句,并将几乎所有内容(JS、CSS、JSON、图像等)加载到运行时包中以进行分发。

更进一步,使用 TypeScript 2.0,您甚至可以使用 wildcard (*) module name declarations 告诉 TypeScript 编译器有关将由捆绑程序(Browserify 或 Webpack)解析和加载的模块路径模式:

declare module "*.json" 
    const value: any;
    export default value;

现在您可以使用 ES6 模块语法在 TypeScript 中导入 JSON:

import tasks from "./tasks.json";

这不会产生任何编译错误,并且会转译为 var tasks = require("./tasks.json") 之类的内容,并且您的捆绑程序将负责解析 require 语句并构建包含 JSON 内容的捆绑包。

【讨论】:

所以请明确一点,如果我们仍然使用 import tasks from "./tasks.json" 语法,我们仍然需要找到某种方法将 json 文件复制到 tsconfig.json 中的 outDir 目录中? @FrankerZ 正确。看看this answer 我认为 json-loader 在这里没有帮助,因为它希望 javascript 本身对 json 有正确的引用?在这种情况下,js 不知道 json 在哪里,我们正在复制它以将其提供给 js。

以上是关于告诉 typescript 编译 json 文件的主要内容,如果未能解决你的问题,请参考以下文章

tsconfig.json

如何将 package.json 导入 Typescript 文件而不将其包含在编译输出中?

typeScript的安装编译

TypeScript的配置文件 tsconfig.json

编译TypeScript(TypeScript转JavaScript)

为啥 TypeScript 编译器会忽略 tsconfig.json?