├── 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 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------