65

Consider the following scenario:

  • There is a code library. The library is written in TypeScript and the typescript code is published in GitHub. The package.json file has a build script which creates JavaScript files based on the TypeScript code and a publish script which then places the resulting JS files on npm.
  • I make a fork of the GitHub repo, make some modifications to the typescript files and push those changes to GitHub. (I also open a PR to the original GitHub repo but there is a time lage before these changes can be merged.)
  • I wish to consume these code changes in a downstream NPM package so in the downstream packages I change the reference (in the downstream's package.json file) to the modified package to the GitHub URL of my fork and do an npm install.

This doesn't work because:

  • The package.json file of the modified package does not list the typescript files in the dist field, only the automatically generated JS files so the TypeScript files are not pulled during the npm install.
  • The compiled JS files aren't present since they aren't checked in to GitHub.

How can I solve this? Is there a way that I can modify the behavoir of npm install so that it fetches files in the repo that aren't in dist and then runs the build script during the install?

2
  • I can only think of maintaining a separate build branch (a branch with same name as a folder would sometimes confuses Git, so avoid dist). Commented Aug 30, 2018 at 14:38
  • I like how well this question is phrased, considering it had never been edited yet (5 years 9 months after being published). An example of an exceptionally good StackOverflow question. Commented Apr 7 at 9:25

3 Answers 3

28

I had the same problem. Saw a lot of articles about monorepos (links below), but not much about how to split a TypeScript project into separate repositories.

In short, you need to build JS files at one step or the other.

See https://github.com/stared/yarn-adding-pure-typescript-package-example for a working code example.

So, there are a few solutions. Let's say that the repository is someuser/package-to-import

Postinstall

Using yarn you can get the project directly from a GitHub repository:

yarn add someuser/package-to-import#master

(or the same with npm install someuser/package-to-import#master)

If you work on a fork of this repository, you can make your life easier by adding to package.json in package-to-import repo this part:

  "scripts": {
    ...,
    "postinstall": "tsc --outDir ./build"
  }

Now it just works with yarn add/upgrade with no extra scripts. Alternatively, you can do it semi-manually, i.e. create a script in your main repository package.json

  "scripts": {
    ...,
    "upgrade-package-to-import": "yarn upgrade package-to-import && cd node_modules/package-to-import && yarn build"
  }

Link

There is a different approach to clone the repository into a different folder, build the files, and link it.

git clone [email protected]:someuser/package-to-import.git
cd package-to-import
npm run build  # or any other instruction to build
npm link

If you use yarn, the two last lines would be:

yarn build
yarn link

Then, enter the folder of your project and write

npm link package-to-import

or in case of yarn

yarn link package-to-import

These are symlinks, so when you pull from the repo and build files, you will use the updated version.

See also:

Monorepos

An entirely different approach. With mixed advice for using git submodules:

3
  • So my postinstall in the module is "postinstall": "tsc -d -p .". If I now run npx tsc in my project that uses the module, it tries to compile the ts files in the module.
    – Elias
    Commented May 1, 2021 at 12:33
  • I think it tries to import the .ts files instead of the defition.
    – Elias
    Commented May 1, 2021 at 12:39
  • stackoverflow.com/questions/67346535/… made a post in case someone cares
    – Elias
    Commented May 1, 2021 at 12:57
13

The docs for the prepack script suggest that it is run after a dependency is installed from a git repo. Try putting something like this in the package.json of the git dependency:

{
  "scripts": {
    "prepack": "call the build script"
  }
}

This should build the package after you npm install it, which sounds like what you want to do. I'm not sure if there are any other problems you are having beyond that.

3
  • 6
    Looks like prepare also works. -- docs.npmjs.com/misc/scripts
    – Polv
    Commented Dec 21, 2019 at 8:50
  • Recently, I've found that prepack hasn't been working for me, despite what the documentation says, but that prepare works well. However, this seems to have gathered 13 upvotes, so I'm reluctant to change my answer which seems to have helped many people. Is this working for people? Or are people, like me, finding this doesn't work and using @Polv's prepare instead?
    – Half
    Commented Sep 26, 2023 at 0:56
  • @Half You can always make an addition to the answer, you don't have to change it. Commented Apr 7 at 9:28
4

Build the files in your fork and commit those to your fork's repo/branch.

I was running into the exact same issue that you were. Amazing that I was able to Google my way onto your StackOverflow question, really, because it's a difficult issue to describe.

In short, I had to fork the react-native-webview library in order to update the kotlinVersion in order to support Gradle Plugin 6.0.0.

I forked it just fine and bumped that version just fine, as you can see here.

I then added this specific fork and branch to my package.json file:

"react-native-webview": "github:cntral/react-native-webview#5_8_2"

I would do npm install after this and would get error Command "tsc" not found.

After much digging, there's a "prepack": "yarn build" script in this library's package.json that will try to run whenever it is included as a git dependency like this. But we're not using TypeScript so it tries to run yarn build which runs yarn tsc and tsc can't be found.

Okay, so I tried to add typescript as a devDependency to my package.json to see if that would fix it. It didn't. I tried using yarn install instead of npm install to see if that would fix it. It would install it just fine but if you looked at the node_modules/react-native-webview directory, the converted JavaScript files that were supposed to be in lib/ weren't there. So that didn't work.

Then I thought, if all this is doing is trying to transpile the TypeScript files into normal JavaScript so it can be consumed by our app, why not do that in our fork and commit those .js files. So I did.

I ran yarn build in my fork of react-native-webview, removed the lib/ directory from .gitignore and then committed those changes, as you can see here.

Then I also had to remove the "prepack": "yarn build" from the package.json of the fork so it wouldn't try to build the library and run tsc again, which was done here.

After that our app was able to consume it just fine, like any other library we consume off of npm or GitHub.

Hope that helps others.

6
  • Problem with this is that if you want to pull request your changes to the source, they probably don't want the built files or the changes you made.
    – meelash
    Commented Jan 4, 2021 at 17:00
  • 1
    @meelash Yeah, you're probably right. What's your suggestion then? A postinstall? Commented Jan 4, 2021 at 17:04
  • 1
    I ended up doing the same to get a fork running. If there's a way to avoid pushing the dist files to my custom fork, I happily take it. Though there isn't any good ressource around where and how one should do it. Apparently, there is a prepare step, but I could not get my build command to work from there.
    – k0pernikus
    Commented Aug 25, 2021 at 15:10
  • @k0pernikus That was exactly my experience as well. Decided to move on after a while and get back to real work instead. : ) Commented Aug 25, 2021 at 15:30
  • I opened a follow-up question, let's see how this goes stackoverflow.com/q/68925785/457268
    – k0pernikus
    Commented Aug 25, 2021 at 15:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.