UNPKG

8.88 kBPlain TextView Raw
1import { dotenvParse, fs, patchEnv } from '@tarojs/helper'
2import { Config, Kernel } from '@tarojs/service'
3import * as minimist from 'minimist'
4import * as path from 'path'
5
6import customCommand from './commands/customCommand'
7import { getPkgVersion } from './util'
8
9const DISABLE_GLOBAL_CONFIG_COMMANDS = ['build', 'global-config', 'doctor', 'update', 'config']
10const DEFAULT_FRAMEWORK = 'react'
11
12export default class CLI {
13 appPath: string
14 constructor (appPath) {
15 this.appPath = appPath || process.cwd()
16 }
17
18 run () {
19 return this.parseArgs()
20 }
21
22 async parseArgs () {
23 const args = minimist(process.argv.slice(2), {
24 alias: {
25 version: ['v'],
26 help: ['h'],
27 port: ['p'],
28 resetCache: ['reset-cache'], // specially for rn, Removes cached files.
29 publicPath: ['public-path'], // specially for rn, assets public path.
30 bundleOutput: ['bundle-output'], // specially for rn, File name where to store the resulting bundle.
31 sourcemapOutput: ['sourcemap-output'], // specially for rn, File name where to store the sourcemap file for resulting bundle.
32 sourceMapUrl: ['sourcemap-use-absolute-path'], // specially for rn, Report SourceMapURL using its full path.
33 sourcemapSourcesRoot: ['sourcemap-sources-root'], // specially for rn, Path to make sourcemaps sources entries relative to.
34 assetsDest: ['assets-dest'], // specially for rn, Directory name where to store assets referenced in the bundle.
35 envPrefix: ['env-prefix'],
36 },
37 boolean: ['version', 'help', 'disable-global-config'],
38 default: {
39 build: true,
40 },
41 })
42 const _ = args._
43 const command = _[0]
44 if (command) {
45 const appPath = this.appPath
46 const presetsPath = path.resolve(__dirname, 'presets')
47 const commandsPath = path.resolve(presetsPath, 'commands')
48 const platformsPath = path.resolve(presetsPath, 'platforms')
49 const commandPlugins = fs.readdirSync(commandsPath)
50 const targetPlugin = `${command}.js`
51
52 // 设置环境变量
53 process.env.NODE_ENV ||= args.env
54 if (process.env.NODE_ENV === 'undefined' && (command === 'build' || command === 'inspect')) {
55 process.env.NODE_ENV = (args.watch ? 'development' : 'production')
56 }
57 args.type ||= args.t
58 if (args.type) {
59 process.env.TARO_ENV = args.type
60 }
61 if (typeof args.plugin === 'string') {
62 process.env.TARO_ENV = 'plugin'
63 }
64 const mode = args.mode || process.env.NODE_ENV
65 // 这里解析 dotenv 以便于 config 解析时能获取 dotenv 配置信息
66 const expandEnv = dotenvParse(appPath, args.envPrefix, mode)
67
68 const disableGlobalConfig = !!(args['disable-global-config'] || DISABLE_GLOBAL_CONFIG_COMMANDS.includes(command))
69
70 const configEnv = {
71 mode,
72 command,
73 }
74 const config = new Config({
75 appPath: this.appPath,
76 disableGlobalConfig: disableGlobalConfig
77 })
78 await config.init(configEnv)
79
80 const kernel = new Kernel({
81 appPath,
82 presets: [
83 path.resolve(__dirname, '.', 'presets', 'index.js')
84 ],
85 config,
86 plugins: []
87 })
88 kernel.optsPlugins ||= []
89
90 // 将自定义的 变量 添加到 config.env 中,实现 definePlugin 字段定义
91 const initialConfig = kernel.config?.initialConfig
92 if (initialConfig) {
93 initialConfig.env = patchEnv(initialConfig, expandEnv)
94 }
95 if (command === 'doctor') {
96 kernel.optsPlugins.push('@tarojs/plugin-doctor')
97 } else if (commandPlugins.includes(targetPlugin)) {
98 // 针对不同的内置命令注册对应的命令插件
99 kernel.optsPlugins.push(path.resolve(commandsPath, targetPlugin))
100 }
101
102 // 把内置命令插件传递给 kernel,可以暴露给其他插件使用
103 kernel.cliCommandsPath = commandsPath
104 kernel.cliCommands = commandPlugins
105 .filter(commandFileName => /^[\w-]+(\.[\w-]+)*\.js$/.test(commandFileName))
106 .map(fileName => fileName.replace(/\.js$/, ''))
107
108 switch (command) {
109 case 'inspect':
110 case 'build': {
111 let plugin
112 let platform = args.type
113 const { publicPath, bundleOutput, sourcemapOutput, sourceMapUrl, sourcemapSourcesRoot, assetsDest } = args
114
115 // 针对不同的内置平台注册对应的端平台插件
116 switch (platform) {
117 case 'weapp':
118 case 'alipay':
119 case 'swan':
120 case 'tt':
121 case 'qq':
122 case 'jd':
123 case 'h5':
124 case 'harmony-hybrid':
125 kernel.optsPlugins.push(`@tarojs/plugin-platform-${platform}`)
126 break
127 default: {
128 // plugin, rn
129 const platformPlugins = fs.readdirSync(platformsPath)
130 const targetPlugin = `${platform}.js`
131 if (platformPlugins.includes(targetPlugin)) {
132 kernel.optsPlugins.push(path.resolve(platformsPath, targetPlugin))
133 }
134 break
135 }
136 }
137
138 // 根据 framework 启用插件
139 const framework = kernel.config?.initialConfig.framework || DEFAULT_FRAMEWORK
140 const frameworkMap = {
141 vue: '@tarojs/plugin-framework-vue2',
142 vue3: '@tarojs/plugin-framework-vue3',
143 react: '@tarojs/plugin-framework-react',
144 preact: '@tarojs/plugin-framework-react',
145 nerv: '@tarojs/plugin-framework-react',
146 }
147 if (frameworkMap[framework]) {
148 kernel.optsPlugins.push(frameworkMap[framework])
149 }
150
151 // 编译小程序插件
152 if (typeof args.plugin === 'string') {
153 plugin = args.plugin
154 platform = 'plugin'
155 kernel.optsPlugins.push(path.resolve(platformsPath, 'plugin.js'))
156 if (plugin === 'weapp' || plugin === 'alipay' || plugin === 'jd') {
157 kernel.optsPlugins.push(`@tarojs/plugin-platform-${plugin}`)
158 }
159 }
160
161 // 传递 inspect 参数即可
162 if (command === 'inspect') {
163 customCommand(command, kernel, args)
164 break
165 }
166
167 customCommand(command, kernel, {
168 _,
169 platform,
170 plugin,
171 isWatch: Boolean(args.watch),
172 // Note: 是否把 Taro 组件编译为原生自定义组件
173 isBuildNativeComp: _[1] === 'native-components',
174 // Note: 新的混合编译模式,支持把组件单独编译为原生组件
175 newBlended: Boolean(args['new-blended']),
176 // Note: 是否禁用编译
177 withoutBuild: !args.build,
178 port: args.port,
179 env: args.env,
180 deviceType: args.platform,
181 resetCache: !!args.resetCache,
182 publicPath,
183 bundleOutput,
184 sourcemapOutput,
185 sourceMapUrl,
186 sourcemapSourcesRoot,
187 assetsDest,
188 qr: !!args.qr,
189 blended: Boolean(args.blended),
190 h: args.h
191 })
192 break
193 }
194 case 'init': {
195 customCommand(command, kernel, {
196 _,
197 appPath,
198 projectName: _[1] || args.name,
199 description: args.description,
200 typescript: args.typescript,
201 framework: args.framework,
202 compiler: args.compiler,
203 npm: args.npm,
204 templateSource: args['template-source'],
205 clone: !!args.clone,
206 template: args.template,
207 css: args.css,
208 h: args.h
209 })
210 break
211 }
212 default:
213 customCommand(command, kernel, args)
214 break
215 }
216 } else {
217 if (args.h) {
218 console.log('Usage: taro <command> [options]')
219 console.log()
220 console.log('Options:')
221 console.log(' -v, --version output the version number')
222 console.log(' -h, --help output usage information')
223 console.log()
224 console.log('Commands:')
225 console.log(' init [projectName] Init a project with default templete')
226 console.log(' config <cmd> Taro config')
227 console.log(' create Create page for project')
228 console.log(' build Build a project with options')
229 console.log(' update Update packages of taro')
230 console.log(' info Diagnostics Taro env info')
231 console.log(' doctor Diagnose taro project')
232 console.log(' inspect Inspect the webpack config')
233 console.log(' help [cmd] display help for [cmd]')
234 } else if (args.v) {
235 console.log(getPkgVersion())
236 }
237 }
238 }
239}