-
-
Notifications
You must be signed in to change notification settings - Fork 532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is this really the best way to make a command line script with ts-node? #995
Comments
Shebangs only allow passing a single argument to the executable. So you cannot do any more than this:
This is a Linux kernel limitation. Linux splits on the first space and nowhere else, and everything before the first space needs to be an absolute path. You're probably on Mac, which has a non-standard shebang implementation in its kernel.
I recommend using a bootstrapper script to keep your stuff portable. This will use the local installations of ts-node and typescript, so you don't need to install anything globally.
|
Thanks for your suggestion, @cspotcode. I tried it and it didn't work for me. My {
"name": "ts-hello",
"version": "0.0.0",
"private": true,
"bin": {
"hello": "src/cli-bootstrap.js"
},
"dependencies": {},
"devDependencies": {
"@types/node": "^13.11.0",
"ts-node": "^8.8.1",
"typescript": "^3.8.3"
}
} … and I added #!/usr/bin/env node
require('ts-node').register();
require('./hello.ts'); In order to link the new script, I run: $ cd ~/projects/ts-hello/
$ npm unlink
removed 1 package in 0.079s
$ npm link
audited 10 packages in 1.247s
found 0 vulnerabilities
/usr/local/bin/hello -> /usr/local/lib/node_modules/ts-hello/src/cli-bootstrap.js
/usr/local/lib/node_modules/ts-hello -> ~/projects/ts-hello As long as I'm in the project directory, the command works fine. As soon as I step out of it, I get the same errors again: $ cd ~/projects/ts-hello/
$ hello a b
Hello, <user>!
Your arguments: [ 'a', 'b' ]
$ cd ..
$ hello a b
~/projects/ts-hello/node_modules/ts-node/src/index.ts:421
return new TSError(diagnosticText, diagnosticCodes)
^
TSError: ⨯ Unable to compile TypeScript:
ts-hello/src/hello.ts:7:16 - error TS2307: Cannot find module 'os'.
7 import os from 'os';
~~~~
ts-hello/src/hello.ts:10:32 - error TS2580: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i @types/node`.
10 console.log('Your arguments:', process.argv.slice(2));
~~~~~~~
at createTSError (~/projects/ts-hello/node_modules/ts-node/src/index.ts:421:12)
at reportTSError (~/projects/ts-hello/node_modules/ts-node/src/index.ts:425:19)
at getOutput (~/projects/ts-hello/node_modules/ts-node/src/index.ts:553:36)
at Object.compile (~/projects/ts-hello/node_modules/ts-node/src/index.ts:758:32)
at Module.m._compile (~/projects/ts-hello/node_modules/ts-node/src/index.ts:837:43)
at Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
at Object.require.extensions.<computed> [as .ts] (~/projects/ts-hello/node_modules/ts-node/src/index.ts:840:12)
at Module.load (internal/modules/cjs/loader.js:996:32)
at Function.Module._load (internal/modules/cjs/loader.js:896:14)
at Module.require (internal/modules/cjs/loader.js:1036:19) If we can get this bootstrap script to work, then this is probably the best solution for now (even though it's a hassle if you want to expose many command line scripts from the same package). Regarding passing multiple arguments to the executable in the shebang, I referenced the documentation and a hack around the Linux kernel limitation. To make my questions and feature requests as concrete as possible:
|
You'll probably need to pass flags to For your use-case, is your package meant to be used by anyone else? Or only yourself? If the former, we expect you'll pre-compile and publish to npm, or at least As to whether IMO resolving symlinks should match the default behavior of nodejs. Node has Typescript does load Sometimes features evolve or are added without the README receiving an update. They may only be documented via API tooltips and the tsconfig JSON schema. We are generally happy to merge PRs that improve the docs. (yay open source!) Omission from the README is not necessarily an indicator that a feature is discouraged, deprecated, or buggy. In this case, I definitely encourage I hope this answers everything! |
Thanks a lot for your elaborate response! You make a lot of good points, especially regarding the delivery of my scripts. While I studied computer science, I'm still relatively new to this whole "Node.js world" and there is still a lot of stuff that confuses me. (And Node.js has only become an option for me since I discovered TypeScript. 🤓) For now, I'm mostly trying to figure out a reasonable toolchain for myself without concerns about performance but a big focus on quick iterations. Thus, I guess I also fall in the "for personal stuff I hate pre-compilation" camp. 😉 (I just want an alternative to Bash scripts that I can tweak whenever my use case changes slightly without having to think about compilation.)
This is a great suggestion and I already wanted to ask you for an example because I wasn't sure about the nesting and the transformation from kebab-case to camelCase but I found one: 🙂 {
"ts-node": {
"transpileOnly": true
},
"compilerOptions": {}
} If I make a pull request later on in order to improve the documentation in the README, I would include this as well. (As far as I can judge by just skimming the README once more, this feature is also not documented there.) I'm confused about |
Here is a pull request that addresses the missing documentation in the README, @cspotcode: #1000 |
Closing as answered, and all necessary work is tracked by other tickets. |
@KasparEtter what solution did you end up going with? I'm also trying to use EDIT: it's working now -- I had to upgrade my globally installed version of |
This allows `ts-node` to be used to run TypeScript files without having to compile them with `tsc` first. It also adds the necessary configs, including `tsconfig-paths` for `paths` import-alias resolution. Unfortunately, this means we have to remove the `--experimental-module-resolution=node` since `ts-node` uses its own loader and thus form of resolving modules. See: Setup * https://medium.com/@jimcraft123hd/setting-up-path-alias-in-typescript-and-tsc-build-without-error-9f1dbc0bccd2 Issues with `ts-node`, ESM, and aliases * TypeStrong/ts-node#1007 - TypeStrong/ts-node#476 - dividab/tsconfig-paths#122 (comment) - TypeStrong/ts-node#1450 (comment) * TypeStrong/ts-node#1414 * TypeStrong/ts-node#995 - TypeStrong/ts-node#639 Node issues with ESM * https://nodejs.org/api/packages.html#determining-module-system * nodejs/node#37468
This allows `ts-node` to be used to run TypeScript files without having to compile them with `tsc` first. It also adds the necessary configs, including `tsconfig-paths` for `paths` import-alias resolution. Unfortunately, this means we have to remove the `--experimental-module-resolution=node` since `ts-node` uses its own loader and thus form of resolving modules. See: Setup * https://medium.com/@jimcraft123hd/setting-up-path-alias-in-typescript-and-tsc-build-without-error-9f1dbc0bccd2 Issues with `ts-node`, ESM, and aliases * TypeStrong/ts-node#1007 - TypeStrong/ts-node#476 - dividab/tsconfig-paths#122 (comment) - TypeStrong/ts-node#1450 (comment) * TypeStrong/ts-node#1414 * TypeStrong/ts-node#995 - TypeStrong/ts-node#639 Node issues with ESM * https://nodejs.org/api/packages.html#determining-module-system * nodejs/node#37468
This allows `ts-node` to be used to run TypeScript files without having to compile them with `tsc` first. It also adds the necessary configs, including `tsconfig-paths` for `paths` import-alias resolution. Unfortunately, this means we have to remove the `--experimental-module-resolution=node` since `ts-node` uses its own loader and thus form of resolving modules. See: Setup * https://medium.com/@jimcraft123hd/setting-up-path-alias-in-typescript-and-tsc-build-without-error-9f1dbc0bccd2 Issues with `ts-node`, ESM, and aliases * TypeStrong/ts-node#1007 - TypeStrong/ts-node#476 - dividab/tsconfig-paths#122 (comment) - TypeStrong/ts-node#1450 (comment) * TypeStrong/ts-node#1414 * TypeStrong/ts-node#995 - TypeStrong/ts-node#639 Node issues with ESM * https://nodejs.org/api/packages.html#determining-module-system * nodejs/node#37468
Desired behavior
I would like to be able to have the following script at
~/projects/ts-hello/src/hello.ts
:… and have
npm
link the executable with the following configuration at~/projects/ts-hello/package.json
:For this to work, both
typescript
andts-node
have to be installed globally (see more on this below):Additionally, we need to enable ES module interoperability with the following argument in
~/projects/ts-hello/tsconfig.json
:We can now change to the project directory, install the dependency and link the package:
Actual behavior
Since the motivation for this issue is in part to document the best approach for others (as far as I can tell a lot of people already struggled with this problem before me), I will elaborate step by step how to arrive at an acceptable solution so that search engines can index all the error messages as well.
First, let's test that the script works without relying on the shebang:
The problems start when you move a directory up:
Since
node ts-hello/src/hello.js first second
works without problems given the following script at~/projects/ts-hello/src/hello.js
:… this is disappointing but can easily be fixed by providing the TypeScript JSON project file explicitly as documented:
(And no, this is not just due to
os
being a native Node.js module.node
also picks up dependencies specified in~/projects/ts-hello/package.json
whilets-node
does not. It would be great ifts-node
could work as similar tonode
as possible, just on.ts
instead of.js
files, of course.)You can simplify the above with the undocumented
--script-mode
option that I discovered:There is also an undocumented command that does the same:
(Given the usefulness of these options, why are they not mentioned in the README?)
Once we give our script the permission to execute it directly (based on the shebang in the first line) with
chmod u+x ts-hello/src/hello.ts
,ts-hello/src/hello.ts first second
fails with the same errors as above (whereasts-hello/src/hello.js first second
runs just fine after giving it the necessary permission as well).By now, we know how to fix this. Just replace the first line of
~/projects/ts-hello/src/hello.ts
with:(It seems to me that
ts-node-script
exists for exactly this purpose. Why was it not recommended in #73, #298, and #639? And if we're already at it: #116 would also benefit from a reference to this issue.)This solves the errors and at this point you could just add an alias to this script to your shell startup script:
However, I would rather like to use the
bin
functionality ofnpm
for this so that others could install my script easily if I decided to publish my package on www.npmjs.com.As the output of the
npm link
command above told you, it created the following symlinks for your script and package:So what happens if we use this linked command now?
Back to field one. Since
--script-mode
just determines the directory name from the script path (see the code) and the script is located in/usr/local/bin
whereas the package exists (resp. is linked) at/usr/local/lib/node_modules/ts-hello
, this couldn't work.So how do we solve this? The best I could come up with is:
A few explanations:
-S
allows you to pass arguments to the specified interpreter.node_modules
directory would be determined dynamically to be more platform-independent but I couldn't get#!/usr/bin/env -S ts-node --project $(npm get prefix)/lib/node_modules/ts-hello/tsconfig.json
to work.#!/usr/bin/env -S npx ts-node --project /usr/local/lib/node_modules/ts-hello/tsconfig.json
in order not to require a global installation ofts-node
, the user needs to installtypescript
manually globally anyway astypescript
is only a peer dependency ofts-node
. (Is there a way around this?)Coming back to the original question: Is this currently really the best way to make a command line script with
ts-node
? (My solution is both complicated and platform-dependent.)If yes, it would be so much easier (and it would have saved me hours) if
--script-mode
could follow the symbolic link before determining the directory. (And the README should state prominently that the correct shebang to use is#!/usr/bin/env ts-node-script
!)The text was updated successfully, but these errors were encountered: