@yidun/cdn-upload-webpack-plugin 1.1.1 → 1.1.3
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/.eslintignore +2 -2
- package/.eslintrc.js +9 -9
- package/README.md +44 -44
- package/example/.vscode/extensions.json +3 -0
- package/example/README.md +4 -0
- package/example/index.html +13 -0
- package/example/package-lock.json +1162 -0
- package/example/package.json +21 -0
- package/example/public/vite.svg +1 -0
- package/example/src/App.vue +30 -0
- package/example/src/assets/vue.svg +1 -0
- package/example/src/components/HelloWorld.vue +38 -0
- package/example/src/main.ts +5 -0
- package/example/src/style.css +79 -0
- package/example/src/vite-env.d.ts +1 -0
- package/example/tsconfig.json +25 -0
- package/example/tsconfig.node.json +11 -0
- package/example/vite.config.ts +25 -0
- package/package.json +24 -23
- package/src/adaptor/nos.js +39 -45
- package/src/index.js +45 -45
- package/src/task.js +103 -103
- package/src/utils.js +41 -41
- package/src/vite.js +46 -39
package/src/task.js
CHANGED
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
const { noop } = require('./utils')
|
|
2
|
-
|
|
3
|
-
const TASK_STATUS = {
|
|
4
|
-
PENDING: 'pending',
|
|
5
|
-
DOING: 'doing',
|
|
6
|
-
REJECTED: 'rejected',
|
|
7
|
-
FULFILLED: 'fulfilled'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
let uid = 0 // 任务 id
|
|
11
|
-
/**
|
|
12
|
-
* task 基类,提供重试等功能,供 adaptor 继承,如 NosUploadTask
|
|
13
|
-
*/
|
|
14
|
-
class Task {
|
|
15
|
-
constructor (invoke) {
|
|
16
|
-
this.status = TASK_STATUS.PENDING
|
|
17
|
-
this._id = uid++
|
|
18
|
-
this._invoke = invoke
|
|
19
|
-
this._retryCount = 3
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async start () {
|
|
23
|
-
let retryCount = this._retryCount
|
|
24
|
-
let res
|
|
25
|
-
try {
|
|
26
|
-
this.status = TASK_STATUS.DOING
|
|
27
|
-
res = await this._invoke()
|
|
28
|
-
} catch (error) {
|
|
29
|
-
retryCount--
|
|
30
|
-
if (retryCount < 0) {
|
|
31
|
-
this.status = TASK_STATUS.REJECTED
|
|
32
|
-
return Promise.reject(error)
|
|
33
|
-
} else {
|
|
34
|
-
res = await this._invoke()
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
this.status = TASK_STATUS.FULFILLED
|
|
38
|
-
return res
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const PARALLEL_COUNT = 3 // 上传并行数
|
|
43
|
-
/**
|
|
44
|
-
* 任务调度器,完成整个上传任务的调度
|
|
45
|
-
*/
|
|
46
|
-
class TaskDispatcher {
|
|
47
|
-
constructor (tasks, callbacks = {}) {
|
|
48
|
-
this.status = TASK_STATUS.PENDING
|
|
49
|
-
this._tasks = tasks
|
|
50
|
-
this._activeTasks = []
|
|
51
|
-
this._onError = callbacks.onError || noop
|
|
52
|
-
this._onSuccess = callbacks.onSuccess || noop
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
getPendingTask (count) {
|
|
56
|
-
return this._tasks.filter(t => t.status === TASK_STATUS.PENDING).slice(0, count)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
getPendingTaskCount () {
|
|
60
|
-
return this._tasks.filter(t => t.status === TASK_STATUS.PENDING).length
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
removeActiveTask (task) {
|
|
64
|
-
const index = this._activeTasks.findIndex(t => task._id === t._id)
|
|
65
|
-
if (index > -1) {
|
|
66
|
-
this._activeTasks.splice(index, 1)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
isAllTaskFulfilled () {
|
|
71
|
-
return this._tasks.every(t => t.status === TASK_STATUS.FULFILLED)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 任务调度入口
|
|
76
|
-
* @param {Number} count
|
|
77
|
-
*/
|
|
78
|
-
dispatch (count = PARALLEL_COUNT) {
|
|
79
|
-
if (TASK_STATUS.REJECTED === this.status) return
|
|
80
|
-
|
|
81
|
-
this._activeTasks = this._activeTasks.concat(this.getPendingTask(count))
|
|
82
|
-
this._activeTasks.forEach(task => {
|
|
83
|
-
if (task.status === TASK_STATUS.PENDING) {
|
|
84
|
-
task.start().then(res => {
|
|
85
|
-
this.removeActiveTask(task)
|
|
86
|
-
if (this.getPendingTaskCount() > 0) {
|
|
87
|
-
this.dispatch(1)
|
|
88
|
-
} else if (this.isAllTaskFulfilled()) {
|
|
89
|
-
this._onSuccess(this._tasks)
|
|
90
|
-
}
|
|
91
|
-
}).catch(error => {
|
|
92
|
-
this.status = TASK_STATUS.REJECTED
|
|
93
|
-
this._onError(error)
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
module.exports = {
|
|
101
|
-
Task,
|
|
102
|
-
TaskDispatcher
|
|
103
|
-
}
|
|
1
|
+
const { noop } = require('./utils')
|
|
2
|
+
|
|
3
|
+
const TASK_STATUS = {
|
|
4
|
+
PENDING: 'pending',
|
|
5
|
+
DOING: 'doing',
|
|
6
|
+
REJECTED: 'rejected',
|
|
7
|
+
FULFILLED: 'fulfilled'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let uid = 0 // 任务 id
|
|
11
|
+
/**
|
|
12
|
+
* task 基类,提供重试等功能,供 adaptor 继承,如 NosUploadTask
|
|
13
|
+
*/
|
|
14
|
+
class Task {
|
|
15
|
+
constructor (invoke) {
|
|
16
|
+
this.status = TASK_STATUS.PENDING
|
|
17
|
+
this._id = uid++
|
|
18
|
+
this._invoke = invoke
|
|
19
|
+
this._retryCount = 3
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async start () {
|
|
23
|
+
let retryCount = this._retryCount
|
|
24
|
+
let res
|
|
25
|
+
try {
|
|
26
|
+
this.status = TASK_STATUS.DOING
|
|
27
|
+
res = await this._invoke()
|
|
28
|
+
} catch (error) {
|
|
29
|
+
retryCount--
|
|
30
|
+
if (retryCount < 0) {
|
|
31
|
+
this.status = TASK_STATUS.REJECTED
|
|
32
|
+
return Promise.reject(error)
|
|
33
|
+
} else {
|
|
34
|
+
res = await this._invoke()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
this.status = TASK_STATUS.FULFILLED
|
|
38
|
+
return res
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const PARALLEL_COUNT = 3 // 上传并行数
|
|
43
|
+
/**
|
|
44
|
+
* 任务调度器,完成整个上传任务的调度
|
|
45
|
+
*/
|
|
46
|
+
class TaskDispatcher {
|
|
47
|
+
constructor (tasks, callbacks = {}) {
|
|
48
|
+
this.status = TASK_STATUS.PENDING
|
|
49
|
+
this._tasks = tasks
|
|
50
|
+
this._activeTasks = []
|
|
51
|
+
this._onError = callbacks.onError || noop
|
|
52
|
+
this._onSuccess = callbacks.onSuccess || noop
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getPendingTask (count) {
|
|
56
|
+
return this._tasks.filter(t => t.status === TASK_STATUS.PENDING).slice(0, count)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getPendingTaskCount () {
|
|
60
|
+
return this._tasks.filter(t => t.status === TASK_STATUS.PENDING).length
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
removeActiveTask (task) {
|
|
64
|
+
const index = this._activeTasks.findIndex(t => task._id === t._id)
|
|
65
|
+
if (index > -1) {
|
|
66
|
+
this._activeTasks.splice(index, 1)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
isAllTaskFulfilled () {
|
|
71
|
+
return this._tasks.every(t => t.status === TASK_STATUS.FULFILLED)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 任务调度入口
|
|
76
|
+
* @param {Number} count
|
|
77
|
+
*/
|
|
78
|
+
dispatch (count = PARALLEL_COUNT) {
|
|
79
|
+
if (TASK_STATUS.REJECTED === this.status) return
|
|
80
|
+
|
|
81
|
+
this._activeTasks = this._activeTasks.concat(this.getPendingTask(count))
|
|
82
|
+
this._activeTasks.forEach(task => {
|
|
83
|
+
if (task.status === TASK_STATUS.PENDING) {
|
|
84
|
+
task.start().then(res => {
|
|
85
|
+
this.removeActiveTask(task)
|
|
86
|
+
if (this.getPendingTaskCount() > 0) {
|
|
87
|
+
this.dispatch(1)
|
|
88
|
+
} else if (this.isAllTaskFulfilled()) {
|
|
89
|
+
this._onSuccess(this._tasks)
|
|
90
|
+
}
|
|
91
|
+
}).catch(error => {
|
|
92
|
+
this.status = TASK_STATUS.REJECTED
|
|
93
|
+
this._onError(error)
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
Task,
|
|
102
|
+
TaskDispatcher
|
|
103
|
+
}
|
package/src/utils.js
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
const Promise = require('bluebird')
|
|
2
|
-
const fs = Promise.promisifyAll(require('fs'))
|
|
3
|
-
const path = require('path')
|
|
4
|
-
|
|
5
|
-
function getFiles (dirs, includeRootDir = true, ignores) {
|
|
6
|
-
if (typeof dirs === 'string') dirs = [dirs]
|
|
7
|
-
if (ignores instanceof RegExp) ignores = [ignores]
|
|
8
|
-
|
|
9
|
-
const res = []
|
|
10
|
-
dirs.forEach(dir => deep(dir, dir))
|
|
11
|
-
|
|
12
|
-
function deep (dir, rootDir) {
|
|
13
|
-
const files = fs.readdirSync(dir)
|
|
14
|
-
files.forEach(fileName => {
|
|
15
|
-
const pathName = `${dir}${path.sep}${fileName}`
|
|
16
|
-
const info = fs.lstatSync(pathName)
|
|
17
|
-
if (info.isDirectory()) {
|
|
18
|
-
deep(pathName, rootDir)
|
|
19
|
-
} else {
|
|
20
|
-
if (!ignores || ignores.every(ignore => !ignore.test(pathName))) {
|
|
21
|
-
res.push({
|
|
22
|
-
path: pathName,
|
|
23
|
-
relative: path.relative(includeRootDir ? path.join(rootDir, '../') : rootDir, pathName)
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return res
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function normalizeObjectKey (objectKey) {
|
|
34
|
-
return path.normalize(objectKey).replace(/\\/g, '/')
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = {
|
|
38
|
-
getFiles,
|
|
39
|
-
normalizeObjectKey,
|
|
40
|
-
noop () {}
|
|
41
|
-
}
|
|
1
|
+
const Promise = require('bluebird')
|
|
2
|
+
const fs = Promise.promisifyAll(require('fs'))
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
function getFiles (dirs, includeRootDir = true, ignores) {
|
|
6
|
+
if (typeof dirs === 'string') dirs = [dirs]
|
|
7
|
+
if (ignores instanceof RegExp) ignores = [ignores]
|
|
8
|
+
|
|
9
|
+
const res = []
|
|
10
|
+
dirs.forEach(dir => deep(dir, dir))
|
|
11
|
+
|
|
12
|
+
function deep (dir, rootDir) {
|
|
13
|
+
const files = fs.readdirSync(dir)
|
|
14
|
+
files.forEach(fileName => {
|
|
15
|
+
const pathName = `${dir}${path.sep}${fileName}`
|
|
16
|
+
const info = fs.lstatSync(pathName)
|
|
17
|
+
if (info.isDirectory()) {
|
|
18
|
+
deep(pathName, rootDir)
|
|
19
|
+
} else {
|
|
20
|
+
if (!ignores || ignores.every(ignore => !ignore.test(pathName))) {
|
|
21
|
+
res.push({
|
|
22
|
+
path: pathName,
|
|
23
|
+
relative: path.relative(includeRootDir ? path.join(rootDir, '../') : rootDir, pathName)
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return res
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function normalizeObjectKey (objectKey) {
|
|
34
|
+
return path.normalize(objectKey).replace(/\\/g, '/')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
getFiles,
|
|
39
|
+
normalizeObjectKey,
|
|
40
|
+
noop () {}
|
|
41
|
+
}
|
package/src/vite.js
CHANGED
|
@@ -1,39 +1,46 @@
|
|
|
1
|
-
const
|
|
2
|
-
const {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* @param {
|
|
8
|
-
* @param {
|
|
9
|
-
* @param {
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {String}
|
|
13
|
-
* @param {String}
|
|
14
|
-
* @param {String}
|
|
15
|
-
* @param {
|
|
16
|
-
* @
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
1
|
+
const fetch = require('node-fetch')
|
|
2
|
+
const { getFiles } = require('./utils')
|
|
3
|
+
const { TaskDispatcher } = require('./task')
|
|
4
|
+
const NosUploadTask = require('./adaptor/nos')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {Object} options
|
|
8
|
+
* @param {Array|String} dirs 待上传的文件目录
|
|
9
|
+
* @param {Boolean} includeRootDir 文件的相对路径计算时包不包含根目录,默认 true
|
|
10
|
+
* @param {RegExp|Array<RegExp>} ignores 排除特定文件
|
|
11
|
+
* @param {Object} client 上传 client 配置,目前只支持 NosClient,不同的 client 配置参数可能不同,以 nos 举例
|
|
12
|
+
* @param {String} accessId
|
|
13
|
+
* @param {String} secretKey
|
|
14
|
+
* @param {String} bucket 桶名
|
|
15
|
+
* @param {String} namespace 不同项目在使用同一个桶时做隔离
|
|
16
|
+
* @param {Number} parallelCount 并行上传数, 默认 3
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
function CdnUploadVitePlugin (options) {
|
|
20
|
+
const { dirs, includeRootDir, ignores, client, parallelCount, domain = 'https://yidunfe.nosdn.127.net' } = options
|
|
21
|
+
return {
|
|
22
|
+
name: 'vite-plugin-cdn-upload',
|
|
23
|
+
closeBundle () {
|
|
24
|
+
const files = getFiles(dirs, includeRootDir, ignores)
|
|
25
|
+
const tasks = files.map((file) => new NosUploadTask(client, file))
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const taskDispatcher = new TaskDispatcher(tasks, {
|
|
28
|
+
onSuccess: async (task) => {
|
|
29
|
+
// cdn 上传成功后健康检查
|
|
30
|
+
const { _file, _namespace } = task?.[0] || {}
|
|
31
|
+
const data = await fetch(`${domain}/${_namespace}/${_file.relative}`)
|
|
32
|
+
if (!data.ok || data.status !== 200) {
|
|
33
|
+
reject(new Error(`CdnUploadVitePlugin Error: url: ${data.url}, status: ${data.status}`))
|
|
34
|
+
}
|
|
35
|
+
console.log('CdnUploadVitePlugin: assets upload success')
|
|
36
|
+
resolve()
|
|
37
|
+
},
|
|
38
|
+
onError: (error) => reject(error)
|
|
39
|
+
})
|
|
40
|
+
console.log('CdnUploadVitePlugin: assets uploading ...')
|
|
41
|
+
taskDispatcher.dispatch(parallelCount)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
module.exports = CdnUploadVitePlugin
|