{"id":15286263,"url":"https://github.com/0x80/isolate-package","last_synced_at":"2025-04-09T09:11:07.801Z","repository":{"id":162553171,"uuid":"637075224","full_name":"0x80/isolate-package","owner":"0x80","description":"Isolate a monorepo package with its internal dependencies to form a self-contained directory with a pruned lockfile","archived":false,"fork":false,"pushed_at":"2024-04-01T13:45:42.000Z","size":403,"stargazers_count":81,"open_issues_count":1,"forks_count":7,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-04-14T07:23:58.620Z","etag":null,"topics":["ci","deploy","firebase","isolate","monorepo","npm","package","pnpm","workspace","yarn"],"latest_commit_sha":null,"homepage":"","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/0x80.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":"0x80"}},"created_at":"2023-05-06T12:35:21.000Z","updated_at":"2024-04-16T15:46:33.803Z","dependencies_parsed_at":"2023-12-18T18:30:57.949Z","dependency_job_id":"d409ffe7-a864-48f4-b0be-6a5f45ee812a","html_url":"https://github.com/0x80/isolate-package","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x80%2Fisolate-package","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x80%2Fisolate-package/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x80%2Fisolate-package/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0x80%2Fisolate-package/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0x80","download_url":"https://codeload.github.com/0x80/isolate-package/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248008630,"owners_count":21032556,"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":["ci","deploy","firebase","isolate","monorepo","npm","package","pnpm","workspace","yarn"],"created_at":"2024-09-30T15:11:33.283Z","updated_at":"2025-04-09T09:11:07.786Z","avatar_url":"https://github.com/0x80.png","language":"TypeScript","readme":"# Isolate Package\n\n- [Quickstart](#quickstart)\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Troubleshooting](#troubleshooting)\n- [Prerequisites](#prerequisites)\n- [Configuration Options](#configuration-options)\n- [API](#api)\n- [The internal packages strategy](#the-internal-packages-strategy)\n- [Firebase](#firebase)\n\n## Quickstart\n\nRun `npx isolate-package isolate` from the monorepo package you would like to\nisolate.\n\nIf you would like to see an example of a modern monorepo with this tool\nintegrated, check out [mono-ts](https://github.com/0x80/mono-ts)\n\n## Features\n\n- Isolate a monorepo workspace package to form a self-contained package that\n  includes internal dependencies and an adapted lockfile for deterministic\n  deployments.\n- Preserve packages file structure, without code bundling\n- Should work with any package manager, and tested with NPM, PNPM, and Yarn\n  (both classic and modern). Bun is partially supported; the output will\n  generate an NPM lockfile.\n- Zero-config for the vast majority of use-cases\n- Isolates dependencies recursively. If package A depends on internal package B\n  which depends on internal package C, all of them will be included\n- Optionally force output to use NPM with matching versions\n- Optionally include devDependencies in the isolated output\n- Optionally pick or omit scripts from the manifest\n- Compatible with the Firebase tools CLI, including 1st and 2nd generation\n  Firebase Functions. For more information see\n  [the Firebase instructions](./docs/firebase.md).\n- Available in a\n  [forked version of firebase-tools](https://github.com/0x80/firebase-tools-with-isolate)\n  to preserve live code updates when running the emulators\n\n## Installation\n\nRun `pnpm install isolate-package -D` or the equivalent for `npm` or `yarn`.\n\nI recommended using `pnpm` over `npm` or `yarn`. Besides being fast and\nefficient, PNPM has better support for monorepos.\n\n## Usage\n\n\u003e !! If you plan use this for Firebase deployments, and you want to preserve\n\u003e live code updates when running the local emulators, you will want to use\n\u003e [firebase-tools-with-isolate](https://github.com/0x80/firebase-tools-with-isolate)\n\u003e instead.\n\nThis package exposes a binary called `isolate`.\n\nRun `npx isolate` from the root of the package you want to isolate. Make sure\nyou build the package first.\n\nThe `isolate` binary will try to infer your build output location from a\n`tsconfig` file, but see the [buildDirName configuration](#builddirname) if you\nare not using Typescript.\n\nBy default the isolated output will become available at `./isolate`.\n\nIf you are here to improve your Firebase deployments check out the\n[Firebase quick start guide](./docs/firebase.md#a-quick-start).\n\n## Troubleshooting\n\nIf something is not working as expected, add an `isolate.config.json` file, and\nset `\"logLevel\"` to `\"debug\"`. This should give you detailed feedback in the\nconsole.\n\nIn addition define an environment variable to debug the configuration being used\nby setting `DEBUG_ISOLATE_CONFIG=true` before you execute `isolate`.\n\nWhen debugging Firebase deployment issues it might be convenient to trigger the\nisolate process manually with `npx isolate` and possibly\n`DEBUG_ISOLATE_CONFIG=true npx isolate`.\n\n## Prerequisites\n\nBecause historically many different approaches to monorepos exist, we need to\nestablish some basic rules for the isolate process to work.\n\n### Define shared dependencies in the package manifest\n\nThis one might sound obvious, but if the `package.json` from the package you are\ntargeting does not list the other monorepo packages it depends on, in either the\n`dependencies` or `devDependencies` list, then the isolate process will not\ninclude them in the output.\n\nHow dependencies are listed with regards to versioning is not important, because\npackages are matched based on their name. For example the following flavors all\nwork (some depending on your package manager):\n\n```cjson\n// package.json\n{\n  \"dependencies\": {\n    \"shared-package\": \"0.0.0\"\n    \"shared-package\": \"*\",\n    \"shared-package\": \"workspace:*\",\n    \"shared-package\": \"../shared-package\",\n  }\n}\n```\n\nSo if the a package name can be found as part of the workspace definition, it\nwill be processed regardless of its version specifier.\n\n### Define \"version\" field in each package manifest\n\nThe `version` field is required for `pack` to execute, because it is use to\ngenerate part of the packed filename. A personal preference is to set it to\n`\"0.0.0\"` to indicate that the version does not have any real meaning.\n\n### Define \"files\" field in each package manifest\n\n\u003e NOTE: This step is not required if you use the\n\u003e [internal packages strategy](#the-internal-packages-strategy) but you could\n\u003e set it to `[\"src\"]` instead of `[\"dist\"]`.\n\nThe isolate process uses (p)npm `pack` to extract files from package\ndirectories, just like publishing a package would.\n\nFor this to work it is required that you define the `files` property in each\npackage manifest, as it declares what files should be included in the published\noutput.\n\nTypically, the value contains an array with only the name of the build output\ndirectory. For example:\n\n```cjson\n// package.json\n{\n  \"files\": [\"dist\"]\n}\n```\n\nA few additional files from the root of your package will be included\nautomatically, like the `package.json`, `LICENSE` and `README` files.\n\n**Tip** If you deploy to Firebase\n[2nd generation](https://firebase.google.com/docs/firestore/extend-with-functions-2nd-gen)\nfunctions, you might want to include some env files in the `files` list, so they\nare packaged and deployed together with your build output (as 1st gen functions\nconfig is no longer supported).\n\n### Use a flat structure inside your packages folders\n\nAt the moment, nesting packages inside packages is not supported.\n\nWhen building the registry of all internal packages, `isolate` doesn't drill\ndown into the folders. So if you declare your packages to live in `packages/*`\nit will only find the packages directly in that folder and not at\n`packages/nested/more-packages`.\n\nYou can, however, declare multiple workspace packages directories. Personally, I\nprefer to use `[\"packages/*\", \"apps/*\", \"services/*\"]`. It is only the structure\ninside them that should be flat.\n\n## Configuration Options\n\nFor most users no configuration should be necessary.\n\nYou can configure the isolate process by placing a `isolate.config.json` file in\nthe package that you want to isolate, except when you're\n[deploying to Firebase from the root of the workspace](#deploying-firebase-from-the-root).\n\nFor the config file to be picked up, you will have to execute `isolate` from the\nsame location, as it uses the current working directory.\n\nBelow you will find a description of every available option.\n\n### logLevel\n\nType: `\"info\" | \"debug\" | \"warn\" | \"error\"`, default: `\"info\"`.\n\nBecause the configuration loader depends on this setting, its output is not\naffected by this setting. If you want to debug the configuration set\n`DEBUG_ISOLATE_CONFIG=true` before you run `isolate`\n\n### forceNpm\n\nType: `boolean`, default: `false`\n\nBy default the isolate process will generate output based on the package manager\nthat you are using for your monorepo, but your deployment target might not be\ncompatible with that package manager.\n\nIt should not really matter what package manager is used in de deployment as\nlong as the versions match your original lockfile.\n\nBy setting this option to `true` you are forcing the isolate output to use NPM.\nA package-lock file will be generated based on the contents of node_modules and\ntherefore should match the versions in your original lockfile.\n\nThis way you can enjoy using PNPM or Yarn for your monorepo, while your\ndeployment requires NPM.\n\n### buildDirName\n\nType: `string | undefined`, default: `undefined`\n\nThe name of the build output directory name. When undefined it is automatically\ndetected via `tsconfig.json`. When you are not using Typescript you can use this\nsetting to specify where the build output files are located.\n\n### includeDevDependencies\n\nType: `boolean`, default: `false`\n\nBy default devDependencies are ignored and stripped from the isolated output\n`package.json` files. If you enable this the devDependencies will be included\nand isolated just like the production dependencies.\n\n### pickFromScripts\n\nType: `string[]`, default: `undefined`\n\nSelect which scripts to include in the output manifest `scripts` field. For\nexample if you want your test script included set it to `[\"test\"]`.\n\nBy default, all scripts are omitted.\n\n### omitFromScripts\n\nType: `string[]`, default: `undefined`\n\nSelect which scripts to omit from the output manifest `scripts` field. For\nexample if you want the build script interferes with your deployment target, but\nyou want to preserve all of the other scripts, set it to `[\"build\"]`.\n\nBy default, all scripts are omitted, and the [pickFromScripts](#pickfromscripts)\nconfiguration overrules this configuration.\n\n### omitPackageManager\n\nType: `boolean`, default: `false`\n\nBy default the packageManager field from the root manifest is copied to the\ntarget manifest. I have found that some platforms (Cloud Run, April 2024) can\nfail on this for some reason. This option allows you to omit the field from the\nisolated package manifest.\n\n### isolateDirName\n\nType: `string`, default: `\"isolate\"`\n\nThe name of the isolate output directory.\n\n### targetPackagePath\n\nType: `string`, default: `undefined`\n\nOnly when you decide to place the isolate configuration in the root of the\nmonorepo, you use this setting to point it to the target you want to isolate,\ne.g. `./packages/my-firebase-package`.\n\nIf this option is used the `workspaceRoot` setting will be ignored and assumed\nto be the current working directory.\n\n### tsconfigPath\n\nType: `string`, default: `\"./tsconfig.json\"`\n\nThe path to the `tsconfig.json` file relative to the package you want to\nisolate. The tsconfig is only used for reading the `compilerOptions.outDir`\nsetting. If no tsconfig is found, possibly because you are not using Typescript\nin your project, the process will fall back to the `buildDirName` setting.\n\n### workspacePackages\n\nType: `string[] | undefined`, default: `undefined`\n\nWhen workspacePackages is not defined, `isolate` will try to find the packages\nin the workspace by looking up the settings in `pnpm-workspace.yaml` or\n`package.json` files depending on the detected package manager.\n\nIn case this fails, you can override this process by specifying globs manually.\nFor example `\"workspacePackages\": [\"packages/*\", \"apps/*\"]`. Paths are relative\nfrom the root of the workspace.\n\n### workspaceRoot\n\nType: `string`, default: `\"../..\"`\n\nThe relative path to the root of the workspace / monorepo. In a typical setup\nyou will have a `packages` directory and possibly also an `apps` and a\n`services` directory, all of which contain packages. So any package you would\nwant to isolate is located 2 levels up from the root.\n\nFor example\n\n```\npackages\n├─ backend\n│  └─ package.json\n└─ ui\n   └─ package.json\napps\n├─ admin\n│  └─ package.json\n└─ web\n   └─ package.json\nservices\n└─ api\n   └─ package.json\n\n```\n\nWhen you use the `targetPackagePath` option, this setting will be ignored.\n\n## API\n\nAlternatively, `isolate` can be integrated in other programs by importing it as\na function. You optionally pass it a some user configuration and possibly a\nlogger to handle any output messages should you need to write them to a\ndifferent location as the standard `node:console`.\n\n```ts\nimport { isolate } from \"isolate-package\";\n\nawait isolate({\n  config: { logLevel: \"debug\" },\n  logger: customLogger,\n});\n```\n\nIf no configuration is passed in, the process will try to read\n`isolate.config.json` from the current working directory.\n\n## The internal packages strategy\n\nAn alternative approach to using internal dependencies in a Typescript monorepo\nis\n[the internal packages strategy](https://turbo.build/blog/you-might-not-need-typescript-project-references),\nin which the package manifest entries point directly to Typescript source files,\nto omit intermediate build steps. The approach is compatible with\nisolate-package and showcased in\n[my example monorepo setup](https://github.com/0x80/mono-ts)\n\nIn summary this is how it works:\n\n1. The package to be deployed lists its internal dependencies as usual, but the\n   package manifests of those dependencies point directly to the Typescript\n   source (and types).\n2. You configure the bundler of your target package to include the source code\n   for those internal packages in its output bundle. In the case of TSUP for the\n   [API service in the mono-ts](https://github.com/0x80/mono-ts/blob/main/services/api/tsup.config.ts)\n   that configuration is: `noExternal: [\"@mono/common\"]`\n3. When `isolate` runs, it does the same thing as always. It detects the\n   internal packages, copies them to the isolate output folder and adjusts any\n   links.\n4. When deploying to Firebase, the cloud pipeline will treat the package\n   manifest as usual, which installs the listed dependencies and any\n   dependencies listed in the linked internal package manifests.\n\nSteps 3 and 4 are no different from a traditional setup.\n\nNote that the manifests for the internal packages in the output will still point\nto the Typescript source files, but since the shared code was embedded in the\nbundle, they will never be referenced via import statements. So the manifest the\nentry declarations are never used. The reason the packages are included in the\nisolated output is to instruct package manager to install their dependencies.\n\n## Firebase\n\nFor detailed information on how to use isolate-package in combination with\nFirebase [see this documentation](./docs/firebase.md#firebase)\n","funding_links":["https://github.com/sponsors/0x80"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x80%2Fisolate-package","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0x80%2Fisolate-package","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0x80%2Fisolate-package/lists"}