UNPKG

@ionic/cli-utils

Version:
338 lines (337 loc) 15.8 kB
"use strict"; 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;