├── apps ├── vite │ ├── .gitignore │ ├── src │ │ ├── vite-env.d.ts │ │ ├── main.tsx │ │ ├── App.css │ │ ├── App.tsx │ │ ├── index.css │ │ └── assets │ │ │ └── react.svg │ ├── tsconfig.node.json │ ├── vite.config.ts │ ├── index.html │ ├── tsconfig.json │ ├── package.json │ └── public │ │ └── vite.svg ├── cra │ ├── .gitignore │ ├── src │ │ ├── hooks │ │ │ └── test.tsx │ │ ├── react-app-env.d.ts │ │ ├── index.tsx │ │ └── components │ │ │ ├── app.spec.tsx │ │ │ └── app.tsx │ ├── tsconfig.paths.json │ ├── public │ │ └── index.html │ ├── tsconfig.json │ ├── package.json │ └── craco.config.js ├── nextjs │ ├── .gitignore │ ├── pages │ │ └── index.tsx │ ├── next-env.d.ts │ ├── package.json │ ├── tsconfig.json │ └── next.config.js ├── webpack │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.build.json │ ├── package.json │ └── webpack.config.js ├── ts-node │ ├── tsconfig.json │ ├── src │ │ └── index.ts │ └── package.json ├── storybook │ ├── tsconfig.json │ ├── src │ │ ├── components │ │ │ └── foo.tsx │ │ └── index.stories.tsx │ ├── .storybook │ │ ├── preview.ts │ │ └── main.ts │ ├── babel.config.json │ └── package.json ├── jest-babel │ ├── tsconfig.json │ ├── src │ │ └── index.ts │ ├── test │ │ └── index.spec.ts │ ├── babel.config.js │ └── package.json ├── nestjs │ ├── nest-cli.json │ ├── tsconfig.build.json │ ├── src │ │ ├── main.ts │ │ ├── app.service.ts │ │ ├── app.module.ts │ │ └── app.controller.ts │ ├── tsconfig.json │ └── package.json ├── rollup │ ├── tsconfig.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ ├── rollup.config.ts │ └── package.json └── jest-tsjest │ ├── src │ └── index.ts │ ├── tsconfig.json │ ├── test │ └── index.spec.ts │ ├── jest.config.ts │ └── package.json ├── .github ├── FUNDING.yml └── workflows │ └── tests.yml ├── .npmrc ├── packages ├── foo │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.build.json │ └── package.json ├── bar │ ├── tsconfig.json │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── package.json └── components │ ├── tsconfig.json │ ├── tsconfig.build.json │ ├── src │ └── button.tsx │ └── package.json ├── .eslintignore ├── pnpm-workspace.yaml ├── .gitignore ├── media ├── monorepo.png ├── find-usage.gif └── build-output.png ├── .eslintrc.js ├── renovate.json ├── tsconfig.json ├── tsconfig.build.json ├── package.json ├── LICENSE └── README.md /apps/vite/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: NiGhTTraX 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=true 2 | -------------------------------------------------------------------------------- /apps/cra/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .eslintcache 3 | -------------------------------------------------------------------------------- /apps/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | 3 | .vercel 4 | -------------------------------------------------------------------------------- /apps/webpack/src/index.ts: -------------------------------------------------------------------------------- 1 | import "@nighttrax/bar"; 2 | -------------------------------------------------------------------------------- /packages/foo/src/index.ts: -------------------------------------------------------------------------------- 1 | export const meaningOfLife = 42; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | !.* 3 | dist 4 | build 5 | .next 6 | -------------------------------------------------------------------------------- /apps/cra/src/hooks/test.tsx: -------------------------------------------------------------------------------- 1 | export const useTest = () => 23; 2 | -------------------------------------------------------------------------------- /apps/vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/**' 3 | - 'apps/**' 4 | -------------------------------------------------------------------------------- /apps/cra/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/ts-node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /apps/webpack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/bar/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/foo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /apps/storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/dist 3 | results 4 | .nyc_output 5 | *.tsbuildinfo 6 | -------------------------------------------------------------------------------- /media/monorepo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/hmmm/master/media/monorepo.png -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: "@nighttrax/eslint-config-tsx", 3 | }; 4 | -------------------------------------------------------------------------------- /media/find-usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/hmmm/master/media/find-usage.gif -------------------------------------------------------------------------------- /media/build-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/hmmm/master/media/build-output.png -------------------------------------------------------------------------------- /apps/storybook/src/components/foo.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Foo = () => foo; 4 | -------------------------------------------------------------------------------- /apps/jest-babel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["jest"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/jest-babel/src/index.ts: -------------------------------------------------------------------------------- 1 | import { meaningOfLife } from "@nighttrax/foo"; 2 | 3 | export const whatIsTheMeaningOfLife = () => meaningOfLife; 4 | -------------------------------------------------------------------------------- /apps/nestjs/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src", 4 | "entryFile": "apps/nestjs/src/main" 5 | } 6 | -------------------------------------------------------------------------------- /apps/rollup/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "resolveJsonModule": true, 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/jest-tsjest/src/index.ts: -------------------------------------------------------------------------------- 1 | import { meaningOfLife } from "@nighttrax/foo"; 2 | 3 | export const whatIsTheMeaningOfLife = () => meaningOfLife; 4 | -------------------------------------------------------------------------------- /apps/ts-node/src/index.ts: -------------------------------------------------------------------------------- 1 | import { meaningOfLife } from "@nighttrax/foo"; 2 | 3 | // eslint-disable-next-line no-console 4 | console.log(meaningOfLife); 5 | -------------------------------------------------------------------------------- /packages/bar/src/index.ts: -------------------------------------------------------------------------------- 1 | import { meaningOfLife } from "@nighttrax/foo"; 2 | 3 | // eslint-disable-next-line no-console 4 | console.log(meaningOfLife); 5 | -------------------------------------------------------------------------------- /apps/nextjs/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button } from "@nighttrax/components/button"; 3 | 4 | export default () => 12 | ); 13 | -------------------------------------------------------------------------------- /apps/cra/src/components/app.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button } from "@nighttrax/components/button"; 3 | import { meaningOfLife } from "@nighttrax/foo"; 4 | import { useTest } from "@hooks/test"; 5 | 6 | export const App = () => { 7 | useTest(); 8 | 9 | return ( 10 |
11 | {meaningOfLife} 12 |
14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /apps/jest-babel/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ["@babel/preset-env", { targets: { node: "current" } }], 4 | "@babel/preset-typescript", 5 | ], 6 | 7 | plugins: [ 8 | [ 9 | "module-resolver", 10 | { 11 | alias: { 12 | "^@nighttrax/(.+)": "../../packages/\\1/src", 13 | }, 14 | }, 15 | ], 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /apps/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/nestjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "module": "commonjs", 6 | "declaration": true, 7 | "removeComments": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "allowSyntheticDefaultImports": true, 11 | "target": "es2017", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "incremental": true, 15 | "noEmit": false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "sourceMap": true, 6 | "strict": true, 7 | "declaration": true, 8 | "declarationMap": true, 9 | "noEmitOnError": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "types": [], 13 | "jsx": "react", 14 | "noEmit": false 15 | }, 16 | "exclude": [ 17 | "node_modules", 18 | "dist" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/foo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/foo", 3 | "version": "1.0.0", 4 | "main": "dist/index", 5 | "types": "dist/index", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "build": "pnpm run clean && pnpm run compile", 11 | "clean": "rimraf ./dist", 12 | "compile": "tsc -p tsconfig.build.json", 13 | "prepublishOnly": "pnpm run build" 14 | }, 15 | "devDependencies": { 16 | "rimraf": "~5.0.0", 17 | "typescript": "~4.9.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/jest-tsjest/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "@jest/types"; 2 | import { pathsToModuleNameMapper } from "ts-jest"; 3 | // Load the config which holds the path aliases. 4 | import { compilerOptions } from "../../tsconfig.json"; 5 | 6 | const config: Config.InitialOptions = { 7 | preset: "ts-jest", 8 | 9 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { 10 | // This has to match the baseUrl defined in tsconfig.json. 11 | prefix: "/../../", 12 | }), 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /apps/cra/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | React App 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/ts-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/ts-node", 3 | "description": "Example of using ts-node in a TS monorepo", 4 | "private": true, 5 | "version": "1.0.0", 6 | "scripts": { 7 | "start": "ts-node -r tsconfig-paths/register src/index.ts", 8 | "test": "pnpm run start" 9 | }, 10 | "dependencies": { 11 | "@nighttrax/foo": "workspace:*" 12 | }, 13 | "devDependencies": { 14 | "@types/node": "~20.11.0", 15 | "ts-node": "~10.9.0", 16 | "tsconfig-paths": "~4.2.0", 17 | "typescript": "~4.9.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/bar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/bar", 3 | "version": "1.0.0", 4 | "main": "dist/index", 5 | "types": "dist/index", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "build": "pnpm run clean && pnpm run compile", 11 | "clean": "rimraf ./dist", 12 | "compile": "tsc -p tsconfig.build.json", 13 | "prepublishOnly": "pnpm run build" 14 | }, 15 | "dependencies": { 16 | "@nighttrax/foo": "~1.0.0" 17 | }, 18 | "devDependencies": { 19 | "rimraf": "~5.0.0", 20 | "typescript": "~4.9.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/jest-tsjest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/jest-example", 3 | "description": "Example of using jest with a TS monorepo", 4 | "private": true, 5 | "version": "1.0.0", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "@nighttrax/foo": "workspace:*" 11 | }, 12 | "devDependencies": { 13 | "@types/jest": "~29.5.0", 14 | "@jest/types": "~29.6.0", 15 | "@types/node": "~20.11.0", 16 | "jest": "~29.7.0", 17 | "ts-jest": "~29.1.0", 18 | "ts-node": "~10.9.0", 19 | "typescript": "~4.9.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/rollup/rollup.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import typescript from "@rollup/plugin-typescript"; 3 | import { defineConfig } from "rollup"; 4 | import pkg from "./package.json"; 5 | 6 | export default defineConfig({ 7 | input: "src/index.tsx", 8 | // Without this, rollup would inline our monorepo dependencies. 9 | external: Object.keys(pkg.dependencies), 10 | plugins: [ 11 | typescript({ 12 | tsconfig: "./tsconfig.build.json", 13 | }), 14 | ], 15 | output: [{ dir: "./dist", format: "cjs", sourcemap: true }], 16 | }); 17 | -------------------------------------------------------------------------------- /apps/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/webpack-example", 3 | "description": "Example of using webpack in a TS monorepo", 4 | "private": true, 5 | "version": "1.0.0", 6 | "scripts": { 7 | "clean": "rimraf dist", 8 | "build": "webpack" 9 | }, 10 | "dependencies": { 11 | "@nighttrax/bar": "workspace:*" 12 | }, 13 | "devDependencies": { 14 | "rimraf": "~5.0.0", 15 | "webpack": "~5.90.0", 16 | "webpack-cli": "~5.1.0", 17 | "ts-loader": "~9.5.0", 18 | "tsconfig-paths-webpack-plugin": "~4.1.0", 19 | "typescript": "~4.9.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/jest-babel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/jest-babel-example", 3 | "description": "Example of using jest + babel with a TS monorepo", 4 | "private": true, 5 | "version": "1.0.0", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "@nighttrax/foo": "workspace:*" 11 | }, 12 | "devDependencies": { 13 | "@babel/core": "~7.24.0", 14 | "@babel/preset-env": "~7.24.0", 15 | "@babel/preset-typescript": "~7.23.0", 16 | "@types/jest": "~29.5.0", 17 | "babel-plugin-module-resolver": "~5.0.0", 18 | "jest": "~29.7.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/rollup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/rollup-example", 3 | "private": true, 4 | "version": "1.0.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "clean": "rimraf dist", 9 | "build": "rollup -c --bundleConfigAsCjs" 10 | }, 11 | "dependencies": { 12 | "@nighttrax/foo": "workspace:*" 13 | }, 14 | "devDependencies": { 15 | "@rollup/plugin-typescript": "~11.1.5", 16 | "@types/node": "~20.11.0", 17 | "rimraf": "~5.0.0", 18 | "rollup": "~4.13.0", 19 | "tslib": "~2.6.0", 20 | "typescript": "~4.9.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/components", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "pnpm run clean && pnpm run compile", 6 | "clean": "rimraf ./dist", 7 | "compile": "tsc -p tsconfig.build.json", 8 | "prepublishOnly": "pnpm run build" 9 | }, 10 | "peerDependencies": { 11 | "react": "~18.2.0" 12 | }, 13 | "dependencies": { 14 | "@nighttrax/foo": "workspace:*" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "~18.2.0", 18 | "react": "~18.2.0", 19 | "react-dom": "~18.2.0", 20 | "rimraf": "~5.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/nextjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/nextjs-example", 3 | "private": true, 4 | "version": "1.0.0", 5 | "scripts": { 6 | "clean": "rimraf .next", 7 | "dev": "next dev", 8 | "build": "next build" 9 | }, 10 | "dependencies": { 11 | "next": "~14.0.0", 12 | "react": "~18.2.0", 13 | "react-dom": "~18.2.0", 14 | "@nighttrax/components": "workspace:*" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "~20.11.0", 18 | "@types/react": "~18.2.0", 19 | "@types/react-dom": "~18.2.0", 20 | "rimraf": "~5.0.0", 21 | "typescript": "~4.9.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); 3 | 4 | module.exports = { 5 | entry: "./src/index.ts", 6 | mode: "development", 7 | output: { 8 | filename: "bundle.js", 9 | }, 10 | resolve: { 11 | extensions: [".ts", ".tsx", ".js"], 12 | plugins: [new TsconfigPathsPlugin()], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.tsx?$/, 18 | loader: "ts-loader", 19 | options: { configFile: "tsconfig.build.json" }, 20 | }, 21 | ], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-monorepo", 3 | "description": "Template for setting up a TypeScript monorepo", 4 | "private": true, 5 | "workspaces": [ 6 | "packages/*", 7 | "apps/*" 8 | ], 9 | "scripts": { 10 | "docs": "doctoc --title '**Table of content**' README.md", 11 | "clean": "pnpm run -r clean", 12 | "build": "pnpm run -r build", 13 | "test": "pnpm run -r test", 14 | "lint": "eslint --ext js,ts,tsx ." 15 | }, 16 | "devDependencies": { 17 | "@nighttrax/eslint-config-tsx": "~12.0.0-alpha.2", 18 | "doctoc": "~2.2.0", 19 | "eslint": "~8.57.0", 20 | "eslint-plugin-import": "~2.29.0", 21 | "typescript": "~4.9.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/cra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.paths.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react-jsx", 21 | "noFallthroughCasesInSwitch": true, 22 | "skipLibCheck": true 23 | }, 24 | "include": [ 25 | "src" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /apps/vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 7 | "allowJs": false, 8 | "skipLibCheck": true, 9 | "esModuleInterop": false, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "module": "ESNext", 14 | "moduleResolution": "Node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "react-jsx", 19 | }, 20 | "include": ["src"], 21 | "references": [{ "path": "./tsconfig.node.json" }] 22 | } 23 | -------------------------------------------------------------------------------- /apps/nextjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve", 20 | "skipLibCheck": true, 21 | "incremental": true 22 | }, 23 | "exclude": [ 24 | "node_modules" 25 | ], 26 | "include": [ 27 | "next-env.d.ts", 28 | "**/*.ts", 29 | "**/*.tsx" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ $default-branch ] 7 | 8 | jobs: 9 | tests: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - uses: pnpm/action-setup@v3.0.0 17 | with: 18 | version: latest 19 | 20 | - name: Install Node 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: 20 24 | cache: 'pnpm' 25 | 26 | - name: Install dependencies 27 | run: pnpm i 28 | 29 | - name: Lint 30 | run: pnpm run lint 31 | 32 | - name: Build 33 | run: pnpm run build 34 | 35 | - name: Test 36 | run: pnpm run test 37 | -------------------------------------------------------------------------------- /apps/vite/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | } 13 | .logo:hover { 14 | filter: drop-shadow(0 0 2em #646cffaa); 15 | } 16 | .logo.react:hover { 17 | filter: drop-shadow(0 0 2em #61dafbaa); 18 | } 19 | 20 | @keyframes logo-spin { 21 | from { 22 | transform: rotate(0deg); 23 | } 24 | to { 25 | transform: rotate(360deg); 26 | } 27 | } 28 | 29 | @media (prefers-reduced-motion: no-preference) { 30 | a:nth-of-type(2) .logo { 31 | animation: logo-spin infinite 20s linear; 32 | } 33 | } 34 | 35 | .card { 36 | padding: 2em; 37 | } 38 | 39 | .read-the-docs { 40 | color: #888; 41 | } 42 | -------------------------------------------------------------------------------- /apps/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/vite-example", 3 | "description": "Example of using Vite in a TS monorepo", 4 | "private": true, 5 | "version": "1.0.0", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "preview": "vite preview", 11 | "clean": "rimraf dist" 12 | }, 13 | "dependencies": { 14 | "@nighttrax/components": "workspace:*", 15 | "@nighttrax/foo": "workspace:*", 16 | "react": "~18.2.0", 17 | "react-dom": "~18.2.0" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "~18.2.0", 21 | "@types/react-dom": "~18.2.0", 22 | "@vitejs/plugin-react": "~4.2.0", 23 | "rimraf": "~5.0.0", 24 | "typescript": "~4.9.3", 25 | "vite-tsconfig-paths": "~4.2.0", 26 | "vite": "~5.1.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/nestjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "clean": "rimraf dist", 7 | "prebuild": "pnpm run clean", 8 | "build": "nest build", 9 | "start:prod": "node dist/apps/nestjs/src/main", 10 | "start:dev": "nest start --watch" 11 | }, 12 | "dependencies": { 13 | "@nestjs/common": "~10.3.0", 14 | "@nestjs/core": "~10.3.0", 15 | "@nestjs/platform-express": "~10.3.0", 16 | "@nighttrax/foo": "workspace:*", 17 | "reflect-metadata": "~0.1.13", 18 | "rimraf": "~5.0.0", 19 | "rxjs": "~7.8.0" 20 | }, 21 | "devDependencies": { 22 | "@nestjs/cli": "~10.3.0", 23 | "@types/express": "~4.17.11", 24 | "@types/node": "~20.11.0", 25 | "ts-node": "~10.9.0", 26 | "tsconfig-paths": "~4.2.0", 27 | "typescript": "~4.9.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/storybook/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import { StorybookConfig } from "@storybook/react-webpack5"; 2 | import assert from "node:assert"; 3 | import { TsconfigPathsPlugin } from "tsconfig-paths-webpack-plugin"; 4 | 5 | const config: StorybookConfig = { 6 | stories: ["../src/*.stories.tsx"], 7 | addons: [], 8 | framework: { 9 | name: "@storybook/react-webpack5", 10 | options: {}, 11 | }, 12 | typescript: { 13 | check: true, 14 | }, 15 | webpackFinal: async (config) => { 16 | assert(config.resolve, "Can't extend the Storybook config"); 17 | 18 | // eslint-disable-next-line no-param-reassign 19 | config.resolve.plugins = [ 20 | ...(config.resolve.plugins || []), 21 | new TsconfigPathsPlugin({ 22 | extensions: config.resolve.extensions, 23 | }), 24 | ]; 25 | 26 | return config; 27 | }, 28 | }; 29 | 30 | export default config; 31 | -------------------------------------------------------------------------------- /apps/storybook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/storybook-example", 3 | "private": true, 4 | "version": "1.0.0", 5 | "dependencies": { 6 | "@nighttrax/components": "workspace:*", 7 | "react": "~18.2.0", 8 | "react-dom": "~18.2.0" 9 | }, 10 | "devDependencies": { 11 | "@babel/core": "~7.24.0", 12 | "@babel/preset-env": "~7.24.0", 13 | "@babel/preset-react": "~7.23.0", 14 | "@babel/preset-typescript": "~7.23.0", 15 | "@storybook/core-common": "~7.6.0", 16 | "@storybook/react": "~7.6.0", 17 | "@storybook/react-webpack5": "~7.6.0", 18 | "@types/react": "~18.2.21", 19 | "@types/react-dom": "~18.2.7", 20 | "babel-loader": "~9.1.0", 21 | "require-from-string": "~2.0.2", 22 | "rimraf": "~5.0.0", 23 | "storybook": "~7.6.0", 24 | "tsconfig-paths-webpack-plugin": "~4.1.0", 25 | "typescript": "~4.9.0", 26 | "webpack": "~5.90.0" 27 | }, 28 | "scripts": { 29 | "storybook": "storybook dev -p 6006", 30 | "build": "storybook build -o dist --quiet", 31 | "clean": "rimraf dist" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Andrei Picus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apps/vite/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@nighttrax/components/button"; 2 | import { meaningOfLife } from "@nighttrax/foo"; 3 | import React, { useState } from "react"; 4 | import reactLogo from "./assets/react.svg"; 5 | import "./App.css"; 6 | 7 | const App = () => { 8 | const [count, setCount] = useState(meaningOfLife); 9 | 10 | return ( 11 |
12 |
13 | 14 | Vite logo 15 | 16 | 17 | React logo 18 | 19 |
20 |

Vite + React

21 |
22 | 26 |

27 | Edit src/App.tsx and save to test HMR 28 |

29 |
30 |

31 | Click on the Vite and React logos to learn more 32 |

33 |
34 | ); 35 | }; 36 | 37 | export default App; 38 | -------------------------------------------------------------------------------- /apps/vite/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | const isNext12 = (config) => !!config.module.rules.find((rule) => rule.oneOf); 2 | 3 | const updateNextGreaterThan12Config = (config) => { 4 | const oneOfRule = config.module.rules.find((rule) => rule.oneOf); 5 | 6 | // Next 12 has multiple TS loaders, and we need to update all of them. 7 | const tsRules = oneOfRule.oneOf.filter( 8 | (rule) => rule.test && rule.test.toString().includes("tsx|ts") 9 | ); 10 | 11 | tsRules.forEach((rule) => { 12 | // eslint-disable-next-line no-param-reassign 13 | rule.include = undefined; 14 | }); 15 | 16 | return config; 17 | }; 18 | 19 | const updateNextLessThan12Config = (config) => { 20 | // Next < 12 uses a single Babel loader. 21 | const tsRule = config.module.rules.find( 22 | (rule) => rule.test && rule.test.toString().includes("tsx|ts") 23 | ); 24 | 25 | tsRule.include = undefined; 26 | tsRule.exclude = /node_modules/; 27 | 28 | return config; 29 | }; 30 | 31 | module.exports = { 32 | /** 33 | * You can use the following experimental flag if you're on Next >= 10.1.0. 34 | * Note that this can change/break without warning. 35 | * @see https://github.com/vercel/next.js/pull/22867 36 | */ 37 | // experimental: { 38 | // externalDir: true, 39 | // }, 40 | webpack: (config) => { 41 | if (isNext12(config)) { 42 | return updateNextGreaterThan12Config(config); 43 | } 44 | 45 | return updateNextLessThan12Config(config); 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /apps/vite/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | line-height: 24px; 5 | font-weight: 400; 6 | 7 | color-scheme: light dark; 8 | color: rgba(255, 255, 255, 0.87); 9 | background-color: #242424; 10 | 11 | font-synthesis: none; 12 | text-rendering: optimizeLegibility; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | -webkit-text-size-adjust: 100%; 16 | } 17 | 18 | a { 19 | font-weight: 500; 20 | color: #646cff; 21 | text-decoration: inherit; 22 | } 23 | a:hover { 24 | color: #535bf2; 25 | } 26 | 27 | body { 28 | margin: 0; 29 | display: flex; 30 | place-items: center; 31 | min-width: 320px; 32 | min-height: 100vh; 33 | } 34 | 35 | h1 { 36 | font-size: 3.2em; 37 | line-height: 1.1; 38 | } 39 | 40 | button { 41 | border-radius: 8px; 42 | border: 1px solid transparent; 43 | padding: 0.6em 1.2em; 44 | font-size: 1em; 45 | font-weight: 500; 46 | font-family: inherit; 47 | background-color: #1a1a1a; 48 | cursor: pointer; 49 | transition: border-color 0.25s; 50 | } 51 | button:hover { 52 | border-color: #646cff; 53 | } 54 | button:focus, 55 | button:focus-visible { 56 | outline: 4px auto -webkit-focus-ring-color; 57 | } 58 | 59 | @media (prefers-color-scheme: light) { 60 | :root { 61 | color: #213547; 62 | background-color: #ffffff; 63 | } 64 | a:hover { 65 | color: #747bff; 66 | } 67 | button { 68 | background-color: #f9f9f9; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /apps/cra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nighttrax/cra-example", 3 | "description": "Example of using create-react-app in a TS monorepo", 4 | "private": true, 5 | "version": "1.0.0", 6 | "dependencies": { 7 | "@nighttrax/foo": "workspace:*", 8 | "@nighttrax/components": "workspace:*", 9 | "react": "~18.2.0", 10 | "react-dom": "~18.2.0" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "~20.11.0", 14 | "@babel/core": "~7.24.0", 15 | "@babel/plugin-syntax-flow": "~7.23.0", 16 | "@babel/plugin-transform-react-jsx": "~7.23.0", 17 | "@craco/craco": "~7.1.0", 18 | "@types/express": "~4.17.13", 19 | "@types/jest": "~29.5.0", 20 | "@types/react": "~18.2.0", 21 | "@types/react-dom": "~18.2.0", 22 | "autoprefixer": "~10.4.0", 23 | "cross-env": "~7.0.2", 24 | "jest": "~29.7.0", 25 | "postcss": "~8.4.31", 26 | "react-scripts": "~5.0.0", 27 | "rimraf": "~5.0.0", 28 | "ts-jest": "~29.1.0", 29 | "tsconfig-paths-webpack-plugin": "~4.1.0", 30 | "typescript": "~4.9.0", 31 | "ts-node": "~10.9.1" 32 | }, 33 | "scripts": { 34 | "clean": "rimraf dist", 35 | "start": "cross-env SKIP_PREFLIGHT_CHECK=true craco start", 36 | "build": "cross-env SKIP_PREFLIGHT_CHECK=true craco build", 37 | "test": "cross-env SKIP_PREFLIGHT_CHECK=true craco test --watchAll=false" 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /apps/cra/craco.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); 3 | const ForkTSCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); 4 | const { pathsToModuleNameMapper } = require("ts-jest"); 5 | const { compilerOptions } = require("./tsconfig.paths.json"); 6 | 7 | module.exports = { 8 | eslint: { enable: false }, 9 | webpack: { 10 | configure: (config) => { 11 | // Remove ModuleScopePlugin which throws when we try to import something 12 | // outside of src/. 13 | config.resolve.plugins.pop(); 14 | 15 | // Resolve the path aliases. 16 | config.resolve.plugins.push(new TsconfigPathsPlugin()); 17 | 18 | // Let Babel compile outside of src/. 19 | const oneOfRule = config.module.rules.find((rule) => rule.oneOf); 20 | const tsRule = oneOfRule.oneOf.find((rule) => 21 | rule.test.toString().includes("ts|tsx") 22 | ); 23 | 24 | tsRule.include = undefined; 25 | tsRule.exclude = /node_modules/; 26 | 27 | return config; 28 | }, 29 | plugins: { 30 | remove: [ 31 | // This plugin is too old and causes problems in monorepos. We'll 32 | // replace it with a newer version. 33 | "ForkTsCheckerWebpackPlugin", 34 | ], 35 | add: [ 36 | // Use newer version of ForkTSCheckerWebpackPlugin to type check 37 | // files across the monorepo. 38 | new ForkTSCheckerWebpackPlugin({ 39 | issue: { 40 | // The exclude rules are copied from CRA. 41 | exclude: [ 42 | { 43 | file: "**/src/**/__tests__/**", 44 | }, 45 | { 46 | file: "**/src/**/?(*.)(spec|test).*", 47 | }, 48 | { 49 | file: "**/src/setupProxy.*", 50 | }, 51 | { 52 | file: "**/src/setupTests.*", 53 | }, 54 | ], 55 | }, 56 | }), 57 | ], 58 | }, 59 | }, 60 | jest: { 61 | configure: { 62 | preset: "ts-jest", 63 | 64 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { 65 | // This has to match the baseUrl defined in tsconfig.json. 66 | prefix: "/", 67 | }), 68 | }, 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /apps/vite/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | ![](media/monorepo.png) 5 | 6 | Template project for setting up a TypeScript monorepo 7 | 8 | [![tests](https://github.com/NiGhTTraX/ts-monorepo/actions/workflows/tests.yml/badge.svg)](https://github.com/NiGhTTraX/ts-monorepo/actions/workflows/tests.yml) 9 | 10 |
11 | 12 | ---- 13 | 14 | 15 | 16 | **Table of content** 17 | 18 | - [Features](#features) 19 | - [Setup](#setup) 20 | - [Docs](#docs) 21 | - [Packages vs apps](#packages-vs-apps) 22 | - [Integrations](#integrations) 23 | - [ts-node](#ts-node) 24 | - [Babel](#babel) 25 | - [webpack](#webpack) 26 | - [jest](#jest) 27 | - [create-react-app](#create-react-app) 28 | - [Vite](#vite) 29 | - [NextJS](#nextjs) 30 | - [NestJS](#nestjs) 31 | - [Storybook](#storybook) 32 | 33 | 34 | 35 | ## Features 36 | 37 | > The main focus of this repo is making the **`Go to definition`** feature in IDEs work without any surprises, meaning it will work after a fresh clone without needing to build the project. 38 | 39 | ![find-usage](./media/find-usage.gif) 40 | 41 | > The secondary focus is to remove surprises when **publishing** packages. The repo is set up so that each package gets a clean build output without any artifacts from other packages. 42 | 43 | ![build-output](./media/build-output.png) 44 | 45 | > Everything else is kept to a **minimum**. Apart from my personal [ESLint config](.eslintrc.js) to keep the code clean, there are no extra tools included — you're free to customize this to your own needs after cloning. Compilation targets, module systems, tree shaking etc. are left up to you to decide. 46 | 47 | ## Setup 48 | 49 | This repo uses [pnpm](https://pnpm.io/), but should work fine with any of the following: 50 | 51 | - [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) 52 | - [npm 7 workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces) 53 | - [npm < 7 and `lerna bootstrap`](https://github.com/lerna/lerna/blob/main/commands/bootstrap/README.md) 54 | 55 | I strongly recommend `pnpm` over the other solutions, not only because it's usually faster, but because it avoids dependency problems caused by hoisting (see https://github.com/NiGhTTraX/ts-monorepo/commit/d93139166b25fab15e9538df58a7d06270b846c9 as an example). 56 | 57 | ```sh 58 | # Install pnpm with your preferred method: https://pnpm.io/installation. 59 | npm i -g pnpm 60 | 61 | # Install all dependencies. 62 | pnpm i 63 | ``` 64 | 65 | ## Docs 66 | 67 | See the following blog posts: 68 | 69 | - [How to set up a TypeScript monorepo and make Go to definition work](https://medium.com/@NiGhTTraX/how-to-set-up-a-typescript-monorepo-with-lerna-c6acda7d4559) 70 | - [Making TypeScript monorepos play nice with other tools](https://medium.com/@NiGhTTraX/making-typescript-monorepos-play-nice-with-other-tools-a8d197fdc680) 71 | 72 | If you're looking for the project references solution checkout the [`project-references`](https://github.com/NiGhTTraX/ts-monorepo/tree/project-references) branch. 73 | 74 | ## Packages vs apps 75 | 76 | This repo contains two types of workspaces: 77 | 78 | - `packages`: meant to be published to npm and installed, 79 | - `apps`: meant to be executed. 80 | 81 | A good example to illustrate the difference is `create-react-app`: you wouldn't publish an app like this to npm, you would run it, more specifically you would build the JS bundle and then deploy that somewhere. 82 | 83 | For packages, you don't want to bundle all the monorepo dependencies, and instead publish them individually. That's why packages have a separate build `tsconfig.json` that resolves monorepo dependencies to `node_modules`. 84 | 85 | ## Integrations 86 | 87 | ### ts-node 88 | 89 | Use [tsconfig-paths](https://www.npmjs.com/package/tsconfig-paths) to resolve the path aliases at runtime: 90 | 91 | ```json 92 | { 93 | "scripts": { 94 | "start": "ts-node -r tsconfig-paths/register src/index.ts" 95 | } 96 | } 97 | ``` 98 | 99 | See the full example [here](apps/ts-node). 100 | 101 | ### Babel 102 | 103 | Use [babel-plugin-module-resolver](https://www.npmjs.com/package/babel-plugin-module-resolver) to resolve the path aliases: 104 | 105 | ```js 106 | module.exports = { 107 | presets: [ 108 | ["@babel/preset-env", { targets: { node: "current" } }], 109 | "@babel/preset-typescript", 110 | ], 111 | 112 | plugins: [ 113 | [ 114 | "module-resolver", 115 | { 116 | alias: { 117 | "^@nighttrax/(.+)": "../\\1/src", 118 | }, 119 | }, 120 | ], 121 | ], 122 | }; 123 | ``` 124 | 125 | See the full example [here](apps/jest-babel). 126 | 127 | ### webpack 128 | 129 | Use [tsconfig-paths-webpack-plugin](https://www.npmjs.com/package/tsconfig-paths-webpack-plugin) to resolve the path aliases: 130 | 131 | ```js 132 | const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); 133 | 134 | module.exports = { 135 | resolve: { 136 | plugins: [new TsconfigPathsPlugin()] 137 | } 138 | }; 139 | ``` 140 | 141 | See the full example [here](apps/webpack). 142 | 143 | ### jest 144 | 145 | If you use `Babel` then see [this example](apps/jest-babel) from the [Babel](#babel) section above. 146 | 147 | If you use [ts-jest](https://github.com/kulshekhar/ts-jest) then you can use its `pathsToModuleNameMapper` helper: 148 | 149 | ```js 150 | const { pathsToModuleNameMapper } = require("ts-jest"); 151 | const { compilerOptions } = require("../../tsconfig.json"); 152 | 153 | module.exports = { 154 | preset: "ts-jest", 155 | 156 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { 157 | // This has to match the baseUrl defined in tsconfig.json. 158 | prefix: "/../../", 159 | }), 160 | }; 161 | ``` 162 | 163 | See the full example [here](apps/jest-tsjest). 164 | 165 | ### create-react-app 166 | 167 | Use [craco](https://www.npmjs.com/package/@craco/craco) or [react-app-rewired](https://www.npmjs.com/package/react-app-rewired) to extend CRA's webpack config and apply the [tsconfig-paths-webpack-plugin](https://www.npmjs.com/package/tsconfig-paths-webpack-plugin): 168 | 169 | ```js 170 | const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); 171 | 172 | module.exports = (config) => { 173 | // Remove the ModuleScopePlugin which throws when we 174 | // try to import something outside of src/. 175 | config.resolve.plugins.pop(); 176 | 177 | // Resolve the path aliases. 178 | config.resolve.plugins.push(new TsconfigPathsPlugin()); 179 | 180 | // Let Babel compile outside of src/. 181 | const oneOfRule = config.module.rules.find((rule) => rule.oneOf); 182 | const tsRule = oneOfRule.oneOf.find((rule) => 183 | rule.test.toString().includes("ts|tsx") 184 | ); 185 | tsRule.include = undefined; 186 | tsRule.exclude = /node_modules/; 187 | 188 | return config; 189 | }; 190 | ``` 191 | 192 | See the full example [here](apps/cra). For tests, see the [jest example](#jest). 193 | 194 | ### Vite 195 | 196 | Use [vite-tsconfig-paths](https://www.npmjs.com/package/vite-tsconfig-paths) in the Vite config: 197 | 198 | ```typescript 199 | import { defineConfig } from "vite"; 200 | import react from "@vitejs/plugin-react"; 201 | import tsconfigPaths from "vite-tsconfig-paths"; 202 | 203 | export default defineConfig({ 204 | plugins: [ 205 | react(), 206 | tsconfigPaths() 207 | ], 208 | }); 209 | ``` 210 | 211 | See full example [here](apps/vite). 212 | 213 | ### NextJS 214 | 215 | Extend Next's webpack config to enable compiling packages from the monorepo: 216 | 217 | ```js 218 | module.exports = { 219 | webpack: (config) => { 220 | // Let Babel compile outside of src/. 221 | const tsRule = config.module.rules.find( 222 | (rule) => rule.test && rule.test.toString().includes("tsx|ts") 223 | ); 224 | tsRule.include = undefined; 225 | tsRule.exclude = /node_modules/; 226 | 227 | return config; 228 | }, 229 | }; 230 | ``` 231 | 232 | See the full example [here](apps/nextjs). 233 | 234 | ### NestJS 235 | 236 | Include the path aliases in both `tsconfig.json` and `tsconfig.build.json` and tell NestJS where to find the `main.js` file: 237 | 238 | ```json 239 | { 240 | "collection": "@nestjs/schematics", 241 | "sourceRoot": "src", 242 | "entryFile": "apps/nestjs/src/main" 243 | } 244 | ``` 245 | 246 | See the full example [here](apps/nestjs). 247 | 248 | ### Storybook 249 | 250 | [Extend Storybook's webpack config](https://storybook.js.org/docs/react/builders/webpack#typescript-module-resolution) and apply the [tsconfig-paths-webpack-plugin](https://www.npmjs.com/package/tsconfig-paths-webpack-plugin): 251 | 252 | ```js 253 | const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); 254 | 255 | module.exports = { 256 | webpackFinal: async (config) => { 257 | config.resolve.plugins = [ 258 | ...(config.resolve.plugins || []), 259 | new TsconfigPathsPlugin({ 260 | extensions: config.resolve.extensions, 261 | }), 262 | ]; 263 | return config; 264 | }, 265 | }; 266 | ``` 267 | 268 | See the full example [here](apps/storybook). 269 | --------------------------------------------------------------------------------