docs-drift-lite 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tejas Kadam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # docs-drift-lite
2
+
3
+ Detects drift between documented API names (e.g. from a README) and the actual exported names of a module — catches stale docs in CI.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install docs-drift-lite
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { checkReadmeDrift, hasUndocumentedExports } from 'docs-drift-lite';
15
+ import { readFileSync } from 'fs';
16
+ import * as pkg from './src/index';
17
+
18
+ const readme = readFileSync('README.md', 'utf8');
19
+ const report = checkReadmeDrift(readme, Object.keys(pkg));
20
+
21
+ console.log(report);
22
+ if (hasUndocumentedExports(report)) process.exit(1);
23
+ ```
24
+
25
+ ## Why docs-drift-lite
26
+
27
+ READMEs go stale the moment someone adds or renames an export without updating the docs — and nobody notices until a user hits a wall. docs-drift-lite pulls a rough set of "documented names" out of Markdown (inline code spans and call-expression heads in code blocks) and diffs them against your module's real export list, reporting names that are documented but no longer exist, exports that are undocumented, and everything that matches. It's intentionally a text heuristic rather than a full doc-comment parser, so it works on any README without extra tooling.
28
+
29
+ ## API
30
+
31
+ - `extractDocumentedNames(markdown)` — pulls candidate identifier names out of Markdown text.
32
+ - `detectDrift(documentedNames, actualExportNames, ignore?)` — compares two name lists into `{ matched, missingFromDocs, undocumentedInCode }`.
33
+ - `checkReadmeDrift(markdown, actualExportNames, ignore?)` — extraction + comparison in one call.
34
+ - `hasUndocumentedExports(report)` — true if any export isn't mentioned in the docs (a common CI gate).
35
+
36
+ ## License
37
+
38
+ MIT
@@ -0,0 +1,25 @@
1
+ interface DriftReport {
2
+ /** Names documented but not found among the actual exports. */
3
+ undocumentedInCode: string[];
4
+ /** Names actually exported but never mentioned in the docs. */
5
+ missingFromDocs: string[];
6
+ /** Names present in both. */
7
+ matched: string[];
8
+ }
9
+ /**
10
+ * Extracts a plausible set of "documented API names" from Markdown text: bare identifiers
11
+ * inside inline code spans (`likeThis`) and the head of fenced code-block call expressions
12
+ * (`likeThis(...)`), each optionally namespaced with a leading module dot.
13
+ */
14
+ declare function extractDocumentedNames(markdown: string): string[];
15
+ /**
16
+ * Compares a list of documented names against a list of actual export names and reports
17
+ * what's undocumented, what's missing from docs, and what matches.
18
+ */
19
+ declare function detectDrift(documentedNames: string[], actualExportNames: string[], ignore?: string[]): DriftReport;
20
+ /** Convenience: runs extractDocumentedNames + detectDrift in one call. */
21
+ declare function checkReadmeDrift(markdown: string, actualExportNames: string[], ignore?: string[]): DriftReport;
22
+ /** True if the report has any names missing from docs (a common CI gate; ignores extra doc mentions). */
23
+ declare function hasUndocumentedExports(report: DriftReport): boolean;
24
+
25
+ export { type DriftReport, checkReadmeDrift, detectDrift, extractDocumentedNames, hasUndocumentedExports };
@@ -0,0 +1,25 @@
1
+ interface DriftReport {
2
+ /** Names documented but not found among the actual exports. */
3
+ undocumentedInCode: string[];
4
+ /** Names actually exported but never mentioned in the docs. */
5
+ missingFromDocs: string[];
6
+ /** Names present in both. */
7
+ matched: string[];
8
+ }
9
+ /**
10
+ * Extracts a plausible set of "documented API names" from Markdown text: bare identifiers
11
+ * inside inline code spans (`likeThis`) and the head of fenced code-block call expressions
12
+ * (`likeThis(...)`), each optionally namespaced with a leading module dot.
13
+ */
14
+ declare function extractDocumentedNames(markdown: string): string[];
15
+ /**
16
+ * Compares a list of documented names against a list of actual export names and reports
17
+ * what's undocumented, what's missing from docs, and what matches.
18
+ */
19
+ declare function detectDrift(documentedNames: string[], actualExportNames: string[], ignore?: string[]): DriftReport;
20
+ /** Convenience: runs extractDocumentedNames + detectDrift in one call. */
21
+ declare function checkReadmeDrift(markdown: string, actualExportNames: string[], ignore?: string[]): DriftReport;
22
+ /** True if the report has any names missing from docs (a common CI gate; ignores extra doc mentions). */
23
+ declare function hasUndocumentedExports(report: DriftReport): boolean;
24
+
25
+ export { type DriftReport, checkReadmeDrift, detectDrift, extractDocumentedNames, hasUndocumentedExports };
package/dist/index.js ADDED
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ checkReadmeDrift: () => checkReadmeDrift,
24
+ detectDrift: () => detectDrift,
25
+ extractDocumentedNames: () => extractDocumentedNames,
26
+ hasUndocumentedExports: () => hasUndocumentedExports
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+ function extractDocumentedNames(markdown) {
30
+ const names = /* @__PURE__ */ new Set();
31
+ const inlineCodeMatches = markdown.match(/`([^`]+)`/g) ?? [];
32
+ for (const raw of inlineCodeMatches) {
33
+ const content = raw.slice(1, -1).trim();
34
+ const identMatch = content.match(/^[A-Za-z_$][A-Za-z0-9_$]*/);
35
+ if (identMatch && identMatch[0] === content.replace(/\(.*$/, "")) {
36
+ names.add(identMatch[0]);
37
+ }
38
+ }
39
+ const callMatches = markdown.matchAll(/\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/g);
40
+ for (const match of callMatches) {
41
+ names.add(match[1]);
42
+ }
43
+ return [...names];
44
+ }
45
+ function detectDrift(documentedNames, actualExportNames, ignore = []) {
46
+ const ignoreSet = new Set(ignore);
47
+ const docSet = new Set(documentedNames.filter((n) => !ignoreSet.has(n)));
48
+ const exportSet = new Set(actualExportNames.filter((n) => !ignoreSet.has(n)));
49
+ const matched = [];
50
+ const missingFromDocs = [];
51
+ for (const name of exportSet) {
52
+ if (docSet.has(name)) matched.push(name);
53
+ else missingFromDocs.push(name);
54
+ }
55
+ const undocumentedInCode = [...docSet].filter((name) => !exportSet.has(name));
56
+ return {
57
+ undocumentedInCode: undocumentedInCode.sort(),
58
+ missingFromDocs: missingFromDocs.sort(),
59
+ matched: matched.sort()
60
+ };
61
+ }
62
+ function checkReadmeDrift(markdown, actualExportNames, ignore = []) {
63
+ return detectDrift(extractDocumentedNames(markdown), actualExportNames, ignore);
64
+ }
65
+ function hasUndocumentedExports(report) {
66
+ return report.missingFromDocs.length > 0;
67
+ }
68
+ // Annotate the CommonJS export names for ESM import in node:
69
+ 0 && (module.exports = {
70
+ checkReadmeDrift,
71
+ detectDrift,
72
+ extractDocumentedNames,
73
+ hasUndocumentedExports
74
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,46 @@
1
+ // src/index.ts
2
+ function extractDocumentedNames(markdown) {
3
+ const names = /* @__PURE__ */ new Set();
4
+ const inlineCodeMatches = markdown.match(/`([^`]+)`/g) ?? [];
5
+ for (const raw of inlineCodeMatches) {
6
+ const content = raw.slice(1, -1).trim();
7
+ const identMatch = content.match(/^[A-Za-z_$][A-Za-z0-9_$]*/);
8
+ if (identMatch && identMatch[0] === content.replace(/\(.*$/, "")) {
9
+ names.add(identMatch[0]);
10
+ }
11
+ }
12
+ const callMatches = markdown.matchAll(/\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/g);
13
+ for (const match of callMatches) {
14
+ names.add(match[1]);
15
+ }
16
+ return [...names];
17
+ }
18
+ function detectDrift(documentedNames, actualExportNames, ignore = []) {
19
+ const ignoreSet = new Set(ignore);
20
+ const docSet = new Set(documentedNames.filter((n) => !ignoreSet.has(n)));
21
+ const exportSet = new Set(actualExportNames.filter((n) => !ignoreSet.has(n)));
22
+ const matched = [];
23
+ const missingFromDocs = [];
24
+ for (const name of exportSet) {
25
+ if (docSet.has(name)) matched.push(name);
26
+ else missingFromDocs.push(name);
27
+ }
28
+ const undocumentedInCode = [...docSet].filter((name) => !exportSet.has(name));
29
+ return {
30
+ undocumentedInCode: undocumentedInCode.sort(),
31
+ missingFromDocs: missingFromDocs.sort(),
32
+ matched: matched.sort()
33
+ };
34
+ }
35
+ function checkReadmeDrift(markdown, actualExportNames, ignore = []) {
36
+ return detectDrift(extractDocumentedNames(markdown), actualExportNames, ignore);
37
+ }
38
+ function hasUndocumentedExports(report) {
39
+ return report.missingFromDocs.length > 0;
40
+ }
41
+ export {
42
+ checkReadmeDrift,
43
+ detectDrift,
44
+ extractDocumentedNames,
45
+ hasUndocumentedExports
46
+ };
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "docs-drift-lite",
3
+ "version": "0.1.0",
4
+ "description": "Detects drift between documented API names (e.g. from a README) and the actual exported names of a module — catches stale docs in CI.",
5
+ "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": ["dist"],
6
+ "license": "MIT", "author": "Tejas Kadam",
7
+ "repository": { "type": "git", "url": "git+https://github.com/tejas821/docs-drift-lite.git" },
8
+ "keywords": ["documentation", "drift", "ci", "typescript"],
9
+ "scripts": { "build": "tsup src/index.ts --format cjs,esm --dts", "test": "jest", "typecheck": "tsc --noEmit" },
10
+ "devDependencies": { "typescript": "^5.5.4", "tsup": "^8.2.4", "jest": "^29.7.0", "ts-jest": "^29.2.5", "@types/jest": "^29.5.12" }
11
+ }