From 8d228ec66774784de7e7ef7f79daf5c0666b0d7b Mon Sep 17 00:00:00 2001 From: luhc228 Date: Mon, 26 Jul 2021 17:06:48 +0800 Subject: [PATCH 1/9] chore: version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2467016..f1c7787 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "appworks-toolkit", "private": true, - "version": "0.2.0", + "version": "0.2.1", "description": "The Frontend Developer Toolkit", "scripts": { "setup": "npm run install:main && npm run install:renderer", From 6b5c003c808ff5199862e97fae9228f58e3359fe Mon Sep 17 00:00:00 2001 From: Hengchang Lu <44047106+luhc228@users.noreply.github.com> Date: Mon, 2 Aug 2021 17:18:28 +0800 Subject: [PATCH 2/9] Feat: same config name tips (#54) --- .../UserGitConfigDialogForm/index.tsx | 21 ++++++++++--------- renderer/src/pages/Git/index.tsx | 5 ----- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/renderer/src/pages/Git/components/UserGitConfigDialogForm/index.tsx b/renderer/src/pages/Git/components/UserGitConfigDialogForm/index.tsx index 3a488a7..b6a3690 100644 --- a/renderer/src/pages/Git/components/UserGitConfigDialogForm/index.tsx +++ b/renderer/src/pages/Git/components/UserGitConfigDialogForm/index.tsx @@ -14,7 +14,7 @@ const UserGitConfigDialogForm: FC = ({ refresh }) const { userGitConfigFormVisible, - // existedUserGitConfigNames + existedUserGitConfigNames, } = state; const field = Field.useField({ parseName: true }); @@ -59,12 +59,15 @@ const UserGitConfigDialogForm: FC = ({ refresh }) dispatcher.getExistedUserGitConfigs(); }, []); - // const validateGitConfigName = (rule: any, setValue: string, callback: (error?: string) => void) => { - // if (existedUserGitConfigNames.includes(setValue)) { - // return callback('配置名已存在,请重新输入'); - // } - // return callback(); - // }; + const validateGitConfigName = (rule: any, setValue: string, callback: (error?: string) => void) => { + if (!/^[a-z]([-_a-z0-9]*)$/i.test(setValue)) { + return callback('请输入字母和数字的组合,以字母开头,如 Github'); + } + if (existedUserGitConfigNames.includes(setValue)) { + return callback('配置名已存在,请重新输入'); + } + }; + const dialogFooter = (
@@ -93,9 +96,7 @@ const UserGitConfigDialogForm: FC = ({ refresh }) label="配置名称" required requiredMessage="请输入配置名称" - pattern={/^[a-z]([-_a-z0-9]*)$/i} - patternMessage="请输入字母和数字的组合,以字母开头,如 Github" - // validator={validateGitConfigName} + validator={validateGitConfigName} > diff --git a/renderer/src/pages/Git/index.tsx b/renderer/src/pages/Git/index.tsx index 84ca8b0..79d3995 100644 --- a/renderer/src/pages/Git/index.tsx +++ b/renderer/src/pages/Git/index.tsx @@ -11,11 +11,6 @@ function Git() {
- {/* */}
); } From e3d986fa2e9c7409c84739ccf9d1b4eb44ecf4ee Mon Sep 17 00:00:00 2001 From: Hengchang Lu <44047106+luhc228@users.noreply.github.com> Date: Tue, 3 Aug 2021 10:31:58 +0800 Subject: [PATCH 3/9] Fix: fail to get internal npm package info (#53) --- CHANGELOG.md | 24 ++++++++++++++++++++++++ main/constants.ts | 1 - main/npm/dependency/getInfo.ts | 17 +++++++++++++---- main/utils/getNpmRegistry.ts | 3 ++- package.json | 1 + 5 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..585aff7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# CHANGELOG + +## 0.2.1 + +- Fix: fail to get internal npm package info + +## 0.2.0 + +- Feat: git manager, including global config, user config and SSH key +- Feat: npm register manager, fast switch between different registries, e.g: npm, yarn, taobao +- Feat: npm package manager, fast install, upgrade, reinstall and uninstall global dependencies + +## 0.1.1 + +- Feat: record DAU +- Feat: download package automatically +- Fix: nvm install fail when bash profile files exist +- Fix: icon size +- Fix: code command was not found + +## 0.1.0 + +- Feat: fast install packages, including VS Code/Git/Node.js/AppWorks/Chrome +- Feat: node version manager \ No newline at end of file diff --git a/main/constants.ts b/main/constants.ts index 3e42eb5..d60145b 100644 --- a/main/constants.ts +++ b/main/constants.ts @@ -34,7 +34,6 @@ export const DEFAULT_PROFILE_FILE = '.bash_profile'; export const NPMRC_PATH = path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], '.npmrc'); export const NPM_REGISTRY = 'https://registry.npmjs.org/'; export const TAOBAO_NPM_REGISTRY = 'https://registry.npm.taobao.org'; -export const ALI_NPM_REGISTRY = 'https://registry.npm.alibaba-inc.com/'; export const TAOBAO_NODE_MIRROR = 'https://npm.taobao.org/mirrors/node'; // git export const GLOBAL_GITCONFIG_PATH = path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], '.gitconfig'); diff --git a/main/npm/dependency/getInfo.ts b/main/npm/dependency/getInfo.ts index 303f230..f082432 100644 --- a/main/npm/dependency/getInfo.ts +++ b/main/npm/dependency/getInfo.ts @@ -1,8 +1,9 @@ import packageJSON, { AbbreviatedMetadata } from 'package-json'; +import getNpmRegistry from '../../utils/getNpmRegistry'; +import getVersionStatus from '../../utils/getVersionStatus'; import executeCommandJSON from '../../utils/executeCommandJSON'; import log from '../../utils/log'; import nodeCache from '../../utils/nodeCache'; -import { getCurrentRegistry } from '../registry'; interface InstalledDependency { version: string; @@ -30,7 +31,14 @@ export async function getGlobalDependencies(force: boolean) { const depsInfo = []; for (const dep of deps) { - const latestVersion = await getLatestVersion(dep); + let latestVersion = ''; + // avoid failing to get the latest version of one package + // so that other packages can't get the latest versions + try { + latestVersion = await getLatestVersion(dep); + } catch (err) { + log.error(err); + } const currentVersion = getCurrentVersion(installedDeps[dep]); depsInfo.push({ @@ -38,7 +46,7 @@ export async function getGlobalDependencies(force: boolean) { type: 'global', currentVersion, latestVersion, - isOutdated: latestVersion !== currentVersion, + isOutdated: getVersionStatus(currentVersion, latestVersion) === 'upgradeable', }); } @@ -54,12 +62,13 @@ export async function getGlobalDependencies(force: boolean) { function getCurrentVersion(installedDependency: InstalledDependency) { return installedDependency.version; } + interface PackageJSON extends AbbreviatedMetadata { version: string; } async function getLatestVersion(name: string) { - const registryUrl = await getCurrentRegistry(); + const registryUrl = await getNpmRegistry(); const { version: latest } = await packageJSON( name, { registryUrl }, diff --git a/main/utils/getNpmRegistry.ts b/main/utils/getNpmRegistry.ts index 0f5808e..b4bc479 100644 --- a/main/utils/getNpmRegistry.ts +++ b/main/utils/getNpmRegistry.ts @@ -1,4 +1,5 @@ -import { TAOBAO_NPM_REGISTRY, ALI_NPM_REGISTRY } from '../constants'; +import { ALI_NPM_REGISTRY } from '@appworks/constant'; +import { TAOBAO_NPM_REGISTRY } from '../constants'; import checkIsAliInternal from './checkIsAliInternal'; async function getNpmRegistry() { diff --git a/package.json b/package.json index f1c7787..50ae991 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ } }, "dependencies": { + "@appworks/constant": "^0.1.3", "@appworks/recorder": "^1.0.0", "@shockpkg/hdi-mac": "^1.6.1", "adm-zip": "^0.5.5", From cd7f98d340372093b58be4c24305d757b7e0f008 Mon Sep 17 00:00:00 2001 From: Hengchang Lu <44047106+luhc228@users.noreply.github.com> Date: Tue, 3 Aug 2021 10:40:36 +0800 Subject: [PATCH 4/9] feat: use default shell to excute sh file (#57) --- main/constants.ts | 5 +++-- main/ipc/installNode.ts | 2 +- main/node/NvmManager.ts | 3 ++- main/packageInfo/cli/node.ts | 3 ++- main/packageInstaller/CliInstaller.ts | 3 ++- main/utils/ensureProfileExists.ts | 10 ++++++---- main/utils/getShellName.ts | 10 ++++++++++ 7 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 main/utils/getShellName.ts diff --git a/main/constants.ts b/main/constants.ts index d60145b..aba33a1 100644 --- a/main/constants.ts +++ b/main/constants.ts @@ -28,8 +28,9 @@ export const INSTALL_COMMAND_PACKAGES = [ ]; export const NOT_REINSTALL_DEPENDENCIES = ['npm']; // bash profile -export const PROFILE_FILES = ['.bash_profile', '.bashrc', '.zshrc']; -export const DEFAULT_PROFILE_FILE = '.bash_profile'; +export const ZSHRC_FILE_NAME = '.zshrc'; +export const BASH_PROFILE_FILE_NAME = '.bash_profile'; +export const PROFILE_FILES = [BASH_PROFILE_FILE_NAME, '.bashrc', ZSHRC_FILE_NAME]; // npm export const NPMRC_PATH = path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], '.npmrc'); export const NPM_REGISTRY = 'https://registry.npmjs.org/'; diff --git a/main/ipc/installNode.ts b/main/ipc/installNode.ts index cf76556..0c69d04 100644 --- a/main/ipc/installNode.ts +++ b/main/ipc/installNode.ts @@ -49,7 +49,7 @@ export default () => { if (status === 'done') { killChannelChildProcess(childProcessMap, installChannel); } else if (status === 'success' && result && result.nodePath) { - // nodeEnvPath e.g: /Users/xxx/.nvm/versions/node/v14.15.0/bin/path -> Users/xxx/.nvm/versions/node/v14.15.0/bin + // nodeEnvPath e.g: /Users/xxx/.nvm/versions/node/v14.15.0/bin/node -> Users/xxx/.nvm/versions/node/v14.15.0/bin const nodeEnvPath = result.nodePath.replace('/bin/node', '/bin'); // process.env.PATH: /usr/local/bin -> /Users/xxx/.nvm/versions/node/v14.15.0/bin:/usr/local/bin process.env.PATH = `${nodeEnvPath}${path.delimiter}${process.env.PATH}`; diff --git a/main/node/NvmManager.ts b/main/node/NvmManager.ts index 0e0d87c..fca0a0e 100644 --- a/main/node/NvmManager.ts +++ b/main/node/NvmManager.ts @@ -6,6 +6,7 @@ import log from '../utils/log'; import formatNodeVersion from '../utils/formatNodeVersion'; import { NOT_REINSTALL_DEPENDENCIES } from '../constants'; import getNpmRegistry from '../utils/getNpmRegistry'; +import getShellName from '../utils/getShellName'; class NvmManager implements INodeManager { channel: string; @@ -37,7 +38,7 @@ class NvmManager implements INodeManager { return new Promise((resolve, reject) => { const args: string[] = [shFilePath, formattedVersion]; - const cp = execa('sh', args); + const cp = execa(getShellName(), args); cp.stdout.on('data', this.listenFunc); diff --git a/main/packageInfo/cli/node.ts b/main/packageInfo/cli/node.ts index 18b6326..340d7d0 100644 --- a/main/packageInfo/cli/node.ts +++ b/main/packageInfo/cli/node.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import * as execa from 'execa'; import { INodeVersionManagerInfo } from '../../types'; import log from '../../utils/log'; +import getShellName from '../../utils/getShellName'; import getLocalCliInfo from './cli'; const nodeManagerInfoProcessor = { @@ -41,7 +42,7 @@ async function getNvmInfo(): Promise { }; const shFilePath = path.resolve(__dirname, '../../data/shells', 'is-nvm-installed.sh'); try { - const { stdout } = await execa('sh', [shFilePath]); + const { stdout } = await execa(getShellName(), [shFilePath]); if (stdout === 'nvm') { nvmInfo.managerVersionStatus = 'installed'; } diff --git a/main/packageInstaller/CliInstaller.ts b/main/packageInstaller/CliInstaller.ts index 6d50d15..95de2e8 100644 --- a/main/packageInstaller/CliInstaller.ts +++ b/main/packageInstaller/CliInstaller.ts @@ -4,6 +4,7 @@ import { IPackageInfo, IPackageInstaller } from '../types'; import log from '../utils/log'; import ensureProfileExists from '../utils/ensureProfileExists'; import writeLog from '../utils/writeLog'; +import getShellName from '../utils/getShellName'; class CliInstaller implements IPackageInstaller { channel: string; @@ -53,7 +54,7 @@ class CliInstaller implements IPackageInstaller { process.send({ channel: this.channel, data: { chunk, ln: false } }); }; - const cp = execa('sh', [shPath]); + const cp = execa(getShellName(), [shPath]); cp.stdout.on('data', listenFunc); diff --git a/main/utils/ensureProfileExists.ts b/main/utils/ensureProfileExists.ts index d438835..8d9f956 100644 --- a/main/utils/ensureProfileExists.ts +++ b/main/utils/ensureProfileExists.ts @@ -1,8 +1,9 @@ import * as path from 'path'; import * as fse from 'fs-extra'; import * as shell from 'shelljs'; -import { PROFILE_FILES, DEFAULT_PROFILE_FILE } from '../constants'; +import { PROFILE_FILES, BASH_PROFILE_FILE_NAME, ZSHRC_FILE_NAME } from '../constants'; import log from './log'; +import getShellName from './getShellName'; /** * Ensure profile file exists. Otherwise, create ~/.bash_profile file by default. @@ -12,9 +13,10 @@ function ensureProfileExists() { return fse.pathExistsSync(path.join(process.env.HOME, bashConfigFile)); }); if (!isProfileExists) { - const defaultBashConfigFilePath = path.join(process.env.HOME, DEFAULT_PROFILE_FILE); - log.info(`${PROFILE_FILES.join(',')} were not found. Create ${defaultBashConfigFilePath}.`); - shell.touch(defaultBashConfigFilePath); + const shellName = getShellName(); + const profilePath = path.join(process.env.HOME, shellName === 'zsh' ? ZSHRC_FILE_NAME : BASH_PROFILE_FILE_NAME); + log.info(`${PROFILE_FILES.join(',')} were not found. Create ${profilePath}.`); + shell.touch(profilePath); } } diff --git a/main/utils/getShellName.ts b/main/utils/getShellName.ts new file mode 100644 index 0000000..7305054 --- /dev/null +++ b/main/utils/getShellName.ts @@ -0,0 +1,10 @@ +/** + * get current shell name. e.g. zsh/sh/bash + */ +function getShellName(): string { + const shellPath = process.env.SHELL; + const splitPaths = shellPath.split('/'); + return splitPaths[splitPaths.length - 1]; +} + +export default getShellName; From 722c95fa755b1fd6ad01eee0a19a1ffa0ffd57d9 Mon Sep 17 00:00:00 2001 From: Hengchang Lu <44047106+luhc228@users.noreply.github.com> Date: Tue, 3 Aug 2021 11:09:10 +0800 Subject: [PATCH 5/9] Feat: add global dependencies prefix path (#56) --- main/constants.ts | 8 +- main/data/shells/install-nvm.sh | 2 +- main/git/ssh.ts | 2 +- main/ipc/handleNpmDependency.ts | 28 ++++++ main/ipc/installNode.ts | 3 - main/node/NvmManager.ts | 63 +----------- main/node/index.ts | 31 +++--- .../dependency/createCustomGlobalDepsDir.ts | 88 +++++++++++++++++ main/npm/dependency/globalDependenciesPath.ts | 24 +++++ main/npm/dependency/index.ts | 2 + main/npm/npmInfo.ts | 13 +++ main/npm/registry.ts | 13 +-- package.json | 5 + .../CustomGlobalDepsPathDialog/index.tsx | 95 +++++++++++++++++++ .../components/NodeInstallResult/index.tsx | 19 +--- .../NodeInstaller/index.module.scss | 14 +++ .../Node/components/NodeInstaller/index.tsx | 68 ++++++++----- renderer/src/pages/Node/models/nodeVersion.ts | 2 +- .../src/pages/Node/models/npmDependency.ts | 33 +++++++ 19 files changed, 370 insertions(+), 143 deletions(-) create mode 100644 main/npm/dependency/createCustomGlobalDepsDir.ts create mode 100644 main/npm/dependency/globalDependenciesPath.ts create mode 100644 main/npm/npmInfo.ts create mode 100644 renderer/src/pages/Node/components/CustomGlobalDepsPathDialog/index.tsx diff --git a/main/constants.ts b/main/constants.ts index aba33a1..f1d47ae 100644 --- a/main/constants.ts +++ b/main/constants.ts @@ -1,8 +1,9 @@ import * as path from 'path'; +import * as os from 'os'; import { ILocalPackageInfo } from './types'; export const APPLICATIONS_DIR_PATH = '/Applications'; - +export const HOME_DIR = os.homedir(); export const PACKAGE_JSON_FILE_NAME = 'package.json'; export const TOOLKIT_DIR = path.join(process.env.HOME, '.toolkit'); @@ -26,15 +27,14 @@ export const INSTALL_COMMAND_PACKAGES = [ commandRelativePath: './Contents/Resources/app/bin/code', }, ]; -export const NOT_REINSTALL_DEPENDENCIES = ['npm']; // bash profile export const ZSHRC_FILE_NAME = '.zshrc'; export const BASH_PROFILE_FILE_NAME = '.bash_profile'; export const PROFILE_FILES = [BASH_PROFILE_FILE_NAME, '.bashrc', ZSHRC_FILE_NAME]; // npm -export const NPMRC_PATH = path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], '.npmrc'); +export const NPMRC_PATH = path.join(HOME_DIR, '.npmrc'); export const NPM_REGISTRY = 'https://registry.npmjs.org/'; export const TAOBAO_NPM_REGISTRY = 'https://registry.npm.taobao.org'; export const TAOBAO_NODE_MIRROR = 'https://npm.taobao.org/mirrors/node'; // git -export const GLOBAL_GITCONFIG_PATH = path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], '.gitconfig'); +export const GLOBAL_GITCONFIG_PATH = path.join(HOME_DIR, '.gitconfig'); diff --git a/main/data/shells/install-nvm.sh b/main/data/shells/install-nvm.sh index 051ed60..53d9394 100644 --- a/main/data/shells/install-nvm.sh +++ b/main/data/shells/install-nvm.sh @@ -68,7 +68,7 @@ nvm_source() { elif [ "_$NVM_METHOD" = "_git" ] || [ -z "$NVM_METHOD" ]; then # NVM_SOURCE_URL="https://github.com/${NVM_GITHUB_REPO}.git" # use China mirror - NVM_SOURCE_URL="https://gitee.com/mirrors/nvm.git" + NVM_SOURCE_URL="https://gitee.com/luhengchang/nvm.git" else nvm_echo >&2 "Unexpected value \"$NVM_METHOD\" for \$NVM_METHOD" return 1 diff --git a/main/git/ssh.ts b/main/git/ssh.ts index 81f4f7c..aa44238 100644 --- a/main/git/ssh.ts +++ b/main/git/ssh.ts @@ -4,10 +4,10 @@ import * as fse from 'fs-extra'; import SSHKeyGen = require('ssh-keygen'); import SSHConfig = require('ssh-config'); import log from '../utils/log'; +import { HOME_DIR } from '../constants'; const SSHKeyGenAsync = util.promisify(SSHKeyGen); -const HOME_DIR = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; export const SSHDir = path.join(HOME_DIR, '.ssh'); const SSHConfigPath = path.join(SSHDir, 'config'); diff --git a/main/ipc/handleNpmDependency.ts b/main/ipc/handleNpmDependency.ts index fcd237b..ca241e0 100644 --- a/main/ipc/handleNpmDependency.ts +++ b/main/ipc/handleNpmDependency.ts @@ -1,4 +1,7 @@ +import * as child_process from 'child_process'; +import * as path from 'path'; import { ipcMain, IpcMainInvokeEvent } from 'electron'; +import { send as sendMainWindow } from '../window'; import { getGlobalDependencies, uninstallGlobalDependency, @@ -6,7 +9,11 @@ import { reinstallGlobalDependency, searchNpmDependencies, installGlobalDependency, + getGlobalDependenciesInfo, } from '../npm/dependency'; +import killChannelChildProcess from '../utils/killChannelChildProcess'; + +const childProcessMap = new Map(); export default () => { ipcMain.handle('get-global-npm-dependencies', async (e: IpcMainInvokeEvent, force) => { @@ -33,4 +40,25 @@ export default () => { ipcMain.handle('search-npm-dependencies', async (e: IpcMainInvokeEvent, query: string) => { return await searchNpmDependencies(query); }); + + ipcMain.handle('get-global-dependencies-info', async () => { + return await getGlobalDependenciesInfo(); + }); + + ipcMain.handle('create-custom-global-dependencies-dir', async (e: IpcMainInvokeEvent, channel: string, currentGlobalDepsPath: string) => { + const childProcess = child_process.fork(path.join(__dirname, '..', 'npm/dependency/createCustomGlobalDepsDir')); + childProcessMap.set(channel, childProcess); + + childProcess.send({ currentGlobalDepsPath, channel }); + + childProcessMap.set(channel, childProcess); + + childProcess.on('message', ({ data }: any) => { + sendMainWindow(channel, data); + }); + }); + + ipcMain.handle('cancel-create-custom-global-dependencies-dir', async (e: IpcMainInvokeEvent, channel: string) => { + killChannelChildProcess(childProcessMap, channel); + }); }; diff --git a/main/ipc/installNode.ts b/main/ipc/installNode.ts index 0c69d04..ea9fb13 100644 --- a/main/ipc/installNode.ts +++ b/main/ipc/installNode.ts @@ -15,13 +15,11 @@ export default () => { { managerName, nodeVersion, - reinstallGlobalDeps, installChannel, processChannel, }: { managerName: string; nodeVersion: string; - reinstallGlobalDeps: boolean; installChannel: string; processChannel: string; }, @@ -38,7 +36,6 @@ export default () => { childProcess.send({ managerName, nodeVersion, - reinstallGlobalDeps, installChannel, processChannel, }); diff --git a/main/node/NvmManager.ts b/main/node/NvmManager.ts index fca0a0e..e1f68d8 100644 --- a/main/node/NvmManager.ts +++ b/main/node/NvmManager.ts @@ -1,11 +1,8 @@ import * as path from 'path'; import * as execa from 'execa'; -import * as shell from 'shelljs'; import { INodeManager } from '../types'; import log from '../utils/log'; import formatNodeVersion from '../utils/formatNodeVersion'; -import { NOT_REINSTALL_DEPENDENCIES } from '../constants'; -import getNpmRegistry from '../utils/getNpmRegistry'; import getShellName from '../utils/getShellName'; class NvmManager implements INodeManager { @@ -13,26 +10,15 @@ class NvmManager implements INodeManager { std: string; - previousNpmPath: string; - - globalNpmDependencies: string[]; - nodePath: string; constructor(channel = '') { this.channel = channel; this.std = ''; - this.previousNpmPath = ''; - this.globalNpmDependencies = []; this.nodePath = ''; } - installNode = (version: string, reinstallGlobalDeps = true) => { - if (reinstallGlobalDeps) { - // get the previous npm path - const { stdout } = shell.which('npm'); - this.previousNpmPath = stdout; - } + installNode = (version: string) => { const formattedVersion = formatNodeVersion(version); const shFilePath = path.resolve(__dirname, '../data/shells', 'nvm-install-node.sh'); @@ -59,37 +45,6 @@ class NvmManager implements INodeManager { }); }; - reinstallDependencies = () => { - return this.getGlobalNpmDependencies() - .then(() => getNpmRegistry()) - .then((npmRegistry) => { - return new Promise((resolve, reject) => { - const args = ['i', '-g', ...this.globalNpmDependencies, '--registry', npmRegistry]; - const cp = execa('npm', args, { - // specify execPath to the node path which installed just now - execPath: this.nodePath, - preferLocal: true, - // Don't extend env because it will not use the current(new) npm to install dependencies - extendEnv: false, - }); - - cp.stdout.on('data', this.listenFunc); - - cp.stderr.on('data', this.listenFunc); - - cp.on('error', (buffer: Buffer) => { - this.listenFunc(buffer); - log.error(buffer.toString()); - reject(buffer.toString()); - }); - - cp.on('exit', () => { - resolve(null); - }); - }); - }); - }; - private listenFunc = (buffer: Buffer) => { const chunk = buffer.toString(); this.std += chunk; @@ -111,22 +66,6 @@ class NvmManager implements INodeManager { } return undefined; } - - private async getGlobalNpmDependencies() { - if (!this.previousNpmPath) { - throw new Error('Npm command was not Found.'); - } - const { stdout } = await execa(this.previousNpmPath, ['list', '-g', '--depth=0', '--json']); - if (stdout) { - const { dependencies = {} } = JSON.parse(stdout); - const depNames = Object.keys(dependencies).filter((dep: string) => !NOT_REINSTALL_DEPENDENCIES.includes(dep)) || []; - - depNames.forEach((dep: string) => { - const { version: depVersion } = dependencies[dep]; - this.globalNpmDependencies.push(`${dep}@${depVersion}`); - }); - } - } } export default NvmManager; diff --git a/main/node/index.ts b/main/node/index.ts index e865a4c..ff2eae5 100644 --- a/main/node/index.ts +++ b/main/node/index.ts @@ -14,31 +14,26 @@ export default function getNodeManager(managerName: string, channel?: string) { return nodeManager; } -function processListener({ managerName, nodeVersion, reinstallGlobalDeps, installChannel, processChannel }) { +function processListener({ managerName, nodeVersion, installChannel, processChannel }) { const nodeManager = getNodeManager(managerName, installChannel); - const tasks = ['installNode', 'reinstallDependencies']; + const task = 'installNode'; install(); async function install() { - for (let i = 0; i < tasks.length; i++) { - const task = tasks[i]; - try { - let result; - - if (task === tasks[0] || (task === tasks[1] && reinstallGlobalDeps)) { - process.send({ channel: processChannel, data: { task, status: 'process' } }); - result = await nodeManager[task](nodeVersion, reinstallGlobalDeps); - } - process.send({ channel: processChannel, data: { task, status: 'success', result } }); - } catch (error) { - const errMsg = error instanceof Error ? error.message : error; - log.error(errMsg); - process.send({ channel: processChannel, data: { task, status: 'error', errMsg } }); - } + try { + process.send({ channel: processChannel, data: { task, status: 'process' } }); + + const result = await nodeManager.installNode(nodeVersion); + process.send({ channel: processChannel, data: { task, status: 'success', result } }); + + process.send({ channel: processChannel, data: { status: 'done' } }); + } catch (error) { + const errMsg = error instanceof Error ? error.message : error; + log.error(errMsg); + process.send({ channel: processChannel, data: { task, status: 'error', errMsg } }); } - process.send({ channel: processChannel, data: { status: 'done' } }); } } diff --git a/main/npm/dependency/createCustomGlobalDepsDir.ts b/main/npm/dependency/createCustomGlobalDepsDir.ts new file mode 100644 index 0000000..ebb7b08 --- /dev/null +++ b/main/npm/dependency/createCustomGlobalDepsDir.ts @@ -0,0 +1,88 @@ +import * as path from 'path'; +import * as fse from 'fs-extra'; +import gulp = require('gulp'); +import gulpZip = require('gulp-zip'); +import shellProfile = require('shell-profile'); +import * as AdmZip from 'adm-zip'; +import { getNpmInfo, setNpmInfo } from '../npmInfo'; +import log from '../../utils/log'; +import { GLOBAL_DEPENDENCIES_PATH } from './globalDependenciesPath'; + +async function createCustomGlobalDependenciesDir(channel: string, currentGlobalDepsPath: string) { + if (!path.relative(GLOBAL_DEPENDENCIES_PATH, currentGlobalDepsPath)) { + process.send({ channel, data: { status: 'error', message: `${currentGlobalDepsPath} 已经是推荐的全局依赖路径` } }); + } + const profilePath = shellProfile(); + try { + // 0. copy prefix global dependencies to target + await copyGlobalDependencies(currentGlobalDepsPath, channel); + // 1. write prefix to npm config + await writePrefixToNpmrc(channel); + // 2. write global dependencies path to profile + await writePathToProfile(profilePath, channel); + + process.send({ channel, data: { percent: 100, status: 'done' } }); + } catch (error) { + log.error(error); + process.send({ channel, data: { status: 'error', message: error.message } }); + } +} + +async function copyGlobalDependencies(currentGlobalDepsPath: string, channel: string) { + const binDestPath = path.join(GLOBAL_DEPENDENCIES_PATH, 'bin'); + const libDestPath = path.join(GLOBAL_DEPENDENCIES_PATH, 'lib'); + + return new Promise((resolve, reject) => { + process.send({ channel, data: { percent: 5, message: '压缩全局依赖中...', status: 'process' } }); + // 0. compress dependencies + gulp + .src(path.join(currentGlobalDepsPath, 'lib', '**')) + .pipe(gulpZip('lib.zip')) + .pipe(gulp.dest(GLOBAL_DEPENDENCIES_PATH)) + .on('finish', () => { + resolve(path.join(GLOBAL_DEPENDENCIES_PATH, 'lib.zip')); + }) + .on('error', (err) => { + reject(err); + }); + }) + .then((zipPath: string) => { + // 1. decompress dependencies to the target path + process.send({ channel, data: { percent: 30, message: '解压全局 npm 依赖中...', status: 'process' } }); + const zip = new AdmZip(zipPath); + zip.extractAllTo(libDestPath, true); + return Promise.resolve(); + }) + .then(() => { + return fse.emptyDir(binDestPath); + }) + .then(() => { + // 2. copy symlink + process.send({ channel, data: { percent: 60, message: '复制 bin 目录软链接中...', status: 'process' } }); + return fse.copy(path.join(currentGlobalDepsPath, 'bin'), path.join(GLOBAL_DEPENDENCIES_PATH, 'bin')); + }) + .catch((err) => { + throw err; + }); +} + +async function writePrefixToNpmrc(channel: string) { + process.send({ channel, data: { percent: 80, message: '写入配置信息至 npmrc...', status: 'process' } }); + const npmInfo = await getNpmInfo(); + npmInfo.prefix = GLOBAL_DEPENDENCIES_PATH; + await setNpmInfo(npmInfo); +} + +async function writePathToProfile(profilePath: string, channel: string) { + process.send({ channel, data: { percent: 90, message: `写入配置信息至 ${profilePath}...`, status: 'process' } }); + const profile = await fse.readFile(profilePath, 'utf-8'); + const exportPathStr = `export PATH=${GLOBAL_DEPENDENCIES_PATH}/bin:$PATH`; + const newProfile = `${exportPathStr}\n${profile}`; + await fse.writeFile(profilePath, newProfile); +} + +function processListener({ channel, currentGlobalDepsPath }) { + createCustomGlobalDependenciesDir(channel, currentGlobalDepsPath); +} + +process.on('message', processListener); diff --git a/main/npm/dependency/globalDependenciesPath.ts b/main/npm/dependency/globalDependenciesPath.ts new file mode 100644 index 0000000..c60ffbc --- /dev/null +++ b/main/npm/dependency/globalDependenciesPath.ts @@ -0,0 +1,24 @@ +import * as path from 'path'; +import * as execa from 'execa'; +import { HOME_DIR } from '../../constants'; +import { getNpmInfo } from '../npmInfo'; + +const GLOBAL_DEPENDENCIES_DIR = 'npm_global'; +export const GLOBAL_DEPENDENCIES_PATH = path.join(HOME_DIR, GLOBAL_DEPENDENCIES_DIR); + +export async function getGlobalDependenciesInfo() { + let prefix; + // first get from the .npmrc + const npmInfo = await getNpmInfo(); + prefix = npmInfo.prefix; + if (!npmInfo.prefix) { + // if prefix was not found in .npmrc, run the npm config command + const { stdout } = await execa('npm', ['config', 'get', 'prefix']); + prefix = stdout; + } + return { + recommendedPath: GLOBAL_DEPENDENCIES_PATH, + currentPath: prefix, + exists: prefix === GLOBAL_DEPENDENCIES_PATH, + }; +} diff --git a/main/npm/dependency/index.ts b/main/npm/dependency/index.ts index c520812..683b5dd 100644 --- a/main/npm/dependency/index.ts +++ b/main/npm/dependency/index.ts @@ -4,3 +4,5 @@ export * from './install'; export * from './update'; export * from './reinstall'; export * from './search'; +export * from './globalDependenciesPath'; +export * from './createCustomGlobalDepsDir'; diff --git a/main/npm/npmInfo.ts b/main/npm/npmInfo.ts new file mode 100644 index 0000000..0724e49 --- /dev/null +++ b/main/npm/npmInfo.ts @@ -0,0 +1,13 @@ +import * as ini from 'ini'; +import * as fse from 'fs-extra'; +import { NPMRC_PATH } from '../constants'; + +export async function getNpmInfo() { + const npmrcExists = await fse.pathExists(NPMRC_PATH); + const npmrc = npmrcExists ? ini.parse(fse.readFileSync(NPMRC_PATH, 'utf-8')) : {}; + return npmrc; +} + +export async function setNpmInfo(npmrc: object) { + await fse.writeFile(NPMRC_PATH, ini.stringify(npmrc)); +} diff --git a/main/npm/registry.ts b/main/npm/registry.ts index dbffb32..0381ceb 100644 --- a/main/npm/registry.ts +++ b/main/npm/registry.ts @@ -1,9 +1,8 @@ -import * as ini from 'ini'; -import * as fse from 'fs-extra'; import store, { packagesDataKey } from '../store'; import { INPMRegistry } from '../types'; -import { NPMRC_PATH, NPM_REGISTRY } from '../constants'; +import { NPM_REGISTRY } from '../constants'; import checkIsAliInternal from '../utils/checkIsAliInternal'; +import { getNpmInfo, setNpmInfo } from './npmInfo'; const REGISTRY_FIELD = 'registry'; @@ -15,7 +14,7 @@ export async function getCurrentRegistry() { export async function setCurrentRegistry(registry: string) { const npmrc = await getNpmInfo(); npmrc[REGISTRY_FIELD] = registry; - await fse.writeFile(NPMRC_PATH, ini.stringify(npmrc)); + await setNpmInfo(npmrc); } export async function getAllRegistries() { @@ -26,9 +25,3 @@ export async function getAllRegistries() { return isAliInternal ? true : !npmRegistry.isInternal; }); } - -async function getNpmInfo() { - const npmrcExists = await fse.pathExists(NPMRC_PATH); - const npmrc = npmrcExists ? ini.parse(fse.readFileSync(NPMRC_PATH, 'utf-8')) : {}; - return npmrc; -} diff --git a/package.json b/package.json index 50ae991..407c402 100644 --- a/package.json +++ b/package.json @@ -92,11 +92,14 @@ "execa": "^5.0.0", "fs-extra": "^9.1.0", "globby": "^11.0.3", + "gulp": "^4.0.2", + "gulp-zip": "^5.1.0", "ice-npm-utils": "^2.1.1", "ini": "^2.0.0", "node-cache": "^5.1.2", "node-fetch": "^2.6.1", "package-json": "^7.0.0", + "shell-profile": "^1.0.3", "shelljs": "^0.8.4", "ssh-config": "^4.0.6", "ssh-keygen": "^0.5.0", @@ -108,6 +111,8 @@ "@commitlint/cli": "^12.1.1", "@iceworks/spec": "^1.0.0", "@types/decompress": "^4.2.3", + "@types/gulp": "^4.0.9", + "@types/gulp-zip": "^4.0.2", "@types/ini": "^1.3.30", "@types/node-fetch": "^2.5.10", "@types/shelljs": "^0.8.8", diff --git a/renderer/src/pages/Node/components/CustomGlobalDepsPathDialog/index.tsx b/renderer/src/pages/Node/components/CustomGlobalDepsPathDialog/index.tsx new file mode 100644 index 0000000..b3e21a8 --- /dev/null +++ b/renderer/src/pages/Node/components/CustomGlobalDepsPathDialog/index.tsx @@ -0,0 +1,95 @@ +import { ipcRenderer, IpcRendererEvent } from 'electron'; +import { Dialog, Form, Input, Progress } from '@alifd/next'; +import { useEffect } from 'react'; +import store from '../../store'; + +const CustomGlobalDepsDialog = () => { + const formItemLayout = { + labelCol: { span: 6 }, + wrapperCol: { span: 18 }, + }; + const [state, dispatcher] = store.useModel('npmDependency'); + const { globalDependenciesInfo: { currentPath, recommendedPath } } = state; + const { customGlobalDepsDialogVisible, customGlobalDepsProcess } = state; + + const channel = 'create-custom-global-deps-dir'; + + const onOk = async () => { + await dispatcher.createCustomGlobalDepsDir({ channel, currentGlobalDepsPath: currentPath }); + }; + + const onCancel = async () => { + await ipcRenderer.invoke( + 'cancel-create-custom-global-dependencies-dir', + channel, + ); + onClose(); + }; + + const onClose = () => { + dispatcher.setCustomGlobalDepsDialogVisible(false); + dispatcher.getGlobalDependenciesInfo(); + dispatcher.initCustomGlobalDepsProcess(); + }; + + useEffect(() => { + function handleUpdateStatus(e: IpcRendererEvent, data) { + dispatcher.setCustomGlobalDepsProcess(data); + if (data.status === 'done') { + onClose(); + } + } + ipcRenderer.on(channel, handleUpdateStatus); + return () => { + ipcRenderer.removeListener(channel, handleUpdateStatus); + }; + }, []); + + const value = { + currentPath, + recommendedPath, + }; + return ( + +
+ + + + + + +
+ {customGlobalDepsProcess.status && ( + <> + +
+ {customGlobalDepsProcess.message} +
+ + )} +
+ ); +}; + +export default CustomGlobalDepsDialog; diff --git a/renderer/src/pages/Node/components/NodeInstallResult/index.tsx b/renderer/src/pages/Node/components/NodeInstallResult/index.tsx index 049259b..7f20c12 100644 --- a/renderer/src/pages/Node/components/NodeInstallResult/index.tsx +++ b/renderer/src/pages/Node/components/NodeInstallResult/index.tsx @@ -4,7 +4,7 @@ import styles from './index.module.scss'; const { Group: TagGroup } = Tag; -const InstallResult = ({ goBack, reinstallGlobalDeps }) => { +const InstallResult = ({ goBack }) => { const [state] = store.useModel('nodeVersion'); const { nodeInstallStatus, installResult, nodeInstallErrMsg } = state; @@ -15,7 +15,6 @@ const InstallResult = ({ goBack, reinstallGlobalDeps }) => { 安装结果 - {/* Node.js install result */} {nodeInstallStatus.installNode === 'success' ? successTag : errorTag}} @@ -43,22 +42,6 @@ const InstallResult = ({ goBack, reinstallGlobalDeps }) => { )} - {/* npm package install result */} - {reinstallGlobalDeps && ( - {nodeInstallStatus.reinstallDependencies === 'success' ? successTag : errorTag}} - > - {nodeInstallStatus.reinstallDependencies === 'error' && ( - <> -
重装全局依赖失败,请自行安装依赖。详细日志如下:
- - {nodeInstallErrMsg.reinstallDependencies} - - - )} -
- )}
+ + ); let mainbody: JSX.Element; @@ -131,20 +143,16 @@ const NodeInstaller: FC = ({ goBack }) => { } - - 重装全局依赖 - } closable={false}> - 安装一个新版本的 Node.js 后,原来全局 npm 包可能会不可用。 - 选择此选项会自动把原来的 npm 包适配到新版本的 Node.js 中。 - - } - required - requiredMessage="请选择是否重装全局依赖" - > - - + {!globalDependenciesInfo.exists && ( + + + + )} 下一步 @@ -154,15 +162,14 @@ const NodeInstaller: FC = ({ goBack }) => { ); break; case 1: - case 2: mainbody = (
); break; - case 3: - mainbody = ; + case 2: + mainbody = ; break; default: break; @@ -175,9 +182,8 @@ const NodeInstaller: FC = ({ goBack }) => { aria-current={index === currentStep ? 'step' : null} key={item.name} title={item.title} - disabled={index === 2 && !nodeInstallFormValue.reinstallGlobalDeps} icon={ - ((index === 1 || index === 2) && currentStep === index) ? STEP_STATUS_ICON[nodeInstallStatus[item.name]] : undefined + (index === 1 && currentStep === index) ? STEP_STATUS_ICON[nodeInstallStatus[item.name]] : undefined } /> ), @@ -215,6 +221,16 @@ const NodeInstaller: FC = ({ goBack }) => { goNext(); } + useEffect(() => { + npmDependencyDispatchers.getGlobalDependenciesInfo(); + }, []); + + useEffect(() => { + if (npmDependencyEffectsState.getGlobalDependenciesInfo.error) { + Message.error(npmDependencyEffectsState.getGlobalDependenciesInfo.error.message); + } + }, [npmDependencyEffectsState.getGlobalDependenciesInfo.error]); + useEffect(() => { ipcRenderer.on(nodeInstallProcessStatusChannel, handleUpdateInstallStatus); return () => { @@ -224,6 +240,7 @@ const NodeInstaller: FC = ({ goBack }) => { ); }; }, []); + return (
@@ -232,6 +249,7 @@ const NodeInstaller: FC = ({ goBack }) => { {mainbody} +
); }; diff --git a/renderer/src/pages/Node/models/nodeVersion.ts b/renderer/src/pages/Node/models/nodeVersion.ts index 35b417b..97c03d1 100644 --- a/renderer/src/pages/Node/models/nodeVersion.ts +++ b/renderer/src/pages/Node/models/nodeVersion.ts @@ -2,7 +2,7 @@ import { IPackageInfo, INodeVersions } from '@/interfaces'; import { ipcRenderer } from 'electron'; const DEFAULT_INSTALL_RESULT = { nodeVersion: '', npmVersion: '' }; -const DEFAULT_NODE_INSTALL_FORM_VALUE = { reinstallGlobalDeps: true }; +const DEFAULT_NODE_INSTALL_FORM_VALUE = {}; const DEFAULT_NODE_INSTALL_STATUS = { installNode: 'wait', reinstallPackages: 'wait', diff --git a/renderer/src/pages/Node/models/npmDependency.ts b/renderer/src/pages/Node/models/npmDependency.ts index 1adcd94..f647a4d 100644 --- a/renderer/src/pages/Node/models/npmDependency.ts +++ b/renderer/src/pages/Node/models/npmDependency.ts @@ -7,6 +7,12 @@ const curDepIndexMap = { uninstall: 'curUninstallDepIndex', }; +const defaultProcess = { + percent: 0, + message: '', + status: '', +}; + export default { state: { npmDependencies: [], @@ -16,6 +22,13 @@ export default { curUninstallDepIndex: [], searchValue: '', queryNpmDependencies: [], + globalDependenciesInfo: { + recommendedPath: '', + currentPath: '', + exists: false, + }, + customGlobalDepsDialogVisible: false, + customGlobalDepsProcess: defaultProcess, }, reducers: { addCurDepIndex(prevState, { type, index }: { type: string; index: number }) { @@ -28,6 +41,15 @@ export default { updateSearchValue(prevState, searchValue: string) { prevState.searchValue = searchValue; }, + setCustomGlobalDepsDialogVisible(prevState, visible: boolean) { + prevState.customGlobalDepsDialogVisible = visible; + }, + setCustomGlobalDepsProcess(prevState, data) { + prevState.customGlobalDepsProcess = data; + }, + initCustomGlobalDepsProcess(prevState) { + prevState.customGlobalDepsProcess = defaultProcess; + }, }, effects: () => ({ async getGlobalNpmDependencies(force = false) { @@ -55,5 +77,16 @@ export default { const queryNpmDependencies = await ipcRenderer.invoke('search-npm-dependencies', query); this.setState({ queryNpmDependencies }); }, + + async getGlobalDependenciesInfo() { + const globalDependenciesInfo = await ipcRenderer.invoke('get-global-dependencies-info'); + this.setState({ + globalDependenciesInfo, + }); + }, + + async createCustomGlobalDepsDir({ channel, currentGlobalDepsPath }: { channel: string; currentGlobalDepsPath: string }) { + await ipcRenderer.invoke('create-custom-global-dependencies-dir', channel, currentGlobalDepsPath); + }, }), }; From 23aa183cd11b940ac1ef5902fcdbaed6a093e5cf Mon Sep 17 00:00:00 2001 From: Hengchang Lu <44047106+luhc228@users.noreply.github.com> Date: Tue, 3 Aug 2021 13:21:33 +0800 Subject: [PATCH 6/9] Feat: add recommend icon to registry (#55) --- main/data/data.json | 8 +--- main/ipc/checkIsAliInternal.ts | 8 ++++ main/ipc/index.ts | 3 ++ main/npm/dependency/install.ts | 4 +- main/npm/dependency/search.ts | 8 ++-- main/npm/dependency/update.ts | 4 +- main/npm/registry.ts | 6 ++- main/types/index.ts | 1 + main/utils/getNpmRegistry.ts | 4 +- .../src/components/BalloonConfirm/index.tsx | 6 +-- renderer/src/interfaces/index.ts | 1 + .../components/InstallNpmDependency/index.tsx | 18 ++++++++- .../NpmDependency/index.module.scss | 2 +- .../components/NpmRegistry/index.module.scss | 15 +++++++ .../Node/components/NpmRegistry/index.tsx | 39 +++++++++++++++++-- renderer/src/pages/Node/models/npmRegistry.ts | 8 +++- 16 files changed, 110 insertions(+), 25 deletions(-) create mode 100644 main/ipc/checkIsAliInternal.ts diff --git a/main/data/data.json b/main/data/data.json index c22e3bc..8f6b250 100644 --- a/main/data/data.json +++ b/main/data/data.json @@ -122,12 +122,8 @@ { "name": "taobao", "registry": "https://registry.npm.taobao.org/", - "isInternal": false - }, - { - "name": "tnpm", - "registry": " https://registry.npm.alibaba-inc.com/", - "isInternal": true + "isInternal": false, + "recommended": true } ], "browserExtensions": [ diff --git a/main/ipc/checkIsAliInternal.ts b/main/ipc/checkIsAliInternal.ts new file mode 100644 index 0000000..4cc244e --- /dev/null +++ b/main/ipc/checkIsAliInternal.ts @@ -0,0 +1,8 @@ +import { ipcMain } from 'electron'; +import checkIsAliInternal from '../utils/checkIsAliInternal'; + +export default () => { + ipcMain.handle('check-is-ali-internal', async () => { + return await checkIsAliInternal(); + }); +}; diff --git a/main/ipc/index.ts b/main/ipc/index.ts index 15f7632..d1ac685 100644 --- a/main/ipc/index.ts +++ b/main/ipc/index.ts @@ -6,6 +6,7 @@ import handleGitConfig from './handleGitConfig'; import getFolderPath from './getFolderPath'; import handleNpmRegistry from './handleNpmRegistry'; import handleNpmDependency from './handleNpmDependency'; +import checkIsAliInternal from './checkIsAliInternal'; export default () => { getBasePackagesInfo(); @@ -23,4 +24,6 @@ export default () => { handleNpmRegistry(); handleNpmDependency(); + + checkIsAliInternal(); }; diff --git a/main/npm/dependency/install.ts b/main/npm/dependency/install.ts index b495dff..b911459 100644 --- a/main/npm/dependency/install.ts +++ b/main/npm/dependency/install.ts @@ -1,5 +1,6 @@ import * as execa from 'execa'; import log from '../../utils/log'; +import getNpmRegistry from '../../utils/getNpmRegistry'; export async function installGlobalDependency(dependency: string, version: string) { if (!dependency) { @@ -8,8 +9,9 @@ export async function installGlobalDependency(dependency: string, version: strin throw new Error(errMsg); } + const npmRegistry = await getNpmRegistry(); try { - const command = `npm install ${dependency}@${version || 'latest'} -g`; + const command = `npm install ${dependency}@${version || 'latest'} -g --registry=${npmRegistry}`; log.info('Command: ', command); await execa.command(command); log.info(`Install ${dependency} successfully.`); diff --git a/main/npm/dependency/search.ts b/main/npm/dependency/search.ts index e6be3be..09ba1be 100644 --- a/main/npm/dependency/search.ts +++ b/main/npm/dependency/search.ts @@ -1,18 +1,18 @@ import fetch from 'node-fetch'; import urljoin = require('url-join'); import log from '../../utils/log'; -import { getCurrentRegistry } from '../registry'; +import getNpmRegistry from '../../utils/getNpmRegistry'; import { getGlobalDependencies } from './getInfo'; export async function searchNpmDependencies(query: string) { if (!query) { - const errorMsg = 'The search content is empty. Please provide it.'; + const errorMsg = '请输入 npm 依赖名称'; log.error(errorMsg); throw new Error(errorMsg); } try { - const currentRegistry: string = await getCurrentRegistry(); - const url = urljoin(currentRegistry, query); + const npmRegistry: string = await getNpmRegistry(); + const url = urljoin(npmRegistry, query); const res = await fetch(url); const content = await res.json(); diff --git a/main/npm/dependency/update.ts b/main/npm/dependency/update.ts index 1f30dd4..d4f0e14 100644 --- a/main/npm/dependency/update.ts +++ b/main/npm/dependency/update.ts @@ -1,5 +1,6 @@ import * as execa from 'execa'; import log from '../../utils/log'; +import getNpmRegistry from '../../utils/getNpmRegistry'; export async function updateGlobalDependency(dependency: string) { if (!dependency) { @@ -8,7 +9,8 @@ export async function updateGlobalDependency(dependency: string) { throw new Error(errMsg); } try { - const command = `npm update -g ${dependency}`; + const npmRegistry = await getNpmRegistry(); + const command = `npm update -g ${dependency} --registry=${npmRegistry}`; log.info('Command: ', command); await execa.command(command); log.info(`Update ${dependency} successfully.`); diff --git a/main/npm/registry.ts b/main/npm/registry.ts index 0381ceb..3fb3f16 100644 --- a/main/npm/registry.ts +++ b/main/npm/registry.ts @@ -20,8 +20,10 @@ export async function setCurrentRegistry(registry: string) { export async function getAllRegistries() { const isAliInternal = await checkIsAliInternal(); const data = store.get(packagesDataKey); - const { npmRegistries = [] }: { npmRegistries: INPMRegistry[] } = data; - return npmRegistries.filter((npmRegistry) => { + const { npmRegistries: originNpmRegistries = [] }: { npmRegistries: INPMRegistry[] } = data; + const npmRegistries = originNpmRegistries.filter((npmRegistry) => { return isAliInternal ? true : !npmRegistry.isInternal; }); + + return npmRegistries; } diff --git a/main/types/index.ts b/main/types/index.ts index d55cfea..e78e6c6 100644 --- a/main/types/index.ts +++ b/main/types/index.ts @@ -59,6 +59,7 @@ export interface INodeVersions { export interface INPMRegistry { name: string; registry: string; + recommended?: boolean; isInternal: boolean; } diff --git a/main/utils/getNpmRegistry.ts b/main/utils/getNpmRegistry.ts index b4bc479..0314352 100644 --- a/main/utils/getNpmRegistry.ts +++ b/main/utils/getNpmRegistry.ts @@ -1,10 +1,10 @@ import { ALI_NPM_REGISTRY } from '@appworks/constant'; -import { TAOBAO_NPM_REGISTRY } from '../constants'; +import { getCurrentRegistry } from '../npm/registry'; import checkIsAliInternal from './checkIsAliInternal'; async function getNpmRegistry() { const isAliInternal = await checkIsAliInternal(); - return isAliInternal ? ALI_NPM_REGISTRY : TAOBAO_NPM_REGISTRY; + return isAliInternal ? ALI_NPM_REGISTRY : await getCurrentRegistry(); } export default getNpmRegistry; diff --git a/renderer/src/components/BalloonConfirm/index.tsx b/renderer/src/components/BalloonConfirm/index.tsx index e9307ec..2ec75ff 100644 --- a/renderer/src/components/BalloonConfirm/index.tsx +++ b/renderer/src/components/BalloonConfirm/index.tsx @@ -1,8 +1,8 @@ import { FC, useState } from 'react'; -import { Button, Balloon } from '@alifd/next'; +import { Button, Balloon, Icon } from '@alifd/next'; interface IBallonConfirm { - title: string; + title: string | React.ReactNode; onConfirm?: any; onCancel?: any; style?: { [k: string]: string }; @@ -29,7 +29,7 @@ const BallonConfirm: FC = ({ return ( {children}} style={style} visible={disable ? false : visible}> -
{title}
+
{title}
- } + } align="t" > 安装 @@ -63,6 +68,17 @@ function InstallNpmDependency() { ); }; + useEffect(() => { + if (effectsState.searchNpmDependencies.error) { + Message.error(effectsState.searchNpmDependencies.error.message); + } + }, [effectsState.searchNpmDependencies.error]); + + useEffect(() => { + if (effectsState.installGlobalNpmDependency.error) { + Message.error(effectsState.installGlobalNpmDependency.error.message); + } + }, [effectsState.installGlobalNpmDependency.error]); return (
{ + dispatchers.checkIsAliInternal(); + }, []); + useEffect(() => { dispatchers.checkNpmInstalled(); }, []); @@ -50,10 +55,37 @@ function NpmRegistry() { Message.error(effectsErrors.setCurrentNpmRegistry.error.message); } }, [effectsErrors.setCurrentNpmRegistry.error]); + + const selectItemRender = (item) => { + return ( +
+ {item.label} + <> + {item.recommended && + recommendIcon + } + +
+ ); + }; + + if (isAliInternal) { + return null; + } + return ( -
npm 镜像源
+
+ 全局 npm 镜像源 + } + align="t" + delay={200} + > + 如果有发 npm 包的需求,请先切换至 npm 官方镜像源。 + +