14

So I have the standard folder structure

dist/
src/

where src has my .ts files and dist has my .js files. (I have "outDir":"dist" in my tsconfig.json file, and "includes" set to 'src').

Note that 'dist' is in my gitignore file, so it is not in version control, and so when it goes to Travis or CircleCI, nothing is in the dist folder until I run tsc.

Here is the problem - if I run npm install first - it will fail because I have this in my package.json:

"bin":{
  "foo" :"dist/cli.js"   // dist/cli.js does not exist yet
}

but if I run tsc first - tsc will then be missing dependencies that it needs for compilation, which arrive if I run npm install.

The only thing I can think of to solve this, is to install all the necessary tsc dependencies first, then run tsc, then run npm install --production.

However that is not the most convenient thing to do.

Has anyone run into this problem and found a good solution?

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 1
    I'm not able to reproduce the problem you describe in your question. I create a new directory, put a `package.json` with the specification you show in your question, add a couple random dependencies, and run `npm install` without any problem whatsoever. – Louis Jun 04 '18 at 11:51
  • 4
    You probably have `install`, or `prepare` script that does something not supposed to. Can you post your package.json? You should probably use `prepublishOnly`. Just having `bin` will not cause `npm install` to fail – unional Jun 06 '18 at 07:04

5 Answers5

4

I don't remember having this problem but in at least one case I did something that will work around the issue.

I put an index.js in the root folder that runs the actual dependency in dist. Then the bin that npm looks for is a file that's present, and it shouldn't freak out.

It won't work until tsc is run, of course. But it should resolve your chicken and egg problem.

SomeCallMeTim
  • 4,503
  • 2
  • 28
  • 27
  • the problem that occurs when I try this is that get a shell function to properly/easily locate a nodejs script, when that nodejs script is not in the PATH. – Alexander Mills Jun 01 '18 at 01:58
  • I guess the best way to do that is the accepted answer here: https://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within – Alexander Mills Jun 01 '18 at 01:59
4

I would check-in a file ./lib/cli the file contents are

#!/usr/bin/env node
require('../dist/cli.js')

and just run npm normally followed by tsc.

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
mohamed hegazy
  • 9,191
  • 4
  • 30
  • 21
2

It looks like preinstall script is what you need

Add in your package.json file as

{
  "scripts": {
    "preinstall" : "tsc ..." // < build stuff
  }
}

Reference https://docs.npmjs.com/misc/scripts

Simone Sanfratello
  • 1,520
  • 1
  • 10
  • 21
  • right but then I still won't have the NPM dependencies I need for tsc to run properly – Alexander Mills May 31 '18 at 06:30
  • usually preinstall script does a lot of operations, I think you should write it properly in bash (or js with shebang), something like "pre-install.sh", then in that script you can do all the tasks you need, like installing "npm i tsc" and " tsc ..." – Simone Sanfratello May 31 '18 at 07:43
1

The answer from @wkrueger is close.

The goal here is to just allows a cheesy step to work without actually making it do anything useful. That cheesy step is making the file references by bin executable, during the install step, which for a local module only makes sense for non-transpiled JavaScript, as for transpiled code the file won't exist yet. Luckily, this cheesy step doesn't actually care if the file is usable or not, it just needs it to exist so it can chmod it without failure.

My work-around for this problem is to simply checkin an empty dist/index.js file:

touch dist/index.js

Then add dist back to .gitignore as you don't want to checkin real builds of the file.

In NPM 6 I would have used a preinstall script in package.json but this has broken in NPM 7, which I happen to be using and I'm not super interested in converting to hooks just to work around this issue. If you are using NPM 6 or earlier the preinstall script would look like this:

  ...
  "scripts": {
    "preinstall": "mkdir -p dist/ && touch dist/index.js"
  }
  ...
huntharo
  • 2,616
  • 21
  • 23
0

Absolutely not your answer, but I usually just prefer to commit the javascript.

Downside: Tons of additional git history/bloat.

My points:

  • In the end you are producing a javascript project. So one should test the javascript runtime, and if that project is supposed to also be consumed from typescript, test the generated .d.ts;
  • The TSC version which generates your code is not supposed to be another moving part for the tests AND your consumers. You would test the generated output, not your source against many TSC versions.
  • By reducing the "moving parts" boundary by including the .js and .d.ts, you gain much more (= predictability) than what you lose (git history bloat).
wkrueger
  • 1,281
  • 11
  • 21