Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(cli): 重构 taro init 选择模板源逻辑 #7269

Merged
merged 5 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -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
224 changes: 155 additions & 69 deletions packages/taro-cli/src/create/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -63,89 +68,45 @@ 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<ITemplates[]> {
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)
this.askDescription(conf, prompts)
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) {
Expand Down Expand Up @@ -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 = [
{
Expand All @@ -284,8 +318,60 @@ export default class Project extends Creator {
}
}

async fetchTemplates (answers): Promise<ITemplates[]> {
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])
}
})
})
}
1 change: 1 addition & 0 deletions packages/taro-helper/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
5 changes: 3 additions & 2 deletions packages/taro-helper/types/constants.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
1 change: 1 addition & 0 deletions packages/taro-helper/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down