Utilice el paquete acumulativo para empaquetar componentes de caché de ts+react y publicarlos en npm

Cree un nuevo directorio de proyecto, por ejemplo, llamado raíz, y cree un archivo de configuración de resumen a continuación: rollup.config.ts Debido a que el resumen admite bien ts y esmodule, use el archivo de configuración ts.

Configuración

Genere un archivo package.json, generado aquí usando pnpm:

pnpm init

Instalar paquete acumulativo y mecanografiado:

pnpm add rollup
pnpm add typescript

Configure el comando 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"
}

El paquete acumulativo se ejecuta en el entorno del nodo. La modularidad del nodo es que commonjs no admite esmodule, por lo que debe configurar un comando acumulativo en el script en ejecución de package.json -bundleConfigAsCjs para analizar el código de esmodule en commonjs para que nodejs pueda reconocerlo. y luego package.json debe agregarse tipo: módulo para admitir esmodule

explicación del comando de compilación:

  1. –c especifica que rollup lee el archivo de configuración rollup.config en el directorio raíz del proyecto para su construcción.
  2. –configPlugin especifica el complemento que se utilizará al crear el paquete acumulativo, incluido el procesamiento del archivo de configuración del paquete acumulativo. Aquí, el complemento typescript2 se especifica para procesar el archivo de configuración ts para evitar errores al leer el archivo de configuración del paquete acumulativo.
  3. –bundleConfigAsCjs es un comando de resumen que se utiliza para convertir esmodule en commonjs para su uso en el entorno de nodo.
  4. cross-env es un complemento que se utiliza para simplificar la forma de configurar variables de entorno en diferentes sistemas operativos. La forma de configurar variables de entorno en diferentes sistemas operativos es diferente. No podemos hacerlo uno por uno, por lo que se utiliza para lograr Configuración multiplataforma de variables de entorno.
  5. build:dev y build:prod se utilizan para realizar diferentes operaciones en función de la obtención del valor process.env.NODE_ENV inyectado por la variable de entorno.
  6. type:module también es un paso para configurarlo para que admita esmodule.

archivo de configuración rollup.config.ts

El archivo requiere exportar una matriz de objetos RollupOptions/RollupOptions[]. Un objeto es la configuración de empaquetado de un archivo. Puede empaquetar tantos objetos de configuración como desee. Aquí especificaré un archivo de entrada para exponer tres interfaces al exterior. El paquete acumulativo irá de acuerdo con la referencia de importación del archivo de entrada. Busque el archivo para configurar el
archivo de configuración del paquete acumulativo de compilación:

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: el archivo de entrada declara la interfaz que esta biblioteca expondrá al mundo exterior:

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

root/global.d.ts: proporcione una declaración de tipo para process.env.NODE_ENV para que haya indicaciones de código:

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

raíz/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: proporciona configuración para el complemento de Babel:

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

Explicación del archivo .babelrc:

  1. @babel/preset-env: Babel ajusta sus propios ajustes preestablecidos de acuerdo con nuestro entorno.
  2. @babel/preset-react: Babel usa ajustes preestablecidos para ajustar su propia configuración en el entorno del proyecto de reacción. Se utiliza para convertir elementos de configuración en ajustes preestablecidos como la verificación de tipos de accesorios jsx. tiempo de ejecución: automático se refiere a la inyección automática en componentes de reacción en tiempo de ejecución. jsx función de conversión Antes de reaccionar 17, necesitábamos importar React al comienzo del componente para crear un componente usando createElement en el objeto React. Las nuevas características después de v17 nos permiten inyectar automáticamente la función de conversión jsx sin importar React. Este asunto finalmente surgió por @babel/plugin -transform-react-jsx lo hace
  3. extensiones: le dice a babel el sufijo del archivo que se va a procesar. Mis componentes solo tienen archivos tsx y ts. archivo
    npmignore: le dice a npm qué archivos ignorar al publicar y no es necesario publicarlos en el paquete npm.
    Esta es la lista de verificación para ser ignorado en mi proyecto:
node_modules
src
.babelrc
.gitignore
.npmignore
.prettierrc
rollup.config.ts
test
pnpm-lock.yaml
global.d.ts
tsconfig.json
.DS_Store
test-project

Excepto los archivos en .npmignore, se cargarán en npm.

Publicar paquete npm

Primero registre una cuenta npm y luego vaya a la página de inicio para buscar el encabezado del paquete. Habrá un mensaje que le pedirá que vincule la política de verificación de seguridad 2FA. Simplemente siga las instrucciones y complete el proceso. Probablemente signifique vincular un otp ( contraseña de un solo uso) contraseña de un solo uso. Descargue una aplicación de autenticación de Google y podrá generar un código escaneando la página web para vincular la cuenta y luego seguir las instrucciones. Esta contraseña de un solo uso le pedirá que ingrese otp la primera vez npm login y npm Publish, y luego te permitirá ir directamente al navegador. Solo verifica

Antes de cada lanzamiento, debe modificar el número de versión de package.json. Si no puede publicar la misma versión, se informará un error.

Antes de publicar, es mejor buscar el nombre del paquete (el nombre de package.json es el nombre del paquete npm) para ver si ya existe; de ​​lo contrario, si usa el nombre existente, se le indicará que No tiene permiso para actualizar los paquetes de otras personas. Es mejor usar @su propio nombre/nombre del paquete. Agregar un prefijo de nombre de usuario a este formato es equivalente a un espacio de nombres y la posibilidad de duplicación será mucho menor.

Ejemplo de código completo de 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"
}

explicación de root/package.json:

  1. palabras clave: Palabras clave para buscar su paquete
  2. descripción: una breve descripción debajo de la miniatura al buscar un paquete
  3. Licencia: el acuerdo del proyecto. ISC es una versión simplificada del MIT, lo que significa que otros pueden modificar y utilizar libremente su proyecto.
  4. peerDependencies: los requisitos de dependencia externa utilizados en el proyecto le dicen al proyecto que usa este paquete que estas versiones de dependencia deben cumplir con este requisito; de lo contrario, se informará un error. Cuando el proyecto que usa este paquete descarga dependencias, descargará peerDep en lugar de descargar todos los Paquetes externos en nuestro proyecto. Ven aquí.

Primero, cambie a la fuente npm antes de ejecutar el inicio de sesión de npm. No use la fuente Taobao, de lo contrario se informará un error. Ingrese la información y siga los pasos.

Después de iniciar sesión correctamente, ejecute npm publicar --acceder a publicación pública

–access public le dice a npm que publique paquetes públicos porque los paquetes privados no se pueden publicar de forma predeterminada. ¿Olvidó si los paquetes privados se cobran o algo así? Si especifica este parámetro de comando, no se informarán errores de permiso.

Después de que no se informen errores en el camino, puede mirar el paquete npm y ver el paquete que publicó:
Publicar paquete npm exitoso

documento

Utilice el documento, que es el archivo README.md en el directorio raíz, para completar este archivo.

Supongo que te gusta

Origin blog.csdn.net/Suk__/article/details/130511222
Recomendado
Clasificación