Closed54

bun+TurborepoでNext.js+Expo+Honoの環境構築をしてみる

こうちゅけこうちゅけ

経緯

学校のPBL実験とかいう面倒な強制共同開発をやる羽目になったから。

こうちゅけこうちゅけ

ゴール

  • Tuborepoの構成を理解する
  • eslint, pretter etc...の構築をする
  • actionsを実装
こうちゅけこうちゅけ

構成(予定)

Server

Hono + tRPC

Website

Next.js + ts + tailwindcss + storybook + etc...

Mobile

Expo

こうちゅけこうちゅけ

では恒例のcreateコマンドを

bunx create-turbo@latest
Log
>>> TURBOREPO

>>> Welcome to Turborepo! Let's get you set up with a new codebase.

? Where would you like to create your turborepo? shokupass
? Which package manager do you want to use? bun workspaces (beta)

Downloading files. This might take a moment.

>>> Created a new Turborepo with the following:

apps
 - apps/docs
 - apps/web
packages
 - packages/eslint-config
 - packages/typescript-config
 - packages/ui

Installing packages. This might take a couple of minutes.

>>> Success! Created a new Turborepo at "shokupass".
Inside that directory, you can run several commands:

  bun run build
     Build all apps and packages

  bun run dev
     Develop all apps and packages

  bun run lint
     Lint all apps and packages

Turborepo will cache locally by default. For an additional
speed boost, enable Remote Caching with Vercel by
entering the following command:

  bunx turbo login

We suggest that you begin by typing:

  cd shokupass
  bunx turbo login
こうちゅけこうちゅけ

あ、bun君色付け対応してないんだ...
プロジェクト名はshokupassになってるを

こうちゅけこうちゅけ

一旦動かしてみる

bun && bun dev
Log
Bun is a fast JavaScript runtime, package manager, bundler, and test runner. (1.0.14 (d8be3e51))

Usage: bun <command> [...flags] [...args]

Commands:
  run       ./my-script.ts       Execute a file with Bun
            lint                 Run a package.json script
  test                           Run unit tests with Bun
  x         nuxi                 Execute a package binary (CLI), installing if needed (bunx)
  repl                           Start a REPL session with Bun

  install                        Install dependencies for a package.json (bun i)
  add       elysia               Add a dependency to package.json (bun a)
  remove    tailwindcss          Remove a dependency from package.json (bun rm)
  update    backbone             Update outdated dependencies
  link      [<package>]          Register or link a local npm package
  unlink                         Unregister a local npm package
  pm <subcommand>                Additional package management utilities

  build     ./a.ts ./b.jsx       Bundle TypeScript & JavaScript into a single file

  init                           Start an empty Bun project from a blank template
  create    elysia               Create a new project from a template (bun c)
  upgrade                        Upgrade to latest version of Bun.
  <command> --help               Print help text for command.

Learn more about Bun:            https://bun.sh/docs
Join our Discord community:      https://bun.sh/discord
$ turbo dev
• Packages in scope: @repo/eslint-config, @repo/typescript-config, @repo/ui, docs, web
• Running dev in 5 packages
• Remote caching disabled
docs:dev: cache bypass, force executing 01b2860241aed340
web:dev: cache bypass, force executing 31f1838a80245ae8
web:dev: $ next dev
docs:dev: $ next dev --port 3001
docs:dev:    ▲ Next.js 14.0.3
docs:dev:    - Local:        http://localhost:3001
docs:dev:
web:dev:    ▲ Next.js 14.0.3
web:dev:    - Local:        http://localhost:3000
web:dev:
docs:dev:  ✓ Ready in 4.5s
web:dev:  ✓ Ready in 4.5s
web:dev:  ○ Compiling / ...
web:dev:  ✓ Compiled / in 4.6s (517 modules)
docs:dev:  ○ Compiling / ...
docs:dev:  ✓ Compiled / in 4.7s (517 modules)
docs:dev:  ○ Compiling /favicon.ico ...
docs:dev:  ✓ Compiled /favicon.ico in 1482ms (513 modules)
こうちゅけこうちゅけ

依存関係を見る

bun turbo run build --graph

digraph {
	compound = "true"
	newrank = "true"
	subgraph "root" {
		"[root] @repo/eslint-config#build" -> "[root] ___ROOT___"
		"[root] @repo/typescript-config#build" -> "[root] ___ROOT___"
		"[root] @repo/ui#build" -> "[root] @repo/eslint-config#build"
		"[root] @repo/ui#build" -> "[root] @repo/typescript-config#build"
		"[root] docs#build" -> "[root] @repo/eslint-config#build"
		"[root] docs#build" -> "[root] @repo/typescript-config#build"
		"[root] docs#build" -> "[root] @repo/ui#build"
		"[root] web#build" -> "[root] @repo/eslint-config#build"
		"[root] web#build" -> "[root] @repo/typescript-config#build"
		"[root] web#build" -> "[root] @repo/ui#build"
	}
}
こうちゅけこうちゅけ

package.jsonの中身を見てみる

apps/web/package.json
{
  "name": "web",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --max-warnings 0"
  },
  "dependencies": {
    "@repo/ui": "*",
    "next": "^14.0.3",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@next/eslint-plugin-next": "^14.0.2",
    "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@types/eslint": "^8.44.7",
    "@types/node": "^17.0.12",
    "@types/react": "^18.0.22",
    "@types/react-dom": "^18.0.7",
    "eslint": "^8.53.0",
    "typescript": "^5.2.2"
  }
}

こうちゅけこうちゅけ

全てのpackage.jsonの@repo@shokupassへ変更して再度インストール

bun i
Log
bun i
bun install v1.0.14 (d8be3e51)
  🔍 @shokupass/ui [11/14]
error: package "@shokupass/ui" not found registry.npmjs.org/@shokupass%2fui 404
  🔍 @shokupass/eslint-config [12/14]
error: package "@shokupass/eslint-config" not found registry.npmjs.org/@shokupass%2feslint-config 404
  🔍 @shokupass/typescript-config [13/14]
error: package "@shokupass/typescript-config" not found registry.npmjs.org/@shokupass%2ftypescript-config 404
error: workspace dependency "@shokupass/eslint-config" not found

Searched in "./packages/eslint-config"

Workspace documentation: https://bun.sh/docs/install/workspaces


error: workspace dependency "@shokupass/typescript-config" not found

Searched in "./packages/typescript-config"

Workspace documentation: https://bun.sh/docs/install/workspaces


error: workspace dependency "@shokupass/eslint-config" not found

Searched in "./*"

Workspace documentation: https://bun.sh/docs/install/workspaces


error: workspace dependency "@shokupass/typescript-config" not found

Searched in "./*"

Workspace documentation: https://bun.sh/docs/install/workspaces


error: workspace dependency "@shokupass/eslint-config" not found

Searched in "./*"

Workspace documentation: https://bun.sh/docs/install/workspaces


error: workspace dependency "@shokupass/typescript-config" not found

Searched in "./*"

Workspace documentation: https://bun.sh/docs/install/workspaces


error: workspace dependency "@shokupass/ui" not found

Searched in "./*"

Workspace documentation: https://bun.sh/docs/install/workspaces

error: workspace dependency "@shokupass/typescript-config" not found

Searched in "./packages/typescript-config

ええ...

こうちゅけこうちゅけ

一旦node_modulesbun.lockbを消してインストール

Log
bun i
bun install v1.0.14 (d8be3e51)
 + @shokupass/ui@workspace:packages/ui
 + docs@workspace:apps/docs
 + web@workspace:apps/web
 + @shokupass/eslint-config@workspace:packages/eslint-config
 + @shokupass/typescript-config@workspace:packages/typescript-config
 + prettier@3.1.0
 + turbo@1.10.16

 589 packages installed [5.62s]

よし

こうちゅけこうちゅけ

特に変わったこともなく実装

File
packages/eslint-config/library.js
/** @type {import("eslint").Linter.Config} */
module.exports = {
  env: {
    node: true,
    es6: true,
  },
  extends: [
    "eslint:recommended",
    "prettier",
    "eslint-config-turbo",
    "plugin:n/recommended",
    "plugin:@typescript-eslint/recommended",
  ],
  plugins: ["only-warn", "import", "prettier", "promise", "@typescript-eslint"],
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
  },
  parser: "@typescript-eslint/parser",
  ignorePatterns: [
    // Ignore dotfiles
    ".*.js",
    "node_modules/",
    "dist/",
  ],
  rules: {
    "import/no-default-export": "error",
    "arrow-body-style": ["error", "as-needed"],
    "import/extensions": "off",
    "import/prefer-default-export": "off",
    "import/no-extraneous-dependencies": ["error", { packageDir: ["./"] }],
    "import/order": [
      "error",
      {
        pathGroups: [
          {
            pattern: "@/**",
            group: "internal",
            position: "before",
          },
          {
            pattern: "#**",
            group: "internal",
            position: "before",
          },
        ],
        alphabetize: {
          order: "asc",
        },
      },
    ],
  },
  overrides: [
    {
      files: ["./*"],
      rules: {
        "import/no-default-export": "off",
      },
    },
  ],
};
packages/eslint-config/react.js

/**
ほぼlibraryと同様
**/

packages/eslint-config/next.js

/**
ほぼlibraryと同様
**/

こうちゅけこうちゅけ

こうなった

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": ["@tsconfig/strictest/tsconfig.json"],
  "display": "Hono",
  "compilerOptions": {
    "lib": ["ESNext"],
    "module": "esnext",
    "target": "esnext",
    "moduleResolution": "bundler",
    "moduleDetection": "force",
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "composite": true,
    "allowSyntheticDefaultImports": true,

    "jsx": "react",
    "allowJs": true,
    "types": ["bun-types"],

    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  }
}
こうちゅけこうちゅけ
bun turbo gen workspace --name prettier-config --type package

>>> Add an empty workspace to "shokupass"

? Where should "prettier-config" be added? packages/prettier-config
? Add workspace dependencies to "prettier-config"? No

>>> Success! Created prettier-config at "packages/prettier-config"
こうちゅけこうちゅけ
tree -I node_modules -I .git -I .next --dirsfirst -a ./packages/prettier-config/
./packages/prettier-config/
├── README.md
└── package.json

1 directory, 2 files

READMEとpackage.jsonが生成された。

こうちゅけこうちゅけ
package.json
{
  "name": "prettier-config",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "echo 'Add dev script here'",
    "build": "echo 'Add build script here'",
    "test": "echo 'Add test script here'",
    "lint": "echo 'Add lint script here'"
  }
}
こうちゅけこうちゅけ

ゴリゴリと書いていく

.prettierrc.cjsを追加

/** @type {import('prettier').Config} */
module.exports = {
  printWidth: 120,
  tabWidth: 2,
  useTabs: false,
  semi: true,
  trailingComma: "all",
  endOfLine: "lf",
  plugins: ["prettier-plugin-tailwindcss"],
  arrowParens: "avoid",
};

package.jsonも変える

{
  "name": "@shokupass/prettier-config",
  "version": "0.0.0",
  "private": true,
  "main": "./.prettierrc.cjs",
  "type": "commonjs",
  "scripts": {
    "fmt": "prettier --write ."
  },
  "devDependencies": {
    "prettier": "^3.1.0",
    "prettier-plugin-tailwindcss": "^0.5.7"
  }
}
tree -I node_modules -I .git -I .next --dirsfirst -a ./packages/prettier-config/
./packages/prettier-config/
├── .prettierrc.cjs
├── README.md
└── package.json

1 directory, 3 files
こうちゅけこうちゅけ

そしたら導入したいディレクトリに.prettierrc.cjsを入れてpackage.jsonも変える

こうちゅけこうちゅけ

こんなかんじ

tree -I node_modules -I .git -I .next --dirsfirst -a ./packages/jest-config/
./packages/jest-config/
├── README.md
├── next.cjs
├── node.cjs
└── package.json

1 directory, 4 files
こうちゅけこうちゅけ

Tilwindcss

こうちゅけこうちゅけ
const plugin = require("tailwindcss/plugin");

module.exports = {
  mode: "jit",
  content: ["./src/**/*.{tsx,ts}"],
  theme: {
    screens: {
      tablet: "640px",
      laptop: "1024px",
      desktop: "1280px",
    },
    extend: {},
  },
  plugins: [
    plugin(function ({ addUtilities }) {
      const newUtilities = {
        ".scrollbar-hidden": {
          "-ms-overflow-style": "none",
          "scrollbar-width": "none",
          "&::-webkit-scrollbar": {
            display: "none",
          },
        },
      };
      addUtilities(newUtilities);
    }),
  ],
};
こうちゅけこうちゅけ

ここまでのpackageの中身

tree -I node_modules -I .git -I .next --dirsfirst -a ./packages/
./packages/
├── eslint-config
│   ├── README.md
│   ├── library.cjs
│   ├── next.cjs
│   ├── package.json
│   └── react.cjs
├── jest-config
│   ├── .turbo
│   │   └── turbo-lint.log
│   ├── README.md
│   ├── next.cjs
│   ├── node.cjs
│   └── package.json
├── postcss-config
│   ├── .turbo
│   │   └── turbo-build.log
│   ├── README.md
│   ├── index.cjs
│   └── package.json
├── prettier-config
│   ├── .prettierrc.cjs
│   ├── README.md
│   └── package.json
├── tailwind-config
│   ├── .turbo
│   │   └── turbo-build.log
│   ├── .eslintignore
│   ├── .eslintrc.cjs
│   ├── .prettierignore
│   ├── .prettierrc.cjs
│   ├── README.md
│   ├── input.css
│   ├── package.json
│   ├── tailwind.config.cjs
│   ├── tsconfig.json
│   └── utils.ts
├── typescript-config
│   ├── base.json
│   ├── hono.json
│   ├── nextjs.json
│   ├── package.json
│   ├── react-library.json
│   └── react-native.json
└── ui
    ├── .turbo
    │   └── turbo-lint.log
    ├── components
    │   ├── card.tsx
    │   └── code.tsx
    ├── turbo
    │   └── generators
    │       ├── templates
    │       │   └── component.hbs
    │       └── config.ts
    ├── .eslintrc.cjs
    ├── package.json
    ├── tsconfig.json
    ├── tsconfig.lint.json
    └── turbo.json
こうちゅけこうちゅけ

Next.js

こうちゅけこうちゅけ

create turbo後のApps配下は特にはいじってないのでこうなっている

./apps/
├── .turbo
│   └── cookies
├── docs
│   ├── .turbo
│   │   └── turbo-build.log
│   ├── src
│   │   ├── app
│   │   │   ├── favicon.ico
│   │   │   ├── globals.css
│   │   │   ├── layout.tsx
│   │   │   ├── page.module.css
│   │   │   └── page.tsx
│   │   └── public
│   │       ├── circles.svg
│   │       ├── next.svg
│   │       ├── turborepo.svg
│   │       └── vercel.svg
│   ├── .eslintignore
│   ├── .eslintrc.cjs
│   ├── .prettierrc.cjs
│   ├── README.md
│   ├── next-env.d.ts
│   ├── next.config.mjs
│   ├── package.json
│   └── tsconfig.json
└── web
    ├── .turbo
    │   ├── cookies
    │   └── turbo-build.log
    ├── src
    │   ├── app
    │   │   ├── favicon.ico
    │   │   ├── globals.css
    │   │   ├── layout.tsx
    │   │   ├── page.module.css
    │   │   └── page.tsx
    │   └── public
    │       ├── circles.svg
    │       ├── next.svg
    │       ├── turborepo.svg
    │       └── vercel.svg
    ├── .eslintignore
    ├── .eslintrc.cjs
    ├── .prettierignore
    ├── .prettierrc.cjs
    ├── README.md
    ├── next-env.d.ts
    ├── next.config.mjs
    ├── package.json
    └── tsconfig.json

14 directories, 37 files
こうちゅけこうちゅけ

b..bun君???

✔ What is your project named? … my-app
✔ Would you like to use TypeScript with this project? … No / Yes
✔ Would you like to use ESLint with this project? … No / Yes
✔ Would you like to use `src/` directory with this project? … No / Yes
✔ Would you like to use experimental `app/` directory with this project? … No / Yes
✔ What import alias would you like configured? … @website/*
Creating a new Next.js app in /Users/kousuke/Desktop/programs/typescript/turbo/shokupass/apps/my-app.

Using npm.

Installing dependencies:
- react
- react-dom
- next
- @next/font
- typescript
- @types/react
- @types/node
- @types/react-dom
- eslint
- eslint-config-next

npm ERR! code EUNSUPPORTEDPROTOCOL
npm ERR! Unsupported URL Type "workspace:": workspace:*

npm ERR! A complete log of this run can be found in: /Users/kousuke/.npm/_logs/2023-12-04T03_51_14_426Z-debug-0.log

Aborting installation.
  npm install --save-exact --save react react-dom next @next/font typescript @types/react @types/node @types/react-dom eslint eslint-config-next has failed.

こうちゅけこうちゅけ

しゃーないのでyarnで入れます

✔ What is your project named? … website
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
✔ What import alias would you like configured? … @website/*
Creating a new Next.js app in 

Using yarn.

Initializing project with template: app-tw


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- autoprefixer
- postcss
- tailwindcss
- eslint
- eslint-config-next

➤ YN0000: ┌ Resolution step
➤ YN0061: │ abab@npm:2.0.6 is deprecated: Use your platform's native atob() and btoa() methods instead
➤ YN0061: │ domexception@npm:4.0.0 is deprecated: Use your platform's native DOMException instead
➤ YN0032: │ fsevents@npm:2.3.3: Implicit dependencies on node-gyp are discouraged
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (pfb849), requested by @typescript-eslint/eslint-plugin
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p919fb), requested by @typescript-eslint/parser
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p9403b), requested by eslint-config-prettier
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p39d25), requested by eslint-plugin-import
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p33c32), requested by eslint-plugin-jsx-a11y
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (pf96cf), requested by eslint-plugin-react
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p6c576), requested by eslint-plugin-react-hooks
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p4d647), requested by eslint-config-airbnb-typescript
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p347ef), requested by eslint-config-airbnb
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p2ce99), requested by eslint-config-next
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p43b1e), requested by eslint-config-turbo
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p0bed1), requested by eslint-plugin-n
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p53956), requested by eslint-plugin-promise
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide eslint (p99462), requested by eslint-plugin-storybook
➤ YN0002: │ @shokupass/eslint-config@workspace:packages/eslint-config doesn't provide tailwindcss (pa7af5), requested by eslint-plugin-tailwindcss
➤ YN0002: │ @shokupass/jest-config@workspace:packages/jest-config doesn't provide react (pae3e2), requested by next
➤ YN0002: │ @shokupass/jest-config@workspace:packages/jest-config doesn't provide react-dom (p7ffa8), requested by next
➤ YN0002: │ @shokupass/jest-config@workspace:packages/jest-config doesn't provide typescript (pcedcd), requested by ts-jest
➤ YN0002: │ @turbo/gen@npm:1.10.16 doesn't provide @types/node (pe6832), requested by ts-node
➤ YN0002: │ @turbo/gen@npm:1.10.16 doesn't provide typescript (p0a547), requested by ts-node
➤ YN0000: │ Some peer dependencies are incorrectly met; run yarn explain peer-requirements <hash> for details, where <hash> is the six-letter p-prefixed code
➤ YN0000: └ Completed in 13s 927ms
➤ YN0000: ┌ Fetch step
➤ YN0013: │ yargs-parser@npm:21.1.1 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ yargs@npm:17.7.2 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ yn@npm:3.1.1 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ yocto-queue@npm:0.1.0 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ typescript@npm:5.3.2 can't be found in the cache and will be fetched from the remote registry
➤ YN0000: └ Completed in 31s 572ms
➤ YN0000: ┌ Link step
➤ YN0031: │ One or more node_modules have been detected and will be removed. This operation may take some time.
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0007: │ core-js-pure@npm:3.33.3 must be built because it never has been before or the last one failed
➤ YN0000: └ Completed in 5s 479ms
➤ YN0000: Done with warnings in 51s 121ms
Success! Created website
このスクラップは5ヶ月前にクローズされました