diff --git a/extensions/cratecast/.eslintrc.json b/extensions/cratecast/.eslintrc.json new file mode 100644 index 0000000000000..03485d697dbbb --- /dev/null +++ b/extensions/cratecast/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "root": true, + "env": { + "es2020": true, + "node": true + }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ] +} diff --git a/extensions/cratecast/.gitignore b/extensions/cratecast/.gitignore new file mode 100644 index 0000000000000..2752eb92e5922 --- /dev/null +++ b/extensions/cratecast/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.DS_Store diff --git a/extensions/cratecast/.prettierrc.yml b/extensions/cratecast/.prettierrc.yml new file mode 100644 index 0000000000000..6b9c164a55035 --- /dev/null +++ b/extensions/cratecast/.prettierrc.yml @@ -0,0 +1,4 @@ +trailingComma: 'es5' +tabWidth: 2 +semi: false +singleQuote: true diff --git a/extensions/cratecast/README.md b/extensions/cratecast/README.md new file mode 100644 index 0000000000000..c9afcf29ccb0d --- /dev/null +++ b/extensions/cratecast/README.md @@ -0,0 +1,8 @@ +# cratecast + +[![lint](https://github.com/gleich/cratecast/actions/workflows/lint.yml/badge.svg)](https://github.com/gleich/cratecast/actions/workflows/lint.yml) +[![build](https://github.com/gleich/cratecast/actions/workflows/build.yml/badge.svg)](https://github.com/gleich/cratecast/actions/workflows/build.yml) + +📦 crates.io from raycast + +![example image](./example.png) diff --git a/extensions/cratecast/assets/icon.png b/extensions/cratecast/assets/icon.png new file mode 100644 index 0000000000000..307c242e10a7a Binary files /dev/null and b/extensions/cratecast/assets/icon.png differ diff --git a/extensions/cratecast/example.png b/extensions/cratecast/example.png new file mode 100644 index 0000000000000..52a1c53eaf86d Binary files /dev/null and b/extensions/cratecast/example.png differ diff --git a/extensions/cratecast/package.json b/extensions/cratecast/package.json new file mode 100644 index 0000000000000..1fe42f536621a --- /dev/null +++ b/extensions/cratecast/package.json @@ -0,0 +1,39 @@ +{ + "name": "cratecast", + "title": "crates.io Search", + "description": "Explore crates.io", + "icon": "icon.png", + "author": "matt", + "license": "MIT", + "commands": [ + { + "name": "index", + "title": "crates.io Search", + "subtitle": "crates.io", + "description": "Search for crates on crates.io", + "mode": "view" + } + ], + "dependencies": { + "@raycast/api": "^1.25.0", + "node-fetch": "^3.0.0", + "use-debounce": "^7.0.0" + }, + "devDependencies": { + "@types/node": "~16.10.0", + "@types/react": "^17.0.28", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^7.32.0", + "eslint-config-prettier": "^8.3.0", + "prettier": "^2.4.1", + "react-devtools": "^4.19.2", + "typescript": "^4.4.3" + }, + "scripts": { + "build": "ray build -e dist", + "dev": "ray develop", + "format": "prettier -w .", + "format::check": "prettier --check ." + } +} diff --git a/extensions/cratecast/src/api.ts b/extensions/cratecast/src/api.ts new file mode 100644 index 0000000000000..9b2b7d08c1d8a --- /dev/null +++ b/extensions/cratecast/src/api.ts @@ -0,0 +1,34 @@ +import fetch from 'node-fetch' + +export interface Crate { + name: string + version: string + downloads: number + documentationURL?: string + homepageURL?: string + repositoryURL?: string +} + +export async function getCrates(search: string): Promise { + if (search === '') { + return [] + } + const response = await fetch( + 'https://crates.io/api/v1/crates?page=1&per_page=100&q=' + + encodeURIComponent(search) + ) + const json = await response.json() + + const crates: Crate[] = [] + for (const crate of Object(json).crates) { + crates.push({ + name: crate.name, + version: crate.max_version, + downloads: crate.downloads, + documentationURL: crate.documentation, + homepageURL: crate.homepage, + repositoryURL: crate.repository, + }) + } + return crates +} diff --git a/extensions/cratecast/src/index.tsx b/extensions/cratecast/src/index.tsx new file mode 100644 index 0000000000000..09b927eb7d853 --- /dev/null +++ b/extensions/cratecast/src/index.tsx @@ -0,0 +1,78 @@ +import { + ActionPanel, + CopyToClipboardAction, + Icon, + List, + OpenInBrowserAction, + render, +} from '@raycast/api' +import { randomUUID } from 'crypto' +import { useState } from 'react' +import { Crate, getCrates } from './api' +import { useDebouncedCallback } from 'use-debounce' + +render(
) + +function Main(): JSX.Element { + const [crates, setCrates] = useState([]) + const [loading, setLoading] = useState(false) + const debounced = useDebouncedCallback(async (v) => { + setLoading(true) + setCrates(await getCrates(v)) + setLoading(false) + }, 500) + + return ( + debounced(v)} + searchBarPlaceholder="Search for a crate..." + > + {crates.map((c) => { + const id = c.name + randomUUID() + return ( + + + {c.documentationURL ? ( + + ) : ( + <> + )} + {c.homepageURL ? ( + + ) : ( + <> + )} + {c.repositoryURL ? ( + + ) : ( + <> + )} + + } + /> + ) + })} + + ) +} diff --git a/extensions/cratecast/tsconfig.json b/extensions/cratecast/tsconfig.json new file mode 100644 index 0000000000000..6bd85946e9e77 --- /dev/null +++ b/extensions/cratecast/tsconfig.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 16", + "include": ["src/**/*"], + "compilerOptions": { + "lib": ["es2020"], + "module": "commonjs", + "target": "es2020", + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx" + } +}