limina 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md CHANGED
@@ -1,3 +1,8 @@
1
+ <!-- markdownlint-disable MD003 MD009 MD025 MD035 MD026 -->
2
+ # limina license
3
+
4
+ limina is released under the MIT license:
5
+
1
6
  MIT License
2
7
 
3
8
  Copyright (c) 2025-present, Senao Xi. and docs islands contributors.
@@ -19,3 +24,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
24
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
26
  SOFTWARE.
27
+
28
+ # Licenses of bundled dependencies
29
+
30
+ The published limina artifact additionally contains code with the following licenses:
31
+
32
+ # Bundled dependencies:
package/README.md CHANGED
@@ -1,383 +1,30 @@
1
1
  # limina
2
2
 
3
+ <p align="center">
4
+ <a href="https://docs.senao.me/docs-islands/limina" target="_blank" rel="noopener noreferrer">
5
+ <img width="180" src="https://docs.senao.me/docs-islands/limina/logo.svg" alt="limina logo">
6
+ </a>
7
+ </p>
3
8
  <p align="center">
4
9
  <a href="https://npmjs.com/package/limina"><img src="https://img.shields.io/npm/v/limina.svg" alt="npm package"></a>
5
10
  <a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/limina.svg" alt="node compatibility"></a>
6
11
  <a href="https://github.com/XiSenao/docs-islands/actions/workflows/ci.yml"><img src="https://github.com/XiSenao/docs-islands/actions/workflows/ci.yml/badge.svg?branch=main" alt="build status"></a>
7
- <a href="https://github.com/XiSenao/docs-islands/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/limina.svg" alt="license"></a>
12
+ <a href="https://github.com/XiSenao/docs-islands/blob/main/packages/limina/LICENSE.md"><img src="https://img.shields.io/npm/l/limina.svg" alt="license"></a>
8
13
  </p>
9
14
 
10
15
  English | [简体中文](./README.zh-CN.md)
11
16
 
12
- `limina` is a configurable governance CLI for TypeScript monorepos. It keeps TypeScript project references, source typechecks, compatibility paths, package export policy, and publish-time package checks in one explicit `limina.config.mjs` file.
13
-
14
- Limina is not a bundler and does not replace `tsc`, `vue-tsc`, tests, or release tooling. It coordinates them and verifies that the architecture they depend on stays consistent.
15
-
16
- ## Why Limina?
17
-
18
- Large TypeScript workspaces often need more than `tsc --noEmit`:
19
-
20
- - project references must match real cross-project imports;
21
- - production graph projects should not depend on tools or tests;
22
- - browser/runtime output should not import Node builtins;
23
- - `workspace:*` dependencies should resolve to source during graph checks;
24
- - generated compatibility `paths` should not silently drift;
25
- - built package outputs need consumer-facing checks before release;
26
- - Vue, docs, playground, and smoke checks may need checker-specific tooling outside native `tsc -b`.
27
-
28
- Limina makes these rules reviewable, runnable, and suitable for CI.
29
-
30
- ## Features
31
-
32
- - **Project graph validation**: checks reachable TypeScript declaration leaves, references, graph-owned imports, package boundaries, and label-based deny rules.
33
- - **Typecheck coverage proof**: verifies that reachable declaration leaves match strict local typecheck companions and that source files are covered by checker entries or allowlist entries.
34
- - **Compatibility path generation**: writes opt-in `tsconfig.dts.paths.generated.json` files for `workspace:*` dependencies whose package exports still point at build artifacts.
35
- - **Checker target runner**: runs configured TypeScript and UI-framework checker entries in `typecheck` or `build` execution mode.
36
- - **Published package checks**: validates built package outputs with `publint`, Are The Types Wrong, and a runtime import boundary audit.
37
- - **Composable pipelines**: combines built-in checks and shell commands into named workflows such as `typecheck`, `package`, and `publish`.
38
- - **Typed configuration**: ships `defineConfig(...)` for editor hints and typed user configs.
39
-
40
- ## Requirements
41
-
42
- - Node.js `^20.19.0 || >=22.12.0`
43
- - pnpm workspace with `pnpm-workspace.yaml`
44
- - TypeScript installed in the consuming repository
45
- - ESM-compatible `limina.config.mjs`
46
-
47
- ## Installation
48
-
49
- ```sh
50
- pnpm add -D limina typescript
51
- ```
52
-
53
- ## Quick start
54
-
55
- Create `limina.config.mjs` at the workspace root:
56
-
57
- ```js
58
- import { defineConfig } from 'limina';
59
-
60
- export default defineConfig({
61
- config: {
62
- checkers: {
63
- typescript: {
64
- preset: 'tsc',
65
- entry: 'tsconfig.build.json',
66
- },
67
- vue: {
68
- preset: 'vue-tsc',
69
- entry: 'tsconfig.vue.build.json',
70
- },
71
- },
72
- },
73
-
74
- graph: {
75
- rules: {
76
- 'runtime-client': {
77
- deny: {
78
- refs: [
79
- {
80
- path: 'packages/app/src/node/tsconfig.lib.dts.json',
81
- reason: 'client runtime must not depend on the Node runtime',
82
- },
83
- ],
84
- },
85
- },
86
- },
87
- },
88
-
89
- proof: {
90
- allowlist: [
91
- {
92
- file: 'src/generated/runtime.d.ts',
93
- reason: 'Generated declaration stub covered by the runtime build process.',
94
- },
95
- ],
96
- },
97
-
98
- packageChecks: {
99
- targets: [
100
- {
101
- name: '@acme/core',
102
- outDir: 'packages/core/dist',
103
- },
104
- ],
105
- },
106
-
107
- pipelines: {
108
- package: ['package:check'],
109
- publish: ['graph:check', 'proof:check', 'package:check'],
110
- },
111
- });
112
- ```
113
-
114
- Add scripts:
115
-
116
- ```json
117
- {
118
- "scripts": {
119
- "typecheck": "limina check",
120
- "lint:package": "limina package check",
121
- "prepublishOnly": "limina check publish"
122
- }
123
- }
124
- ```
125
-
126
- Run checks:
127
-
128
- ```sh
129
- pnpm typecheck
130
- pnpm exec limina graph check
131
- pnpm exec limina package check --package @acme/core
132
- ```
133
-
134
- ## Concepts
135
-
136
- ### Checker entry
137
-
138
- Each checker has one required `config.checkers.<name>.entry`, usually a `tsconfig*.build.json` graph aggregator. Built-in first-class presets (`tsc` and `vue-tsc`) participate in graph, source, proof, and build checks. Source-only presets such as `svelte-check` prove source coverage and run direct checker execution through `limina checker typecheck`.
139
-
140
- ### Declaration leaf and local companion
141
-
142
- Declaration leaves should have strict local companions. For example, `tsconfig.lib.dts.json` pairs with `tsconfig.lib.json`, and `tsconfig.dts.json` pairs with `tsconfig.json`.
143
-
144
- The default `tsconfig.json` is the IDE/typecheck entry for its directory. A single-environment directory should use it as the local leaf; a multi-environment directory should make it a pure aggregator with `files: []` and `references`.
145
-
146
- ### Source dependencies and artifact dependencies
147
-
148
- A dependency declared as `workspace:*` is considered a source dependency. It should be represented by project references and source-facing package exports.
149
-
150
- A dependency declared as `link:`, `file:`, `catalog:`, or normal semver is treated as an artifact dependency. It should not be modeled as a project reference unless it is intentionally consumed as source.
151
-
152
- ### Package checks
153
-
154
- Source graph checks do not prove that an installed package works for consumers. `limina package check` inspects built package outputs under `packageChecks.targets[].outDir` and checks the actual package manifest, exports, type resolution, and runtime imports. Publishable outputs whose `package.json` does not set `private: true` must also include root `README.md` and `LICENSE.md` files.
155
-
156
- ## CLI
157
-
158
- ```sh
159
- limina [--config limina.config.mjs] [--mode mode] <command>
160
- ```
161
-
162
- | Command | Description |
163
- | ----------------------------------------------- | ------------------------------------------------------------------------------------- |
164
- | `limina check` | Run the built-in default check pipeline. |
165
- | `limina check <pipeline>` | Run a named user pipeline from `pipelines`. |
166
- | `limina graph check` | Validate project references and architecture import rules. |
167
- | `limina proof check` | Prove declaration configs, local typecheck configs, and source coverage stay aligned. |
168
- | `limina paths generate` | Generate compatibility source `paths` configs for artifact-facing workspace exports. |
169
- | `limina paths apply` | Compatibility alias for `paths generate`. |
170
- | `limina paths check` | Fail when generated path files are stale. |
171
- | `limina checker build` | Run build execution for checker entries that support it. |
172
- | `limina checker typecheck` | Run source-only checker entries such as `svelte-check`. |
173
- | `limina package check` | Run configured package output checks. |
174
- | `limina package check --package <name>` | Check one configured package target. |
175
- | `limina package check --tool <tool>` | Run only `publint`, `attw`, or `boundary`. |
176
- | `limina package check --attw-profile <profile>` | Override the ATTW profile: `strict`, `node16`, or `esm-only`. |
177
-
178
- ## Configuration reference
179
-
180
- ### `config`
181
-
182
- ```js
183
- config: {
184
- checkers: {
185
- typescript: {
186
- preset: 'tsc',
187
- entry: 'tsconfig.build.json',
188
- },
189
- vue: {
190
- preset: 'vue-tsc',
191
- entry: 'tsconfig.vue.build.json',
192
- },
193
- },
194
- source: {
195
- exclude: ['node_modules', 'dist', '.tsbuild'],
196
- },
197
- }
198
- ```
199
-
200
- `config.checkers` defines checker entries. Every configured checker must declare a non-empty `entry` and use a built-in preset. Checker `extensions` are fixed by Limina and cannot be configured; if `source.include` is omitted, Limina derives the source boundary from configured checker extensions, then applies `source.exclude`.
201
-
202
- ### `graph`
203
-
204
- ```js
205
- graph: {
206
- rules: {
207
- 'runtime-client': {
208
- deny: {
209
- refs: [
210
- {
211
- path: 'packages/app/src/node/tsconfig.lib.dts.json',
212
- reason: 'client runtime must stay independent from Node runtime',
213
- },
214
- ],
215
- deps: [
216
- {
217
- name: '@acme/internal-node',
218
- reason: 'client runtime must not consume Node-only packages',
219
- },
220
- {
221
- name: 'node:*',
222
- reason: 'client runtime must not import Node builtins',
223
- },
224
- {
225
- name: '#server/*',
226
- reason: 'client runtime must not use server-only package imports',
227
- },
228
- ],
229
- },
230
- },
231
- },
232
- }
233
- ```
234
-
235
- A declaration leaf opts into a rule by adding a `limina` label:
236
-
237
- ```jsonc
238
- {
239
- "limina": "runtime-client",
240
- "extends": ["./tsconfig.json", "../../tsconfig.dts.base.json"],
241
- "references": [],
242
- }
243
- ```
244
-
245
- ### `paths`
246
-
247
- ```js
248
- paths: {
249
- generatedFileName: 'tsconfig.dts.paths.generated.json',
250
- conditionPriority: ['source', 'development', 'types'],
251
- artifactDirectories: ['dist', 'build', 'lib', 'esm', 'cjs', 'out'],
252
- }
253
- ```
254
-
255
- Use generated paths only when a workspace package must keep artifact-facing exports while still being consumed as a graph-owned source dependency.
256
-
257
- ### `proof`
258
-
259
- ```js
260
- proof: {
261
- allowlist: [
262
- {
263
- file: 'src/generated/runtime.d.ts',
264
- reason: 'Generated file validated by the build pipeline.',
265
- },
266
- ],
267
- }
268
- ```
269
-
270
- Checker entries cover files validated by TypeScript or framework-aware tools. Allowlist entries are the final fallback after all configured checker entries fail to cover a source file; they should be rare and must include a reason.
271
-
272
- ### `packageChecks`
273
-
274
- ```js
275
- packageChecks: {
276
- targets: [
277
- {
278
- name: '@acme/core',
279
- outDir: 'packages/core/dist',
280
- checks: ['publint', 'attw', 'boundary'],
281
- publint: { strict: true },
282
- attw: { profile: 'esm-only' },
283
- boundary: {
284
- environment: (file) => file.startsWith('node/') ? 'node' : 'browser',
285
- ignoredExternalPackages: ['@acme/runtime-shim'],
286
- },
287
- },
288
- ],
289
- }
290
- ```
291
-
292
- `outDir` must point at the built package directory that contains the publish-ready `package.json`. If that manifest does not set `private: true`, the same directory must also contain `README.md` and `LICENSE.md`.
293
-
294
- ### `pipelines`
295
-
296
- ```js
297
- pipelines: {
298
- package: [
299
- { type: 'command', command: 'pnpm', args: ['build'] },
300
- 'package:check',
301
- ],
302
- }
303
- ```
304
-
305
- `limina check` runs the built-in default pipeline: `graph:check`, `source:check`, `proof:check`, `checker:build`, and `checker:typecheck`. `limina check <pipeline>` only runs user pipelines from `limina.config.mjs#pipelines`; if the name is missing, Limina reports the missing pipeline and asks you to define it there.
306
-
307
- String steps can be built-in task names or simple commands. Use object command steps when arguments, `cwd`, or `env` need to be explicit.
308
-
309
- ## CI example
310
-
311
- ```yaml
312
- name: Typecheck
313
-
314
- on:
315
- pull_request:
316
- push:
317
- branches: [main]
318
-
319
- jobs:
320
- typecheck:
321
- runs-on: ubuntu-latest
322
- steps:
323
- - uses: actions/checkout@v4
324
- - uses: pnpm/action-setup@v4
325
- - uses: actions/setup-node@v4
326
- with:
327
- node-version: 20.19.0
328
- cache: pnpm
329
- - run: pnpm install --frozen-lockfile
330
- - run: pnpm exec limina check
331
- ```
332
-
333
- ## Programmatic API
334
-
335
- ```ts
336
- import { defineConfig, loadConfig } from 'limina';
337
-
338
- export default defineConfig({
339
- pipelines: {
340
- typecheck: ['graph:check'],
341
- },
342
- });
343
-
344
- const config = await loadConfig();
345
- ```
346
-
347
- Most users only need `defineConfig(...)`. `loadConfig(...)` is available for custom wrappers and tests.
348
-
349
- ## Troubleshooting
350
-
351
- ### `Unable to find limina config`
352
-
353
- Run the command from inside the workspace, or pass `--config ./limina.config.mjs`.
354
-
355
- ### `no pnpm-workspace.yaml was found`
356
-
357
- Limina infers the workspace root from `pnpm-workspace.yaml`. Place the config inside the workspace or pass a config path located under the workspace root.
358
-
359
- ### `packageChecks.targets[x].outDir` is invalid
360
-
361
- Set `outDir` to the built package directory, not the source package directory, unless that directory is itself the publish-ready package output.
362
-
363
- ### Generated paths are stale
364
-
365
- Run:
366
-
367
- ```sh
368
- pnpm exec limina paths generate
369
- ```
370
-
371
- Then add the generated file to the first position of the listed `extends` arrays and commit the generated file if your repository policy requires reproducible `tsc -b` without a pre-generation step.
17
+ > Architecture governance CLI for TypeScript monorepos
372
18
 
373
- ## Design principles
19
+ - Keep source dependency graphs aligned with type build graphs
20
+ - Guard package, runtime, and workspace boundaries
21
+ - Cover TypeScript and framework-specific checkers
22
+ - Manage compatibility paths for workspace source dependencies
23
+ - Validate package outputs before release
24
+ - Compose checks for local development, CI, and publishing
374
25
 
375
- - Explicit policy is better than hidden presets.
376
- - Source graph checks and package artifact checks validate different surfaces.
377
- - Build graph configs should be strict, small, and directly referenced.
378
- - Generated compatibility paths should be transitional, not the default architecture.
379
- - Limina should fail with actionable messages instead of silently accepting graph drift.
26
+ Limina helps teams turn drifting TypeScript monorepo constraints into explicit, reviewable, runnable checks. It keeps source relationships, type coverage, build coordination, package metadata, and publishable outputs aligned across everyday development, code review, and pre-release workflows.
380
27
 
381
- ## License
28
+ Limina is not a bundler, test runner, or release tool, and it does not replace TypeScript or framework checkers. It runs those existing tools and verifies that the monorepo structure they rely on is still trustworthy.
382
29
 
383
- MIT
30
+ [Read the Docs to Learn More](https://docs.senao.me/docs-islands/limina/)
@@ -0,0 +1,30 @@
1
+ # limina
2
+
3
+ <p align="center">
4
+ <a href="https://docs.senao.me/docs-islands/limina/zh" target="_blank" rel="noopener noreferrer">
5
+ <img width="180" src="https://docs.senao.me/docs-islands/limina/logo.svg" alt="limina logo">
6
+ </a>
7
+ </p>
8
+ <p align="center">
9
+ <a href="https://npmjs.com/package/limina"><img src="https://img.shields.io/npm/v/limina.svg" alt="npm package"></a>
10
+ <a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/limina.svg" alt="node compatibility"></a>
11
+ <a href="https://github.com/XiSenao/docs-islands/actions/workflows/ci.yml"><img src="https://github.com/XiSenao/docs-islands/actions/workflows/ci.yml/badge.svg?branch=main" alt="build status"></a>
12
+ <a href="https://github.com/XiSenao/docs-islands/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/limina.svg" alt="license"></a>
13
+ </p>
14
+
15
+ [English](./README.md) | 简体中文
16
+
17
+ > TypeScript monorepo 的架构治理 CLI
18
+
19
+ - 保持源码依赖图与类型构建图一致
20
+ - 守住 package、运行时与工程边界
21
+ - 覆盖 TypeScript 与框架专属 checker
22
+ - 管理 workspace 源码依赖的兼容路径
23
+ - 验证发布前 package 产物
24
+ - 组合适合本地、CI 与发布流程的检查
25
+
26
+ Limina 帮助团队把 TypeScript monorepo 中容易漂移的工程约束变成显式、可审查、可运行的检查。它让源码关系、类型覆盖、构建协作、package metadata 和发布产物保持一致,适合放进日常开发、代码审查和发布前流程。
27
+
28
+ Limina 不是 bundler、测试框架或发布工具,也不会替代 TypeScript / framework checker。它调用这些已有工具,并验证它们依赖的 monorepo 结构是否仍然可靠。
29
+
30
+ [阅读文档了解更多](https://docs.senao.me/docs-islands/limina/zh/)
package/bin/limina.js CHANGED
@@ -1,25 +1,36 @@
1
1
  #!/usr/bin/env node
2
- import "../chunks/dep-lkQg1P9Q.js";
3
- import { existsSync } from "node:fs";
4
- import path from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
6
- import { spawnSync } from "node:child_process";
2
+ import { spawnSync } from 'node:child_process';
3
+ import { existsSync } from 'node:fs';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+ import path from 'pathe';
6
+
7
+ const packageDir = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
8
+ const distCliPath = path.join(packageDir, 'cli.js');
9
+ const sourceCliPath = path.join(packageDir, 'src/cli.ts');
10
+ const tsxBinName = process.platform === 'win32' ? 'tsx.cmd' : 'tsx';
11
+ const tsxCliPath =
12
+ [
13
+ path.join(packageDir, 'node_modules/.bin', tsxBinName),
14
+ path.join(packageDir, '../../node_modules/.bin', tsxBinName),
15
+ ].find((candidate) => existsSync(candidate)) ?? 'tsx';
7
16
 
8
- //#region bin/limina.js
9
- const packageDir = path.resolve(fileURLToPath(new URL("..", import.meta.url)));
10
- const distCliPath = path.join(packageDir, "cli.js");
11
- const sourceCliPath = path.join(packageDir, "src/cli.ts");
12
- const tsxBinName = process.platform === "win32" ? "tsx.cmd" : "tsx";
13
- const tsxCliPath = [path.join(packageDir, "node_modules/.bin", tsxBinName), path.join(packageDir, "../../node_modules/.bin", tsxBinName)].find((candidate) => existsSync(candidate)) ?? "tsx";
14
17
  if (existsSync(sourceCliPath)) {
15
- const result = spawnSync(tsxCliPath, [sourceCliPath, ...process.argv.slice(2)], {
16
- stdio: "inherit",
17
- shell: process.platform === "win32"
18
- });
19
- if (result.error) throw result.error;
20
- process.exit(result.status ?? 1);
21
- } else if (existsSync(distCliPath)) await import(pathToFileURL(distCliPath).href);
22
- else throw new Error(`Unable to find limina CLI entry. Expected ${distCliPath}.`);
18
+ const result = spawnSync(
19
+ tsxCliPath,
20
+ [sourceCliPath, ...process.argv.slice(2)],
21
+ {
22
+ stdio: 'inherit',
23
+ shell: process.platform === 'win32',
24
+ },
25
+ );
26
+
27
+ if (result.error) {
28
+ throw result.error;
29
+ }
23
30
 
24
- //#endregion
25
- export { };
31
+ process.exit(result.status ?? 1);
32
+ } else if (existsSync(distCliPath)) {
33
+ await import(pathToFileURL(distCliPath).href);
34
+ } else {
35
+ throw new Error(`Unable to find limina CLI entry. Expected ${distCliPath}.`);
36
+ }