Alexander Mills
Alexander Mills

Reputation: 100200

How to resolve Chicken/Egg situation with `tsc` and `npm install`?

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?

Upvotes: 14

Views: 1087

Answers (5)

huntharo
huntharo

Reputation: 2856

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"
  }
  ...

Upvotes: 1

wkrueger
wkrueger

Reputation: 1373

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).

Upvotes: 0

mohamed hegazy
mohamed hegazy

Reputation: 9509

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.

Upvotes: 4

Simone Sanfratello
Simone Sanfratello

Reputation: 1610

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

Upvotes: 2

SomeCallMeTim
SomeCallMeTim

Reputation: 4802

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.

Upvotes: 4

Related Questions