@ionic/cli-utils
Version:
Ionic CLI Utils
338 lines (337 loc) • 15.8 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const cli_framework_1 = require("@ionic/cli-framework");
const format_1 = require("@ionic/cli-framework/utils/format");
const node_1 = require("@ionic/cli-framework/utils/node");
const utils_fs_1 = require("@ionic/utils-fs");
const chalk_1 = require("chalk");
const Debug = require("debug");
const lodash = require("lodash");
const path = require("path");
const constants_1 = require("../../constants");
const guards_1 = require("../../guards");
const errors_1 = require("../errors");
const integrations_1 = require("../integrations");
const debug = Debug('ionic:cli-utils:lib:project');
function determineProjectType(projectDir, projectName, projectConfig, deps) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let type;
if (guards_1.isProjectConfig(projectConfig)) {
type = projectConfig.type;
}
else if (guards_1.isMultiProjectConfig(projectConfig)) {
const name = projectName ? projectName : projectConfig.defaultProject;
if (!name) {
throw new Error(`Multi-app workspace detected, but cannot determine which project to use.\n` +
`Please set a ${chalk_1.default.green('defaultProject')} in ${chalk_1.default.bold(constants_1.PROJECT_FILE)} or specify the project using the global ${chalk_1.default.green('--project')} option.`);
}
const config = projectConfig.projects[name];
if (config) {
type = config.type;
}
else {
throw new Error(`Multi-app workspace detected, but project was not found in configuration.\n` +
`Project ${chalk_1.default.green(name)} could not be found in the workspace. Did you add it to ${chalk_1.default.bold(constants_1.PROJECT_FILE)}?`);
}
if (type && !constants_1.MULTI_PROJECT_TYPES.includes(type)) {
throw new Error(`Multi-app workspace detected, but project is of an unsupported type.\n` +
`Project type ${chalk_1.default.green(type)} is not supported in multi-app workspaces. Please set ${chalk_1.default.green(`projects.${name}.type`)} to one of: ${constants_1.MULTI_PROJECT_TYPES.map(v => chalk_1.default.green(v)).join(', ')}.`);
}
}
if (type && constants_1.PROJECT_TYPES.includes(type)) {
debug(`Project type from config: ${chalk_1.default.bold(prettyProjectName(type))} ${type ? chalk_1.default.bold(`(${type})`) : ''}`);
return type;
}
for (const projectType of constants_1.PROJECT_TYPES) {
const p = yield createProjectFromType(path.resolve(projectDir, constants_1.PROJECT_FILE), projectName, deps, projectType);
if (yield p.detected()) {
debug(`Project type detected: ${chalk_1.default.bold(prettyProjectName(p.type))} ${p.type ? chalk_1.default.bold(`(${p.type})`) : ''}`);
return p.type;
}
}
const listWrapOptions = { width: format_1.TTY_WIDTH - 8 - 3, indentation: 1 };
// TODO: move some of this to the CLI docs
throw new Error(`Could not determine project type (project config: ${chalk_1.default.bold(format_1.prettyPath(path.resolve(projectDir, constants_1.PROJECT_FILE)))}).\n` +
`- ${format_1.wordWrap(`For ${chalk_1.default.bold(prettyProjectName('angular'))} projects, make sure ${chalk_1.default.green('@ionic/angular')} is listed as a dependency in ${chalk_1.default.bold('package.json')}.`, listWrapOptions)}\n` +
`- ${format_1.wordWrap(`For ${chalk_1.default.bold(prettyProjectName('ionic-angular'))} projects, make sure ${chalk_1.default.green('ionic-angular')} is listed as a dependency in ${chalk_1.default.bold('package.json')}.`, listWrapOptions)}\n` +
`- ${format_1.wordWrap(`For ${chalk_1.default.bold(prettyProjectName('ionic1'))} projects, make sure ${chalk_1.default.green('ionic')} is listed as a dependency in ${chalk_1.default.bold('bower.json')}.`, listWrapOptions)}\n\n` +
`Alternatively, set ${chalk_1.default.bold('type')} attribute in ${chalk_1.default.bold(constants_1.PROJECT_FILE)} to one of: ${constants_1.PROJECT_TYPES.map(v => chalk_1.default.green(v)).join(', ')}.\n\n` +
`If the Ionic CLI does not know what type of project this is, ${chalk_1.default.green('ionic build')}, ${chalk_1.default.green('ionic serve')}, and other commands may not work. You can use the ${chalk_1.default.green('custom')} project type if that's okay.`);
});
}
exports.determineProjectType = determineProjectType;
function createProjectFromType(filePath, name, deps, type) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let project;
if (type === 'angular') {
const { AngularProject } = yield Promise.resolve().then(() => require('./angular'));
project = new AngularProject(filePath, name, deps);
}
else if (type === 'ionic-angular') {
const { IonicAngularProject } = yield Promise.resolve().then(() => require('./ionic-angular'));
project = new IonicAngularProject(filePath, name, deps);
}
else if (type === 'ionic1') {
const { Ionic1Project } = yield Promise.resolve().then(() => require('./ionic1'));
project = new Ionic1Project(filePath, name, deps);
}
else if (type === 'custom') {
const { CustomProject } = yield Promise.resolve().then(() => require('./custom'));
project = new CustomProject(filePath, name, deps);
}
else {
throw new errors_1.FatalException(`Bad project type: ${chalk_1.default.bold(type)}`); // TODO?
}
return project;
});
}
exports.createProjectFromType = createProjectFromType;
class ProjectConfig extends cli_framework_1.BaseConfig {
constructor(p, options) {
super(p, options);
const c = this.c;
// <4.0.0 project config migration
if (typeof c.app_id === 'string') {
if (c.app_id) {
this.set('pro_id', c.app_id);
}
this.unset('app_id');
}
}
provideDefaults() {
return {
name: 'New Ionic App',
integrations: {},
};
}
}
exports.ProjectConfig = ProjectConfig;
class Project {
constructor(
/**
* The file path to the configuration file.
*/
filePath,
/**
* If provided, this is a multi-app project and will be configured to use
* the app identified by this string. Otherwise, this is a single-app
* project.
*/
name, e) {
this.filePath = filePath;
this.name = name;
this.e = e;
this.directory = path.dirname(filePath);
}
get config() {
const options = typeof this.name === 'undefined'
? {}
: { pathPrefix: ['projects', this.name] };
return new ProjectConfig(this.filePath, options);
}
getBuildRunner() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
return yield this.requireBuildRunner();
}
catch (e) {
if (!(e instanceof errors_1.RunnerNotFoundException)) {
throw e;
}
}
});
}
getServeRunner() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
return yield this.requireServeRunner();
}
catch (e) {
if (!(e instanceof errors_1.RunnerNotFoundException)) {
throw e;
}
}
});
}
getGenerateRunner() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
return yield this.requireGenerateRunner();
}
catch (e) {
if (!(e instanceof errors_1.RunnerNotFoundException)) {
throw e;
}
}
});
}
requireProId() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const proId = this.config.get('pro_id');
if (!proId) {
throw new errors_1.FatalException(`Your project file (${chalk_1.default.bold(format_1.prettyPath(this.filePath))}) does not contain '${chalk_1.default.bold('pro_id')}'. ` +
`Run ${chalk_1.default.green('ionic link')}.`);
}
return proId;
});
}
get packageJsonPath() {
return path.resolve(this.directory, 'package.json');
}
getPackageJson(pkgName) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let pkg;
let pkgPath;
try {
pkgPath = pkgName ? node_1.resolve(`${pkgName}/package`, { paths: node_1.compileNodeModulesPaths(this.directory) }) : this.packageJsonPath;
pkg = yield node_1.readPackageJsonFile(pkgPath);
}
catch (e) {
this.e.log.error(`Error loading ${chalk_1.default.bold(pkgName ? pkgName : `project's`)} ${chalk_1.default.bold('package.json')}: ${e}`);
}
return [pkg, pkgPath ? path.dirname(pkgPath) : undefined];
});
}
requirePackageJson(pkgName) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
const pkgPath = pkgName ? node_1.resolve(`${pkgName}/package`, { paths: node_1.compileNodeModulesPaths(this.directory) }) : this.packageJsonPath;
return yield node_1.readPackageJsonFile(pkgPath);
}
catch (e) {
if (e instanceof SyntaxError) {
throw new errors_1.FatalException(`Could not parse ${chalk_1.default.bold(pkgName ? pkgName : `project's`)} ${chalk_1.default.bold('package.json')}. Is it a valid JSON file?`);
}
else if (e === node_1.ERROR_INVALID_PACKAGE_JSON) {
throw new errors_1.FatalException(`The ${chalk_1.default.bold(pkgName ? pkgName : `project's`)} ${chalk_1.default.bold('package.json')} file seems malformed.`);
}
throw e; // Probably file not found
}
});
}
getDocsUrl() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return 'https://ionicframework.com/docs';
});
}
getSourceDir(sourceRoot = 'src') {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return path.resolve(this.directory, sourceRoot);
});
}
getDistDir() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return path.resolve(this.directory, 'www');
});
}
getInfo() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const integrations = yield this.getIntegrations();
const integrationInfo = lodash.flatten(yield Promise.all(integrations.map((i) => tslib_1.__awaiter(this, void 0, void 0, function* () { return i.getInfo(); }))));
return integrationInfo;
});
}
personalize(details) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const { name, projectId, description, version } = details;
this.config.set('name', name);
const pkg = yield this.requirePackageJson();
pkg.name = projectId;
pkg.version = version ? version : '0.0.1';
pkg.description = description ? description : 'An Ionic project';
yield utils_fs_1.writeJsonFile(this.packageJsonPath, pkg, { encoding: 'utf8' });
const integrations = yield this.getIntegrations();
yield Promise.all(integrations.map((i) => tslib_1.__awaiter(this, void 0, void 0, function* () { return i.personalize(details); })));
});
}
registerAilments(registry) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ailments = yield Promise.resolve().then(() => require('../doctor/ailments'));
const deps = Object.assign({}, this.e, { project: this });
registry.register(new ailments.NpmInstalledLocally(deps));
registry.register(new ailments.IonicCLIInstalledLocally(deps));
registry.register(new ailments.GitNotUsed(deps));
registry.register(new ailments.GitConfigInvalid(deps));
registry.register(new ailments.IonicNativeOldVersionInstalled(deps));
registry.register(new ailments.UnsavedCordovaPlatforms(deps));
registry.register(new ailments.DefaultCordovaBundleIdUsed(deps));
registry.register(new ailments.ViewportFitNotSet(deps));
registry.register(new ailments.CordovaPlatformsCommitted(deps));
});
}
createIntegration(name) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return integrations_1.BaseIntegration.createFromName({
config: this.e.config,
project: this,
shell: this.e.shell,
log: this.e.log,
}, name);
});
}
getIntegration(name) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const integration = this.config.get('integrations')[name];
if (!integration) {
throw new errors_1.FatalException(`Could not find ${chalk_1.default.bold(name)} integration in the ${chalk_1.default.bold(this.name ? this.name : 'default')} project.`);
}
return {
enabled: integration.enabled !== false,
root: integration.root === undefined ? this.directory : path.resolve(this.directory, integration.root),
};
});
}
getIntegrations() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const integrationsFromConfig = this.config.get('integrations');
const names = Object.keys(integrationsFromConfig); // TODO
const integrationNames = names.filter(n => {
const c = integrationsFromConfig[n];
return c && c.enabled !== false;
});
const integrations = yield Promise.all(integrationNames.map((name) => tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
return yield this.createIntegration(name);
}
catch (e) {
if (!(e instanceof errors_1.IntegrationNotFoundException)) {
throw e;
}
this.e.log.warn(e.message);
}
})));
return integrations.filter((i) => typeof i !== 'undefined');
});
}
}
exports.Project = Project;
function prettyProjectName(type) {
if (!type) {
return 'Unknown';
}
if (type === 'angular') {
return 'ionic/angular 4';
}
else if (type === 'ionic-angular') {
return 'Ionic Angular 3';
}
else if (type === 'ionic1') {
return 'Ionic 1';
}
return type;
}
exports.prettyProjectName = prettyProjectName;
function prettyProjectTooling(type) {
if (type === 'angular') {
return 'Angular 6+, Angular CLI';
}
else if (type === 'ionic-angular') {
return 'Angular 5, @ionic/app-scripts';
}
else if (type === 'ionic1') {
return 'AngularJS, gulp, sass';
}
return 'unknown tooling';
}
exports.prettyProjectTooling = prettyProjectTooling;
;