[READ-ONLY] a fast, modern browser for the npm registry
at main 180 lines 6.2 kB view raw
1import { describe, expect, it } from 'vitest' 2import type { PackageFileTree } from '../../../../shared/types' 3import { 4 createImportResolver, 5 flattenFileTree, 6 resolveRelativeImport, 7} from '../../../../server/utils/import-resolver' 8 9describe('flattenFileTree', () => { 10 it('flattens nested trees into a file set', () => { 11 const tree: PackageFileTree[] = [ 12 { 13 name: 'dist', 14 path: 'dist', 15 type: 'directory', 16 children: [ 17 { name: 'index.js', path: 'dist/index.js', type: 'file', size: 10 }, 18 { 19 name: 'utils', 20 path: 'dist/utils', 21 type: 'directory', 22 children: [{ name: 'format.js', path: 'dist/utils/format.js', type: 'file', size: 5 }], 23 }, 24 ], 25 }, 26 ] 27 28 const files = flattenFileTree(tree) 29 30 expect(files.has('dist/index.js')).toBe(true) 31 expect(files.has('dist/utils/format.js')).toBe(true) 32 expect(files.has('dist/utils')).toBe(false) 33 }) 34 35 it('returns an empty set for an empty tree', () => { 36 const files = flattenFileTree([]) 37 expect(files.size).toBe(0) 38 }) 39 40 it('includes root-level files', () => { 41 const tree: PackageFileTree[] = [ 42 { name: 'index.js', path: 'index.js', type: 'file', size: 5 }, 43 { name: 'cli.js', path: 'cli.js', type: 'file', size: 3 }, 44 ] 45 46 const files = flattenFileTree(tree) 47 48 expect(files.has('index.js')).toBe(true) 49 expect(files.has('cli.js')).toBe(true) 50 }) 51}) 52 53describe('resolveRelativeImport', () => { 54 it('resolves a relative import with extension priority for JS files', () => { 55 const files = new Set<string>(['dist/utils.js', 'dist/utils.ts']) 56 const resolved = resolveRelativeImport('./utils', 'dist/index.js', files) 57 58 expect(resolved?.path).toBe('dist/utils.js') 59 }) 60 61 it('resolves a relative import with extension priority for TS files', () => { 62 const files = new Set<string>(['src/utils.ts', 'src/utils.js']) 63 const resolved = resolveRelativeImport('./utils', 'src/index.ts', files) 64 65 expect(resolved?.path).toBe('src/utils.ts') 66 }) 67 68 it('resolves a relative import to .d.ts when source is a declaration file', () => { 69 const files = new Set<string>(['dist/types.d.ts', 'dist/types.ts']) 70 const resolved = resolveRelativeImport('./types', 'dist/index.d.ts', files) 71 72 expect(resolved?.path).toBe('dist/types.d.ts') 73 }) 74 75 it('resolves an exact extension match', () => { 76 const files = new Set<string>(['src/utils.ts', 'src/utils.js']) 77 const resolved = resolveRelativeImport('./utils.ts', 'src/index.ts', files) 78 79 expect(resolved?.path).toBe('src/utils.ts') 80 }) 81 82 it('resolves a quoted specifier', () => { 83 const files = new Set<string>(['dist/utils.js']) 84 const resolved = resolveRelativeImport("'./utils'", 'dist/index.js', files) 85 86 expect(resolved?.path).toBe('dist/utils.js') 87 }) 88 89 it('resolves a relative import with extension priority for MTS files', () => { 90 const files = new Set<string>(['src/utils.mts', 'src/utils.mjs', 'src/utils.ts']) 91 const resolved = resolveRelativeImport('./utils', 'src/index.mts', files) 92 93 expect(resolved?.path).toBe('src/utils.mts') 94 }) 95 96 it('resolves a relative import with extension priority for MJS files', () => { 97 const files = new Set<string>(['dist/utils.mjs', 'dist/utils.js']) 98 const resolved = resolveRelativeImport('./utils', 'dist/index.mjs', files) 99 100 expect(resolved?.path).toBe('dist/utils.mjs') 101 }) 102 103 it('resolves a relative import with extension priority for CTS files', () => { 104 const files = new Set<string>(['src/utils.cts', 'src/utils.cjs', 'src/utils.ts']) 105 const resolved = resolveRelativeImport('./utils', 'src/index.cts', files) 106 107 expect(resolved?.path).toBe('src/utils.cts') 108 }) 109 110 it('resolves a relative import with extension priority for CJS files', () => { 111 const files = new Set<string>(['dist/utils.cjs', 'dist/utils.js']) 112 const resolved = resolveRelativeImport('./utils', 'dist/index.cjs', files) 113 114 expect(resolved?.path).toBe('dist/utils.cjs') 115 }) 116 117 it('resolves directory imports to index files', () => { 118 const files = new Set<string>(['dist/components/index.js']) 119 const resolved = resolveRelativeImport('./components', 'dist/index.js', files) 120 121 expect(resolved?.path).toBe('dist/components/index.js') 122 }) 123 124 it('resolves parent directory paths', () => { 125 const files = new Set<string>(['dist/shared/helpers.js']) 126 const resolved = resolveRelativeImport('../shared/helpers', 'dist/pages/home.js', files) 127 128 expect(resolved?.path).toBe('dist/shared/helpers.js') 129 }) 130 131 it('returns null when the path would go above the package root', () => { 132 const files = new Set<string>(['dist/index.js']) 133 const resolved = resolveRelativeImport('../../outside', 'dist/index.js', files) 134 135 expect(resolved).toBeNull() 136 }) 137 138 it('returns null for non-relative imports', () => { 139 const files = new Set<string>(['dist/utils.js']) 140 const resolved = resolveRelativeImport('react', 'dist/index.js', files) 141 142 expect(resolved).toBeNull() 143 }) 144 145 it('returns null when no matching file is found', () => { 146 const files = new Set<string>(['dist/utils.js']) 147 const resolved = resolveRelativeImport('./missing', 'dist/index.js', files) 148 149 expect(resolved).toBeNull() 150 }) 151}) 152 153describe('createImportResolver', () => { 154 it('creates a resolver that returns code browser URLs', () => { 155 const files = new Set<string>(['dist/utils.js']) 156 const resolver = createImportResolver(files, 'dist/index.js', 'pkg-name', '1.2.3') 157 158 const url = resolver('./utils') 159 160 expect(url).toBe('/package-code/pkg-name/v/1.2.3/dist/utils.js') 161 }) 162 163 it('returns null when the import cannot be resolved', () => { 164 const files = new Set<string>(['dist/utils.js']) 165 const resolver = createImportResolver(files, 'dist/index.js', 'pkg-name', '1.2.3') 166 167 const url = resolver('./missing') 168 169 expect(url).toBeNull() 170 }) 171 172 it('handles scoped package names in URLs', () => { 173 const files = new Set<string>(['dist/utils.js']) 174 const resolver = createImportResolver(files, 'dist/index.js', '@scope/pkg', '1.2.3') 175 176 const url = resolver('./utils') 177 178 expect(url).toBe('/package-code/@scope/pkg/v/1.2.3/dist/utils.js') 179 }) 180})