The typescript compiler works fine when I import a json file using
const tasks = require('./tasks.json')
However, when I run tsc
, the output directory does not contain no tasks.json
file, causing a runtime error.
Is there a way to tell the compiler that it should copy all json files, or should I manually copy/paste all my json files into the dist
directory ?
my tsc compilerOptions currently reads
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"sourceMap": true,
"noImplicitAny": true,
"removeComments": false,
"outDir": "./dist/",
"sourceMap": true,
"pretty": true,
"noImplicitThis": true,
"strictNullChecks": true,
"sourceMap": true
},
Thanks !
Another way of automating the TypeScript compilation is by using command line interface or the Command Prompt. The second way is pretty simple to achieve; we just need to write a single line to watch any changes in our TypeScript file.
As you know, TypeScript files can be compiled using the tsc <file name>. ts command.
For people wanting to copy all JSON files, it's really difficult in TypeScript. Even with "resolveJsonModule": true
, tsc
will only copy .json
files which are directly referenced by an import
.
Here is some example code that wants to do a dynamic runtime require()
. This can only work if all the JSON files have been copied into the dist/
folder, which tsc
refuses to do.
// 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);
}
Assuming you have /src/
and /dist/
folders, you could keep your JSON files in the project's /
folder. Then a script located at /src/config/load-config.ts
could do this at runtime:
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'));
This is the simplest solution. You just need to make sure the necessary config files will be in place in the production environment.
The remaining solutions will deal with the case when you really want to keep the config files in your src/
folder, and have them appear in your dist/
folder.
For the above example we could do:
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';
This should cause the specified json files to be copied into the dist/
folder, so our require()
should now work.
Disadvantage: If someone wants to add a new .json
file, then they must also add a new import line.
The tsc-hooks
plugin allows you to copy all files from the src tree to the dist tree, and optionally exclude some.
// Install it into your project
$ yarn add tsc-hooks --dev
// Configure your tsconfig.json
{
"compilerOptions": {
"outDir": "dist"
},
// This tells tsc to run the hook during/after building
"hooks": [ "copy-files" ]
// Process everything except .txt files
"include": [ "src/**/*" ],
"exclude": [ "src/**/*.txt" ],
// Alternatively, process only the specified filetypes
"include": [ "src/**/*.{ts,js,json}" ],
}
I found it tsc-hooks announced here.
Before tsc-hooks
, we could add a cpy-cli
or copyfiles
step to the npm build process to copy all .json
files into the dist/
folder, after tsc
has finished.
This assumes you do your builds with npm run build
or something similar.
For example:
$ 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/",
Now npm run build
should run tsc
, and afterwards run cpy
.
Disadvantages: It requires an extra devDependency
. And you must make this part of your build process.
Alternatively, don't use .json
files. Move them into .js
files instead, and enable "allowJs": true
in your tsconfig.json
. Then tsc
will copy the files over for you.
Your new .js
files will need to look like this: module.exports = { ... };
I found this idea recommended here.
Note: In order to enable
"allowJs": true
you might also need to add"esModuleInterop": true
and"declaration": false
, and maybe even"skipLibCheck": true
. It depends on your existing setup.
And there is one other concern (sorry I didn't test this):
tsc
transpile your config files if they are not all statically referenced by other files? Your files or their folders may need to be referenced explicitly in the files
or include
options of your tsconfig.json
.Sounds easy, but there are still some concerns to consider:
Your config files will now look something like this: const config = { ... }; export default config;
See the note above about files
/ include
options.
If you load the config files dynamically at runtime, don't forget they will have been transpiled into .js
files. So don't go trying to require()
.ts
files because they won't be there!
If someone wants to change a config file, they should do a whole new tsc
build. They could hack around with transpiled .js
files in the dist
folder, but this should be avoided because the changes may be overwritten by a future build.
When experimenting with this, please be sure to clear your dist/
folder and tsconfig.tsbuildinfo
file between builds, in order to properly test the process.
(tsc
does not always clean the dist/
folder, sometimes it just adds new files to it. So if you don't remove them, old files left over from earlier experiments may produce misleading results!)
In tsconfig.json, add
{
"compilerOptions": {
"resolveJsonModule": true,
},
"include": [
"src/config/*.json"
]
}
Notice that it won't copy those json
files which are require
d. If you need to dynamically require some json
files and need them to be copied to dist
, then you need to change from, for example,
return require("some.json") as YourType
to
return (await import("some.json")) as YourType
.
In typescript 2.9+ you can use JSON files directly and it automatically copied to dist directories.
This is tsconfig.json
with minimum needed configuration:
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"esModuleInterop" : true,
"module" : "commonjs",
"outDir" : "./dist",
"resolveJsonModule" : true,
"target" : "es6"
},
"exclude" : [
"node_modules"
]
}
Then you can create a json file.
{
"address": "127.0.0.1",
"port" : 8080
}
Sample usage:
import config from './config.json';
class Main {
public someMethod(): void {
console.log(config.port);
}
}
new Main().someMethod();
If you don't use esModuleInterop
property you should access your json properties encapsulated in default field. config.default.port
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With