@cnguu/vite-plugin-uni-cdn 2.1.0 → 2.2.0

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/dist/index.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fsPromises = require('node:fs/promises');
4
4
  const path = require('node:path');
5
+ const compilerSfc = require('@vue/compiler-sfc');
5
6
  const vite = require('vite');
6
7
 
7
8
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -10,6 +11,7 @@ const fsPromises__default = /*#__PURE__*/_interopDefaultCompat(fsPromises);
10
11
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
11
12
 
12
13
  const PLUGIN_NAME = "vite-plugin-uni-cdn";
14
+ const POSTCSS_PLUGIN_NAME = "postcss-plugin-uni-cdn";
13
15
  const VIRTUAL_MODULE_ID = `virtual:${PLUGIN_NAME}`;
14
16
  const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
15
17
 
@@ -107,7 +109,7 @@ class Context {
107
109
  this.options = {
108
110
  cdn: "",
109
111
  sourceDir: "static/cdn",
110
- include: ["**/*.{vue,css,scss,sass,less,styl}"],
112
+ include: ["**/*.vue"],
111
113
  exclude: ["**/node_modules/**", "**/uni_modules/**", "**/dist/**", "**/unpackage/**"],
112
114
  deleteOutputFiles: true,
113
115
  verbose: true,
@@ -138,6 +140,48 @@ class Context {
138
140
  return `${code.trim()}
139
141
  `;
140
142
  }
143
+ config() {
144
+ if (!this.cdnBasePath || !this.options.sourceDir) {
145
+ return;
146
+ }
147
+ const relSourceDir = vite.normalizePath(path__default.normalize(this.options.sourceDir)).replace(/^\/+/, "");
148
+ const staticSubPath = relSourceDir.startsWith("src/") ? relSourceDir.slice("src/".length) : relSourceDir;
149
+ const staticDir = `/${staticSubPath.replace(/^\/+/, "")}`;
150
+ const cdnBasePath = this.cdnBasePath;
151
+ const urlRegex = /url\(\s*(['"]?)([^'")\s]+)\1\s*\)/g;
152
+ return {
153
+ css: {
154
+ postcss: {
155
+ plugins: [
156
+ {
157
+ postcssPlugin: POSTCSS_PLUGIN_NAME,
158
+ Declaration: (decl) => {
159
+ if (!decl.value || !decl.value.startsWith("url(") || decl.value.includes("http") || decl.value.includes("data:")) {
160
+ return;
161
+ }
162
+ decl.value = decl.value.replace(urlRegex, (match, quote, originalPath) => {
163
+ if (originalPath.startsWith(staticDir)) {
164
+ let outputFileName = this.replaceUrlCache.get(originalPath);
165
+ if (!outputFileName) {
166
+ let relativePath = originalPath.slice(staticDir.length);
167
+ if (!relativePath.startsWith("/")) {
168
+ relativePath = `/${relativePath}`;
169
+ }
170
+ outputFileName = `${cdnBasePath}${relativePath}`;
171
+ this.replaceUrlCache.set(originalPath, outputFileName);
172
+ this.logger?.pathReplace?.(originalPath, outputFileName);
173
+ }
174
+ return `url(${quote}${outputFileName}${quote})`;
175
+ }
176
+ return match;
177
+ });
178
+ }
179
+ }
180
+ ]
181
+ }
182
+ }
183
+ };
184
+ }
141
185
  async configResolved(resolvedConfig) {
142
186
  this.projectRoot = resolvedConfig.root;
143
187
  this.logger = createLogger(this.options.verbose, resolvedConfig.logger);
@@ -194,51 +238,43 @@ class Context {
194
238
  if (!this.filter(filepath)) {
195
239
  return { code };
196
240
  }
197
- const transformed = this.replaceStaticToCdn(code);
198
- return { code: transformed };
199
- }
200
- generateBundle(options, bundle) {
201
- if (!this.sourceDirAbs || !this.assetDir) {
202
- return;
203
- }
204
- for (const [fileName, chunk] of Object.entries(bundle)) {
205
- if (chunk.type === "asset") {
206
- if (typeof chunk.source !== "string") {
207
- continue;
208
- }
209
- if (!/\.(?:css|js|mjs|html)$/.test(fileName)) {
210
- continue;
211
- }
212
- const before = chunk.source;
213
- chunk.source = this.replaceStaticToCdn(before);
214
- } else if (chunk.type === "chunk") {
215
- const before = chunk.code;
216
- chunk.code = this.replaceStaticToCdn(before);
217
- }
241
+ if (id.endsWith(".vue")) {
242
+ return this.processVueSfc(code);
218
243
  }
244
+ const transformedCode = this.replaceStaticToCdn(code);
245
+ return { code: transformedCode };
219
246
  }
220
247
  async closeBundle() {
221
248
  if (!this.sourceDirAbs || !this.assetDir) {
222
249
  return;
223
250
  }
224
251
  await this.uploadAliOSS();
225
- this.deleteOutputFiles();
252
+ await this.deleteOutputFiles();
253
+ }
254
+ processVueSfc(code) {
255
+ let transformedCode = code;
256
+ try {
257
+ const sfc = compilerSfc.parse(code);
258
+ transformedCode = this.processSfcBlock(transformedCode, sfc.descriptor.template);
259
+ transformedCode = this.processSfcBlock(transformedCode, sfc.descriptor.scriptSetup);
260
+ transformedCode = this.processSfcBlock(transformedCode, sfc.descriptor.script);
261
+ } catch (error) {
262
+ this.logger.error("\u89E3\u6790 Vue SFC \u5931\u8D25", error);
263
+ transformedCode = this.replaceStaticToCdn(code);
264
+ }
265
+ return { code: transformedCode };
266
+ }
267
+ processSfcBlock(code, block) {
268
+ if (!block) {
269
+ return code;
270
+ }
271
+ const { content, loc } = block;
272
+ const transformedContent = this.replaceStaticToCdn(content);
273
+ return code.slice(0, loc.start.offset) + transformedContent + code.slice(loc.end.offset);
226
274
  }
227
275
  replaceStaticToCdn(code) {
228
- const escapedStaticPrefix = escapeRegExp(this.assetDir);
229
- let transformed = code.replace(new RegExp(
230
- `url\\(\\s*(['"]?)(${escapedStaticPrefix}[^'")\\s]+)\\1\\s*\\)`,
231
- "g"
232
- ), (match, quote, originalPath) => {
233
- try {
234
- return this.codeReplaceMatch(originalPath, match, quote, true);
235
- } catch (error) {
236
- this.logger.error(`\u5904\u7406 CSS \u5931\u8D25`, error);
237
- return match;
238
- }
239
- });
240
- transformed = transformed.replace(new RegExp(
241
- `(['"])(${escapedStaticPrefix}[^'"]*)\\1`,
276
+ return code.replace(new RegExp(
277
+ `(['"])(${escapeRegExp(this.assetDir)}[^'"]*)\\1`,
242
278
  "g"
243
279
  ), (match, quote, originalPath) => {
244
280
  try {
@@ -248,9 +284,8 @@ class Context {
248
284
  return match;
249
285
  }
250
286
  });
251
- return transformed;
252
287
  }
253
- codeReplaceMatch(originalPath, match, quote, css = false) {
288
+ codeReplaceMatch(originalPath, match, quote) {
254
289
  let outputFileName = this.replaceUrlCache.get(originalPath) || "";
255
290
  if (!outputFileName) {
256
291
  if (isInvalidOriginalPath(originalPath)) {
@@ -264,9 +299,6 @@ class Context {
264
299
  this.replaceUrlCache.set(originalPath, outputFileName);
265
300
  this.logger.pathReplace(originalPath, outputFileName);
266
301
  }
267
- if (css) {
268
- return `url(${quote || ""}${outputFileName}${quote || ""})`;
269
- }
270
302
  return `${quote}${outputFileName}${quote}`;
271
303
  }
272
304
  async uploadAliOSS() {
@@ -331,7 +363,7 @@ class Context {
331
363
  }
332
364
  }
333
365
 
334
- const index = (options) => {
366
+ function VitePluginUniCdn(options) {
335
367
  const ctx = new Context(options);
336
368
  let plugin = {
337
369
  name: PLUGIN_NAME,
@@ -352,6 +384,9 @@ const index = (options) => {
352
384
  }
353
385
  plugin = {
354
386
  ...plugin,
387
+ config() {
388
+ return ctx.config();
389
+ },
355
390
  async configResolved(resolvedConfig) {
356
391
  await ctx.configResolved(resolvedConfig);
357
392
  },
@@ -361,14 +396,11 @@ const index = (options) => {
361
396
  transform(code, id) {
362
397
  return ctx.transform(code, id);
363
398
  },
364
- generateBundle(options2, bundle) {
365
- ctx.generateBundle(options2, bundle);
366
- },
367
399
  async closeBundle() {
368
400
  await ctx.closeBundle();
369
401
  }
370
402
  };
371
403
  return plugin;
372
- };
404
+ }
373
405
 
374
- module.exports = index;
406
+ exports.VitePluginUniCdn = VitePluginUniCdn;
package/dist/index.d.cts CHANGED
@@ -47,7 +47,7 @@ interface VitePluginUniCdnOption {
47
47
  }
48
48
  type AliOSSModule = typeof AliOSS | null;
49
49
 
50
- declare const _default: (options?: VitePluginUniCdnOption) => Plugin;
50
+ declare function VitePluginUniCdn(options?: VitePluginUniCdnOption): Plugin;
51
51
 
52
- export { _default as default };
52
+ export { VitePluginUniCdn };
53
53
  export type { AliOSSModule, AliOSSOption, VitePluginUniCdnOption };
package/dist/index.d.mts CHANGED
@@ -47,7 +47,7 @@ interface VitePluginUniCdnOption {
47
47
  }
48
48
  type AliOSSModule = typeof AliOSS | null;
49
49
 
50
- declare const _default: (options?: VitePluginUniCdnOption) => Plugin;
50
+ declare function VitePluginUniCdn(options?: VitePluginUniCdnOption): Plugin;
51
51
 
52
- export { _default as default };
52
+ export { VitePluginUniCdn };
53
53
  export type { AliOSSModule, AliOSSOption, VitePluginUniCdnOption };
package/dist/index.d.ts CHANGED
@@ -47,7 +47,7 @@ interface VitePluginUniCdnOption {
47
47
  }
48
48
  type AliOSSModule = typeof AliOSS | null;
49
49
 
50
- declare const _default: (options?: VitePluginUniCdnOption) => Plugin;
50
+ declare function VitePluginUniCdn(options?: VitePluginUniCdnOption): Plugin;
51
51
 
52
- export { _default as default };
52
+ export { VitePluginUniCdn };
53
53
  export type { AliOSSModule, AliOSSOption, VitePluginUniCdnOption };
package/dist/index.mjs CHANGED
@@ -1,8 +1,10 @@
1
1
  import fsPromises from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { parse } from '@vue/compiler-sfc';
3
4
  import { createFilter, normalizePath } from 'vite';
4
5
 
5
6
  const PLUGIN_NAME = "vite-plugin-uni-cdn";
7
+ const POSTCSS_PLUGIN_NAME = "postcss-plugin-uni-cdn";
6
8
  const VIRTUAL_MODULE_ID = `virtual:${PLUGIN_NAME}`;
7
9
  const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
8
10
 
@@ -100,7 +102,7 @@ class Context {
100
102
  this.options = {
101
103
  cdn: "",
102
104
  sourceDir: "static/cdn",
103
- include: ["**/*.{vue,css,scss,sass,less,styl}"],
105
+ include: ["**/*.vue"],
104
106
  exclude: ["**/node_modules/**", "**/uni_modules/**", "**/dist/**", "**/unpackage/**"],
105
107
  deleteOutputFiles: true,
106
108
  verbose: true,
@@ -131,6 +133,48 @@ class Context {
131
133
  return `${code.trim()}
132
134
  `;
133
135
  }
136
+ config() {
137
+ if (!this.cdnBasePath || !this.options.sourceDir) {
138
+ return;
139
+ }
140
+ const relSourceDir = normalizePath(path.normalize(this.options.sourceDir)).replace(/^\/+/, "");
141
+ const staticSubPath = relSourceDir.startsWith("src/") ? relSourceDir.slice("src/".length) : relSourceDir;
142
+ const staticDir = `/${staticSubPath.replace(/^\/+/, "")}`;
143
+ const cdnBasePath = this.cdnBasePath;
144
+ const urlRegex = /url\(\s*(['"]?)([^'")\s]+)\1\s*\)/g;
145
+ return {
146
+ css: {
147
+ postcss: {
148
+ plugins: [
149
+ {
150
+ postcssPlugin: POSTCSS_PLUGIN_NAME,
151
+ Declaration: (decl) => {
152
+ if (!decl.value || !decl.value.startsWith("url(") || decl.value.includes("http") || decl.value.includes("data:")) {
153
+ return;
154
+ }
155
+ decl.value = decl.value.replace(urlRegex, (match, quote, originalPath) => {
156
+ if (originalPath.startsWith(staticDir)) {
157
+ let outputFileName = this.replaceUrlCache.get(originalPath);
158
+ if (!outputFileName) {
159
+ let relativePath = originalPath.slice(staticDir.length);
160
+ if (!relativePath.startsWith("/")) {
161
+ relativePath = `/${relativePath}`;
162
+ }
163
+ outputFileName = `${cdnBasePath}${relativePath}`;
164
+ this.replaceUrlCache.set(originalPath, outputFileName);
165
+ this.logger?.pathReplace?.(originalPath, outputFileName);
166
+ }
167
+ return `url(${quote}${outputFileName}${quote})`;
168
+ }
169
+ return match;
170
+ });
171
+ }
172
+ }
173
+ ]
174
+ }
175
+ }
176
+ };
177
+ }
134
178
  async configResolved(resolvedConfig) {
135
179
  this.projectRoot = resolvedConfig.root;
136
180
  this.logger = createLogger(this.options.verbose, resolvedConfig.logger);
@@ -187,51 +231,43 @@ class Context {
187
231
  if (!this.filter(filepath)) {
188
232
  return { code };
189
233
  }
190
- const transformed = this.replaceStaticToCdn(code);
191
- return { code: transformed };
192
- }
193
- generateBundle(options, bundle) {
194
- if (!this.sourceDirAbs || !this.assetDir) {
195
- return;
196
- }
197
- for (const [fileName, chunk] of Object.entries(bundle)) {
198
- if (chunk.type === "asset") {
199
- if (typeof chunk.source !== "string") {
200
- continue;
201
- }
202
- if (!/\.(?:css|js|mjs|html)$/.test(fileName)) {
203
- continue;
204
- }
205
- const before = chunk.source;
206
- chunk.source = this.replaceStaticToCdn(before);
207
- } else if (chunk.type === "chunk") {
208
- const before = chunk.code;
209
- chunk.code = this.replaceStaticToCdn(before);
210
- }
234
+ if (id.endsWith(".vue")) {
235
+ return this.processVueSfc(code);
211
236
  }
237
+ const transformedCode = this.replaceStaticToCdn(code);
238
+ return { code: transformedCode };
212
239
  }
213
240
  async closeBundle() {
214
241
  if (!this.sourceDirAbs || !this.assetDir) {
215
242
  return;
216
243
  }
217
244
  await this.uploadAliOSS();
218
- this.deleteOutputFiles();
245
+ await this.deleteOutputFiles();
246
+ }
247
+ processVueSfc(code) {
248
+ let transformedCode = code;
249
+ try {
250
+ const sfc = parse(code);
251
+ transformedCode = this.processSfcBlock(transformedCode, sfc.descriptor.template);
252
+ transformedCode = this.processSfcBlock(transformedCode, sfc.descriptor.scriptSetup);
253
+ transformedCode = this.processSfcBlock(transformedCode, sfc.descriptor.script);
254
+ } catch (error) {
255
+ this.logger.error("\u89E3\u6790 Vue SFC \u5931\u8D25", error);
256
+ transformedCode = this.replaceStaticToCdn(code);
257
+ }
258
+ return { code: transformedCode };
259
+ }
260
+ processSfcBlock(code, block) {
261
+ if (!block) {
262
+ return code;
263
+ }
264
+ const { content, loc } = block;
265
+ const transformedContent = this.replaceStaticToCdn(content);
266
+ return code.slice(0, loc.start.offset) + transformedContent + code.slice(loc.end.offset);
219
267
  }
220
268
  replaceStaticToCdn(code) {
221
- const escapedStaticPrefix = escapeRegExp(this.assetDir);
222
- let transformed = code.replace(new RegExp(
223
- `url\\(\\s*(['"]?)(${escapedStaticPrefix}[^'")\\s]+)\\1\\s*\\)`,
224
- "g"
225
- ), (match, quote, originalPath) => {
226
- try {
227
- return this.codeReplaceMatch(originalPath, match, quote, true);
228
- } catch (error) {
229
- this.logger.error(`\u5904\u7406 CSS \u5931\u8D25`, error);
230
- return match;
231
- }
232
- });
233
- transformed = transformed.replace(new RegExp(
234
- `(['"])(${escapedStaticPrefix}[^'"]*)\\1`,
269
+ return code.replace(new RegExp(
270
+ `(['"])(${escapeRegExp(this.assetDir)}[^'"]*)\\1`,
235
271
  "g"
236
272
  ), (match, quote, originalPath) => {
237
273
  try {
@@ -241,9 +277,8 @@ class Context {
241
277
  return match;
242
278
  }
243
279
  });
244
- return transformed;
245
280
  }
246
- codeReplaceMatch(originalPath, match, quote, css = false) {
281
+ codeReplaceMatch(originalPath, match, quote) {
247
282
  let outputFileName = this.replaceUrlCache.get(originalPath) || "";
248
283
  if (!outputFileName) {
249
284
  if (isInvalidOriginalPath(originalPath)) {
@@ -257,9 +292,6 @@ class Context {
257
292
  this.replaceUrlCache.set(originalPath, outputFileName);
258
293
  this.logger.pathReplace(originalPath, outputFileName);
259
294
  }
260
- if (css) {
261
- return `url(${quote || ""}${outputFileName}${quote || ""})`;
262
- }
263
295
  return `${quote}${outputFileName}${quote}`;
264
296
  }
265
297
  async uploadAliOSS() {
@@ -324,7 +356,7 @@ class Context {
324
356
  }
325
357
  }
326
358
 
327
- const index = (options) => {
359
+ function VitePluginUniCdn(options) {
328
360
  const ctx = new Context(options);
329
361
  let plugin = {
330
362
  name: PLUGIN_NAME,
@@ -345,6 +377,9 @@ const index = (options) => {
345
377
  }
346
378
  plugin = {
347
379
  ...plugin,
380
+ config() {
381
+ return ctx.config();
382
+ },
348
383
  async configResolved(resolvedConfig) {
349
384
  await ctx.configResolved(resolvedConfig);
350
385
  },
@@ -354,14 +389,11 @@ const index = (options) => {
354
389
  transform(code, id) {
355
390
  return ctx.transform(code, id);
356
391
  },
357
- generateBundle(options2, bundle) {
358
- ctx.generateBundle(options2, bundle);
359
- },
360
392
  async closeBundle() {
361
393
  await ctx.closeBundle();
362
394
  }
363
395
  };
364
396
  return plugin;
365
- };
397
+ }
366
398
 
367
- export { index as default };
399
+ export { VitePluginUniCdn };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cnguu/vite-plugin-uni-cdn",
3
3
  "type": "module",
4
- "version": "2.1.0",
4
+ "version": "2.2.0",
5
5
  "description": "Vite 插件,在 uni-app 中替换资源链接为 CDN 链接",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/cnguu/vite-plugin-uni-cdn",
@@ -54,6 +54,9 @@
54
54
  "optional": true
55
55
  }
56
56
  },
57
+ "dependencies": {
58
+ "@vue/compiler-sfc": "3.4.21"
59
+ },
57
60
  "devDependencies": {
58
61
  "@types/ali-oss": "6.16.13",
59
62
  "ali-oss": "6.23.0",