@yidun/cdn-upload-webpack-plugin 1.0.1 → 1.1.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/.eslintignore ADDED
@@ -0,0 +1,3 @@
1
+ /dist/
2
+ /dashboard/
3
+ node_modules
package/.eslintrc.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ root: true,
3
+ env: {
4
+ node: true
5
+ },
6
+ extends: [
7
+ 'standard'
8
+ ]
9
+ }
package/README.md CHANGED
@@ -1,45 +1,45 @@
1
- ## cdn-upload-webpack-plugin, 用于将静态资源上传到 cdn
2
-
3
- ### usage
4
-
5
- ### 构造函数
6
-
7
- ```
8
- /**
9
- * @param {Object} options
10
- * @param {Array|String} dirs 待上传的文件目录
11
- * @param {Boolean} includeRootDir 文件的相对路径计算时包不包含根目录,默认 true
12
- * @param {RegExp|Array<RegExp>} ignore 排除特定文件
13
- * @param {Object} client 上传 client 配置,目前只支持 NosClient,不同的 client 配置参数可能不同,以 nos 举例
14
- * @param {String} accessId
15
- * @param {String} secretKey
16
- * @param {String} bucket 桶名
17
- * @param {String} namespace 不同项目在使用同一个桶时做隔离
18
- */
19
- new CdnUploadWebpackPlugin(options)
20
- ```
21
-
22
- ### 以 vue-cli 项目举例
23
-
24
- ```
25
- configureWebpack: config => {
26
- if (process.env.NODE_ENV === 'production') {
27
- config.plugins.push(...[
28
- new CdnUploadWebpackPlugin({
29
- dirs: path.resolve(__dirname, './dist'),
30
- ignore: /.html$/,
31
- client: {
32
- bucket: '',
33
- accessId: '',
34
- secretKey: '',
35
- namespace: 'yidunfe/cdntest'
36
- }
37
- })
38
- ])
39
- }
40
- },
41
- ```
42
-
43
- ### adaptor
44
-
1
+ ## cdn-upload-webpack-plugin, 用于将静态资源上传到 cdn
2
+
3
+ ### usage
4
+
5
+ ### 构造函数
6
+
7
+ ```
8
+ /**
9
+ * @param {Object} options
10
+ * @param {Array|String} dirs 待上传的文件目录
11
+ * @param {Boolean} includeRootDir 文件的相对路径计算时包不包含根目录,默认 true
12
+ * @param {RegExp|Array<RegExp>} ignores 排除特定文件
13
+ * @param {Object} client 上传 client 配置,目前只支持 NosClient,不同的 client 配置参数可能不同,以 nos 举例
14
+ * @param {String} accessId
15
+ * @param {String} secretKey
16
+ * @param {String} bucket 桶名
17
+ * @param {String} namespace 不同项目在使用同一个桶时做隔离
18
+ */
19
+ new CdnUploadWebpackPlugin(options)
20
+ ```
21
+
22
+ ### 以 vue-cli 项目举例
23
+
24
+ ```
25
+ configureWebpack: config => {
26
+ if (process.env.NODE_ENV === 'production') {
27
+ config.plugins.push(...[
28
+ new CdnUploadWebpackPlugin({
29
+ dirs: path.resolve(__dirname, './dist'),
30
+ ignore: /.html$/,
31
+ client: {
32
+ bucket: '',
33
+ accessId: '',
34
+ secretKey: '',
35
+ namespace: 'yidunfe/cdntest'
36
+ }
37
+ })
38
+ ])
39
+ }
40
+ },
41
+ ```
42
+
43
+ ### adaptor
44
+
45
45
  目前仅支持 nos 上传,后续如有其他的上传需求,可自行扩展 adaptor,可通过 client 参数区分
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "@yidun/cdn-upload-webpack-plugin",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "A webpack plugin for upload dist to cdn.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
7
  "test": "node test/index.js"
8
8
  },
9
- "files": [
10
- "src"
11
- ],
12
9
  "author": "zhanglulu01",
13
10
  "license": "ISC",
14
11
  "devDependencies": {
@@ -1,45 +1,45 @@
1
- const { Task } = require('../task')
2
- const NosClient = require('nos-node-sdk2')
3
- const { normalizeObjectKey } = require('../utils')
4
-
5
- class NosUploadTask extends Task {
6
- constructor (nosOptions, file) {
7
- super(() => this.upload())
8
- const nosClient = new NosClient()
9
- const { accessId, secretKey, bucket, endpoint = 'nos.netease.com', port = 80, namespace = '' } = nosOptions
10
- nosClient.setAccessId(accessId)
11
- nosClient.setSecretKey(secretKey)
12
- nosClient.setEndpoint(endpoint)
13
- nosClient.setPort(port)
14
- this._nosClient = nosClient
15
- this._bucket = bucket
16
- this._file = file
17
- this._namespace = namespace
18
- }
19
-
20
- upload () {
21
- return new Promise((resolve, reject) => {
22
- const putCallback = result => {
23
- if (result.statusCode !== 200) {
24
- console.error(`upload to nos fail , because ${result}`)
25
- return reject(result)
26
- }
27
- resolve(result)
28
- }
29
- try {
30
- const map = {
31
- bucket: this._bucket,
32
- filepath: this._file.path,
33
- key: normalizeObjectKey(`${this._namespace}/${this._file.relative}`)
34
- }
35
- // 最大为 100M
36
- this._nosClient.put_file(map, putCallback)
37
- } catch (error) {
38
- console.error(`nos instance init fail , because ${error}`)
39
- reject(error)
40
- }
41
- })
42
- }
43
- }
44
-
45
- module.exports = NosUploadTask
1
+ const { Task } = require('../task')
2
+ const NosClient = require('nos-node-sdk2')
3
+ const { normalizeObjectKey } = require('../utils')
4
+
5
+ class NosUploadTask extends Task {
6
+ constructor (nosOptions, file) {
7
+ super(() => this.upload())
8
+ const nosClient = new NosClient()
9
+ const { accessId, secretKey, bucket, endpoint = 'nos.netease.com', port = 80, namespace = '' } = nosOptions
10
+ nosClient.setAccessId(accessId)
11
+ nosClient.setSecretKey(secretKey)
12
+ nosClient.setEndpoint(endpoint)
13
+ nosClient.setPort(port)
14
+ this._nosClient = nosClient
15
+ this._bucket = bucket
16
+ this._file = file
17
+ this._namespace = namespace
18
+ }
19
+
20
+ upload () {
21
+ return new Promise((resolve, reject) => {
22
+ const putCallback = result => {
23
+ if (result.statusCode !== 200) {
24
+ console.error(`upload to nos fail , because ${result}`)
25
+ return reject(result)
26
+ }
27
+ resolve(result)
28
+ }
29
+ try {
30
+ const map = {
31
+ bucket: this._bucket,
32
+ filepath: this._file.path,
33
+ key: normalizeObjectKey(`${this._namespace}/${this._file.relative}`)
34
+ }
35
+ // 最大为 100M
36
+ this._nosClient.put_file(map, putCallback)
37
+ } catch (error) {
38
+ console.error(`nos instance init fail , because ${error}`)
39
+ reject(error)
40
+ }
41
+ })
42
+ }
43
+ }
44
+
45
+ module.exports = NosUploadTask
package/src/index.js CHANGED
@@ -1,42 +1,45 @@
1
- const { getFiles } = require('./utils')
2
- const { TaskDispatcher } = require('./task')
3
- const NosUploadTask = require('./adaptor/nos')
4
-
5
- module.exports = class CdnUploadWebpackPlugin {
6
- /**
7
- * @param {Object} options
8
- * @param {Array|String} dirs 待上传的文件目录
9
- * @param {Boolean} includeRootDir 文件的相对路径计算时包不包含根目录,默认 true
10
- * @param {RegExp|Array<RegExp>} ignore 排除特定文件
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
- */
17
- constructor (options) {
18
- const { dirs, includeRootDir, ignore, client } = options
19
- this._dirs = dirs
20
- this._includeRootDir = includeRootDir
21
- this._ignore = ignore
22
- this._client = client
23
- }
24
-
25
- apply (compiler) {
26
- compiler.hooks.afterEmit.tapAsync('CdnUploadWebpackPlugin', (compiler, callback) => {
27
- const files = getFiles(this._dirs, this._includeRootDir, this._ignore)
28
- const tasks = files.map(file => new NosUploadTask(this._client, file))
29
- const taskDispatcher = new TaskDispatcher(tasks, {
30
- onSuccess: () => {
31
- console.log(`[CdnUploadWebpackPlugin]: assets upload success, now timestamp is ${Date.now()}`)
32
- callback()
33
- },
34
- onError: error => {
35
- callback(new Error(`[CdnUploadWebpackPlugin]: upload failed, ${error.message}`))
36
- }
37
- })
38
- console.log(`[CdnUploadWebpackPlugin]: assets uploading ..., now timestamp is ${Date.now()}`)
39
- taskDispatcher.dispatch()
40
- })
41
- }
42
- }
1
+ const { getFiles } = require('./utils')
2
+ const { TaskDispatcher } = require('./task')
3
+ const NosUploadTask = require('./adaptor/nos')
4
+ const CdnUploadVitePlugin = require('./vite')
5
+
6
+ exports.CdnUploadWebpackPlugin = class CdnUploadWebpackPlugin {
7
+ /**
8
+ * @param {Object} options
9
+ * @param {Array|String} dirs 待上传的文件目录
10
+ * @param {Boolean} includeRootDir 文件的相对路径计算时包不包含根目录,默认 true
11
+ * @param {RegExp|Array<RegExp>} ignores 排除特定文件
12
+ * @param {Object} client 上传 client 配置,目前只支持 NosClient,不同的 client 配置参数可能不同,以 nos 举例
13
+ * @param {String} accessId
14
+ * @param {String} secretKey
15
+ * @param {String} bucket 桶名
16
+ * @param {String} namespace 不同项目在使用同一个桶时做隔离
17
+ * @param {Number} parallelCount 并行上传数, 默认 3
18
+ */
19
+ constructor (options) {
20
+ const { dirs, includeRootDir, ignores, client, parallelCount } = options
21
+ this._dirs = dirs
22
+ this._includeRootDir = includeRootDir
23
+ this._ignores = ignores
24
+ this._client = client
25
+ this._parallelCount = parallelCount
26
+ }
27
+
28
+ apply (compiler) {
29
+ compiler.hooks.afterEmit.tapAsync('CdnUploadWebpackPlugin', (compiler, callback) => {
30
+ const files = getFiles(this._dirs, this._includeRootDir, this._ignores)
31
+ const tasks = files.map(file => new NosUploadTask(this._client, file))
32
+ const taskDispatcher = new TaskDispatcher(tasks, {
33
+ onSuccess: () => {
34
+ console.log('CdnUploadWebpackPlugin: assets upload success')
35
+ callback()
36
+ },
37
+ onError: error => callback(error)
38
+ })
39
+ console.log('CdnUploadWebpackPlugin: assets uploading ...')
40
+ taskDispatcher.dispatch(this._parallelCount)
41
+ })
42
+ }
43
+ }
44
+
45
+ exports.CdnUploadVitePlugin = CdnUploadVitePlugin
package/src/task.js CHANGED
@@ -1,95 +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
- }
20
-
21
- async start () {
22
- try {
23
- this.status = TASK_STATUS.DOING
24
- const res = await this._invoke()
25
- this.status = TASK_STATUS.FULFILLED
26
- return res
27
- } catch (error) {
28
- this.status = TASK_STATUS.REJECTED
29
- return Promise.reject(error)
30
- }
31
- }
32
- }
33
-
34
- const PARALLEL_COUNT = 3 // 上传并行数
35
- /**
36
- * 任务调度器,完成整个上传任务的调度
37
- */
38
- class TaskDispatcher {
39
- constructor (tasks, callbacks = {}) {
40
- this.status = TASK_STATUS.PENDING
41
- this._tasks = tasks
42
- this._activeTasks = []
43
- this._onError = callbacks.onError || noop
44
- this._onSuccess = callbacks.onSuccess || noop
45
- }
46
-
47
- getPendingTask (count) {
48
- return this._tasks.filter(t => t.status === TASK_STATUS.PENDING).slice(0, count)
49
- }
50
-
51
- getPendingTaskCount () {
52
- return this._tasks.filter(t => t.status === TASK_STATUS.PENDING).length
53
- }
54
-
55
- removeActiveTask (task) {
56
- const index = this._activeTasks.findIndex(t => task._id === t._id)
57
- if (index > -1) {
58
- this._activeTasks.splice(index, 1)
59
- }
60
- }
61
-
62
- isAllTaskFulfilled () {
63
- return this._tasks.every(t => t.status === TASK_STATUS.FULFILLED)
64
- }
65
-
66
- /**
67
- * 任务调度入口
68
- * @param {Number} count
69
- */
70
- dispatch (count = PARALLEL_COUNT) {
71
- if (TASK_STATUS.REJECTED === this.status) return
72
-
73
- this._activeTasks = this._activeTasks.concat(this.getPendingTask(count))
74
- this._activeTasks.forEach(task => {
75
- if (task.status === TASK_STATUS.PENDING) {
76
- task.start().then(res => {
77
- this.removeActiveTask(task)
78
- if (this.getPendingTaskCount() > 0) {
79
- this.dispatch(1)
80
- } else if (this.isAllTaskFulfilled()) {
81
- this._onSuccess(this._tasks)
82
- }
83
- }).catch(error => {
84
- this.status = TASK_STATUS.REJECTED
85
- this._onError(error)
86
- })
87
- }
88
- })
89
- }
90
- }
91
-
92
- module.exports = {
93
- Task,
94
- TaskDispatcher
95
- }
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 ADDED
@@ -0,0 +1,39 @@
1
+ const { getFiles } = require('./utils')
2
+ const { TaskDispatcher } = require('./task')
3
+ const NosUploadTask = require('./adaptor/nos')
4
+
5
+ /**
6
+ * @param {Object} options
7
+ * @param {Array|String} dirs 待上传的文件目录
8
+ * @param {Boolean} includeRootDir 文件的相对路径计算时包不包含根目录,默认 true
9
+ * @param {RegExp|Array<RegExp>} ignores 排除特定文件
10
+ * @param {Object} client 上传 client 配置,目前只支持 NosClient,不同的 client 配置参数可能不同,以 nos 举例
11
+ * @param {String} accessId
12
+ * @param {String} secretKey
13
+ * @param {String} bucket 桶名
14
+ * @param {String} namespace 不同项目在使用同一个桶时做隔离
15
+ * @param {Number} parallelCount 并行上传数, 默认 3
16
+ * @returns
17
+ */
18
+ function CdnUploadVitePlugin (options) {
19
+ const { dirs, includeRootDir, ignores, client, parallelCount } = options
20
+ return {
21
+ name: 'vite-plugin-cdn-upload',
22
+ closeBundle () {
23
+ const files = getFiles(dirs, includeRootDir, ignores)
24
+ const tasks = files.map((file) => new NosUploadTask(client, file))
25
+ return new Promise((resolve, reject) => {
26
+ const taskDispatcher = new TaskDispatcher(tasks, {
27
+ onSuccess: () => {
28
+ console.log('CdnUploadWebpackPlugin: assets upload success')
29
+ resolve()
30
+ },
31
+ onError: (error) => reject(error)
32
+ })
33
+ console.log('CdnUploadWebpackPlugin: assets uploading ...')
34
+ taskDispatcher.dispatch(parallelCount)
35
+ })
36
+ }
37
+ }
38
+ }
39
+ module.exports = CdnUploadVitePlugin