diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3f09d971a52f..7a6e9a579e6a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: 新建一个新的 Issue - url: https://nervjs.github.io/taro-issue-helper/ + url: https://tls.jd.com/taro-issue-helper about: 请点击 「Open」 按钮打开新链接创建 Issue diff --git a/packages/taro-cli/src/create/project.ts b/packages/taro-cli/src/create/project.ts index 7c8580545168..7b370a0e5b86 100644 --- a/packages/taro-cli/src/create/project.ts +++ b/packages/taro-cli/src/create/project.ts @@ -2,8 +2,11 @@ import * as path from 'path' import * as fs from 'fs-extra' import * as inquirer from 'inquirer' import * as semver from 'semver' +import * as request from 'request' +import * as ora from 'ora' import { DEFAULT_TEMPLATE_SRC, + DEFAULT_TEMPLATE_SRC_GITEE, TARO_CONFIG_FLODER, TARO_BASE_CONFIG, getUserHomeDir, @@ -39,6 +42,8 @@ interface AskMethods { (conf: IProjectConf, prompts: object[], choices?: ITemplates[]): void; } +const NONE_AVALIABLE_TEMPLATE = '无可用模板' + export default class Project extends Creator { public rootPath: string public conf: IProjectConf @@ -63,60 +68,25 @@ export default class Project extends Creator { } init () { - console.log(chalk.green('Taro即将创建一个新项目!')) - console.log('Need help? Go and open issue: https://github.com/NervJS/taro/issues/new') + console.log(chalk.green('Taro 即将创建一个新项目!')) + console.log(`Need help? Go and open issue: ${chalk.blueBright('https://tls.jd.com/taro-issue-helper')}`) console.log() } - create () { - this.fetchTemplates() - .then((templateChoices: ITemplates[]) => { - return this.ask(templateChoices) - }) - .then(answers => { - const date = new Date() - this.conf = Object.assign(this.conf, answers) - this.conf.date = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}` - this.write() - }) - .catch(err => console.log(chalk.red('创建项目失败: ', err))) - } - - async fetchTemplates (): Promise { - const conf = this.conf - // 使用默认模版 - if (conf.template && conf.template === 'default') { - return Promise.resolve([]) + async create () { + try { + const answers = await this.ask() + const date = new Date() + this.conf = Object.assign(this.conf, answers) + this.conf.date = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}` + this.write() + } catch (error) { + console.log(chalk.red('创建项目失败: ', error)) } - - // 处理模版源取值 - if (!conf.templateSource) { - const homedir = getUserHomeDir() - if (!homedir) { - chalk.yellow('找不到用户根目录,使用默认模版源!') - conf.templateSource = DEFAULT_TEMPLATE_SRC - } - - const taroConfigPath = path.join(homedir, TARO_CONFIG_FLODER) - const taroConfig = path.join(taroConfigPath, TARO_BASE_CONFIG) - - if (fs.existsSync(taroConfig)) { - const config = await fs.readJSON(taroConfig) - conf.templateSource = config && config.templateSource ? config.templateSource : DEFAULT_TEMPLATE_SRC - } else { - await fs.createFile(taroConfig) - await fs.writeJSON(taroConfig, { templateSource: DEFAULT_TEMPLATE_SRC }) - conf.templateSource = DEFAULT_TEMPLATE_SRC - } - } - - // 从模板源下载模板 - return fetchTemplate(this.conf.templateSource, this.templatePath(''), this.conf.clone) } - ask (templateChoices: ITemplates[]) { - const prompts: object[] = [] - const templateChoicesPrompts: object[] = [] + async ask () { + let prompts: object[] = [] const conf = this.conf this.askProjectName(conf, prompts) @@ -124,28 +94,19 @@ export default class Project extends Creator { this.askFramework(conf, prompts) this.askTypescript(conf, prompts) this.askCSS(conf, prompts) + await this.askTemplateSource(conf, prompts) - return inquirer.prompt(prompts).then(answers => { - const newTemplateChoices: ITemplates[] = templateChoices - .filter(templateChoice => { - const { platforms } = templateChoice - if (typeof platforms === 'string' && platforms) { - return answers.framework === templateChoice.platforms - } else if (isArray(platforms)) { - return templateChoice.platforms?.includes(answers.framework) - } else { - return true - } - }) - this.askTemplate(conf, templateChoicesPrompts, newTemplateChoices) - return inquirer.prompt(templateChoicesPrompts) - .then(templateChoiceAnswer => { - return { - ...answers, - ...templateChoiceAnswer - } - }) - }) + const answers = await inquirer.prompt(prompts) + + prompts = [] + const templates = await this.fetchTemplates(answers) + await this.askTemplate(conf, prompts, templates) + const templateChoiceAnswer = await inquirer.prompt(prompts) + + return { + ...answers, + ...templateChoiceAnswer + } } askProjectName: AskMethods = function (conf, prompts) { @@ -262,6 +223,79 @@ export default class Project extends Creator { } } + askTemplateSource: AskMethods = async function (conf, prompts) { + if (conf.template === 'default' || conf.templateSource) return + + const homedir = getUserHomeDir() + const taroConfigPath = path.join(homedir, TARO_CONFIG_FLODER) + const taroConfig = path.join(taroConfigPath, TARO_BASE_CONFIG) + + let localTemplateSource: string + + // 检查本地配置 + if (fs.existsSync(taroConfig)) { + // 存在则把模板源读出来 + const config = await fs.readJSON(taroConfig) + localTemplateSource = config?.templateSource + } else { + // 不存在则创建配置 + await fs.createFile(taroConfig) + await fs.writeJSON(taroConfig, { templateSource: DEFAULT_TEMPLATE_SRC }) + localTemplateSource = DEFAULT_TEMPLATE_SRC + } + + const choices = [ + { + name: 'Gitee(最快)', + value: DEFAULT_TEMPLATE_SRC_GITEE + }, + { + name: 'Github(最新)', + value: DEFAULT_TEMPLATE_SRC + }, + { + name: '输入', + value: 'self-input' + }, + { + name: '社区优质模板源', + value: 'open-source' + } + ] + + if (localTemplateSource && localTemplateSource !== DEFAULT_TEMPLATE_SRC && localTemplateSource !== DEFAULT_TEMPLATE_SRC_GITEE) { + choices.unshift({ + name: `本地模板源:${localTemplateSource}`, + value: localTemplateSource + }) + } + + prompts.push({ + type: 'list', + name: 'templateSource', + message: '请选择模板源', + choices + }, { + type: 'input', + name: 'templateSource', + message: '请输入模板源!', + when (answers) { + return answers.templateSource === 'self-input' + } + }, { + type: 'list', + name: 'templateSource', + message: '请选择社区模板源', + async choices (answers) { + const choices = await getOpenSourceTemplates(answers.framework) + return choices + }, + when (answers) { + return answers.templateSource === 'open-source' + } + }) + } + askTemplate: AskMethods = function (conf, prompts, list = []) { const choices = [ { @@ -284,8 +318,60 @@ export default class Project extends Creator { } } + async fetchTemplates (answers): Promise { + const { templateSource, framework } = answers + this.conf.templateSource = this.conf.templateSource || templateSource + + // 使用默认模版 + if (this.conf?.template === 'default' || answers.templateSource === NONE_AVALIABLE_TEMPLATE) return Promise.resolve([]) + + // 从模板源下载模板 + const isClone = /gitee/.test(this.conf.templateSource) || this.conf.clone + const templateChoices = await fetchTemplate(this.conf.templateSource, this.templatePath(''), isClone) + + // 根据用户选择的框架筛选模板 + const newTemplateChoices: ITemplates[] = templateChoices + .filter(templateChoice => { + const { platforms } = templateChoice + if (typeof platforms === 'string' && platforms) { + return framework === templateChoice.platforms + } else if (isArray(platforms)) { + return templateChoice.platforms?.includes(framework) + } else { + return true + } + }) + + return newTemplateChoices + } + write (cb?: () => void) { this.conf.src = SOURCE_DIR createApp(this, this.conf, cb).catch(err => console.log(err)) } } + +function getOpenSourceTemplates (platform) { + return new Promise((resolve, reject) => { + const spinner = ora('正在拉取开源模板列表...').start() + request.get('https://gitee.com/NervJS/awesome-taro/raw/next/index.json', (error, _response, body) => { + if (error) { + spinner.fail(chalk.red('拉取开源模板列表失败!')) + return reject(new Error()) + } + + spinner.succeed(`${chalk.grey('拉取开源模板列表成功!')}`) + + const collection = JSON.parse(body) + + switch (platform) { + case 'react': + return resolve(collection.react) + case 'vue': + return resolve(collection.vue) + default: + return resolve([NONE_AVALIABLE_TEMPLATE]) + } + }) + }) +} diff --git a/packages/taro-helper/src/constants.ts b/packages/taro-helper/src/constants.ts index 3117a316afe0..c2d36f892e73 100644 --- a/packages/taro-helper/src/constants.ts +++ b/packages/taro-helper/src/constants.ts @@ -175,6 +175,7 @@ export const DEVICE_RATIO_NAME = 'deviceRatio' export const isWindows = os.platform() === 'win32' export const DEFAULT_TEMPLATE_SRC = 'github:NervJS/taro-project-templates#v3' +export const DEFAULT_TEMPLATE_SRC_GITEE = 'direct:https://gitee.com/o2team/taro-project-templates.git#v3' export const TARO_CONFIG_FLODER = '.taro3' export const TARO_BASE_CONFIG = 'index.json' diff --git a/packages/taro-helper/types/constants.d.ts b/packages/taro-helper/types/constants.d.ts index 2341f5072d08..2c09fbf8c555 100644 --- a/packages/taro-helper/types/constants.d.ts +++ b/packages/taro-helper/types/constants.d.ts @@ -73,8 +73,9 @@ export declare const taroJsMobx = "@tarojs/mobx"; export declare const taroJsMobxCommon = "@tarojs/mobx-common"; export declare const DEVICE_RATIO_NAME = "deviceRatio"; export declare const isWindows: boolean; -export declare const DEFAULT_TEMPLATE_SRC = "github:NervJS/taro-project-templates#2.0"; -export declare const TARO_CONFIG_FLODER = ".taro2"; +export declare const DEFAULT_TEMPLATE_SRC = "github:NervJS/taro-project-templates#v3"; +export declare const DEFAULT_TEMPLATE_SRC_GITEE = "direct:https://gitee.com/o2team/taro-project-templates.git#v3"; +export declare const TARO_CONFIG_FLODER = ".taro3"; export declare const TARO_BASE_CONFIG = "index.json"; export declare const OUTPUT_DIR = "dist"; export declare const SOURCE_DIR = "src"; diff --git a/packages/taro-helper/types/index.d.ts b/packages/taro-helper/types/index.d.ts index c5ff175adca6..a068055643fa 100644 --- a/packages/taro-helper/types/index.d.ts +++ b/packages/taro-helper/types/index.d.ts @@ -132,6 +132,7 @@ declare interface helper { DEVICE_RATIO_NAME: "deviceRatio"; isWindows: boolean; DEFAULT_TEMPLATE_SRC: "github:NervJS/taro-project-templates#v3"; + DEFAULT_TEMPLATE_SRC_GITEE: "direct:https://gitee.com/o2team/taro-project-templates.git#v3"; TARO_CONFIG_FLODER: ".taro3"; TARO_BASE_CONFIG: "index.json"; OUTPUT_DIR: "dist";