@struggler/cli 1.0.9 → 1.0.11
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/command/upload.js +4 -2
- package/index.js +1 -1
- package/lib/config.js +23 -9
- package/lib/i18n.js +2 -0
- package/lib/mime.js +55 -0
- package/package.json +5 -3
- package/test/config.test.js +12 -0
- package/test/upload.test.js +70 -0
package/command/upload.js
CHANGED
|
@@ -19,6 +19,7 @@ const { printMessage } = require('../lib/output');
|
|
|
19
19
|
const { computeFileMd5, readCache, writeCache, isCacheHit, updateCacheEntry } = require('../lib/cache');
|
|
20
20
|
const { createProgressBar } = require('../lib/progress');
|
|
21
21
|
const { getLocale } = require('../lib/i18n');
|
|
22
|
+
const { resolveMimeType } = require('../lib/mime');
|
|
22
23
|
|
|
23
24
|
async function main(options, runtime = {}) {
|
|
24
25
|
const qiniuConfig = getJsonData(getQiniuConfig(options))
|
|
@@ -89,9 +90,10 @@ async function main(options, runtime = {}) {
|
|
|
89
90
|
config.useCdnDomain = true;
|
|
90
91
|
|
|
91
92
|
var formUploader = new qiniu.form_up.FormUploader(config);
|
|
92
|
-
var putExtra = new qiniu.form_up.PutExtra();
|
|
93
|
-
|
|
94
93
|
function upload(plan, attempt = 1) {
|
|
94
|
+
var putExtra = new qiniu.form_up.PutExtra();
|
|
95
|
+
putExtra.mimeType = resolveMimeType(plan.localFile);
|
|
96
|
+
|
|
95
97
|
var uploadOptions = {
|
|
96
98
|
scope: `${qiniuConfig.Bucket}:${plan.key}`
|
|
97
99
|
};
|
package/index.js
CHANGED
|
@@ -58,7 +58,7 @@ program.configureHelp({
|
|
|
58
58
|
},
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
program.name("struggler-cli").description(locale.appDescription).version(packageJson.version, "-v, --version", locale.options.version).helpOption("-h, --help", locale.options.help).option("-c, --config <path>", locale.options.config, "./command/qiniu.json").option("-d, --dir <path>", locale.options.dir, "./dist").option("--dry-run", locale.options.dryRun).option("--concurrency <number>", locale.options.concurrency, "5").option("--exclude <pattern>", locale.options.exclude).option("--ignore-file <path>", locale.options.ignoreFile, ".strugglerignore").option("--manifest <path>", locale.options.manifest).option("--json", locale.options.json).option("--skip-init", locale.options.skipInit).option("--skip-refresh", locale.options.skipRefresh).option("--no-cache", locale.options.noCache).option("--lang <lang>", locale.options.lang, lang)
|
|
61
|
+
program.name("struggler-cli").description(locale.appDescription).version(packageJson.version, "-v, --version", locale.options.version).helpOption("-h, --help", locale.options.help).option("-c, --config <path>", locale.options.config, "./command/qiniu.json").option("--config-dir <path>", locale.options.configDir).option("-d, --dir <path>", locale.options.dir, "./dist").option("--dry-run", locale.options.dryRun).option("--concurrency <number>", locale.options.concurrency, "5").option("--exclude <pattern>", locale.options.exclude).option("--ignore-file <path>", locale.options.ignoreFile, ".strugglerignore").option("--manifest <path>", locale.options.manifest).option("--json", locale.options.json).option("--skip-init", locale.options.skipInit).option("--skip-refresh", locale.options.skipRefresh).option("--no-cache", locale.options.noCache).option("--lang <lang>", locale.options.lang, lang)
|
|
62
62
|
|
|
63
63
|
program
|
|
64
64
|
.command("init")
|
package/lib/config.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
let path = require('path')
|
|
2
|
+
const { getJsonData } = require('./files');
|
|
2
3
|
|
|
3
4
|
const DEFAULT_QINIU_CONFIG = './command/qiniu.json';
|
|
5
|
+
const DEFAULT_META_DIR = './command';
|
|
4
6
|
const DEFAULT_CONFIG = './command/config.json';
|
|
5
7
|
const DEFAULT_CACHE = './command/upload-cache.json';
|
|
6
8
|
|
|
@@ -12,22 +14,31 @@ function getQiniuConfigPath(options = {}) {
|
|
|
12
14
|
return resolveFromCwd(options.config || DEFAULT_QINIU_CONFIG);
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
/** config.json / upload-cache.json 所在目录;优先级:CLI --config-dir > qiniu.json configDir > 与 qiniu 同目录 > ./command */
|
|
18
|
+
function getMetaDir(options = {}) {
|
|
19
|
+
if (options.configDir) {
|
|
20
|
+
return resolveFromCwd(options.configDir);
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
const qiniuConfigPath = getQiniuConfigPath(options);
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
const qiniuConfig = getJsonData(qiniuConfigPath);
|
|
25
|
+
if (qiniuConfig.configDir) {
|
|
26
|
+
return resolveFromCwd(qiniuConfig.configDir);
|
|
27
|
+
}
|
|
23
28
|
|
|
24
|
-
function getCachePath(options = {}) {
|
|
25
29
|
if (!options.config) {
|
|
26
|
-
return resolveFromCwd(
|
|
30
|
+
return resolveFromCwd(DEFAULT_META_DIR);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
return path.dirname(qiniuConfigPath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getConfigPath(options = {}) {
|
|
37
|
+
return path.join(getMetaDir(options), 'config.json');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getCachePath(options = {}) {
|
|
41
|
+
return path.join(getMetaDir(options), 'upload-cache.json');
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
module.exports = {
|
|
@@ -40,6 +51,9 @@ module.exports = {
|
|
|
40
51
|
getCachePath: (options) => {
|
|
41
52
|
return getCachePath(options)
|
|
42
53
|
},
|
|
54
|
+
getMetaDir: (options) => {
|
|
55
|
+
return getMetaDir(options)
|
|
56
|
+
},
|
|
43
57
|
getDir: (options) => {
|
|
44
58
|
return resolveFromCwd(options.dir || './dist')
|
|
45
59
|
},
|
package/lib/i18n.js
CHANGED
|
@@ -38,6 +38,7 @@ const LANGUAGES = {
|
|
|
38
38
|
options: {
|
|
39
39
|
version: '显示版本号。',
|
|
40
40
|
config: '指定上传配置文件路径。',
|
|
41
|
+
configDir: '指定 config.json、upload-cache.json 所在目录(默认与 -c 同目录,未传 -c 时为 ./command)。',
|
|
41
42
|
dir: '指定要上传的目录。',
|
|
42
43
|
dryRun: '仅预览执行计划,不写文件也不调用七牛接口。',
|
|
43
44
|
concurrency: '设置上传并发数。',
|
|
@@ -111,6 +112,7 @@ const LANGUAGES = {
|
|
|
111
112
|
options: {
|
|
112
113
|
version: 'Display the version number.',
|
|
113
114
|
config: 'Specify the path to the upload configuration file.',
|
|
115
|
+
configDir: 'Directory for config.json and upload-cache.json (default: same dir as -c, or ./command).',
|
|
114
116
|
dir: 'Specify the directory to upload.',
|
|
115
117
|
dryRun: 'Preview actions without writing files or calling Qiniu APIs.',
|
|
116
118
|
concurrency: 'Set upload concurrency.',
|
package/lib/mime.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
const MIME_TYPES = {
|
|
4
|
+
'.js': 'application/javascript',
|
|
5
|
+
'.mjs': 'application/javascript',
|
|
6
|
+
'.cjs': 'application/javascript',
|
|
7
|
+
'.css': 'text/css',
|
|
8
|
+
'.html': 'text/html',
|
|
9
|
+
'.htm': 'text/html',
|
|
10
|
+
'.json': 'application/json',
|
|
11
|
+
'.map': 'application/json',
|
|
12
|
+
'.txt': 'text/plain',
|
|
13
|
+
'.xml': 'application/xml',
|
|
14
|
+
'.svg': 'image/svg+xml',
|
|
15
|
+
'.png': 'image/png',
|
|
16
|
+
'.jpg': 'image/jpeg',
|
|
17
|
+
'.jpeg': 'image/jpeg',
|
|
18
|
+
'.gif': 'image/gif',
|
|
19
|
+
'.webp': 'image/webp',
|
|
20
|
+
'.ico': 'image/x-icon',
|
|
21
|
+
'.woff': 'font/woff',
|
|
22
|
+
'.woff2': 'font/woff2',
|
|
23
|
+
'.ttf': 'font/ttf',
|
|
24
|
+
'.otf': 'font/otf',
|
|
25
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
26
|
+
'.wasm': 'application/wasm',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function loadMimeResolver() {
|
|
30
|
+
try {
|
|
31
|
+
const qiniuDir = path.dirname(require.resolve('qiniu'));
|
|
32
|
+
const mimePath = require.resolve('mime', { paths: [qiniuDir] });
|
|
33
|
+
return require(mimePath);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const mimeResolver = loadMimeResolver();
|
|
40
|
+
|
|
41
|
+
function resolveMimeType(filePath) {
|
|
42
|
+
if (mimeResolver && typeof mimeResolver.getType === 'function') {
|
|
43
|
+
const detected = mimeResolver.getType(filePath);
|
|
44
|
+
if (detected) {
|
|
45
|
+
return detected;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
50
|
+
return MIME_TYPES[extension] || null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
resolveMimeType,
|
|
55
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@struggler/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "CLI to Upload vite packaged files to Qiniu Cloud OSS.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,14 +16,16 @@
|
|
|
16
16
|
],
|
|
17
17
|
"author": "moqi(str@li.cm)",
|
|
18
18
|
"license": "ISC",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=16"
|
|
21
|
+
},
|
|
19
22
|
"dependencies": {
|
|
20
23
|
"chalk": "^v4.1.2",
|
|
21
24
|
"clear": "^0.1.0",
|
|
22
25
|
"commander": "^11.0.0",
|
|
23
|
-
"figlet": "^1.6.0",
|
|
24
26
|
"qiniu": "^7.8.0"
|
|
25
27
|
},
|
|
26
28
|
"publicConfig": {
|
|
27
29
|
"registry": "http://registry.npmjs.org/"
|
|
28
30
|
}
|
|
29
|
-
}
|
|
31
|
+
}
|
package/test/config.test.js
CHANGED
|
@@ -24,6 +24,18 @@ test('config helpers resolve qiniu and derived config paths together', () => {
|
|
|
24
24
|
);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
+
test('config-dir overrides default meta directory', () => {
|
|
28
|
+
const options = {
|
|
29
|
+
config: './test/command/qiniu.json',
|
|
30
|
+
configDir: './test/meta',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
assert.equal(
|
|
34
|
+
getConfig(options),
|
|
35
|
+
`${process.cwd()}/test/meta/config.json`
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
27
39
|
test('deploy helpers normalize concurrency and build remote keys', () => {
|
|
28
40
|
assert.equal(normalizeConcurrency('0'), 5);
|
|
29
41
|
assert.equal(normalizeConcurrency('3'), 3);
|
package/test/upload.test.js
CHANGED
|
@@ -3,8 +3,10 @@ const assert = require('node:assert/strict');
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const qiniu = require('qiniu');
|
|
6
7
|
const upload = require('../command/upload');
|
|
7
8
|
const deploy = require('../command/deploy');
|
|
9
|
+
const { resolveMimeType } = require('../lib/mime');
|
|
8
10
|
|
|
9
11
|
function createWorkspace() {
|
|
10
12
|
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'struggler-cli-upload-'));
|
|
@@ -111,3 +113,71 @@ test('deploy dry-run supports skip-refresh and json-friendly summary data', asyn
|
|
|
111
113
|
fs.rmSync(workspace, { recursive: true, force: true });
|
|
112
114
|
}
|
|
113
115
|
});
|
|
116
|
+
|
|
117
|
+
test('upload sets explicit mimeType for web assets', async () => {
|
|
118
|
+
const previousCwd = process.cwd();
|
|
119
|
+
const workspace = createWorkspace();
|
|
120
|
+
const captured = [];
|
|
121
|
+
|
|
122
|
+
const originalMac = qiniu.auth.digest.Mac;
|
|
123
|
+
const originalPutPolicy = qiniu.rs.PutPolicy;
|
|
124
|
+
const originalFormUploader = qiniu.form_up.FormUploader;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
process.chdir(workspace);
|
|
128
|
+
|
|
129
|
+
fs.writeFileSync(path.join(workspace, 'command/qiniu.json'), JSON.stringify({
|
|
130
|
+
accessKey: 'ak',
|
|
131
|
+
secretKey: 'sk',
|
|
132
|
+
Bucket: 'demo-bucket',
|
|
133
|
+
zone: 'Zone_z0',
|
|
134
|
+
path: 'demo-app',
|
|
135
|
+
domain: 'https://cdn.example.com/',
|
|
136
|
+
}, null, 2));
|
|
137
|
+
|
|
138
|
+
qiniu.auth.digest.Mac = function MockMac() {};
|
|
139
|
+
qiniu.rs.PutPolicy = function MockPutPolicy() {
|
|
140
|
+
this.uploadToken = () => 'mock-token';
|
|
141
|
+
};
|
|
142
|
+
qiniu.form_up.FormUploader = function MockFormUploader() {
|
|
143
|
+
this.putFile = (uploadToken, key, localFile, putExtra, callback) => {
|
|
144
|
+
captured.push({
|
|
145
|
+
uploadToken,
|
|
146
|
+
key,
|
|
147
|
+
localFile,
|
|
148
|
+
mimeType: putExtra.mimeType,
|
|
149
|
+
});
|
|
150
|
+
callback(null, { hash: `${path.basename(localFile)}-hash` }, { statusCode: 200 });
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const summary = await upload({
|
|
155
|
+
config: './command/qiniu.json',
|
|
156
|
+
dir: './dist',
|
|
157
|
+
concurrency: '1',
|
|
158
|
+
}, {
|
|
159
|
+
suppressOutput: true,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
assert.equal(summary.failedCount, 0);
|
|
163
|
+
assert.deepEqual(
|
|
164
|
+
captured.map((item) => [path.basename(item.localFile), item.mimeType]).sort(),
|
|
165
|
+
[
|
|
166
|
+
['app.js', 'application/javascript'],
|
|
167
|
+
['index.html', 'text/html'],
|
|
168
|
+
]
|
|
169
|
+
);
|
|
170
|
+
} finally {
|
|
171
|
+
qiniu.auth.digest.Mac = originalMac;
|
|
172
|
+
qiniu.rs.PutPolicy = originalPutPolicy;
|
|
173
|
+
qiniu.form_up.FormUploader = originalFormUploader;
|
|
174
|
+
process.chdir(previousCwd);
|
|
175
|
+
fs.rmSync(workspace, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('resolveMimeType covers common assets and keeps safe fallback', () => {
|
|
180
|
+
assert.equal(resolveMimeType('/tmp/app.js'), 'application/javascript');
|
|
181
|
+
assert.equal(resolveMimeType('/tmp/logo.svg'), 'image/svg+xml');
|
|
182
|
+
assert.equal(resolveMimeType('/tmp/data.unknown-ext'), null);
|
|
183
|
+
});
|