diff --git a/README.md b/README.md
index 383cf0b..9902efa 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# vite-plugin-cos-cdnizer
-> 在日常开发中,经常需要将静态资源上传到云服务的对象存储中。传统方式需要开发者手动登录云服务器控制台进行上传,步骤繁琐。使用本插件后,开发者可以将静态资源放置在项目本地,插件将自动将这些资源上传到对象存储中。同时,插件会自动替换代码中的 `import xxx from 'xxx.png'` 为 CDN 地址,简化了开发流程,提高了效率。
+> 在日常开发中,经常需要将静态资源上传到云服务的对象存储中。传统方式需要开发者手动登录云服务器控制台进行上传,步骤繁琐。
+
+使用本插件后,只需要将静态资源放置在项目本地,插件将自动将命中的资源上传到对象存储中。同时,插件会自动将代码中的 import xxx from 'xxx.png' 替换为 CDN 地址,简化开发流程。
## 安装
@@ -27,11 +29,21 @@ export default defineConfig({
});
```
-
+## 效果展示
+
+插件会自动将本地引入的静态资源上传至 CDN,并替换原有代码中的引用地址为 CDN 地址。
+
+### 使用前
+
+![image-20240228下午63239358](https://static.rux.ink/uPic/image-20240228%E4%B8%8B%E5%8D%8863239358.png)
+
+### 使用后
+
+![image-20240228下午63154556](https://static.rux.ink/uPic/image-20240228%E4%B8%8B%E5%8D%8863154556.png)
![success](https://static.rux.ink/uPic/success.gif)
-> 已上传的文件会自动创建 `.cache.json` 进行记录,以便减少无用上传。
+> 插件会自动创建 `.cache.json` 文件进行记录,以减少无用的重复上传。
![cache](https://static.rux.ink/uPic/cache.gif)
diff --git a/examples/vite5-react/package.json b/examples/vite5-react/package.json
index 98cdbab..93ca5ed 100644
--- a/examples/vite5-react/package.json
+++ b/examples/vite5-react/package.json
@@ -19,6 +19,7 @@
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"@vitejs/plugin-react": "^4.2.1",
+ "dotenv": "^16.4.5",
"eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
diff --git a/examples/vite5-react/src/app.css b/examples/vite5-react/src/app.css
index b9d355d..338535f 100644
--- a/examples/vite5-react/src/app.css
+++ b/examples/vite5-react/src/app.css
@@ -1,42 +1,156 @@
-#root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
+@layer demo {
+ .compare {
+ display: grid;
+
+ > * {
+ grid-area: 1 / 1;
+ }
+
+ > section {
+ display: grid;
+ place-content: center;
+ }
+ }
+
+ .before {
+ clip-path: inset(0 calc(100% - var(--pos, 50%)) 0 0);
+ }
+
+ .after {
+ clip-path: inset(0 0 0 var(--pos, 50%));
+ }
+
+ input[type='range'] {
+ z-index: 1;
+ appearance: none;
+ appearance: none;
+ background: transparent;
+ cursor: pointer;
+
+ &::-webkit-slider-thumb {
+ appearance: none;
+ width: 4px;
+ height: 100dvh;
+ background-color: CanvasText;
+ }
+
+ &::-moz-range-thumb {
+ appearance: none;
+ width: 4px;
+ height: 100dvh;
+ background-color: CanvasText;
+ }
+ }
+}
+
+@layer demo.support {
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ }
+
+ html {
+ block-size: 100%;
+ color-scheme: dark light;
+ }
+
+ body {
+ min-block-size: 100%;
+ font-family: system-ui, sans-serif;
+ display: grid;
+ }
+
+ img {
+ max-block-size: 80dvh;
+ }
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: 'Figtree', sans-serif;
+}
+
+body {
+ display: grid;
+ place-content: center;
+ min-height: 100vh;
+ background: #000;
+}
+
+.container {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
+ gap: 1em;
+ width: 800px;
+ height: 500px;
+ transition: all 400ms;
+}
+
+.container:hover .box {
+ filter: grayscale(100%) opacity(24%);
+}
+
+.box {
+ position: relative;
+ background: var(--img) center center;
+ background-size: cover;
+ transition: all 400ms;
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
+.container .box:hover {
+ filter: grayscale(0%) opacity(100%);
}
-.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
+
+.container:has(.box-1:hover) {
+ grid-template-columns: 3fr 1fr 1fr 1fr 1fr;
}
-.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafbaa);
+
+.container:has(.box-2:hover) {
+ grid-template-columns: 1fr 3fr 1fr 1fr 1fr;
+}
+
+.container:has(.box-3:hover) {
+ grid-template-columns: 1fr 1fr 3fr 1fr 1fr;
+}
+
+.container:has(.box-4:hover) {
+ grid-template-columns: 1fr 1fr 1fr 3fr 1fr;
+}
+
+.container:has(.box-5:hover) {
+ grid-template-columns: 1fr 1fr 1fr 1fr 3fr;
}
-@keyframes logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
+.box:nth-child(odd) {
+ transform: translateY(-16px);
}
-@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
- }
+.box:nth-child(even) {
+ transform: translateY(16px);
}
-.card {
- padding: 2em;
+.box::after {
+ content: attr(data-text);
+ position: absolute;
+ bottom: 20px;
+ background: #000;
+ color: #fff;
+ padding: 10px 10px 10px 14px;
+ letter-spacing: 4px;
+ text-transform: uppercase;
+ transform: translateY(60px);
+ opacity: 0;
+ transition: all 400ms;
}
-.read-the-docs {
- color: #888;
+.box:hover::after {
+ transform: translateY(0);
+ opacity: 1;
+ transition-delay: 400ms;
}
diff --git a/examples/vite5-react/src/app.tsx b/examples/vite5-react/src/app.tsx
index afe48ac..0098ed6 100644
--- a/examples/vite5-react/src/app.tsx
+++ b/examples/vite5-react/src/app.tsx
@@ -1,35 +1,76 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from '/vite.svg'
-import './App.css'
+import React, { useState } from 'react';
+import Roboto from './assets/Roboto.svg';
+import Runner from './assets/Runner.svg';
+import img1 from './assets/img-1.jpg';
+import img2 from './assets/img-2.jpg';
+import img3 from './assets/img-3.jpg';
+import img4 from './assets/img-4.jpg';
+import img5 from './assets/img-5.jpg';
+import './app.css';
-function App() {
- const [count, setCount] = useState(0)
+const peoples = [
+ {
+ img: img1,
+ name: 'Renji'
+ },
+ {
+ img: img2,
+ name: 'Sora'
+ },
+ {
+ img: img3,
+ name: 'Kaito'
+ },
+ {
+ img: img4,
+ name: 'Tsuki'
+ },
+ {
+ img: img5,
+ name: 'Mitsui'
+ }
+];
- return (
- <>
-
- Vite + React
-
-
-
- Edit src/App.tsx
and save to test HMR
-
-
-
- Click on the Vite and React logos to learn more
-
- >
- )
-}
+export default function App() {
+ const [rangeValue, setRangeValue] = useState('50');
-export default App
+ return (
+ <>
+
+
+
+
+
+
+
+
) =>
+ setRangeValue(e.target.value)
+ }
+ />
+
+
+ {peoples.map((item, index) => (
+
+ ))}
+
+ >
+ );
+}
diff --git a/examples/vite5-react/src/assets/Roboto.svg b/examples/vite5-react/src/assets/Roboto.svg
new file mode 100644
index 0000000..3d6683d
--- /dev/null
+++ b/examples/vite5-react/src/assets/Roboto.svg
@@ -0,0 +1,49 @@
+
\ No newline at end of file
diff --git a/examples/vite5-react/src/assets/Runner.svg b/examples/vite5-react/src/assets/Runner.svg
new file mode 100644
index 0000000..a499607
--- /dev/null
+++ b/examples/vite5-react/src/assets/Runner.svg
@@ -0,0 +1,67 @@
+
\ No newline at end of file
diff --git a/examples/vite5-react/src/assets/img-1.jpg b/examples/vite5-react/src/assets/img-1.jpg
new file mode 100644
index 0000000..f2aa0db
Binary files /dev/null and b/examples/vite5-react/src/assets/img-1.jpg differ
diff --git a/examples/vite5-react/src/assets/img-2.jpg b/examples/vite5-react/src/assets/img-2.jpg
new file mode 100644
index 0000000..b97291e
Binary files /dev/null and b/examples/vite5-react/src/assets/img-2.jpg differ
diff --git a/examples/vite5-react/src/assets/img-3.jpg b/examples/vite5-react/src/assets/img-3.jpg
new file mode 100644
index 0000000..17de42b
Binary files /dev/null and b/examples/vite5-react/src/assets/img-3.jpg differ
diff --git a/examples/vite5-react/src/assets/img-4.jpg b/examples/vite5-react/src/assets/img-4.jpg
new file mode 100644
index 0000000..1be262e
Binary files /dev/null and b/examples/vite5-react/src/assets/img-4.jpg differ
diff --git a/examples/vite5-react/src/assets/img-5.jpg b/examples/vite5-react/src/assets/img-5.jpg
new file mode 100644
index 0000000..b4f107c
Binary files /dev/null and b/examples/vite5-react/src/assets/img-5.jpg differ
diff --git a/examples/vite5-react/src/assets/react.svg b/examples/vite5-react/src/assets/react.svg
deleted file mode 100644
index 6c87de9..0000000
--- a/examples/vite5-react/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/vite5-react/src/index.css b/examples/vite5-react/src/index.css
deleted file mode 100644
index 6119ad9..0000000
--- a/examples/vite5-react/src/index.css
+++ /dev/null
@@ -1,68 +0,0 @@
-:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-a:hover {
- color: #535bf2;
-}
-
-body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
-}
-
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
-}
diff --git a/examples/vite5-react/src/main.tsx b/examples/vite5-react/src/main.tsx
index 3d7150d..62a33ee 100644
--- a/examples/vite5-react/src/main.tsx
+++ b/examples/vite5-react/src/main.tsx
@@ -1,10 +1,4 @@
-import React from 'react'
-import ReactDOM from 'react-dom/client'
-import App from './App.tsx'
-import './index.css'
+import ReactDOM from 'react-dom/client';
+import App from './app';
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
- ,
-)
+ReactDOM.createRoot(document.getElementById('root')!).render();
diff --git a/examples/vite5-react/vite.config.ts b/examples/vite5-react/vite.config.ts
index 5a33944..f5511d8 100644
--- a/examples/vite5-react/vite.config.ts
+++ b/examples/vite5-react/vite.config.ts
@@ -1,7 +1,20 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import dotenv from 'dotenv';
+import path from 'node:path';
+import cdnizer from '../../dist/index';
+
+dotenv.config({ path: path.resolve(process.cwd(), '../', '.env.local') });
-// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react()],
-})
+ plugins: [
+ react(),
+ cdnizer({
+ secretId: process.env.VITE_SECRET_ID!,
+ secretKey: process.env.VITE_SECRET_KEY!,
+ bucket: 'md-1307877784',
+ region: 'ap-beijing',
+ domain: 'https://static.rux.ink/'
+ })
+ ]
+});
diff --git a/src/index.ts b/index.ts
similarity index 84%
rename from src/index.ts
rename to index.ts
index 659f764..0a681fc 100644
--- a/src/index.ts
+++ b/index.ts
@@ -2,8 +2,8 @@ import path from 'node:path';
import fs from 'node:fs';
import crypto from 'node:crypto';
import COS from 'cos-nodejs-sdk-v5';
+import chalk from 'chalk';
import { normalizePath, type Plugin } from 'vite';
-import { concatDomainAndPath, getTime, log, type TLogLevel } from './utils';
interface IStaticCdnizerPluginOptions {
/**
@@ -57,6 +57,28 @@ type TUploadFileResp = {
type TCacheData = Record;
+type TLogLevel = keyof typeof chalkConfig;
+
+type TLogMethods = {
+ [K in TLogLevel]: (msg: any) => void;
+};
+
+// Log config
+const chalkConfig = {
+ success: chalk.bold.green,
+ cache: chalk.bold.blue,
+ error: chalk.bold.red,
+ info: chalk.bold.gray
+};
+
+const logger = Object.keys(chalkConfig).reduce(
+ (acc, cur) => ({
+ ...acc,
+ [cur as TLogLevel]: (msg: any) => console.log(chalkConfig[cur as TLogLevel](msg))
+ }),
+ {} as TLogMethods
+);
+
const LOG_BANNER =
'\n-------------------------------------------------------\n\t\t 📝 COS uploadFile log\n-------------------------------------------------------';
@@ -74,6 +96,17 @@ const statusCodeLogLevels: Record = {
[StatusCode.NOTFOUND]: 'info'
};
+const concatDomainAndPath = (domain: string, path: string) =>
+ `${domain.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
+
+export const getTime = () => {
+ const date = new Date();
+ const hours = String(date.getHours()).padStart(2, '0');
+ const minutes = String(date.getMinutes()).padStart(2, '0');
+ const seconds = String(date.getSeconds()).padStart(2, '0');
+ return `${hours}:${minutes}:${seconds}`;
+};
+
export default function StaticCdnizerPlugin(options: IStaticCdnizerPluginOptions): Plugin {
const {
bucket,
@@ -209,15 +242,15 @@ export default function StaticCdnizerPlugin(options: IStaticCdnizerPluginOptions
: include.includes(fileExtension)
) {
if (++isFirst === 1) {
- log.info(LOG_BANNER);
+ logger.info(LOG_BANNER);
}
// 只关注获取到的结果
const { status, url, message } = await uploadFile(normalizedFile);
- // log 统一处理
+ // logger 统一处理
const logLevel = statusCodeLogLevels[status];
const logPrefix = message ? `${logLevel}: ${message}` : logLevel;
- log[logLevel](`[${getTime()}] (${logPrefix}) ${file} => ${url}`);
+ logger[logLevel](`[${getTime()}] (${logPrefix}) ${file} => ${url}`);
if ([StatusCode.SUCCESS, StatusCode.CACHE].includes(status)) {
return `export default '${url}';`;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6694fa5..9375587 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -89,6 +89,9 @@ importers:
'@vitejs/plugin-react':
specifier: ^4.2.1
version: 4.2.1(vite@5.1.4)
+ dotenv:
+ specifier: ^16.4.5
+ version: 16.4.5
eslint:
specifier: ^8.56.0
version: 8.57.0
@@ -893,6 +896,7 @@ packages:
resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
requiresBuild: true
dev: true
optional: true
@@ -901,6 +905,7 @@ packages:
resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
requiresBuild: true
dev: true
optional: true
@@ -909,6 +914,7 @@ packages:
resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
requiresBuild: true
dev: true
optional: true
@@ -917,6 +923,7 @@ packages:
resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
requiresBuild: true
dev: true
optional: true
@@ -925,6 +932,7 @@ packages:
resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
cpu: [x64]
os: [linux]
+ libc: [musl]
requiresBuild: true
dev: true
optional: true
@@ -1632,6 +1640,11 @@ packages:
is-obj: 2.0.0
dev: false
+ /dotenv@16.4.5:
+ resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
+ engines: {node: '>=12'}
+ dev: true
+
/ecc-jsbn@0.1.2:
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
dependencies:
diff --git a/src/utils/concatDomainAndPath.ts b/src/utils/concatDomainAndPath.ts
deleted file mode 100644
index 55c6aeb..0000000
--- a/src/utils/concatDomainAndPath.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const concatDomainAndPath = (domain: string, path: string) =>
- `${domain.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
diff --git a/src/utils/getTime.ts b/src/utils/getTime.ts
deleted file mode 100644
index 403eea1..0000000
--- a/src/utils/getTime.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const getTime = () => {
- const date = new Date();
- const hours = String(date.getHours()).padStart(2, '0');
- const minutes = String(date.getMinutes()).padStart(2, '0');
- const seconds = String(date.getSeconds()).padStart(2, '0');
- return `${hours}:${minutes}:${seconds}`;
-};
diff --git a/src/utils/index.ts b/src/utils/index.ts
deleted file mode 100644
index 2c82d1b..0000000
--- a/src/utils/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { concatDomainAndPath } from './concatDomainAndPath';
-export { getTime } from './getTime';
-export { log, TLogLevel } from './log';
diff --git a/src/utils/log.ts b/src/utils/log.ts
deleted file mode 100644
index 1ec661d..0000000
--- a/src/utils/log.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import chalk from 'chalk';
-
-export type TLogLevel = keyof typeof chalkConfig;
-
-export type TLogMethods = {
- [K in TLogLevel]: (msg: any) => void;
-};
-
-// Log config
-const chalkConfig = {
- success: chalk.bold.green,
- cache: chalk.bold.blue,
- error: chalk.bold.red,
- info: chalk.bold.gray
-};
-
-export const log = Object.keys(chalkConfig).reduce(
- (acc, cur) => ({
- ...acc,
- [cur as TLogLevel]: (msg: any) => console.log(chalkConfig[cur as TLogLevel](msg))
- }),
- {} as TLogMethods
-);
diff --git a/tsconfig.json b/tsconfig.json
index 67d3964..55f9274 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -31,7 +31,7 @@
"skipLibCheck": true
},
"include": [
- "src"
+ "index.ts"
],
"exclude": [
"node_modules"