1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const child_process_1 = require("child_process");
|
4 | const timers_1 = require("timers");
|
5 | const path_1 = require("path");
|
6 | const fs_1 = require("./util/fs");
|
7 | const fs_2 = require("fs");
|
8 | const emoji_1 = require("./util/emoji");
|
9 | const term_1 = require("./util/term");
|
10 | const semver = require("semver");
|
11 | const inquirer = require("inquirer");
|
12 | const chalk = require('chalk');
|
13 | async 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 | }
|
20 | exports.check = check;
|
21 | async 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 | }
|
38 | exports.checkWebDir = checkWebDir;
|
39 | async 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 | }
|
47 | exports.checkPackage = checkPackage;
|
48 | async 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 | }
|
69 | exports.checkAppConfig = checkAppConfig;
|
70 | async function checkAppDir(config, dir) {
|
71 | if (!/^\S*$/.test(dir)) {
|
72 | return `Your app directory should not contain spaces`;
|
73 | }
|
74 | return null;
|
75 | }
|
76 | exports.checkAppDir = checkAppDir;
|
77 | async 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 | }
|
86 | exports.checkAppId = checkAppId;
|
87 | async function checkAppName(config, name) {
|
88 |
|
89 | if (!name || !name.length) {
|
90 | return `Must provide an app name. For example: 'Spacebook'`;
|
91 | }
|
92 | return null;
|
93 | }
|
94 | exports.checkAppName = checkAppName;
|
95 | async function checkNpmClient(config, client) {
|
96 |
|
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 | }
|
102 | exports.checkNpmClient = checkNpmClient;
|
103 | async function readJSON(path) {
|
104 | const data = await fs_1.readFileAsync(path, 'utf8');
|
105 | return JSON.parse(data);
|
106 | }
|
107 | exports.readJSON = readJSON;
|
108 | function writePrettyJSON(path, data) {
|
109 | return fs_1.writeFileAsync(path, JSON.stringify(data, null, ' ') + '\n');
|
110 | }
|
111 | exports.writePrettyJSON = writePrettyJSON;
|
112 | function 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 | }
|
132 | exports.readXML = readXML;
|
133 | function 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 | }
|
143 | exports.parseXML = parseXML;
|
144 | function 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 | }
|
153 | exports.writeXML = writeXML;
|
154 | function 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 | }
|
159 | exports.buildXmlElement = buildXmlElement;
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | async 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 |
|
177 | config.loadExternalConfig();
|
178 | }
|
179 | exports.getOrCreateConfig = getOrCreateConfig;
|
180 | async 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 |
|
184 | config.loadExternalConfig();
|
185 | }
|
186 | exports.mergeConfig = mergeConfig;
|
187 | function log(...args) {
|
188 | console.log(...args);
|
189 | }
|
190 | exports.log = log;
|
191 | function logSuccess(...args) {
|
192 | const chalk = require('chalk');
|
193 | console.log(chalk.green('[success]'), ...args);
|
194 | }
|
195 | exports.logSuccess = logSuccess;
|
196 | function logInfo(...args) {
|
197 | const chalk = require('chalk');
|
198 | console.log(chalk.bold.cyan('[info]'), ...args);
|
199 | }
|
200 | exports.logInfo = logInfo;
|
201 | function logWarn(...args) {
|
202 | const chalk = require('chalk');
|
203 | console.log(chalk.bold.yellow('[warn]'), ...args);
|
204 | }
|
205 | exports.logWarn = logWarn;
|
206 | function logError(...args) {
|
207 | const chalk = require('chalk');
|
208 | console.error(chalk.red('[error]'), ...args);
|
209 | }
|
210 | exports.logError = logError;
|
211 | function logFatal(...args) {
|
212 | logError(...args);
|
213 | return process.exit(1);
|
214 | }
|
215 | exports.logFatal = logFatal;
|
216 | async 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 | }
|
229 | exports.isInstalled = isInstalled;
|
230 | function wait(time) {
|
231 | return new Promise((resolve) => timers_1.setTimeout(resolve, time));
|
232 | }
|
233 | exports.wait = wait;
|
234 | function 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 | }
|
246 | exports.runCommand = runCommand;
|
247 | async 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 | }
|
270 | exports.runTask = runTask;
|
271 | const TIME_UNITS = ['s', 'ms', 'μp'];
|
272 | function 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 | }
|
282 | exports.formatHrTime = formatHrTime;
|
283 | async 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 | }
|
295 | exports.getName = getName;
|
296 | async 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 | }
|
308 | exports.getAppId = getAppId;
|
309 | function 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 |
|
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 | }
|
335 | exports.getNpmClient = getNpmClient;
|
336 | async function copyTemplate(src, dst) {
|
337 | await fs_1.copyAsync(src, dst);
|
338 |
|
339 |
|
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 | }
|
345 | exports.copyTemplate = copyTemplate;
|
346 | async 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 | }
|
362 | exports.printNextSteps = printNextSteps;
|
363 | async 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 | }
|
382 | exports.checkPlatformVersions = checkPlatformVersions;
|
383 | function 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 | }
|
399 | exports.resolveNode = resolveNode;
|
400 | function 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 | }
|
415 | exports.resolveNodeFrom = resolveNodeFrom;
|
416 |
|
417 | exports.hasYarn = async (config, projectDir) => {
|
418 |
|
419 | const npmClient = config.cli.npmClient;
|
420 | if (npmClient === 'yarn') {
|
421 | return true;
|
422 | }
|
423 | else if (npmClient === 'npm') {
|
424 | return false;
|
425 | }
|
426 |
|
427 | return fs_2.existsSync(path_1.join(projectDir || process.cwd(), 'yarn.lock'));
|
428 | };
|
429 |
|
430 | async function installDeps(projectDir, deps, config) {
|
431 | return runCommand(`cd "${projectDir}" && ${await exports.hasYarn(config, projectDir) ? 'yarn add' : 'npm install --save'} ${deps.join(' ')}`);
|
432 | }
|
433 | exports.installDeps = installDeps;
|
434 | async 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 | }
|
443 | exports.checkNPMVersion = checkNPMVersion;
|