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.
@@ -1,360 +0,0 @@
1
- import { createRequire } from "node:module";
2
- import { existsSync } from "node:fs";
3
- import path from "node:path";
4
- import { pathToFileURL } from "node:url";
5
- import { z } from "zod";
6
-
7
- //#region src/utils/path.ts
8
- function toPosixPath(value) {
9
- return value.split(path.sep).join("/");
10
- }
11
- function normalizeAbsolutePath(value) {
12
- return toPosixPath(path.resolve(value));
13
- }
14
- function toRelativePath(rootDir, absolutePath) {
15
- const relativePath = toPosixPath(path.relative(rootDir, path.resolve(absolutePath)));
16
- return relativePath.length === 0 ? "." : relativePath;
17
- }
18
- function toAbsolutePath(rootDir, workspacePath) {
19
- return path.resolve(rootDir, workspacePath);
20
- }
21
- function normalizeWorkspacePath(rootDir, value) {
22
- return toRelativePath(rootDir, value);
23
- }
24
- function normalizeSlashes(value) {
25
- return value.replaceAll("\\", "/");
26
- }
27
- function isPathInsideDirectory(filePath, directoryPath) {
28
- const normalizedFilePath = normalizeAbsolutePath(filePath);
29
- const normalizedDirectoryPath = normalizeAbsolutePath(directoryPath);
30
- return normalizedFilePath === normalizedDirectoryPath || normalizedFilePath.startsWith(`${normalizedDirectoryPath}/`);
31
- }
32
-
33
- //#endregion
34
- //#region src/checkers.ts
35
- function createTscCommandTarget(options) {
36
- const relativeConfigPath = toRelativePath(options.projectRootDir, options.configPath);
37
- return {
38
- args: [
39
- "-b",
40
- relativeConfigPath,
41
- "--pretty",
42
- "false"
43
- ],
44
- command: options.commandOverride ?? "tsc",
45
- label: `tsc -b ${relativeConfigPath}`
46
- };
47
- }
48
- function createVueTscCommandTarget(options) {
49
- const relativeConfigPath = toRelativePath(options.projectRootDir, options.configPath);
50
- return {
51
- args: [
52
- "-b",
53
- relativeConfigPath,
54
- "--pretty",
55
- "false"
56
- ],
57
- command: "vue-tsc",
58
- label: `${options.checker.name}: vue-tsc -b ${relativeConfigPath}`
59
- };
60
- }
61
- function createSvelteCheckCommandTarget(options) {
62
- const relativeConfigPath = toRelativePath(options.projectRootDir, options.configPath);
63
- return {
64
- args: ["--tsconfig", relativeConfigPath],
65
- command: "svelte-check",
66
- label: `${options.checker.name}: svelte-check --tsconfig ${relativeConfigPath}`
67
- };
68
- }
69
- const builtinCheckerAdapters = {
70
- "svelte-check": {
71
- createCommandTarget: createSvelteCheckCommandTarget,
72
- defaultExtensions: [".svelte"],
73
- execution: "typecheck",
74
- packageNames: ["svelte-check"],
75
- preset: "svelte-check",
76
- sourceGraph: false,
77
- tier: "source-only"
78
- },
79
- tsc: {
80
- createCommandTarget: createTscCommandTarget,
81
- defaultExtensions: [
82
- ".ts",
83
- ".tsx",
84
- ".cts",
85
- ".mts",
86
- ".d.ts",
87
- ".d.cts",
88
- ".d.mts",
89
- ".json"
90
- ],
91
- execution: "build",
92
- packageNames: ["typescript"],
93
- preset: "tsc",
94
- sourceGraph: true,
95
- tier: "first-class"
96
- },
97
- "vue-tsc": {
98
- createCommandTarget: createVueTscCommandTarget,
99
- defaultExtensions: [".vue"],
100
- execution: "build",
101
- packageNames: ["vue-tsc", "@vue/compiler-sfc"],
102
- preset: "vue-tsc",
103
- sourceGraph: true,
104
- tier: "first-class"
105
- }
106
- };
107
- function isBuiltinCheckerPreset(value) {
108
- return Object.hasOwn(builtinCheckerAdapters, value);
109
- }
110
- function getCheckerAdapter(preset) {
111
- return isBuiltinCheckerPreset(preset) ? builtinCheckerAdapters[preset] : null;
112
- }
113
- function resolveCheckerPackageFromRoot(options) {
114
- const requireFromRoot = createRequire(path.join(options.projectRootDir, "package.json"));
115
- try {
116
- return requireFromRoot.resolve(`${options.packageName}/package.json`);
117
- } catch (error) {
118
- if (error && typeof error === "object" && "code" in error && error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED") return options.packageName;
119
- if (error && typeof error === "object" && "code" in error && error.code === "MODULE_NOT_FOUND") return;
120
- throw error;
121
- }
122
- }
123
- function collectMissingCheckerPeerDependencies(options) {
124
- const resolvePackage = options.resolvePackage ?? resolveCheckerPackageFromRoot;
125
- const missingCheckersByPackage = /* @__PURE__ */ new Map();
126
- for (const checker of options.checkers) {
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
- }
137
- }
138
- return [...missingCheckersByPackage.entries()].map(([packageName, checkerNames]) => ({
139
- checkerNames: [...checkerNames].sort((left, right) => left.localeCompare(right)),
140
- packageName
141
- })).sort((left, right) => left.packageName.localeCompare(right.packageName));
142
- }
143
- function formatMissingCheckerPeerDependencies(missingDependencies) {
144
- const packageNames = missingDependencies.map((dependency) => dependency.packageName);
145
- return [
146
- "Missing checker peer dependencies:",
147
- ...missingDependencies.map((dependency) => {
148
- const checkerList = dependency.checkerNames.map((checkerName) => `"${checkerName}"`).join(", ");
149
- return ` - ${dependency.packageName} (used by checker ${checkerList})`;
150
- }),
151
- `Fix: pnpm add -D ${packageNames.join(" ")}`
152
- ].join("\n");
153
- }
154
- function getCheckerExtensions(checker) {
155
- const adapter = getCheckerAdapter(checker.preset);
156
- if (adapter) return normalizeExtensions(adapter.defaultExtensions);
157
- throw new Error(`Checker preset "${checker.preset}" is not supported.`);
158
- }
159
- function getResolvedCheckers(config) {
160
- const checkers = config.config?.checkers;
161
- if (!checkers) return [];
162
- return Object.entries(checkers).map(([name, checker]) => ({
163
- entry: checker.entry.trim(),
164
- extensions: getCheckerExtensions(checker),
165
- name,
166
- preset: checker.preset
167
- })).sort((left, right) => left.name.localeCompare(right.name));
168
- }
169
- function normalizeExtensions(extensions) {
170
- return [...new Set(extensions)].sort((left, right) => {
171
- const lengthDelta = right.length - left.length;
172
- return lengthDelta === 0 ? left.localeCompare(right) : lengthDelta;
173
- });
174
- }
175
-
176
- //#endregion
177
- //#region src/config.ts
178
- function defineConfig(config) {
179
- return config;
180
- }
181
- const nonEmptyStringSchema = z.string().refine((value) => value.trim().length > 0);
182
- const checkerObjectSchema = z.looseObject({});
183
- const checkerConfigShapeSchema = z.looseObject({
184
- entry: nonEmptyStringSchema,
185
- preset: nonEmptyStringSchema
186
- });
187
- const liminaConfigShapeSchema = z.looseObject({ config: z.looseObject({ checkers: z.record(z.string(), checkerConfigShapeSchema).optional() }).optional() });
188
- function formatUnknownValue(value) {
189
- if (value === void 0) return "undefined";
190
- return JSON.stringify(value);
191
- }
192
- function formatZodPath(pathSegments) {
193
- return pathSegments.map((segment) => typeof segment === "number" ? `[${segment}]` : `.${String(segment)}`).join("").replace(/^\./u, "");
194
- }
195
- function getValueAtPath(value, pathSegments) {
196
- let current = value;
197
- for (const segment of pathSegments) {
198
- if (current === void 0 || current === null) return;
199
- current = current[segment];
200
- }
201
- return current;
202
- }
203
- function formatLiminaConfigShapeIssue(value, issue) {
204
- const pathSegments = issue.path;
205
- const field = formatZodPath(pathSegments);
206
- if (pathSegments.length === 0) return "limina config must export or return an object.";
207
- if (field === "config") return [
208
- "Invalid Limina config:",
209
- " field: config",
210
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
211
- " reason: config must be an object."
212
- ].join("\n");
213
- if (field === "config.checkers") return [
214
- "Invalid Limina checker config:",
215
- " field: config.checkers",
216
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
217
- " reason: config.checkers must be an object keyed by checker name."
218
- ].join("\n");
219
- if (pathSegments[0] === "config" && pathSegments[1] === "checkers") {
220
- const checkerName = pathSegments[2];
221
- const checkerField = `config.checkers.${String(checkerName)}`;
222
- if (pathSegments.length === 3) return [
223
- "Invalid Limina checker config:",
224
- ` field: ${checkerField}`,
225
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
226
- " reason: checker entries must be objects."
227
- ].join("\n");
228
- if (pathSegments[3] === "preset") return [
229
- "Invalid Limina checker config:",
230
- ` field: ${checkerField}.preset`,
231
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
232
- " reason: checker preset must be a non-empty string."
233
- ].join("\n");
234
- if (pathSegments[3] === "entry") return [
235
- "Invalid Limina checker entry config:",
236
- ` field: ${checkerField}.entry`,
237
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
238
- " reason: checker entry must be a non-empty string path."
239
- ].join("\n");
240
- }
241
- return [
242
- "Invalid Limina config:",
243
- ` field: ${field}`,
244
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
245
- ` reason: ${issue.message}`
246
- ].join("\n");
247
- }
248
- function collectLiminaConfigShapeProblems(value) {
249
- const result = liminaConfigShapeSchema.safeParse(value);
250
- if (result.success) return [];
251
- return result.error.issues.map((issue) => formatLiminaConfigShapeIssue(value, issue));
252
- }
253
- function collectCheckerConfigProblems(config) {
254
- const problems = collectLiminaConfigShapeProblems(config);
255
- if (!checkerObjectSchema.safeParse(config).success) return problems;
256
- const checkers = config.config?.checkers;
257
- if (checkers === void 0) return problems;
258
- if (!checkerObjectSchema.safeParse(checkers).success) return problems;
259
- for (const [checkerName, checker] of Object.entries(checkers)) {
260
- const field = `config.checkers.${checkerName}`;
261
- const checkerObjectResult = checkerObjectSchema.safeParse(checker);
262
- if (!checkerObjectResult.success) continue;
263
- const checkerRecord = checkerObjectResult.data;
264
- const preset = checkerRecord.preset;
265
- if (Object.hasOwn(checkerRecord, "extensions")) problems.push([
266
- "Invalid Limina checker config:",
267
- ` field: ${field}.extensions`,
268
- ` value: ${formatUnknownValue(checkerRecord.extensions)}`,
269
- " reason: checker extensions are fixed by built-in presets and cannot be configured."
270
- ].join("\n"));
271
- if (Object.hasOwn(checkerRecord, "routes")) problems.push([
272
- "Invalid Limina checker config:",
273
- ` field: ${field}.routes`,
274
- ` value: ${formatUnknownValue(checkerRecord.routes)}`,
275
- " reason: checker routes are not supported; move routes.build to entry and migrate routes.typecheck targets to tsconfig*.dts.json leaves reachable from that entry with local companions."
276
- ].join("\n"));
277
- if (typeof preset !== "string" || preset.trim().length === 0) continue;
278
- if (!getCheckerAdapter(preset)) {
279
- problems.push([
280
- "Unsupported Limina checker preset:",
281
- ` field: ${field}.preset`,
282
- ` value: ${formatUnknownValue(preset)}`,
283
- " reason: configured checker entries require a built-in checker adapter."
284
- ].join("\n"));
285
- continue;
286
- }
287
- }
288
- return problems;
289
- }
290
- function validateLiminaConfig(config) {
291
- const problems = collectCheckerConfigProblems(config);
292
- if (problems.length > 0) throw new Error(problems.join("\n\n"));
293
- }
294
- function getActiveCheckers(config) {
295
- validateLiminaConfig(config);
296
- return getResolvedCheckers(config);
297
- }
298
- function getActiveCheckerExtensions(config) {
299
- return normalizeExtensions(getActiveCheckers(config).flatMap((checker) => checker.extensions));
300
- }
301
- function normalizeConfig(value) {
302
- const config = value;
303
- validateLiminaConfig(config);
304
- return config;
305
- }
306
- function createConfigEnv(options) {
307
- return {
308
- command: options.command ?? "check",
309
- mode: options.mode ?? process.env.NODE_ENV ?? "default"
310
- };
311
- }
312
- function findPnpmWorkspaceRoot(startDir) {
313
- let currentDir = path.resolve(startDir);
314
- while (true) {
315
- if (existsSync(path.join(currentDir, "pnpm-workspace.yaml"))) return currentDir;
316
- const parentDir = path.dirname(currentDir);
317
- if (parentDir === currentDir) return null;
318
- currentDir = parentDir;
319
- }
320
- }
321
- function findLiminaConfigPath(startDir, rootDir) {
322
- let currentDir = path.resolve(startDir);
323
- const workspaceRootDir = path.resolve(rootDir);
324
- while (isPathInsideDirectory(currentDir, workspaceRootDir)) {
325
- const candidatePath = path.join(currentDir, "limina.config.mjs");
326
- if (existsSync(candidatePath)) return candidatePath;
327
- if (currentDir === workspaceRootDir) return null;
328
- const parentDir = path.dirname(currentDir);
329
- if (parentDir === currentDir) return null;
330
- currentDir = parentDir;
331
- }
332
- return null;
333
- }
334
- function inferWorkspaceRoot(startDir) {
335
- const rootDir = findPnpmWorkspaceRoot(startDir);
336
- if (!rootDir) throw new Error([`Unable to infer Limina workspace root from ${startDir}:`, "no pnpm-workspace.yaml was found in this directory or its parents."].join(" "));
337
- return rootDir;
338
- }
339
- function validateConfigPathInsideWorkspace(configPath, rootDir) {
340
- if (isPathInsideDirectory(configPath, rootDir)) return;
341
- throw new Error([`Unable to load Limina config at ${configPath}:`, `config file must be inside the governed pnpm workspace at ${rootDir}.`].join(" "));
342
- }
343
- async function resolveConfigExport(configExport, configEnv) {
344
- return normalizeConfig(typeof configExport === "function" ? await configExport(configEnv) : await configExport);
345
- }
346
- async function loadConfig(options = {}) {
347
- const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
348
- const rootDir = inferWorkspaceRoot(cwd);
349
- const configPath = options.configPath ? path.resolve(cwd, options.configPath) : findLiminaConfigPath(cwd, rootDir);
350
- if (configPath) validateConfigPathInsideWorkspace(configPath, rootDir);
351
- if (!configPath || !existsSync(configPath)) throw new Error(options.configPath ? `Unable to find limina config at ${configPath}` : `Unable to find limina config. Searched for limina.config.mjs from ${cwd} up to the pnpm workspace root at ${rootDir}.`);
352
- return {
353
- ...await resolveConfigExport((await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`)).default, createConfigEnv(options)),
354
- configPath,
355
- rootDir
356
- };
357
- }
358
-
359
- //#endregion
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 };