Deno: shebang support

Created on 6 Oct 2018  路  5Comments  路  Source: denoland/deno

When using deno to write applications or tools, it is desirable to execute those directly as binary [argv], rather than deno path/to/binary.ts [argv].

It could be possible to build custom versions of deno with code embedded, but I won't cover that here (see #986).
In this issue I'm looking at using deno as an executable interpreter with a shebang, such as this file binary containing:

#!/usr/bin/env deno
import { main } from "../src/main";
main();

This currently does not work, for two separate reasons:

  • If the executable name is extension-less (just binary rather than binary.ts), deno will not load it.
  • Even if it has a valid extension, deno will include the shebang line in the source, causing a syntax error.

Ultimately this means that it doesn't seem like you can do this with deno directly, rather you need something like a wrapper shell script or binary around invoking deno.

Ignoring the shebang line seems entirely reasonable, as that is not valid JavaScript | TypeScript in the first place.

I'm not sure what would be an acceptable solution regarding the extension.
Node just interprets files without an extension as .js files.

Most helpful comment

Perhaps "how should deno handle extensionless files" should be a separate issue from "deno should ignore shebangs"?

All 5 comments

I would assume that deno would use TypeScript for extensionless file names.

But I'm not really sure what about extensions in file names with a shebang line.

Relying on the file name in this particular case is tricky because it's often the case that an executable may be symlinked or renamed during installation. It's common in Node today with scripts installed with npm.

I don't want file extensions in my /usr/local/bin but if the file happens to be script.js that needs to be parsed as JS and not TS then script.js cannot be installed to /usr/bin as just "script".

The solutions I see are all ugly - maybe someone else has some better ideas.

  1. use a switch in the shebang line

Works well for #!/usr/local/bin/deno --js but for #!/usr/bin/env deno you need hacks like this that I once needed to use feature flags in node:

#!/bin/sh
":" //#; exec /usr/bin/env node --harmony-proxies "$0" "$@"
  1. use some magic comment
#!/usr/bin/env deno
// deno-language: js

or something like that. I don't like it really but being optional for cases when there is no file extension that would at least make it possible to write scripts to be installed as system commands.

  1. use a special binary name:
#!/usr/bin/env deno
// ... ts code here

and

#!/usr/bin/env denojs
// ... js code here

This is the last one that I thought about and doesn't seem that bad. But it would mean either installing two binaries or one binary and a symlink.

All of the above have some issues, meybe someone has a better idea.

All JS currently passes through the TypeScript compiler currently, which is likely to persist, especially if we end up supporting type checking of JS via JSDocs. I do not think treating the vast majority of extensionless files as TypeScript though would create any userland problems, as the compiler is in a very loose mode, and any JavaScript that would be invalid TypeScript is probably a bad idea anyways. It would be best to treat it as TypeScript and then only deal with it if it actually causes problems we can't solve.

@kitsonk I don't think it's that simple. Even this trivial example:

let x = 1;
x = 'a';
console.log(x);

passes as JS but not as TS - both in Deno itself - see:

screen shot 2018-10-12 at 5 49 29 pm

Update:

I know that in theory TS is syntactically a superset of JS, but even though all JS code can in principle be parsed as TS, it doesn't necessarily mean that it doesn't violate the type system.

To make it work we would need to use any as the type for all variables that are not explicitly typed instead of using the type inference that is used everywhere else, and we would need to do it for all TS code if we want any JS code to be parsed as TS with no type errors.

As for me, I'd rather go the other way and compile the TS code with --strict by default (with a --loose switch in deno instead of non-strict by default) but that's just me and I know it will probably be non-strict by default (with hopefully a short switch like -s for strict parsing) but I hope Deno will not be even less strict than the default non-strict tsc mode, by switching off the type inference.

@rsp there are a few issues here that then get all coupled together that should be solved before we try to solve JavaScript support for shebang:

  • Supporting ES Modules natively (#975)
  • Supporting the privileged side determining the media type (#702)
  • Supporting CheckJS (#976)

(A couple of them are things that have been talked about but I realised didn't have issues)

My opinion is that we could/should implement shebang support for TypeScript only and then deliver the right solution for JavaScript as an incremental change.

Perhaps "how should deno handle extensionless files" should be a separate issue from "deno should ignore shebangs"?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zugende picture zugende  路  3Comments

ry picture ry  路  3Comments

benjamingr picture benjamingr  路  3Comments

sh7dm picture sh7dm  路  3Comments

kyeotic picture kyeotic  路  3Comments