[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: add e18e lint plugin (#1019)

Co-authored-by: Daniel Roe <daniel@roe.dev>

authored by

James Garbutt
Daniel Roe
and committed by
GitHub
18bcb8a3 26f139b0

+72 -56
+6 -2
.github/workflows/ci.yml
··· 33 33 34 34 - uses: pnpm/action-setup@1e1c8eafbd745f64b1ef30a7d7ed7965034c486c # 1e1c8eafbd745f64b1ef30a7d7ed7965034c486c 35 35 name: 🟧 Install pnpm 36 - # pnpm cache skipped deliberately as the project is not actually installed here 36 + with: 37 + cache: true 38 + 39 + - name: 📦 Install dependencies (root only, no scripts) 40 + run: pnpm install --filter . --ignore-scripts 37 41 38 42 - name: 🔠 Lint project 39 - run: node scripts/lint.ts 43 + run: pnpm lint 40 44 41 45 types: 42 46 name: 💪 Type check
+21 -1
.oxlintrc.json
··· 1 1 { 2 2 "$schema": "https://unpkg.com/oxlint/configuration_schema.json", 3 3 "plugins": ["unicorn", "typescript", "oxc", "vue", "vitest"], 4 + "jsPlugins": ["@e18e/eslint-plugin"], 4 5 "categories": { 5 6 "correctness": "error", 6 7 "suspicious": "warn", ··· 11 12 "no-await-in-loop": "off", 12 13 "unicorn/no-array-sort": "off", 13 14 "no-restricted-globals": "error", 14 - "typescript/consistent-type-imports": "error" 15 + "typescript/consistent-type-imports": "error", 16 + "e18e/prefer-array-from-map": "error", 17 + "e18e/prefer-timer-args": "error", 18 + "e18e/prefer-date-now": "error", 19 + "e18e/prefer-regex-test": "error", 20 + "e18e/prefer-array-some": "error" 15 21 }, 22 + "overrides": [ 23 + { 24 + "files": [ 25 + "server/**/*", 26 + "cli/**/*", 27 + "scripts/**/*", 28 + "modules/**/*", 29 + "app/components/OgImage/*" 30 + ], 31 + "rules": { 32 + "no-console": "off" 33 + } 34 + } 35 + ], 16 36 "ignorePatterns": [ 17 37 ".output/**", 18 38 ".data/**",
+3
knip.ts
··· 39 39 40 40 /** Some components import types from here, but installing it directly could lead to a version mismatch */ 41 41 'vue-router', 42 + 43 + /** Oxlint plugins don't get picked up yet */ 44 + '@e18e/eslint-plugin', 42 45 ], 43 46 ignoreUnresolved: ['#components', '#oauth/config'], 44 47 },
+1
package.json
··· 101 101 "vue-data-ui": "3.14.7" 102 102 }, 103 103 "devDependencies": { 104 + "@e18e/eslint-plugin": "0.1.4", 104 105 "@intlify/core-base": "11.2.8", 105 106 "@npm/types": "2.1.0", 106 107 "@playwright/test": "1.58.1",
+22
pnpm-lock.yaml
··· 201 201 specifier: 3.14.7 202 202 version: 3.14.7(vue@3.5.27(typescript@5.9.3)) 203 203 devDependencies: 204 + '@e18e/eslint-plugin': 205 + specifier: 0.1.4 206 + version: 0.1.4(eslint@9.39.2(jiti@2.6.1)) 204 207 '@intlify/core-base': 205 208 specifier: 11.2.8 206 209 version: 11.2.8 ··· 1057 1060 1058 1061 '@dxup/unimport@0.1.2': 1059 1062 resolution: {integrity: sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==} 1063 + 1064 + '@e18e/eslint-plugin@0.1.4': 1065 + resolution: {integrity: sha512-nN0zo9lIsYJynhFyN+kNVINc9pDMotKjfEr1NcRRXCFKJt89V5S4DL6ZSXN2USJb2XezkMvyLOQMZ0lZTpvFeQ==} 1066 + peerDependencies: 1067 + eslint: ^9.0.0 1060 1068 1061 1069 '@emnapi/core@1.8.1': 1062 1070 resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} ··· 5564 5572 resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} 5565 5573 engines: {node: '>=6.0'} 5566 5574 hasBin: true 5575 + 5576 + eslint-plugin-depend@1.4.0: 5577 + resolution: {integrity: sha512-MQs+m4nHSfgAO9bJDsBzqw0ofK/AOA0vfeY/6ahofqcUMLeM6/D1sTYs21fOhc17kNU/gn58YCtj20XaAssh2A==} 5567 5578 5568 5579 eslint-scope@5.1.1: 5569 5580 resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} ··· 10645 10656 10646 10657 '@dxup/unimport@0.1.2': {} 10647 10658 10659 + '@e18e/eslint-plugin@0.1.4(eslint@9.39.2(jiti@2.6.1))': 10660 + dependencies: 10661 + eslint: 9.39.2(jiti@2.6.1) 10662 + eslint-plugin-depend: 1.4.0 10663 + 10648 10664 '@emnapi/core@1.8.1': 10649 10665 dependencies: 10650 10666 '@emnapi/wasi-threads': 1.1.0 ··· 15638 15654 esutils: 2.0.3 15639 15655 optionalDependencies: 15640 15656 source-map: 0.6.1 15657 + 15658 + eslint-plugin-depend@1.4.0: 15659 + dependencies: 15660 + empathic: 2.0.0 15661 + module-replacements: 2.11.0 15662 + semver: 7.7.3 15641 15663 15642 15664 eslint-scope@5.1.1: 15643 15665 dependencies:
-33
scripts/lint.ts
··· 1 - /** 2 - * This script runs oxlint and oxfmt in a CI environment, without the need to install the entire 3 - * project. It reads the required version from pnpm-lock.yaml and executes the linters accordingly. 4 - * It's "stupid by design" so it could work in minimal Node.js environments. 5 - */ 6 - 7 - import { spawnSync } from 'node:child_process' 8 - 9 - function getDependencyVersion(dependencyName: string): string { 10 - const result = spawnSync('npm', ['pkg', 'get', `devDependencies.${dependencyName}`], { 11 - encoding: 'utf8', 12 - }) 13 - 14 - if (result.status) { 15 - throw new Error(`Command failed: pnpm info ${dependencyName} version`) 16 - } 17 - 18 - return JSON.parse(result.stdout) 19 - } 20 - 21 - function runCommand(command: string, args: string[]) { 22 - const result = spawnSync(command, args, { stdio: 'inherit' }) 23 - 24 - if (result.status) { 25 - throw new Error(`Command failed: ${command} ${args.join(' ')}`) 26 - } 27 - } 28 - 29 - const oxlintVersion = getDependencyVersion('oxlint') 30 - const oxfmtVersion = getDependencyVersion('oxfmt') 31 - 32 - runCommand('pnpx', [`oxlint@${oxlintVersion}`]) 33 - runCommand('pnpx', [`oxfmt@${oxfmtVersion}`, '--check'])
+9 -10
server/api/atproto/author-profiles.get.ts
··· 49 49 50 50 if (handles.length === 0) { 51 51 return { 52 - authors: authors.map(author => ({ 53 - ...author, 54 - avatar: null, 55 - profileUrl: null, 56 - })), 52 + authors: authors.map(author => Object.assign(author, { avatar: null, profileUrl: null })), 57 53 } 58 54 } 59 55 ··· 68 64 } 69 65 } 70 66 71 - const resolvedAuthors: ResolvedAuthor[] = authors.map(author => ({ 72 - ...author, 73 - avatar: author.blueskyHandle ? avatarMap.get(author.blueskyHandle) || null : null, 74 - profileUrl: author.blueskyHandle ? `https://bsky.app/profile/${author.blueskyHandle}` : null, 75 - })) 67 + const resolvedAuthors: ResolvedAuthor[] = authors.map(author => 68 + Object.assign(author, { 69 + avatar: author.blueskyHandle ? avatarMap.get(author.blueskyHandle) || null : null, 70 + profileUrl: author.blueskyHandle 71 + ? `https://bsky.app/profile/${author.blueskyHandle}` 72 + : null, 73 + }), 74 + ) 76 75 77 76 return { authors: resolvedAuthors } 78 77 },
+1 -1
server/utils/dependency-analysis.ts
··· 179 179 const resolved = await resolveDependencyTree(name, version, { trackDepth: true }) 180 180 181 181 // Convert to array with query info 182 - const packages: PackageQueryInfo[] = [...resolved.values()].map(pkg => ({ 182 + const packages: PackageQueryInfo[] = Array.from(resolved.values(), pkg => ({ 183 183 name: pkg.name, 184 184 version: pkg.version, 185 185 depth: pkg.depth!,
+8 -8
server/utils/readme.ts
··· 264 264 return resolved 265 265 } 266 266 267 + // Helper to prefix id attributes with 'user-content-' 268 + function prefixId(tagName: string, attribs: sanitizeHtml.Attributes) { 269 + if (attribs.id && !attribs.id.startsWith('user-content-')) { 270 + attribs.id = `user-content-${attribs.id}` 271 + } 272 + return { tagName, attribs } 273 + } 274 + 267 275 export async function renderReadmeHtml( 268 276 content: string, 269 277 packageName: string, ··· 397 405 marked.setOptions({ renderer }) 398 406 399 407 const rawHtml = marked.parse(content) as string 400 - 401 - // Helper to prefix id attributes with 'user-content-' 402 - const prefixId = (tagName: string, attribs: sanitizeHtml.Attributes) => { 403 - if (attribs.id && !attribs.id.startsWith('user-content-')) { 404 - attribs.id = `user-content-${attribs.id}` 405 - } 406 - return { tagName, attribs } 407 - } 408 408 409 409 const sanitized = sanitizeHtml(rawHtml, { 410 410 allowedTags: ALLOWED_TAGS,
+1 -1
test/e2e/test-utils.ts
··· 313 313 const pathname = decodeURIComponent(url.pathname) 314 314 315 315 // README file requests - return 404 (package pages work fine without README) 316 - if (pathname.match(/readme/i)) { 316 + if (/readme/i.test(pathname)) { 317 317 await route.fulfill({ status: 404, body: 'Not found' }) 318 318 return true 319 319 }