Skip to content

Commit bd6afb0

Browse files
committed
Merge commit '51be9ac68e4e8d8f9771fa1fb37ae17e1917d305'
2 parents 59048ef + 51be9ac commit bd6afb0

9 files changed

Lines changed: 154 additions & 63 deletions

File tree

packages/@ionic/cli-framework/src/utils/npm.ts

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,58 +34,102 @@ export function compileNodeModulesPaths(filePath: string): string[] {
3434
* Poorly implemented shim for Node 8+ `require.resolve()`, with `paths`
3535
* option.
3636
*
37+
* Only supports use case 4: LOAD_NODE_MODULES
38+
*
3739
* @see https://nodejs.org/docs/latest-v8.x/api/modules.html#modules_require_resolve_request_options
3840
* @see https://nodejs.org/docs/latest-v8.x/api/modules.html#modules_all_together
3941
*/
40-
export function resolve(pkg: string, options?: { paths?: string[] }): string {
42+
export function resolve(m: string, options?: { paths?: string[] }): string {
4143
const paths = options && options.paths ? options.paths : undefined;
4244

4345
if (!paths) {
4446
// There is absolutely no reason to use this shim without the `paths`
4547
// option--use the built-in resolver.
46-
return require.resolve(pkg);
48+
return require.resolve(m);
4749
}
4850

49-
const safeStat = (filePath: string): fs.Stats | undefined => {
50-
try {
51-
return fs.statSync(filePath);
52-
} catch (e) {
53-
// ignore
51+
// LOAD_NODE_MODULES
52+
for (const p of paths) {
53+
const filePath = path.join(p, m);
54+
const foundPathAsFile = resolve.LOAD_AS_FILE(filePath);
55+
56+
if (foundPathAsFile) {
57+
return foundPathAsFile;
5458
}
55-
};
5659

57-
for (const p of paths) {
58-
const filePath = path.join(p, pkg);
60+
const foundPathAsDirectory = resolve.LOAD_AS_DIRECTORY(filePath);
5961

60-
const filePaths = [filePath, filePath + '.js', filePath + '.json'];
61-
const stats = filePaths.map((p): [string, fs.Stats | undefined] => [p, safeStat(p)]);
62-
const found = stats.find(([p, s]) => typeof s !== 'undefined' && s.isFile());
62+
if (foundPathAsDirectory) {
63+
return foundPathAsDirectory;
64+
}
65+
}
6366

64-
if (found) {
65-
return found[0];
67+
const err: NodeJS.ErrnoException = new Error(`Cannot find module '${m}'`);
68+
err.code = 'MODULE_NOT_FOUND';
69+
throw err;
70+
}
71+
72+
export namespace resolve {
73+
export function LOAD_AS_FILE(x: string): string | undefined {
74+
const exts = ['', '.js', '.json'];
75+
76+
for (const ext of exts) {
77+
const p = x + ext;
78+
const stat = safeStatSync(p);
79+
80+
if (stat && stat.isFile()) {
81+
return p;
82+
}
6683
}
84+
}
6785

68-
const [ [ , stat ] ] = stats;
86+
export function LOAD_INDEX(x: string): string | undefined {
87+
const exts = ['.js', '.json'];
6988

70-
if (stat && stat.isDirectory()) {
71-
try {
72-
const packageJson = JSON.parse(fs.readFileSync(path.join(filePath, 'package.json'), { encoding: 'utf8' }));
89+
for (const ext of exts) {
90+
const p = path.join(x, `index${ext}`);
91+
const stat = safeStatSync(p);
7392

74-
if (packageJson.main) {
75-
const mainPath = path.join(filePath, packageJson.main);
76-
const mainStat = safeStat(mainPath);
93+
if (stat && stat.isFile()) {
94+
return p;
95+
}
96+
}
97+
}
98+
99+
export function LOAD_AS_DIRECTORY(x: string): string | undefined {
100+
try {
101+
const packageJson = JSON.parse(fs.readFileSync(path.join(x, 'package.json'), { encoding: 'utf8' }));
102+
103+
if (packageJson.main) {
104+
const m = path.join(x, packageJson.main);
105+
const foundPathAsFile = resolve.LOAD_AS_FILE(m);
106+
107+
if (foundPathAsFile) {
108+
return foundPathAsFile;
109+
}
110+
111+
const foundPathAsIndex = resolve.LOAD_INDEX(m);
77112

78-
if (mainStat && mainStat.isFile()) {
79-
return mainPath;
80-
}
113+
if (foundPathAsIndex) {
114+
return foundPathAsIndex;
81115
}
82-
} catch (e) {
83-
// ignore
84116
}
117+
} catch (e) {
118+
// ignore fs and json errors
119+
}
120+
121+
const foundPath = resolve.LOAD_INDEX(x);
122+
123+
if (foundPath) {
124+
return foundPath;
85125
}
86126
}
127+
}
87128

88-
const err: NodeJS.ErrnoException = new Error(`Cannot find module '${pkg}'`);
89-
err.code = 'MODULE_NOT_FOUND';
90-
throw err;
129+
function safeStatSync(filePath: string): fs.Stats | undefined {
130+
try {
131+
return fs.statSync(filePath);
132+
} catch (e) {
133+
// ignore
134+
}
91135
}

packages/@ionic/cli-utils/src/lib/doctor/ailments/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { AppClient } from '../../app';
1212
import { getIonicRemote, isRepoInitialized } from '../../git';
1313
import { pkgFromRegistry, pkgManagerArgs } from '../../utils/npm';
1414
import { getPlatforms } from '../../integrations/cordova/project';
15-
import { ConfigXml } from '../../integrations/cordova/config';
15+
import { loadConfigXml } from '../../integrations/cordova/config';
1616

1717
import { Ailment, AutomaticallyTreatableAilment, AutomaticallyTreatableAilmentDeps } from './base';
1818
export * from './base';
@@ -357,7 +357,7 @@ class UnsavedCordovaPlatforms extends Ailment {
357357
}
358358

359359
const platforms = await getPlatforms(this.project.directory);
360-
const conf = await ConfigXml.load(this.project.directory);
360+
const conf = await loadConfigXml({ project: this.project });
361361
const engines = conf.getPlatformEngines();
362362
const engineNames = new Set([...engines.map(e => e.name)]);
363363

@@ -392,7 +392,7 @@ class DefaultCordovaBundleIdUsed extends Ailment {
392392
return false;
393393
}
394394

395-
const conf = await ConfigXml.load(this.project.directory);
395+
const conf = await loadConfigXml({ project: this.project });
396396

397397
return conf.getBundleId() === 'io.ionic.starter';
398398
}

packages/@ionic/cli-utils/src/lib/integrations/cordova/config.ts

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import * as path from 'path';
22

3+
import chalk from 'chalk';
34
import * as et from 'elementtree';
4-
5-
import { ResourcesPlatform } from '../../../definitions';
5+
import * as Debug from 'debug';
66
import { fsReadFile, fsWriteFile } from '@ionic/cli-framework/utils/fs';
7+
import { prettyPath } from '@ionic/cli-framework/utils/format';
8+
9+
import { IProject, ResourcesPlatform } from '../../../definitions';
10+
import { FatalException } from '../../errors';
11+
12+
const debug = Debug('ionic:cli-utils:lib:integrations:cordova:config');
713

814
export interface PlatformEngine {
915
name: string;
@@ -12,42 +18,42 @@ export interface PlatformEngine {
1218
}
1319

1420
export class ConfigXml {
15-
protected _filePath?: string;
1621
protected _doc?: et.ElementTree;
1722
protected saving = false;
1823

24+
constructor(public filePath: string) {}
25+
1926
get doc() {
2027
if (!this._doc) {
21-
throw new Error('No doc loaded. Call load() properly.');
28+
throw new Error('No doc loaded.');
2229
}
2330

2431
return this._doc;
2532
}
2633

27-
get filePath() {
28-
if (!this._filePath) {
29-
throw new Error('No file path given. Call load() properly.');
34+
static async load(filePath: string): Promise<ConfigXml> {
35+
if (!filePath) {
36+
throw new Error('Must supply file path.');
3037
}
3138

32-
return this._filePath;
33-
}
39+
const conf = new ConfigXml(filePath);
40+
await conf.reload();
3441

35-
static async load(projectDir: string): Promise<ConfigXml> {
36-
if (!projectDir) {
37-
throw new Error('Must supply project directory.');
38-
}
42+
return conf;
43+
}
3944

40-
const conf = new ConfigXml();
41-
conf._filePath = path.join(projectDir, 'config.xml');
42-
const configFileContents = await fsReadFile(conf.filePath, { encoding: 'utf8' });
45+
async reload(): Promise<void> {
46+
const configFileContents = await fsReadFile(this.filePath, { encoding: 'utf8' });
4347

4448
if (!configFileContents) {
4549
throw new Error(`Cannot load empty config.xml file.`);
4650
}
4751

48-
conf._doc = et.parse(configFileContents);
49-
50-
return conf;
52+
try {
53+
this._doc = et.parse(configFileContents);
54+
} catch (e) {
55+
throw new Error(`Cannot parse config.xml file: ${e.stack ? e.stack : e}`);
56+
}
5157
}
5258

5359
async save(): Promise<void> {
@@ -267,3 +273,14 @@ export class ConfigXml {
267273
return { name: name ? name : '', spec: spec ? spec : '', ...engine.attrib };
268274
}
269275
}
276+
277+
export async function loadConfigXml({ project }: { project: IProject }): Promise<ConfigXml> {
278+
const filePath = path.resolve(project.directory, 'config.xml');
279+
debug(`Using config.xml: ${filePath}`);
280+
281+
try {
282+
return await ConfigXml.load(filePath);
283+
} catch (e) {
284+
throw new FatalException(`Cannot load ${chalk.bold(prettyPath(filePath))}: ${e.stack ? e.stack : e}`);
285+
}
286+
}

packages/@ionic/cli-utils/src/lib/integrations/cordova/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ export class Integration extends BaseIntegration {
103103
}
104104

105105
async personalize({ appName, bundleId }: ProjectPersonalizationDetails) {
106-
const { ConfigXml } = await import('./config');
107-
const conf = await ConfigXml.load(this.project.directory);
106+
const { loadConfigXml } = await import('./config');
107+
const conf = await loadConfigXml({ project: this.project });
108108
conf.setName(appName);
109109

110110
if (bundleId) {

packages/@ionic/cli-utils/src/lib/project/angular/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,23 @@ export class Project extends BaseProject {
2424
ionicAngularVersion,
2525
ionicCoreVersion,
2626
angularCLIVersion,
27+
angularDevKitCoreVersion,
28+
angularDevKitSchematicsVersion,
2729
] = await Promise.all([
2830
this.getFrameworkVersion(),
2931
this.getCoreVersion(),
3032
this.getAngularCLIVersion(),
33+
this.getAngularDevKitCoreVersion(),
34+
this.getAngularDevKitCoreVersion(),
3135
]);
3236

3337
return [
3438
...(await super.getInfo()),
3539
{ type: 'local-packages', key: 'Ionic Framework', value: ionicAngularVersion ? `@ionic/angular ${ionicAngularVersion}` : 'not installed' },
3640
{ type: 'local-packages', key: '@ionic/core', value: ionicCoreVersion ? ionicCoreVersion : 'not installed' },
3741
{ type: 'local-packages', key: '@angular/cli', value: angularCLIVersion ? angularCLIVersion : 'not installed' },
42+
{ type: 'local-packages', key: '@angular-devkit/core', value: angularDevKitCoreVersion ? angularDevKitCoreVersion : 'not installed' },
43+
{ type: 'local-packages', key: '@angular-devkit/schematics', value: angularDevKitSchematicsVersion ? angularDevKitSchematicsVersion : 'not installed' },
3844
];
3945
}
4046

@@ -115,4 +121,28 @@ export class Project extends BaseProject {
115121
this.log.error(`Error loading ${chalk.bold(pkgName)} package: ${e}`);
116122
}
117123
}
124+
125+
async getAngularDevKitCoreVersion() {
126+
const pkgName = '@angular-devkit/core';
127+
128+
try {
129+
const pkgPath = resolve(`${pkgName}/package`, { paths: compileNodeModulesPaths(this.directory) });
130+
const pkg = await readPackageJsonFile(pkgPath);
131+
return pkg.version;
132+
} catch (e) {
133+
this.log.error(`Error loading ${chalk.bold(pkgName)} package: ${e}`);
134+
}
135+
}
136+
137+
async getAngularDevKitSchematicsVersion() {
138+
const pkgName = '@angular-devkit/schematics';
139+
140+
try {
141+
const pkgPath = resolve(`${pkgName}/package`, { paths: compileNodeModulesPaths(this.directory) });
142+
const pkg = await readPackageJsonFile(pkgPath);
143+
return pkg.version;
144+
} catch (e) {
145+
this.log.error(`Error loading ${chalk.bold(pkgName)} package: ${e}`);
146+
}
147+
}
118148
}

packages/ionic/src/commands/cordova/base.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export abstract class CordovaCommand extends Command {
101101
}
102102

103103
async preRunChecks(runinfo: CommandInstanceInfo) {
104-
const { ConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
104+
const { loadConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
105105

106106
await this.checkCordova(runinfo);
107107

@@ -117,7 +117,7 @@ export abstract class CordovaCommand extends Command {
117117
}
118118
}
119119

120-
const conf = await ConfigXml.load(this.env.project.directory);
120+
const conf = await loadConfigXml({ project: this.env.project });
121121
conf.resetContentSrc();
122122
await conf.save();
123123
}

packages/ionic/src/commands/cordova/resources.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ This command uses Ionic servers, so we require you to be logged into your free I
9595
}
9696

9797
async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise<void> {
98-
const { ConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
98+
const { loadConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
9999
const { getPlatforms, installPlatform } = await import('@ionic/cli-utils/lib/integrations/cordova/project');
100100

101101
const {
@@ -112,7 +112,7 @@ This command uses Ionic servers, so we require you to be logged into your free I
112112
const [ platform ] = inputs;
113113
const { force } = options;
114114

115-
let conf = await ConfigXml.load(this.env.project.directory);
115+
const conf = await loadConfigXml({ project: this.env.project });
116116

117117
// if no resource filters are passed as arguments assume to use all.
118118
let resourceTypes = AVAILABLE_RESOURCE_TYPES.filter((type, index, array) => options[type]);
@@ -135,7 +135,7 @@ This command uses Ionic servers, so we require you to be logged into your free I
135135

136136
if (confirm) {
137137
await installPlatform(this.env, platform);
138-
conf = await ConfigXml.load(this.env.project.directory);
138+
await conf.reload();
139139
platforms = await getPlatforms(this.env.project.directory);
140140
debug(`platforms=${platforms.map(e => chalk.bold(e)).join(', ')}`);
141141
} else {

packages/ionic/src/commands/cordova/run.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,9 @@ ${chalk.cyan('[1]')}: ${chalk.bold('https://ionicframework.com/docs/developer-re
174174
}
175175

176176
async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise<void> {
177-
const { ConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
177+
const { loadConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
178178

179-
const conf = await ConfigXml.load(this.env.project.directory);
179+
const conf = await loadConfigXml({ project: this.env.project });
180180

181181
onBeforeExit(async () => {
182182
conf.resetContentSrc();

packages/ionic/src/commands/monitoring/syncmaps.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ By default, ${chalk.green('ionic monitoring syncmaps')} will upload the sourcema
4545
const [ snapshotId ] = inputs;
4646
const doBuild = options.build ? true : false;
4747

48-
const { ConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
49-
const conf = await ConfigXml.load(this.env.project.directory);
48+
const { loadConfigXml } = await import('@ionic/cli-utils/lib/integrations/cordova/config');
49+
const conf = await loadConfigXml({ project: this.env.project });
5050
const cordovaInfo = conf.getProjectInfo();
5151

5252
const appVersion = cordovaInfo.version;

0 commit comments

Comments
 (0)