UNPKG

15.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const child_process_1 = require("child_process");
4const timers_1 = require("timers");
5const path_1 = require("path");
6const fs_1 = require("./util/fs");
7const fs_2 = require("fs");
8const emoji_1 = require("./util/emoji");
9const term_1 = require("./util/term");
10const semver = require("semver");
11const inquirer = require("inquirer");
12const chalk = require('chalk');
13async function check(config, checks) {
14 const results = await Promise.all(checks.map(f => f(config)));
15 const errors = results.filter(r => r != null);
16 if (errors.length > 0) {
17 throw errors.join('\n');
18 }
19}
20exports.check = check;
21async function checkWebDir(config) {
22 const invalidFolders = ['', '.', '..', '../', './'];
23 if (invalidFolders.includes(config.app.webDir)) {
24 return `"${config.app.webDir}" is not a valid value for webDir`;
25 }
26 if (!await fs_1.existsAsync(config.app.webDirAbs)) {
27 return `Capacitor could not find the web assets directory "${config.app.webDirAbs}".
28 Please create it, and make sure it has an index.html file. You can change
29 the path of this directory in capacitor.config.json.
30 More info: https://capacitor.ionicframework.com/docs/basics/configuring-your-app`;
31 }
32 if (!await fs_1.existsAsync(path_1.join(config.app.webDirAbs, 'index.html'))) {
33 return `The web directory (${config.app.webDirAbs}) must contain a "index.html".
34 It will be the entry point for the web portion of the Capacitor app.`;
35 }
36 return null;
37}
38exports.checkWebDir = checkWebDir;
39async function checkPackage(_config) {
40 if (!await fs_1.existsAsync('package.json')) {
41 return `Capacitor needs to run at the root of an npm package.
42 Make sure you have a "package.json" in the directory where you run capacitor.
43 More info: https://docs.npmjs.com/cli/init`;
44 }
45 return null;
46}
47exports.checkPackage = checkPackage;
48async function checkAppConfig(config) {
49 if (!config.app.appId) {
50 return 'Missing appId for new platform. Please add it in capacitor.config.json or run npx cap init.';
51 }
52 if (!config.app.appName) {
53 return 'Missing appName for new platform. Please add it in capacitor.config.json or run npx cap init.';
54 }
55 const appIdError = await checkAppId(config, config.app.appId);
56 if (appIdError) {
57 return appIdError;
58 }
59 const appNameError = await checkAppName(config, config.app.appName);
60 if (appNameError) {
61 return appNameError;
62 }
63 const npmClientError = await checkNpmClient(config, config.cli.npmClient);
64 if (npmClientError) {
65 return npmClientError;
66 }
67 return null;
68}
69exports.checkAppConfig = checkAppConfig;
70async function checkAppDir(config, dir) {
71 if (!/^\S*$/.test(dir)) {
72 return `Your app directory should not contain spaces`;
73 }
74 return null;
75}
76exports.checkAppDir = checkAppDir;
77async function checkAppId(config, id) {
78 if (!id) {
79 return `Invalid App ID. Must be in Java package form with no dashes (ex: com.example.app)`;
80 }
81 if (/^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$/.test(id.toLowerCase())) {
82 return null;
83 }
84 return `Invalid App ID "${id}". Must be in Java package form with no dashes (ex: com.example.app)`;
85}
86exports.checkAppId = checkAppId;
87async function checkAppName(config, name) {
88 // We allow pretty much anything right now, have fun
89 if (!name || !name.length) {
90 return `Must provide an app name. For example: 'Spacebook'`;
91 }
92 return null;
93}
94exports.checkAppName = checkAppName;
95async function checkNpmClient(config, client) {
96 // npm client must be npm, yarn, or undefined
97 if (client && client !== 'npm' && client !== 'yarn') {
98 return `npm client must be "npm" or "yarn". If you are not sure, choose "npm"`;
99 }
100 return null;
101}
102exports.checkNpmClient = checkNpmClient;
103async function readJSON(path) {
104 const data = await fs_1.readFileAsync(path, 'utf8');
105 return JSON.parse(data);
106}
107exports.readJSON = readJSON;
108function writePrettyJSON(path, data) {
109 return fs_1.writeFileAsync(path, JSON.stringify(data, null, ' ') + '\n');
110}
111exports.writePrettyJSON = writePrettyJSON;
112function readXML(path) {
113 return new Promise((resolve, reject) => {
114 fs_2.readFile(path, 'utf8', async (err, xmlStr) => {
115 if (err) {
116 reject(`Unable to read: ${path}`);
117 }
118 else {
119 const xml2js = await Promise.resolve().then(() => require('xml2js'));
120 xml2js.parseString(xmlStr, (err, result) => {
121 if (err) {
122 reject(`Error parsing: ${path}, ${err}`);
123 }
124 else {
125 resolve(result);
126 }
127 });
128 }
129 });
130 });
131}
132exports.readXML = readXML;
133function parseXML(xmlStr) {
134 const parseString = require('xml2js').parseString;
135 var xmlObj;
136 parseString(xmlStr, (err, result) => {
137 if (!err) {
138 xmlObj = result;
139 }
140 });
141 return xmlObj;
142}
143exports.parseXML = parseXML;
144function writeXML(object) {
145 return new Promise(async (resolve, reject) => {
146 const xml2js = await Promise.resolve().then(() => require('xml2js'));
147 const builder = new xml2js.Builder({ headless: true, explicitRoot: false, rootName: 'deleteme' });
148 let xml = builder.buildObject(object);
149 xml = xml.replace('<deleteme>', '').replace('</deleteme>', '');
150 resolve(xml);
151 });
152}
153exports.writeXML = writeXML;
154function buildXmlElement(configElement, rootName) {
155 const xml2js = require('xml2js');
156 const builder = new xml2js.Builder({ headless: true, explicitRoot: false, rootName: rootName });
157 return builder.buildObject(configElement);
158}
159exports.buildXmlElement = buildXmlElement;
160/**
161 * Check for or create our main configuration file.
162 * @param config
163 */
164async function getOrCreateConfig(config) {
165 const configPath = path_1.join(config.app.rootDir, config.app.extConfigName);
166 if (await fs_1.existsAsync(configPath)) {
167 return configPath;
168 }
169 await writePrettyJSON(config.app.extConfigFilePath, {
170 appId: config.app.appId,
171 appName: config.app.appName,
172 bundledWebRuntime: config.app.bundledWebRuntime,
173 npmClient: config.cli.npmClient,
174 webDir: path_1.basename(path_1.resolve(config.app.rootDir, config.app.webDir))
175 });
176 // Store our newly created or found external config as the default
177 config.loadExternalConfig();
178}
179exports.getOrCreateConfig = getOrCreateConfig;
180async function mergeConfig(config, settings) {
181 const configPath = path_1.join(config.app.rootDir, config.app.extConfigName);
182 await writePrettyJSON(config.app.extConfigFilePath, Object.assign(Object.assign({}, config.app.extConfig), settings));
183 // Store our newly created or found external config as the default
184 config.loadExternalConfig();
185}
186exports.mergeConfig = mergeConfig;
187function log(...args) {
188 console.log(...args);
189}
190exports.log = log;
191function logSuccess(...args) {
192 const chalk = require('chalk');
193 console.log(chalk.green('[success]'), ...args);
194}
195exports.logSuccess = logSuccess;
196function logInfo(...args) {
197 const chalk = require('chalk');
198 console.log(chalk.bold.cyan('[info]'), ...args);
199}
200exports.logInfo = logInfo;
201function logWarn(...args) {
202 const chalk = require('chalk');
203 console.log(chalk.bold.yellow('[warn]'), ...args);
204}
205exports.logWarn = logWarn;
206function logError(...args) {
207 const chalk = require('chalk');
208 console.error(chalk.red('[error]'), ...args);
209}
210exports.logError = logError;
211function logFatal(...args) {
212 logError(...args);
213 return process.exit(1);
214}
215exports.logFatal = logFatal;
216async function isInstalled(command) {
217 const which = await Promise.resolve().then(() => require('which'));
218 return new Promise((resolve) => {
219 which(command, (err) => {
220 if (err) {
221 resolve(false);
222 }
223 else {
224 resolve(true);
225 }
226 });
227 });
228}
229exports.isInstalled = isInstalled;
230function wait(time) {
231 return new Promise((resolve) => timers_1.setTimeout(resolve, time));
232}
233exports.wait = wait;
234function runCommand(command) {
235 return new Promise((resolve, reject) => {
236 child_process_1.exec(command, (error, stdout, stderr) => {
237 if (error) {
238 reject(stdout + stderr);
239 }
240 else {
241 resolve(stdout);
242 }
243 });
244 });
245}
246exports.runCommand = runCommand;
247async function runTask(title, fn) {
248 const ora = require('ora');
249 const spinner = ora(title).start();
250 try {
251 const start = process.hrtime();
252 let taskInfoMessage;
253 const value = await fn((message) => taskInfoMessage = message);
254 const elapsed = process.hrtime(start);
255 const chalk = require('chalk');
256 if (taskInfoMessage) {
257 spinner.info(`${title} ${chalk.dim('– ' + taskInfoMessage)}`);
258 }
259 else {
260 spinner.succeed(`${title} ${chalk.dim('in ' + formatHrTime(elapsed))}`);
261 }
262 return value;
263 }
264 catch (e) {
265 spinner.fail(`${title}: ${e.message ? e.message : ''}`);
266 spinner.stop();
267 throw e;
268 }
269}
270exports.runTask = runTask;
271const TIME_UNITS = ['s', 'ms', 'μp'];
272function formatHrTime(hrtime) {
273 let time = (hrtime[0] + (hrtime[1] / 1e9));
274 let index = 0;
275 for (; index < TIME_UNITS.length - 1; index++, time *= 1000) {
276 if (time >= 1) {
277 break;
278 }
279 }
280 return time.toFixed(2) + TIME_UNITS[index];
281}
282exports.formatHrTime = formatHrTime;
283async function getName(config, name) {
284 if (!name) {
285 const answers = await inquirer.prompt([{
286 type: 'input',
287 name: 'name',
288 default: config.app.appName ? config.app.appName : config.app.package.name ? config.app.package.name : 'App',
289 message: `App name`
290 }]);
291 return answers.name;
292 }
293 return name;
294}
295exports.getName = getName;
296async function getAppId(config, id) {
297 if (!id) {
298 const answers = await inquirer.prompt([{
299 type: 'input',
300 name: 'id',
301 default: config.app.appId ? config.app.appId : 'com.example.app',
302 message: 'App Package ID (in Java package format, no dashes)'
303 }]);
304 return answers.id;
305 }
306 return id;
307}
308exports.getAppId = getAppId;
309function getNpmClient(config, npmClient) {
310 return new Promise(async (resolve) => {
311 if (!npmClient) {
312 if (await exports.hasYarn(config))
313 return resolve('yarn');
314 child_process_1.exec('yarn --version', async (err, stdout) => {
315 // Don't show prompt if yarn is not installed
316 if (err || !term_1.isInteractive()) {
317 resolve('npm');
318 }
319 else {
320 const answers = await inquirer.prompt([{
321 type: 'list',
322 name: 'npmClient',
323 message: 'Which npm client would you like to use?',
324 choices: ['npm', 'yarn']
325 }]);
326 resolve(answers.npmClient);
327 }
328 });
329 }
330 else {
331 resolve(npmClient);
332 }
333 });
334}
335exports.getNpmClient = getNpmClient;
336async function copyTemplate(src, dst) {
337 await fs_1.copyAsync(src, dst);
338 // npm renames .gitignore to something else, so our templates
339 // have .gitignore as gitignore, we need to rename it here.
340 const gitignorePath = path_1.join(dst, 'gitignore');
341 if (await fs_1.existsAsync(gitignorePath)) {
342 await fs_1.renameAsync(gitignorePath, path_1.join(dst, '.gitignore'));
343 }
344}
345exports.copyTemplate = copyTemplate;
346async function printNextSteps(config, appDir) {
347 log('\n');
348 log(`${chalk.bold(`${emoji_1.emoji('🎉', '*')} Your Capacitor project is ready to go! ${emoji_1.emoji('🎉', '*')}`)}\n`);
349 if (appDir !== '') {
350 log(`Next steps:`);
351 log('');
352 log(chalk `cd {bold ./${appDir}}`);
353 log('');
354 }
355 log(`Add platforms using "npx cap add":\n`);
356 log(` npx cap add android`);
357 log(` npx cap add ios`);
358 log(` npx cap add electron`);
359 log('');
360 log(`Follow the Developer Workflow guide to get building:\n${chalk.bold(`https://capacitor.ionicframework.com/docs/basics/workflow`)}\n`);
361}
362exports.printNextSteps = printNextSteps;
363async function checkPlatformVersions(config, platform) {
364 const cliPackagePath = resolveNode(config, '@capacitor/cli', 'package.json');
365 if (!cliPackagePath) {
366 logFatal('Unable to find node_modules/@capacitor/cli/package.json. Are you sure', '@capacitor/cli is installed? This file is currently required for Capacitor to function.');
367 return;
368 }
369 const platformPackagePath = resolveNode(config, `@capacitor/${platform}`, 'package.json');
370 if (!platformPackagePath) {
371 logFatal(`Unable to find node_modules/@capacitor/${platform}/package.json. Are you sure`, `@capacitor/${platform} is installed? This file is currently required for Capacitor to function.`);
372 return;
373 }
374 const cliVersion = (await readJSON(cliPackagePath)).version;
375 const platformVersion = (await readJSON(platformPackagePath)).version;
376 if (semver.gt(cliVersion, platformVersion)) {
377 log('\n');
378 logInfo(`Your @capacitor/cli version is greater than @capacitor/${platform} version`);
379 log(`Consider updating to matching version ${chalk `{bold npm install @capacitor/${platform}@${cliVersion}}`}`);
380 }
381}
382exports.checkPlatformVersions = checkPlatformVersions;
383function resolveNode(config, ...pathSegments) {
384 const id = pathSegments[0];
385 const path = pathSegments.slice(1);
386 let modulePath;
387 const starts = [config.app.rootDir];
388 for (let start of starts) {
389 modulePath = resolveNodeFrom(start, id);
390 if (modulePath) {
391 break;
392 }
393 }
394 if (!modulePath) {
395 return null;
396 }
397 return path_1.join(modulePath, ...path);
398}
399exports.resolveNode = resolveNode;
400function resolveNodeFrom(start, id) {
401 const rootPath = path_1.parse(start).root;
402 let basePath = path_1.resolve(start);
403 let modulePath;
404 while (true) {
405 modulePath = path_1.join(basePath, 'node_modules', id);
406 if (fs_2.existsSync(modulePath)) {
407 return modulePath;
408 }
409 if (basePath === rootPath) {
410 return null;
411 }
412 basePath = path_1.dirname(basePath);
413 }
414}
415exports.resolveNodeFrom = resolveNodeFrom;
416// Does the current project use yarn?
417exports.hasYarn = async (config, projectDir) => {
418 // npmClient in config
419 const npmClient = config.cli.npmClient;
420 if (npmClient === 'yarn') {
421 return true;
422 }
423 else if (npmClient === 'npm') {
424 return false;
425 }
426 // yarn.lock
427 return fs_2.existsSync(path_1.join(projectDir || process.cwd(), 'yarn.lock'));
428};
429// Install deps with NPM or Yarn
430async function installDeps(projectDir, deps, config) {
431 return runCommand(`cd "${projectDir}" && ${await exports.hasYarn(config, projectDir) ? 'yarn add' : 'npm install --save'} ${deps.join(' ')}`);
432}
433exports.installDeps = installDeps;
434async function checkNPMVersion() {
435 const minVersion = '5.5.0';
436 const version = await runCommand('npm -v');
437 const semver = await Promise.resolve().then(() => require('semver'));
438 if (semver.gt(minVersion, version)) {
439 return `Capacitor CLI requires at least NPM ${minVersion}`;
440 }
441 return null;
442}
443exports.checkNPMVersion = checkNPMVersion;