告诉 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": true
,tsc
也只会复制.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-cli
或copyfiles
步骤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.json
的 files
或 include
选项中明确引用。
解决方案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
文件,它们是 require
d。如果你需要动态 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
只复制import
ed json 文件。它不会复制 all json 文件或 require
d 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 文件的主要内容,如果未能解决你的问题,请参考以下文章
如何将 package.json 导入 Typescript 文件而不将其包含在编译输出中?