mdx-tsx-import-checker 0.1.0
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/dist/cli.d.ts +2 -0
- package/dist/cli.js +592 -0
- package/dist/cli.js.map +7 -0
- package/package.json +30 -0
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli.ts
|
|
27
|
+
var fs5 = __toESM(require("fs"));
|
|
28
|
+
var path4 = __toESM(require("path"));
|
|
29
|
+
|
|
30
|
+
// ../core/src/index.ts
|
|
31
|
+
var fs4 = __toESM(require("fs"));
|
|
32
|
+
var path3 = __toESM(require("path"));
|
|
33
|
+
|
|
34
|
+
// ../core/src/parser.ts
|
|
35
|
+
var MAX_IMPORT_LINES = 50;
|
|
36
|
+
function parseImports(text) {
|
|
37
|
+
const results = [];
|
|
38
|
+
const lines = text.split("\n");
|
|
39
|
+
let i = 0;
|
|
40
|
+
while (i < lines.length) {
|
|
41
|
+
const line = lines[i];
|
|
42
|
+
if (!line.trimStart().startsWith("import ")) {
|
|
43
|
+
i++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
let raw = line;
|
|
47
|
+
let j = i;
|
|
48
|
+
const limit = Math.min(i + MAX_IMPORT_LINES, lines.length - 1);
|
|
49
|
+
while (!hasFromClause(raw) && j < limit) {
|
|
50
|
+
j++;
|
|
51
|
+
raw += " " + lines[j];
|
|
52
|
+
}
|
|
53
|
+
const parsed = parseSingleImport(raw, i);
|
|
54
|
+
if (parsed) results.push(parsed);
|
|
55
|
+
i = j + 1;
|
|
56
|
+
}
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
function hasFromClause(raw) {
|
|
60
|
+
const fromIdx = raw.indexOf(" from ");
|
|
61
|
+
if (fromIdx === -1) return false;
|
|
62
|
+
const afterFrom = raw.slice(fromIdx + 6).trimStart();
|
|
63
|
+
return afterFrom.startsWith("'") || afterFrom.startsWith('"');
|
|
64
|
+
}
|
|
65
|
+
function parseSingleImport(raw, line) {
|
|
66
|
+
const fromMatch = raw.match(/from\s+['"]([^'"]+)['"]/);
|
|
67
|
+
if (!fromMatch) return null;
|
|
68
|
+
const moduleSpecifier = fromMatch[1];
|
|
69
|
+
const braceMatch = raw.match(/\{([^}]+)\}/);
|
|
70
|
+
if (!braceMatch) return null;
|
|
71
|
+
const namedImports = braceMatch[1].split(",").map((s) => {
|
|
72
|
+
const m = s.match(/^\s*(\w+)(?:\s+as\s+\w+)?\s*$/);
|
|
73
|
+
return m ? m[1] : s.trim();
|
|
74
|
+
}).filter(Boolean);
|
|
75
|
+
if (namedImports.length === 0) return null;
|
|
76
|
+
return { namedImports, moduleSpecifier, line, raw };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ../core/src/resolver.ts
|
|
80
|
+
var fs = __toESM(require("fs"));
|
|
81
|
+
var path = __toESM(require("path"));
|
|
82
|
+
var EXTENSIONS = [
|
|
83
|
+
".ts",
|
|
84
|
+
".tsx",
|
|
85
|
+
".d.ts",
|
|
86
|
+
".js",
|
|
87
|
+
".jsx",
|
|
88
|
+
".mjs",
|
|
89
|
+
".cjs",
|
|
90
|
+
"/index.ts",
|
|
91
|
+
"/index.tsx",
|
|
92
|
+
"/index.d.ts",
|
|
93
|
+
"/index.js",
|
|
94
|
+
"/index.mjs"
|
|
95
|
+
];
|
|
96
|
+
function resolveModulePath(specifier, fromFile, aliases, projectRoot) {
|
|
97
|
+
let resolved = null;
|
|
98
|
+
if (specifier.startsWith(".")) {
|
|
99
|
+
resolved = resolveRelative(specifier, fromFile);
|
|
100
|
+
} else {
|
|
101
|
+
const aliasResolved = resolveAlias(specifier, aliases);
|
|
102
|
+
if (aliasResolved !== null) {
|
|
103
|
+
resolved = aliasResolved;
|
|
104
|
+
} else {
|
|
105
|
+
resolved = resolveFromNodeModules(specifier, fromFile, projectRoot);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (resolved !== null && projectRoot !== null) {
|
|
109
|
+
if (!isWithinAllowedRoots(resolved, fromFile, projectRoot)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return resolved;
|
|
114
|
+
}
|
|
115
|
+
function isWithinAllowedRoots(resolved, _fromFile, projectRoot) {
|
|
116
|
+
try {
|
|
117
|
+
const realProject = fs.realpathSync(path.resolve(projectRoot)) + path.sep;
|
|
118
|
+
const realResolved = fs.realpathSync(path.resolve(resolved));
|
|
119
|
+
return realResolved.startsWith(realProject);
|
|
120
|
+
} catch {
|
|
121
|
+
const absProject = path.resolve(projectRoot) + path.sep;
|
|
122
|
+
const absResolved = path.resolve(resolved);
|
|
123
|
+
return absResolved.startsWith(absProject);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function resolveRelative(specifier, fromFile) {
|
|
127
|
+
const dir = path.dirname(fromFile);
|
|
128
|
+
return resolveWithExtensions(path.resolve(dir, specifier));
|
|
129
|
+
}
|
|
130
|
+
function resolveAlias(specifier, aliases) {
|
|
131
|
+
for (const [alias, aliasRoot] of Object.entries(aliases)) {
|
|
132
|
+
const prefix = alias.endsWith("/*") ? alias.slice(0, -1) : alias + "/";
|
|
133
|
+
const bare = alias.replace("/*", "");
|
|
134
|
+
if (specifier.startsWith(prefix) || specifier === bare) {
|
|
135
|
+
const rest = specifier.slice(prefix.length);
|
|
136
|
+
return resolveWithExtensions(path.join(aliasRoot, rest));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
function resolveFromNodeModules(specifier, fromFile, projectRoot) {
|
|
142
|
+
const nmRoots = collectNodeModuleRoots(fromFile, projectRoot);
|
|
143
|
+
for (const nmRoot of nmRoots) {
|
|
144
|
+
const result = tryResolvePackage(specifier, nmRoot);
|
|
145
|
+
if (result) return result;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
function collectNodeModuleRoots(fromFile, projectRoot) {
|
|
150
|
+
const roots = [];
|
|
151
|
+
let dir = path.dirname(fromFile);
|
|
152
|
+
const stopAt = projectRoot ? path.resolve(projectRoot) : path.parse(dir).root;
|
|
153
|
+
while (true) {
|
|
154
|
+
const candidate = path.join(dir, "node_modules");
|
|
155
|
+
if (fs.existsSync(candidate)) roots.push(candidate);
|
|
156
|
+
if (path.resolve(dir) === stopAt || dir === path.parse(dir).root) break;
|
|
157
|
+
dir = path.dirname(dir);
|
|
158
|
+
}
|
|
159
|
+
return roots;
|
|
160
|
+
}
|
|
161
|
+
function tryResolvePackage(specifier, nmRoot) {
|
|
162
|
+
const { pkgName, subPath } = splitSpecifier(specifier);
|
|
163
|
+
const pkgDir = path.join(nmRoot, pkgName);
|
|
164
|
+
if (!fs.existsSync(pkgDir)) return null;
|
|
165
|
+
const pkgJson = readPackageJson(pkgDir);
|
|
166
|
+
if (subPath) {
|
|
167
|
+
return resolveSubPath(subPath, pkgDir, pkgJson);
|
|
168
|
+
}
|
|
169
|
+
return resolvePackageRoot(pkgDir, pkgJson);
|
|
170
|
+
}
|
|
171
|
+
function splitSpecifier(specifier) {
|
|
172
|
+
if (specifier.startsWith("@")) {
|
|
173
|
+
const parts = specifier.split("/");
|
|
174
|
+
return {
|
|
175
|
+
pkgName: parts[0] + "/" + parts[1],
|
|
176
|
+
subPath: parts.slice(2).join("/")
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const slashIdx = specifier.indexOf("/");
|
|
180
|
+
return {
|
|
181
|
+
pkgName: slashIdx === -1 ? specifier : specifier.slice(0, slashIdx),
|
|
182
|
+
subPath: slashIdx === -1 ? "" : specifier.slice(slashIdx + 1)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function readPackageJson(pkgDir) {
|
|
186
|
+
try {
|
|
187
|
+
return JSON.parse(
|
|
188
|
+
fs.readFileSync(path.join(pkgDir, "package.json"), "utf-8")
|
|
189
|
+
);
|
|
190
|
+
} catch {
|
|
191
|
+
return {};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function resolveSubPath(subPath, pkgDir, pkgJson) {
|
|
195
|
+
const exportsField = pkgJson["exports"];
|
|
196
|
+
if (exportsField) {
|
|
197
|
+
const resolved = resolveExportsField(exportsField, "./" + subPath, pkgDir);
|
|
198
|
+
if (resolved) return resolved;
|
|
199
|
+
}
|
|
200
|
+
return resolveWithExtensions(path.join(pkgDir, subPath));
|
|
201
|
+
}
|
|
202
|
+
function resolvePackageRoot(pkgDir, pkgJson) {
|
|
203
|
+
for (const field of ["types", "typings"]) {
|
|
204
|
+
const entry = pkgJson[field];
|
|
205
|
+
if (entry) {
|
|
206
|
+
const candidate = path.resolve(pkgDir, entry);
|
|
207
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const exportsField = pkgJson["exports"];
|
|
211
|
+
if (exportsField) {
|
|
212
|
+
const resolved = resolveExportsField(exportsField, ".", pkgDir);
|
|
213
|
+
if (resolved) return resolved;
|
|
214
|
+
}
|
|
215
|
+
const main2 = pkgJson["main"];
|
|
216
|
+
if (main2) {
|
|
217
|
+
const candidate = path.resolve(pkgDir, main2);
|
|
218
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
219
|
+
}
|
|
220
|
+
return resolveWithExtensions(path.join(pkgDir, "index"));
|
|
221
|
+
}
|
|
222
|
+
function resolveExportsField(exportsField, key, pkgDir) {
|
|
223
|
+
const entry = exportsField[key];
|
|
224
|
+
if (!entry) return null;
|
|
225
|
+
for (const candidate of extractExportCandidates(entry)) {
|
|
226
|
+
if (typeof candidate !== "string") continue;
|
|
227
|
+
const resolved = path.resolve(pkgDir, candidate);
|
|
228
|
+
if (fs.existsSync(resolved)) return resolved;
|
|
229
|
+
const withExt = resolveWithExtensions(resolved.replace(/\.[^.]+$/, ""));
|
|
230
|
+
if (fs.existsSync(withExt)) return withExt;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
function extractExportCandidates(entry) {
|
|
235
|
+
if (typeof entry === "string") return [entry];
|
|
236
|
+
if (typeof entry !== "object" || entry === null) return [];
|
|
237
|
+
const obj = entry;
|
|
238
|
+
const results = [];
|
|
239
|
+
for (const cond of ["types", "import", "require", "default"]) {
|
|
240
|
+
if (cond in obj) results.push(...extractExportCandidates(obj[cond]));
|
|
241
|
+
}
|
|
242
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
243
|
+
if (!["types", "import", "require", "default"].includes(k)) {
|
|
244
|
+
results.push(...extractExportCandidates(v));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
function resolveWithExtensions(base) {
|
|
250
|
+
if (path.extname(base) && fs.existsSync(base)) return base;
|
|
251
|
+
for (const ext of EXTENSIONS) {
|
|
252
|
+
const candidate = base + ext;
|
|
253
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
254
|
+
}
|
|
255
|
+
return base;
|
|
256
|
+
}
|
|
257
|
+
function findProjectRoot(fromFile) {
|
|
258
|
+
let dir = path.dirname(fromFile);
|
|
259
|
+
const root = path.parse(dir).root;
|
|
260
|
+
while (dir !== root) {
|
|
261
|
+
for (const marker of ["tsconfig.json", "package.json", ".git"]) {
|
|
262
|
+
if (fs.existsSync(path.join(dir, marker))) return dir;
|
|
263
|
+
}
|
|
264
|
+
dir = path.dirname(dir);
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ../core/src/tsconfig.ts
|
|
270
|
+
var fs2 = __toESM(require("fs"));
|
|
271
|
+
var path2 = __toESM(require("path"));
|
|
272
|
+
function resolveAliases(projectRoot, tsconfigPath, log = () => {
|
|
273
|
+
}) {
|
|
274
|
+
const aliases = {};
|
|
275
|
+
const tsconfig = tsconfigPath ?? findTsconfig(projectRoot);
|
|
276
|
+
if (!tsconfig || !fs2.existsSync(tsconfig)) return aliases;
|
|
277
|
+
try {
|
|
278
|
+
const raw = fs2.readFileSync(tsconfig, "utf-8");
|
|
279
|
+
const parsed = parseJsonWithComments(raw);
|
|
280
|
+
if (!parsed) return aliases;
|
|
281
|
+
const compilerOptions = parsed.compilerOptions ?? {};
|
|
282
|
+
const paths = compilerOptions.paths ?? {};
|
|
283
|
+
const baseUrl = compilerOptions.baseUrl ?? ".";
|
|
284
|
+
const absoluteBase = path2.resolve(path2.dirname(tsconfig), baseUrl);
|
|
285
|
+
log(` tsconfig paths: ${JSON.stringify(paths)}, baseUrl: ${baseUrl}`);
|
|
286
|
+
for (const [alias, targets] of Object.entries(paths)) {
|
|
287
|
+
if (targets.length > 0) {
|
|
288
|
+
const target = targets[0].endsWith("/*") ? targets[0].slice(0, -2) : targets[0];
|
|
289
|
+
aliases[alias] = path2.resolve(absoluteBase, target);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} catch (e) {
|
|
293
|
+
log(` tsconfig error: ${String(e)}`);
|
|
294
|
+
}
|
|
295
|
+
return aliases;
|
|
296
|
+
}
|
|
297
|
+
function findTsconfig(projectRoot) {
|
|
298
|
+
const candidates = [
|
|
299
|
+
"tsconfig.json",
|
|
300
|
+
"packages/docs/tsconfig.json",
|
|
301
|
+
"packages/web/tsconfig.json"
|
|
302
|
+
];
|
|
303
|
+
for (const c of candidates) {
|
|
304
|
+
const full = path2.join(projectRoot, c);
|
|
305
|
+
if (fs2.existsSync(full)) return full;
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
function parseJsonWithComments(raw) {
|
|
310
|
+
try {
|
|
311
|
+
const cleaned = stripJsonComments(raw);
|
|
312
|
+
const noTrailing = cleaned.replace(/,(\s*[}\]])/g, "$1");
|
|
313
|
+
return JSON.parse(noTrailing);
|
|
314
|
+
} catch {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function stripJsonComments(input) {
|
|
319
|
+
let state = "code";
|
|
320
|
+
let out = "";
|
|
321
|
+
let i = 0;
|
|
322
|
+
while (i < input.length) {
|
|
323
|
+
const ch = input[i];
|
|
324
|
+
const next = input[i + 1];
|
|
325
|
+
switch (state) {
|
|
326
|
+
case "code":
|
|
327
|
+
if (ch === '"') {
|
|
328
|
+
state = "string";
|
|
329
|
+
out += ch;
|
|
330
|
+
} else if (ch === "/" && next === "/") {
|
|
331
|
+
state = "lineComment";
|
|
332
|
+
i += 2;
|
|
333
|
+
continue;
|
|
334
|
+
} else if (ch === "/" && next === "*") {
|
|
335
|
+
state = "blockComment";
|
|
336
|
+
i += 2;
|
|
337
|
+
continue;
|
|
338
|
+
} else {
|
|
339
|
+
out += ch;
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
case "string":
|
|
343
|
+
out += ch;
|
|
344
|
+
if (ch === "\\" && next !== void 0) {
|
|
345
|
+
out += next;
|
|
346
|
+
i += 2;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (ch === '"') {
|
|
350
|
+
state = "code";
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
case "lineComment":
|
|
354
|
+
if (ch === "\n") {
|
|
355
|
+
out += "\n";
|
|
356
|
+
state = "code";
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
case "blockComment":
|
|
360
|
+
if (ch === "*" && next === "/") {
|
|
361
|
+
state = "code";
|
|
362
|
+
i += 2;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (ch === "\n") out += "\n";
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
i++;
|
|
369
|
+
}
|
|
370
|
+
return out;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ../core/src/exports.ts
|
|
374
|
+
var fs3 = __toESM(require("fs"));
|
|
375
|
+
function getNamedExports(filePath) {
|
|
376
|
+
let source;
|
|
377
|
+
try {
|
|
378
|
+
source = fs3.readFileSync(filePath, "utf-8");
|
|
379
|
+
} catch {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
const namedExports = /* @__PURE__ */ new Set();
|
|
383
|
+
collectDeclarationExports(source, namedExports);
|
|
384
|
+
collectBraceExports(source, namedExports);
|
|
385
|
+
if (/export\s+default\b/.test(source)) {
|
|
386
|
+
namedExports.add("default");
|
|
387
|
+
}
|
|
388
|
+
return namedExports;
|
|
389
|
+
}
|
|
390
|
+
function collectDeclarationExports(source, result) {
|
|
391
|
+
const pattern = /export\s+(?:async\s+)?(?:function|const|let|var|class|type|interface|enum)\s+(\w+)/g;
|
|
392
|
+
for (const m of source.matchAll(pattern)) {
|
|
393
|
+
result.add(m[1]);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function collectBraceExports(source, result) {
|
|
397
|
+
const pattern = /export\s*\{([^}]+)\}/g;
|
|
398
|
+
for (const m of source.matchAll(pattern)) {
|
|
399
|
+
for (const entry of m[1].split(",")) {
|
|
400
|
+
const asMatch = entry.match(/\w+\s+as\s+(\w+)/);
|
|
401
|
+
if (asMatch) {
|
|
402
|
+
result.add(asMatch[1]);
|
|
403
|
+
} else {
|
|
404
|
+
const name = entry.trim();
|
|
405
|
+
if (name) result.add(name);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ../core/src/index.ts
|
|
412
|
+
function checkFile(filePath, options = {}) {
|
|
413
|
+
const log = options.logger ?? (() => {
|
|
414
|
+
});
|
|
415
|
+
const errors = [];
|
|
416
|
+
let text;
|
|
417
|
+
try {
|
|
418
|
+
text = fs4.readFileSync(filePath, "utf-8");
|
|
419
|
+
} catch (e) {
|
|
420
|
+
errors.push({ file: filePath, line: 0, column: 0, message: `Cannot read file: ${String(e)}` });
|
|
421
|
+
return errors;
|
|
422
|
+
}
|
|
423
|
+
const imports = parseImports(text);
|
|
424
|
+
log(` parsed ${imports.length} import(s) in ${filePath}`);
|
|
425
|
+
if (imports.length === 0) return errors;
|
|
426
|
+
const projectRoot = options.tsconfigPath ? path3.dirname(options.tsconfigPath) : findProjectRoot(filePath);
|
|
427
|
+
log(` projectRoot: ${projectRoot ?? "(null)"}`);
|
|
428
|
+
const aliases = projectRoot ? resolveAliases(projectRoot, options.tsconfigPath, log) : {};
|
|
429
|
+
log(` aliases: ${JSON.stringify(aliases)}`);
|
|
430
|
+
const lines = text.split("\n");
|
|
431
|
+
for (const imp of imports) {
|
|
432
|
+
log(` checking: { ${imp.namedImports.join(", ")} } from '${imp.moduleSpecifier}'`);
|
|
433
|
+
const resolvedPath = resolveModulePath(imp.moduleSpecifier, filePath, aliases, projectRoot);
|
|
434
|
+
log(` resolved: ${resolvedPath ?? "(null \u2014 skipped)"}`);
|
|
435
|
+
if (resolvedPath === null) continue;
|
|
436
|
+
if (!fs4.existsSync(resolvedPath)) {
|
|
437
|
+
errors.push({
|
|
438
|
+
file: filePath,
|
|
439
|
+
line: imp.line + 1,
|
|
440
|
+
column: 0,
|
|
441
|
+
message: `Cannot find module '${imp.moduleSpecifier}' (resolved to: ${resolvedPath})`
|
|
442
|
+
});
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const namedExports = getNamedExports(resolvedPath);
|
|
446
|
+
if (namedExports === null) continue;
|
|
447
|
+
log(` exports: ${[...namedExports].join(", ")}`);
|
|
448
|
+
for (const name of imp.namedImports) {
|
|
449
|
+
if (!namedExports.has(name)) {
|
|
450
|
+
const col = lines[imp.line]?.indexOf(name) ?? -1;
|
|
451
|
+
const available = [...namedExports].filter((e) => e !== "default").sort().join(", ");
|
|
452
|
+
errors.push({
|
|
453
|
+
file: filePath,
|
|
454
|
+
line: imp.line + 1,
|
|
455
|
+
column: col >= 0 ? col + 1 : 0,
|
|
456
|
+
message: `'${name}' is not exported from '${imp.moduleSpecifier}'` + (available ? `. Available: ${available}` : "")
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return errors;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/cli.ts
|
|
465
|
+
function parseArgs(argv) {
|
|
466
|
+
const args = argv.slice(2);
|
|
467
|
+
const options = {
|
|
468
|
+
patterns: [],
|
|
469
|
+
verbose: false,
|
|
470
|
+
format: "pretty"
|
|
471
|
+
};
|
|
472
|
+
for (let i = 0; i < args.length; i++) {
|
|
473
|
+
const arg = args[i];
|
|
474
|
+
if (arg === "--tsconfig" && args[i + 1]) {
|
|
475
|
+
options.tsconfigPath = path4.resolve(process.cwd(), args[++i]);
|
|
476
|
+
} else if (arg === "--verbose") {
|
|
477
|
+
options.verbose = true;
|
|
478
|
+
} else if (arg === "--format" && args[i + 1]) {
|
|
479
|
+
const fmt = args[++i];
|
|
480
|
+
if (fmt === "pretty" || fmt === "github" || fmt === "json") {
|
|
481
|
+
options.format = fmt;
|
|
482
|
+
} else {
|
|
483
|
+
console.error(`Unknown format: ${fmt}. Use pretty, github, or json.`);
|
|
484
|
+
process.exit(1);
|
|
485
|
+
}
|
|
486
|
+
} else if (!arg.startsWith("--")) {
|
|
487
|
+
options.patterns.push(arg);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return options;
|
|
491
|
+
}
|
|
492
|
+
function findMdxFiles(patterns) {
|
|
493
|
+
const files = [];
|
|
494
|
+
for (const pattern of patterns) {
|
|
495
|
+
const resolved = path4.resolve(process.cwd(), pattern);
|
|
496
|
+
if (fs5.existsSync(resolved) && fs5.statSync(resolved).isDirectory()) {
|
|
497
|
+
walkDir(resolved, files);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (pattern.includes("*")) {
|
|
501
|
+
const base = pattern.split("*")[0];
|
|
502
|
+
const baseDir = path4.resolve(process.cwd(), base.endsWith("/") ? base : path4.dirname(base));
|
|
503
|
+
if (fs5.existsSync(baseDir)) {
|
|
504
|
+
walkDir(baseDir, files);
|
|
505
|
+
}
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
if (fs5.existsSync(resolved) && resolved.endsWith(".mdx")) {
|
|
509
|
+
files.push(resolved);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return [...new Set(files)];
|
|
513
|
+
}
|
|
514
|
+
function walkDir(dir, results) {
|
|
515
|
+
let entries;
|
|
516
|
+
try {
|
|
517
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
518
|
+
} catch {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
for (const entry of entries) {
|
|
522
|
+
const full = path4.join(dir, entry.name);
|
|
523
|
+
if (entry.isDirectory()) {
|
|
524
|
+
if (entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
525
|
+
walkDir(full, results);
|
|
526
|
+
}
|
|
527
|
+
} else if (entry.isFile() && entry.name.endsWith(".mdx")) {
|
|
528
|
+
results.push(full);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
function formatPretty(file, errors, cwd) {
|
|
533
|
+
const rel = path4.relative(cwd, file);
|
|
534
|
+
for (const err of errors) {
|
|
535
|
+
const loc = err.column > 0 ? `${err.line}:${err.column}` : `${err.line}`;
|
|
536
|
+
console.error(` ${rel}:${loc} error ${err.message}`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function formatGithub(errors) {
|
|
540
|
+
for (const err of errors) {
|
|
541
|
+
const col = err.column > 0 ? `,col=${err.column}` : "";
|
|
542
|
+
console.error(`::error file=${err.file},line=${err.line}${col}::${err.message}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function main() {
|
|
546
|
+
const options = parseArgs(process.argv);
|
|
547
|
+
const cwd = process.cwd();
|
|
548
|
+
if (options.patterns.length === 0) {
|
|
549
|
+
console.error("Usage: mdx-tsx-import-checker <path-or-glob> [--tsconfig <path>] [--format pretty|github|json] [--verbose]");
|
|
550
|
+
console.error("Example: mdx-tsx-import-checker ./src/content/docs");
|
|
551
|
+
process.exit(1);
|
|
552
|
+
}
|
|
553
|
+
const files = findMdxFiles(options.patterns);
|
|
554
|
+
if (files.length === 0) {
|
|
555
|
+
console.log("No MDX files found.");
|
|
556
|
+
process.exit(0);
|
|
557
|
+
}
|
|
558
|
+
if (options.verbose || options.format === "pretty") {
|
|
559
|
+
console.log(`Checking ${files.length} MDX file(s)...
|
|
560
|
+
`);
|
|
561
|
+
}
|
|
562
|
+
const logger = options.verbose ? (msg) => console.log(msg) : void 0;
|
|
563
|
+
let totalErrors = 0;
|
|
564
|
+
const allErrors = [];
|
|
565
|
+
for (const file of files) {
|
|
566
|
+
const errors = checkFile(file, {
|
|
567
|
+
tsconfigPath: options.tsconfigPath,
|
|
568
|
+
logger
|
|
569
|
+
});
|
|
570
|
+
totalErrors += errors.length;
|
|
571
|
+
if (options.format === "pretty" && errors.length > 0) {
|
|
572
|
+
formatPretty(file, errors, cwd);
|
|
573
|
+
} else if (options.format === "github") {
|
|
574
|
+
formatGithub(errors);
|
|
575
|
+
}
|
|
576
|
+
allErrors.push(...errors);
|
|
577
|
+
}
|
|
578
|
+
if (options.format === "json") {
|
|
579
|
+
console.log(JSON.stringify(allErrors, null, 2));
|
|
580
|
+
}
|
|
581
|
+
if (options.format === "pretty") {
|
|
582
|
+
console.log("");
|
|
583
|
+
if (totalErrors === 0) {
|
|
584
|
+
console.log(`\u2713 No import errors found in ${files.length} file(s).`);
|
|
585
|
+
} else {
|
|
586
|
+
console.error(`\u2717 Found ${totalErrors} error(s) in ${files.length} file(s).`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
process.exit(totalErrors > 0 ? 1 : 0);
|
|
590
|
+
}
|
|
591
|
+
main();
|
|
592
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli.ts", "../../core/src/index.ts", "../../core/src/parser.ts", "../../core/src/resolver.ts", "../../core/src/tsconfig.ts", "../../core/src/exports.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { checkFile, CheckError } from \"@mdx-tsx-import-checker/core\";\n\n// -----------------------------------------------------------------------\n// CLI entry point\n// Usage:\n// mdx-tsx-import-checker <path-or-glob> [options]\n//\n// Options:\n// --tsconfig <path> Path to tsconfig.json\n// --verbose Show debug logs\n// --format <fmt> Output format: pretty (default) | github | json\n// -----------------------------------------------------------------------\n\ninterface CliOptions {\n patterns: string[];\n tsconfigPath?: string;\n verbose: boolean;\n format: \"pretty\" | \"github\" | \"json\";\n}\n\nfunction parseArgs(argv: string[]): CliOptions {\n const args = argv.slice(2);\n const options: CliOptions = {\n patterns: [],\n verbose: false,\n format: \"pretty\",\n };\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--tsconfig\" && args[i + 1]) {\n options.tsconfigPath = path.resolve(process.cwd(), args[++i]);\n } else if (arg === \"--verbose\") {\n options.verbose = true;\n } else if (arg === \"--format\" && args[i + 1]) {\n const fmt = args[++i];\n if (fmt === \"pretty\" || fmt === \"github\" || fmt === \"json\") {\n options.format = fmt;\n } else {\n console.error(`Unknown format: ${fmt}. Use pretty, github, or json.`);\n process.exit(1);\n }\n } else if (!arg.startsWith(\"--\")) {\n options.patterns.push(arg);\n }\n }\n\n return options;\n}\n\nfunction findMdxFiles(patterns: string[]): string[] {\n const files: string[] = [];\n\n for (const pattern of patterns) {\n const resolved = path.resolve(process.cwd(), pattern);\n\n // If it's a directory, walk it recursively\n if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {\n walkDir(resolved, files);\n continue;\n }\n\n // If it's a glob-like pattern, do manual expansion\n if (pattern.includes(\"*\")) {\n const base = pattern.split(\"*\")[0];\n const baseDir = path.resolve(process.cwd(), base.endsWith(\"/\") ? base : path.dirname(base));\n if (fs.existsSync(baseDir)) {\n walkDir(baseDir, files);\n }\n continue;\n }\n\n // Direct file\n if (fs.existsSync(resolved) && resolved.endsWith(\".mdx\")) {\n files.push(resolved);\n }\n }\n\n return [...new Set(files)];\n}\n\nfunction walkDir(dir: string, results: string[]): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name !== \"node_modules\" && !entry.name.startsWith(\".\")) {\n walkDir(full, results);\n }\n } else if (entry.isFile() && entry.name.endsWith(\".mdx\")) {\n results.push(full);\n }\n }\n}\n\nfunction formatPretty(file: string, errors: CheckError[], cwd: string): void {\n const rel = path.relative(cwd, file);\n for (const err of errors) {\n const loc = err.column > 0 ? `${err.line}:${err.column}` : `${err.line}`;\n console.error(` ${rel}:${loc} error ${err.message}`);\n }\n}\n\nfunction formatGithub(errors: CheckError[]): void {\n for (const err of errors) {\n const col = err.column > 0 ? `,col=${err.column}` : \"\";\n console.error(`::error file=${err.file},line=${err.line}${col}::${err.message}`);\n }\n}\n\nfunction main(): void {\n const options = parseArgs(process.argv);\n const cwd = process.cwd();\n\n if (options.patterns.length === 0) {\n console.error(\"Usage: mdx-tsx-import-checker <path-or-glob> [--tsconfig <path>] [--format pretty|github|json] [--verbose]\");\n console.error(\"Example: mdx-tsx-import-checker ./src/content/docs\");\n process.exit(1);\n }\n\n const files = findMdxFiles(options.patterns);\n\n if (files.length === 0) {\n console.log(\"No MDX files found.\");\n process.exit(0);\n }\n\n if (options.verbose || options.format === \"pretty\") {\n console.log(`Checking ${files.length} MDX file(s)...\\n`);\n }\n\n const logger = options.verbose ? (msg: string) => console.log(msg) : undefined;\n\n let totalErrors = 0;\n const allErrors: CheckError[] = [];\n\n for (const file of files) {\n const errors = checkFile(file, {\n tsconfigPath: options.tsconfigPath,\n logger,\n });\n\n totalErrors += errors.length;\n\n if (options.format === \"pretty\" && errors.length > 0) {\n formatPretty(file, errors, cwd);\n } else if (options.format === \"github\") {\n formatGithub(errors);\n }\n\n allErrors.push(...errors);\n }\n\n if (options.format === \"json\") {\n console.log(JSON.stringify(allErrors, null, 2));\n }\n\n if (options.format === \"pretty\") {\n console.log(\"\");\n if (totalErrors === 0) {\n console.log(`\u2713 No import errors found in ${files.length} file(s).`);\n } else {\n console.error(`\u2717 Found ${totalErrors} error(s) in ${files.length} file(s).`);\n }\n }\n\n process.exit(totalErrors > 0 ? 1 : 0);\n}\n\nmain();\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { CheckError, CheckOptions } from \"./types\";\nimport { parseImports } from \"./parser\";\nimport { resolveModulePath, findProjectRoot } from \"./resolver\";\nimport { resolveAliases } from \"./tsconfig\";\nimport { getNamedExports } from \"./exports\";\n\nexport type { ImportStatement, CheckError, CheckOptions, PathAliases } from \"./types\";\nexport { parseImports } from \"./parser\";\nexport { resolveModulePath, resolveWithExtensions, findProjectRoot } from \"./resolver\";\nexport { resolveAliases } from \"./tsconfig\";\nexport { getNamedExports } from \"./exports\";\n\n/**\n * Check a single MDX file for import errors.\n * Returns an array of CheckError (empty array = no errors).\n */\nexport function checkFile(filePath: string, options: CheckOptions = {}): CheckError[] {\n const log = options.logger ?? (() => {});\n const errors: CheckError[] = [];\n\n let text: string;\n try {\n text = fs.readFileSync(filePath, \"utf-8\");\n } catch (e) {\n errors.push({ file: filePath, line: 0, column: 0, message: `Cannot read file: ${String(e)}` });\n return errors;\n }\n\n const imports = parseImports(text);\n log(` parsed ${imports.length} import(s) in ${filePath}`);\n if (imports.length === 0) return errors;\n\n const projectRoot = options.tsconfigPath\n ? path.dirname(options.tsconfigPath)\n : findProjectRoot(filePath);\n log(` projectRoot: ${projectRoot ?? \"(null)\"}`);\n\n const aliases = projectRoot\n ? resolveAliases(projectRoot, options.tsconfigPath, log)\n : {};\n log(` aliases: ${JSON.stringify(aliases)}`);\n\n const lines = text.split(\"\\n\");\n\n for (const imp of imports) {\n log(` checking: { ${imp.namedImports.join(\", \")} } from '${imp.moduleSpecifier}'`);\n\n const resolvedPath = resolveModulePath(imp.moduleSpecifier, filePath, aliases, projectRoot);\n log(` resolved: ${resolvedPath ?? \"(null \u2014 skipped)\"}`);\n\n if (resolvedPath === null) continue;\n\n if (!fs.existsSync(resolvedPath)) {\n errors.push({\n file: filePath,\n line: imp.line + 1,\n column: 0,\n message: `Cannot find module '${imp.moduleSpecifier}' (resolved to: ${resolvedPath})`,\n });\n continue;\n }\n\n const namedExports = getNamedExports(resolvedPath);\n if (namedExports === null) continue;\n log(` exports: ${[...namedExports].join(\", \")}`);\n\n for (const name of imp.namedImports) {\n if (!namedExports.has(name)) {\n const col = lines[imp.line]?.indexOf(name) ?? -1;\n const available = [...namedExports].filter((e) => e !== \"default\").sort().join(\", \");\n errors.push({\n file: filePath,\n line: imp.line + 1,\n column: col >= 0 ? col + 1 : 0,\n message:\n `'${name}' is not exported from '${imp.moduleSpecifier}'` +\n (available ? `. Available: ${available}` : \"\"),\n });\n }\n }\n }\n\n return errors;\n}\n", "import { ImportStatement } from \"./types\";\n\n/**\n * Extracts all named import statements from MDX text.\n *\n * Handles:\n * import { A, B } from 'mod'\n * import { A as B } from 'mod' <- original name A is checked\n * import Default, { A } from 'mod'\n * multi-line imports\n *\n * Skipped:\n * import Default from 'mod' <- no named imports\n *\n * Security: multi-line collection is capped at MAX_IMPORT_LINES to\n * prevent memory exhaustion from malformed or adversarial MDX files.\n */\n\n/** Maximum number of lines a single import statement may span. */\nconst MAX_IMPORT_LINES = 50;\n\nexport function parseImports(text: string): ImportStatement[] {\n const results: ImportStatement[] = [];\n const lines = text.split(\"\\n\");\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n if (!line.trimStart().startsWith(\"import \")) {\n i++;\n continue;\n }\n\n // Collect continuation lines until `from '...'` is complete,\n // but never beyond MAX_IMPORT_LINES to prevent memory exhaustion.\n let raw = line;\n let j = i;\n const limit = Math.min(i + MAX_IMPORT_LINES, lines.length - 1);\n\n while (!hasFromClause(raw) && j < limit) {\n j++;\n raw += \" \" + lines[j];\n }\n\n const parsed = parseSingleImport(raw, i);\n if (parsed) results.push(parsed);\n i = j + 1;\n }\n\n return results;\n}\n\n/**\n * Checks whether `raw` contains a complete `from '...'` clause.\n *\n * Uses a simple indexOf-based approach instead of a regex on a\n * potentially huge string to avoid ReDoS risk.\n */\nfunction hasFromClause(raw: string): boolean {\n const fromIdx = raw.indexOf(\" from \");\n if (fromIdx === -1) return false;\n const afterFrom = raw.slice(fromIdx + 6).trimStart();\n return afterFrom.startsWith(\"'\") || afterFrom.startsWith('\"');\n}\n\nfunction parseSingleImport(raw: string, line: number): ImportStatement | null {\n const fromMatch = raw.match(/from\\s+['\"]([^'\"]+)['\"]/);\n if (!fromMatch) return null;\n const moduleSpecifier = fromMatch[1];\n\n const braceMatch = raw.match(/\\{([^}]+)\\}/);\n if (!braceMatch) return null;\n\n const namedImports = braceMatch[1]\n .split(\",\")\n .map((s) => {\n // \"X as Y\" \u2192 we check the original export name X\n const m = s.match(/^\\s*(\\w+)(?:\\s+as\\s+\\w+)?\\s*$/);\n return m ? m[1] : s.trim();\n })\n .filter(Boolean);\n\n if (namedImports.length === 0) return null;\n return { namedImports, moduleSpecifier, line, raw };\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { PathAliases } from \"./types\";\n\nconst EXTENSIONS = [\n \".ts\", \".tsx\", \".d.ts\", \".js\", \".jsx\", \".mjs\", \".cjs\",\n \"/index.ts\", \"/index.tsx\", \"/index.d.ts\", \"/index.js\", \"/index.mjs\",\n];\n\n/**\n * Resolves a module specifier to an absolute file path.\n *\n * Resolution order:\n * 1. Relative import (`./foo`, `../bar`)\n * 2. Path alias (`@/components/foo` via tsconfig paths)\n * 3. node_modules (`react`, `@astrojs/starlight/components`)\n *\n * Returns null if the specifier cannot be resolved to a local file\n * (e.g. a bare package name with no node_modules found).\n *\n * Security: resolved paths are always validated to remain within\n * the project root to prevent path traversal attacks.\n */\nexport function resolveModulePath(\n specifier: string,\n fromFile: string,\n aliases: PathAliases,\n projectRoot: string | null\n): string | null {\n let resolved: string | null = null;\n\n if (specifier.startsWith(\".\")) {\n resolved = resolveRelative(specifier, fromFile);\n } else {\n const aliasResolved = resolveAlias(specifier, aliases);\n if (aliasResolved !== null) {\n resolved = aliasResolved;\n } else {\n resolved = resolveFromNodeModules(specifier, fromFile, projectRoot);\n }\n }\n\n // Security: reject any path that escapes the project root\n if (resolved !== null && projectRoot !== null) {\n if (!isWithinAllowedRoots(resolved, fromFile, projectRoot)) {\n return null;\n }\n }\n\n return resolved;\n}\n\n/**\n * Returns true if `resolved` is within the project root.\n *\n * Uses fs.realpathSync() on both sides so that pnpm's symlinked\n * virtual store (node_modules/.pnpm/<pkg>/node_modules/<pkg>/) is\n * correctly compared against the real project root path.\n *\n * npm/yarn: resolved path is already real \u2014 realpathSync is a no-op.\n * pnpm: resolved path is a symlink inside node_modules that points\n * to .pnpm/<pkg>@<ver>/node_modules/<pkg>/ \u2014 still under\n * the project root, so the prefix check holds after expansion.\n *\n * This prevents path traversal: e.g. `../../etc/passwd` resolving\n * outside the project is rejected.\n */\nfunction isWithinAllowedRoots(\n resolved: string,\n _fromFile: string,\n projectRoot: string\n): boolean {\n try {\n const realProject = fs.realpathSync(path.resolve(projectRoot)) + path.sep;\n const realResolved = fs.realpathSync(path.resolve(resolved));\n return realResolved.startsWith(realProject);\n } catch {\n // realpathSync throws if the path does not exist yet (e.g. extension\n // probing for a file that will never exist). Fall back to a plain\n // string check so legitimate missing-module errors are still reported\n // rather than silently swallowed.\n const absProject = path.resolve(projectRoot) + path.sep;\n const absResolved = path.resolve(resolved);\n return absResolved.startsWith(absProject);\n }\n}\n\nfunction resolveRelative(specifier: string, fromFile: string): string {\n const dir = path.dirname(fromFile);\n return resolveWithExtensions(path.resolve(dir, specifier));\n}\n\nfunction resolveAlias(specifier: string, aliases: PathAliases): string | null {\n for (const [alias, aliasRoot] of Object.entries(aliases)) {\n const prefix = alias.endsWith(\"/*\") ? alias.slice(0, -1) : alias + \"/\";\n const bare = alias.replace(\"/*\", \"\");\n if (specifier.startsWith(prefix) || specifier === bare) {\n const rest = specifier.slice(prefix.length);\n return resolveWithExtensions(path.join(aliasRoot, rest));\n }\n }\n return null;\n}\n\nfunction resolveFromNodeModules(\n specifier: string,\n fromFile: string,\n projectRoot: string | null\n): string | null {\n const nmRoots = collectNodeModuleRoots(fromFile, projectRoot);\n for (const nmRoot of nmRoots) {\n const result = tryResolvePackage(specifier, nmRoot);\n if (result) return result;\n }\n return null;\n}\n\n/**\n * Walk up the directory tree from `fromFile` collecting every\n * node_modules directory found, stopping at projectRoot (or fs root).\n *\n * Security: the loop now always terminates at projectRoot when provided,\n * limiting the search boundary and preventing unbounded traversal.\n */\nfunction collectNodeModuleRoots(fromFile: string, projectRoot: string | null): string[] {\n const roots: string[] = [];\n let dir = path.dirname(fromFile);\n // If projectRoot is known, stop there; otherwise stop at fs root.\n // Importantly, we do NOT walk past fs root under any circumstance.\n const stopAt = projectRoot ? path.resolve(projectRoot) : path.parse(dir).root;\n\n while (true) {\n const candidate = path.join(dir, \"node_modules\");\n if (fs.existsSync(candidate)) roots.push(candidate);\n if (path.resolve(dir) === stopAt || dir === path.parse(dir).root) break;\n dir = path.dirname(dir);\n }\n\n return roots;\n}\n\nfunction tryResolvePackage(specifier: string, nmRoot: string): string | null {\n const { pkgName, subPath } = splitSpecifier(specifier);\n const pkgDir = path.join(nmRoot, pkgName);\n if (!fs.existsSync(pkgDir)) return null;\n\n const pkgJson = readPackageJson(pkgDir);\n\n if (subPath) {\n return resolveSubPath(subPath, pkgDir, pkgJson);\n }\n\n return resolvePackageRoot(pkgDir, pkgJson);\n}\n\n/** Split `@scope/pkg/sub` \u2192 `{ pkgName: \"@scope/pkg\", subPath: \"sub\" }` */\nfunction splitSpecifier(specifier: string): { pkgName: string; subPath: string } {\n if (specifier.startsWith(\"@\")) {\n const parts = specifier.split(\"/\");\n return {\n pkgName: parts[0] + \"/\" + parts[1],\n subPath: parts.slice(2).join(\"/\"),\n };\n }\n const slashIdx = specifier.indexOf(\"/\");\n return {\n pkgName: slashIdx === -1 ? specifier : specifier.slice(0, slashIdx),\n subPath: slashIdx === -1 ? \"\" : specifier.slice(slashIdx + 1),\n };\n}\n\nfunction readPackageJson(pkgDir: string): Record<string, unknown> {\n try {\n return JSON.parse(\n fs.readFileSync(path.join(pkgDir, \"package.json\"), \"utf-8\")\n ) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction resolveSubPath(\n subPath: string,\n pkgDir: string,\n pkgJson: Record<string, unknown>\n): string | null {\n // 1. Try package.json \"exports\" map\n const exportsField = pkgJson[\"exports\"] as Record<string, unknown> | undefined;\n if (exportsField) {\n const resolved = resolveExportsField(exportsField, \"./\" + subPath, pkgDir);\n if (resolved) return resolved;\n }\n // 2. Fall back to direct file path\n return resolveWithExtensions(path.join(pkgDir, subPath));\n}\n\nfunction resolvePackageRoot(\n pkgDir: string,\n pkgJson: Record<string, unknown>\n): string | null {\n // 1. \"types\" / \"typings\"\n for (const field of [\"types\", \"typings\"]) {\n const entry = pkgJson[field] as string | undefined;\n if (entry) {\n const candidate = path.resolve(pkgDir, entry);\n if (fs.existsSync(candidate)) return candidate;\n }\n }\n // 2. exports[\".\"]\n const exportsField = pkgJson[\"exports\"] as Record<string, unknown> | undefined;\n if (exportsField) {\n const resolved = resolveExportsField(exportsField, \".\", pkgDir);\n if (resolved) return resolved;\n }\n // 3. \"main\"\n const main = pkgJson[\"main\"] as string | undefined;\n if (main) {\n const candidate = path.resolve(pkgDir, main);\n if (fs.existsSync(candidate)) return candidate;\n }\n // 4. index file\n return resolveWithExtensions(path.join(pkgDir, \"index\"));\n}\n\n/**\n * Resolve a key from a package.json `exports` field.\n * Condition priority: types > import > require > default\n */\nfunction resolveExportsField(\n exportsField: Record<string, unknown>,\n key: string,\n pkgDir: string\n): string | null {\n const entry = exportsField[key];\n if (!entry) return null;\n\n for (const candidate of extractExportCandidates(entry)) {\n if (typeof candidate !== \"string\") continue;\n const resolved = path.resolve(pkgDir, candidate);\n if (fs.existsSync(resolved)) return resolved;\n const withExt = resolveWithExtensions(resolved.replace(/\\.[^.]+$/, \"\"));\n if (fs.existsSync(withExt)) return withExt;\n }\n return null;\n}\n\nfunction extractExportCandidates(entry: unknown): string[] {\n if (typeof entry === \"string\") return [entry];\n if (typeof entry !== \"object\" || entry === null) return [];\n\n const obj = entry as Record<string, unknown>;\n const results: string[] = [];\n for (const cond of [\"types\", \"import\", \"require\", \"default\"]) {\n if (cond in obj) results.push(...extractExportCandidates(obj[cond]));\n }\n for (const [k, v] of Object.entries(obj)) {\n if (![\"types\", \"import\", \"require\", \"default\"].includes(k)) {\n results.push(...extractExportCandidates(v));\n }\n }\n return results;\n}\n\nexport function resolveWithExtensions(base: string): string {\n if (path.extname(base) && fs.existsSync(base)) return base;\n for (const ext of EXTENSIONS) {\n const candidate = base + ext;\n if (fs.existsSync(candidate)) return candidate;\n }\n return base;\n}\n\n/**\n * Walk up from `fromFile` looking for tsconfig.json / package.json / .git\n * to determine the project root.\n */\nexport function findProjectRoot(fromFile: string): string | null {\n let dir = path.dirname(fromFile);\n const root = path.parse(dir).root;\n\n while (dir !== root) {\n for (const marker of [\"tsconfig.json\", \"package.json\", \".git\"]) {\n if (fs.existsSync(path.join(dir, marker))) return dir;\n }\n dir = path.dirname(dir);\n }\n return null;\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { PathAliases, TsConfig, TsConfigCompilerOptions } from \"./types\";\n\n/**\n * Reads tsconfig.json and extracts path aliases as absolute paths.\n *\n * Example tsconfig:\n * { \"compilerOptions\": { \"paths\": { \"@/*\": [\"./src/*\"] }, \"baseUrl\": \".\" } }\n *\n * Result:\n * { \"@/*\": \"/abs/path/to/src\" }\n */\nexport function resolveAliases(\n projectRoot: string,\n tsconfigPath: string | undefined,\n log: (msg: string) => void = () => {}\n): PathAliases {\n const aliases: PathAliases = {};\n const tsconfig = tsconfigPath ?? findTsconfig(projectRoot);\n if (!tsconfig || !fs.existsSync(tsconfig)) return aliases;\n\n try {\n const raw = fs.readFileSync(tsconfig, \"utf-8\");\n const parsed = parseJsonWithComments(raw);\n if (!parsed) return aliases;\n\n const compilerOptions = (parsed.compilerOptions ?? {}) as TsConfigCompilerOptions;\n const paths = compilerOptions.paths ?? {};\n const baseUrl = compilerOptions.baseUrl ?? \".\";\n const absoluteBase = path.resolve(path.dirname(tsconfig), baseUrl);\n\n log(` tsconfig paths: ${JSON.stringify(paths)}, baseUrl: ${baseUrl}`);\n\n for (const [alias, targets] of Object.entries(paths)) {\n if (targets.length > 0) {\n // \"./src/*\" \u2192 \"./src\" (strip trailing /*)\n const target = targets[0].endsWith(\"/*\") ? targets[0].slice(0, -2) : targets[0];\n aliases[alias] = path.resolve(absoluteBase, target);\n }\n }\n } catch (e) {\n log(` tsconfig error: ${String(e)}`);\n }\n\n return aliases;\n}\n\n/**\n * Search for tsconfig.json starting from projectRoot.\n * Also checks common monorepo locations.\n */\nfunction findTsconfig(projectRoot: string): string | null {\n const candidates = [\n \"tsconfig.json\",\n \"packages/docs/tsconfig.json\",\n \"packages/web/tsconfig.json\",\n ];\n for (const c of candidates) {\n const full = path.join(projectRoot, c);\n if (fs.existsSync(full)) return full;\n }\n return null;\n}\n\n/**\n * Parse JSON that may contain comments and trailing commas (tsconfig format).\n *\n * Handles:\n * // single-line comments\n * block comments (slash-star ... star-slash)\n * trailing commas { \"a\": 1, }\n *\n * Security: uses a character-level state machine so that comment\n * sequences inside string literals (e.g. \"https://example.com\") are\n * never mistakenly stripped. The previous regex-only approach would\n * erase `//` inside strings, corrupting string values.\n */\nfunction parseJsonWithComments(raw: string): TsConfig | null {\n try {\n const cleaned = stripJsonComments(raw);\n // Remove trailing commas before ] or }\n const noTrailing = cleaned.replace(/,(\\s*[}\\]])/g, \"$1\");\n return JSON.parse(noTrailing) as TsConfig;\n } catch {\n return null;\n }\n}\n\n/**\n * Strip `//` and block comments from a JSON-with-comments string,\n * correctly skipping content inside double-quoted string literals.\n *\n * State machine states:\n * \"code\" \u2014 normal JSON tokens\n * \"string\" \u2014 inside a \"...\" value\n * \"lineComment\" \u2014 after //\n * \"blockComment\" \u2014 inside slash-star ... star-slash\n */\nfunction stripJsonComments(input: string): string {\n type State = \"code\" | \"string\" | \"lineComment\" | \"blockComment\";\n let state: State = \"code\";\n let out = \"\";\n let i = 0;\n\n while (i < input.length) {\n const ch = input[i];\n const next = input[i + 1];\n\n switch (state) {\n case \"code\":\n if (ch === '\"') {\n state = \"string\";\n out += ch;\n } else if (ch === \"/\" && next === \"/\") {\n state = \"lineComment\";\n i += 2;\n continue;\n } else if (ch === \"/\" && next === \"*\") {\n state = \"blockComment\";\n i += 2;\n continue;\n } else {\n out += ch;\n }\n break;\n\n case \"string\":\n out += ch;\n if (ch === \"\\\\\" && next !== undefined) {\n // Consume escaped character so \\\" doesn't end the string\n out += next;\n i += 2;\n continue;\n }\n if (ch === '\"') {\n state = \"code\";\n }\n break;\n\n case \"lineComment\":\n if (ch === \"\\n\") {\n // Preserve newline so line numbers stay accurate\n out += \"\\n\";\n state = \"code\";\n }\n break;\n\n case \"blockComment\":\n if (ch === \"*\" && next === \"/\") {\n state = \"code\";\n i += 2;\n continue;\n }\n // Preserve newlines inside block comments for line accuracy\n if (ch === \"\\n\") out += \"\\n\";\n break;\n }\n\n i++;\n }\n\n return out;\n}\n", "import * as fs from \"fs\";\n\n/**\n * Extracts named exports from a TypeScript/JavaScript file using regex-based\n * static analysis. Covers the most common export patterns:\n *\n * export function Foo\n * export const Foo / export let Foo / export var Foo\n * export class Foo\n * export type Foo / export interface Foo / export enum Foo\n * export async function Foo\n * export { Foo, Bar }\n * export { Foo as Bar } \u2190 Bar is the exported name\n * export default \u2190 registered as \"default\"\n *\n * Returns null if the file cannot be read.\n */\nexport function getNamedExports(filePath: string): Set<string> | null {\n let source: string;\n try {\n source = fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n const namedExports = new Set<string>();\n\n collectDeclarationExports(source, namedExports);\n collectBraceExports(source, namedExports);\n\n if (/export\\s+default\\b/.test(source)) {\n namedExports.add(\"default\");\n }\n\n return namedExports;\n}\n\n/** Matches: export [async] function|const|let|var|class|type|interface|enum Name */\nfunction collectDeclarationExports(source: string, result: Set<string>): void {\n const pattern =\n /export\\s+(?:async\\s+)?(?:function|const|let|var|class|type|interface|enum)\\s+(\\w+)/g;\n for (const m of source.matchAll(pattern)) {\n result.add(m[1]);\n }\n}\n\n/** Matches: export { Foo, Bar as Baz } */\nfunction collectBraceExports(source: string, result: Set<string>): void {\n const pattern = /export\\s*\\{([^}]+)\\}/g;\n for (const m of source.matchAll(pattern)) {\n for (const entry of m[1].split(\",\")) {\n const asMatch = entry.match(/\\w+\\s+as\\s+(\\w+)/);\n if (asMatch) {\n result.add(asMatch[1]);\n } else {\n const name = entry.trim();\n if (name) result.add(name);\n }\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["fs", "path", "fs", "path", "main", "fs", "path", "fs"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mdx-tsx-import-checker",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool to validate named imports of React/TSX components in MDX files",
|
|
5
|
+
"main": "./dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mdx-tsx-import-checker": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": ["dist"],
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18.0.0"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "npm run typecheck && node build.mjs",
|
|
15
|
+
"typecheck": "tsc -p ./tsconfig.json --noEmit",
|
|
16
|
+
"watch": "node build.mjs --watch"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@mdx-tsx-import-checker/core": "*",
|
|
20
|
+
"@types/node": "^20.0.0",
|
|
21
|
+
"esbuild": "^0.25.0",
|
|
22
|
+
"typescript": "^5.0.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": ["mdx", "tsx", "react", "import", "checker", "lint", "cli"],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/ToruTamahashi/mdx-tsx-import-checker.git"
|
|
29
|
+
}
|
|
30
|
+
}
|