UNPKG

@mdfriday/foundry

Version:

The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.

403 lines 18.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const pathparser_1 = require("../vo/pathparser"); const type_1 = require("../type"); // Create a global test processor instance const testProcessor = new pathparser_1.PathProcessorImpl(); describe('PathParser', () => { describe('Parse', () => { const testCases = [ { name: 'Basic Markdown file', path: '/a/b/c.md', assertions: (p) => { expect(p.isContent()).toBe(true); expect(p.isLeafBundle()).toBe(false); expect(p.name()).toBe('c.md'); expect(p.base()).toBe('/a/b/c'); expect(p.section()).toBe('a'); expect(p.baseNameNoIdentifier()).toBe('c'); expect(p.path()).toBe('/a/b/c.md'); expect(p.dir()).toBe('/a/b'); expect(p.container()).toBe('b'); expect(p.containerDir()).toBe('/a/b'); expect(p.ext()).toBe('.md'); }, }, { name: 'Basic text file, 1 space in dir', path: '/a b/c.txt', assertions: (p) => { expect(p.base()).toBe('/a-b/c.txt'); }, }, { name: 'Basic text file, 2 spaces in dir', path: '/a b/c.txt', assertions: (p) => { expect(p.base()).toBe('/a--b/c.txt'); }, }, { name: 'Basic text file, 1 space in filename', path: '/a/b c.txt', assertions: (p) => { expect(p.base()).toBe('/a/b-c.txt'); }, }, { name: 'Basic text file, 2 spaces in filename', path: '/a/b c.txt', assertions: (p) => { expect(p.base()).toBe('/a/b--c.txt'); }, }, { name: 'Basic md file, with index.md name', path: '/a/index.md', assertions: (p) => { expect(p.base()).toBe('/a'); expect(p.isLeafBundle()).toBe(true); expect(p.bundleType()).toBe(type_1.PathType.Leaf); }, }, { name: 'Basic md file, with _index.md name', path: '/a/_index.md', assertions: (p) => { expect(p.base()).toBe('/a'); expect(p.isBranchBundle()).toBe(true); expect(p.bundleType()).toBe(type_1.PathType.Branch); }, }, { name: 'Basic md file, with root _index.md name', path: '/_index.md', assertions: (p) => { expect(p.base()).toBe('/'); expect(p.isBranchBundle()).toBe(true); }, }, { name: 'Basic text file, mixed case and spaces, unnormalized', path: '/abc/Foo BAR.txt', assertions: (p) => { const pp = p.unnormalized(); expect(pp).not.toBeNull(); expect(pp.baseNameNoIdentifier()).toBe('Foo BAR'); expect(pp.section()).toBe('abc'); }, }, { name: 'Content single page', path: '/blog/my-post.md', assertions: (p) => { expect(p.bundleType()).toBe(type_1.PathType.ContentSingle); expect(p.isContent()).toBe(true); expect(p.isBundle()).toBe(false); expect(p.section()).toBe('blog'); }, }, { name: 'Nested leaf bundle', path: '/blog/2023/my-post/index.md', assertions: (p) => { expect(p.bundleType()).toBe(type_1.PathType.Leaf); expect(p.isLeafBundle()).toBe(true); expect(p.container()).toBe('my-post'); expect(p.section()).toBe('blog'); }, }, { name: 'Static resource', path: '/images/logo.png', assertions: (p) => { expect(p.bundleType()).toBe(type_1.PathType.File); expect(p.isContent()).toBe(false); expect(p.ext()).toBe('.png'); }, }, { name: 'HTML content file', path: '/pages/about.html', assertions: (p) => { expect(p.bundleType()).toBe(type_1.PathType.ContentSingle); expect(p.isContent()).toBe(true); expect(p.isHTML()).toBe(true); expect(p.ext()).toBe('.html'); }, }, { name: 'Multi-language content', path: '/blog/post.en.md', assertions: (p) => { expect(p.bundleType()).toBe(type_1.PathType.ContentSingle); expect(p.isContent()).toBe(true); expect(p.lang()).toBe('.en'); expect(p.ext()).toBe('.md'); }, }, ]; testCases.forEach(({ name, path, assertions }) => { test(name, () => { const parsed = testProcessor.parse('content', path); assertions(parsed); }); }); }); // Add specific test cases for the nameNoIdentifier issue describe('nameNoIdentifier issue verification', () => { test('should return correct nameNoIdentifier for root _index.md', () => { const path = testProcessor.parse('content', '/_index.md'); const nameNoId = path.nameNoIdentifier(); console.log('Root _index.md test:'); console.log(' Input path: /_index.md'); console.log(' nameNoIdentifier():', nameNoId); console.log(' Expected: _index'); expect(nameNoId).toBe('_index'); }); test('should return correct nameNoIdentifier for nested _index.md', () => { const path = testProcessor.parse('content', '/categories/Development/_index.md'); const nameNoId = path.nameNoIdentifier(); console.log('Nested _index.md test:'); console.log(' Input path: /categories/Development/_index.md'); console.log(' nameNoIdentifier():', nameNoId); console.log(' Expected: _index'); expect(nameNoId).toBe('_index'); }); test('should return correct nameNoIdentifier for root _index.md', () => { const path = testProcessor.parse('content', '/_index.md'); const nameNoId = path.nameNoIdentifier(); console.log('Root _index.md test:'); console.log(' Input path: /_index.md'); console.log(' nameNoIdentifier():', nameNoId); console.log(' Expected: index'); expect(nameNoId).toBe('_index'); }); test('should return correct nameNoIdentifier for nested _index.md', () => { const path = testProcessor.parse('content', '/blog/my-post/_index.md'); const nameNoId = path.nameNoIdentifier(); console.log('Nested _index.md test:'); console.log(' Input path: /blog/my-post/_index.md'); console.log(' nameNoIdentifier():', nameNoId); console.log(' Expected: index'); expect(nameNoId).toBe('_index'); }); test('should return correct nameNoIdentifier for regular content files', () => { const path = testProcessor.parse('content', '/blog/my-post.md'); const nameNoId = path.nameNoIdentifier(); console.log('Regular content file test:'); console.log(' Input path: /blog/my-post.md'); console.log(' nameNoIdentifier():', nameNoId); console.log(' Expected: my-post'); expect(nameNoId).toBe('my-post'); }); // Additional edge cases test('should handle paths with multiple identifiers correctly', () => { const path = testProcessor.parse('content', '/blog/post.en.md'); const nameNoId = path.nameNoIdentifier(); console.log('Multi-identifier test:'); console.log(' Input path: /blog/post.en.md'); console.log(' nameNoIdentifier():', nameNoId); console.log(' Expected: post'); expect(nameNoId).toBe('post'); }); }); describe('HasExt', () => { test('should detect extensions correctly', () => { expect(pathparser_1.PathParserUtils.hasExtension('/a/b/c.txt')).toBe(true); expect(pathparser_1.PathParserUtils.hasExtension('/a/b.c/d.txt')).toBe(true); expect(pathparser_1.PathParserUtils.hasExtension('/a/b/c')).toBe(false); expect(pathparser_1.PathParserUtils.hasExtension('/a/b.c/d')).toBe(false); }); }); describe('ParseIdentity', () => { test('should parse identity correctly', () => { const identity = testProcessor.parseIdentity('assets', '/a/b.css'); expect(identity.identifierBase()).toContain('b.css'); }); }); describe('ParseBaseAndBaseNameNoIdentifier', () => { test('should parse base and base name without identifier', () => { const [base, baseName] = testProcessor.parseBaseAndBaseNameNoIdentifier('content', '/blog/post.md'); expect(base).toBe('/blog/post'); expect(baseName).toBe('post'); }); }); describe('Component handling', () => { test('should handle different components correctly', () => { const contentPath = testProcessor.parse('content', '/blog/post.md'); const staticPath = testProcessor.parse('static', '/images/logo.png'); const archetypePath = testProcessor.parse('archetypes', '/post.md'); expect(contentPath.isContent()).toBe(true); expect(staticPath.isContent()).toBe(false); expect(archetypePath.isContent()).toBe(true); }); }); describe('Bundle detection', () => { test('should detect leaf bundles correctly', () => { const leafBundle = testProcessor.parse('content', '/blog/my-post/index.md'); expect(leafBundle.isLeafBundle()).toBe(true); expect(leafBundle.isBundle()).toBe(false); expect(leafBundle.bundleType()).toBe(type_1.PathType.Leaf); }); test('should detect branch bundles correctly', () => { const branchBundle = testProcessor.parse('content', '/blog/_index.md'); expect(branchBundle.isBranchBundle()).toBe(true); expect(branchBundle.isBundle()).toBe(true); expect(branchBundle.bundleType()).toBe(type_1.PathType.Branch); }); test('should handle section-level leaf bundles', () => { const sectionLeaf = testProcessor.parse('content', '/blog/index.md'); expect(sectionLeaf.isLeafBundle()).toBe(true); expect(sectionLeaf.section()).toBe('blog'); }); }); describe('Edge cases', () => { test('should handle root paths', () => { const rootIndex = testProcessor.parse('content', '/_index.md'); expect(rootIndex.section()).toBe(''); expect(rootIndex.isBranchBundle()).toBe(true); }); test('should handle paths without extensions', () => { const noExt = testProcessor.parse('content', '/blog/post'); expect(noExt.ext()).toBe(''); expect(noExt.isContent()).toBe(false); }); test('should handle empty paths', () => { const emptyPath = testProcessor.parse('content', ''); expect(emptyPath.path()).toBe('/'); }); test('should handle Windows-style paths', () => { // Mock Windows platform const originalPlatform = process.platform; Object.defineProperty(process, 'platform', { value: 'win32' }); const windowsPath = testProcessor.parse('content', '\\blog\\post.md'); expect(windowsPath.path()).toBe('/blog/post.md'); // Restore original platform Object.defineProperty(process, 'platform', { value: originalPlatform }); }); }); }); describe('BasicPathNormalizer', () => { test('should normalize with default settings', () => { const normalizer = new pathparser_1.BasicPathNormalizer(); expect(normalizer.normalize('BLOG/My Post.md')).toBe('blog/my-post.md'); }); test('should normalize with custom settings', () => { const normalizer = new pathparser_1.BasicPathNormalizer(false, true); expect(normalizer.normalize('BLOG/My Post.md')).toBe('BLOG/My-Post.md'); }); test('should handle spaces correctly', () => { const normalizer = new pathparser_1.BasicPathNormalizer(true, true); expect(normalizer.normalize('foo bar')).toBe('foo-bar'); expect(normalizer.normalize('foo\tbar')).toBe('foo-bar'); expect(normalizer.normalize('foo\nbar')).toBe('foo-bar'); }); }); describe('DefaultFileExtensionChecker', () => { const checker = new pathparser_1.DefaultFileExtensionChecker(); test('should identify content extensions', () => { expect(checker.isContentExt('.md')).toBe(true); expect(checker.isContentExt('.markdown')).toBe(true); expect(checker.isContentExt('.html')).toBe(true); expect(checker.isContentExt('.txt')).toBe(false); expect(checker.isContentExt('.png')).toBe(false); }); test('should identify HTML extensions', () => { expect(checker.isHTML('.html')).toBe(true); expect(checker.isHTML('.htm')).toBe(true); expect(checker.isHTML('.md')).toBe(false); }); test('should handle case insensitive extensions', () => { expect(checker.isContentExt('.MD')).toBe(true); expect(checker.isHTML('.HTML')).toBe(true); }); test('should detect extension presence', () => { expect(checker.hasExt('/path/file.txt')).toBe(true); expect(checker.hasExt('/path/file')).toBe(false); expect(checker.hasExt('/path.dir/file')).toBe(false); expect(checker.hasExt('/path.dir/file.txt')).toBe(true); }); }); describe('PathParserUtils', () => { describe('parseBasic', () => { test('should parse basic path components', () => { const result = pathparser_1.PathParserUtils.parseBasic('/blog/posts/hello-world.md'); expect(result).toEqual({ dir: '/blog/posts', name: 'hello-world.md', ext: '.md', nameWithoutExt: 'hello-world' }); }); test('should handle paths without directory', () => { const result = pathparser_1.PathParserUtils.parseBasic('file.txt'); expect(result).toEqual({ dir: '', name: 'file.txt', ext: '.txt', nameWithoutExt: 'file' }); }); test('should handle paths without extension', () => { const result = pathparser_1.PathParserUtils.parseBasic('/blog/posts/hello'); expect(result).toEqual({ dir: '/blog/posts', name: 'hello', ext: '', nameWithoutExt: 'hello' }); }); }); describe('join', () => { test('should join path segments correctly', () => { expect(pathparser_1.PathParserUtils.join('blog', 'posts', 'hello.md')).toBe('blog/posts/hello.md'); expect(pathparser_1.PathParserUtils.join('/blog/', '/posts/', '/hello.md/')).toBe('blog/posts/hello.md'); expect(pathparser_1.PathParserUtils.join('', 'blog', '', 'posts')).toBe('blog/posts'); }); }); describe('extractSection', () => { test('should extract section correctly', () => { expect(pathparser_1.PathParserUtils.extractSection('/blog/posts/hello.md')).toBe('blog'); expect(pathparser_1.PathParserUtils.extractSection('blog/posts/hello.md')).toBe('blog'); expect(pathparser_1.PathParserUtils.extractSection('/blog')).toBe('blog'); expect(pathparser_1.PathParserUtils.extractSection('blog')).toBe('blog'); }); }); describe('removeExtension', () => { test('should remove extensions correctly', () => { expect(pathparser_1.PathParserUtils.removeExtension('/blog/post.md')).toBe('/blog/post'); expect(pathparser_1.PathParserUtils.removeExtension('/blog/post')).toBe('/blog/post'); expect(pathparser_1.PathParserUtils.removeExtension('/path.dir/post.md')).toBe('/path.dir/post'); }); }); describe('isBundle', () => { test('should detect bundle files', () => { expect(pathparser_1.PathParserUtils.isBundle('/blog/index.md')).toBe(true); expect(pathparser_1.PathParserUtils.isBundle('/blog/_index.md')).toBe(true); expect(pathparser_1.PathParserUtils.isBundle('/blog/post.md')).toBe(false); }); }); }); describe('Performance and Memory', () => { test('should handle large number of paths efficiently', () => { const start = performance.now(); for (let i = 0; i < 1000; i++) { const path = testProcessor.parse('content', `/blog/post-${i}.md`); expect(path.isContent()).toBe(true); } const end = performance.now(); expect(end - start).toBeLessThan(1000); // Should complete within 1 second }); test('should not leak memory with unnormalized paths', () => { const paths = []; for (let i = 0; i < 100; i++) { const path = testProcessor.parse('content', `/Blog/Post ${i}.md`); paths.push(path.unnormalized()); } // All unnormalized paths should have different identities but same references when possible expect(paths.length).toBe(100); }); }); //# sourceMappingURL=pathparser.test.js.map