Skip to content
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

Create ts-transpile bin script #939

Merged
merged 4 commits into from Feb 15, 2020
Merged

Create ts-transpile bin script #939

merged 4 commits into from Feb 15, 2020

Conversation

G-Rath
Copy link
Contributor

@G-Rath G-Rath commented Jan 12, 2020

Fixes #703

@coveralls
Copy link

coveralls commented Jan 12, 2020

Coverage Status

Coverage increased (+0.1%) to 78.864% when pulling 60e850e on G-Rath:create-ts-transpile-binscript into b90754e on TypeStrong:master.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 78.764% when pulling 47d9852 on G-Rath:create-ts-transpile-binscript into b90754e on TypeStrong:master.

@cspotcode
Copy link
Collaborator

Now that we can specify configuration via tsconfig.json, do we need this? What is your use-case?

We should also think about the naming. ts-transpile implies it merely transpiles, not executes, especially when compared to the name ts-node. For https://www.npmjs.com/package/ts-node-to I used the names ts-node-transpile-only or ts-node-to for short.

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 15, 2020

@cspotcode It's useful for shebang purposes: I don't have to rely on there always being env variables set, or a tsconfig.json that's configured to not typecheck.

The ability to specify config via tsconfig.json solves a different problem: that ts-node often requires slightly different settings than the rest of the TS project.

As for the name, I don't think it's any more confusing than the -T flag :)

@blakeembrey
Copy link
Member

a tsconfig.json that's configured to not typecheck

Why is this an issue? I'd also love to lean on this feature here if possible, otherwise it'd be easy to have a lot of different bin scripts.

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 16, 2020

Debugging:

It's useful for debugging, as it means I can quickly write a TypeScript script w/o having to change anything outside of the file I'm working in, and still have it compile quickly while getting nice autocompletion & type checking via my IDE.

This also means I don't risk committing the disabling of type checking for the whole project.

Teaching:

There are a lot of stuff controlled by tsconfig.json that can be very overwhelming if you're introducing someone to TypeScript for the first time. This way I don't have to pass them two files, and explain how "ignore this tsconfig.json file, but always carry it around with the ts script at all times otherwise it might behaviour differently, and will definitely go slower"

Quick utility scripting

I like TypeScript a lot, because it helps keep my code safe & greatly empowers my IDE to provide far better support, with things like auto completion. I also work as the technical lead of the team responsible for looking after my company's infrastructure, and responding to high priority requests such as application outages.

As such, the ability to write a single TS file that can be ran anywhere (since it's tied only to its own file and that ts-node exists globally) on the server I'm working on, that will be typechecked by my editor, and that I can confidently switch between what is effectively fast & slow mode by just changing the bang at the top of the file is a huge advantage.

This also removes the hum and haring over adding and removing an extra dependency: iirc I think I tried ts-node-to without success, but if it did it introduces a dependency that must be considered - if I'm not using ts-node-to anywhere, then it really should be removed from my dependency tree. But that means as soon as I want to write a TS file that isn't typechecked (i.e I'm dealing w/ a downed server that I'm writing hacky scripts as a fix for) I have to install, ship, build, etc a new dependency. Alternatively, I have to introduce a new tsconfig.json in a manner that it targets a single file, or make every file not type checked.

The alternative is to introduce a build step somewhere, which is an whole other ball of wax that defeats the point.


The underlying theme that's common across the above is that this allows you to easily write files that are completely self contained - ts-node already has a problem similar problem w/ @types/node (sadly I've not had the time to finalise my fix for that yet).

I actually think that having a ts-transpile is the final missing thing from ts-node overall; it's also the complement of ts-node/register, which has a transpile form :)

Ultimately I think that tsconfig.json-based configuration solves the majority of configuration problems for ts-node except transpile vs not, since it doesn't let you target specific files - while you can achieve that by creating separate tsconfig.jsons, that just puts you back where you started, since you now have to point ts-node at that.

@cspotcode
Copy link
Collaborator

What problems did you hit with ts-node-to? I'm struggling to think of a situation where you can't install ts-node-to. You can have it in your package.json dependencies locally, or you can install it globally. Having an extra dependency isn't any different than having extra files installed via a single dependency.

I'd be more agreeable if the name started with the ts-node prefix so that all binaries installed by ts-node were essentially namespaced. I'm also not a fan of ts-script. I'd personally prefer it to be called ts-node-rel.

It also begs the question: do we make a ts-node-rel-to that combines both flags? If the goal is to facilitate shebangs, then every permutation should exist.


Someone on the TypeScript team is building a new "twoslash" tool for embedding interactive TypeScript demos into websites. It allows specifying compiler flags via comments at the top of the file. I wonder if someone can build a solution similar to that?

https://www.typescriptlang.org/v2/dev/twoslash/
Similar to how env2 can parse a more complex shebang from the second line of a file.
https://www.npmjs.com/package/env2-shebang

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 16, 2020

What problems did you hit with ts-node-to?

g-rath@CINDY:/c/Users/G-Rath$ echo "console.log('hello world');" > myfile.ts
g-rath@CINDY:/c/Users/G-Rath$ ts-node myfile.ts
hello world
g-rath@CINDY:/c/Users/G-Rath$ ts-node-to myfile.ts
g-rath@CINDY:/c/Users/G-Rath$

ts-node-to doesn't provide any output, where as ts-node does.

You can have it in your package.json dependencies locally, or you can install it globally.

Requiring me to explain to others that they now have to install a third global dependency for something.

While I agree that one package is typically very harmless, when working with developers who have mostly only worked with a single primary language that operators rather differently (In this case Ruby), adding another package to the list of things they have to keep in their head makes it harder to get them to dip their toes into the water.

Having an extra dependency isn't any different than having extra files installed via a single dependency.

Yes it is different - it's introducing a new dependency into the tree that has to be justified, and that has to be managed.

Case and point (and also overlapping with my previous point about making things more scary for developers who have only ever worked in one language space), installing ts-node-to gives me this:

npm WARN ts-node-to@0.0.3 requires a peer of ts-node@* but none is installed. You must install peer dependencies yourself.

"But, I've got ts-node already installed globally...??"

I understand that this is an npm issue, but it's still more differences than "none" vs adding a new file to ts-node.

Additionally, projects can have policies in place that have strict rules around packages that can and cannot be installed - while I think attempting to audit every package you install manually is not a good use of one's time, it's harder for me to get approval for two packages that I want for what some could call a convenience than one package.

As a real-world example, the New Zealand government provide a high security platform called CWP, which has very high security requirements around packages; such as not being allowed to use any package that isn't on a stable release channel (i.e no 0.x.x, no -RCs, etc). The calls on what packages we can and cannot use at times comes down to if the auditor decides they want to add the package in question to their list of packages to watch & audit when new releases come out.

I'd be more agreeable if the name started with the ts-node prefix so that all binaries installed by ts-node were essentially namespaced

I'm happy with that. It would also align it with the register name; would you like me to rename it to ts-node-transpile-only?

It also begs the question: do we make a ts-node-rel-to that combines both flags? If the goal is to facilitate shebangs, then every permutation should exist.

I assume you're meaning both flags of ts-script?

I think it's reasonable to say that it's not reasonable to facilitate every permutation, but I do think it's reasonable to support transpile mode as a shebang, due to what it does.

Additionally, ts-script will be unneeded in the very near future as Node rolls out native support for ES modules over the course of this year. Meanwhile, ts-transpile is something that will generally retain its usefulness for a longer time, as ultimately any additional solution will always require the adding of another package (and you can't make a wrapper for ts-node that handles installing ts-node itself as packages that handle config loading look for ts-node).

If the goal is to facilitate shebangs

No, that is not the goal.

The goal of this specific PR is one of accessibility and convenience, since a large number of distro don't support passing arguments to shebangs.

@blakeembrey
Copy link
Member

@G-Rath this makes sense, happy to make it a feature. I’m good with either naming, but agree it’s probably better to namespace under ts-node - it was my mistake with the script entry point but we can fix that in the next major release to align with the naming here.

@cspotcode
Copy link
Collaborator

For what it's worth, my preference is to name it ts-node-to and/or ts-node-transpile-only. ts-node-to is quick to type and namespaced, so that's what I tend to use in my shebangs and in an interactive shell.

Despite what I said earlier, I'm also happy to get this merged. Seems like a really high-value utility.

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 24, 2020

I'm not a fan of ts-node-to because it's meaningless: what magical effects could sticking -to on the end of your shebang do?

I think it's a cute shortening, but prefer the more expressive ts-node-transpile-only as that clearly tells you what it's doing + shebangs are shortish anyway so it's very low cost for you to create an alias in your profile.

I'll rename and push.

@G-Rath G-Rath mentioned this pull request Jan 24, 2020
@cspotcode
Copy link
Collaborator

what magical effects could sticking -to on the end of your shebang do?

It's an acronym for "Transpile Only", just as ts is an acronym for "TypeScript." However, people on my team did not realize that and were pronouncing it "too", so I agree it's confusing.


I hit a use-case for --script-mode today. I'm writing some helper scripts for my team. They end up being extracted into a subdirectory of each project, via git submodules. They're written in TypeScript for simplicity, and I want them to run using a bundled tsconfig.json and in transpile-only mode for performance. I'm going to write them with #!/usr/bin/env ts-script (soon to be ts-node-script) and
set "transpileOnly": true in the bundled tsconfig file. I kinda wish --script-mode was the default behavior. I can't think of any situation where non-script-mode is necessary for me.

Do either of you know where people typically rely on the non-script-mode behavior?

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 27, 2020

Do either of you know where people typically rely on the non-script-mode behavior?

No, but I also rely on the defaults, so not sure the impact - I'll run ts-node in both modes over my company's TS codebase, and pick out the differences for us to compare :)

(that'll be in about 30 minutes, as I'm just about to leave for work)

@cspotcode
Copy link
Collaborator

@G-Rath thanks, that'd be great.

As far as I know, the only difference between script-mode and non-script-mode is the default for --project.

  • non-script-mode: current working directory
  • script-mode: directory of the script being invoked

For my case, if my helper scripts don't use script-mode, then someone tries to run ./.ci/workflows/publish.ts and it uses their ./tsconfig.json file instead of the one bundled in ./.ci/workflows. This breaks publish.ts if the tsconfigs are much different.

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 27, 2020

thanks, that'd be great.

Sadly I don't think it'll provide as much insight as I hoped now that I understand the difference.

As far as I know, the only difference between script-mode and non-script-mode is the default for --project.

I think I've hit this before, and it's an interesting one - overall I think it tends to be rather contextually based, as sometimes you want project, and other times you want script, which is why I don't think that running ts-script vs ts-node against our TS codebase will tell you much: it depends on where you call it.

That's why I think overall the project default is the correct default behaviour: it's reasonable to expect (or hope) it'll behave the same as often as possible regardless of things like what directory you're in - otherwise it's another thing I have to discuss with developers:

"so to regenerate the rules table you just run this script - but make sure you only call it in this directory; or a directory that has a tsconfig.json that looks like the one in this directory. But not at the top level, because that tsconfig.json is configured differently..."

In saying that, I think the actual default should be to act as TS does for resolving the tsconfig.json which is what I hope is how --project behaves - if not then that might be worth changing.

Then, ts-node-script lets you say "hey this is a script, not something project-based".

@cspotcode
Copy link
Collaborator

To clarify, it sounds like we agree that the default should be "--script-mode." Do I have that right?

That's why I think overall the project default is the correct default behaviour: it's reasonable to expect (or hope) it'll behave the same as often as possible regardless of things like what directory you're in - otherwise it's another thing I have to discuss with developers:

This describes the behavior of --script-mode: it discovers the correct tsconfig file based on the location of the script being invoked. (it ignores what directory you're in)

It matches the behavior of node as well. For example, node searches the parent directory of the script (not cwd) to find a package.json "type" field so it knows whether the file is ESM or not. Similarly, ts-node should consult the parent directory of the script to figure out how to compile and run.

tsc has a different use-case. You pass it a --project pointing to a tsconfig.json, (defaulting to cwd) and from there it discovers the source files to compile. This is opposite of node and ts-node, which accept a source file and work backwards from there.

  • ts-node and node: pass location of a script.ts, they discover tsconfig.json and package.json from there (--script-mode behavior)
  • tsc: pass location of a tsconfig.json, it discovers all source files from there

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 27, 2020

This describes the behavior of --script-mode

Yes my apologies - for some reason I crossed the two behaviours in my head when replying, so "script" & "project" should be inverted in my previous comment.

It matches the behavior of node as well.
tsc has a different use-case.

Good point; given that I think it's better to match nodes behaviour over tsc.

I'm going to start using ts-script in place of ts-node to see if does change anything for me, but I don't think it'll reveal any great revelation.

(btw I think this might be worth breaking out into a seperate top-level issue, so we can discuss it without bogging down this PR - we could also pin it to increase visibility, and hopefully get some other people to have a go at using ts-script in place of ts-node in their workflow to see what happens).

@cspotcode
Copy link
Collaborator

Yeah, good call, I created #949.

@G-Rath
Copy link
Contributor Author

G-Rath commented Feb 11, 2020

@andrewbranch is there anything actions still outstanding before this PR can be merged?

@andrewbranch
Copy link

@G-Rath did you ping the right person? I am a member of TypeStrong, but I’ve never been involved with ts-node.

@G-Rath
Copy link
Contributor Author

G-Rath commented Feb 11, 2020

@andrewbranch My apologies - I mixed you up with @cspotcode who is also an Andrew, but really I was aiming for @blakeembrey, so I failed on two fronts :/

Cheers for the heads up, and thanks for the work you've done with TypeScript 🙂

@cspotcode cspotcode added this to In Review in Andrew Bradley's Tasks Feb 11, 2020
Copy link
Collaborator

@cspotcode cspotcode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some time to merge and publish this, but I think the bin files need to be renamed. After that I'll try cutting a release.

package.json Outdated
@@ -6,7 +6,8 @@
"types": "dist/index.d.ts",
"bin": {
"ts-node": "dist/bin.js",
"ts-script": "dist/script.js"
"ts-script": "dist/script.js",
"ts-node-transpile-only": "dist/transpile.js"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on #948 (comment) I think we need to rename to dist/bin-script.js and dist/bin-transpile.js. Once that's done I can take a stab at merging and publishing a new release.

@G-Rath G-Rath deleted the create-ts-transpile-binscript branch March 31, 2020 07:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Andrew Bradley's Tasks
Merged to master
Development

Successfully merging this pull request may close these issues.

Add ability to --transpile-only from a shebang
5 participants