unplugin-aliyun-oss
v1.1.1
Published
Aliyun OSS plugin powered by unplugin.
Maintainers
Readme
unplugin-aliyun-oss
Upload generated build assets to Aliyun OSS after bundling. Powered by unplugin, with adapters for Vite, Webpack, Rollup, Rolldown, esbuild, Rspack, and Farm.
Features
- Runs after the bundler has written build assets.
- Supports dry runs with
test: true, so OSS credentials are not required while checking file discovery and object keys. - Converts local output paths into OSS object keys with
buildRoot,dist, and optionalsetOssPathcontrol. - Can skip existing objects when
overwrite: falseis enabled. - Can remove uploaded local files and prune empty directories after upload.
- Can publish version metadata through a user-provided
setVersioncallback.
Support
The plugin runtime supports Node.js >=20.19.0. This matches the minimum Node
version required by the current unplugin and builder adapter toolchain.
| Builder | Import | Notes |
| -------- | ------------------------------ | --------------------------------------------------------------------------------------- |
| Vite | unplugin-aliyun-oss/vite | Vite ^4 to ^8; output root is inferred from build.outDir. |
| Webpack | unplugin-aliyun-oss/webpack | Webpack 4 and 5; output root is inferred from output.path. |
| Rollup | unplugin-aliyun-oss/rollup | Rollup ^3 and ^4; output root is inferred from output.dir or output.file. |
| Rolldown | unplugin-aliyun-oss/rolldown | Rolldown ^1; uses the Rollup-compatible output options. |
| esbuild | unplugin-aliyun-oss/esbuild | esbuild >=0.18; output root is inferred from outdir or outfile. |
| Rspack | unplugin-aliyun-oss/rspack | Rspack ^1 and ^2; output root is inferred from output.path. |
| Farm | unplugin-aliyun-oss/farm | Uses the unplugin Farm adapter. Set buildRoot explicitly for predictable object keys. |
Installation
npm i -D unplugin-aliyun-osspnpm add -D unplugin-aliyun-ossyarn add -D unplugin-aliyun-ossbun add -D unplugin-aliyun-ossCredentials
Keep OSS credentials outside source control and read them from environment variables:
OSS_REGION=oss-cn-hangzhou
OSS_BUCKET=my-bucket
OSS_ACCESS_KEY_ID=your-access-key-id
OSS_ACCESS_KEY_SECRET=your-access-key-secretregion, bucket, accessKeyId, and accessKeySecret are required unless
test: true is enabled.
Quick Start
// vite.config.ts
import { defineConfig } from "vite";
import Oss from "unplugin-aliyun-oss/vite";
export default defineConfig({
plugins: [
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION!,
accessKeyId: process.env.OSS_ACCESS_KEY_ID!,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!,
bucket: process.env.OSS_BUCKET!,
}),
],
});With the config above, dist/assets/app.js is uploaded to
/static/assets/app.js.
Framework Usage
// vite.config.ts
import { defineConfig } from "vite";
import Oss from "unplugin-aliyun-oss/vite";
export default defineConfig({
plugins: [
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION!,
accessKeyId: process.env.OSS_ACCESS_KEY_ID!,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!,
bucket: process.env.OSS_BUCKET!,
}),
],
});// webpack.config.js
import path from "node:path";
import Oss from "unplugin-aliyun-oss/webpack";
export default {
output: {
path: path.resolve("dist"),
},
plugins: [
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
quitWpOnError: true,
}),
],
};// rollup.config.ts
import Oss from "unplugin-aliyun-oss/rollup";
export default {
input: "src/index.ts",
output: {
dir: "dist",
format: "esm",
},
plugins: [
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
}),
],
};// rolldown.config.ts
import Oss from "unplugin-aliyun-oss/rolldown";
export default {
input: "src/index.ts",
output: {
dir: "dist",
format: "esm",
},
plugins: [
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
}),
],
};// build.ts
import { build } from "esbuild";
import Oss from "unplugin-aliyun-oss/esbuild";
await build({
entryPoints: ["src/index.ts"],
bundle: true,
outdir: "dist",
plugins: [
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
}),
],
});// rspack.config.js
import path from "node:path";
import Oss from "unplugin-aliyun-oss/rspack";
export default {
output: {
path: path.resolve("dist"),
},
plugins: [
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
quitWpOnError: true,
}),
],
};// farm.config.ts
import Oss from "unplugin-aliyun-oss/farm";
export default {
plugins: [
Oss({
from: "dist/**/*",
buildRoot: "dist",
dist: "/static",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
}),
],
};Common Patterns
Dry run
Use test: true to verify matched files and OSS keys without creating an Aliyun
OSS client:
Oss({
from: "dist/**/*",
dist: "/static",
test: true,
});Custom object keys
Use setOssPath when the OSS key should not mirror the build output directory:
import path from "node:path";
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
setOssPath(filePath) {
return `/assets/${path.basename(filePath)}`;
},
});Use from to exclude files from the upload set. In the current version,
setOssPath should return a concrete string for every matched file.
Skip existing objects
Set overwrite: false to check whether an object already exists before upload.
Existing objects are skipped, and upload requests include
x-oss-forbid-overwrite.
Oss({
from: "dist/**/*",
overwrite: false,
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
});Delete local files after upload
Oss({
from: "dist/**/*",
deleteOrigin: true,
deleteEmptyDir: true,
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
});Publish version metadata
setVersion is called after a non-test upload pass finishes when version is
not empty:
Oss({
from: "dist/**/*",
version: process.env.APP_VERSION ?? "",
region: process.env.OSS_REGION,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
bucket: process.env.OSS_BUCKET,
async setVersion({ version }) {
await fetch("https://example.com/assets-version", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ version }),
});
},
});Object Key Rules
fromselects local files with glob patterns. Directories are ignored before upload.- If
setOssPathis provided, its returned string is used as the per-file object path. - Otherwise, the object path is relative to
buildRootwhen you provide it. - If
buildRootis not provided, most adapters infer the bundler output root. distis prefixed to the object path.
Examples:
| Local file | Config | OSS key |
| -------------------- | ----------------------------------------------------------- | ----------------------- |
| dist/index.html | from: "dist/**/*", buildRoot: "dist", dist: "/static" | /static/index.html |
| dist/assets/app.js | from: "dist/**/*", buildRoot: "dist", dist: "/static" | /static/assets/app.js |
| dist/assets/app.js | dist: "/cdn", setOssPath: () => "/app.js" | /cdn/app.js |
Options
interface Options {
region?: string;
accessKeyId?: string;
accessKeySecret?: string;
bucket?: string;
from: string | string[];
test?: boolean;
verbose?: boolean;
dist?: string;
buildRoot?: string;
deleteOrigin?: boolean;
deleteEmptyDir?: boolean;
timeout?: number;
setOssPath?: (filePath: string) => string | false | null | undefined;
overwrite?: boolean;
quitWpOnError?: boolean;
version?: string;
setVersion?: (data: { version: string }) => void | Promise<void>;
}| Option | Required | Default | Description |
| ----------------- | ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| from | Yes | - | Files to upload. Supports one glob or an array of globs, such as dist/**/*. Directories matched by the glob are ignored. |
| region | Yes, unless test: true | - | Aliyun OSS region, for example oss-cn-hangzhou. |
| accessKeyId | Yes, unless test: true | - | Aliyun access key id. |
| accessKeySecret | Yes, unless test: true | - | Aliyun access key secret. |
| bucket | Yes, unless test: true | - | Aliyun OSS bucket name. |
| test | No | false | Dry run mode. Files are discovered and target paths are printed without creating an OSS client. |
| verbose | No | true | Prints grouped logs with source file, OSS key, object status, upload action, result URL, and failures. |
| dist | No | "" | OSS object key prefix. |
| buildRoot | No | "." | Local root used to make object keys relative. Most adapters infer the bundler output path when buildRoot is not provided. |
| deleteOrigin | No | false | Deletes local files after successful upload. |
| deleteEmptyDir | No | false | Removes empty parent directories after deleting uploaded files. |
| timeout | No | 60000 | OSS request timeout in milliseconds. |
| setOssPath | No | - | Overrides the generated OSS object key for each matched file. Return a string for every matched file; use from to filter files out of the upload set. |
| overwrite | No | true | Overwrites existing OSS objects by default. Set overwrite: false to skip existing objects and send x-oss-forbid-overwrite. |
| quitWpOnError | No | false | Makes Webpack and Rspack builds fail when an upload fails. Other adapters collect the failure and continue the upload pass. |
| version | No | "" | Version value passed to setVersion. |
| setVersion | No | - | Callback used to publish version metadata after a non-test upload pass finishes and version is not empty. Callback errors are logged. |
Troubleshooting
| Symptom | What to check |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| No files are uploaded | Check that from points at files that exist after the build has finished. Use test: true to print matched files without OSS writes. |
| OSS keys include too much local path | Set buildRoot to the build output directory, for example buildRoot: "dist". |
| Farm uploads use unexpected keys | Set buildRoot explicitly because Farm does not provide an inferred output path here. |
| Webpack or Rspack succeeds even when upload fails | Set quitWpOnError: true. |
| Existing OSS objects should not be replaced | Set overwrite: false. |
Development
This repository uses Node.js 20.19.0 for local development. Use the version
declared in .node-version.
pnpm install
pnpm run typecheck
pnpm run build
pnpm run testFocused checks:
pnpm run test -- tests/uploader.test.ts
pnpm run test -- tests/builders.test.tsAgent Usage
Copy this prompt into your coding agent when you want it to add Aliyun OSS uploads to an existing project:
Add `unplugin-aliyun-oss` to this project and wire it into the current bundler.
Requirements:
1. Detect the package manager and install `unplugin-aliyun-oss` as a dev
dependency.
2. Detect the active bundler and import the matching adapter:
- Vite: `unplugin-aliyun-oss/vite`
- Webpack: `unplugin-aliyun-oss/webpack`
- Rollup: `unplugin-aliyun-oss/rollup`
- Rolldown: `unplugin-aliyun-oss/rolldown`
- esbuild: `unplugin-aliyun-oss/esbuild`
- Rspack: `unplugin-aliyun-oss/rspack`
- Farm: `unplugin-aliyun-oss/farm`
3. Read OSS settings from environment variables. Do not hard-code secrets:
- `OSS_REGION`
- `OSS_BUCKET`
- `OSS_ACCESS_KEY_ID`
- `OSS_ACCESS_KEY_SECRET`
4. Configure the plugin to upload the production build output. Use the actual
output directory from the bundler config, usually `dist/**/*`.
5. Set `dist` to the OSS object prefix I want assets published under. If I have
not specified one, ask before choosing it.
6. For Farm, or whenever the output root cannot be inferred safely, set
`buildRoot` explicitly to the build output directory.
7. Keep local validation non-destructive. Use `test: true` first unless I
explicitly provide a real OSS test bucket and ask for live upload
verification.
8. After editing config, run the project typecheck/build command that already
exists in `package.json`. Do not invent unrelated tooling.
9. Report the changed files, the selected adapter, the final OSS key mapping for
one example file, and the verification command output.Example target config the agent can adapt:
import Oss from "unplugin-aliyun-oss/vite";
Oss({
from: "dist/**/*",
dist: "/static",
region: process.env.OSS_REGION!,
bucket: process.env.OSS_BUCKET!,
accessKeyId: process.env.OSS_ACCESS_KEY_ID!,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!,
});