Closed6

モノレポ採用時のconfig系の管理メモ

kobokobo

モノレポ構成を取るときにeslintの設定やtsconfig、storybookやjestの設定を共通化したい

毎回構築するのが面倒なのでメモ

どこかのタイミングでちゃんとテンプレート化したい

kobokobo

ESLint

packages/shared-eslint

packages/shared-eslint/package.json
{
  "name": "@shared/eslint-plugin",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "private": true,
  "main": "index.js",
  "type": "commonjs",
  "dependencies": {
    "@typescript-eslint/eslint-plugin": "^6.5.0",
    "eslint": "8.48.0",
    "eslint-config-next": "13.4.19",
    "eslint-config-prettier": "9.0.0",
    "eslint-config-turbo": "1.10.12",
    "eslint-plugin-import": "^2.28.1",
    "eslint-plugin-jsx-a11y": "^6.7.1",
    "eslint-plugin-react": "7.33.2"
  }
}
packages/shared-eslint/index.js
/**
 * @type {import('eslint').Linter.Config'}
 */
module.exports = {
  configs: {
    base: {
      extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier',
      ],
      rules: {
        'array-callback-return': 'error',
        'no-await-in-loop': 'error',
        'no-duplicate-imports': 'error',
        'no-self-compare': 'error',
        'no-template-curly-in-string': 'error',
        'no-unreachable-loop': 'error',
        'no-use-before-define': 'error',
        'no-console': 'warn',
        'eol-last': ['error', 'always'],
        camelcase: 'warn',
        eqeqeq: 'error',
      },
    },
    react: {
      extends: ['plugin:@shared/base', 'plugin:react/recommended'],
      env: {
        node: true,
      },
      rules: {
        'react/jsx-uses-react': 'off',
        'react/react-in-jsx-scope': 'off',
      },
      settings: {
        react: {
          version: 'detect',
        },
      },
    },
    next: {
      extends: ['plugin:@shared/react', 'next/core-web-vitals', 'turbo'],
    },
  },
}
kobokobo

Jest

packages/shared-jest

packages/shared-jest/package.json
{
  "name": "@shared/jest",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "private": true,
  "devDependencies": {
    "@types/jest": "^29.5.4"
  },
  "dependencies": {
    "@jest/types": "^29.6.3",
    "@swc/core": "^1.3.81",
    "@swc/jest": "^0.2.29",
    "@testing-library/jest-dom": "^6.1.2",
    "@testing-library/react": "^14.0.0",
    "jest": "^29.6.4",
    "jest-environment-jsdom": "^29.6.4"
  }
}
packages/shared-jest/base/jest.config.ts
import type { Config } from '@jest/types'

const config: Config.InitialOptions = {
  verbose: true,
  testMatch: ['**/*.spec.ts', '**/*.spec.tsx'],
  transformIgnorePatterns: ['/node_modules/'],
}

export default config
packages/shared-jest/react/jest.config.ts
import type { Config } from '@jest/types'
import type { Config as SWCConfig } from '@swc/core'
import baseConfig from '../base/jest.config'

const swcConfig: SWCConfig = {
  sourceMaps: true,
  module: {
    type: 'commonjs',
  },
  jsc: {
    parser: {
      syntax: 'typescript',
      tsx: true,
    },
    transform: {
      react: {
        runtime: 'automatic',
      },
    },
  },
}

const config: Config.InitialOptions = {
  ...baseConfig,
  testEnvironment: 'jest-environment-jsdom',
  transform: {
    '^.+\\.tsx?$': [
      '@swc/jest',
      swcConfig as unknown as Record<string, unknown>,
    ],
  },
}

export default config
packages/shared-jest/react/jest.setup.ts
import '@testing-library/jest-dom'
kobokobo

Storybook

packages/shared-storybook

packages/shared-storybook/package.json
{
  "name": "@shared/storybook",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "private": true,
  "scripts": {
    "lint": "eslint ."
  },
  "dependencies": {
    "@shared/eslint-plugin": "*",
    "@shared/tsconfig": "*",
    "@storybook/addon-interactions": "^7.4.0",
    "@storybook/react": "^7.4.0",
    "@storybook/react-vite": "^7.4.0",
    "@types/node": "^20.5.7",
    "storybook": "^7.4.0"
  },
  "devDependencies": {
    "@storybook/types": "^7.4.0"
  }
}
packages/shared-storybook/tsconfig.json
{
  "extends": "@shared/tsconfig/node/tsconfig.json",
  "include": ["**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}
packages/shared-storybook/preview.ts
import type { Preview } from "@storybook/react";

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: "^on[A-Z].*" },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
};

export default preview;
packages/shared-storybook/main/base.ts
import type { StorybookConfig } from '@storybook/types'

export const config: StorybookConfig = {
  stories: [],
  addons: [
    '@storybook/addon-docs',
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-onboarding',
    '@storybook/addon-interactions',
  ],
  docs: {
    autodocs: 'tag',
  },
}
packages/shared-storybook/main/react.ts
import type { StorybookConfig } from "@storybook/react-vite";
import { config as baseConfig } from "./base";

export const config: StorybookConfig = {
  stories: [],
  addons: baseConfig.addons ? baseConfig.addons : [],
  framework: {
    name: "@storybook/react-vite",
    options: {},
  },
  docs: baseConfig.docs ? baseConfig.docs : {},
};
packages/shared-storybook/main/with-interactions.ts
import type { StorybookConfig } from '@storybook/types'

export const withInteractions = (config: StorybookConfig) => {
  return {
    ...config,
    addons: config.addons
      ? [...config.addons, '@storybook/addon-interactions']
      : [],
    features: {
      ...config.features,
      interactionsDebugger: true,
    },
  }
}
packages/shared-storybook/main/with-tailwind.ts
import type { StorybookConfig } from '@storybook/types'

export const withTailwind = (config: StorybookConfig) => {
  return {
    ...config,
    addons: config.addons
      ? [
          ...config.addons,
          {
            name: '@storybook/addon-postcss',
            options: {
              postcssLoaderOptions: {
                implementation: require('postcss'),
              },
            },
          },
        ]
      : [],
  }
}
kobokobo

Tailwind

packages/shared-tailwind

packages/shared-tailwind/package.json
{
  "name": "@shared/tailwind",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "private": true,
  "files": [
    "tailwind.config.js",
    "postcss.config.js"
  ],
  "dependencies": {
    "autoprefixer": "10.4.15",
    "postcss": "8.4.28",
    "tailwindcss": "3.3.3"
  }
}
packages/shared-tailwind/tailwind.config.ts
import type { Config } from "tailwindcss";

const config: Config = {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [],
};

export default config;
packages/shared-tailwind/postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}
kobokobo

TSConfig

packages/shared-tsconfig

packages/shared-tsconfig/package.json
{
  "name": "@shared/tsconfig",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "private": true,
  "dependencies": {
    "@tsconfig/create-react-app": "^2.0.1",
    "@tsconfig/next": "^2.0.0",
    "@tsconfig/node20": "^20.1.2",
    "@tsconfig/strictest": "^2.0.1",
    "typescript": "5.2.2"
  }
}
packages/shared-tsconfig/base/tsconfig.json
{
  "extends": [
    "@tsconfig/node20/tsconfig.json",
    "@tsconfig/strictest/tsconfig.json"
  ]
}
packages/shared-tsconfig/react/tsconfig.json
{
  "extends": [
    "../node/tsconfig.json",
    "@tsconfig/create-react-app/tsconfig.json"
  ]
}
packages/shared-tsconfig/next/tsconfig.json
{
  "extends": ["../react/tsconfig.json", "@tsconfig/next/tsconfig.json"]
}
このスクラップは2023/11/06にクローズされました