Skip to content

Commit 3016623

Browse files
m1gacb1kenobi
andauthored
feat(cli): add Alloy app to create menu (#14387)
* feat(cli): add Alloy app to create menu * refactor code * fix lint issue * fix: add alloy check, move app_alloy to alloy, use numbers in create * fix: try/catch around alloy create * Update cli/commands/create.js Co-authored-by: Chris Barber <chris@cb1inc.com> * Update cli/commands/create.js Co-authored-by: Chris Barber <chris@cb1inc.com> * fix: move check into success branch * move success after error * move success after error * remove duplicated message * optimize success message * fix linter --------- Co-authored-by: Chris Barber <chris@cb1inc.com>
1 parent b48b6d1 commit 3016623

File tree

7 files changed

+286
-181
lines changed

7 files changed

+286
-181
lines changed

cli/commands/create.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class CreateCommand {
6161
const typeConf = {};
6262

6363
for (const filename of fs.readdirSync(creatorDir)) {
64-
if (!jsRegExp.test(filename)) {
64+
if (!jsRegExp.test(filename) || filename === 'base_app.js') {
6565
continue;
6666
}
6767

@@ -99,14 +99,14 @@ export class CreateCommand {
9999
options: Object.assign({
100100
type: {
101101
abbr: 't',
102-
default: cli.argv.prompt ? undefined : 'app',
102+
default: cli.argv.prompt ? undefined : '1',
103103
desc: 'the type of project to create',
104104
order: 100,
105105
prompt: (callback) => {
106106
callback(fields.select({
107107
title: 'What type of project would you like to create?',
108108
promptLabel: 'Select a type by number or name',
109-
default: 'app',
109+
default: '1',
110110
margin: '',
111111
numbered: true,
112112
relistOnError: true,
@@ -148,6 +148,12 @@ export class CreateCommand {
148148
var type = cli.argv.type,
149149
creator = this.creators[type];
150150

151+
let useAlloy = false;
152+
if (creator.type === 'alloy') {
153+
useAlloy = true;
154+
creator.type = 'app';
155+
}
156+
151157
// load the project type lib
152158
logger.info(`Creating ${type.cyan} project`);
153159

@@ -176,14 +182,18 @@ export class CreateCommand {
176182
cli.emit('create.finalize', creator, function () {
177183
if (err) {
178184
logger.error(`Failed to create project after ${appc.time.prettyDiff(cli.startTime, Date.now())}\n`);
185+
} else if (cli.argv.alloy !== undefined || useAlloy) {
186+
try {
187+
const output = execSync(`alloy new "${path.join(cli.argv['workspace-dir'], cli.argv.name)}"`, { encoding: 'utf8' });
188+
(output?.trim() || '').split('\n').forEach(line => logger.info(line));
189+
logger.info(`Project created successfully in ${appc.time.prettyDiff(cli.startTime, Date.now())}\n`);
190+
} catch (_alloyError) {
191+
logger.error('Alloy is not installed. Run "npm i -g alloy" to install it, then run "alloy new" inside the project folder.');
192+
}
179193
} else {
180194
logger.info(`Project created successfully in ${appc.time.prettyDiff(cli.startTime, Date.now())}\n`);
181195
}
182196

183-
if (cli.argv.alloy !== undefined) {
184-
execSync(`alloy new "${path.join(cli.argv['workspace-dir'], cli.argv.name)}"`, { stdio: 'inherit' });
185-
}
186-
187197
finished(err);
188198
});
189199
});

cli/lib/creator.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export class Creator {
6363
* @param {Function} callback - A function to call after the function finishes
6464
*/
6565
run() {
66+
if (this.cli.argv.type === 'alloy') {
67+
// alloy app - reset type to normal app
68+
this.cli.argv.type = 'app';
69+
}
6670
this.projectType = this.cli.argv.type;
6771
this.sdk = this.cli.env.getSDK(this.cli.argv.sdk);
6872
}

cli/lib/creators/app.js

Lines changed: 6 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,14 @@
1010
* Please see the LICENSE included with this distribution for details.
1111
*/
1212

13-
import appc from 'node-appc';
14-
import { Creator } from '../creator.js';
15-
import fs from 'fs-extra';
16-
import path from 'node:path';
17-
import ti from 'node-titanium-sdk';
18-
import { randomUUID } from 'node:crypto';
13+
import { BaseAppCreator } from './base_app.js';
1914

2015
/**
2116
* Creates application projects.
2217
*
2318
* @module lib/creators/app
2419
*/
25-
export class AppCreator extends Creator {
20+
export class AppCreator extends BaseAppCreator {
2621
/**
2722
* Constructs the app creator.
2823
* @class
@@ -33,172 +28,11 @@ export class AppCreator extends Creator {
3328
* @param {Object} cli - The CLI instance
3429
*/
3530
constructor(logger, config, cli) { // eslint-disable-line no-unused-vars
36-
super(logger, config, cli);
37-
38-
this.title = 'Titanium App';
39-
this.titleOrder = 1;
40-
this.type = 'app';
41-
42-
// build list of all valid platforms
43-
const availablePlatforms = {},
44-
validPlatforms = {};
45-
46-
ti.platforms.forEach(function (platform) {
47-
if (/^iphone|ios|ipad$/.test(platform)) {
48-
validPlatforms['iphone'] = availablePlatforms['iphone'] = 1;
49-
validPlatforms['ipad'] = availablePlatforms['ipad'] = 1;
50-
validPlatforms['ios'] = 1;
51-
} else {
52-
validPlatforms[platform] = availablePlatforms[platform] = 1;
53-
}
31+
super(logger, config, cli, {
32+
title: 'Titanium App (Classic)',
33+
titleOrder: 1,
34+
type: 'app'
5435
});
55-
56-
// add "all"
57-
validPlatforms['all'] = 1;
58-
59-
this.availablePlatforms = [ 'all' ].concat(Object.keys(availablePlatforms));
60-
this.validPlatforms = validPlatforms;
61-
}
62-
63-
/**
64-
* Initializes the app creator.
65-
* @return {object}
66-
*/
67-
init() {
68-
return {
69-
options: {
70-
id: this.configOptionId(150),
71-
name: this.configOptionName(140),
72-
platforms: this.configOptionPlatforms(120),
73-
template: this.configOptionTemplate(110),
74-
'workspace-dir': this.configOptionWorkspaceDir(170)
75-
}
76-
};
77-
}
78-
79-
/**
80-
* Creates the project directory and copies the project files.
81-
* @param {Function} callback - A function to call after the project has been created
82-
*/
83-
run(callback) {
84-
super.run();
85-
86-
const argv = this.cli.argv,
87-
platforms = ti.scrubPlatforms(argv.platforms),
88-
projectDir = appc.fs.resolvePath(argv['workspace-dir'], argv.name);
89-
90-
fs.ensureDirSync(projectDir);
91-
92-
// download/install the project template
93-
this.processTemplate(function (err, templateDir) {
94-
if (err) {
95-
return callback(err);
96-
}
97-
98-
let projectConfig = null;
99-
const tasks = [
100-
function (next) {
101-
// copy the template files, if exists
102-
const dir = path.join(templateDir, 'template');
103-
if (!fs.existsSync(dir)) {
104-
next();
105-
} else {
106-
this.logger.info(`Template directory: ${templateDir.cyan}`);
107-
this.copyDir(dir, projectDir, next);
108-
}
109-
},
110-
111-
function (next) {
112-
// create the tiapp.xml
113-
const params = {
114-
id: argv.id,
115-
name: argv.name,
116-
url: argv.url || '',
117-
version: '1.0',
118-
guid: randomUUID(),
119-
'deployment-targets': {},
120-
'sdk-version': this.sdk.name
121-
},
122-
tiappFile = path.join(projectDir, 'tiapp.xml');
123-
124-
if (platforms.original.indexOf('ios') !== -1) {
125-
platforms.original.indexOf('ipad') !== -1 || platforms.original.push('ipad');
126-
platforms.original.indexOf('iphone') !== -1 || platforms.original.push('iphone');
127-
}
128-
129-
ti.availablePlatformsNames.forEach(function (p) {
130-
if (p !== 'ios') {
131-
params['deployment-targets'][p] = platforms.original.indexOf(p) !== -1;
132-
}
133-
});
134-
135-
this.cli.createHook('create.populateTiappXml', this, function (tiapp, params, done) {
136-
// read and populate the tiapp.xml
137-
this.logger.info('Writing tiapp.xml');
138-
projectConfig = appc.util.mix(tiapp, params);
139-
projectConfig.save(tiappFile);
140-
done();
141-
}.bind(this))(fs.existsSync(tiappFile) ? new ti.tiappxml(tiappFile) : new ti.tiappxml(), params, next);
142-
},
143-
144-
function (next) {
145-
// make sure the Resources dir exists
146-
const dir = path.join(projectDir, 'Resources');
147-
fs.ensureDirSync(dir);
148-
next();
149-
}
150-
];
151-
152-
platforms.scrubbed.forEach(function (platform) {
153-
// if we're using the built-in template, load the platform specific template hooks
154-
const usingBuiltinTemplate = templateDir.indexOf(this.sdk.path) === 0,
155-
platformTemplateDir = path.join(this.sdk.path, platform, 'templates', this.projectType, this.cli.argv.template);
156-
157-
if (usingBuiltinTemplate) {
158-
this.cli.scanHooks(path.join(platformTemplateDir, 'hooks'));
159-
}
160-
161-
tasks.push(function (next) {
162-
this.cli.emit([
163-
'create.pre.platform.' + platform,
164-
'create.pre.' + this.projectType + '.platform.' + platform
165-
], this, async function (err) {
166-
if (err) {
167-
return next(err);
168-
}
169-
170-
const finalize = () => {
171-
this.cli.emit([
172-
'create.post.' + this.projectType + '.platform.' + platform,
173-
'create.post.platform.' + platform
174-
], this, next);
175-
};
176-
177-
// legacy... only copy platform specific files if we're copying from a built-in template
178-
if (!usingBuiltinTemplate) {
179-
return finalize();
180-
}
181-
182-
const p = path.join(this.sdk.path, platform, 'cli', 'commands', '_create.js');
183-
if (fs.existsSync(p)) {
184-
this.logger.info(`Copying ${platform.cyan} platform resources`);
185-
const { run } = await import(p);
186-
await run(this.logger, this.config, this.cli, projectConfig);
187-
return finalize();
188-
}
189-
190-
// does this platform have new or old style implementations?
191-
const templatePath = path.join(platformTemplateDir, 'template');
192-
if (!fs.existsSync(templatePath)) {
193-
return finalize();
194-
}
195-
this.copyDir(templatePath, projectDir, finalize);
196-
}.bind(this));
197-
});
198-
}, this);
199-
200-
appc.async.series(this, tasks, callback);
201-
}.bind(this));
20236
}
20337
}
20438

cli/lib/creators/app_alloy.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @overview
3+
* Logic for creating new Titanium apps.
4+
*
5+
* @copyright
6+
* Copyright TiDev, Inc. 04/07/2022-Present
7+
*
8+
* @license
9+
* Licensed under the terms of the Apache Public License
10+
* Please see the LICENSE included with this distribution for details.
11+
*/
12+
13+
import { BaseAppCreator } from './base_app.js';
14+
import { execSync } from 'node:child_process';
15+
16+
/**
17+
* Creates application projects.
18+
*
19+
* @module lib/creators/app
20+
*/
21+
export class AppCreator extends BaseAppCreator {
22+
/**
23+
* Constructs the app creator.
24+
* @class
25+
* @classdesc Creates an app project.
26+
* @constructor
27+
* @param {Object} logger - The logger instance
28+
* @param {Object} config - The CLI config
29+
* @param {Object} cli - The CLI instance
30+
*/
31+
constructor(logger, config, cli) { // eslint-disable-line no-unused-vars
32+
super(logger, config, cli, {
33+
title: 'Titanium App (Alloy)',
34+
titleOrder: 2,
35+
type: 'alloy'
36+
});
37+
}
38+
39+
init() {
40+
// check if alloy is installed
41+
try {
42+
execSync('alloy --version');
43+
} catch (err) {
44+
throw new Error('Alloy is not installed');
45+
}
46+
return super.init();
47+
}
48+
}
49+
50+
export default AppCreator;

cli/lib/creators/applewatch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class AppleWatchCreator extends Creator {
4242
super(logger, config, cli);
4343

4444
this.title = 'Apple Watch™ App';
45-
this.titleOrder = 3;
45+
this.titleOrder = 4;
4646
this.type = 'applewatch';
4747
}
4848

0 commit comments

Comments
 (0)