data-structure-typed 1.53.2 → 1.53.3
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/CHANGELOG.md +1 -1
- package/README.md +6 -0
- package/dist/cjs/data-structures/heap/heap.d.ts +165 -0
- package/dist/cjs/data-structures/heap/heap.js +165 -0
- package/dist/cjs/data-structures/heap/heap.js.map +1 -1
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.d.ts +426 -0
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js +430 -2
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/mjs/data-structures/heap/heap.d.ts +165 -0
- package/dist/mjs/data-structures/heap/heap.js +165 -0
- package/dist/mjs/data-structures/heap/heap.js.map +1 -1
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.d.ts +426 -0
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.js +430 -2
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/umd/data-structure-typed.js +2 -2
- package/dist/umd/data-structure-typed.min.js +1 -1
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +12 -11
- package/src/data-structures/heap/heap.ts +165 -0
- package/src/data-structures/linked-list/doubly-linked-list.ts +428 -2
- package/test/unit/data-structures/heap/heap.test.ts +173 -0
- package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +434 -0
- package/test/utils/string.ts +49 -1
- package/testToExample.ts +215 -0
package/testToExample.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as ts from 'typescript';
|
|
4
|
+
import { toPascalCase } from './test/utils';
|
|
5
|
+
|
|
6
|
+
const isReplaceMD = false;
|
|
7
|
+
const START_MARKER = '[//]: # (No deletion!!! Start of Example Replace Section)';
|
|
8
|
+
const END_MARKER = '[//]: # (No deletion!!! End of Example Replace Section)';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Recursively retrieve all `.ts` files in a directory.
|
|
12
|
+
*/
|
|
13
|
+
function getAllTestFiles(dir: string): string[] {
|
|
14
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
15
|
+
|
|
16
|
+
const files = entries
|
|
17
|
+
.filter(file => !file.isDirectory() && file.name.endsWith('.ts'))
|
|
18
|
+
.map(file => path.join(dir, file.name));
|
|
19
|
+
|
|
20
|
+
const directories = entries.filter(entry => entry.isDirectory());
|
|
21
|
+
|
|
22
|
+
for (const directory of directories) {
|
|
23
|
+
files.push(...getAllTestFiles(path.join(dir, directory.name)));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return files;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extract test cases with `@example` from TypeScript files using AST.
|
|
31
|
+
*/
|
|
32
|
+
function extractExamplesFromFile(filePath: string): { name: string; body: string }[] {
|
|
33
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
34
|
+
const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true);
|
|
35
|
+
|
|
36
|
+
const examples: { name: string; body: string }[] = [];
|
|
37
|
+
|
|
38
|
+
function visit(node: ts.Node) {
|
|
39
|
+
if (
|
|
40
|
+
ts.isCallExpression(node) && // Ensure it's a function call
|
|
41
|
+
node.arguments.length >= 2 && // At least two arguments
|
|
42
|
+
ts.isStringLiteral(node.arguments[0]) && // First argument is a string
|
|
43
|
+
node.arguments[0].text.startsWith('@example') && // Matches @example
|
|
44
|
+
ts.isArrowFunction(node.arguments[1]) // Second argument is an arrow function
|
|
45
|
+
) {
|
|
46
|
+
const exampleName = node.arguments[0].text.replace('@example ', '').trim();
|
|
47
|
+
const bodyNode = node.arguments[1].body;
|
|
48
|
+
|
|
49
|
+
let exampleBody: string;
|
|
50
|
+
if (ts.isBlock(bodyNode)) {
|
|
51
|
+
// If it's a block, remove outer {}
|
|
52
|
+
exampleBody = bodyNode.statements
|
|
53
|
+
.map(stmt => stmt.getFullText(sourceFile))
|
|
54
|
+
.join('')
|
|
55
|
+
.trim();
|
|
56
|
+
} else {
|
|
57
|
+
// If it's a single expression, use it directly
|
|
58
|
+
exampleBody = bodyNode.getFullText(sourceFile).trim();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const transformedBody = exampleBody
|
|
62
|
+
.replace(
|
|
63
|
+
/expect\((.*?)\)\.(toEqual|toBe|toStrictEqual|toHaveLength|toMatchObject)\((.*?)\);/gs, // Use `s` flag for multiline
|
|
64
|
+
(match, actual, method, expected) => {
|
|
65
|
+
expected = expected.replace(/\n/g, '\n //')
|
|
66
|
+
return `console.log(${actual}); // ${expected}`;
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
.replace(
|
|
70
|
+
/expect\((.*?)\)\.(toBeUndefined|toBeNull)\(\);/g,
|
|
71
|
+
(match, actual, method) => {
|
|
72
|
+
const expectedValue = method === 'toBeUndefined' ? 'undefined' : 'null';
|
|
73
|
+
return `console.log(${actual}); // ${expectedValue}`;
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
.trim();
|
|
77
|
+
|
|
78
|
+
examples.push({ name: exampleName, body: transformedBody });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ts.forEachChild(node, visit);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
visit(sourceFile);
|
|
85
|
+
|
|
86
|
+
return examples;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Add examples to the corresponding class in the source file.
|
|
91
|
+
*/
|
|
92
|
+
function addExamplesToSourceFile(
|
|
93
|
+
sourceFilePath: string,
|
|
94
|
+
className: string,
|
|
95
|
+
examples: { name: string; body: string }[]
|
|
96
|
+
): void {
|
|
97
|
+
if (!fs.existsSync(sourceFilePath)) {
|
|
98
|
+
console.warn(`Source file not found: ${sourceFilePath}`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const sourceContent = fs.readFileSync(sourceFilePath, 'utf-8');
|
|
103
|
+
const sourceFile = ts.createSourceFile(sourceFilePath, sourceContent, ts.ScriptTarget.Latest, true);
|
|
104
|
+
|
|
105
|
+
let updatedContent = sourceContent;
|
|
106
|
+
|
|
107
|
+
const classNode = sourceFile.statements.find(
|
|
108
|
+
stmt => ts.isClassDeclaration(stmt) && stmt.name?.text === className
|
|
109
|
+
) as ts.ClassDeclaration | undefined;
|
|
110
|
+
|
|
111
|
+
if (classNode) {
|
|
112
|
+
const classStart = classNode.getStart(sourceFile);
|
|
113
|
+
const classEnd = classNode.getEnd();
|
|
114
|
+
const classText = classNode.getFullText(sourceFile);
|
|
115
|
+
|
|
116
|
+
// Extract annotation content
|
|
117
|
+
const existingCommentMatch = classText.match(/\/\*\*([\s\S]*?)\*\//);
|
|
118
|
+
if (!existingCommentMatch) {
|
|
119
|
+
console.warn(`No existing comment found for class: ${className}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const existingCommentInner = existingCommentMatch[1].replace(/^\n \* /, ''); // Extract comment content (excluding `/**` and `*/`)
|
|
124
|
+
|
|
125
|
+
// Replace @example part
|
|
126
|
+
const exampleSection = examples
|
|
127
|
+
.map(
|
|
128
|
+
example =>
|
|
129
|
+
` * @example \n * \/\/ ${example.name} \n${example.body
|
|
130
|
+
.split('\n')
|
|
131
|
+
.map(line => ` * ${line}`)
|
|
132
|
+
.join('\n')}`
|
|
133
|
+
)
|
|
134
|
+
.join('\n') + '\n ';
|
|
135
|
+
|
|
136
|
+
let newComment = '';
|
|
137
|
+
if (existingCommentInner.includes('@example')) {
|
|
138
|
+
newComment = existingCommentInner.replace(/ \* @example[\s\S]*?(?=\*\/|$)/g, exampleSection);
|
|
139
|
+
} else {
|
|
140
|
+
newComment = existingCommentInner + `${exampleSection}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
// Replace original content
|
|
145
|
+
updatedContent =
|
|
146
|
+
sourceContent.slice(0, classStart - existingCommentInner.length - 3) +
|
|
147
|
+
newComment +
|
|
148
|
+
classText.slice(existingCommentMatch[0].length).trim() +
|
|
149
|
+
sourceContent.slice(classEnd);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fs.writeFileSync(sourceFilePath, updatedContent, 'utf-8');
|
|
153
|
+
console.log(`Updated examples in ${sourceFilePath}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Process all test files and update README.md and source files.
|
|
159
|
+
*/
|
|
160
|
+
function updateExamples(testDir: string, readmePath: string, sourceBaseDir: string): void {
|
|
161
|
+
const testFiles = getAllTestFiles(testDir);
|
|
162
|
+
|
|
163
|
+
let allExamples: string[] = [];
|
|
164
|
+
for (const file of testFiles) {
|
|
165
|
+
const examples = extractExamplesFromFile(file);
|
|
166
|
+
|
|
167
|
+
if (examples.length === 0) {
|
|
168
|
+
console.log(`No @example found in test file: ${file}`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const relativePath = path.relative(testDir, file);
|
|
173
|
+
const sourceFilePath = path.resolve(sourceBaseDir, relativePath.replace('.test.ts', '.ts'));
|
|
174
|
+
const className = path.basename(sourceFilePath, '.ts');
|
|
175
|
+
|
|
176
|
+
addExamplesToSourceFile(sourceFilePath, toPascalCase(className), examples);
|
|
177
|
+
|
|
178
|
+
allExamples = allExamples.concat(
|
|
179
|
+
examples.map(example => `### ${example.name}\n\`\`\`typescript\n${example.body}\n\`\`\``)
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (isReplaceMD && allExamples.length > 0) {
|
|
184
|
+
replaceExamplesInReadme(readmePath, allExamples);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Replace content between markers in README.md.
|
|
190
|
+
*/
|
|
191
|
+
function replaceExamplesInReadme(readmePath: string, newExamples: string[]): void {
|
|
192
|
+
const readmeContent = fs.readFileSync(readmePath, 'utf-8');
|
|
193
|
+
|
|
194
|
+
const startIdx = readmeContent.indexOf(START_MARKER);
|
|
195
|
+
const endIdx = readmeContent.indexOf(END_MARKER);
|
|
196
|
+
|
|
197
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
198
|
+
throw new Error(`Markers not found in ${readmePath}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const before = readmeContent.slice(0, startIdx + START_MARKER.length);
|
|
202
|
+
const after = readmeContent.slice(endIdx);
|
|
203
|
+
|
|
204
|
+
const updatedContent = `${before}\n\n${newExamples.join('\n\n')}\n\n${after}`;
|
|
205
|
+
fs.writeFileSync(readmePath, updatedContent, 'utf-8');
|
|
206
|
+
|
|
207
|
+
console.log(`README.md updated with new examples.`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Run the script
|
|
211
|
+
const testDir = path.resolve(__dirname, 'test/unit');
|
|
212
|
+
const readmePath = path.resolve(__dirname, 'README.md');
|
|
213
|
+
const sourceBaseDir = path.resolve(__dirname, 'src');
|
|
214
|
+
|
|
215
|
+
updateExamples(testDir, readmePath, sourceBaseDir);
|