TypeScript + React + JSX + MUI + React Query な npm パッケージを自作する

# はじめに

- Webpack も Rollup も Babel も使わない
- tsconfig.json も使わない。コマンドライン引数だけを利用する
- React はパッケージ利用者側のものを使用する
- パッケージを公開するときに必要な認証周りについては特に触れていない

# 内容

まず最小の npm パッケージを作成し、徐々に拡張していく。

## 1. 最小の npm パッケージ

```json:package.json
{
  "name": "@penguinshunya/hoge",
  "version": "0.0.1"
}
```

```js:index.js
```

たった4行で npm パッケージを完成させられる。

## 2. GitHub Packages に publish

```json:package.json
{
  "name": "@penguinshunya/hoge",
  "version": "0.0.2",
  "publishConfig": {
    "registry": "https://npm.pkg.github.com/"
  }
}
```

```js:index.js
```

もしくは、次のコマンドでレジストリを設定する。

```sh
npm config set @penguinshunya:registry=https://npm.pkg.github.com/
```

この設定を行えば `"publishConfig"` の設定は不要になる。

## 3. React

```json:package.json
{
  "name": "@penguinshunya/hoge",
  "version": "0.0.3",
  "peerDependencies": {
    "react": "^18.2.0"
  }
}
```

```js:index.js
const { createElement } = require("react");
exports.HelloWorld = () => createElement("div", null, "hello, world");
```

## 4. React + TypeScript

```json:package.json
{
  "name": "@penguinshunya/hoge",
  "version": "0.0.4",
  "peerDependencies": {
    "react": "^18.2.0"
  },
  "devDependencies": {
    "@types/node": "^20.4.1",
    "@types/react": "^18.2.14",
    "typescript": "^5.1.6"
  }
}
```

```typescript:index.ts
import { createElement } from "react";

export const HelloWorld = () => {
  return createElement("div", null, "hello, world");
}
```

次のコマンドで index.js を作成する。

```sh
npx tsc index.ts
```

## 5. React + TypeScript + JSX

```json:package.json
{
  "name": "@penguinshunya/hoge",
  "version": "0.0.5",
  "peerDependencies": {
    "react": "^18.2.0"
  },
  "devDependencies": {
    "@types/node": "^20.4.1",
    "@types/react": "^18.2.14",
    "typescript": "^5.1.6"
  }
}
```

```tsx:index.tsx
import * as React from "react";

export function HelloWorld() {
  return <div>hello, world</div>;
}
```

次のコマンドで index.js を作成する。

```sh
npx tsc index.tsx --jsx react
```

`tsc` コマンドだけで .tsx から .js に変換できる。

## 6. React + TypeScript + JSX + MUI

```json:package.json
{
  "name": "@penguinshunya/hoge",
  "version": "0.0.6",
  "peerDependencies": {
    "react": "^18.2.0"
  },
  "devDependencies": {
    "@types/node": "^20.4.1",
    "@types/react": "^18.2.14",
    "typescript": "^5.1.6"
  },
  "dependencies": {
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/material": "^5.14.0"
  }
}
```

```tsx:index.tsx
import { Button, ButtonProps } from "@mui/material";
import * as React from "react";

export type MyButtonProps = ButtonProps;

export function MyButton({ ...props }: MyButtonProps) {
  return <Button variant="outlined" size="small" {...props} />
}
```

次のコマンドで index.js を作成する。

```sh
npx tsc index.tsx --jsx react --allowSyntheticDefaultImports
# --allowSyntheticDefaultImports を付けないと以下のエラーが発生する
# Module '"/path/to/hoge/node_modules/@types/prop-types/index"' has no default export.
```

## 7. React + TypeScript + JSX + MUI + React Query

```json:package.json
{
  "name": "@penguinshunya/hoge",
  "version": "0.0.7",
  "peerDependencies": {
    "react": "^18.2.0"
  },
  "devDependencies": {
    "@types/node": "^20.4.1",
    "@types/react": "^18.2.14",
    "typescript": "^5.1.6"
  },
  "dependencies": {
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/material": "^5.14.0",
    "@tanstack/react-query": "^4.29.19"
  }
}
```

```tsx:index.tsx
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from "@tanstack/react-query";
import * as React from "react";

const queryClient = new QueryClient();

export interface APIProviderProps {
  children?: React.ReactNode;
}

export function APIProvider({ children }: APIProviderProps) {
  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
}

export function useListMember() {
  return useQuery(["ListMember"], async () => {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return [
      { id: 1, name: "foo" },
      { id: 2, name: "bar" },
    ];
  });
}
```

次のコマンドで index.js を作成する。

```sh
npx tsc index.tsx --jsx react --allowSyntheticDefaultImports
```