├── .gitignore ├── README.md ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── pkg1 ├── .gitignore ├── .npmignore ├── package-lock.json ├── package.json ├── src │ ├── foo.ts │ └── index.ts └── tsconfig.json ├── pkg2 ├── .gitignore ├── .npmignore ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── pkg3 ├── .gitignore ├── .npmignore ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── tsconfig.json └── tsconfig.settings.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | lerna-debug.log 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lerna + Project References 2 | 3 | This is a "bare minimum" repo that shows one way to configure TypeScript Project References with lerna. There are a lot of different ways you can set things up and this isn't intended to be authoratitive guidance or exclusionary of other ways that might work better in your project. 4 | 5 | # Setting up this repo 6 | 7 | ``` 8 | > git clone https://github.com/RyanCavanaugh/learn-a.git 9 | > cd learn-a 10 | > npm install 11 | > lerna bootstrap 12 | > tsc -b packages 13 | ``` 14 | 15 | Note that you'll need a 3.0 version of `tsc` (currently available at `npm install -g typescript@next`). 16 | 17 | ### General Structure 18 | 19 | As with a normal lerna repo, there's a `packages` folder. Inside we have three creatively named packages `pkg1`, `pkg2`, and `pkg3`. 20 | 21 | ``` 22 | packages/ 23 | | tsconfig.settings.json 24 | | tsconfig.json 25 | | pkg1/ 26 | | tsconfig.json 27 | | src/ 28 | | | (typescript files) 29 | | lib/ 30 | | | (javascript files) 31 | | | (.d.ts files) 32 | | pkg2/ 33 | | (same as pkg1) 34 | | pkg3/ 35 | | (same as pkg1) 36 | ``` 37 | 38 | Let's review each file in the repo and explain what's going on 39 | 40 | #### `tsconfig.settings.json` 41 | ```js 42 | { 43 | "compilerOptions": { 44 | // Always include these settings 45 | "composite": true, 46 | "declaration": true, 47 | "declarationMap": true, 48 | "sourceMap": true, 49 | 50 | // These settings are totally up to you 51 | "esModuleInterop": true, 52 | "target": "es5", 53 | "module": "commonjs", 54 | "strict": true 55 | } 56 | } 57 | ``` 58 | This file contains the "default" settings that all packages will use for compilation. You will definitely want the `composite`, `declaration`, `declarationMap`, and `sourceMap` settings enabled for all projects, so include those in this file. Other settings, like `target` and `strict`, can be specified here if you'd like to enable them by default. You'll also be able to override these settings on a per-package basis if needed. 59 | 60 | #### `tsconfig.json` 61 | ```json 62 | { 63 | "files": [], 64 | "references": [ 65 | { "path": "pkg1" }, 66 | { "path": "pkg2" }, 67 | { "path": "pkg3" } 68 | ] 69 | } 70 | ``` 71 | This file is pretty simple - simply list the packages that need to be built with `tsc` in the `references` array. 72 | You should also include `"files": []` in this file - this will prevent an incorrect invocation of `tsc` without `-b` from trying to build the entire packages folder source files as one compilation (which will fail, but drop a bunch of .js files in random places as an annoying side effect). 73 | 74 | #### `packages/pkg2/tsconfig.json` 75 | 76 | We'll just cover one of the `pkg1` / `pkg2` / `pkg3` packages since they're basically identical for the purposes of this demo. Here's `pkg2`'s `tsconfig.json`: 77 | ```json 78 | { 79 | "extends": "../tsconfig.settings.json", 80 | "compilerOptions": { 81 | "outDir": "lib", 82 | "rootDir": "src" 83 | }, 84 | "references": [ 85 | { "path": "../pkg1" } 86 | ] 87 | } 88 | ``` 89 | The `extends` property pulls in the settings we wrote in `tsconfig.settings.json`, so we don't have to duplicate any settings described there. 90 | 91 | In `compilerOptions`, we've set `outDir` to `lib` and `rootDir` to `src`, then placed all my `.ts` files in `src`. This means `src/index.ts` will build to `lib/index.js` and `lib/index.d.ts`. This is also the place where you could override settings like `strict` or `target` if you needed to change them on a per-project basis. 92 | 93 | In the `references` array, we list the paths to the other projects' `tsconfig.json` files (or containing folders, as shown here). This will both ensure that we locate the `.d.ts` files correctly, and set up a proper build ordering. 94 | 95 | #### `packages/pkg2/src/index.ts` 96 | ```ts 97 | import * as p1 from '@ryancavanaugh/pkg1'; 98 | 99 | export function fn4() { 100 | p1.fn(); 101 | } 102 | ``` 103 | Nothing unusual going on here. We import and export with the usual syntax. Notably, if you open this repo in an editor, you can still "Go to Definition (F12)" on `p1.fn` here and land in `pkg1/foo.ts` - the original sourcecode - even though "under the covers" it's using the much faster `.d.ts` file for typechecking. 104 | 105 | #### `packages/pkg2/package.json` 106 | Here are the relevant excerpts from the `package.json`: 107 | ```json 108 | { 109 | "main": "lib/index.js", 110 | "typings": "lib/index.d.ts", 111 | "scripts": { 112 | "prepublishOnly": "tsc -b ." 113 | }, 114 | "devDependencies": { 115 | "typescript": "^3.0.0-dev.20180626" 116 | } 117 | } 118 | ``` 119 | 120 | Because we build to `lib`, we need to set `main` to the `.js` file there *and* `typings` to the `.d.ts` file. 121 | 122 | In `scripts`, we use the local copy of `tsc` (listed here as a dev dependency) to run a *build mode* compilation on the project. This will ensure that the `lib` folder is always built before `npm publish`, and blocks any publishes that try to push non-compiling code. 123 | 124 | #### `packages/pkg2/.npmignore` / `packages/pkg2/.gitignore` 125 | 126 | *.gitignore* 127 | ``` 128 | lib/ 129 | ``` 130 | 131 | *.npmignore* 132 | ``` 133 | # Empty, but needs to exist 134 | ``` 135 | 136 | The `.gitignore` stops us from checking in build outputs, which is generally a good idea. By default, `npm` won't publish files that are ignored by `git`, so we need a separate `.npmignore` file so that the `lib` folder still gets published! 137 | 138 | # Workflow 139 | 140 | All your lerna commands and workflow will work as expected here. 141 | 142 | To build the TypeScript projects, you can run individual builds with `tsc -b`: 143 | ``` 144 | > tsc -b packages/pkg3 145 | ``` 146 | Or just build everything: 147 | ``` 148 | > tsc -b packages 149 | ``` 150 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.11.0", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "3.0.2" 7 | } 8 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "typescript": { 6 | "version": "3.0.0-dev.20180626", 7 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.0-dev.20180626.tgz", 8 | "integrity": "sha512-OQH9osIC4CdsVzVvsb2RenRTVPRKwSIMIpRy2J42XNOEUP+vhX56BX1Z47K3l//LEGY0xG7zF7qVKCDlUhhrlg==", 9 | "dev": true 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "lerna": "^2.11.0", 4 | "typescript": "^3.0.0-dev.20180626" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/pkg1/.gitignore: -------------------------------------------------------------------------------- 1 | lib -------------------------------------------------------------------------------- /packages/pkg1/.npmignore: -------------------------------------------------------------------------------- 1 | # Empty to override .gitignore ignoring the output folder 2 | -------------------------------------------------------------------------------- /packages/pkg1/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "typescript": { 6 | "version": "3.0.0-dev.20180626", 7 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.0-dev.20180626.tgz", 8 | "integrity": "sha512-OQH9osIC4CdsVzVvsb2RenRTVPRKwSIMIpRy2J42XNOEUP+vhX56BX1Z47K3l//LEGY0xG7zF7qVKCDlUhhrlg==" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/pkg1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ryancavanaugh/pkg1", 3 | "version": "3.0.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "prepublishOnly": "tsc -b ." 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "typescript": "^3.0.0-dev.20180626" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/pkg1/src/foo.ts: -------------------------------------------------------------------------------- 1 | export function fn() { 2 | return "Hello world"; 3 | } 4 | -------------------------------------------------------------------------------- /packages/pkg1/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./foo"; 2 | -------------------------------------------------------------------------------- /packages/pkg1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | } 7 | } -------------------------------------------------------------------------------- /packages/pkg2/.gitignore: -------------------------------------------------------------------------------- 1 | lib -------------------------------------------------------------------------------- /packages/pkg2/.npmignore: -------------------------------------------------------------------------------- 1 | # Empty to override .gitignore ignoring the output folder 2 | -------------------------------------------------------------------------------- /packages/pkg2/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "typescript": { 6 | "version": "3.0.0-dev.20180626", 7 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.0-dev.20180626.tgz", 8 | "integrity": "sha512-OQH9osIC4CdsVzVvsb2RenRTVPRKwSIMIpRy2J42XNOEUP+vhX56BX1Z47K3l//LEGY0xG7zF7qVKCDlUhhrlg==" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/pkg2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ryancavanaugh/pkg2", 3 | "version": "3.0.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "prepublishOnly": "tsc -b ." 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@ryancavanaugh/pkg1": "^3.0.2" 16 | }, 17 | "devDependencies": { 18 | "typescript": "^3.0.0-dev.20180626" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/pkg2/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as p1 from '@ryancavanaugh/pkg1'; 2 | 3 | export function fn4() { 4 | p1.fn(); 5 | } 6 | 7 | -------------------------------------------------------------------------------- /packages/pkg2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { "path": "../pkg1" } 9 | ] 10 | } -------------------------------------------------------------------------------- /packages/pkg3/.gitignore: -------------------------------------------------------------------------------- 1 | lib -------------------------------------------------------------------------------- /packages/pkg3/.npmignore: -------------------------------------------------------------------------------- 1 | # Empty to override .gitignore ignoring the output folder 2 | -------------------------------------------------------------------------------- /packages/pkg3/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "typescript": { 6 | "version": "3.0.0-dev.20180626", 7 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.0-dev.20180626.tgz", 8 | "integrity": "sha512-OQH9osIC4CdsVzVvsb2RenRTVPRKwSIMIpRy2J42XNOEUP+vhX56BX1Z47K3l//LEGY0xG7zF7qVKCDlUhhrlg==" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/pkg3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ryancavanaugh/pkg3", 3 | "version": "3.0.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "prepublishOnly": "tsc -b ." 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@ryancavanaugh/pkg1": "^3.0.2", 16 | "@ryancavanaugh/pkg2": "^3.0.2" 17 | }, 18 | "devDependencies": { 19 | "typescript": "^3.0.0-dev.20180626" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/pkg3/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as p1 from "@ryancavanaugh/pkg1"; 2 | import * as p2 from "@ryancavanaugh/pkg2"; 3 | 4 | p1.fn(); 5 | p2.fn4(); 6 | -------------------------------------------------------------------------------- /packages/pkg3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { "path": "../pkg1" }, 9 | { "path": "../pkg2" } 10 | ] 11 | } -------------------------------------------------------------------------------- /packages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "pkg1" }, 5 | { "path": "pkg2" }, 6 | { "path": "pkg3" } 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/tsconfig.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "sourceMap": true, 8 | "strict": true, 9 | "composite": true, 10 | "esModuleInterop": true 11 | } 12 | } 13 | --------------------------------------------------------------------------------