vite-plugin-cross-origin-storage 1.8.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -201
- package/README.md +61 -110
- package/dist/index.d.mts +32 -0
- package/dist/index.mjs +242 -0
- package/dist/loader.d.mts +38 -0
- package/dist/loader.entry.d.mts +1 -0
- package/dist/loader.entry.mjs +7 -0
- package/dist/loader.mjs +51 -0
- package/package.json +53 -51
- package/dist/index.d.ts +0 -17
- package/dist/index.js +0 -2115
- package/dist/loader.js +0 -134
- package/loader.js +0 -134
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import MagicString from "magic-string";
|
|
5
|
+
import { rolldown } from "rolldown";
|
|
6
|
+
import { parseAst } from "rolldown/parseAst";
|
|
7
|
+
|
|
8
|
+
//#region src/index.ts
|
|
9
|
+
const MANIFEST_PLACEHOLDER = "__COS_MANIFEST__";
|
|
10
|
+
/**
|
|
11
|
+
* Recipe version embedded in every content-addressed specifier. Bump this
|
|
12
|
+
* whenever the build recipe (bundler version, options, define replacements)
|
|
13
|
+
* changes in a way that alters emitted bytes, so chunks built under different
|
|
14
|
+
* recipes cannot silently collide on the same SHA-256.
|
|
15
|
+
*/
|
|
16
|
+
const RECIPE = "cos1";
|
|
17
|
+
function defaultLoaderEntry() {
|
|
18
|
+
for (const ext of ["mjs", "ts"]) {
|
|
19
|
+
const candidate = fileURLToPath(new URL(`./loader.entry.${ext}`, import.meta.url));
|
|
20
|
+
if (existsSync(candidate)) return candidate;
|
|
21
|
+
}
|
|
22
|
+
throw new Error("[cos] could not locate the runtime loader entry");
|
|
23
|
+
}
|
|
24
|
+
function contentSpecifier(hash) {
|
|
25
|
+
return `${RECIPE}:${hash}`;
|
|
26
|
+
}
|
|
27
|
+
function toMatchers(packages) {
|
|
28
|
+
return packages.map((p) => typeof p === "string" ? new RegExp(`^${p}$`) : p);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Bundle the runtime loader into a self-contained IIFE with rolldown, leaving
|
|
32
|
+
* `__COS_MANIFEST__` as a literal token for the caller to substitute. Bundling
|
|
33
|
+
* from source keeps the loader correct regardless of how the host build loaded
|
|
34
|
+
* this plugin.
|
|
35
|
+
*/
|
|
36
|
+
async function bundleLoader(entry) {
|
|
37
|
+
const builder = await rolldown({
|
|
38
|
+
input: entry,
|
|
39
|
+
platform: "browser",
|
|
40
|
+
treeshake: true
|
|
41
|
+
});
|
|
42
|
+
const { output } = await builder.generate({
|
|
43
|
+
format: "iife",
|
|
44
|
+
minify: true
|
|
45
|
+
});
|
|
46
|
+
await builder.close();
|
|
47
|
+
return output[0].code;
|
|
48
|
+
}
|
|
49
|
+
/** Collect every static and dynamic import/export source string literal. */
|
|
50
|
+
function collectImportSources(code) {
|
|
51
|
+
const sources = [];
|
|
52
|
+
const visit = (node) => {
|
|
53
|
+
if (!node || typeof node !== "object") return;
|
|
54
|
+
if (Array.isArray(node)) {
|
|
55
|
+
for (const child of node) visit(child);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const record = node;
|
|
59
|
+
if (record.type === "ImportDeclaration" || record.type === "ExportNamedDeclaration" || record.type === "ExportAllDeclaration" || record.type === "ImportExpression") {
|
|
60
|
+
const source = record.source;
|
|
61
|
+
if (source?.type === "Literal" && typeof source.value === "string" && typeof source.start === "number" && typeof source.end === "number") sources.push({
|
|
62
|
+
value: source.value,
|
|
63
|
+
start: source.start,
|
|
64
|
+
end: source.end
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
for (const key in record) if (key !== "type") visit(record[key]);
|
|
68
|
+
};
|
|
69
|
+
visit(parseAst(code));
|
|
70
|
+
return sources;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Rewrite import/export specifiers by AST position rather than by pattern, so a
|
|
74
|
+
* managed specifier appearing in an ordinary string literal is never touched
|
|
75
|
+
* and dynamic imports are handled the same as static ones. Returns a sourcemap
|
|
76
|
+
* only when `withMap` is set (i.e. the source chunk already had one to keep
|
|
77
|
+
* valid); the standalone cos chunks have no downstream map and skip it.
|
|
78
|
+
*/
|
|
79
|
+
function rewriteSpecifiers(code, rewrites, fileName, withMap) {
|
|
80
|
+
const edits = collectImportSources(code).filter((s) => rewrites.has(s.value));
|
|
81
|
+
if (!edits.length) return { code };
|
|
82
|
+
const magic = new MagicString(code);
|
|
83
|
+
for (const { value, start, end } of edits) {
|
|
84
|
+
const quote = code[start];
|
|
85
|
+
magic.overwrite(start, end, `${quote}${rewrites.get(value)}${quote}`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
code: magic.toString(),
|
|
89
|
+
map: withMap ? magic.generateMap({
|
|
90
|
+
source: fileName,
|
|
91
|
+
hires: "boundary"
|
|
92
|
+
}) : void 0
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function joinBase(base, assetsDir) {
|
|
96
|
+
const prefix = base.endsWith("/") ? base : `${base}/`;
|
|
97
|
+
const dir = assetsDir.replace(/^\/+|\/+$/g, "");
|
|
98
|
+
return dir ? `${prefix}${dir}/` : prefix;
|
|
99
|
+
}
|
|
100
|
+
function cosPlugin(options) {
|
|
101
|
+
const packages = toMatchers(options.packages);
|
|
102
|
+
const loaderEntry = options.loaderEntry ?? defaultLoaderEntry();
|
|
103
|
+
const collected = /* @__PURE__ */ new Set();
|
|
104
|
+
let assetsDir = "assets";
|
|
105
|
+
let resolvedBase = "/";
|
|
106
|
+
let loaderTemplate;
|
|
107
|
+
let scriptContent = "";
|
|
108
|
+
return {
|
|
109
|
+
name: "vite-plugin-cos",
|
|
110
|
+
enforce: "pre",
|
|
111
|
+
apply: "build",
|
|
112
|
+
configResolved(config) {
|
|
113
|
+
assetsDir = config.build.assetsDir;
|
|
114
|
+
resolvedBase = config.base;
|
|
115
|
+
},
|
|
116
|
+
resolveId: {
|
|
117
|
+
order: "pre",
|
|
118
|
+
filter: { id: packages },
|
|
119
|
+
async handler(id, importer, resolveOptions) {
|
|
120
|
+
const resolved = await this.resolve(id, importer, {
|
|
121
|
+
...resolveOptions,
|
|
122
|
+
skipSelf: true
|
|
123
|
+
});
|
|
124
|
+
if (!resolved) return;
|
|
125
|
+
collected.add(resolved.id);
|
|
126
|
+
return {
|
|
127
|
+
id: `cos-ext:${resolved.id}`,
|
|
128
|
+
external: true
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
async generateBundle(_outputOptions, bundle) {
|
|
133
|
+
if (!collected.size) return;
|
|
134
|
+
const base = options.base ?? joinBase(resolvedBase, assetsDir);
|
|
135
|
+
const assetPrefix = assetsDir ? `${assetsDir.replace(/^\/+|\/+$/g, "")}/` : "";
|
|
136
|
+
const raw = /* @__PURE__ */ new Map();
|
|
137
|
+
const queue = [...collected];
|
|
138
|
+
while (queue.length) {
|
|
139
|
+
const input = queue.shift();
|
|
140
|
+
if (raw.has(input)) continue;
|
|
141
|
+
const deps = /* @__PURE__ */ new Set();
|
|
142
|
+
let code;
|
|
143
|
+
try {
|
|
144
|
+
const builder = await rolldown({
|
|
145
|
+
input,
|
|
146
|
+
platform: "browser",
|
|
147
|
+
treeshake: false,
|
|
148
|
+
plugins: [{
|
|
149
|
+
name: "cos-externalise-deps",
|
|
150
|
+
async resolveId(id, importer) {
|
|
151
|
+
if (!importer) return null;
|
|
152
|
+
const dep = await this.resolve(id, importer, { skipSelf: true });
|
|
153
|
+
if (!dep) return null;
|
|
154
|
+
deps.add(dep.id);
|
|
155
|
+
return {
|
|
156
|
+
id: `cos-dep:${dep.id}`,
|
|
157
|
+
external: true
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}]
|
|
161
|
+
});
|
|
162
|
+
const { output } = await builder.generate({
|
|
163
|
+
file: "chunk.js",
|
|
164
|
+
codeSplitting: false,
|
|
165
|
+
minify: true
|
|
166
|
+
});
|
|
167
|
+
await builder.close();
|
|
168
|
+
code = output[0].code;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
throw new Error(`[cos] cannot bundle managed package as a standalone chunk:\n ${input}\nIt likely imports build-time virtuals (e.g. \`#build/*\`, \`#imports\`) that only resolve inside the host build, so it is not a self-contained, shareable artifact. Only depend on packages whose source resolves from disk on its own.\n\nUnderlying error: ${error.message}`, { cause: error });
|
|
171
|
+
}
|
|
172
|
+
raw.set(input, {
|
|
173
|
+
code,
|
|
174
|
+
deps: [...deps]
|
|
175
|
+
});
|
|
176
|
+
queue.push(...deps);
|
|
177
|
+
}
|
|
178
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
179
|
+
const managed = {};
|
|
180
|
+
const visit = (id, stack) => {
|
|
181
|
+
const existing = hashes.get(id);
|
|
182
|
+
if (existing) return existing;
|
|
183
|
+
if (stack.includes(id)) throw new Error(`[cos] dependency cycle between managed packages: ${[...stack, id].join(" -> ")}`);
|
|
184
|
+
const { code, deps } = raw.get(id);
|
|
185
|
+
const rewrites = /* @__PURE__ */ new Map();
|
|
186
|
+
for (const dep of deps) rewrites.set(`cos-dep:${dep}`, contentSpecifier(visit(dep, [...stack, id])));
|
|
187
|
+
const { code: resolved } = rewriteSpecifiers(code, rewrites, "", false);
|
|
188
|
+
const hash = createHash("sha256").update(resolved).digest("hex");
|
|
189
|
+
const fileName = `${assetPrefix}${hash}.js`;
|
|
190
|
+
hashes.set(id, hash);
|
|
191
|
+
managed[contentSpecifier(hash)] = {
|
|
192
|
+
file: `${hash}.js`,
|
|
193
|
+
hash
|
|
194
|
+
};
|
|
195
|
+
bundle[fileName] = {
|
|
196
|
+
type: "asset",
|
|
197
|
+
fileName,
|
|
198
|
+
name: hash,
|
|
199
|
+
names: [hash],
|
|
200
|
+
originalFileName: null,
|
|
201
|
+
originalFileNames: [],
|
|
202
|
+
needsCodeReference: false,
|
|
203
|
+
source: resolved
|
|
204
|
+
};
|
|
205
|
+
return hash;
|
|
206
|
+
};
|
|
207
|
+
for (const id of raw.keys()) visit(id, []);
|
|
208
|
+
const appRewrites = /* @__PURE__ */ new Map();
|
|
209
|
+
for (const id of collected) appRewrites.set(`cos-ext:${id}`, contentSpecifier(hashes.get(id)));
|
|
210
|
+
let entry;
|
|
211
|
+
for (const file of Object.values(bundle)) {
|
|
212
|
+
if (file.type !== "chunk") continue;
|
|
213
|
+
const { code, map } = rewriteSpecifiers(file.code, appRewrites, file.fileName, !!file.map);
|
|
214
|
+
file.code = code;
|
|
215
|
+
if (map) file.map = map;
|
|
216
|
+
if (file.isEntry) entry = {
|
|
217
|
+
specifier: `${RECIPE}:entry`,
|
|
218
|
+
file: file.fileName.replace(new RegExp(`^${assetPrefix}`), "")
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (!entry) return;
|
|
222
|
+
const manifest = {
|
|
223
|
+
base,
|
|
224
|
+
entry,
|
|
225
|
+
chunks: managed
|
|
226
|
+
};
|
|
227
|
+
loaderTemplate ??= bundleLoader(loaderEntry);
|
|
228
|
+
scriptContent = (await loaderTemplate).replace(MANIFEST_PLACEHOLDER, JSON.stringify(manifest));
|
|
229
|
+
options.onGenerated?.(scriptContent);
|
|
230
|
+
},
|
|
231
|
+
transformIndexHtml: {
|
|
232
|
+
order: "post",
|
|
233
|
+
handler(html) {
|
|
234
|
+
if (options.onGenerated || !scriptContent) return html;
|
|
235
|
+
return html.replace(/<script type="module"[^>]*src="[^"]*"[^>]*><\/script>/g, "").replace("</head>", `<script id="cos-loader">${scriptContent}<\/script></head>`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//#endregion
|
|
242
|
+
export { cosPlugin, cosPlugin as default };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/loader.d.ts
|
|
2
|
+
declare global {
|
|
3
|
+
interface Navigator {
|
|
4
|
+
crossOriginStorage?: {
|
|
5
|
+
requestFileHandles: (descriptors: Array<{
|
|
6
|
+
algorithm: string;
|
|
7
|
+
value: string;
|
|
8
|
+
}>, options?: {
|
|
9
|
+
create?: boolean;
|
|
10
|
+
}) => Promise<Array<{
|
|
11
|
+
getFile: () => Promise<File>;
|
|
12
|
+
createWritable: () => Promise<{
|
|
13
|
+
write: (data: Blob) => Promise<void>;
|
|
14
|
+
close: () => Promise<void>;
|
|
15
|
+
}>;
|
|
16
|
+
}>>;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
interface CosManifest {
|
|
21
|
+
/** Public base path that managed chunks are served from, e.g. `/_nuxt/`. */
|
|
22
|
+
base: string;
|
|
23
|
+
/**
|
|
24
|
+
* The entry chunk to import once the import map is ready. It is app-specific, so it is
|
|
25
|
+
* loaded straight from the network rather than stored in COS by a content hash.
|
|
26
|
+
*/
|
|
27
|
+
entry: {
|
|
28
|
+
specifier: string;
|
|
29
|
+
file: string;
|
|
30
|
+
};
|
|
31
|
+
/** Map of content-addressed specifier to `{ file, hash }` for every COS-managed chunk. */
|
|
32
|
+
chunks: Record<string, {
|
|
33
|
+
file: string;
|
|
34
|
+
hash: string;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { CosManifest };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/loader.mjs
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region src/loader.ts
|
|
2
|
+
async function runCosLoader(manifest) {
|
|
3
|
+
const cos = navigator.crossOriginStorage;
|
|
4
|
+
const imports = {};
|
|
5
|
+
async function resolveChunk(hash, file) {
|
|
6
|
+
if (cos) try {
|
|
7
|
+
const [handle] = await cos.requestFileHandles([{
|
|
8
|
+
algorithm: "SHA-256",
|
|
9
|
+
value: hash
|
|
10
|
+
}]);
|
|
11
|
+
if (handle) {
|
|
12
|
+
const blob = await handle.getFile();
|
|
13
|
+
return URL.createObjectURL(new Blob([blob], { type: "text/javascript" }));
|
|
14
|
+
}
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error?.name !== "NotFoundError") console.error("[cos] lookup failed", error);
|
|
17
|
+
}
|
|
18
|
+
const response = await fetch(file);
|
|
19
|
+
const blob = new Blob([await response.blob()], { type: "text/javascript" });
|
|
20
|
+
if (cos) try {
|
|
21
|
+
const [handle] = await cos.requestFileHandles([{
|
|
22
|
+
algorithm: "SHA-256",
|
|
23
|
+
value: hash
|
|
24
|
+
}], { create: true });
|
|
25
|
+
if (handle) {
|
|
26
|
+
const writable = await handle.createWritable();
|
|
27
|
+
await writable.write(blob);
|
|
28
|
+
await writable.close();
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("[cos] store failed", error);
|
|
32
|
+
}
|
|
33
|
+
return URL.createObjectURL(blob);
|
|
34
|
+
}
|
|
35
|
+
await Promise.all(Object.entries(manifest.chunks).map(async ([specifier, { file, hash }]) => {
|
|
36
|
+
imports[specifier] = await resolveChunk(hash, manifest.base + file);
|
|
37
|
+
}));
|
|
38
|
+
imports[manifest.entry.specifier] = new URL(manifest.base + manifest.entry.file, location.origin).href;
|
|
39
|
+
const script = document.createElement("script");
|
|
40
|
+
script.type = "importmap";
|
|
41
|
+
script.textContent = JSON.stringify({ imports });
|
|
42
|
+
document.head.appendChild(script);
|
|
43
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
44
|
+
await import(
|
|
45
|
+
/* @vite-ignore */
|
|
46
|
+
manifest.entry.specifier
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { runCosLoader };
|
package/package.json
CHANGED
|
@@ -1,70 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-cross-origin-storage",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "2.0.1",
|
|
5
|
+
"description": "Vite plugin to extract shared dependencies into content-addressed chunks loaded from Cross-Origin Storage",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Daniel Roe",
|
|
8
|
+
"email": "daniel@roe.dev",
|
|
9
|
+
"url": "https://github.com/danielroe"
|
|
10
|
+
},
|
|
11
|
+
"contributors": [
|
|
12
|
+
{
|
|
13
|
+
"name": "Thomas Steiner",
|
|
14
|
+
"url": "https://github.com/tomayac"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/danielroe/cross-origin-storage.git",
|
|
21
|
+
"directory": "packages/vite-plugin-cross-origin-storage"
|
|
22
|
+
},
|
|
5
23
|
"keywords": [
|
|
6
|
-
"vite",
|
|
7
|
-
"plugin",
|
|
24
|
+
"vite-plugin",
|
|
8
25
|
"cross-origin-storage",
|
|
9
26
|
"cos",
|
|
10
|
-
"
|
|
27
|
+
"content-addressed",
|
|
11
28
|
"performance"
|
|
12
29
|
],
|
|
13
|
-
"
|
|
14
|
-
"license": "Apache-2.0",
|
|
15
|
-
"homepage": "https://github.com/tomayac/vite-plugin-cross-origin-storage#readme",
|
|
16
|
-
"repository": {
|
|
17
|
-
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/tomayac/vite-plugin-cross-origin-storage.git"
|
|
19
|
-
},
|
|
20
|
-
"bugs": {
|
|
21
|
-
"url": "https://github.com/tomayac/vite-plugin-cross-origin-storage/issues"
|
|
22
|
-
},
|
|
23
|
-
"type": "module",
|
|
24
|
-
"main": "./dist/index.js",
|
|
25
|
-
"module": "./dist/index.js",
|
|
26
|
-
"types": "./dist/index.d.ts",
|
|
30
|
+
"sideEffects": false,
|
|
27
31
|
"exports": {
|
|
28
32
|
".": {
|
|
29
|
-
"
|
|
30
|
-
"import": "./dist/index.js"
|
|
33
|
+
"import": "./dist/index.mjs"
|
|
31
34
|
}
|
|
32
35
|
},
|
|
36
|
+
"main": "./dist/index.mjs",
|
|
37
|
+
"module": "./dist/index.mjs",
|
|
38
|
+
"types": "./dist/index.d.mts",
|
|
33
39
|
"files": [
|
|
34
|
-
"dist"
|
|
35
|
-
"loader.js"
|
|
40
|
+
"dist"
|
|
36
41
|
],
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"prepublishOnly": "npm run build",
|
|
40
|
-
"test:simple:dev": "npm run build && vite testing/simple-test",
|
|
41
|
-
"test:simple:build": "npm run build && vite build testing/simple-test",
|
|
42
|
-
"test:simple:preview": "vite preview testing/simple-test",
|
|
43
|
-
"test:react-hello-world:dev": "npm run build && vite testing/react-hello-world",
|
|
44
|
-
"test:react-hello-world:build": "npm run build && vite build testing/react-hello-world",
|
|
45
|
-
"test:react-hello-world:preview": "vite preview testing/react-hello-world",
|
|
46
|
-
"test:react-todo:dev": "npm run build && vite testing/react-todo",
|
|
47
|
-
"test:react-todo:build": "npm run build && vite build testing/react-todo",
|
|
48
|
-
"test:react-todo:preview": "vite preview --port 4174 testing/react-todo"
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20.19.0"
|
|
49
44
|
},
|
|
50
45
|
"peerDependencies": {
|
|
51
|
-
"vite": "^
|
|
46
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"magic-string": "^0.30.21",
|
|
50
|
+
"rolldown": "1.1.0"
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
54
|
-
"@
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"typescript": "^6.0.3",
|
|
65
|
-
"vite": "^8.0.11"
|
|
53
|
+
"@types/node": "latest",
|
|
54
|
+
"eslint": "^10.4.1",
|
|
55
|
+
"hookable": "^5.5.3",
|
|
56
|
+
"playwright-core": "^1.61.1",
|
|
57
|
+
"tsdown": "^0.20.3",
|
|
58
|
+
"typescript": "~6.0.3",
|
|
59
|
+
"unhead": "^2.1.0",
|
|
60
|
+
"vite": "^7.3.5",
|
|
61
|
+
"vitest": "^4.1.8",
|
|
62
|
+
"vue": "3.5.39"
|
|
66
63
|
},
|
|
67
|
-
"
|
|
68
|
-
"
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsdown",
|
|
66
|
+
"dev": "vitest dev",
|
|
67
|
+
"lint": "eslint .",
|
|
68
|
+
"test": "pnpm test:unit && pnpm test:types",
|
|
69
|
+
"test:unit": "vitest run",
|
|
70
|
+
"test:types": "tsc --noEmit && vitest run --typecheck.only"
|
|
69
71
|
}
|
|
70
|
-
}
|
|
72
|
+
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Plugin } from 'vite';
|
|
2
|
-
|
|
3
|
-
interface CosPluginOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Pattern to include chunks to be managed by COS.
|
|
6
|
-
* Matches against the output filename (e.g. `assets/vendor-*.js`).
|
|
7
|
-
* Default: `['**\/*']` (all chunks, except the entry implementation detail)
|
|
8
|
-
*/
|
|
9
|
-
include?: string | RegExp | (string | RegExp)[];
|
|
10
|
-
/**
|
|
11
|
-
* Pattern to exclude chunks from being managed by COS.
|
|
12
|
-
*/
|
|
13
|
-
exclude?: string | RegExp | (string | RegExp)[];
|
|
14
|
-
}
|
|
15
|
-
declare function cosPlugin(options?: CosPluginOptions): Plugin;
|
|
16
|
-
|
|
17
|
-
export { type CosPluginOptions, cosPlugin as default };
|