Skip to content

Commit

Permalink
feat(cli): create TS configuration files in init (#3999)
Browse files Browse the repository at this point in the history
  • Loading branch information
imhoffd authored Jan 11, 2021
1 parent a0c48e4 commit fa7003e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 46 deletions.
55 changes: 44 additions & 11 deletions cli/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { pathExists, readFile, readJSON } from '@ionic/utils-fs';
import {
pathExists,
readFile,
readJSON,
writeFile,
writeJSON,
} from '@ionic/utils-fs';
import Debug from 'debug';
import { dirname, join, relative, resolve } from 'path';
import { dirname, extname, join, relative, resolve } from 'path';

import c from './colors';
import type {
Expand All @@ -16,11 +22,16 @@ import { OS } from './definitions';
import { fatal, isFatal } from './errors';
import { logger } from './log';
import { tryFn } from './util/fn';
import { formatJSObject } from './util/js';
import { resolveNode, requireTS } from './util/node';
import { lazy } from './util/promise';

const debug = Debug('capacitor:config');

export const CONFIG_FILE_NAME_TS = 'capacitor.config.ts';
export const CONFIG_FILE_NAME_JS = 'capacitor.config.js';
export const CONFIG_FILE_NAME_JSON = 'capacitor.config.json';

export async function loadConfig(): Promise<Config> {
const appRootDir = process.cwd();
const cliRootDir = dirname(__dirname);
Expand Down Expand Up @@ -56,6 +67,22 @@ export async function loadConfig(): Promise<Config> {
return config;
}

export async function writeConfig(
extConfig: ExternalConfig,
extConfigFilePath: string,
): Promise<void> {
switch (extname(extConfigFilePath)) {
case '.json': {
await writeJSON(extConfigFilePath, extConfig, { spaces: 2 });
break;
}
case '.ts': {
await writeFile(extConfigFilePath, formatConfigTS(extConfig));
break;
}
}
}

type ExtConfigPairs = Pick<
AppConfig,
'extConfigType' | 'extConfigName' | 'extConfigFilePath' | 'extConfig'
Expand Down Expand Up @@ -117,26 +144,23 @@ async function loadExtConfigJS(
}

async function loadExtConfig(rootDir: string): Promise<ExtConfigPairs> {
const extConfigNameTS = 'capacitor.config.ts';
const extConfigFilePathTS = resolve(rootDir, extConfigNameTS);
const extConfigFilePathTS = resolve(rootDir, CONFIG_FILE_NAME_TS);

if (await pathExists(extConfigFilePathTS)) {
return loadExtConfigTS(rootDir, extConfigNameTS, extConfigFilePathTS);
return loadExtConfigTS(rootDir, CONFIG_FILE_NAME_TS, extConfigFilePathTS);
}

const extConfigNameJS = 'capacitor.config.js';
const extConfigFilePathJS = resolve(rootDir, extConfigNameJS);
const extConfigFilePathJS = resolve(rootDir, CONFIG_FILE_NAME_JS);

if (await pathExists(extConfigFilePathJS)) {
return loadExtConfigJS(rootDir, extConfigNameJS, extConfigFilePathJS);
return loadExtConfigJS(rootDir, CONFIG_FILE_NAME_JS, extConfigFilePathJS);
}

const extConfigName = 'capacitor.config.json';
const extConfigFilePath = resolve(rootDir, extConfigName);
const extConfigFilePath = resolve(rootDir, CONFIG_FILE_NAME_JSON);

return {
extConfigType: 'json',
extConfigName,
extConfigName: CONFIG_FILE_NAME_JSON,
extConfigFilePath: extConfigFilePath,
extConfig: (await tryFn(readJSON, extConfigFilePath)) ?? {},
};
Expand Down Expand Up @@ -404,3 +428,12 @@ function determineCocoapodPath(): string {

return 'pod';
}

function formatConfigTS(extConfig: ExternalConfig): string {
// TODO: <reference> tags
return `import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = ${formatJSObject(extConfig)};
export default config;\n`;
}
2 changes: 1 addition & 1 deletion cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function runProgram(config: Config): void {

program
.command('init [appName] [appId]')
.description(`create a ${c.strong('capacitor.config.json')} file`)
.description(`Initialize Capacitor configuration`)
.option(
'--web-dir <value>',
'Optional: Directory of your projects built web assets',
Expand Down
89 changes: 55 additions & 34 deletions cli/src/tasks/init.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { writeJSON } from '@ionic/utils-fs';
import { basename, dirname, resolve } from 'path';

import c from '../colors';
import { check, checkAppId, checkAppName, runTask } from '../common';
import {
CONFIG_FILE_NAME_JSON,
CONFIG_FILE_NAME_TS,
writeConfig,
} from '../config';
import { getCordovaPreferences } from '../cordova';
import type { Config, ExternalConfig } from '../definitions';
import { fatal, isFatal } from '../errors';
import { output, logSuccess, logPrompt } from '../log';
import { resolveNode } from '../util/node';
import { checkInteractive, isInteractive } from '../util/term';

export async function initCommand(
Expand All @@ -28,6 +34,8 @@ export async function initCommand(
);
}

const isNewConfig = Object.keys(config.app.extConfig).length === 0;
const tsInstalled = !!resolveNode(config.app.rootDir, 'typescript');
const appName = await getName(config, name);
const appId = await getAppId(config, id);
const webDir = isInteractive()
Expand All @@ -41,22 +49,17 @@ export async function initCommand(

const cordova = await getCordovaPreferences(config);

await runTask(
`Creating ${c.strong(config.app.extConfigName)} in ${c.input(
config.app.rootDir,
)}`,
async () => {
await mergeConfig(config, {
appId,
appName,
webDir,
bundledWebRuntime: false,
cordova,
});
await runMergeConfig(
config,
{
appId,
appName,
webDir,
bundledWebRuntime: false,
cordova,
},
isNewConfig && tsInstalled ? 'ts' : 'json',
);

printNextSteps(config);
} catch (e) {
if (!isFatal(e)) {
output.write(
Expand All @@ -71,18 +74,6 @@ export async function initCommand(
}
}

function printNextSteps(config: Config) {
logSuccess(`${c.strong(config.app.extConfigName)} created!`);
output.write(
`\nAdd platforms using ${c.input('npx cap add')}:\n` +
` ${c.input('npx cap add android')}\n` +
` ${c.input('npx cap add ios')}\n\n` +
`Follow the Developer Workflow guide to get building:\n${c.strong(
`https://capacitorjs.com/docs/v3/basics/workflow`,
)}\n`,
);
}

async function getName(config: Config, name: string) {
if (!name) {
const answers = await logPrompt(
Expand Down Expand Up @@ -138,18 +129,48 @@ async function getWebDir(config: Config, webDir?: string) {
return webDir;
}

async function runMergeConfig(
config: Config,
extConfig: ExternalConfig,
type: 'json' | 'ts',
) {
const configDirectory = dirname(config.app.extConfigFilePath);
const newConfigPath = resolve(
configDirectory,
type === 'ts' ? CONFIG_FILE_NAME_TS : CONFIG_FILE_NAME_JSON,
);

await runTask(
`Creating ${c.strong(basename(newConfigPath))} in ${c.input(
config.app.rootDir,
)}`,
async () => {
await mergeConfig(config, extConfig, newConfigPath);
},
);

printNextSteps(basename(newConfigPath));
}

async function mergeConfig(
config: Config,
extConfig: ExternalConfig,
newConfigPath: string,
): Promise<void> {
const oldConfig = { ...config.app.extConfig };
const newConfig = { ...oldConfig, ...extConfig };

await writeJSON(
config.app.extConfigFilePath,
{
...oldConfig,
...extConfig,
},
{ spaces: 2 },
await writeConfig(newConfig, newConfigPath);
}

function printNextSteps(newConfigName: string) {
logSuccess(`${c.strong(newConfigName)} created!`);
output.write(
`\nAdd platforms using ${c.input('npx cap add')}:\n` +
` ${c.input('npx cap add android')}\n` +
` ${c.input('npx cap add ios')}\n\n` +
`Follow the Developer Workflow guide to get building:\n${c.strong(
`https://capacitorjs.com/docs/v3/basics/workflow`,
)}\n`,
);
}
17 changes: 17 additions & 0 deletions cli/src/util/js.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import util from 'util';

export function formatJSObject(o: { [key: string]: any }): string {
try {
o = JSON.parse(JSON.stringify(o));
} catch (e) {
throw new Error(`Cannot parse object as JSON: ${e.stack ? e.stack : e}`);
}

return util.inspect(o, {
compact: false,
breakLength: Infinity,
depth: Infinity,
maxArrayLength: Infinity,
maxStringLength: Infinity,
});
}

0 comments on commit fa7003e

Please sign in to comment.