14
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

複数の Nuxt.js プロジェクトでコンポーネントやロジックなどを共有するために monorepo する

Last updated at Posted at 2021-01-17

ほぼチーム共有用ですが、一般的なノウハウなので Qiita に書きます。

私事ですが、Nuxt で作っている BtoBtoC サービスで「管理画面」と「その管理者を管理する管理画面」を用意する場合、ほぼ同じ画面構成・見た目という理由からリポジトリごとコピーして必要に応じて弄る、という方法がとられていました。

当たり前ですが、共通部分のコードに変更が必要になると、同じ変更を2回(実際のところ何回もコピーしたので2回じゃ済まないんですが...)やらないといけなくなっています。

あと地味にしんどいのが dependabot のお世話ですね。少し目を離すと彼が一番熱心なコミッターになるというのはあるあるです。

これを脱するため、monorepo にして共有できるものは共有しようという試みがこの記事です(移行のコストが地味にあって実現できるかは不明ですが...)

※余談ですが、共通化には罠もあるので取り扱いには注意ですね。

リポジトリ

元ネタ

https://medium.com/dailyjs/5-tips-for-sharing-code-between-nuxtjs-projects-6ffb5b7f8a25 の方法を使って、より実用的なかたちで書いたのがこの記事になります。

Nuxt プロジェクトはパッケージとして publish する必要はなく、ビルドしてデプロイできればよいです。

なので、一般的な monorepo とは違い、それぞれのパッケージをひとつにまとめて publish できるようにして lib とかを参照するのではなく、パッケージ間でソースコードをそのまま import するやり方をとります(もはやこれを monorepo というのは謎ですが)。

構造

スクリーンショット 2021-01-15 20.15.07.png

どういう開発体験か

プロジェクトAの開発サーバーを立ち上げてるときに、ベースプロジェクトにコンポーネントを追加すると、プロジェクトAのテンプレートHTMLですぐに使えてその変更がホットリロードされる、というとなんとなくいい感じというイメージが伝わるかなと思います。

Image from Gyazo

利用ライブラリなど(create-nuxt-app で ON にした機能+α)

  • TypeScript
  • ESLint
  • Prettier
  • Lint staged files
  • StyleLint
  • Commitlint
  • Jest
  • Semantic Pull Requests
  • Dependabot
  • Github Actions
  • Sass(@nuxtjs/style-resources も)
  • Composiiton API

それでは以下、手順。

yarn をセットアップ

yarn init

適当に入力。 package.json が出力される。

yarn workspaces を有効に

private: true にして、 workspaces を追加。

package.json
{
    ...,
+   "private": true,
+   "workspaces": [
+       "packages/*"
+   ]
}

lerna のインストール

追加しないと差分がうるさくなるので先に .gitignore を追加しておきます。あとで create-nuxt-app で書き出されるもので上書きするのでとりあえずこれだけで OK。

.gitignore
node_modules

ルートの pacakge.jsonlerna をインストール

yarn add -W -D lerna

lerna のセットアップ

--independent 設定で バージョニングが個別になります。オープンソースライブラリのような「メインとそのサブパッケージ」という構成でなければ independent で良いかと。

yarn lerna init --independent

lerna.json が書き出されます。

yarn workspaces を使うので以下のように書き換えます。

lerna.json
{
  "packages": [
    "packages/*"
  ],
+ "npmClient": "yarn",
+ "useWorkspaces": true,
  "version": "independent"
}

ベースプロジェクトの作成

ベースとなるプロジェクトを作成します。ここから Extend するかたちで他プロジェクトを作成します。

mkdir -p packages/base
cd packages/base

create-nuxt-app します。 create-nuxt-app のバージョンは v3.4.0 です。

yarn create nuxt-app

よしなにします。 今回は以下のように。
スクリーンショット 2021-01-14 14.17.05.png

ルートで git 管理するので、 Version control systemNone にします。

create-nuxt-app で生成される dependencies, devDependencies はすべてのプロジェクトで共通なので、そのままルートにコピペして、ベースプロジェクトからは削除します。ルートの package.json に既に記載してある lerna は残しましょう。

packages/base/package.json
{
  ...
  以下を全部ルートの package.json へ移動
  "dependencies": {
    "@nuxt/typescript-runtime": "^2.0.0",
    "core-js": "^3.6.5",
    "nuxt": "^2.14.6"
  },
  "devDependencies": {
    "@commitlint/cli": "^11.0.0",
    "@commitlint/config-conventional": "^11.0.0",
    "@nuxt/types": "^2.14.6",
    "@nuxt/typescript-build": "^2.0.3",
    "@nuxtjs/eslint-config": "^3.1.0",
    "@nuxtjs/eslint-config-typescript": "^3.0.0",
    "@nuxtjs/eslint-module": "^2.0.0",
    "@nuxtjs/stylelint-module": "^4.0.0",
    "@vue/test-utils": "^1.1.0",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^26.5.0",
    "eslint": "^7.10.0",
    "eslint-config-prettier": "^6.12.0",
    "eslint-plugin-nuxt": "^1.0.0",
    "eslint-plugin-prettier": "^3.1.4",
    "husky": "^4.3.0",
    "jest": "^26.5.0",
    "lint-staged": "^10.4.0",
    "prettier": "^2.1.2",
    "stylelint": "^13.7.2",
    "stylelint-config-prettier": "^8.0.2",
    "stylelint-config-standard": "^20.0.0",
    "ts-jest": "^26.4.1",
    "vue-jest": "^3.0.4"
  }
}

ちなみにこの状態(移動しなくても)でも依存パッケージはすべてルートの node_modules に入ります(ホイスティング)。どのプロジェクトもルートの node_modules を参照して動いているということを頭に入れておくと、後々良いかもしれません。

設定ファイルをルートに移す

base の設定ファイル郡を nuxt.config.jspackage.json だけになるように移動します。ルートが以下のようになります。

スクリーンショット 2021-01-14 14.21.34.png

base で必要な設定をする

ファイルのExtendやコマンドの修正が必要になります。

Typescript

tsconfig.json を親から extend します。

packages/base/tsconfig.json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "baseUrl": "."
  }
}

あとで Jest のためにまた弄ることになりますが、Jest 無しならこれで大丈夫なはずです。

ESLint

create-nuxt-app で作成したプロジェクトは lint スクリプトが eslint --ext .js,.vue --ignore-path .gitignore . になっています。

.gitignore は今はルートにあるので以下に変更します。

packages/base/package.json
{
 ...
-  "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
-  "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore", 
+  "lint:js": "eslint --ext .js,.vue --ignore-path ../../.gitignore .",
+  "lint:style": "stylelint **/*.{vue,css} --ignore-path ../../.gitignore",
 ...
}

コマンドは以下で実行します。

yarn lerna run lint --scope base

Jest

Jest に関しては、lerna ではなく Jest の projects 機能を使って実行します。

必要なライブラリをインストールします。@vue/cli-service を入れるのは、Vue プロジェクトで Jest だとカバレッジが正しくとれない問題の解決として、 jest コマンドの代わりに vue-cli-service test:unit を使うためです。

yarn lerna add -W -D @types/jest @vue/cli-service @vue/cli-plugin-unit-jest

まず、ルートの設定を以下にします。

jest.config.js
module.exports = {
  projects: ['<rootDir>/packages/*'],
}
package.json
{
  ...
+ "scripts": {
+   "test:base": "vue-cli-service test:unit --projects packages/base",
+   "test": "vue-cli-service test:unit"
+ },
  ...
}

それぞれのプロジェクトで Extend するためのベースの設定ファイルを書きます(create-nuxt-app で書き出されたものを修正したもの)。

jest.config.base.js
module.exports = {
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '^~/(.*)$': '<rootDir>/$1',
    '^vue$': 'vue/dist/vue.common.js',
  },
  moduleFileExtensions: ['ts', 'js', 'vue', 'json'],
  transform: {
    '^.+\\.ts$': 'ts-jest',
    '^.+\\.js$': 'babel-jest',
    '.*\\.(vue)$': 'vue-jest',
  },
  collectCoverage: true,
  collectCoverageFrom: ['<rootDir>/components/**/*.vue'],
  testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$',
  preset: 'ts-jest',
}

ベースプロジェクトの設定ですが、まずは tsconfig.json@types/jest を追加します。

packages/base/tsconfig.json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
+   "types": [
+     "@types/jest",
+   ]
  }
}

jest.config.js はルートの jest.config.base.js を Extend して名前だけつけてあげます(名前をつけないと複数プロジェクトで正しく動かなかったです)。

packages/base/jest.config.js
const config = require('../../jest.config.base')

module.exports = { name: 'base', displayName: 'Base Tests', ...config }

テストも TypeScript ファイル(*.spec.ts)を作成する想定なので .vue ファイルの型を追加しましょう。

packages/base/vue-shims.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

使わないので packages.json から test コマンドを削除します。

packages/base/package.json
{
  ...
  "scripts": {
    ...
-    "lint": "yarn lint:js && yarn lint:style",
-    "test": "jest"
+    "lint": "yarn lint:js && yarn lint:style"
  },
  ...
}

コマンドは以下で実行します。

yarn test # すべてのプロジェクトをテストする場合
yarn test:base # base だけテストする場合

Vetur

Vetur はデフォルトでルートの tsconfig.json を参照するので vetur.config.js でプロジェクトごとに tsconfig.json の参照先を指定する必要があります。

vetur.config.js
module.exports = {
  settings: {
    'vetur.useWorkspaceDependencies': true,
    'vetur.experimental.templateInterpolationService': true,
  },
  projects: [
    {
      root: './packages/base',
      tsconfig: './tsconfig.json',
    },
  ],
}

ベースプロジェクトのファイルを共有するための module.js を作成

この記事 の通りに、以下を作成します。

packages/base/module.js
import path from 'path'
import { createRoutes, relativeTo } from '@nuxt/utils'
import serveStatic from 'serve-static'

// ページを追加するごとに追加、動的に生成したほうがよいかと
const pages = ['pages/index.vue']

export default function NuxtModule() {
  const { routeNameSplitter, trailingSlash } = this.options.router
  this.extendRoutes((routes) => {
    routes.push(
      ...createRoutes({
        files: pages,
        srcDir: __dirname,
        pagesDir: 'pages',
        routeNameSplitter,
        trailingSlash,
      })
    )
  })

  const layoutPath = (file) =>
    relativeTo(this.options.buildDir, path.resolve(__dirname, 'layouts', file))

  this.nuxt.hook('build:templates', ({ templateVars }) => {
    templateVars.layouts.default = layoutPath('default.vue')
  })

  this.addServerMiddleware(serveStatic(path.resolve(__dirname, 'static')))

  this.nuxt.hook('components:dirs', (dirs) => {
    dirs.unshift({ path: path.resolve(__dirname, 'components'), level: 1 })
  })

  this.nuxt.hook('components:extend', (components) => {
    // If there are duplicates, give last the priority
    const noDupes = Object.values(
      components.reduce(
        (map, comp) => ({ ...map, [comp.pascalName]: comp }),
        {}
      )
    )
    components.length = 0
    components.push(...noDupes)
  })
}

pages, layouts, components, static が受け継ぐことができる Nuxt Module を作成できます。これを別プロジェクトの nuxt.config.jsmodules に追加すると受け継げます。

読み込み側のプロジェクトを作成

ルートや base から設定などを引き継ぐので、create-nuxt-app は使わずに一つひとつファイルを作成します。管理画面を想定しているので admin という名前で作成します。

mkdir -p packages/admin
cd packages/admin

base から package.json をコピーしてきて name だけ修正します。

packages/admin/package.json
{
  "name": "admin",
  ...
}

nuxt.config.jsbase を流用して必要な箇所だけ変更します。 head は一応すべて定義し直しにしてます。

packages/admin/nuxt.config.js
import defaultConfig from 'base/nuxt.config'

export default {
  ...defaultConfig,
  head: {
    title: 'admin',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },
}

base で必要だった作業をこちらでもします。

packages/admin/tsconfig.json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "types": [
      "@types/jest",
    ]
  }
}
packages/admin/vue-shims.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}
packages/admin/jest.config.js
const config = require('../../jest.config.base')

module.exports = { name: 'admin', displayName: 'Admin Tests', ...config }
vetur.config.js
module.exports = {
  settings: {
    'vetur.useWorkspaceDependencies': true,
    'vetur.experimental.templateInterpolationService': true,
  },
  projects: [
+   {
+     root: './packages/admin',
+     tsconfig: './tsconfig.json',
+   },
    {
      root: './packages/base',
      tsconfig: './tsconfig.json',
    },
  ],
}
package.json
{
  ...
  "scripts": {
    "test:base": "vue-cli-service test:unit --projects packages/base",
+   "test:admin": "vue-cli-service test:unit --projects packages/admin",
    "test": "vue-cli-service test:unit"
  },
  ...
}

では、base のファイルを受け継ぐ方法です。

pages の共有

base に以下のファイルを作成して

packages/base/pages/index.vue
<template>
  <div class="container">Hoge</div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({})
</script>

module.jspages にそのページのパスを記述すると、そのモジュールを読み込んだ先でページが表示できます。

packages/base/module.js
...
const pages = ['pages/index.vue']
...

admin 側で nuxt.config.jsbase/module を読み込みます。

packages/admin/nuxt.config.js
{
...
  modules: ['base/module']
...
}

開発サーバーを起動してみましょう。

yarn lerna run --stream --scope admin dev

http://localhost:3000 を開いてみるとページが閲覧できるかと思います。

components の共有

base につくったコンポーネントを共有する例です。

packages/base/components/BaseButton.vue
<template>
  <button class="b-btn">Hoge</button>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({})
</script>

baseadminnuxt.config.jscomponents: true になっていることを確認しましょう。以下のように記述するとボタンが表示されるかと思います。

packages/admin/pages/index.vue
<template>
  <base-button />
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({})
</script>

しれっと書いてますが、 baseadminpages のパスがかぶると、継承先の方が優先されます。

開発サーバーを起動してみましょう。

yarn lerna run --stream --scope admin dev

http://localhost:3000 を開いてみると、BaseButton が表示できているか思います。

layouts の共有

module.js に記述している以下ですが、 defaultbasedefault.vue で塗り替えるようになります。

packages/base/module.js
...
  this.nuxt.hook('build:templates', ({ templateVars }) => {
    templateVars.layouts.default = layoutPath('default.vue')
  })
...

この設定だと admin 側に default.vue を置いても反映されないので、運用によっては以下のように別の名前にして、塗り替えないようにするのも良いかもしれません。

packages/base/module.js
...
  this.nuxt.hook('build:templates', ({ templateVars }) => {
-    templateVars.layouts.default = layoutPath('default.vue')
+    templateVars.layouts.base_default = layoutPath('default.vue')
  })
...
packages/admin/pages/index.vue
<template>
  <base-button />
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  layout: 'base_default',
})
</script>

static の共有

base/static に置いたファイルが適応されます。同じファイル名であれば、継承先が優先されます。

CSS の共有

CSS はプロジェクトごとにCSSファイルを作って、CSSの中で base の CSS を @import するようにします(試したところ、@nuxtjs/style-resources を使う場合はそうしかできませんでした)。

まずは Sass の導入から。変数を Vue ファイルで使いたいので @nuxtjs/style-resources を入れます。

yarn add -D -W sass-loader node-sass @nuxtjs/style-resources

ベースプロジェクトに適当な scss ファイルを作成します。

packages/base/assets/scss/foundation.scss
$crimson: crimson;

body {
  background: lightsalmon;
}

CSS の設定を nuxt.config.js に記述します。

packages/base/nuxt.config.js
{
  ...
- css: [],
+ css: ['~/assets/scss/foundation.scss'],
  ...,
- modules: [],
+ modules: ['@nuxtjs/style-resources'],
  ...,
+ styleResources: {
+   scss: ['~/assets/scss/*.scss'],
+ },
}

admin の方で CSS を import します。

packages/admin/assets/scss/foundation.scss
@import '~base/assets/scss/foundation.scss';

nuxt.config.js は以下のようになります。

packages/admin/nuxt.config.js
import defaultConfig from 'base/nuxt.config'

export default {
  ...defaultConfig,
  head: {
    title: 'admin',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },
+ css: ['~/assets/scss/foundation.scss'],
- modules: ['base/module'],
+ modules: [...defaultConfig.modules, 'base/module'],
}

$crimson が使えるようになっています。

packages/admin/pages/index.vue
<template>
  <div class="hoge">ほげ</div>
</template>

<style lang="scss">
.hoge {
  color: $crimson;
}
</style>

画像の共有

baseassets の画像を admin で使いたいときは以下のようにすれば良いです。

packages/admin/pages/index.vue
<template>
  <div>
    <img src="~base/assets/images/hoge.jpg" alt="" />
  </div>
</template>

middleware, plugins, store の共有

これらを admin 側にファイルを作成せずに組み込みたい場合は、Nuxt Module を作成してロジックを共有する方法が書かれたこちらの記事が参考になります。

今回はその方法は使わず、シンプルに .ts ファイルを作成して base 側のファイルを読み込む方法をとります。

middleware の共有

packages/base/middleware/log.ts
import { Middleware } from '@nuxt/types'

const middleware: Middleware = ({ route }) => {
  console.log(route.path)
}

export default middleware
packages/admin/middleware/log.ts
export { default } from 'base/middleware/log'

plugins の共有

packages/base/plugins/log.ts
import { Plugin } from '@nuxt/types'

const plugin: Plugin = (_, inject) => {
  inject('log', () => {
    console.log('Hello from plugin!')
  })
}

export default plugin
packages/admin/plugins/log.ts
export { default } from 'base/plugins/log'

inject したプラグインは、型定義を追加することを忘れずに。

types/nuxt/index.d.ts
import Vue from 'vue'

declare module 'vue/types/vue' {
  interface Vue {
    $log: () => void
  }
}

declare module 'vuex' {
  interface Store<S> {
    $log: () => void
  }
}
packages/base/tsconfig.json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "types": [
      "@types/jest",
+     "../../types/nuxt"
    ]
  }
}
packages/admin/tsconfig.json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "types": [
      "@types/jest",
+     "../../types/nuxt"
    ]
  }
}

store の共有

packages/base/store/simple.ts
import { GetterTree, MutationTree } from 'vuex'

interface State {
  text: string
}

export const state: () => State = () => ({
  text: '',
})

export const getters: GetterTree<State, {}> = {
  text: ({ text }) => text,
}

export const mutations: MutationTree<State> = {
  setText(currentState, { text }) {
    currentState.text = text
  },
}
packages/admin/store/simple.ts
export * from 'base/store/simple'

Composition API ロジックの共有

切り出したロジックを共有する場合です。こちらはそのまま base にあるファイルを参照できます。@vue/composition-api のインストールなどは省略します。(ちなみに @nuxtjs/composition-api を使わないのは @vue/composition-api に依存していて、@vue/composition-api 側が変更されたら追従するまで動かなくなったりして辛かったからです。。)

packages/base/compositions/index.ts
import { reactive, toRefs } from '@vue/composition-api'

export const useText = () => {
  const state = reactive({
    text: '0',
  })

  const changeText = () => {
    state.text = `${Math.random()}`
  }

  return {
    changeText,
    ...toRefs(state),
  }
}
packages/admin/components/SuperButton.vue
<template>
  <button class="b-btn" @click="changeText">{{ text }}</button>
</template>

<script lang="ts">
import { defineComponent } from '@vue/composition-api'
import { useText } from 'base/compositions'

export default defineComponent({
  setup() {
    const { text, changeText } = useText()

    return {
      text,
      changeText,
    }
  },
})
</script>

運用できるか

まだ、これから新しく作るプロジェクトと、既存プロジェクトの改修に適用しようとしている段階なので、なんとも言えません。
追記:使ってみました(2021-02-15)

単純に monorepo 特有の問題がいろいろありそうです。今はまだサービスが初期段階なので、まとめてリリースでも問題なさそうですが、リリース周りをうまくやらないとなあという印象があります。

monorepoからmanyrepoへ移行した記事には以下のような問題点が挙げられていたり

  • 共通処理パッケージのバージョン管理ができない
    • 他サービスで更新したが、まだそれを適用したくないサービスも存在する
    • サービスが増えるごとに影響範囲が大きく、共通処理パッケージの変更が怖くなり、あまり共通化しなくなることも……(テストが充実すればある程度怖さはなくなるかも?)
  • コミット履歴としては1つのため、1サービスに閉じた更新も全サービスの更新に近く、不安感がある
    • Aというサービスの更新をして、次にBの更新をすると、パッケージは別れているものの、コミット履歴的にBはAの更新により影響を受けていないか不安になる

Git をこねくりまわして monorepo のリリースを頑張ってるこの記事みたいに、ゆくゆくはリリースを分割できるようにしないといけない気がしてます。

何かしら課題が出てくるかもしれないので、何かあれば気が向いたら更新します。

追記(2021-02-15)

進捗を報告します。

実際の構成

Firebase(Hosting, Functions, ...) + Github Actions というデプロイ構成です。dev, stg, pro 環境それぞれ Firebase Project を作ってそれを切り替えるかたちにしてます。

lerna で管理している packages は以下です。

  • base: 共通で使うファイル郡
  • user: エンドユーザー用ビュー
  • admin: 管理画面用ビュー
  • super: 管理者を管理する画面用ビュー
  • functions: Cloud Functions のファイル

デプロイ

問題なくできています、master プッシュ発火の Github Actions のファイルは以下です。

.github/workflows/ci.yml
name: ci

on:
  push:
    branches:
      - main
      - master

jobs:
  ci:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-latest]
        node: [12]

    steps:
      - name: Checkout 🛎
        uses: actions/checkout@master

      - name: Setup node env 🏗
        uses: actions/setup-node@v2.1.2
        with:
          node-version: ${{ matrix.node }}

      - name: Setup env 🏗
        run: |
          echo "FIREBASE_API_KEY=XXXXXXXXXXXXXXXXXXXXXX" >> $GITHUB_ENV
          echo "FIREBASE_AUTH_DOMAIN=xxxxxxxxxxxx.firebaseapp.com" >> $GITHUB_ENV
          echo "FIREBASE_DATABASE_URL=https://xxxxxxxxxxxx.firebaseio.com" >> $GITHUB_ENV
          echo "FIREBASE_PROJECT_ID=xxxxxxxxxxx" >> $GITHUB_ENV
          echo "FIREBASE_STORAGE_BUCKET=xxxxxxxxxxx.appspot.com" >> $GITHUB_ENV
          echo "FIREBASE_MESSAGING_SENDER_ID=XXXXXXXXXXXXX" >> $GITHUB_ENV
          echo "FIREBASE_APP_ID=xxxxxxxxxxxxxxxxxxxxxxxxxx" >> $GITHUB_ENV

      - name: Get yarn cache directory path 🛠
        id: yarn-cache-dir-path
        run: echo "::set-output name=dir::$(yarn cache dir)"

      - name: Cache node_modules 📦
        uses: actions/cache@v2
        id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
        with:
          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      - name: Install dependencies 👨🏻‍💻
        run: yarn

      - name: Run linter 👀
        run: yarn lerna run lint

      - name: Run tests 🧪
        run: yarn test

      - name: Run build 📦
        run: yarn lerna run build

      - name: Run generate 🏭
        run: yarn lerna run generate

      - name: Run deploy 🚀
        run: yarn deploy:dev --token=${{ secrets.FIREBASE_TOKEN }}

functions だけ、他と同じノリで monorepo だからと dependencies から依存を削除するとデプロイでコケるので注意です。

依存の更新(dependabot)

create-nuxt-app で初期設定されたバージョンから dependabot でガンガンアップデートしましたが、ほぼ問題なかったです。

sass-loader@11.0.1 だけ更新に失敗しましたが、こちらはこの構成だからというよりVueとの組み合わせによる問題のようです。 10系で止めるかたちで対処しています。

気になった点・問題点

以下3点です。すべて慣れれば問題なく運用できましたが、 tailwindcss が必須な人は頑張らないといけないかもです。

  • @nuxtjs/tailwindcss が使えない(設定頑張ったら使える?)
    • purge の対象ファイル設定が単体プロジェクト前提なので、baseで利用しているクラス名がないものとして扱われます
    • nuxt.hook とか駆使したらもしかしたら設定できるかもです
    • 僕の場合、そもそも @nuxtjs/tailwindcss ほとんど使ってなかったのでライブラリごと削除しました
  • パッケージをまたぐファイル読み込みの際は、ファイルを追加したあと Window Reload が必要
    • 定義されてないと言われる
      スクリーンショット 2021-02-15 14.40.49.png
    • Command+Shift+P で Reload Window
      スクリーンショット 2021-02-15 14.41.39.png
  • base の component で img を指定するとき ~assets/.. ってやると、その component を別パッケージで呼び出したとき動かないから ~base/assets/.. で呼び出す必要がある

追記(2021-06-18)

本番運用を開始しています。

ほぼ問題はなかったのですが、 packages/base/static に置いたファイルを引き継ぐ設定にしているにもかかわらず、各プロジェクトのビルド結果に配置されていなかったので、各プロジェクトの static に同ファイルを置く対応をしました。

参考記事

14
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?