Utilisez le rollup pour empaqueter les composants de cache ts+react et les publier sur npm

Créez un nouveau répertoire de projet, par exemple, appelé root, et créez un fichier de configuration de rollup ci-dessous : rollup.config.ts Étant donné que le rollup prend bien en charge ts et esmodule, utilisez le fichier de configuration ts.

Installation

Générez un fichier package.json, généré ici à l'aide de pnpm :

pnpm init

Installez le rollup et Typescript :

pnpm add rollup
pnpm add typescript

Configurez la commande pnpm build de package.json :

{
    
    
  "scripts": {
    
    
    "build": "rollup --c --configPlugin typescript2 --bundleConfigAsCjs",
    "build:dev": "cross-env NODE_ENV=development pnpm build",
    "build:prod": "cross-env NODE_ENV=production pnpm build"
  },
  "type": "module"
}

Le rollup s'exécute dans l'environnement du nœud. La modularité du nœud est que commonjs ne prend pas en charge esmodule, vous devez donc configurer une commande rollup dans le script en cours d'exécution de package.json -bundleConfigAsCjs pour analyser le code esmodule dans commonjs afin que nodejs puisse le reconnaître. , puis package.json doit être Add type:module pour prendre en charge esmodule

explication de la commande build :

  1. –c spécifie que le rollup lit le fichier de configuration rollup.config dans le répertoire racine du projet pour la construction.
  2. –configPlugin spécifie le plug-in à utiliser lors de la création du rollup, y compris le traitement du fichier de configuration du rollup. Ici, le plug-in typescript2 est spécifié pour traiter le fichier de configuration ts afin d'éviter les erreurs lors de la lecture du fichier de configuration du rollup.
  3. –bundleConfigAsCjs est une commande de rollup utilisée pour convertir esmodule en commonjs pour une utilisation dans l'environnement de nœud.
  4. cross-env est un plug-in utilisé pour faciliter la définition des variables d'environnement dans différents systèmes d'exploitation. La manière de définir les variables d'environnement dans différents systèmes d'exploitation est différente. Nous ne pouvons pas le faire une par une, il est donc utilisé pour réaliser réglage multiplateforme des variables d'environnement.
  5. build:dev et build:prod permettent d'effectuer différentes opérations basées sur l'obtention de la valeur process.env.NODE_ENV injectée par la variable d'environnement.
  6. type:module est également une étape à configurer pour prendre en charge esmodule.

fichier de configuration rollup.config.ts

Le fichier nécessite l'exportation d'un tableau d'objets RollupOptions/RollupOptions[]. Un objet est la configuration d'empaquetage d'un fichier. Vous pouvez empaqueter autant d'objets de configuration que vous le souhaitez. Ici, je vais spécifier un fichier d'entrée pour exposer trois interfaces à l'extérieur. Le rollup se déroulera en fonction de la référence d'importation du fichier d'entrée. Recherchez le fichier pour configurer le
fichier de configuration du build rollup :

import nodeResolve from '@rollup/plugin-node-resolve'
import typescript2 from 'rollup-plugin-typescript2'
// @ts-ignore
import babel from 'rollup-plugin-babel'
import commonjs from '@rollup/plugin-commonjs'
import {
    
     join, resolve } from 'path'
import {
    
     readdir } from 'fs/promises'
import {
    
     RollupOptions, defineConfig } from 'rollup'
import {
    
     IOptions } from 'rollup-plugin-typescript2/dist/ioptions'
import {
    
     existsSync } from 'fs'
import {
    
     unlink, rmdir, lstat } from 'fs/promises'
const commonPlugins = [
  nodeResolve({
    
    
    extensions: ['.ts', '.tsx'], // 告诉node要解析的文件扩展名
  }),
  typescript2({
    
    
    tsConfig: resolve(__dirname, 'tsconfig.json'), // 指定ts配置文件位置
    // useTsconfigDeclarationDir: true, // 使用配置文件里的DeclarationDir 不开启默认强制生成在和文件同级目录同名文件
  } as Partial<IOptions>),
  babel({
    
    
    babelrc: true, // 使用.babelrc配置文件
  }),
  commonjs(), // 这个插件比如加 用来转换成commonjs 然后注入react17新的jsx组件转换函数_JSX react17+不再用createElement 不用这个插件只用babel处理会报错
]
/**
 * @description 根据路径删除目录
 * @param dirs 删除的目录路径
 */
const removeDir = async (...dirs: string[]) => {
    
    
  for (const dir of dirs) {
    
    
    const absolutePath = resolve(__dirname, dir)
    if (existsSync(absolutePath)) {
    
    
      const dirStack = [absolutePath]
      while (dirStack.length > 0) {
    
    
        const initPath = dirStack[dirStack.length - 1]
        const fileStat = await lstat(initPath)
        if (fileStat.isDirectory()) {
    
    
          const files = await readdir(initPath)
          if (files.length > 0) {
    
    
            dirStack.push(...files.map((e) => join(initPath, e)))
          } else {
    
    
            await rmdir(initPath)
            dirStack.pop()
          }
        } else if (fileStat.isFile()) {
    
    
          await unlink(initPath)
          dirStack.pop()
        }
      }
    }
  }
}
const resolveRollupOptions = async () => {
    
    
  const results: RollupOptions[] = []
  const dirStack = [resolve(__dirname, 'src')]
  while (dirStack.length > 0) {
    
    
    const initPath = dirStack.shift()!
    const fileStat = await lstat(initPath)
    if (fileStat.isDirectory()) {
    
    
      const files = await readdir(initPath)
      if (files.length > 0) {
    
    
        dirStack.push(...files.map((e) => join(initPath, e)))
      }
    } else if (fileStat.isFile()) {
    
    
      const rollupOption: RollupOptions =
        process.env.NODE_ENV === 'development'
          ? {
    
    
              input: initPath,
              treeshake: false,
              external: ['react', 'react-dom'],
              output: {
    
    
                file: initPath
                  .replace(/src/, 'lib')
                  .replace(/\.(tsx|ts)/, '.js'),
                format: 'esm',
                sourcemap: true,
              },
              plugins: commonPlugins,
            }
          : {
    
    
              input: initPath,
              treeshake: true,
              external: ['react', 'react-dom'],
              output: {
    
    
                file: initPath
                  .replace(/src/, 'lib')
                  .replace(/\.(tsx|ts)/, '.min.js'),
                format: 'esm',
                sourcemap: false,
              },
              plugins: [...commonPlugins],
            }
      results.push(rollupOption)
    }
  }
  return results
}
export default defineConfig(async (/* commandLineArgs */) => {
    
    
  // 每次构建前先删除上一次的产物
  await removeDir('es', 'lib')
  // 生成两个产物 一个esmodule模块 一个umd通用模块
  return [
    {
    
    
      input: resolve(__dirname, 'src/index.ts'), // 指定入口文件
      treeshake: true, // 开启treeshaking
      external: ['react', 'react-dom'], // 第三方库使用外部依赖
      output: {
    
    
        name: 'ReactAlive', // 这个name用于打包成umd/iife模块时模块挂到全局对象上的key
        file: resolve(__dirname, 'es/index.js'), // 构建的产物输出位置和文件名
        format: 'esm', // 构建产物的模块化类型
        sourcemap: false, // 关闭sourcemap
        // 指定被排除掉的外部依赖在全局对象上的key
        globals: {
    
    
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
      plugins: commonPlugins,
    },
    {
    
    
      input: resolve(__dirname, 'src/index.ts'),
      treeshake: true,
      external: ['react', 'react-dom'],
      output: {
    
    
        name: 'ReactAlive',
        file: resolve(__dirname, 'lib/index.js'),
        format: 'umd',
        sourcemap: false,
        globals: {
    
    
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
      plugins: commonPlugins,
    },
  ] as RollupOptions[]
})

root/src/index.ts : Le fichier d'entrée déclare l'interface que cette bibliothèque exposera au monde extérieur :

import KeepAliveScope from './components/keepalive-scope'
import KeepAliveItem, {
    
     useCacheDestroy } from './components/keepalive-item'
export {
    
     KeepAliveItem, KeepAliveScope, useCacheDestroy }

root/global.d.ts : fournissez une déclaration de type pour process.env.NODE_ENV afin qu'il y ait des invites de code :

declare namespace NodeJS {
    
    
  interface ProcessEnv {
    
    
    NODE_ENV: 'development' | 'production'
  }
}

racine/tsconfig.json :

{
    
    
  "compilerOptions": {
    
    
    "target": "ESNext",
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": true, // 跳过第三方库类型声明文件的检查
    "esModuleInterop": true, // 开启将esm代码编译成cjs
    "allowSyntheticDefaultImports": true, // 启用默认导出 .default访问默认导出的module.exports内容
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "sourceMap": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "types": ["node"],
    "experimentalDecorators": true, // 开启装饰器语法
    "jsx": "react-jsx", // react17+这里可以改成react-jsx 17+后会自动引入一个编译jsx函数 配置babel的automatic
    "baseUrl": ".",
    // "paths": {
    
    
    //   "@/*": ["src/*"]
    // },
    "declaration": true // 是否生成类型声明文件
    // "declarationDir": "lib/types" // 类型声明文件默认生成在对应ts文件同级目录 指定一个目录统一生成
  },
  "exclude": ["node_modules"],
  "include": ["src"]
}

root/.babelrc : fournit la configuration du plug-in babel :

{
    
    
  "presets": [
    "@babel/preset-env",
    [
      "@babel/preset-react",
      {
    
    
        "runtime": "automatic"
      }
    ]
  ],
  "extensions": [".ts", ".tsx"],
  "include": ["src"],
  "exclude": ["node_modules"]
}

Explication du fichier .babelrc :

  1. @babel/preset-env : Babel ajuste ses propres préréglages de configuration en fonction de notre environnement.
  2. @babel/preset-react : Babel utilise des préréglages pour ajuster sa propre configuration dans l'environnement du projet React. Il est utilisé pour convertir des éléments de configuration en préréglages tels que la vérification du type de props jsx. runtime : automatique fait référence à l'injection automatique dans les composants React au moment de l'exécution. jsx fonction de conversion Avant React17, nous devions importer React au début du composant pour créer un composant en utilisant createElement sur l'objet React. Les nouvelles fonctionnalités après la v17 nous permettent d'injecter automatiquement la fonction de conversion jsx sans importer React. Ce sujet a finalement été évoqué par @babel/plugin -transform-react-jsx le fait
  3. extensions : indique à babel le suffixe du fichier à traiter. Mes composants n'ont que des fichiers tsx et ts.
    npmignore file : indique à npm quels fichiers ignorer lors de la publication et n'ont pas besoin de les publier dans le package npm.
    Ceci est la liste de contrôle à ignorer dans mon projet:
node_modules
src
.babelrc
.gitignore
.npmignore
.prettierrc
rollup.config.ts
test
pnpm-lock.yaml
global.d.ts
tsconfig.json
.DS_Store
test-project

À l'exception des fichiers en .npmignore, ils seront téléchargés sur npm.

Publier le package npm

Enregistrez d'abord un compte npm, puis accédez à la page d'accueil pour rechercher l'en-tête du package. Une invite vous demandera de lier la politique de vérification de sécurité 2FA. Suivez simplement les invites et terminez le processus. Cela signifie probablement lier un otp ( mot de passe à usage unique) mot de passe à usage unique. Téléchargez une application d'authentification Google et vous pourrez générer un code en scannant la page Web pour lier le compte, puis en suivant les instructions. Ce mot de passe à usage unique vous demandera de saisir otp lors de votre première connexion. npm login et npm submit, puis il vous permettra d'accéder directement au navigateur. Vérifiez simplement

Avant chaque release, vous devez modifier le numéro de version de package.json. Si vous ne parvenez pas à sortir la même version, une erreur sera signalée.

Avant de publier, il est préférable de rechercher le nom du package (le nom de package.json est le nom du package npm) pour voir s'il existe déjà. Sinon, si vous utilisez le nom existant, il vous sera indiqué que vous n'avez pas la permission de mettre à jour les packages d'autres personnes. Il est préférable d'utiliser @votre propre nom/nom de package. L'ajout d'un préfixe de nom d'utilisateur à ce format équivaut à un espace de noms et le risque de duplication sera beaucoup plus faible.

Exemple de code complet root/package.json :

{
    
    
  "name": "@williamyi74/react-keepalive",
  "version": "1.1.2",
  "description": "基于react18+的缓存组件,拥有类似vue的KeepAlive组件功能效果",
  "main": "index.js",
  "scripts": {
    
    
    "build": "rollup --c --configPlugin typescript2 --bundleConfigAsCjs",
    "build:dev": "cross-env NODE_ENV=development pnpm build",
    "build:prod": "cross-env NODE_ENV=production pnpm build"
  },
  "keywords": [
    "alive",
    "keep alive",
    "react",
    "react keep alive",
    "react keep alive item",
    "react keep alive scope",
    "react keep scope",
    "react hooks keep alive"
  ],
  "author": "williamyi",
  "license": "ISC",
  "peerDependencies": {
    
    
    "react": ">=18.2.0",
    "react-dom": ">=18.2.0",
    "typescript": ">=5.0.4"
  },
  "devDependencies": {
    
    
    "@babel/core": "^7.21.4",
    "@babel/preset-env": "^7.21.4",
    "@babel/preset-react": "^7.18.6",
    "@rollup/plugin-commonjs": "^24.1.0",
    "@rollup/plugin-node-resolve": "^15.0.2",
    "@types/react": "^18.2.0",
    "@types/react-dom": "^18.2.1",
    "cross-env": "^7.0.3",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "rollup": "^3.21.0",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-typescript2": "^0.34.1",
    "typescript": "^5.0.4"
  },
  "type": "module"
}

Explication root/package.json :

  1. mots-clés : Mots-clés pour rechercher votre colis
  2. description : Une brève description sous la vignette lors de la recherche d'un package
  3. licence : l'accord du projet. ISC est une version simplifiée du MIT. Cela signifie que d'autres peuvent librement modifier et utiliser votre projet.
  4. peerDependencies : les exigences de dépendances externes utilisées dans le projet indiquent au projet utilisant ce package que ces versions de dépendances doivent répondre à cette exigence, sinon une erreur sera signalée. Lorsque le projet utilisant ce package télécharge des dépendances, il téléchargera peerDep au lieu de télécharger toutes les dépendances. packages en externe dans notre projet. venez ici

Tout d'abord, passez à la source npm avant d'exécuter la connexion npm. N'utilisez pas la source Taobao, sinon une erreur sera signalée. Entrez les informations et suivez les étapes.

Après une connexion réussie, exécutez npm submit --access public submit

–access public indique à npm de publier les packages publics car les packages privés ne peuvent pas être publiés par défaut. Vous avez oublié si les packages privés sont facturés ou quelque chose comme ça ? Si vous spécifiez ce paramètre de commande, les erreurs d'autorisation ne seront pas signalées.

Une fois qu'aucune erreur n'est signalée en cours de route, vous pouvez consulter le package npm et voir le package que vous avez publié :
Publier un package npm réussi

document

Utilisez simplement le fichier README.md dans le répertoire racine pour compléter ce fichier.

Je suppose que tu aimes

Origine blog.csdn.net/Suk__/article/details/130511222
conseillé
Classement