diff --git a/bin/md-tree.js b/bin/md-tree.js index b2388eb..90c4df7 100755 --- a/bin/md-tree.js +++ b/bin/md-tree.js @@ -385,13 +385,23 @@ For more information, visit: https://github.com/ksylvan/markdown-tree-parser const content = await this.readFile(resolvedPath); const tree = await this.parser.parse(content); const links = this.parser.selectAll(tree, 'link'); + const definitions = this.parser.selectAll(tree, 'definition'); + + const allUrls = []; + for (const link of links) { + allUrls.push(link.url); + } + for (const definition of definitions) { + allUrls.push(definition.url); + } + + const uniqueUrls = new Set(allUrls); console.log( - `\nšŸ”— Checking ${links.length} links in ${path.basename(resolvedPath)}:` + `\nšŸ”— Checking ${uniqueUrls.size} unique URLs in ${path.basename(resolvedPath)}:` ); - for (const link of links) { - const url = link.url; + for (const url of uniqueUrls) { if (!url || url.startsWith('#')) { continue; } diff --git a/package.json b/package.json index fc56130..efc373e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kayvan/markdown-tree-parser", - "version": "1.5.0", + "version": "1.5.1", "description": "A powerful JavaScript library and CLI tool for parsing and manipulating markdown files as tree structures using the remark/unified ecosystem", "type": "module", "main": "index.js", diff --git a/test/sample.md b/test/sample.md index efd378d..c908e9a 100644 --- a/test/sample.md +++ b/test/sample.md @@ -1,153 +1,3 @@ -# Sample Document +# Sample File -This is a sample markdown document for testing the markdown-tree-parser package. - -## Overview - -This document demonstrates various markdown features and structures that can be parsed and manipulated. - -### Key Features - -- **Bold text** and *italic text* -- `Inline code` and code blocks -- [Links](https://example.com) and ![images](image.png) -- Lists and tables - -### Architecture - -The document follows a hierarchical structure with multiple heading levels. - -## Installation - -Follow these steps to get started: - -### Prerequisites - -Make sure you have the following installed: - -1. Node.js (version 18 or higher) -2. npm package manager -3. A text editor - -### Quick Install - -```bash -npm install markdown-tree-parser -``` - -### Configuration - -You can configure the parser with various options: - -```javascript -const parser = new MarkdownTreeParser({ - bullet: '-', - emphasis: '_' -}); -``` - -## Usage Examples - -Here are some common usage patterns. - -### Basic Usage - -```javascript -import { MarkdownTreeParser } from 'markdown-tree-parser'; - -const parser = new MarkdownTreeParser(); -const tree = await parser.parse(markdown); -``` - -### Advanced Features - -For more complex scenarios: - -- Extract specific sections -- Search with CSS selectors -- Transform document structure -- Generate statistics - -### CLI Usage - -The package includes a powerful CLI: - -```bash -md-tree list document.md -md-tree extract document.md "Installation" -``` - -## API Reference - -### Core Methods - -The main parser class provides these methods: - -- `parse(markdown)` - Parse markdown to AST -- `stringify(tree)` - Convert AST to markdown -- `extractSection(tree, heading)` - Extract sections -- `selectAll(tree, selector)` - CSS-like search - -### Utility Functions - -Additional helper functions: - -- `getHeadingsList(tree)` - List all headings -- `getStats(tree)` - Document statistics -- `generateTableOfContents(tree)` - Create TOC - -## Testing - -The package includes comprehensive tests: - -```bash -npm test -npm run test:cli -``` - -### Test Coverage - -- Unit tests for all core functions -- Integration tests for CLI commands -- End-to-end examples - -## Contributing - -We welcome contributions from the community! - -### Getting Started - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Run the tests -5. Submit a pull request - -### Code Standards - -Please follow these guidelines: - -- Use ESLint for code quality -- Write meaningful commit messages -- Include tests for new features -- Update documentation as needed - -## Support - -Need help? Here are your options: - -### Documentation - -- Check the README for basic usage -- Browse the examples directory -- Read the API documentation - -### Community - -- Open an issue on GitHub -- Join our discussions -- Check Stack Overflow - -## License - -This project is licensed under the MIT License. +This is a sample file for testing links. diff --git a/test/test-email-links.md b/test/test-email-links.md deleted file mode 100644 index e69de29..0000000 diff --git a/test/test-reference-links.md b/test/test-reference-links.md new file mode 100644 index 0000000..cc73ed6 --- /dev/null +++ b/test/test-reference-links.md @@ -0,0 +1,8 @@ +# Test File for Reference-Style Links + +This is a [link to Google][google]. +Here is another [link to a local file][local-doc]. +And an [undefined link][undefined]. + +[google]: https://www.google.com +[local-doc]: ./test-dummy-sample.md diff --git a/test/test.js b/test/test.js index 95bfbae..d9415ae 100644 --- a/test/test.js +++ b/test/test.js @@ -5,6 +5,7 @@ */ import { MarkdownTreeParser, createParser, extractSection } from '../index.js'; +import { MarkdownCLI } from '../bin/md-tree.js'; // Added for checkLinks test import fs from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -583,6 +584,101 @@ Plain text with email@domain.com should not be a link. ); }); + // Test suite for checkLinks + await test('Check Links Functionality', async () => { + const cli = new MarkdownCLI(); + const testReferenceLinksFile = path.join( + __dirname, + 'test-reference-links.md' + ); + const testDummySampleFilePath = path.join( + __dirname, + 'test-dummy-sample.md' + ); + + // Create a unique dummy file for local link checking to pass + await fs.writeFile( + testDummySampleFilePath, + '# Test Dummy Sample File\n\nThis is a test dummy file for testing links.' + ); + + await test('Check reference-style links', async () => { + const markdownContent = await fs.readFile( + testReferenceLinksFile, + 'utf-8' + ); + const tree = await cli.parser.parse(markdownContent); + const links = cli.parser.selectAll(tree, 'link'); + const definitions = cli.parser.selectAll(tree, 'definition'); + + const collectedUrls = new Set(); + for (const link of links) { + if (link.url) collectedUrls.add(link.url); + } + for (const def of definitions) { + if (def.url) collectedUrls.add(def.url); + } + + assert( + collectedUrls.has('https://www.google.com'), + 'Should find Google link from definition' + ); + assert( + collectedUrls.has('./test-dummy-sample.md'), + 'Should find local-doc link from definition' + ); + assert( + !collectedUrls.has('[undefined]'), + 'Should not treat undefined reference as a URL' + ); + // The following assertions were removed because selectAll(tree, 'link') for reference-style links + // correctly returns linkReference nodes which do not have the 'url' property populated directly. + // The url is resolved by checkLinks by looking at 'definition' nodes, + // which is already tested by the collectedUrls assertions and the checkLinks output assertions. + // assert(links.some(link => link.reference === 'google' && link.url === 'https://www.google.com'), 'Link object for google should have url'); + // assert(links.some(link => link.reference === 'local-doc' && link.url === './sample.md'), 'Link object for local-doc should have url'); + + // Test the checkLinks output + const originalConsoleLog = console.log; + const logs = []; + console.log = (...args) => { + logs.push(args.join(' ')); + }; + + try { + await cli.checkLinks(testReferenceLinksFile); + } finally { + console.log = originalConsoleLog; + // Clean up test dummy file + try { + await fs.unlink(testDummySampleFilePath); + } catch (error) { + // Ignore cleanup errors - file might not exist + console.warn( + `Warning: Could not clean up ${testDummySampleFilePath}:`, + error.message + ); + } + } + + const output = logs.join('\n'); + assert( + output.includes('šŸ”— Checking 2 unique URLs'), + 'Should check 2 unique URLs' + ); + assert( + output.includes('āœ… https://www.google.com'), + 'Should successfully check Google link' + ); + // Note: The actual local file path in output might be resolved. + // We check for the original URL './test-dummy-sample.md' and the "āœ…" status. + assert( + output.includes('āœ… ./test-dummy-sample.md'), + 'Should successfully check local test dummy sample file link' + ); + }); + }); + // Summary console.log(`\nšŸ“Š Test Results: ${passedTests}/${testCount} passed`);