關於tsup工具構建專案庫使用過程

羊先生發表於2024-12-11

最近一直在寫前端專案、然後又使用strapi在寫後端服務,在使用到加解密這快內容,相同的程式碼需要複製貼上,索性寫一個工具吧,於是帶著研究,百度了一番,目前常見的工具有webpack、vite、rollup,相對主流的vite和rollup目前比較流行,webpack實在是卡頓

於是我又找找看看,又翻翻看,看到了tsup,構建方式簡單,不用繁瑣的配置,這不正是我想要的嘛

介紹一下tsup

tsup 是一個用於打包web指令碼的輕量級工具,它基於 esbuild,旨在提供快速且簡單的打包體驗。tsup 支援多種輸出格式,如 CommonJS、ES 模組、UMD 等

特點

  1. 快速打包:利用 esbuild 的高效能,tsup 能夠非常快速地打包 TypeScript 專案
  2. 多格式輸出:支援多種輸出格式,包括 CommonJS (cjs)、ES 模組 (esm)、UMD 等
  3. 零配置:預設情況下,tsup 可以自動檢測專案配置並進行打包,無需複雜的配置檔案
  4. 靈活配置:可以透過命令列引數或配置檔案進行靈活配置
  5. 樹搖最佳化:自動進行樹搖最佳化,減少最終包的大小
  6. TypeScript 支援:原生支援 TypeScript,無需額外配置

看下如何使用

需要先安裝它,我們先npm init,初始化基本專案專案環境,然後進行安裝

npm i tsup -D
# Or Yarn
yarn add tsup --dev
# Or pnpm
pnpm add tsup -D

安裝完成

在根目錄下手動建一個tsup.config.ts檔案,這個就是tsup的配置檔案,這裡我列出我的配置檔案

import {  defineConfig } from 'tsup';

export default defineConfig([
    {
        format: ['esm', 'cjs', 'iife'], // 指定輸出的模組格式。常見的格式有 esm(ES 模組)、cjs(CommonJS)、iife(立即執行函式表示式)等
        entry: ['./example/index.js'], // 指定專案的入口檔案。可以是一個或多個檔案路徑
        outDir: 'lib', // 指定輸出檔案的目錄
        platform: 'neutral', // 指定目標平臺。常見的值有 neutral(中立平臺,適用於所有環境)、browser(瀏覽器環境)、node(Node.js 環境)
        globalName: 'WebUtils', //當輸出格式為 iife 時,指定全域性變數的名稱
        outExtension({ format }) { // 自定義輸出檔案的副檔名。接收一個物件引數,包含當前的格式資訊
            if (format === 'iife') return { js: '.browser.js' };
            return { js: `.${format}.js` };
        },
        minify: true, // 是否啟用程式碼壓縮
        sourcemap: true, // 是否生成源對映檔案
        shims: true, // 是否啟用 polyfills 和 shims,以確保相容性
        clean: true,  // 清理輸出目錄
        dts: true, // 是否生成 TypeScript 宣告檔案
    }
]);

我已經在後面都標註說明了,不要我在解釋了吧

編寫加解密程式碼

這裡我使用的是crypto-jsjsencrypt,兩個倉庫,一個是對稱加密,一個和做非對稱加密,這個兩個相容瀏覽器端和Node.js環境

import CryptoJS from "crypto-js";
import JSEncrypt from "jsencrypt"
/**
 * 對稱加密
 * @param word
 * @param iv
 * @param key
 * @returns {string}
 */
function aesEncrypt(word, iv, key) {
    let str = typeof word == "string" ? word : JSON.stringify(word);
    const data = CryptoJS.enc.Utf8.parse(str);
    const encrypted = CryptoJS.AES.encrypt(data, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return encrypted.toString();
}


/**
 * 對稱解密
 * @param word
 * @param iv
 * @param key
 * @returns {string}
 */
function aesDecrypt(word, iv, key) {
    const decrypt = CryptoJS.AES.decrypt(word, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return CryptoJS.enc.Utf8.stringify(decrypt);
}


/**
 * 獲取加密的金鑰
 * @param iv
 * @param key
 */
function getCrypto({iv = '', key = ''} = {}) {
    return {
        CryptoJS,
        iv: CryptoJS.enc.Utf8.parse(iv),
        key: CryptoJS.enc.Utf8.parse(key), //16位
    }
}


/**
 * 非對稱加密
 * @param str
 * @param publicKey
 * @returns {*}
 */
const rsaEncrypt = (str, publicKey) => {
    let encrypted = new JSEncrypt() // 建立加密物件例項
    encrypted.setPublicKey(publicKey) // 設定公鑰
    return encrypted.encrypt(str) // 對內容進行加密
}


/**
 * 非對稱解密
 * @param str
 * @param privateKey
 */
const rasDecrypt = (str, privateKey) => {
    const decrypted = new JSEncrypt() // 建立解密物件例項
    decrypted.setPrivateKey(privateKey) // 設定私鑰
    return decrypted.decrypt(str) // 拿私鑰解密內容
}

/**
 * 非對稱生成Key
 * @param keySize
 */
const getJSEncrypt = (keySize = '512') => {
    const crypt = new JSEncrypt({default_key_size: keySize});
    return new Promise((resolve) => {
        crypt.getKey(function () {
            return resolve({
                privateKey: crypt.getPrivateKey(),
                publicKey: crypt.getPublicKey()
            })
        })
    })
}

export {
    aesEncrypt,
    aesDecrypt,
    getCrypto,
    rsaEncrypt,
    rasDecrypt,
    getJSEncrypt
}

打包構建

執行npx tsup

image.png

構建成功,並且我也生成了lib目錄下的檔案

測試指令碼

我寫了個列子進行測試

import {aesEncrypt,aesDecrypt, getCrypto,rasDecrypt,rsaEncrypt,getJSEncrypt} from "../packages/index";

const {iv,key} = getCrypto({
    iv: '1234567890123456',
    key: '1234567890123456'
})

getJSEncrypt().then((res)=>{
    console.log(res)
})

使用node進行測試

node ./lib/index.cjs.js

image.png

發現jsencrypt出現了window,這肯定就不行了哇,node環境那來的window的,如是我逛了下百度,找到了答案,也就是在程式碼的前面加上window=this

這是打包出來的程式碼,需要在前面加上window=this,我先手動加上進行測試

image.png

測試正常,這裡我生成了一個非對稱金鑰對

image.png

自動加window=this

當實現自動化的時候,我就想到了gulp, Gulp 是一個流式構建系統,用於自動化前端工作流程。它使用 JavaScript 和 Node.js,透過一系列外掛和任務來處理檔案和資源,它的特點如下

  1. 流式處理:Gulp 使用 Node.js 流來處理檔案,這意味著檔案可以在記憶體中被處理,而不需要多次讀寫磁碟,從而提高效能。
  2. 簡潔的 API:Gulp 的 API 非常簡潔,易於理解和使用。
  3. 豐富的外掛生態系統:Gulp 擁有龐大的外掛生態系統,可以輕鬆找到你需要的外掛,如編譯 LESS/SASS、壓縮 CSS/JS、影像最佳化等。
  4. 任務自動化:Gulp 可以自動化常見的開發任務,如程式碼檢查、檔案合併、程式碼壓縮、檔案監聽等。

使用gulp

在根目錄下新建檔案gulpfile.js,內容如下

const {src, dest} = require('gulp');
const insert = require('gulp-insert');

function defaultTask(cb) {
    src('lib/index.cjs.js') // 指定要編輯的檔案路徑
        .pipe(insert.prepend('window = this;')) // 向檔案開頭插入指定的字串
        .pipe(dest('lib')); // 指定輸出目錄
    cb();
}

exports.default = defaultTask

注意gulp的寫法,gulp一直在升級,寫法和使用方式一直在變化

執行gulp

測試成功
image.png

迴歸功能,完整加解密程式碼

import CryptoJS from "crypto-js";
import JSEncrypt from "jsencrypt"
/**
 * 對稱加密
 * @param word
 * @param iv
 * @param key
 * @returns {string}
 */
function aesEncrypt(word, iv, key) {
    let str = typeof word == "string" ? word : JSON.stringify(word);
    const data = CryptoJS.enc.Utf8.parse(str);
    const encrypted = CryptoJS.AES.encrypt(data, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return encrypted.toString();
}


/**
 * 對稱解密
 * @param word
 * @param iv
 * @param key
 * @returns {string}
 */
function aesDecrypt(word, iv, key) {
    const decrypt = CryptoJS.AES.decrypt(word, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return CryptoJS.enc.Utf8.stringify(decrypt);
}


/**
 * 獲取加密的金鑰
 * @param iv
 * @param key
 */
function getCrypto({iv = '', key = ''} = {}) {
    return {
        CryptoJS,
        iv: CryptoJS.enc.Utf8.parse(iv),
        key: CryptoJS.enc.Utf8.parse(key), //16位
    }
}


/**
 * 非對稱加密
 * @param str
 * @param publicKey
 * @returns {*}
 */
const rsaEncrypt = (str, publicKey) => {
    let encrypted = new JSEncrypt() // 建立加密物件例項
    encrypted.setPublicKey(publicKey) // 設定公鑰
    return encrypted.encrypt(str) // 對內容進行加密
}


/**
 * 非對稱解密
 * @param str
 * @param privateKey
 */
const rasDecrypt = (str, privateKey) => {
    const decrypted = new JSEncrypt() // 建立解密物件例項
    decrypted.setPrivateKey(privateKey) // 設定私鑰
    return decrypted.decrypt(str) // 拿私鑰解密內容
}

/**
 * 非對稱生成Key
 * @param keySize
 */
const getJSEncrypt = (keySize = '512') => {
    const crypt = new JSEncrypt({default_key_size: keySize});
    return new Promise((resolve) => {
        crypt.getKey(function () {
            return resolve({
                privateKey: crypt.getPrivateKey(),
                publicKey: crypt.getPublicKey()
            })
        })
    })
}

export {
    aesEncrypt,
    aesDecrypt,
    getCrypto,
    rsaEncrypt,
    rasDecrypt,
    getJSEncrypt
}

測試程式碼

import {aesEncrypt,aesDecrypt, getCrypto,rasDecrypt,rsaEncrypt,getJSEncrypt} from "../packages/index";

const {iv,key} = getCrypto({
    iv: '1234567890123456',
    key: '1234567890123456'
})

// getJSEncrypt().then((res)=>{
//     console.log(res)
// })

//
const privateKey = '-----BEGIN RSA PRIVATE KEY-----\n' +
    'MIIBOAIBAAJASj9QjNul1xu4Bpq6MByZJePADyBsoLvoGj+sSLuaPEy4pdJ6lEqW\n' +
    '0EjpT1nakDzlwXB1IDHQeeHlga31KkSUKwIDAQABAkAxWlLFvr9G9ELoCPOYRXo7\n' +
    'aF9i7q+mTCFlSUvQ8Pr9915Aot8qwjW3tcH0HhRbytikEmZ2esplPd832ie5qRKZ\n' +
    'AiEAigM6NID/wtv01bGBbmOgUePeD5UnHbD2IiwnAKcJ6/cCIQCJuLmkVNrOcVNe\n' +
    'Fz4vFRqvjOd95XBwlFkI4nfTVC7EbQIgYGPYpwrhlmqhGQ6cY0jZk9geI6v8YdRS\n' +
    'U5Oaue3wFAkCIDXE9F3Pb1oYbrcWlgWl1LRja+IAWUTq9lP8r1HH1TaFAiBSZ8oJ\n' +
    'Obty329SSgnl7ZICHome91o1BoxwWU5IBRKNaQ==\n' +
    '-----END RSA PRIVATE KEY-----'

const publicKey = '-----BEGIN PUBLIC KEY-----\n' +
    'MFswDQYJKoZIhvcNAQEBBQADSgAwRwJASj9QjNul1xu4Bpq6MByZJePADyBsoLvo\n' +
    'Gj+sSLuaPEy4pdJ6lEqW0EjpT1nakDzlwXB1IDHQeeHlga31KkSUKwIDAQAB\n' +
    '-----END PUBLIC KEY-----'


const str1  = aesEncrypt('123456', iv, key)
console.log(str1)
console.log(aesDecrypt(str1, iv, key))
console.log('------')

const str2 = rsaEncrypt('abcdefg', publicKey)
console.log(str2)
console.log(rasDecrypt(str2, privateKey))

執行結果,均沒有問題

image.png

原始碼我放在了github上面有興趣的看一看https://github.com/hangjob/web-utils

相關文章