limina 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -34,6 +34,7 @@ Limina makes these rules reviewable, runnable, and suitable for CI.
34
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
35
  - **Checker target runner**: runs configured TypeScript and UI-framework checker entries in `typecheck` or `build` execution mode.
36
36
  - **Published package checks**: validates built package outputs with `publint`, Are The Types Wrong, and a runtime import boundary audit.
37
+ - **Release checks**: verifies npm tarball hygiene, source/packed manifest consistency, and registry-backed workspace publish order.
37
38
  - **Composable pipelines**: combines built-in checks and shell commands into named workflows such as `typecheck`, `package`, and `publish`.
38
39
  - **Typed configuration**: ships `defineConfig(...)` for editor hints and typed user configs.
39
40
 
@@ -95,8 +96,8 @@ export default defineConfig({
95
96
  ],
96
97
  },
97
98
 
98
- packageChecks: {
99
- targets: [
99
+ package: {
100
+ entries: [
100
101
  {
101
102
  name: '@acme/core',
102
103
  outDir: 'packages/core/dist',
@@ -106,7 +107,7 @@ export default defineConfig({
106
107
 
107
108
  pipelines: {
108
109
  package: ['package:check'],
109
- publish: ['graph:check', 'proof:check', 'package:check'],
110
+ publish: ['graph:check', 'proof:check', 'package:check', 'release:check'],
110
111
  },
111
112
  });
112
113
  ```
@@ -135,7 +136,7 @@ pnpm exec limina package check --package @acme/core
135
136
 
136
137
  ### Checker entry
137
138
 
138
- Each checker has one required `config.checkers.<name>.entry`, usually a `tsconfig*.build.json` graph aggregator. `limina checker build` runs the checker's build execution from that entry when the preset supports it. `limina checker typecheck` walks the same entry, finds reachable `tsconfig*.dts.json` declaration leaves, and checks their paired local companions.
139
+ 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
 
140
141
  ### Declaration leaf and local companion
141
142
 
@@ -151,7 +152,7 @@ A dependency declared as `link:`, `file:`, `catalog:`, or normal semver is treat
151
152
 
152
153
  ### Package checks
153
154
 
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
+ Source graph checks do not prove that an installed package works for consumers. `limina package check` inspects built package outputs under `package.entries[].outDir` and checks the actual package manifest, exports, type resolution, and runtime imports with `publint`, `attw`, and `boundary`. `limina release check` owns npm publish hygiene: private-output rejection, required README/license files, source map bans, source/packed manifest consistency, and registry-backed workspace publish order.
155
156
 
156
157
  ## CLI
157
158
 
@@ -168,13 +169,14 @@ limina [--config limina.config.mjs] [--mode mode] <command>
168
169
  | `limina paths generate` | Generate compatibility source `paths` configs for artifact-facing workspace exports. |
169
170
  | `limina paths apply` | Compatibility alias for `paths generate`. |
170
171
  | `limina paths check` | Fail when generated path files are stale. |
171
- | `limina checker typecheck` | Run typecheck targets derived from checker entries. |
172
172
  | `limina checker build` | Run build execution for checker entries that support it. |
173
- | `limina checker typecheck --concurrency <n>` | Limit concurrent checker processes. |
173
+ | `limina checker typecheck` | Run source-only checker entries such as `svelte-check`. |
174
174
  | `limina package check` | Run configured package output checks. |
175
- | `limina package check --package <name>` | Check one configured package target. |
175
+ | `limina package check --package <name>` | Check one configured package entry. |
176
176
  | `limina package check --tool <tool>` | Run only `publint`, `attw`, or `boundary`. |
177
177
  | `limina package check --attw-profile <profile>` | Override the ATTW profile: `strict`, `node16`, or `esm-only`. |
178
+ | `limina release check` | Check release hygiene and dependency consistency for the cwd package entry. |
179
+ | `limina release check --package <name>` | Check release hygiene and dependency consistency for one or more package entries. |
178
180
 
179
181
  ## Configuration reference
180
182
 
@@ -198,7 +200,7 @@ config: {
198
200
  }
199
201
  ```
200
202
 
201
- `config.checkers` defines checker entries. Every configured checker must declare a non-empty `entry`. Built-in presets can omit `extensions`; if `source.include` is omitted, Limina derives the source boundary from configured checker extensions, then applies `source.exclude`.
203
+ `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`.
202
204
 
203
205
  ### `graph`
204
206
 
@@ -270,11 +272,11 @@ proof: {
270
272
 
271
273
  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.
272
274
 
273
- ### `packageChecks`
275
+ ### `package`
274
276
 
275
277
  ```js
276
- packageChecks: {
277
- targets: [
278
+ package: {
279
+ entries: [
278
280
  {
279
281
  name: '@acme/core',
280
282
  outDir: 'packages/core/dist',
@@ -290,7 +292,7 @@ packageChecks: {
290
292
  }
291
293
  ```
292
294
 
293
- `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`.
295
+ `outDir` must point at the built package directory that contains the publish-ready `package.json`. `package:check` uses this output for consumer-facing resolver and runtime checks; `release:check` packs the same output and verifies publish hygiene such as README/license files and source map bans.
294
296
 
295
297
  ### `pipelines`
296
298
 
@@ -303,7 +305,7 @@ pipelines: {
303
305
  }
304
306
  ```
305
307
 
306
- `limina check` runs the built-in default pipeline: `graph:check`, `source:check`, `proof:check`, 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.
308
+ `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.
307
309
 
308
310
  String steps can be built-in task names or simple commands. Use object command steps when arguments, `cwd`, or `env` need to be explicit.
309
311
 
@@ -357,7 +359,7 @@ Run the command from inside the workspace, or pass `--config ./limina.config.mjs
357
359
 
358
360
  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.
359
361
 
360
- ### `packageChecks.targets[x].outDir` is invalid
362
+ ### `package.entries[x].outDir` is invalid
361
363
 
362
364
  Set `outDir` to the built package directory, not the source package directory, unless that directory is itself the publish-ready package output.
363
365
 
@@ -35,39 +35,30 @@ function isPathInsideDirectory(filePath, directoryPath) {
35
35
  function createTscCommandTarget(options) {
36
36
  const relativeConfigPath = toRelativePath(options.projectRootDir, options.configPath);
37
37
  return {
38
- args: options.executionKind === "build" ? [
38
+ args: [
39
39
  "-b",
40
40
  relativeConfigPath,
41
41
  "--pretty",
42
42
  "false"
43
- ] : [
44
- "-p",
45
- relativeConfigPath,
46
- "--noEmit"
47
43
  ],
48
44
  command: options.commandOverride ?? "tsc",
49
- label: options.executionKind === "build" ? `tsc -b ${relativeConfigPath}` : `tsc: ${relativeConfigPath}`
45
+ label: `tsc -b ${relativeConfigPath}`
50
46
  };
51
47
  }
52
48
  function createVueTscCommandTarget(options) {
53
49
  const relativeConfigPath = toRelativePath(options.projectRootDir, options.configPath);
54
50
  return {
55
- args: options.executionKind === "build" ? [
51
+ args: [
56
52
  "-b",
57
53
  relativeConfigPath,
58
54
  "--pretty",
59
55
  "false"
60
- ] : [
61
- "-p",
62
- relativeConfigPath,
63
- "--noEmit"
64
56
  ],
65
57
  command: "vue-tsc",
66
- label: options.executionKind === "build" ? `${options.checker.name}: vue-tsc -b ${relativeConfigPath}` : `${options.checker.name}: vue-tsc -p ${relativeConfigPath}`
58
+ label: `${options.checker.name}: vue-tsc -b ${relativeConfigPath}`
67
59
  };
68
60
  }
69
61
  function createSvelteCheckCommandTarget(options) {
70
- if (options.executionKind === "build") throw new Error(`Checker "${options.checker.name}" uses svelte-check, which does not support checker:build.`);
71
62
  const relativeConfigPath = toRelativePath(options.projectRootDir, options.configPath);
72
63
  return {
73
64
  args: ["--tsconfig", relativeConfigPath],
@@ -79,10 +70,11 @@ const builtinCheckerAdapters = {
79
70
  "svelte-check": {
80
71
  createCommandTarget: createSvelteCheckCommandTarget,
81
72
  defaultExtensions: [".svelte"],
82
- graph: false,
83
- packageName: "svelte-check",
73
+ execution: "typecheck",
74
+ packageNames: ["svelte-check"],
84
75
  preset: "svelte-check",
85
- supportedExecutions: ["typecheck"]
76
+ sourceGraph: false,
77
+ tier: "source-only"
86
78
  },
87
79
  tsc: {
88
80
  createCommandTarget: createTscCommandTarget,
@@ -96,18 +88,20 @@ const builtinCheckerAdapters = {
96
88
  ".d.mts",
97
89
  ".json"
98
90
  ],
99
- graph: true,
100
- packageName: "typescript",
91
+ execution: "build",
92
+ packageNames: ["typescript"],
101
93
  preset: "tsc",
102
- supportedExecutions: ["typecheck", "build"]
94
+ sourceGraph: true,
95
+ tier: "first-class"
103
96
  },
104
97
  "vue-tsc": {
105
98
  createCommandTarget: createVueTscCommandTarget,
106
99
  defaultExtensions: [".vue"],
107
- graph: false,
108
- packageName: "vue-tsc",
100
+ execution: "build",
101
+ packageNames: ["vue-tsc", "@vue/compiler-sfc"],
109
102
  preset: "vue-tsc",
110
- supportedExecutions: ["typecheck", "build"]
103
+ sourceGraph: true,
104
+ tier: "first-class"
111
105
  }
112
106
  };
113
107
  function isBuiltinCheckerPreset(value) {
@@ -130,15 +124,16 @@ function collectMissingCheckerPeerDependencies(options) {
130
124
  const resolvePackage = options.resolvePackage ?? resolveCheckerPackageFromRoot;
131
125
  const missingCheckersByPackage = /* @__PURE__ */ new Map();
132
126
  for (const checker of options.checkers) {
133
- const packageName = getCheckerAdapter(checker.preset)?.packageName;
134
- if (!packageName) continue;
135
- if (resolvePackage({
136
- packageName,
137
- projectRootDir: options.projectRootDir
138
- })) continue;
139
- const checkerNames = missingCheckersByPackage.get(packageName) ?? /* @__PURE__ */ new Set();
140
- checkerNames.add(checker.name);
141
- missingCheckersByPackage.set(packageName, checkerNames);
127
+ const packageNames = getCheckerAdapter(checker.preset)?.packageNames ?? [];
128
+ for (const packageName of packageNames) {
129
+ if (resolvePackage({
130
+ packageName,
131
+ projectRootDir: options.projectRootDir
132
+ })) continue;
133
+ const checkerNames = missingCheckersByPackage.get(packageName) ?? /* @__PURE__ */ new Set();
134
+ checkerNames.add(checker.name);
135
+ missingCheckersByPackage.set(packageName, checkerNames);
136
+ }
142
137
  }
143
138
  return [...missingCheckersByPackage.entries()].map(([packageName, checkerNames]) => ({
144
139
  checkerNames: [...checkerNames].sort((left, right) => left.localeCompare(right)),
@@ -157,10 +152,9 @@ function formatMissingCheckerPeerDependencies(missingDependencies) {
157
152
  ].join("\n");
158
153
  }
159
154
  function getCheckerExtensions(checker) {
160
- if (checker.extensions) return normalizeExtensions(checker.extensions);
161
155
  const adapter = getCheckerAdapter(checker.preset);
162
- if (adapter?.defaultExtensions) return normalizeExtensions(adapter.defaultExtensions);
163
- throw new Error(`Checker preset "${checker.preset}" must declare non-empty extensions because it is not a built-in preset.`);
156
+ if (adapter) return normalizeExtensions(adapter.defaultExtensions);
157
+ throw new Error(`Checker preset "${checker.preset}" is not supported.`);
164
158
  }
165
159
  function getResolvedCheckers(config) {
166
160
  const checkers = config.config?.checkers;
@@ -185,11 +179,9 @@ function defineConfig(config) {
185
179
  return config;
186
180
  }
187
181
  const nonEmptyStringSchema = z.string().refine((value) => value.trim().length > 0);
188
- const dotPrefixedStringSchema = nonEmptyStringSchema.refine((value) => value.startsWith("."));
189
182
  const checkerObjectSchema = z.looseObject({});
190
183
  const checkerConfigShapeSchema = z.looseObject({
191
184
  entry: nonEmptyStringSchema,
192
- extensions: z.array(dotPrefixedStringSchema).nonempty().optional(),
193
185
  preset: nonEmptyStringSchema
194
186
  });
195
187
  const liminaConfigShapeSchema = z.looseObject({ config: z.looseObject({ checkers: z.record(z.string(), checkerConfigShapeSchema).optional() }).optional() });
@@ -245,15 +237,6 @@ function formatLiminaConfigShapeIssue(value, issue) {
245
237
  ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
246
238
  " reason: checker entry must be a non-empty string path."
247
239
  ].join("\n");
248
- if (pathSegments[3] === "extensions") {
249
- const extensionsPath = pathSegments.slice(0, 4);
250
- return [
251
- "Invalid Limina checker config:",
252
- ` field: ${checkerField}.extensions`,
253
- ` value: ${formatUnknownValue(getValueAtPath(value, extensionsPath))}`,
254
- " reason: checker extensions must be a non-empty array of dot-prefixed strings."
255
- ].join("\n");
256
- }
257
240
  }
258
241
  return [
259
242
  "Invalid Limina config:",
@@ -279,11 +262,11 @@ function collectCheckerConfigProblems(config) {
279
262
  if (!checkerObjectResult.success) continue;
280
263
  const checkerRecord = checkerObjectResult.data;
281
264
  const preset = checkerRecord.preset;
282
- if (checkerRecord.extensions === void 0 && typeof preset === "string" && !isBuiltinCheckerPreset(preset)) problems.push([
265
+ if (Object.hasOwn(checkerRecord, "extensions")) problems.push([
283
266
  "Invalid Limina checker config:",
284
267
  ` field: ${field}.extensions`,
285
- " value: undefined",
286
- " reason: extensions may only be omitted for built-in presets."
268
+ ` value: ${formatUnknownValue(checkerRecord.extensions)}`,
269
+ " reason: checker extensions are fixed by built-in presets and cannot be configured."
287
270
  ].join("\n"));
288
271
  if (Object.hasOwn(checkerRecord, "routes")) problems.push([
289
272
  "Invalid Limina checker config:",
@@ -374,4 +357,4 @@ async function loadConfig(options = {}) {
374
357
  }
375
358
 
376
359
  //#endregion
377
- export { validateLiminaConfig as a, getCheckerAdapter as c, normalizeSlashes as d, normalizeWorkspacePath as f, toRelativePath as h, loadConfig as i, isPathInsideDirectory as l, toPosixPath as m, getActiveCheckerExtensions as n, collectMissingCheckerPeerDependencies as o, toAbsolutePath as p, getActiveCheckers as r, formatMissingCheckerPeerDependencies as s, defineConfig as t, normalizeAbsolutePath as u };
360
+ export { validateLiminaConfig as a, getCheckerAdapter as c, normalizeAbsolutePath as d, normalizeSlashes as f, toRelativePath as g, toPosixPath as h, loadConfig as i, normalizeExtensions as l, toAbsolutePath as m, getActiveCheckerExtensions as n, collectMissingCheckerPeerDependencies as o, normalizeWorkspacePath as p, getActiveCheckers as r, formatMissingCheckerPeerDependencies as s, defineConfig as t, isPathInsideDirectory as u };