{"id":16222621,"url":"https://github.com/moishinetzer/monorepo-test","last_synced_at":"2025-04-08T01:40:34.334Z","repository":{"id":127263285,"uuid":"559703153","full_name":"moishinetzer/monorepo-test","owner":"moishinetzer","description":null,"archived":false,"fork":false,"pushed_at":"2022-10-30T22:44:06.000Z","size":703,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T20:43:13.840Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moishinetzer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-30T22:44:03.000Z","updated_at":"2023-03-07T06:20:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"a35ff323-5d7a-4bb1-aff2-cacd9f921861","html_url":"https://github.com/moishinetzer/monorepo-test","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":"NiGhTTraX/ts-monorepo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fmonorepo-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fmonorepo-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fmonorepo-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fmonorepo-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moishinetzer","download_url":"https://codeload.github.com/moishinetzer/monorepo-test/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247761051,"owners_count":20991533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-10T12:14:43.425Z","updated_at":"2025-04-08T01:40:34.311Z","avatar_url":"https://github.com/moishinetzer.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--suppress HtmlDeprecatedAttribute --\u003e\n\u003cdiv align=\"center\"\u003e\n\n![](media/monorepo.png)\n\nTemplate project for setting up a TypeScript monorepo\n\n[![tests](https://github.com/NiGhTTraX/ts-monorepo/actions/workflows/tests.yml/badge.svg)](https://github.com/NiGhTTraX/ts-monorepo/actions/workflows/tests.yml)\n\n\u003c/div\u003e\n\n----\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of content**\n\n- [Features](#features)\n- [Setup](#setup)\n- [Docs](#docs)\n- [Packages vs apps](#packages-vs-apps)\n- [Integrations](#integrations)\n  - [ts-node](#ts-node)\n  - [Babel](#babel)\n  - [webpack](#webpack)\n  - [jest](#jest)\n  - [create-react-app](#create-react-app)\n  - [NextJS](#nextjs)\n  - [NestJS](#nestjs)\n  - [Storybook](#storybook)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Features\n\n\u003e 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.\n\n![find-usage](./media/find-usage.gif)\n\n\u003e 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.\n\n![build-output](./media/build-output.png)\n\n\u003e 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.\n\n## Setup\n\nThis repo uses [pnpm](https://pnpm.io/), but should work fine with any of the following:\n\n- [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/)\n- [npm 7 workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces)\n- [npm \u003c 7 and `lerna bootstrap`](https://github.com/lerna/lerna/blob/main/commands/bootstrap/README.md)\n\nI 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).\n\n```sh\n# Install pnpm with your preferred method: https://pnpm.io/installation.\nnpm i -g pnpm\n\n# Install all dependencies.\npnpm i\n```\n\n## Docs\n\nSee the following blog posts:\n\n- [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)\n- [Making TypeScript monorepos play nice with other tools](https://medium.com/@NiGhTTraX/making-typescript-monorepos-play-nice-with-other-tools-a8d197fdc680)\n\nIf you're looking for the project references solution checkout the [`project-references`](https://github.com/NiGhTTraX/ts-monorepo/tree/project-references) branch.\n\n## Packages vs apps\n\nThis repo contains two types of workspaces:\n\n- `packages`: meant to be published to npm and installed,\n- `apps`: meant to be executed.\n\nA 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.\n\nFor 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`.\n\n## Integrations\n\n### ts-node\n\nUse [tsconfig-paths](https://www.npmjs.com/package/tsconfig-paths) to resolve the path aliases at runtime:\n\n```json\n{\n  \"scripts\": {\n    \"start\": \"ts-node -r tsconfig-paths/register src/index.ts\"\n  }\n}\n```\n\nSee the full example [here](apps/ts-node).\n\n### Babel\n\nUse [babel-plugin-module-resolver](https://www.npmjs.com/package/babel-plugin-module-resolver) to resolve the path aliases:\n\n```js\nmodule.exports = {\n  presets: [\n    [\"@babel/preset-env\", { targets: { node: \"current\" } }],\n    \"@babel/preset-typescript\",\n  ],\n\n  plugins: [\n    [\n      \"module-resolver\",\n      {\n        alias: {\n          \"^@nighttrax/(.+)\": \"../\\\\1/src\",\n        },\n      },\n    ],\n  ],\n};\n```\n\nSee the full example [here](apps/jest-babel).\n\n### webpack\n\nUse [tsconfig-paths-webpack-plugin](https://www.npmjs.com/package/tsconfig-paths-webpack-plugin) to resolve the path aliases:\n\n```js\nconst TsconfigPathsPlugin = require(\"tsconfig-paths-webpack-plugin\");\n\nmodule.exports = {\n  resolve: {\n    plugins: [new TsconfigPathsPlugin()]\n  }\n};\n```\n\nSee the full example [here](apps/webpack).\n\n### jest\n\nIf you use `Babel` then see [this example](apps/jest-babel) from the [Babel](#babel) section above.\n\nIf you use [ts-jest](https://github.com/kulshekhar/ts-jest) then you can use its `pathsToModuleNameMapper` helper: \n\n```js\nconst { pathsToModuleNameMapper } = require(\"ts-jest\");\nconst { compilerOptions } = require(\"../../tsconfig.json\");\n\nmodule.exports = {\n  preset: \"ts-jest\",\n\n  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {\n    // This has to match the baseUrl defined in tsconfig.json.\n    prefix: \"\u003crootDir\u003e/../../\",\n  }),\n};\n```\n\nSee the full example [here](apps/jest-tsjest).\n\n### create-react-app\n\nUse [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):\n\n```js\nconst TsconfigPathsPlugin = require(\"tsconfig-paths-webpack-plugin\");\n\nmodule.exports = (config) =\u003e {\n  // Remove the ModuleScopePlugin which throws when we\n  // try to import something outside of src/.\n  config.resolve.plugins.pop();\n\n  // Resolve the path aliases.\n  config.resolve.plugins.push(new TsconfigPathsPlugin());\n\n  // Let Babel compile outside of src/.\n  const oneOfRule = config.module.rules.find((rule) =\u003e rule.oneOf);\n    const tsRule = oneOfRule.oneOf.find((rule) =\u003e\n      rule.test.toString().includes(\"ts|tsx\")\n    );\n  tsRule.include = undefined;\n  tsRule.exclude = /node_modules/;\n\n  return config;\n};\n```\n\nSee the full example [here](apps/cra). For tests, see the [jest example](#jest).\n\n### NextJS\n\nExtend Next's webpack config to enable compiling packages from the monorepo:\n\n```js\nmodule.exports = {\n  webpack: (config) =\u003e {\n    // Let Babel compile outside of src/.\n    const tsRule = config.module.rules.find(\n      (rule) =\u003e rule.test \u0026\u0026 rule.test.toString().includes(\"tsx|ts\")\n    );\n    tsRule.include = undefined;\n    tsRule.exclude = /node_modules/;\n\n    return config;\n  },\n};\n```\n\nSee the full example [here](apps/nextjs).\n\n### NestJS\n\nInclude the path aliases in both `tsconfig.json` and `tsconfig.build.json` and tell NestJS where to find the `main.js` file:\n\n```json\n{\n  \"collection\": \"@nestjs/schematics\",\n  \"sourceRoot\": \"src\",\n  \"entryFile\": \"apps/nestjs/src/main\"\n}\n```\n\nSee the full example [here](apps/nestjs).\n\n### Storybook\n\n[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):\n\n```js\nconst TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');\n\nmodule.exports = {\n  webpackFinal: async (config) =\u003e {\n    config.resolve.plugins = [\n      ...(config.resolve.plugins || []),\n      new TsconfigPathsPlugin({\n        extensions: config.resolve.extensions,\n      }),\n    ];\n    return config;\n  },\n};\n```\n\nSee the full example [here](apps/storybook).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoishinetzer%2Fmonorepo-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoishinetzer%2Fmonorepo-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoishinetzer%2Fmonorepo-test/lists"}