Typescript compiler --copy-files option to copy non-typescript files to dist folder
marcospgp opened this issue ยท 63 comments
Search Terms
typescript tsc copy files
Suggestion
The typescript compiler should have an option to copy non-compiled files, similarly to Babel: https://babeljs.io/docs/en/babel-cli#copy-files
Use Cases
Almost all use cases where typescript files are compiled into a different folder will require bringing along other non-typescript files.
For the frontend, usually there already is a build system in place so this is trivial.
In the backend however, there is rarely a need for a build system, and thus developers often go out of their way to set up a system where non-typescript files are watched for changes and copied.
Existing npm packages for this kind of functionality are all poorly maintained and unsafe. Currently my only option is either copying all files on any change or writing a custom script.
Examples
tsc -w --copy-files
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
If no one else has been able to write a good file copy script, I don't think we'll be the first.
Anyway explicit non-goal 4 is:
Provide an end-to-end build pipeline
This is a great idea. Especially for those who want have minimum build steps (e.g. just tsc
) for a prototype or something
This would be really handy!
For those who need this feature, you can use copyfiles to implement it.
https://quangphamsoft.wordpress.com/2017/05/23/typescript-compiler-copy-html/
This is a great idea. Especially for those who want have minimum build steps (e.g. just
tsc
) for a prototype or something
add script{ scripts: { copy-files: "copyfiles -u 1 src/**/*.html src/**/*.js lib"}}
using copyfiles
and add it to postbuild
script
For example my scripts looks like this
build: "yarn run clean && tsc",
"postbuild": "ef-tspm && yarn run copy-files",
"clean": "rimraf lib/",
"prepare": "yarn run build",
"copy-files": "copyfiles -u 1 src/**/*.js lib"
honestly, I have just spent hours and hours on a very weird problem just to realize that the reason was that copyfiles does not work the same way under linux and windows.
Therefore, I would not recommend copyfiles especially if your build script is run as part of a docker build (under linux).
=== update =====
Well, after further testing, it seems that adding double quotes should do the trick (hopefully):
"copy-files": "copyfiles -u 1 \"src/**/*.js\" lib"
My version:
{
"scripts": {
"prebuild": "del lib/",
"build": "tsc",
"postbuild": "cpy '**/*' '!**/*.ts' ../lib/ --cwd=src/ --no-overwrite --parents"
},
"devDependencies": {
"cpy-cli": "^2.0.0",
"del-cli": "^3.0.0"
}
}
@RyanCavanaugh babel-cli provides this ability: https://babeljs.io/docs/en/babel-cli#copy-files
So why not TypeScript... ๐
tsc -w --copy-files
would end up copying all kinds of files (.md, etc.) you don't need/want in your /dist. I think it would be better for tsconfig to allow specifying which files you want to copy.
@RyanCavanaugh: Please please reopen this issue: The discussion here is absolutely misleading, putting this feature in the context of "build-piplines"... However what it is really about is "be polite, keep something in the state you encountered on arrival and dont steal stuff".
We do not want build pipelines we want to TS to keep our code directory as it was and do not mess this code/state up. The contents of any directory has been created under much thought. As already mentioned, especially on the serverside you might have other code-snippets, config and stuff in non .ts endings that you need colocated with your code. And deleting these files (by not copying them) is the worst DX you can imagine of. TS (and the transpilation process) should behave as I would have written directly a JS files instead of an TS file and keep the directory tree completely intact as it was.
Please include an option (as marcospgp has greatly described) as to simply "keep the state as TS encountered it", when using "outDir": "./dist"
. Over 50 Votes in half a year speaks for itself! Thanks very much.
If no one else has been able to write a good file copy script, I don't think we'll be the first.
Another thing to consider. Although I could write a build step to do this for me, the fact that TypeScript already has the list of projects where I need this to happen (via project references), means we'll now have to maintain this list in two places.
I ended up creating a tscopy
command for use within our company monorepo to copy all non JavaScript / TypeScript assets for any project references in the given tsconfig.json
:
include
directives, and assumes that you follow the convention of putting all of your source code within src
, like we do.
Another use case, is in a mixed source environment. I want my javascript source code to be "transpiled" (the reason I use quotes, is that this may only be a nop / straight copy, or it could convert from ES?? to requested target)
to the destination.
This SHOULD BE (imho) in the purview of the tsc compiler.
I have found that the "declaration" compiler option excludes me from this scenario.
Yes, because I would still like to output the *.d.ts files. However if you use the allowJs:true, you may not also use the declaration: true.
However if you use the allowJs:true, you may not also use the declaration: true.
@redevill, this was fixed in TypeScript 3.7.
OH!! sweet - Missed that part, I'm on the previous version. Thank you!
has this been resolved?
@naufalkhalid - if you are looking to have things like assets *.jpg files copied to the dist folder, my understanding is that the compiler will not do it - and the OP feature request remains unresolved. The argument above, is to keep the responsibility of the compiler strictly to compilation. Package assembly, to be delegated as a separate responsibility, to some other software lib.
In my special case I wanted Javascript files copied to the dist folder. In this case you can tell the compiler to include javascript files in your transpile process, and they will be copied to the dist folder as part of this process. (compiling javascript as well as typescript is part of the transpilers job)
If TS is unwilling to implement an option like this is there any sort of hook that could be used at the end of TS running to activate a copy script? Perhaps that could be an acceptable solution where TS doesn't do the copying itself but provides a way to hook into it post run to allow the dev to do whatever else they please.
I have seen a several implementations (not hooks) but build scripts where such copy procedures are bundled together with the Compilation request... Gulp, Grunt... or even script section of the package.json can be used to launch your own JS to perform these actions in a "postMyScriptNameHere" script.
For archeological purposes, I'm posting here a dirty file scripts/tsc.ts which runs both tsc (even ttsc actually due to another strong lack of feature in TS - being able to relativize paths in the output) and cpx, both for full and for watch modes. It copies (and watches) all other files from src/ to dist. So much code.
#!/usr/bin/env TS_NODE_COMPILER_OPTIONS={"module":"commonjs"} yarn -s ts-node
import concurrently, { CommandObj, Options } from "concurrently";
import glob from "glob";
import { basename, dirname } from "path";
import { chdir } from "process";
import { randomColor } from ".";
const watch = process.argv.includes("--watch");
const parallel = watch;
// Fastest time & best config:
// - using incremental build (watch-incremental is ~20% slower than
// watch-non-incremental, thus acceptable)
// - for the full build, use -b
// - for watch, use WITHOUT -b (much faster, ~0.5s without -b vs. 3s with -b)
// Seems related: https://github.com/microsoft/TypeScript/issues/31932
chdir(`${__dirname}/..`);
const options: Options = {
killOthers: ["failure"],
prefix: "[{time} {name}]",
timestampFormat: "mm:ss.S",
};
let commands: CommandObj[] = [];
if (!parallel) {
commands.push({
command: "ttsc -b --preserveWatchOutput",
name: "tsc",
prefixColor: "yellow",
});
}
for (const src of glob.sync("packages/*/src")) {
if (parallel) {
const dir = dirname(src);
commands.push({
command:
`ttsc --preserveWatchOutput --project '${dir}/tsconfig.json'` +
(watch ? " --watch" : ""),
name: basename(dir),
prefixColor: randomColor(),
});
}
// Copy all files BUT *.ts and *.tsx (which will be turned into JS by TS).
// Notice that we include *.d.ts, they're perfectly valid to copy.
commands.push({
command:
`cpx '${src}/**/{*.d.ts,!(*.ts|*.tsx)}' '${src}/../dist'` +
(watch ? " --watch" : ""),
name: "cpx",
});
}
concurrently(commands, options).catch(() => process.exit(1));
If you are hunting for solutions, you may find some at: https://stackoverflow.com/questions/40471152/tell-typescript-to-compile-json-files "Tell TypeScript to compile json files"
I collected some of the suggestions from here into an answer there, to summarise the various options.
Since I don't want to define all sorts of locations on what files needs to be included and what not, I tried re-using the tsconfig.json
and make use of the getParsedCommandLineOfConfigFile
provided by the Typescript API. You can trick this function into resolving more files using the extraFileExtensions
. I ended up creating a Gulp script to easily copy the asset files and leave all others up to Typescript:
const {src, dest} = require('gulp');
const {relative, extname} = require('path');
const {getParsedCommandLineOfConfigFile, sys, ScriptKind, Extension} = require('typescript');
function asExtension(extension) {
return { extension, scriptKind: ScriptKind.Deferred };
}
async function assets() {
// File extensions we want to copy as assets.
const assetExtensions = ['.yaml'];
// Let Typescript handle these file extensions and retrieve all other files with the given asset extensions.
const typescriptExtensions = [Extension.Ts, Extension.Tsx, Extension.Js, Extension.Jsx];
const parsed = getParsedCommandLineOfConfigFile('tsconfig.json', {}, sys, undefined, undefined, assetExtensions.map(asExtension));
const assetFiles = parsed.fileNames.filter(fileName => !typescriptExtensions.includes(extname(fileName)))
.map(fileName => relative(parsed.options.baseUrl, fileName));
return src(assetFiles, { base: './' }).pipe(dest(parsed.options.outDir))
}
module.exports = { assets };
Hope this helps someone.
Any hope this will be reopened? TS compiler mess with config json files as they're referenced at run time and not copied to the destination path.
I'm using tsc
to compile src
modules. Now I just want to copy my templates ejs files to dist
folder. I use nodejs fs
module to read the files, but it can not be found in dist
. ๆๆณๆๅบ่ฏฅๅชไผๅๅ dist
็ฎๅฝ๏ผ่ไธไผๅๅธ src
็ฎๅฝ๏ผๆไปฅ templates
็ๅบๅจ dist
ไธญๅ
ๅซใ
My version:
{ "scripts": { "prebuild": "del lib/", "build": "tsc", "postbuild": "cpy '**/*' '!**/*.ts' ../lib/ --cwd=src/ --no-overwrite --parents" }, "devDependencies": { "cpy-cli": "^2.0.0", "del-cli": "^3.0.0" } }
Worked for me, but only without the ''
in the copy script. Tested on Windows 10.
My copy script: cpy **/* !**/*.ts ../dist --cwd=src --no-overwrite --parents
This is partial, because watch mode is not covered, and we all spend 99% of time working with tsc in watch mode typically. Build from scratch is the least interesting thing typically.
I know that cpy supports watch mode too, but it all complicates package.json scripts a lot.
+1! I'm also struggling with getting /public
included in my Typescript build. Of course I could copy it into /dist
, but it only works as a local solution. I'm deploying to Heroku, and they prefer to build their own bundle, so it must be included during build time.
Will this be considered?
@tomcatmwi same issue.
Im not sure how it works with Heroku because I have never used it.
But localy this works for me:
"scripts": {
"start": "node dist/server.js",
"build": "tsc && cp -R src/public dist",
"dev": "nodemon --exec ts-node src/server.ts"
},
I try to keep things simple. tsc practically my only build tool. It would be nice if I could list my html and css files that are in my src folder in either "include" or "files" and have them copied to outDir along with everything else without having to resort to making my own script or other build tools...
If someone has a problem by copying their .d.ts
files. I found on stackoverflow an answer really clear : https://stackoverflow.com/questions/56018167/typescript-does-not-copy-d-ts-files-to-build
Is there any update on this feature? This would be great! Definitely +1
If this helps anyone, I arrived at this issue because I was reading files with __dirname
import fs from "fs/promises";
import path from "path";
fs.readFile(path.join(__dirname, './hello.txt'))
Since then I've switched to using process.cwd(), so it works with Typescript/ts-node and the compiled JS/node.
import fs from "fs/promises";
import path from "path";
import process from "process";
fs.readFile(path.join(process.cwd(), './resources/hello.txt'))
The 2nd example assumes you run node/ts-node from the project directory with the file structure below
project
resources
hello.txt
src
foo.ts
dist
foo.js
Just created a new NodeJS package called tsc-hooks. It adds hooks to the TypeScript compiler, I was originally writing it for the sole purpose of this issue, but then thought there might be more short comings of tsc
.
So if you would like a seamless solution for this you can install tsc-hooks as a dev dependency using
yarn add tsc-hooks --dev
or if you want to install it to your global tsc
command run yarn global add tsc-hooks
.
After installation use the new hooks option in your tsconfig like so:
tsconfig.json
{
"compilerOptions": {
"outDir": "dist"
},
"include": [ "src/**/*" ],
"exclude": [ "src/**/*.txt" ],
"hooks": [ "copy-files" ] /* <-- NEW OPTION HERE */
}
Whatever is in include and not exclude will be copied over to the outDir.
And that's it! Run tsc
from your scripts in package.json or if you installed it globally just run tsc
Note: For some reason there have been some issues with nvm when using npm, so I'd recommend using yarn.
If you have any questions or need help, just open up a discussion here or if there are any issues open up an issue here.
[Just set]
"allowJs": true
in tsconfig.json [and add"./src/**/*.js"
to your tsconfig.json includes]
To โcopyโ JS files during the build, you can just add the "allowJs": true in tsconfig.json. It would actually โtranspileโ your JS files into JS that follows the rules you set in the configurations, which is nice. However, that does not fix the issue with other files like HTML and CSS.
โ๏ธ Reference:
https://vccolombo.github.io/blog/tsc-how-to-copy-non-typescript-files-when-building/#allowjs-true-in-tsconfigjson
My issue was we have this node serverless functions project that is mostly all js
however we have been starting to sprinkle in a little ts
in there as well, and we didn't necessarily want to do a whole build system just to be able to get all the build-ready files into a clean separate ./build
directory [to avoid complex .gitignore rules]--but we needed it to also work with tsc -w
at the same time as well, so that it would copy them over in realtime when debugging.
So in our case, the above "allowJs": true
option seems to work just perfectly for us whereas all we needed was the js
and the ts
to both be compiled into the same output directory without having to deal with a bunch of random compiled js
files within our ./src
directory folder.
I actually thought of this first thing I just didn't think to add the js
files to our tsconfig.json includes
until I read the above referenced article. Thank you good sir!
Just created a new NodeJS package called tsc-hooks. It adds hooks to the TypeScript compiler, I was originally writing it for the sole purpose of this issue, but then thought there might be more short comings of
tsc
.So if you would like a seamless solution for this you can install tsc-hooks as a dev dependency using
yarn add tsc-hooks --dev
or if you want to install it to your globaltsc
command runyarn global add tsc-hooks
.After installation use the new hooks option in your tsconfig like so:
tsconfig.json
{ "compilerOptions": { "outDir": "dist" }, "include": [ "src/**/*" ], "exclude": [ "src/**/*.txt" ], "hooks": [ "copy-files" ] /* <-- NEW OPTION HERE */ }Whatever is in include and not exclude will be copied over to the outDir.
And that's it! Run
tsc
from your scripts in package.json or if you installed it globally just runtsc
Note: For some reason there have been some issues with nvm when using npm, so I'd recommend using yarn.
If you have any questions or need help, just open up a discussion here or if there are any issues open up an issue here.
This is the way.
It's also possible to use postbuild
hook in npm
if you use npm run build
to run tsc. That can then do some copy operations.
I find that resolveJsonModule
is not very robust. It makes it seem we will copy over JSON files, but it might not unless ./src/**/*.json
is put into the includes
array (I found a case where it worked without it, and a case where it doesn't work).
Given that JSON files aren't the only "non-ts" and "non-js" files in ./src
relying on resolveJsonModule
is just not very robust solution.
A more robust solution is something that handles copying over all the non-transpiled files (all the files not handled by tsc) in ./src
to ./dist
. A solution above suggested tsc-hooks
, but this is so specific to tsc
, one could imagine instead a postbuild
hook in npm
to really finish the equation.
The question is what does tsc
want to be? It's not a full featured bundler like webpack... etc. I removed webpack from my libraries as it had a lot over head. But if there was a "simpler" and faster bundler that builds on top of tsc
, then it might be the job of the bundler to be moving non-transpiled files to the right places.
It's also possible to use postbuild hook in npm
No, it's not possible. Watch mode in development aspect.
Our use case which is broken is Project References (which is a build pipeline IMO). We import Avatar from "./Avatar.png
which typescript supports and when we run an incremental build it builds the dependencies as required. But these dependencies are broken until the images are also moved to outDir
so we have to run some hack to copy them, without knowing which dependencies typescript built so we lose some of the benefit of incremental builds
Just created a new NodeJS package called tsc-hooks. It adds hooks to the TypeScript compiler, I was originally writing it for the sole purpose of this issue, but then thought there might be more short comings of
tsc
.So if you would like a seamless solution for this you can install tsc-hooks as a dev dependency using
yarn add tsc-hooks --dev
or if you want to install it to your globaltsc
command runyarn global add tsc-hooks
.After installation use the new hooks option in your tsconfig like so:
tsconfig.json
{ "compilerOptions": { "outDir": "dist" }, "include": [ "src/**/*" ], "exclude": [ "src/**/*.txt" ], "hooks": [ "copy-files" ] /* <-- NEW OPTION HERE */ }Whatever is in include and not exclude will be copied over to the outDir.
And that's it! Run
tsc
from your scripts in package.json or if you installed it globally just runtsc
Note: For some reason there have been some issues with nvm when using npm, so I'd recommend using yarn.
If you have any questions or need help, just open up a discussion here or if there are any issues open up an issue here.
Seem like this does not work on macOS Monterey (tested with npm and typescript 4.5)
There is a difference between process.cwd() and __dirname
process.cwd()
returns the current working directory (where the script was launched from) __dirname
returns the directory of the current file.
If you are writing a CLI you can imagine that using process.cwd()
could cause problems because you can run your CLI from any directory
If this helps anyone, I arrived at this issue because I was reading files with __dirname
import fs from "fs/promises"; import path from "path"; fs.readFile(path.join(__dirname, './hello.txt'))Since then I've switched to using process.cwd(), so it works with Typescript/ts-node and the compiled JS/node.
import fs from "fs/promises"; import path from "path"; import process from "process"; fs.readFile(path.join(process.cwd(), './resources/hello.txt'))The 2nd example assumes you run node/ts-node from the project directory with the file structure below
project resources hello.txt src foo.ts dist foo.js
There is a difference between process.cwd() and __dirname
process.cwd()
returns the current working directory (where the script was launched from) __dirname
returns the directory of the current file.
If you are writing a CLI you can imagine that using process.cwd()
could cause problems because you can run your CLI from any directory
so as it stand right now, we basically need to copy manually? tsc should provide this functionality.. its really important, since we mostly work on web stuff, we normally work with file other than *.ts and *.js
Honestly I don't get it, it feels like hypocrisy to me.
As mentioned here #30835 (comment)
tsc already provides a --watch
feature in the first place when gulp, nodemon, scripts are an option
If tsc is already walking through all files in the source, copying one to another wouldn't be much of a hassle to implement
I wanted to share my use case too.
I am using a light weight database library that uses .sql
files to define migrations and queries.
I would love to NOT have to manually copy those files, and if I were using babel I wouldn't have to.
I would also like to avoid having to put migrations and query sql files outside of my /src
.
For now it seems I'll just have to add a manual copy step for these items.
Spoiler: I am the author of TSCP
I was struggling with the same problem, and then created typescript-cp.
It detects the source and output directories based on your project's tsconfig.json (using typescript's own config parser, to work as smooth as possible) and copies the non-TS files to the outDir.
It has a very basic CLI, with similar arguments to typescript's tsc, but this is tscp
. It also supports package references.
# Copy
$ tscp
# Copy for TS project references
$ tscp -b
# Watcher
$ tscp -w
# Watcher for TS project references
$ tscp -b -w
# Custom compiler settings
$ tscp -p tsconfig.production.json
# Help
$ tscp -h
Example
package.json
{
//...
"scripts": {
"start": "tsc -w & tscp -w",
"build": "tsc && tscp"
},
//...
}
Feel free to open an issue if something isn't working as expected.
Knowing this is likely never happening anyways, I felt the need to propose a response to this comment:
Anyway explicit non-goal 4 is:
Provide an end-to-end build pipeline
I think the point that is missed here is that this isn't a random build issue people want the TypeScript compiler to solve for them. This is a problem created by TypeScript. A lot of npm packages don't need to have a build process at all, since the source code is directly executed (there's nothing to build). But the nature of TypeScript always requires a build process, and in an ecosystem that is often used to not needing it. Since the build output is dumped in a different folder, all of the existing resource files are lost in that process.
If someone wants to obfuscate their code, then I agree, that's not the responsibility of tsc. They would need a build process to do that anyways. But when tsc is the sole reason for the problem existing, it feels like it should provide a solution.
All that said, I think the right option would be for this to be handled via an entry in tsconfig.json rather than a command line flag. Manually specifying what input files your code requires at runtime with glob patterns seems like a pretty straightforward solution.
@stabai - You could even surmise that: You may NOT use Typescript unless you also also choose to use some kind of CI. (as TSC - does not provide copy services.)
I'd like to add my use case here. I'm experimenting with TypeScript as part of an AWS SAM application. In this application, I've got a few different folders with related files in them, and each has a package.json
and corresponding node_modules
folder.
When I run tsc
over these, the node_modules folders aren't copied. Resource files aren't copied. As a result, I can't run tests - if I point it at the generated code, important files aren't present, but if I point it at the original, it can't understand the code
Similarly, I can't use sam-local to test and debug running code, because the transpired version doesn't have the node modules. Using sam build works around this, because it does an NPM ci step as part of building, but then SAM is running off that build artefact.
Simply copying the JS files, as is, instead of transpiling, would go a long way to solve this. In particular, making this work with tsc --watch
is important, so that the other resources are copied when modified. A workaround would be to have a second watch process running to copy those files, but that's frankly a ridiculous hack.
What do you mean by "node_modules folders aren't copied"? They aren't supposed to be copied. Also, what do you mean by "simply copying the js files?" If you are currently migrating, you can enable js files in tsconfig
I can't run the generated JS files if they're in a directory by themselves. They need to have the rest of the files of the project with them - e.g. the node_modules folder, any resource files, etc. Otherwise I can't even run tests.
So having tsc --watch
generate files to an output directory is useless if it doesn't also put the non-TS files in the output directory as well - at least as an option.
As for "simply copying the JS files" - I would like a mode where the JS files were copied - not transpiled to the target ES version. Using allowJS does not put the exact JS files in - it puts a transpired version in, possibly with significant changes. And, in my test case, the transpiled version broke tests, whilst putting the original version in did not.
Now, this can be mitigated by not using an outDir - having the TS files transpile to JS in the same folder, and putting up with any warnings for the existing JS files that tsc
tries to overwrite with themselves. But this is shitty, because it's a bad idea for generated files to go into the same directory as source code, and it's especially shitty when only some of the JS files are generated - people edit the generated file by accident, it's annoying to manage with version control, and you need to make sure your CI bundling doesn't include the TS files (because why would you want to?)
Simply put, it's important to be able to have a directory, that has the JS files that you're going to publish, in a spot where you can use them locally, and to have that be updated during development. This is not a CI concern - it's an issue for local development.
I can't run the generated JS files if they're in a directory by themselves. They need to have the rest of the files of the project with them - e.g. the node_modules folder, any resource files, etc. Otherwise I can't even run tests.
So having
tsc --watch
generate files to an output directory is useless if it doesn't also put the non-TS files in the output directory as well - at least as an option.As for "simply copying the JS files" - I would like a mode where the JS files were copied - not transpiled to the target ES version. Using allowJS does not put the exact JS files in - it puts a transpired version in, possibly with significant changes. And, in my test case, the transpiled version broke tests, whilst putting the original version in did not.
Now, this can be mitigated by not using an outDir - having the TS files transpile to JS in the same folder, and putting up with any warnings for the existing JS files that
tsc
tries to overwrite with themselves. But this is shitty, because it's a bad idea for generated files to go into the same directory as source code, and it's especially shitty when only some of the JS files are generated - people edit the generated file by accident, it's annoying to manage with version control, and you need to make sure your CI bundling doesn't include the TS files (because why would you want to?)Simply put, it's important to be able to have a directory, that has the JS files that you're going to publish, in a spot where you can use them locally, and to have that be updated during development. This is not a CI concern - it's an issue for local development.
This seems like a different issue; Do you have a repo with minimal repro? @twasink
I'll be making one for a blog post/rant this weekend. The repo I tried this out with is a private work one.
But this is the issue that I'm talking about: having a directory that I can use for things such as running unit tests, that has a consistent view of all of my code base (including dependencies, non-JS files, etc). If the only tool I'm using that produces transformed code is tsc
, then that should be something tsc
should be able to do...
For anyone struggling to work around this issue, this is what worked for my use case (docker multistage build):
- Remove/comment out
outDir
fromtsconfig.json
entirely, allowingnpx tsc
command to generate JS files exactly where their TS siblings are. - Compile TS to JS -
npx tsc
(or whatever your command is). - Remove any unneeded files with
rm -rf
, in my case it wasrm -rf src/**/*.ts src/*.ts
It's a moderately jank solution, but no extra packages/installations required & it's relatively fast in execution time.
A couple of other approaches which may be helpful to someone.. (Copying a ../templates
folder to ../../dist/templates
in this example).
Using fs-extra
and execute node my-script-name.js
in package.json
const fs = require('fs-extra')
const { resolve } = require('path')
fs.copySync(
resolve(__dirname, '..', 'templates'),
resolve(__dirname, '..', '..', 'dist', 'templates')
)
Using webpack
and execute webpack --mode production
in package.json
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { resolve } = require('path')
module.exports = {
// ...other configuration options...
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: resolve(__dirname, '..', 'templates'), to: resolve(__dirname, '..', '..', 'dist', 'templates')},
],
}),
],
};
I had to copy css files (I need to be able to import the css files from another package), this worked for me:
npm install copyfiles -g
Inside "scripts" of package.json
:
tsc --build && copyfiles -u 1 src/**/*.css dist
Had to copy .sql
files deep in src
dir, doing like that:
"postbuild": "node ./node_modules/copyfiles/copyfiles -u 1 -e \"**/*.js\" -e \"**/*.ts\" \"src/**/*\" dist"
Just created a new NodeJS package called tsc-hooks. It adds hooks to the TypeScript compiler, I was originally writing it for the sole purpose of this issue, but then thought there might be more short comings of
tsc
.
So if you would like a seamless solution for this you can install tsc-hooks as a dev dependency usingyarn add tsc-hooks --dev
or if you want to install it to your globaltsc
command runyarn global add tsc-hooks
.
After installation use the new hooks option in your tsconfig like so:
tsconfig.json{ "compilerOptions": { "outDir": "dist" }, "include": [ "src/**/*" ], "exclude": [ "src/**/*.txt" ], "hooks": [ "copy-files" ] /* <-- NEW OPTION HERE */ }Whatever is in include and not exclude will be copied over to the outDir.
And that's it! Runtsc
from your scripts in package.json or if you installed it globally just runtsc
Note: For some reason there have been some issues with nvm when using npm, so I'd recommend using yarn.
If you have any questions or need help, just open up a discussion here or if there are any issues open up an issue here.Seem like this does not work on macOS Monterey (tested with npm and typescript 4.5)
Just created a new NodeJS package called tsc-hooks. It adds hooks to the TypeScript compiler, I was originally writing it for the sole purpose of this issue, but then thought there might be more short comings of
tsc
.
So if you would like a seamless solution for this you can install tsc-hooks as a dev dependency usingyarn add tsc-hooks --dev
or if you want to install it to your globaltsc
command runyarn global add tsc-hooks
.
After installation use the new hooks option in your tsconfig like so:
tsconfig.json{ "compilerOptions": { "outDir": "dist" }, "include": [ "src/**/*" ], "exclude": [ "src/**/*.txt" ], "hooks": [ "copy-files" ] /* <-- NEW OPTION HERE */ }Whatever is in include and not exclude will be copied over to the outDir.
And that's it! Runtsc
from your scripts in package.json or if you installed it globally just runtsc
Note: For some reason there have been some issues with nvm when using npm, so I'd recommend using yarn.
If you have any questions or need help, just open up a discussion here or if there are any issues open up an issue here.Seem like this does not work on macOS Monterey (tested with npm and typescript 4.5)
Thanks for the tip. It worked for me! Cheers!
The below way works for me
# Install the dependence
npm install --save-dev copyfiles
# Update the scripts section in the package.json file
{
"copy-files": "copyfiles -u 1 src/**/*.js dist/",
}
I have created a simple tsc
wrapper @rnm/tscx to solve this problem.
npm install typescript @rnm/tscx -D
{
"scripts": {
"build": "tscx -p tsconfig.json", // same as "tsc -p tsconfig.json"
"dev": "tscx -p tsconfig.json --watch --copyfiles --exec bootstrap.js"
}
}
npm run dev
It works and makes my life easier.๐คฃ
5 years passed, no official solution??? it's 100% necessary feature for 99.99999% frontend project.