tt-minigame-ide-cli 0.0.1-beta.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/lib/create.js ADDED
@@ -0,0 +1,106 @@
1
+ // 创建小程序模版
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const inquirer = require('inquirer');
5
+ const chalk = require('chalk');
6
+ const axios = require('axios');
7
+ const unzipper = require('unzipper');
8
+ const { createProjectConfigFile } = require('./util/config');
9
+ const { clearConsole } = require('./util/logger');
10
+ const { getVersion } = require('./util/version');
11
+ const TEMPLATE_DOWNLOAD_URL = 'https://sf1-cdn-tos.douyinstatic.com/obj/microapp/frontend/ide/template/tma-showcase.zip';
12
+
13
+ async function create(projectName, options) {
14
+ const cwd = options.cwd || process.cwd();
15
+ const targetDir = path.resolve(cwd, projectName || '.');
16
+ clearConsole(chalk.bold.green(`miniprogram ide CLI v${getVersion()}`));
17
+ if (fs.existsSync(targetDir)) {
18
+ if (options.force) {
19
+ await fs.remove(targetDir);
20
+ } else {
21
+ const { action } = await inquirer.prompt([
22
+ {
23
+ name: 'action',
24
+ type: 'list',
25
+ message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
26
+ choices: [
27
+ { name: 'Overwrite', value: 'overwrite' },
28
+ { name: 'Cancel', value: false }
29
+ ]
30
+ }
31
+ ]);
32
+ if (!action) {
33
+ return;
34
+ } else if (action === 'overwrite') {
35
+ console.log(`\nRemoving ${chalk.cyan(targetDir)}...`);
36
+ await fs.remove(targetDir);
37
+ }
38
+ }
39
+ }
40
+ // Choose template
41
+ const { action } = await inquirer.prompt([
42
+ {
43
+ name: 'action',
44
+ type: 'list',
45
+ message: `Please select a template:`,
46
+ choices: [
47
+ { name: 'Blank template', value: 'blank' },
48
+ { name: 'Capability-rich template', value: 'rich' },
49
+ { name: 'Cancel', value: false }
50
+ ]
51
+ }
52
+ ]);
53
+
54
+ if (!action) return;
55
+
56
+ fs.ensureDirSync(targetDir);
57
+ if (action === 'rich') {
58
+ // Pull the template from the web
59
+ console.log(`⚙\u{fe0f} Installing template. This might take a while....`);
60
+ await downloadRemoteTemplate(TEMPLATE_DOWNLOAD_URL, targetDir)
61
+ } else {
62
+ // Copy template from local
63
+ await fs.copy(path.join(__dirname, '../template'), targetDir);
64
+ await createProjectConfigFile(projectName, targetDir);
65
+ }
66
+
67
+ console.log(`🎉 Successfully created project ${chalk.yellow(projectName)}.`);
68
+ }
69
+
70
+ async function downloadRemoteTemplate(sourcePath, targetPath) {
71
+ return new Promise(async (resolve, reject) => {
72
+ const zipPath = path.join(targetPath, 'template.zip');
73
+ const stream = fs.createWriteStream(zipPath);
74
+ const response = await axios({
75
+ url: sourcePath,
76
+ method: "GET",
77
+ responseType: "stream",
78
+ });
79
+ response.data.pipe(stream);
80
+
81
+ stream.on('finish', () => {
82
+ fs.createReadStream(zipPath).
83
+ pipe(unzipper.Extract({ path: targetPath }))
84
+ .on('close', () => {
85
+ fs.unlinkSync(zipPath);
86
+ resolve();
87
+ })
88
+ .on('error', (err) => {
89
+ fs.unlinkSync(zipPath);
90
+ reject(err);
91
+ });
92
+ });
93
+
94
+ stream.on('error', (err) => {
95
+ reject(err);
96
+ });
97
+ });
98
+ }
99
+
100
+
101
+ module.exports = (...args) => {
102
+ return create(...args).catch(err => {
103
+ console.log(chalk.red(`[CLI CREATE ERROR]: ${err}`));
104
+ process.exit(1);
105
+ });
106
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ interface PreviewOptions {
2
+ entry?: string;
3
+ copy?: boolean;
4
+ force?: boolean;
5
+ small?: boolean;
6
+ proxy?: string;
7
+ }
8
+
9
+ interface UploadOptions {
10
+ entry?: string;
11
+ version: string;
12
+ changeLog: string;
13
+ proxy?: string;
14
+ copy?: boolean;
15
+ }
16
+
17
+ declare namespace ttIdeCli {
18
+ function create(projectName: string, options: { force?: boolean }): void;
19
+ function loginByName(email: string, password: string): void;
20
+ function login(options: { type?: string; proxy?: string }): void;
21
+ function preview(options: PreviewOptions): void;
22
+ function upload(options: UploadOptions): void;
23
+ }
package/lib/index.js ADDED
@@ -0,0 +1,79 @@
1
+ const _preview = require('./preview');
2
+ const _upload = require('./upload');
3
+ const _create = require('./create');
4
+ const _auth = require('./auth');
5
+ const buildNpm = require('./build-npm');
6
+ const config = require('./util/config');
7
+ const cookieHandler = require('./util/cookie');
8
+
9
+ function create(projectName, { force = false }) {
10
+ return _create(projectName, {
11
+ force,
12
+ });
13
+ }
14
+
15
+ function loginByEmail(email, password, customOptions) {
16
+ const options = {
17
+ type: 'direct',
18
+ email,
19
+ password,
20
+ ...customOptions,
21
+ };
22
+
23
+ return _auth(options);
24
+ }
25
+
26
+ function login({ type = '', proxy = '' }) {
27
+ if (type === 'mobile' || type === 'email') {
28
+ return _auth({ [type]: true, proxy });
29
+ } else {
30
+ return _auth({ proxy });
31
+ }
32
+ }
33
+
34
+ function preview({
35
+ entry = '',
36
+ copy = false,
37
+ force = false,
38
+ small = false,
39
+ proxy = '',
40
+ tempDir = null
41
+ }) {
42
+ return _preview(entry, {
43
+ copy,
44
+ force,
45
+ small,
46
+ proxy,
47
+ tempDir,
48
+ });
49
+ }
50
+
51
+ function upload({ entry = '', version, changeLog, proxy = '', copy = false, tempDir = null }) {
52
+ return _upload(entry, {
53
+ appVersion: version,
54
+ appChangelog: changeLog,
55
+ proxy,
56
+ copy,
57
+ tempDir
58
+ });
59
+ }
60
+
61
+ function setConfig(customConfig) {
62
+ console.log('configconfigconfig=:', customConfig);
63
+ return config.setConfig(customConfig);
64
+ }
65
+
66
+ async function checkSession() {
67
+ return await cookieHandler.checkSession();
68
+ }
69
+
70
+ module.exports = {
71
+ create,
72
+ loginByEmail,
73
+ login,
74
+ preview,
75
+ upload,
76
+ buildNpm,
77
+ setConfig,
78
+ checkSession,
79
+ };
package/lib/preview.js ADDED
@@ -0,0 +1,86 @@
1
+ // Preview project
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const chalk = require('chalk');
5
+ const { hashElement } = require('folder-hash');
6
+ const { outputResult, clearConsole } = require('./util/logger');
7
+ const { checkBeforeUpload } = require('./util/check');
8
+ const { zipFile, savePreviewResult, getCachedPreviewResult } = require('./util/helper');
9
+ const { uploadProject, getCompileProgress, getQrCode } = require('./util/request');
10
+ const { generateQrCode } = require('./util/qrcode');
11
+
12
+ async function preview(entry, options) {
13
+ const cwd = options.cwd || process.cwd();
14
+ if (!entry || entry === '.') {
15
+ entry = cwd;
16
+ } else {
17
+ entry = path.resolve(cwd, entry);
18
+ }
19
+ const dirHash = await hashElement(entry, { folders: { exclude: ['node_modules'] } });
20
+ const cacheDisabled = options.force || options.disableCache;
21
+ if (!cacheDisabled) {
22
+ // check local cache
23
+ const cacheRet = getCachedPreviewResult('native', dirHash.hash);
24
+ if (cacheRet) {
25
+ generateQrCode(cacheRet, options);
26
+ console.log(chalk.cyan(`Since local files have not changed, use the cache result\nUse option ${chalk.yellow('-f')} to ignore cache`));
27
+ return;
28
+ }
29
+ }
30
+
31
+ const { success, msg, appId } = await checkBeforeUpload(entry);
32
+ if (!success) {
33
+ const errMsg = `[CLI PREVIEW ERROR]: ${msg}`;
34
+ console.log(chalk.red(errMsg));
35
+ throw new Error(errMsg);
36
+ return;
37
+ }
38
+
39
+ const tempDir = options.tempDir || path.join(__dirname, '../temp');
40
+ await fs.emptyDir(tempDir);
41
+ const { filePath } = await zipFile(entry, tempDir);
42
+ if (!filePath) {
43
+ const errMsg = `[CLI PREVIEW ERROR]`;
44
+ console.log(chalk.red(errMsg));
45
+ throw new Error(errMsg);
46
+ return;
47
+ }
48
+ await uploadProject({ type: 'preview', appId, zipPath: filePath, proxy: options.proxy });
49
+
50
+ outputResult('info', 'WAIT', 'Compiling...\n');
51
+ return await new Promise((resolve, reject) => {
52
+ const timer = setInterval(async () => {
53
+ const { success, msg, done } = await getCompileProgress(appId, options.proxy);
54
+ if (!success) {
55
+ const errMsg = `[CLI PREVIEW ERROR]: ${msg}`;
56
+ console.log(chalk.red(errMsg));
57
+ clearInterval(timer);
58
+ reject(errMsg);
59
+ return;
60
+ }
61
+ if (success && done) {
62
+ clearInterval(timer);
63
+ const ret = await getQrCode(appId, options.proxy);
64
+ if (ret.success) {
65
+ clearConsole();
66
+ outputResult('success', `DONE`, `Scan the QR Code for remote preview\n`);
67
+ generateQrCode(ret.url, options);
68
+ // save preview result
69
+ savePreviewResult(ret.url, 'native', dirHash.hash);
70
+ resolve(ret.url);
71
+ } else {
72
+ console.log(chalk.red(`[CLI PREVIEW ERROR]: ${ret.msg}`));
73
+ reject(ret.msg);
74
+ }
75
+ }
76
+ }, 1500);
77
+ });
78
+ }
79
+
80
+ module.exports = (...args) => {
81
+ return preview(...args).catch(err => {
82
+ const errMsg = `[CLI PREVIEW ERROR]: ${err}`;
83
+ console.log(chalk.red(errMsg));
84
+ throw new Error(errMsg);
85
+ });
86
+ }
package/lib/upload.js ADDED
@@ -0,0 +1,120 @@
1
+ // Upload project
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const chalk = require('chalk');
5
+ const {
6
+ outputResult,
7
+ clearConsole
8
+ } = require('./util/logger');
9
+ const {
10
+ checkBeforeUpload,
11
+ isValidAppVersion
12
+ } = require('./util/check');
13
+ const {
14
+ zipFile
15
+ } = require('./util/helper');
16
+ const {
17
+ uploadProject,
18
+ getCompileProgress,
19
+ checkUserPermissions,
20
+ getQrCode
21
+ } = require('./util/request');
22
+ const {
23
+ generateQrCode
24
+ } = require('./util/qrcode');
25
+
26
+ async function upload(entry, options) {
27
+ const cwd = options.cwd || process.cwd();
28
+ if (!entry) {
29
+ entry = cwd
30
+ } else if (path.isAbsolute(entry)) {
31
+ // skip if entry if absolute path
32
+ } else {
33
+ // resolve the relative entry with cwd
34
+ entry = path.resolve(cwd, entry);
35
+ }
36
+
37
+ if (!isValidAppVersion(options.appVersion)) {
38
+ const errMsg = `[CLI UPLOAD ERROR]: App version ${options.appVersion} is not valid, expected [major].[minor].[patch]`;
39
+ console.log(chalk.red(errMsg));
40
+ throw new Error(errMsg);
41
+ }
42
+
43
+ const {
44
+ success: beforeSuccess,
45
+ msg: beforeMsg,
46
+ appId
47
+ } = await checkBeforeUpload(entry);
48
+ if (!beforeSuccess) {
49
+ const errMsg = `[CLI UPLOAD ERROR]: ${beforeMsg}`;
50
+ console.log(chalk.red(errMsg));
51
+ throw new Error(errMsg);
52
+ }
53
+ const tempDir = options.tempDir || path.join(__dirname, '../temp');
54
+ await fs.emptyDir(tempDir);
55
+
56
+ const {
57
+ success: authSuccess,
58
+ msg: authMsg
59
+ } = await checkUserPermissions(appId, options.proxy);
60
+ if (!authSuccess) {
61
+ const errMsg = `[CLI UPLOAD ERROR]: ${authMsg}`;
62
+ console.log(chalk.red(errMsg));
63
+ throw new Error(errMsg);
64
+ }
65
+
66
+ const { filePath } = await zipFile(entry, tempDir);
67
+ if (!filePath) {
68
+ const errMsg = `[CLI UPLOAD ERROR]`;
69
+ console.log(chalk.red(errMsg));
70
+ throw new Error(errMsg);
71
+ }
72
+ await uploadProject({
73
+ type: 'upload',
74
+ appId,
75
+ zipPath: filePath,
76
+ versionInfo: options.appVersion,
77
+ updateInfo: options.appChangelog,
78
+ proxy: options.proxy,
79
+ });
80
+
81
+ outputResult('info', 'WAIT', 'Compiling...\n');
82
+ return new Promise((resolve, reject) => {
83
+ const timer = setInterval(async () => {
84
+ const {
85
+ success,
86
+ msg,
87
+ done
88
+ } = await getCompileProgress(appId, options.proxy);
89
+ if (!success) {
90
+ const errMsg = `[CLI UPLOAD ERROR]: ${msg}`;
91
+ console.log(chalk.red(errMsg));
92
+ clearInterval(timer);
93
+ reject(errMsg);
94
+ }
95
+ if (success && done) {
96
+ clearConsole();
97
+ outputResult('success', `v${options.appVersion}`, ` 🎉 Successfully uploaded project`);
98
+ const ret = await getQrCode(appId, options.proxy, 'latest');
99
+ if (ret.success) {
100
+ outputResult('success', `DONE`, `Scan the QR Code for remote preview\n`);
101
+ generateQrCode(ret.url, options);
102
+ resolve(ret.url);
103
+ } else {
104
+ const errMsg = `[CLI UPLOAD ERROR]: ${ret.msg}`;
105
+ console.log(chalk.red(errMsg));
106
+ reject(ret.msg);
107
+ }
108
+ clearInterval(timer);
109
+ }
110
+ }, 1500);
111
+ });
112
+ }
113
+
114
+ module.exports = (...args) => {
115
+ return upload(...args).catch(err => {
116
+ const errMsg = `[CLI UPLOAD ERROR]: ${err}`;
117
+ console.log(chalk.red(errMsg));
118
+ throw new Error(errMsg);
119
+ });
120
+ }
@@ -0,0 +1,84 @@
1
+ const fs = require('fs-extra');
2
+ const chalk = require('chalk');
3
+ const path = require('path');
4
+ const semver = require('semver');
5
+ const { checkRemoteVersion } = require('./request');
6
+ const { getUserCookies } = require('./cookie');
7
+ const { getLatestVersionFromLocal, saveLatestVersionToLocal, getVersion } = require('./version');
8
+
9
+ function isValidPhoneNumber(number) {
10
+ return /^1[3-9]\d{9}$/.test(number);
11
+ }
12
+
13
+ function isValidVerificationCode(number) {
14
+ return /^\d{4}$/.test(number);
15
+ }
16
+
17
+ function isValidEmail(email) {
18
+ return /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(email);
19
+ }
20
+
21
+ function isValidAppId(id) {
22
+ return /^tt[0-9a-f]{16}([0-9a-f]{2})?$/.test(id);
23
+ }
24
+
25
+ function isValidAppVersion(version) {
26
+ return /^\d+\.\d+\.\d+$/.test(version);
27
+ }
28
+
29
+ async function checkBeforeUpload(entry) {
30
+ const configPath = path.join(entry, 'project.config.json');
31
+ const exists = await fs.pathExists(configPath);
32
+ if (!exists) {
33
+ return {
34
+ success: false,
35
+ msg: 'Invalid project: project.config.json file not found'
36
+ }
37
+ }
38
+ const appConfig = await fs.readFile(configPath, 'utf-8');
39
+ const appConfigJSON = JSON.parse(appConfig);
40
+ const appId = appConfigJSON.ttappid || appConfigJSON.appid;
41
+ if (!isValidAppId(appId)) {
42
+ return { success: false, msg: `AppId ${chalk.cyan(appId)} is not valid` };
43
+ }
44
+ if (!getUserCookies()) {
45
+ console.log(chalk.red());
46
+ return {
47
+ success: false,
48
+ msg: `This feature requires login, please login via ${chalk.cyan(
49
+ 'tmg login'
50
+ )} command`,
51
+ };
52
+ }
53
+ return { success: true, appId };
54
+ }
55
+ // 该方法暂无调用
56
+ async function checkNeedUpdate() {
57
+ let needUpdate = false;
58
+ const versionCache = await getLatestVersionFromLocal();
59
+ const daysPassed = (Date.now() - versionCache.time || 0) / (60 * 60 * 1000 * 24);
60
+ if (daysPassed > 1 || !versionCache.version) {
61
+ const { version } = await checkRemoteVersion();
62
+ versionCache.version = version || '0.0.0';
63
+ saveLatestVersionToLocal(version, Date.now());
64
+ }
65
+ const currentVersion = getVersion();
66
+ if (semver.gt(versionCache.version, currentVersion)) {
67
+ needUpdate = true;
68
+ }
69
+ return {
70
+ needUpdate,
71
+ latest: versionCache.version,
72
+ current: currentVersion
73
+ }
74
+ }
75
+
76
+ module.exports = {
77
+ isValidPhoneNumber,
78
+ isValidEmail,
79
+ isValidVerificationCode,
80
+ isValidAppId,
81
+ isValidAppVersion,
82
+ checkBeforeUpload,
83
+ checkNeedUpdate
84
+ }
@@ -0,0 +1,41 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const os = require('os');
4
+ let customConfig = {};
5
+
6
+ function setConfig(config = {}) {
7
+ return customConfig = {
8
+ ...customConfig,
9
+ ...config
10
+ };
11
+ }
12
+
13
+ function createProjectConfigFile(projectName, projectDir) {
14
+ const configJson = `{
15
+ "setting": {
16
+ "urlCheck": true,
17
+ "es6": true,
18
+ "postcss": true,
19
+ "minified": true,
20
+ "newFeature": true
21
+ },
22
+ "miniprogramRoot": "./",
23
+ "appid": "testappId",
24
+ "projectname": "${projectName}"
25
+ }`;
26
+ const filePath = path.resolve(projectDir, 'project.config.json');
27
+ return fs.writeFile(filePath, configJson);
28
+ }
29
+
30
+ function getStoragePath() {
31
+ if (customConfig.storagePath) {
32
+ return customConfig.storagePath;
33
+ }
34
+ return path.join(os.homedir(), `.tmg-cli/`);
35
+ }
36
+
37
+ module.exports = {
38
+ setConfig,
39
+ createProjectConfigFile,
40
+ getStoragePath
41
+ };
@@ -0,0 +1,36 @@
1
+ // const os = require('os');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const { getStoragePath } = require('./config');
5
+ let cookieStr = '';
6
+
7
+ async function saveUserCookies(cookies) {
8
+ const cookieSavedPath = path.join(getStoragePath(), '.cookies');
9
+ fs.ensureFileSync(cookieSavedPath);
10
+ if (!cookies || cookies.length < 1) return;
11
+ cookieStr = cookies.map(cookie => cookie.split(';')[0]).join(';');
12
+ try {
13
+ fs.writeFile(path.join(cookieSavedPath), cookieStr);
14
+ }
15
+ catch (err) {
16
+ console.error(err);
17
+ }
18
+ }
19
+
20
+
21
+ function getUserCookies() {
22
+ const cookieSavedPath = path.join(getStoragePath(), '.cookies');
23
+ return cookieStr || fs.readFileSync(cookieSavedPath, 'utf-8').trim();
24
+ }
25
+
26
+ async function checkSession() {
27
+ const cookieSavedPath = path.join(getStoragePath(), '.cookies');
28
+ const cookieExists = await fs.pathExists(cookieSavedPath);
29
+ return cookieExists;
30
+ }
31
+
32
+ module.exports = {
33
+ saveUserCookies,
34
+ getUserCookies,
35
+ checkSession
36
+ }