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): support TS/JS config files #3756

Merged
merged 22 commits into from
Nov 9, 2020
Merged
Show file tree
Hide file tree
Changes from 13 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
6 changes: 4 additions & 2 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"engines": {
"node": ">=10.3.0"
},
"main": "./dist/index.js",
"main": "dist/index.js",
"types": "dist/declarations.d.ts",
"scripts": {
"build": "npm run clean && npm run assets && tsc",
"clean": "rimraf ./dist",
Expand All @@ -23,7 +24,8 @@
"files": [
"assets/",
"bin/",
"dist/"
"dist/**/*.js",
"dist/declarations.d.ts"
],
"keywords": [
"ionic",
Expand Down
8 changes: 4 additions & 4 deletions cli/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { setTimeout } from 'timers';
import xml2js from 'xml2js';

import c from './colors';
import type { Config, PackageJson, ExternalConfig } from './definitions';
import type { Config, ExternalConfig, PackageJson } from './definitions';
import { output, logger } from './log';

export type CheckFunction = () => Promise<string | null>;
Expand All @@ -34,7 +34,7 @@ export async function checkWebDir(config: Config): Promise<string | null> {
`Please create it and make sure it has an ${c.strong(
'index.html',
)} file. You can change the path of this directory in ${c.strong(
'capacitor.config.json',
config.app.extConfigName,
)} (${c.input(
'webDir',
)} option). You may need to compile the web assets for your app (typically ${c.input(
Expand Down Expand Up @@ -87,15 +87,15 @@ export async function checkAppConfig(config: Config): Promise<string | null> {
if (!config.app.appId) {
return (
`Missing ${c.input('appId')} for new platform.\n` +
`Please add it in capacitor.config.json or run ${c.input(
`Please add it in ${config.app.extConfigName} or run ${c.input(
'npx cap init',
)}.`
);
}
if (!config.app.appName) {
return (
`Missing ${c.input('appName')} for new platform.\n` +
`Please add it in capacitor.config.json or run ${c.input(
`Please add it in ${config.app.extConfigName} or run ${c.input(
'npx cap init',
)}.`
);
Expand Down
133 changes: 97 additions & 36 deletions cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,36 @@ import { pathExists, readJSON } from '@ionic/utils-fs';
import Debug from 'debug';
import { dirname, join, resolve } from 'path';

import { runCommand } from './common';
import c from './colors';
import { logFatal, resolveNode, runCommand } from './common';
import type {
AndroidConfig,
AppConfig,
CLIConfig,
Config,
ExternalConfig,
CLIConfig,
AndroidConfig,
IOSConfig,
PackageJson,
WebConfig,
} from './definitions';
import { OS } from './definitions';
import { tryFn } from './util/fn';
import { requireTS } from './util/node';

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

export const EXTERNAL_CONFIG_FILE = 'capacitor.config.json';

export async function loadConfig(): Promise<Config> {
const appRootDir = process.cwd();
const cliRootDir = dirname(__dirname);
const extConfig = await loadExternalConfig(
resolve(appRootDir, EXTERNAL_CONFIG_FILE),
);
const conf = await loadExtConfig(appRootDir);

const appId = extConfig.appId ?? '';
const appName = extConfig.appName ?? '';
const webDir = extConfig.webDir ?? 'www';
const appId = conf.extConfig.appId ?? '';
const appName = conf.extConfig.appName ?? '';
const webDir = conf.extConfig.webDir ?? 'www';
const cli = await loadCLIConfig(cliRootDir);

const config = {
android: await loadAndroidConfig(appRootDir, extConfig, cli),
ios: await loadIOSConfig(appRootDir, extConfig, cli),
const config: Config = {
android: await loadAndroidConfig(appRootDir, conf.extConfig, cli),
ios: await loadIOSConfig(appRootDir, conf.extConfig, cli),
web: await loadWebConfig(appRootDir, webDir),
cli,
app: {
Expand All @@ -41,14 +40,12 @@ export async function loadConfig(): Promise<Config> {
appName,
webDir,
webDirAbs: resolve(appRootDir, webDir),
package: (await readPackageJSON(resolve(appRootDir, 'package.json'))) ?? {
package: (await tryFn(readJSON, resolve(appRootDir, 'package.json'))) ?? {
name: appName,
version: '1.0.0',
},
extConfigName: EXTERNAL_CONFIG_FILE,
extConfigFilePath: resolve(appRootDir, EXTERNAL_CONFIG_FILE),
extConfig,
bundledWebRuntime: extConfig.bundledWebRuntime ?? false,
...conf,
bundledWebRuntime: conf.extConfig.bundledWebRuntime ?? false,
},
};

Expand All @@ -57,6 +54,86 @@ export async function loadConfig(): Promise<Config> {
return config;
}

type ExtConfigPairs = Pick<
AppConfig,
'extConfigType' | 'extConfigName' | 'extConfigFilePath' | 'extConfig'
>;

async function loadExtConfigTS(
rootDir: string,
extConfigName: string,
extConfigFilePath: string,
): Promise<ExtConfigPairs> {
try {
const tsPath = resolveNode(rootDir, 'typescript');

if (!tsPath) {
logFatal(
'Could not find installation of TypeScript.\n' +
`To use ${c.strong(
extConfigName,
)} files, you must install TypeScript in your project, e.g. w/ ${c.input(
'npm install -D typescript',
)}`,
);
}

const ts = require(tsPath); // eslint-disable-line @typescript-eslint/no-var-requires

return {
extConfigType: 'ts',
extConfigName,
extConfigFilePath: extConfigFilePath,
extConfig: requireTS(ts, extConfigFilePath) as any,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be quite useful to optionally support exporting a () => Promise<ExternalConfig> instead of just ExternalConfig.

Would you accept a pull request?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was thinking about this as well. I would be happy to look at a PR that adds that functionality.

};
} catch (e) {
logFatal(`Parsing ${c.strong(extConfigName)} failed.\n\n${e.stack ?? e}`);
}
}

async function loadExtConfigJS(
rootDir: string,
extConfigName: string,
extConfigFilePath: string,
): Promise<ExtConfigPairs> {
try {
return {
extConfigType: 'js',
extConfigName,
extConfigFilePath: extConfigFilePath,
extConfig: require(extConfigFilePath),
};
} catch (e) {
logFatal(`Parsing ${c.strong(extConfigName)} failed.\n\n${e.stack ?? e}`);
}
}

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

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

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

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

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

return {
extConfigType: 'json',
extConfigName,
extConfigFilePath: extConfigFilePath,
extConfig: (await tryFn(readJSON, extConfigFilePath)) ?? {},
};
}

async function loadCLIConfig(rootDir: string): Promise<CLIConfig> {
const assetsName = 'assets';

Expand Down Expand Up @@ -202,19 +279,3 @@ async function determineAndroidStudioPath(os: OS): Promise<string> {

return '';
}

async function loadExternalConfig(p: string): Promise<ExternalConfig> {
try {
return await readJSON(p);
} catch (e) {
return {};
}
}

async function readPackageJSON(p: string): Promise<PackageJson | null> {
try {
return await readJSON(p);
} catch (e) {
return null;
}
}
5 changes: 2 additions & 3 deletions cli/src/cordova.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ export async function getCordovaPreferences(config: Config): Promise<any> {
const answers = await logPrompt(
`${c.strong(
`Cordova preferences can be automatically ported to ${c.strong(
'capacitor.config.json',
config.app.extConfigName,
imhoffd marked this conversation as resolved.
Show resolved Hide resolved
)}.`,
)}\n` +
`Keep in mind: Not all values can be automatically migrated from ${c.strong(
Expand All @@ -550,8 +550,7 @@ export async function getCordovaPreferences(config: Config): Promise<any> {
{
type: 'confirm',
name: 'confirm',
message:
'capacitor.config.json already contains Cordova preferences. Overwrite?',
message: `${config.app.extConfigName} already contains Cordova preferences. Overwrite?`,
},
],
{ onCancel: () => process.exit(1) },
Expand Down
Loading