├── src ├── layouts │ └── index.ts ├── components │ ├── shared │ │ ├── index.ts │ │ └── opengraph │ │ │ ├── types.ts │ │ │ └── index.tsx │ ├── index.ts │ └── modules │ │ ├── index.ts │ │ ├── table │ │ ├── types.ts │ │ └── index.tsx │ │ └── search │ │ ├── search.module.sass │ │ └── index.tsx ├── stores │ ├── index.ts │ └── data.ts ├── services │ ├── index.ts │ └── flags.ts ├── styles │ └── global.sass ├── models │ └── index.ts └── pages │ ├── _document.tsx │ ├── _app.tsx │ └── index.tsx ├── __mocks__ └── styleMock.js ├── .pnpm-debug.log ├── .gitignore ├── public ├── cover.png ├── cover.psd └── bun.svg ├── postcss.config.js ├── .prettierrc ├── next-env.d.ts ├── jest.setup.js ├── tailwind.config.js ├── __tests__ └── page.specs.tsx ├── .github └── workflows │ └── pr.yml ├── .eslintrc.js ├── README.md ├── next.config.js ├── package.json ├── jest.config.js ├── tools └── withPwa.js └── tsconfig.json /src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | -------------------------------------------------------------------------------- /src/components/shared/index.ts: -------------------------------------------------------------------------------- 1 | export {} -------------------------------------------------------------------------------- /src/stores/index.ts: -------------------------------------------------------------------------------- 1 | export { data, symbol } from './data' 2 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | // ? For mocking styles file 2 | module.exports = {} -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | export { isProduction, isServer } from './flags' 2 | -------------------------------------------------------------------------------- /.pnpm-debug.log: -------------------------------------------------------------------------------- 1 | { 2 | "0 debug pnpm:scope": { 3 | "selected": 1 4 | } 5 | } -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { CollapsibleTable, Search } from './modules' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | out 4 | .DS_Store 5 | yarn-error.log 6 | .vscode 7 | -------------------------------------------------------------------------------- /public/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SaltyAom/is-bun-ready/HEAD/public/cover.png -------------------------------------------------------------------------------- /public/cover.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SaltyAom/is-bun-ready/HEAD/public/cover.psd -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/components/modules/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CollapsibleTable } from './table' 2 | export { default as Search } from './search' 3 | -------------------------------------------------------------------------------- /src/services/flags.ts: -------------------------------------------------------------------------------- 1 | export const isProduction = process.env.NODE_ENV === 'production' 2 | export const isServer = typeof window === 'undefined' 3 | -------------------------------------------------------------------------------- /src/components/modules/table/types.ts: -------------------------------------------------------------------------------- 1 | export type Sort = 'index' | 'name' | 'supports' | 'lastUpdate' 2 | 3 | export type SortBy = `${Sort}.${'asc' | 'desc'}` 4 | -------------------------------------------------------------------------------- /src/components/modules/search/search.module.sass: -------------------------------------------------------------------------------- 1 | .search-outer > div > div > div 2 | @apply bg-white 3 | 4 | .search-inner > input 5 | @apply bg-transparent border-transparent focus:border-transparent focus:ring-0 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "trailingComma": "none", 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": false 9 | } 10 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /src/styles/global.sass: -------------------------------------------------------------------------------- 1 | @tailwind base 2 | @tailwind utilities 3 | @tailwind variants 4 | @tailwind components 5 | 6 | * 7 | box-sizing: border-box 8 | 9 | body 10 | background-color: #f9faff 11 | 12 | @apply font-sans 13 | @apply dark:bg-gray-800 14 | -------------------------------------------------------------------------------- /src/models/index.ts: -------------------------------------------------------------------------------- 1 | export interface Item { 2 | name: string 3 | type: 'server' | 'frontend' | 'database' | 'utility' | 'others' 4 | supports: 'full' | 'partial' | 'none' 5 | lastUpdate: string 6 | detail?: string 7 | bunVersion: string 8 | link: string 9 | } 10 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | require('@testing-library/jest-dom') 2 | 3 | /* Mock jest dynamic */ 4 | jest.mock('next/dynamic', () => () => { 5 | const LoadableComponent = () => null 6 | LoadableComponent.displayName = 'LoadableComponent' 7 | LoadableComponent.preload = jest.fn() 8 | return LoadableComponent; 9 | }); -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | content: ['src/**/*.{jsx,tsx,js,ts}'], 4 | darkMode: 'class', 5 | theme: { 6 | extend: {} 7 | }, 8 | variants: { 9 | extend: {} 10 | }, 11 | plugins: [ 12 | require('@tailwindcss/aspect-ratio'), 13 | require('@tailwindcss/forms') 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /__tests__/page.specs.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | import Landing from '@app/index' 6 | 7 | import { render } from '@testing-library/react' 8 | 9 | describe('App', () => { 10 | it('renders without crashing', () => { 11 | const { baseElement } = render() 12 | 13 | expect(baseElement).toBeTruthy() 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /src/components/shared/opengraph/types.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent } from 'react' 2 | 3 | export interface OpenGraphProps { 4 | canonical: string 5 | title: string 6 | alternativeTitle?: string[] 7 | description: string 8 | author?: string 9 | icon: string 10 | image: { 11 | src: string 12 | width: number 13 | height: number 14 | } 15 | name?: string 16 | twitterDevAccount?: string 17 | id?: number 18 | } 19 | 20 | export type OpenGraphComponent = FunctionComponent 21 | -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-danger */ 2 | import Document, { Html, Head, Main, NextScript } from 'next/document' 3 | 4 | class OpenerDocument extends Document { 5 | render() { 6 | return ( 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | ) 15 | } 16 | } 17 | 18 | export default OpenerDocument 19 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from 'next/app' 2 | 3 | import OpenGraph from '@shared/opengraph' 4 | 5 | import '@styles/global.sass' 6 | 7 | export default function App({ Component, pageProps }: AppProps) { 8 | return ( 9 | <> 10 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Checks 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | 7 | test: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | node-version: [14.x] 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: "Init: Summon NodeJS" 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: ${{ matrix.node-version }} 18 | - name: "Init: Install yarn" 19 | run: | 20 | curl -o- -L https://yarnpkg.com/install.sh | bash 21 | - name: "Cache: node_modules" 22 | uses: actions/cache@v1 23 | with: 24 | path: node_modules 25 | key: ${{ runner.OS }}-node-${{ hashFiles('**/yarn.lock') }} 26 | restore-keys: | 27 | ${{ runner.OS }}-node-${{ env.cache-name }}- 28 | ${{ runner.OS }}-node- 29 | - name: "Init: Install dependencies" 30 | run: | 31 | yarn install 32 | - name: "Test: Run test suite" 33 | run: | 34 | yarn test 35 | - name: "Test: Code linting" 36 | run: | 37 | yarn test:lint 38 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true 5 | }, 6 | extends: [ 7 | 'plugin:react/recommended', 8 | 'airbnb', 9 | 'eslint:recommended', 10 | 'eslint-config-prettier' 11 | ], 12 | parser: '@typescript-eslint/parser', 13 | parserOptions: { 14 | ecmaFeatures: { 15 | jsx: true 16 | }, 17 | ecmaVersion: 12, 18 | sourceType: 'module' 19 | }, 20 | plugins: ['react', '@typescript-eslint'], 21 | rules: { 22 | 'import/no-unresolved': 0, 23 | 'import/prefer-default-export': 0, 24 | 'prefer-const': 0, 25 | 'react/jsx-filename-extension': [1, { extensions: ['.jsx', '.tsx'] }], 26 | 'react/jsx-props-no-spreading': 0, 27 | 'react/prop-types': 0, 28 | 'react/react-in-jsx-scope': 0, 29 | 'react/jsx-indent': 0, 30 | 'react/jsx-indent-props': 0, 31 | 'react/jsx-one-expression-per-line': 0, 32 | 'react/jsx-closing-tag-location': 0, 33 | 'react/destructuring-assignment': 0, 34 | 'jsx-a11y/anchor-is-valid': 0, 35 | 'import/extensions': 0, 36 | 'consistent-return': 0, 37 | 'prefer-template': 0, 38 | 'arrow-body-style': 0 39 | }, 40 | ignorePatterns: [ 41 | '__tests__', 42 | '__mocks__', 43 | 'next.config.js', 44 | 'jest.config.js', 45 | 'jest.setup.js', 46 | 'postcss.config.js', 47 | 'tailwind.config.js' 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Is Bun Ready 2 | A community guide for tracking Bun supports library / framework 3 | 4 | ## Contribute 5 | To add more library: 6 | - fork 7 | - edit `src/stores/data.ts` 8 | - submit PR 9 | 10 | The data must follow the [type definition of Item](https://github.com/saltyaom/is-bun-ready/tree/main/src/models/index.ts) which consists of: 11 | - name 12 | - Name of the library / framework 13 | - type: 14 | - Should be one of the following: 15 | - 'server' 16 | - 'frontend' 17 | - 'database' 18 | - 'utility' 19 | - 'others' 20 | - supports: 21 | - Should be one of the following 22 | - 'full' - is fully supports Bun and should work without problems 23 | - 'partial' - can use with Bun but not ready for production, some feature is not available 24 | - 'none' - can't use with Bun yet 25 | - lastUpdate 26 | - The date which add / edit the library / framework 27 | - detail 28 | - Note or explanation of the current status 29 | - Can be Markdown 30 | - link 31 | - Link to the repository / documentaion of the library / framework 32 | 33 | Example: 34 | The following is representation for Express: 35 | ```typescript 36 | const data = [ 37 | { 38 | name: 'Express', 39 | type: 'web server', 40 | supports: 'partial', 41 | lastUpdate: '22 Dec 2022', 42 | bunVersion: '0.2.1', 43 | detail: `Work but not fully optimized yet, see [Bun HTTP Benchmark](https://github.com/SaltyAom/bun-http-framework-benchmark) for web framework benchmark.`, 44 | link: 'https://github.com/expressjs/express' 45 | } 46 | ] 47 | ``` 48 | -------------------------------------------------------------------------------- /src/components/modules/search/index.tsx: -------------------------------------------------------------------------------- 1 | import { data } from '@stores' 2 | 3 | import { Autocomplete, Chip, TextField } from '@mui/material' 4 | 5 | import styles from './search.module.sass' 6 | 7 | export default function SearchInput({ 8 | value, 9 | set 10 | }: { 11 | value: string[] 12 | // eslint-disable-next-line no-unused-vars 13 | set: (newValue: string[]) => void 14 | }) { 15 | return ( 16 |
17 | set(newValue)} 22 | renderTags={(_, getTagProps) => 23 | value.map((option: string, index: number) => ( 24 | 30 | )) 31 | } 32 | options={data.map(({ name }) => name)} 33 | renderInput={(params) => ( 34 | 44 | )} 45 | /> 46 |
47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@mui/material' 2 | 3 | import { CollapsibleTable } from '@components' 4 | 5 | export default function Index() { 6 | return ( 7 | <> 8 |
9 |
10 | Bun icon 15 |
16 |

17 | Is Bun ready? 18 |

19 |

20 | Community guide for tracking Bun supports library / 21 | framework 22 |

23 |
24 | Not find what you're looking for?{' '} 25 | 28 |
29 |
30 |
31 | 32 |

33 | ✅ Fully compatible 34 |
35 | ⚠️ Partial support, not all feature might work 36 |
37 | 🚫 Not working 38 |

39 |
40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const { join } = require('path') 2 | 3 | const withPwa = require('next-pwa') 4 | const withAnalyze = require('@next/bundle-analyzer')({ 5 | enabled: process.env.ANALYZE === 'true' 6 | }) 7 | 8 | const withPlugins = require('next-compose-plugins') 9 | const pwaConfig = require('./tools/withPwa') 10 | 11 | module.exports = withPlugins( 12 | [ 13 | [withPwa, pwaConfig] 14 | // [withAnalyze] 15 | ], 16 | { 17 | swcMinify: true, 18 | async rewrites() { 19 | return [ 20 | { 21 | source: '/service-worker.js', 22 | destination: '/_next/static/service-worker.js' 23 | } 24 | ] 25 | }, 26 | images: { 27 | deviceSizes: [640, 750, 828, 1080], 28 | imageSizes: [16, 32, 48, 64, 96], 29 | path: '/_next/image', 30 | loader: 'default' 31 | }, 32 | webpack(config, options) { 33 | config.resolve.alias = { 34 | ...config.resolve.alias, 35 | '@app': join(__dirname, 'src/app'), 36 | '@pages': join(__dirname, 'src/pages'), 37 | '@layouts': join(__dirname, 'src/layouts'), 38 | '@components': join(__dirname, 'src/components'), 39 | '@shared': join(__dirname, 'src/components/shared'), 40 | '@modules': join(__dirname, 'src/components/modules'), 41 | '@styles': join(__dirname, 'src/styles'), 42 | '@services': join(__dirname, 'src/services'), 43 | '@models': join(__dirname, 'src/models'), 44 | '@stores': join(__dirname, 'src/stores'), 45 | '@public': join(__dirname, 'public'), 46 | '@': __dirname 47 | } 48 | 49 | return config 50 | } 51 | } 52 | ) 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-starter", 3 | "version": "0.5.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "next", 8 | "build": "NODE_ENV=production next build && next export", 9 | "start": "NODE_ENV=production next start", 10 | "analyze": "ANALYZE=true next build", 11 | "export": "next export", 12 | "test": "jest", 13 | "lint": "eslint 'src/**/*.{ts,tsx}'" 14 | }, 15 | "dependencies": { 16 | "@emotion/react": "^11.10.5", 17 | "@emotion/styled": "^11.10.5", 18 | "@mui/icons-material": "^5.11.0", 19 | "@mui/material": "^5.11.1", 20 | "jotai": "^1.12.0", 21 | "next": "^13.0.0", 22 | "next-pwa": "^5.6.0", 23 | "react": "^18.2.0", 24 | "react-dom": "^18.2.0", 25 | "react-markdown": "^8.0.4", 26 | "tailwindcss": "^3.2.1" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.19.6", 30 | "@next/bundle-analyzer": "^13.0.0", 31 | "@tailwindcss/aspect-ratio": "^0.4.2", 32 | "@tailwindcss/forms": "^0.5.3", 33 | "@testing-library/dom": "^8.19.0", 34 | "@testing-library/jest-dom": "^5.16.5", 35 | "@testing-library/react": "^13.4.0", 36 | "@types/jest": "^29.2.0", 37 | "@types/node": "^18.11.5", 38 | "@types/react": "^18.0.23", 39 | "@typescript-eslint/eslint-plugin": "^5.41.0", 40 | "@typescript-eslint/parser": "^5.41.0", 41 | "autoprefixer": "^10.4.12", 42 | "babel-jest": "^29.2.2", 43 | "babel-plugin-module-resolver": "^4.1.0", 44 | "eslint": "^8.26.0", 45 | "eslint-config-airbnb": "^19.0.4", 46 | "eslint-config-next": "^13.0.0", 47 | "eslint-config-prettier": "^8.5.0", 48 | "eslint-plugin-import": "^2.26.0", 49 | "eslint-plugin-jsx-a11y": "^6.6.1", 50 | "eslint-plugin-react": "^7.31.10", 51 | "eslint-plugin-react-hooks": "^4.6.0", 52 | "eslint-react": "^0.0.4", 53 | "jest": "^29.2.2", 54 | "jest-environment-jsdom": "^29.2.2", 55 | "next-compose-plugins": "^2.2.1", 56 | "postcss": "^8.4.18", 57 | "postcss-preset-env": "^7.8.2", 58 | "sass": "^1.55.0", 59 | "typescript": "^4.8.4", 60 | "webpack": "5.74.0" 61 | }, 62 | "browserslist": "> 0.6%, not dead, not IE 11" 63 | } 64 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: [''], 3 | setupFilesAfterEnv: ['/jest.setup.js'], 4 | testPathIgnorePatterns: ['/.next/', '/node_modules/'], 5 | moduleNameMapper: { 6 | '\\.(css|scss|less|sass)$': '/__mocks__/styleMock.js', 7 | '^@app(.*)$': '/src/app$1', 8 | '^@layouts(.*)$': '/src/layouts$1', 9 | '^@components(.*)$': '/src/components$1', 10 | '^@shared(.*)$': '/src/shared$1', 11 | '^@modules(.*)$': '/src/modules$1', 12 | '^@styles(.*)$': '/src/styles$1', 13 | '^@services(.*)$': '/src/services$1', 14 | '^@models(.*)$': '/src/models$1', 15 | '^@stores(.*)$': '/src/stores$1', 16 | '^@public(.*)$': '/public$1', 17 | '^@/(.*)$': '/$1', 18 | }, 19 | transform: { 20 | // Use babel-jest to transpile tests with the next/babel preset 21 | // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object 22 | '^.+\\.(js|jsx|ts|tsx)$': [ 23 | 'babel-jest', 24 | { 25 | presets: ['next/babel'], 26 | plugins: [ 27 | [ 28 | 'module-resolver', 29 | { 30 | root: ['./'], 31 | alias: { 32 | '@app': './src/app', 33 | '@layouts': './src/layouts', 34 | '@components': './src/components', 35 | '@shared': './src/components/shared', 36 | '@modules': './src/components/modules', 37 | '@styles': './src/styles', 38 | '@services': './src/services', 39 | '@models': './src/models', 40 | '@stores': './src/stores', 41 | '@public': './public', 42 | '@': './' 43 | } 44 | } 45 | ] 46 | ] 47 | } 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/components/shared/opengraph/index.tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import { useRouter } from 'next/router' 3 | 4 | import type { OpenGraphComponent } from './types' 5 | 6 | // eslint-disable-next-line react/function-component-definition 7 | const OpenGraph: OpenGraphComponent = ({ 8 | canonical, 9 | title, 10 | alternativeTitle = [], 11 | description, 12 | author = '', 13 | icon, 14 | image = { 15 | src: '', 16 | width: 1920, 17 | height: 1080 18 | }, 19 | name = title, 20 | twitterDevAccount = '@SaltyAom' 21 | }) => { 22 | let { asPath = '/' } = useRouter() ?? { asPath: '/' } 23 | 24 | return ( 25 | 26 | {title} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ) 62 | } 63 | 64 | export default OpenGraph 65 | -------------------------------------------------------------------------------- /tools/withPwa.js: -------------------------------------------------------------------------------- 1 | const Time = { 2 | second: 0, 3 | minute: 1, 4 | hour: 2, 5 | day: 3, 6 | week: 4, 7 | month: 5, 8 | year: 6 9 | } 10 | 11 | const getTime = (time, unit) => { 12 | switch (unit) { 13 | case unit.second: 14 | return time 15 | 16 | case unit.minute: 17 | return time * 60 18 | 19 | case unit.hour: 20 | return time * 3_600 21 | 22 | case unit.day: 23 | return time * 86_400 24 | 25 | case unit.week: 26 | return time * 604_800 27 | 28 | case unit.month: 29 | return time * 2_419_200 30 | 31 | case unit.year: 32 | return time * 29_030_400 33 | 34 | default: 35 | return time 36 | } 37 | } 38 | 39 | const pwaConfig = { 40 | dest: 'static/service-worker.js', 41 | runtimeCaching: [ 42 | { 43 | urlPattern: '/', 44 | handler: 'NetworkFirst', 45 | options: { 46 | cacheName: 'start-url' 47 | } 48 | }, 49 | { 50 | urlPattern: /^https?.*/, 51 | handler: 'NetworkFirst', 52 | options: { 53 | cacheName: 'https-calls', 54 | networkTimeoutSeconds: 15, 55 | expiration: { 56 | maxEntries: 150, 57 | maxAgeSeconds: getTime(6, Time.hour) 58 | }, 59 | cacheableResponse: { 60 | statuses: [0, 200] 61 | } 62 | } 63 | }, 64 | { 65 | urlPattern: /\/_next\/image\?url/i, 66 | handler: 'StaleWhileRevalidate', 67 | options: { 68 | cacheName: 'next-image-assets' 69 | } 70 | }, 71 | { 72 | urlPattern: /api/, 73 | handler: 'NetworkFirst', 74 | options: { 75 | cacheName: 'api', 76 | networkTimeoutSeconds: 15, 77 | expiration: { 78 | maxEntries: 150, 79 | maxAgeSeconds: getTime(1, Time.hour) 80 | }, 81 | cacheableResponse: { 82 | statuses: [0, 200] 83 | } 84 | } 85 | }, 86 | { 87 | urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i, 88 | handler: 'StaleWhileRevalidate', 89 | options: { 90 | cacheName: 'static-font-assets' 91 | } 92 | }, 93 | { 94 | urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i, 95 | handler: 'StaleWhileRevalidate', 96 | options: { 97 | cacheName: 'static-image-assets' 98 | } 99 | }, 100 | { 101 | urlPattern: /\.(?:js)$/i, 102 | handler: 'StaleWhileRevalidate', 103 | options: { 104 | cacheName: 'static-js-assets' 105 | } 106 | }, 107 | { 108 | urlPattern: /\.(?:css)$/i, 109 | handler: 'StaleWhileRevalidate', 110 | options: { 111 | cacheName: 'static-style-assets' 112 | } 113 | }, 114 | { 115 | urlPattern: /\.(?:json|xml|csv)$/i, 116 | handler: 'NetworkFirst', 117 | options: { 118 | cacheName: 'static-data-assets' 119 | } 120 | } 121 | ] 122 | } 123 | 124 | module.exports = pwaConfig 125 | -------------------------------------------------------------------------------- /public/bun.svg: -------------------------------------------------------------------------------- 1 | Bun Logo -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 7 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 8 | "lib": [ 9 | "DOM", 10 | "DOM.Iterable", 11 | "ESNext" 12 | ] /* Specify library files to be included in the compilation. */, 13 | // "allowJs": true, /* Allow javascript files to be compiled. */ 14 | // "checkJs": true, /* Report errors in .js files. */ 15 | "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, 16 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 17 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 18 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 19 | // "outFile": "./", /* Concatenate and emit output to single file. */ 20 | // "outDir": "./", /* Redirect output structure to the directory. */ 21 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 22 | // "composite": true, /* Enable project compilation */ 23 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 24 | // "removeComments": true, /* Do not emit comments to output. */ 25 | // "noEmit": true, /* Do not emit outputs. */ 26 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 27 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 28 | "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, 29 | /* Strict Type-Checking Options */ 30 | "strict": true /* Enable all strict type-checking options. */, 31 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 32 | // "strictNullChecks": true, /* Enable strict null checks. */ 33 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 34 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 35 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 36 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 37 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 38 | /* Additional Checks */ 39 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 40 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 41 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 42 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 43 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 44 | /* Module Resolution Options */ 45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | "baseUrl": "." /* Base directory to resolve non-absolute module names. */, 47 | "paths": { 48 | "@pages": ["src/pages/index.ts"], 49 | "@pages/*": ["src/pages/*"], 50 | "@layouts": ["src/layouts/index.ts"], 51 | "@layouts/*": ["src/layouts/*"], 52 | "@components": ["src/components/index.ts"], 53 | "@components/*": ["src/components/*"], 54 | "@shared": ["src/components/shared/index.ts"], 55 | "@shared/*": ["src/components/shared/*"], 56 | "@modules": ["src/components/modules/index.ts"], 57 | "@modules/*": ["src/components/modules/*"], 58 | "@styles/*": ["src/styles/*"], 59 | "@services": ["src/services/index.ts"], 60 | "@services/*": ["src/services/*"], 61 | "@models": ["src/models/index.ts"], 62 | "@models/*": ["src/models/*"], 63 | "@stores": ["src/stores/index.ts"], 64 | "@stores/*": ["src/stores/*"], 65 | "@public/*": ["public/*"], 66 | "@/*": ["*"] 67 | } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, 68 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 69 | // "typeRoots": [], /* List of folders to include type definitions from. */ 70 | // "types": [], /* Type declaration files to be included in compilation. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 72 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 73 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 74 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 75 | /* Source Map Options */ 76 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 77 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 78 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 79 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 80 | /* Experimental Options */ 81 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 82 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 83 | /* Advanced Options */ 84 | "skipLibCheck": true /* Skip type checking of declaration files. */, 85 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 86 | "types": ["node", "jest"], 87 | "allowJs": true, 88 | "noEmit": true, 89 | "moduleResolution": "node", 90 | "resolveJsonModule": true, 91 | "incremental": true, 92 | "plugins": [ 93 | { 94 | "name": "next" 95 | } 96 | ] 97 | }, 98 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 99 | "exclude": ["node_modules", "tailwind.config.js"] 100 | } 101 | -------------------------------------------------------------------------------- /src/stores/data.ts: -------------------------------------------------------------------------------- 1 | import { Item } from '@models' 2 | 3 | export const symbol: Record = { 4 | full: '✅', 5 | partial: '⚠️', 6 | none: '⛔️' 7 | } 8 | 9 | export const data: Item[] = [ 10 | { 11 | name: 'Express', 12 | type: 'server', 13 | supports: 'partial', 14 | lastUpdate: '22 Dec 2022', 15 | bunVersion: '0.2.1', 16 | detail: `Body parser, and querystring doesn't work yet, slower compared to native Bun framework, see [Bun HTTP Benchmark](https://github.com/SaltyAom/bun-http-framework-benchmark).\n\n 17 | It's recommended to use native Bun framework like [Elysia](https://github.com/elysiajs/elysia) or [Hono](https://github.com/honojs/hono) for now instead. 18 | `, 19 | link: 'https://github.com/expressjs/express' 20 | }, 21 | { 22 | name: 'Fastify', 23 | type: 'server', 24 | supports: 'none', 25 | lastUpdate: '22 Dec 2022', 26 | bunVersion: '-', 27 | link: 'https://github.com/fastify/fastify' 28 | }, 29 | { 30 | name: 'Nestjs', 31 | type: 'server', 32 | supports: 'partial', 33 | lastUpdate: '26 Apr 2023', 34 | bunVersion: '0.5.9', 35 | link: 'https://github.com/nestjs/nest', 36 | detail: `Running built code with \`bun dist/main.js\` works for now while not using complex code.\n\nYou can track Nestjs supports at [Bun#1641](https://github.com/oven-sh/bun/issues/1641)` 37 | }, 38 | { 39 | name: 'Koa', 40 | type: 'server', 41 | supports: 'partial', 42 | lastUpdate: '24 Dec 2022', 43 | bunVersion: '0.4.0', 44 | detail: `Body parser, and querystring doesn't work yet, slower compared to native Bun framework, see [Bun HTTP Benchmark](https://github.com/SaltyAom/bun-http-framework-benchmark).\n\n 45 | It's recommended to use native Bun framework like [Elysia](https://github.com/elysiajs/elysia) or [Hono](https://github.com/honojs/hono) for now instead. 46 | `, 47 | link: 'https://github.com/expressjs/express' 48 | }, 49 | { 50 | name: 'Hono', 51 | type: 'server', 52 | supports: 'full', 53 | lastUpdate: '22 Dec 2022', 54 | bunVersion: '0.1.10', 55 | detail: `Some plugin like [serve-static](https://https://honojs.dev/docs/builtin-middleware/serve-static/) and [JWT](https://honojs.dev/docs/builtin-middleware/jwt/) doesn't work with Bun yet.`, 56 | link: 'https://github.com/honojs/hono' 57 | }, 58 | { 59 | name: 'Elysia', 60 | type: 'server', 61 | supports: 'full', 62 | lastUpdate: '22 Dec 2022', 63 | bunVersion: '0.1.10', 64 | link: 'https://github.com/elysiajs/elysia' 65 | }, 66 | { 67 | name: 'React', 68 | type: 'frontend', 69 | supports: 'full', 70 | lastUpdate: '22 Dec 2022', 71 | bunVersion: '0.2.1', 72 | link: 'https://github.com/facebook/react' 73 | }, 74 | { 75 | name: 'Nextjs', 76 | type: 'frontend', 77 | supports: 'partial', 78 | lastUpdate: '22 Dec 2022', 79 | bunVersion: '0.2.1', 80 | link: 'https://github.com/vercel/next.js' 81 | }, 82 | { 83 | name: 'Svelte', 84 | type: 'frontend', 85 | supports: 'partial', 86 | lastUpdate: '22 Dec 2022', 87 | bunVersion: '0.1.11', 88 | link: 'https://github.com/sveltejs/svelte', 89 | detail: 'Work thanks to community driver, [Svelte Adapter Bun](https://github.com/gornostay25/svelte-adapter-bun)' 90 | }, 91 | { 92 | name: 'Remix', 93 | type: 'frontend', 94 | supports: 'none', 95 | lastUpdate: '23 Feb 2023', 96 | bunVersion: '0.2.1', 97 | link: 'https://github.com/remix-run', 98 | detail: 'Blocking on `AbortableFetch` (https://github.com/elysiajs/elysia/issues/12)' 99 | }, 100 | { 101 | name: 'Prisma', 102 | type: 'database', 103 | supports: 'none', 104 | lastUpdate: '22 Dec 2022', 105 | bunVersion: '-', 106 | link: 'https://github.com/prisma/prisma' 107 | }, 108 | { 109 | name: 'pg', 110 | type: 'database', 111 | supports: 'full', 112 | lastUpdate: '23 Feb 2023', 113 | bunVersion: '0.5.2', 114 | link: 'https://github.com/brianc/node-postgres' 115 | }, 116 | { 117 | name: 'postgres', 118 | type: 'database', 119 | supports: 'full', 120 | lastUpdate: '19 Jan 2023', 121 | bunVersion: '0.5.0', 122 | link: 'https://github.com/porsager/postgres' 123 | }, 124 | { 125 | name: 'MySQL (mysql2)', 126 | type: 'database', 127 | supports: 'full', 128 | lastUpdate: '19 Jan 2023', 129 | bunVersion: '0.5.0', 130 | link: 'https://github.com/brianc/node-postgres' 131 | }, 132 | { 133 | name: 'Mongoose', 134 | type: 'database', 135 | supports: 'full', 136 | lastUpdate: '23 Feb 2023', 137 | bunVersion: '0.5.2', 138 | link: 'https://github.com/Automattic/mongoose' 139 | }, 140 | { 141 | name: 'MongoDB', 142 | type: 'database', 143 | supports: 'full', 144 | lastUpdate: '23 Feb 2023', 145 | bunVersion: '0.5.2', 146 | link: 'https://github.com/mongodb/node-mongodb-native' 147 | }, 148 | { 149 | name: 'Firebase', 150 | type: 'database', 151 | supports: 'none', 152 | lastUpdate: '23 Feb 2023', 153 | bunVersion: '-', 154 | link: 'https://github.com/firebase/firebase-js-sdk', 155 | detail: 'Missing `http2` package' 156 | }, 157 | { 158 | name: 'Supabase', 159 | type: 'database', 160 | supports: 'partial', 161 | lastUpdate: '23 Feb 2023', 162 | bunVersion: '-', 163 | link: 'https://github.com/supabase/supabase-js' 164 | }, 165 | { 166 | name: 'Cassandra', 167 | type: 'database', 168 | supports: 'none', 169 | lastUpdate: '24 Dec 2022', 170 | bunVersion: '-', 171 | link: 'https://github.com/datastax/nodejs-driver', 172 | detail: 'Blocking on `vm`' 173 | }, 174 | { 175 | name: 'ScyllaDB', 176 | type: 'database', 177 | supports: 'none', 178 | lastUpdate: '24 Dec 2022', 179 | bunVersion: '-', 180 | link: 'https://github.com/datastax/nodejs-driver', 181 | detail: 'ScyllaDB use same driver as [Cassandra](https://github.com/datastax/nodejs-driver)' 182 | }, 183 | { 184 | name: 'GraphQL Yoga', 185 | type: 'utility', 186 | supports: 'full', 187 | lastUpdate: '24 Dec 2022', 188 | bunVersion: '0.2.1', 189 | link: 'https://github.com/dotansimha/graphql-yoga' 190 | }, 191 | { 192 | name: 'GraphQL Mesh', 193 | type: 'utility', 194 | supports: 'full', 195 | lastUpdate: '24 Dec 2022', 196 | bunVersion: '0.2.1', 197 | link: 'https://github.com/urigo/graphql-mesh' 198 | }, 199 | { 200 | name: 'Jose', 201 | type: 'utility', 202 | supports: 'full', 203 | lastUpdate: '22 Dec 2022', 204 | bunVersion: '0.2.1', 205 | link: 'https://github.com/panva/jose' 206 | }, 207 | { 208 | name: 'Lyra', 209 | type: 'utility', 210 | supports: 'full', 211 | lastUpdate: '22 Dec 2022', 212 | bunVersion: '0.2.1', 213 | link: 'https://github.com/lyrasearch/lyra' 214 | }, 215 | { 216 | name: 'Cronner', 217 | type: 'utility', 218 | supports: 'full', 219 | lastUpdate: '22 Dec 2022', 220 | bunVersion: '0.2.1', 221 | link: 'https://github.com/hexagon/croner' 222 | }, 223 | { 224 | name: 'Shumai', 225 | type: 'utility', 226 | supports: 'full', 227 | lastUpdate: '22 Dec 2022', 228 | bunVersion: '0.1.11', 229 | link: 'https://github.com/facebookresearch/shumai' 230 | }, 231 | { 232 | name: 'Windows', 233 | type: 'others', 234 | supports: 'none', 235 | lastUpdate: '24 Dec 2022', 236 | bunVersion: '-', 237 | link: 'https://www.microsoft.com/en-us/windows', 238 | detail: `You can track Windows supports at [Bun#43](https://github.com/oven-sh/bun/issues/43)` 239 | }, 240 | { 241 | name: 'MacOS', 242 | type: 'others', 243 | supports: 'full', 244 | lastUpdate: '24 Dec 2022', 245 | bunVersion: '0.1.0', 246 | link: 'https://www.apple.com/macos' 247 | }, 248 | { 249 | name: 'Linux', 250 | type: 'others', 251 | supports: 'full', 252 | lastUpdate: '24 Dec 2022', 253 | bunVersion: '0.1.0', 254 | link: 'https://en.wikipedia.org/wiki/Linux' 255 | } 256 | ] 257 | -------------------------------------------------------------------------------- /src/components/modules/table/index.tsx: -------------------------------------------------------------------------------- 1 | import { ChangeEvent, useState } from 'react' 2 | 3 | import { 4 | Box, 5 | Collapse, 6 | IconButton, 7 | TablePagination, 8 | Table, 9 | TableBody, 10 | TableCell, 11 | TableContainer, 12 | TableHead, 13 | TableRow, 14 | Paper, 15 | TableSortLabel 16 | } from '@mui/material' 17 | 18 | import { 19 | KeyboardArrowDown as KeyboardArrowDownIcon, 20 | KeyboardArrowUp as KeyboardArrowUpIcon, 21 | Launch as LaunchIcon 22 | } from '@mui/icons-material' 23 | 24 | import Markdown from 'react-markdown' 25 | import type { NormalComponents } from 'react-markdown/lib/complex-types' 26 | import type { SpecialComponents } from 'react-markdown/lib/ast-to-react' 27 | 28 | import { Search } from '@components' 29 | import { symbol, data as rows } from '@stores' 30 | import type { Item } from '@models' 31 | import type { Sort, SortBy } from './types' 32 | 33 | const components: Partial> = { 34 | a(props) { 35 | return ( 36 | // eslint-disable-next-line jsx-a11y/anchor-has-content 37 | 38 | ) 39 | } 40 | } 41 | 42 | function Row({ 43 | name, 44 | type, 45 | supports, 46 | bunVersion, 47 | lastUpdate, 48 | detail, 49 | link 50 | }: Item) { 51 | const [open, setOpen] = useState(false) 52 | 53 | return ( 54 | <> 55 | *': { borderBottom: 'unset' } }}> 56 | 57 | {detail ? ( 58 | setOpen(!open)} 62 | className="h-8" 63 | > 64 | {open ? ( 65 | 66 | ) : ( 67 | 68 | )} 69 | 70 | ) : ( 71 |
72 | )} 73 | 74 | 75 | {name} 76 | 77 | {symbol[supports]} 78 | 79 | {type} 80 | 81 | {bunVersion} 82 | {lastUpdate} 83 | 84 | 85 | 86 | 87 | 88 | 89 | {detail && ( 90 | 91 | 95 | 96 | 97 | 98 | {detail} 99 | 100 | 101 | 102 | 103 | 104 | )} 105 | 106 | ) 107 | } 108 | 109 | const handleSort = ({ 110 | name, 111 | orderBy, 112 | setOrderBy 113 | }: { 114 | name: Sort 115 | orderBy: string 116 | // eslint-disable-next-line no-unused-vars 117 | setOrderBy: (newValue: SortBy) => void 118 | }) => { 119 | return { 120 | active: orderBy === `${name}.asc`, 121 | direction: (orderBy === `${name}.asc` ? 'asc' : 'desc') as 122 | | 'asc' 123 | | 'desc', 124 | onClick: () => { 125 | if (orderBy === `${name}.asc`) setOrderBy(`${name}.desc`) 126 | else if (orderBy === `${name}.desc`) setOrderBy('index.asc') 127 | else setOrderBy(`${name}.asc`) 128 | } 129 | } 130 | } 131 | 132 | export default function CollapsibleTable() { 133 | const [search, setSearch] = useState([]) 134 | const [orderBy, setOrderBy] = useState('index.asc') 135 | 136 | const [page, setPage] = useState(0) 137 | const [rowsPerPage, setRowsPerPage] = useState(25) 138 | 139 | const handleChangePage = (event: unknown, newPage: number) => { 140 | setPage(newPage) 141 | } 142 | 143 | const handleChangeRowsPerPage = (event: ChangeEvent) => { 144 | setRowsPerPage(+event.target.value) 145 | setPage(0) 146 | } 147 | 148 | return ( 149 | 150 | 151 | 152 | 153 | 154 | 155 | 162 | 163 | 164 | 171 | Library 172 | 173 | 174 | 175 | 182 | Supports 183 | 184 | 185 | Type 186 | Tested on 187 | 188 | 195 | Last update 196 | 197 | 198 | Link 199 | 200 | 201 | 202 | {rows 203 | .filter( 204 | ({ name }) => 205 | !search.length || 206 | search.find((request) => request.includes(name)) 207 | ) 208 | .sort((a, b) => { 209 | let [key, order] = orderBy.split('.') as [ 210 | Sort, 211 | 'asc' | 'desc' 212 | ] 213 | 214 | if (key === 'index') return order === 'asc' ? 1 : -1 215 | 216 | if (key === 'lastUpdate') 217 | if (order === 'asc') 218 | return ( 219 | new Date(a[key]).getTime() - 220 | new Date(b[key]).getTime() 221 | ) 222 | else 223 | return ( 224 | new Date(b[key]).getTime() - 225 | new Date(a[key]).getTime() 226 | ) 227 | 228 | if (order === 'asc') 229 | return a[key].localeCompare(b[key]) 230 | 231 | return b[key].localeCompare(a[key]) 232 | }) 233 | .slice( 234 | page * rowsPerPage, 235 | page * rowsPerPage + rowsPerPage 236 | ) 237 | .map((row) => ( 238 | 239 | ))} 240 | 241 |
242 | 251 |
252 | ) 253 | } 254 | --------------------------------------------------------------------------------