Blame
| 2f14f6 | Thai Pangsakulyanont | 2026-02-03 09:01:33 | 1 | --- |
| 2 | name: packlets |
|||
| 3 | description: Learn the rules of packlets for managing a JavaScript/TypeScript project. Use this skill whenever a user mentions packlets or when working in a project with packlets (src/packlets) directory. |
|||
| 4 | --- |
|||
| 5 | ||||
| 6 | [Packlets](https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets) are a nice way to keep your [JavaScript](JavaScript) codebase somewhat loosely-coupled, without having to separate things into different npm packages. |
|||
| 7 | ||||
| 8 | You follow these [5 rules](https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets#:~:text=5-,rules%20for%20packlets,-With%20packlets%2C%20our): |
|||
| 9 | ||||
| 10 | 1. Packlets live in `./src/packlets/<name>/` and the entry point is `index.ts`. |
|||
| 11 | 2. Files outside a packlet can only import from packlet’s entry point, `index.ts`. |
|||
| 12 | 3. Files inside a packlet may not import its own entry point, `index.ts`. |
|||
| 13 | 4. Packlets can import other packlets but cannot create circular dependencies. |
|||
| 14 | 5. Packlets can only import other packlets and npm packages. |
|||
| 15 | ||||
| 16 | [There is an ESLint plugin to enforce these rules.](https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets) |
|||
| 17 | ||||
| 18 | # About packlets |
|||
| 19 | ||||
| 20 | Source: <https://github.com/microsoft/rushstack/edit/main/eslint/eslint-plugin-packlets/README.md> |
|||
| 21 | ||||
| 22 | ## Motivation |
|||
| 23 | ||||
| 24 | When building a large application, it's a good idea to organize source files into modules, so that their dependencies can be managed. For example, suppose an application's source files can be grouped as follows: |
|||
| 25 | ||||
| 26 | - `src/logging/*.ts` - the logging system |
|||
| 27 | - `src/data-model/*.ts` - the data model |
|||
| 28 | - `src/reports/*.ts` - the report engine |
|||
| 29 | - `src/*.ts` - other arbitrary files such as startup code and the main application |
|||
| 30 | ||||
| 31 | Using file folders is helpful, but it's not very strict. Files under `src/logging` can easily import files from `/src/reports`, creating a confusing circular import. They can also import arbitrary application files. Also, there is no clear distinction between which files are the "public API" for `src/logging` versus its private implementation details. |
|||
| 32 | ||||
| 33 | All these problems can be solved by reorganizing the project into NPM packages (or [Rush projects](https://rushjs.io/)). Something like this: |
|||
| 34 | ||||
| 35 | - `@my-app/logging` - the logging system |
|||
| 36 | - `@my-app/data-model` - the data model |
|||
| 37 | - `@my-app/reports` - the report engine |
|||
| 38 | - `@my-app/application` - other arbitrary files such as startup code and the main application |
|||
| 39 | ||||
| 40 | However, separating code in this way has some downsides. The projects need to build separately, which has some tooling costs (for example, "watch mode" now needs to consider multiple projects). In a large monorepo, the library may attract other consumers, before the API has been fully worked out. |
|||
| 41 | ||||
| 42 | Packlets provide a lightweight alternative that offers many of the same benefits of packages, but without the `package.json` file. It's a great way to prototype your project organization before later graduating your packlets into proper NPM packages. |
|||
| 43 | ||||
| 44 | ## 5 rules for packlets |
|||
| 45 | ||||
| 46 | With packlets, our folders would be reorganized as follows: |
|||
| 47 | ||||
| 48 | - `src/packlets/logging/*.ts` - the logging system |
|||
| 49 | - `src/packlets/data-model/*.ts` - the data model |
|||
| 50 | - `src/packlets/reports/*.ts` - the report engine |
|||
| 51 | - `src/*.ts` - other arbitrary files such as startup code and the main application |
|||
| 52 | ||||
| 53 | The [packlets-tutorial](https://github.com/microsoft/rushstack-samples/tree/main/other/packlets-tutorial) sample project illustrates this layout in full detail. |
|||
| 54 | ||||
| 55 | The basic design can be summarized in 5 rules: |
|||
| 56 | ||||
| 57 | 1. A "packlet" is defined to be a folder path `./src/packlets/<packlet-name>/index.ts`. The **index.ts** file will have the exported APIs. The `<packlet-name>` name must consist of lower case words separated by hyphens, similar to an NPM package name. |
|||
| 58 | ||||
| 59 | Example file paths: |
|||
| 60 | ``` |
|||
| 61 | src/packlets/controls |
|||
| 62 | src/packlets/logger |
|||
| 63 | src/packlets/my-long-name |
|||
| 64 | ``` |
|||
| 65 | ||||
| 66 | > **NOTE:** The `packlets` cannot be nested deeper in the tree. Like with NPM packages, `src/packlets` is a flat namespace. |
|||
| 67 | ||||
| 68 | 2. Files outside the packlet folder can only import the packlet root **index.ts**: |
|||
| 69 | ||||
| 70 | **src/app/App.ts** |
|||
| 71 | ```ts |
|||
| 72 | // Okay |
|||
| 73 | import { MainReport } from '../packlets/reports'; |
|||
| 74 | ||||
| 75 | // Error: The import statement does not use the packlet's entry point (@rushstack/packlets/mechanics) |
|||
| 76 | import { MainReport } from '../packlets/reports/index'; |
|||
| 77 | ||||
| 78 | // Error: The import statement does not use the packlet's entry point (@rushstack/packlets/mechanics) |
|||
| 79 | import { MainReport } from '../packlets/reports/MainReport'; |
|||
| 80 | ``` |
|||
| 81 | ||||
| 82 | 3. Files inside a packlet folder should import their siblings directly, not via their own **index.ts** (which might create a circular reference): |
|||
| 83 | ||||
| 84 | **src/packlets/logging/Logger.ts** |
|||
| 85 | ```ts |
|||
| 86 | // Okay |
|||
| 87 | import { MessageType } from "./MessageType"; |
|||
| 88 | ||||
| 89 | // Error: Files under a packlet folder must not import from their own index.ts file (@rushstack/packlets/mechanics) |
|||
| 90 | import { MessageType } from "."; |
|||
| 91 | ||||
| 92 | // Error: Files under a packlet folder must not import from their own index.ts file (@rushstack/packlets/mechanics) |
|||
| 93 | import { MessageType } from "./index"; |
|||
| 94 | ``` |
|||
| 95 | ||||
| 96 | ||||
| 97 | 4. Packlets may reference other packlets, but not in a way that would introduce a circular dependency: |
|||
| 98 | ||||
| 99 | **src/packlets/data-model/DataModel.ts** |
|||
| 100 | ```ts |
|||
| 101 | // Okay |
|||
| 102 | import { Logger } from '../../packlets/logging'; |
|||
| 103 | ``` |
|||
| 104 | ||||
| 105 | **src/packlets/logging/Logger.ts** |
|||
| 106 | ```ts |
|||
| 107 | // Error: Packlet imports create a circular reference: (@rushstack/packlets/circular-deps) |
|||
| 108 | // "logging" is referenced by src/packlets/data-model/DataModel.ts |
|||
| 109 | // "data-model" is referenced by src/packlets/logging/Logger.ts |
|||
| 110 | import { DataModel } from '../../packlets/data-model'; |
|||
| 111 | ``` |
|||
| 112 | ||||
| 113 | 5. Other source files are allowed outside the **src/packlets** folder. They may import a packlet, but packlets must only import from other packlets or NPM packages. |
|||
| 114 | ||||
| 115 | **src/app/App.ts** |
|||
| 116 | ||||
| 117 | ```ts |
|||
| 118 | // Okay |
|||
| 119 | import { MainReport } from '../packlets/reports'; |
|||
| 120 | ``` |
|||
| 121 | ||||
| 122 | **src/packlets/data-model/ExampleModel.ts** |
|||
| 123 | ```ts |
|||
| 124 | // Error: A local project file cannot be imported. A packlet's dependencies must be |
|||
| 125 | // NPM packages and/or other packlets. (@rushstack/packlets/mechanics) |
|||
| 126 | import { App } from '../../app/App'; |
|||
| 127 | ``` |
