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

feat(cli): create TS configuration files in init #3999

Merged
merged 9 commits into from
Jan 11, 2021
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
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` +
imhoffd marked this conversation as resolved.
Show resolved Hide resolved
`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,
});
}