@doccident/doccident 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 +315 -123
- package/bin/cmd.js +25 -24
- package/dist/doctest.js +89 -102
- package/dist/languages/c.js +58 -0
- package/dist/languages/cobol.js +50 -0
- package/dist/languages/fortran.js +56 -0
- package/dist/languages/go.js +45 -0
- package/dist/languages/interface.js +2 -0
- package/dist/languages/javascript.js +24 -0
- package/dist/languages/python.js +31 -0
- package/dist/languages/rust.js +56 -0
- package/dist/languages/shell.js +37 -0
- package/dist/parse-code-snippets-from-markdown.js +53 -30
- package/dist/reporter.js +78 -0
- package/dist/types.js +2 -0
- package/dist/utils.js +6 -0
- package/package.json +12 -18
|
@@ -1,35 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
function startNewSnippet(snippets, fileName, lineNumber) {
|
|
12
|
-
var skip = snippets.skip;
|
|
3
|
+
// Capture indentation (group 1) and language (group 2)
|
|
4
|
+
const START_REGEX = /^(\s*)```\W*(JavaScript|js|es6|ts|typescript|python|py|bash|sh|zsh|shell|go|rust|rs|fortran|f90|f95|cobol|cob|c)\s?$/i;
|
|
5
|
+
const isStartOfSnippet = (line) => line.match(START_REGEX);
|
|
6
|
+
const isEndOfSnippet = (line) => line.trim() === "```";
|
|
7
|
+
const isSkip = (line) => line.trim() === "<!-- skip-example -->";
|
|
8
|
+
const isCodeSharedInFile = (line) => line.trim() === "<!-- share-code-between-examples -->";
|
|
9
|
+
function startNewSnippet(snippets, fileName, lineNumber, language, indentation) {
|
|
10
|
+
const skip = snippets.skip;
|
|
13
11
|
snippets.skip = false;
|
|
12
|
+
let normalizedLang = 'javascript';
|
|
13
|
+
const langLower = language.toLowerCase();
|
|
14
|
+
if (['python', 'py'].includes(langLower)) {
|
|
15
|
+
normalizedLang = 'python';
|
|
16
|
+
}
|
|
17
|
+
else if (['bash', 'sh', 'zsh', 'shell'].includes(langLower)) {
|
|
18
|
+
normalizedLang = langLower === 'shell' ? 'bash' : langLower;
|
|
19
|
+
}
|
|
20
|
+
else if (langLower === 'go') {
|
|
21
|
+
normalizedLang = 'go';
|
|
22
|
+
}
|
|
23
|
+
else if (['rust', 'rs'].includes(langLower)) {
|
|
24
|
+
normalizedLang = 'rust';
|
|
25
|
+
}
|
|
26
|
+
else if (['fortran', 'f90', 'f95'].includes(langLower)) {
|
|
27
|
+
normalizedLang = 'fortran';
|
|
28
|
+
}
|
|
29
|
+
else if (['cobol', 'cob'].includes(langLower)) {
|
|
30
|
+
normalizedLang = 'cobol';
|
|
31
|
+
}
|
|
32
|
+
else if (langLower === 'c') {
|
|
33
|
+
normalizedLang = 'c';
|
|
34
|
+
}
|
|
14
35
|
return Object.assign(snippets, {
|
|
15
36
|
snippets: snippets.snippets.concat([
|
|
16
|
-
{ code: "",
|
|
37
|
+
{ code: "", language: normalizedLang, fileName, lineNumber, complete: false, skip: skip ?? false, indentation }
|
|
17
38
|
])
|
|
18
39
|
});
|
|
19
40
|
}
|
|
20
41
|
function addLineToLastSnippet(line) {
|
|
21
42
|
return function addLine(snippets) {
|
|
22
|
-
|
|
43
|
+
const lastSnippet = snippets.snippets[snippets.snippets.length - 1];
|
|
23
44
|
if (lastSnippet && !lastSnippet.complete) {
|
|
24
|
-
|
|
45
|
+
let lineToAdd = line;
|
|
46
|
+
if (lastSnippet.indentation && line.startsWith(lastSnippet.indentation)) {
|
|
47
|
+
lineToAdd = line.slice(lastSnippet.indentation.length);
|
|
48
|
+
}
|
|
49
|
+
lastSnippet.code += lineToAdd + "\n";
|
|
25
50
|
}
|
|
26
51
|
return snippets;
|
|
27
52
|
};
|
|
28
53
|
}
|
|
29
|
-
function endSnippet(snippets,
|
|
30
|
-
|
|
31
|
-
console.log("endSnippet", snippets, fileName, lineNumber);
|
|
32
|
-
var lastSnippet = snippets.snippets[snippets.snippets.length - 1];
|
|
54
|
+
function endSnippet(snippets, _fileName, _lineNumber) {
|
|
55
|
+
const lastSnippet = snippets.snippets[snippets.snippets.length - 1];
|
|
33
56
|
if (lastSnippet) {
|
|
34
57
|
lastSnippet.complete = true;
|
|
35
58
|
}
|
|
@@ -44,8 +67,10 @@ function shareCodeInFile(snippets) {
|
|
|
44
67
|
return snippets;
|
|
45
68
|
}
|
|
46
69
|
function parseLine(line) {
|
|
47
|
-
|
|
48
|
-
|
|
70
|
+
const startMatch = isStartOfSnippet(line);
|
|
71
|
+
if (startMatch) {
|
|
72
|
+
// startMatch[1] is indentation, startMatch[2] is language
|
|
73
|
+
return (snippets, fileName, lineNumber) => startNewSnippet(snippets, fileName, lineNumber, startMatch[2], startMatch[1]);
|
|
49
74
|
}
|
|
50
75
|
if (isEndOfSnippet(line)) {
|
|
51
76
|
return endSnippet;
|
|
@@ -59,27 +84,25 @@ function parseLine(line) {
|
|
|
59
84
|
return addLineToLastSnippet(line);
|
|
60
85
|
}
|
|
61
86
|
function parseCodeSnippets(args) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
const contents = args.contents;
|
|
88
|
+
const fileName = args.fileName;
|
|
89
|
+
const initialState = {
|
|
65
90
|
snippets: [],
|
|
66
91
|
shareCodeInFile: false,
|
|
67
92
|
complete: false
|
|
68
93
|
};
|
|
69
|
-
|
|
94
|
+
const results = contents
|
|
70
95
|
.split("\n")
|
|
71
96
|
.map(parseLine)
|
|
72
|
-
.reduce(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
var codeSnippets = results.snippets;
|
|
76
|
-
var lastSnippet = codeSnippets[codeSnippets.length - 1];
|
|
97
|
+
.reduce((snippets, lineAction, index) => lineAction(snippets, fileName, index + 1), initialState);
|
|
98
|
+
const codeSnippets = results.snippets;
|
|
99
|
+
const lastSnippet = codeSnippets[codeSnippets.length - 1];
|
|
77
100
|
if (lastSnippet && !lastSnippet.complete) {
|
|
78
101
|
throw new Error("Snippet parsing was incomplete");
|
|
79
102
|
}
|
|
80
103
|
return {
|
|
81
|
-
fileName
|
|
82
|
-
codeSnippets
|
|
104
|
+
fileName,
|
|
105
|
+
codeSnippets,
|
|
83
106
|
shareCodeInFile: results.shareCodeInFile
|
|
84
107
|
};
|
|
85
108
|
}
|
package/dist/reporter.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.printResults = printResults;
|
|
7
|
+
/* eslint-disable no-console */
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
function printResults(results) {
|
|
10
|
+
results.filter((result) => result.status === "fail").forEach(printFailure);
|
|
11
|
+
const passingCount = results.filter((result) => result.status === "pass")
|
|
12
|
+
.length;
|
|
13
|
+
const failingCount = results.filter((result) => result.status === "fail")
|
|
14
|
+
.length;
|
|
15
|
+
const skippingCount = results.filter((result) => result.status === "skip")
|
|
16
|
+
.length;
|
|
17
|
+
function successfulRun() {
|
|
18
|
+
return failingCount === 0;
|
|
19
|
+
}
|
|
20
|
+
console.log(chalk_1.default.green("Passed: " + passingCount));
|
|
21
|
+
if (skippingCount > 0) {
|
|
22
|
+
console.log(chalk_1.default.yellow("Skipped: " + skippingCount));
|
|
23
|
+
}
|
|
24
|
+
if (successfulRun()) {
|
|
25
|
+
console.log(chalk_1.default.green("\nSuccess!"));
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.log(chalk_1.default.red("Failed: " + failingCount));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function printFailure(result) {
|
|
32
|
+
console.log(chalk_1.default.red(`Failed - ${markDownErrorLocation(result)}`));
|
|
33
|
+
const stackDetails = relevantStackDetails(result.stack);
|
|
34
|
+
console.log(stackDetails);
|
|
35
|
+
const variableNotDefined = stackDetails.match(/(\w{1,256}) is not defined/);
|
|
36
|
+
if (variableNotDefined) {
|
|
37
|
+
const variableName = variableNotDefined[1];
|
|
38
|
+
console.log(`You can declare ${chalk_1.default.blue(variableName)} in the ${chalk_1.default.blue("globals")} section in ${chalk_1.default.grey(".doccident-setup.js")}`);
|
|
39
|
+
console.log(`
|
|
40
|
+
For example:
|
|
41
|
+
${chalk_1.default.grey("// .doccident-setup.js")}
|
|
42
|
+
module.exports = {
|
|
43
|
+
globals: {
|
|
44
|
+
${chalk_1.default.blue(variableName)}: ...
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function relevantStackDetails(stack) {
|
|
51
|
+
const evalIndex = stack.indexOf("at eval");
|
|
52
|
+
if (evalIndex !== -1) {
|
|
53
|
+
return stack.substring(0, evalIndex);
|
|
54
|
+
}
|
|
55
|
+
const lines = stack.split("\n");
|
|
56
|
+
for (let i = 0; i < lines.length; i++) {
|
|
57
|
+
const line = lines[i];
|
|
58
|
+
if (line.includes("doctest.js") && line.trim().startsWith("at ")) {
|
|
59
|
+
return lines.slice(0, i).join("\n") + "\n";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return stack;
|
|
63
|
+
}
|
|
64
|
+
function markDownErrorLocation(result) {
|
|
65
|
+
const lines = result.stack.split("\n");
|
|
66
|
+
for (const line of lines) {
|
|
67
|
+
if (line.includes("eval")) {
|
|
68
|
+
const match = line.match(/<([^><]+)>:(\d+):(\d+)/);
|
|
69
|
+
if (match) {
|
|
70
|
+
const mdLineNumber = parseInt(match[2], 10);
|
|
71
|
+
const columnNumber = parseInt(match[3], 10);
|
|
72
|
+
const lineNumber = result.codeSnippet.lineNumber + mdLineNumber;
|
|
73
|
+
return `${result.codeSnippet.fileName}:${lineNumber}:${columnNumber}`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return `${result.codeSnippet.fileName}:${result.codeSnippet.lineNumber}`;
|
|
78
|
+
}
|
package/dist/types.js
ADDED
package/dist/utils.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doccident/doccident",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Test all the code in your markdown docs!",
|
|
5
5
|
"main": "dist/doctest.js",
|
|
6
6
|
"files": [
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc",
|
|
15
|
-
"lint": "eslint .
|
|
16
|
-
"test": "
|
|
17
|
-
"
|
|
15
|
+
"lint": "eslint .",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:readme": "node bin/cmd.js README.md",
|
|
18
|
+
"precommit": "npm run lint && npm run test && npm run test:readme",
|
|
18
19
|
"clean": "rm -rf dist",
|
|
19
20
|
"prepublishOnly": "npm run clean && npm run build"
|
|
20
21
|
},
|
|
@@ -37,29 +38,22 @@
|
|
|
37
38
|
},
|
|
38
39
|
"homepage": "https://github.com/BillaudCipher/doccident",
|
|
39
40
|
"devDependencies": {
|
|
40
|
-
"@babel/preset-typescript": "^7.27.1",
|
|
41
41
|
"@eslint/eslintrc": "^3.3.1",
|
|
42
42
|
"@eslint/js": "^9.23.0",
|
|
43
43
|
"@types/node": "^22.15.17",
|
|
44
44
|
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
|
45
45
|
"@typescript-eslint/parser": "^8.32.0",
|
|
46
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
46
47
|
"eslint": "^9.24.0",
|
|
47
48
|
"eslint-plugin-import": "^2.31.0",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
49
|
+
"typescript": "^5.8.3",
|
|
50
|
+
"vitest": "^4.0.16"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"glob": "^7.2.3",
|
|
53
|
+
"chalk": "^4.1.2",
|
|
54
|
+
"commander": "^14.0.2",
|
|
55
|
+
"esbuild": "^0.27.2",
|
|
56
|
+
"fast-glob": "^3.3.3",
|
|
57
57
|
"globals": "^16.1.0"
|
|
58
|
-
},
|
|
59
|
-
"babel": {
|
|
60
|
-
"presets": [
|
|
61
|
-
"@babel/preset-env",
|
|
62
|
-
"@babel/preset-typescript"
|
|
63
|
-
]
|
|
64
58
|
}
|
|
65
59
|
}
|