This commit is contained in:
Tobe O 2017-10-19 21:06:15 -04:00
parent 7c27a329e5
commit bff1f6aed1
8 changed files with 306 additions and 29 deletions

View file

@ -4,21 +4,11 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/bin/packages" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/.pub" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/bin/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/build" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/test/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/test/services/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/tool/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/web/images/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/web/packages" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
<excludeFolder url="file://$MODULE_DIR$/tool/packages" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

View file

@ -17,6 +17,7 @@ main(List<String> args) async {
..addCommand(new KeyCommand())
..addCommand(new ServiceCommand())
..addCommand(new InitCommand())
..addCommand(new InstallCommand())
..addCommand(new TestCommand())
..addCommand(new PluginCommand())
..addCommand(new StartCommand())

View file

@ -4,6 +4,7 @@ export 'controller.dart';
export "doctor.dart";
export "key.dart";
export "init.dart";
export "install.dart";
export "make.dart";
export "plugin.dart";
export "rename.dart";

View file

@ -19,7 +19,14 @@ class InitCommand extends Command {
"Initializes a new Angel project in the current directory.";
InitCommand() {
argParser.addFlag('pub-get', defaultsTo: true);
argParser
..addFlag('pub-get', defaultsTo: true)
..addFlag(
'legacy',
help:
'Generate a project using Angel 1.0.x boilerplates, rather than 1.1.x+.',
negatable: false,
);
}
@override
@ -60,7 +67,7 @@ class InitCommand extends Command {
..text('\nCongratulations! You are ready to start developing with Angel!')
..text('\nTo start the server (with file watching), run ')
..magenta()
..text('`dart bin/server.dart`')
..text(argResults['legacy'] ? '`dart bin/server.dart`' : '`dart bin/dev.dart`')
..normal()
..text(' in your terminal.')
..text('\n\nFind more documentation about Angel:')
@ -122,7 +129,9 @@ class InitCommand extends Command {
}
print('Choose a project type before continuing:');
var boilerplateChooser = new Chooser<BoilerplateInfo>(allBoilerplates);
var boilerplateChooser = new Chooser<BoilerplateInfo>(
argResults['legacy'] ? legacyBoilerplates : boilerplates,
);
var boilerplate = await boilerplateChooser.choose();
print(
@ -137,6 +146,17 @@ class InitCommand extends Command {
throw new Exception("Could not clone repo.");
}
if (boilerplate.ref != null) {
git = await Process.start("git", ["checkout", boilerplate.ref]);
stdout.addStream(git.stdout);
stderr.addStream(git.stderr);
if (await git.exitCode != 0) {
throw new Exception("Could not checkout branch ${boilerplate.ref}.");
}
}
var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git"));
if (await gitDir.exists()) await gitDir.delete(recursive: true);
} catch (e) {
@ -176,30 +196,45 @@ preBuild(Directory projectDir) async {
}
const BoilerplateInfo fullApplicationBoilerplate = const BoilerplateInfo(
'Full Application',
'A complete project including authentication, multi-threading, and more.',
'https://github.com/angel-dart/angel.git');
'Full Application',
'A complete project including authentication, multi-threading, and more.',
'https://github.com/angel-dart/angel.git',
ref: '1.0.x',
);
const BoilerplateInfo lightBoilerplate = const BoilerplateInfo(
'Light',
'Minimal starting point for new users.',
'https://github.com/angel-dart/boilerplate_light.git');
'Light',
'Minimal starting point for new users.',
'https://github.com/angel-dart/boilerplate_light.git',
);
const BoilerplateInfo ormBoilerplate = const BoilerplateInfo(
'ORM',
"A starting point for applications that use Angel's ORM.",
'https://github.com/angel-dart/boilerplate_orm.git');
'ORM',
"A starting point for applications that use Angel's ORM.",
'https://github.com/angel-dart/boilerplate_orm.git',
);
const List<BoilerplateInfo> allBoilerplates = const [
const List<BoilerplateInfo> legacyBoilerplates = const [
fullApplicationBoilerplate,
lightBoilerplate,
ormBoilerplate
];
class BoilerplateInfo {
final String name, description, url;
const BoilerplateInfo basicBoilerplate = const BoilerplateInfo(
'Basic',
'Minimal starting point - A simple server with only a few additional packages.',
'https://github.com/angel-dart/angel.git',
);
const BoilerplateInfo(this.name, this.description, this.url);
const List<BoilerplateInfo> boilerplates = const [
basicBoilerplate,
ormBoilerplate,
];
class BoilerplateInfo {
final String name, description, url, ref;
const BoilerplateInfo(this.name, this.description, this.url, {this.ref});
@override
String toString() => '$name ($description)';

View file

@ -0,0 +1,245 @@
import 'dart:async';
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:console/console.dart';
import 'package:glob/glob.dart';
import 'package:homedir/homedir.dart';
import 'package:mustache4dart/mustache4dart.dart' as mustache;
import 'package:path/path.dart' as p;
import 'package:pubspec/pubspec.dart';
import 'package:yaml/yaml.dart' as yaml;
import 'make/maker.dart';
class InstallCommand extends Command {
static const String repo = 'https://github.com/angel-dart/install.git';
static final Directory installRepo =
new Directory.fromUri(homeDir.uri.resolve('./.angel/addons'));
@override
String get name => 'install';
@override
String get description =>
'Installs additional add-ons to minimize boilerplate.';
InstallCommand() {
argParser
..addFlag(
'list',
help: 'List all currently-installed add-ons.',
negatable: false,
defaultsTo: false,
)
..addFlag(
'update',
help: 'Update the local add-on repository.',
negatable: false,
defaultsTo: false,
)
..addFlag(
'wipe',
help: 'Wipe the local add-on repository.',
negatable: false,
defaultsTo: false,
);
}
@override
run() async {
if (argResults['wipe']) {
if (await installRepo.exists()) await installRepo.delete();
} else if (argResults['list']) {
var addons = await list();
print('${addons.length} add-on(s) installed:');
for (var addon in addons) {
print(' * ${addon.name}@${addon.version}: ${addon.description}');
}
} else if (argResults['update']) {
await update();
} else if (argResults.rest.isNotEmpty) {
if (!await installRepo.exists())
throw 'No local add-on database exists. Run `angel install --update` first.';
var pubspec = await PubSpec.load(Directory.current);
for (var packageName in argResults.rest) {
var packageDir =
new Directory.fromUri(installRepo.uri.resolve(packageName));
if (!await packageDir.exists())
throw 'No add-on named "$packageName" is installed. You might need to run `angel install --update`.';
Map<String, dynamic> values = {
'project_name': pubspec.name,
'pubspec': pubspec,
};
List<Glob> globs = [];
var promptFile =
new File.fromUri(packageDir.uri.resolve('angel_cli.yaml'));
var projectPubspec = await PubSpec.load(packageDir);
if (await promptFile.exists()) {
var contents = await promptFile.readAsString();
var y = yaml.loadYamlDocument(contents);
var cfg = y.contents.value as Map<String, dynamic>;
// Loads globs
if (cfg['templates'] is List) {
globs.addAll(cfg['templates'].map((p) => new Glob(p)));
}
if (cfg['values'] is Map) {
var val = cfg['values'] as Map<String, dynamic>;
for (var key in val.keys) {
var desc = val[key]['description'] ?? key;
if (val[key]['type'] == 'prompt') {
Prompter prompt;
if (val[key]['default'] != null) {
prompt = new Prompter('$desc (${val[key]['default']}): ');
} else {
prompt = new Prompter('$desc: ');
}
if (val[key]['default'] != null) {
var v = await prompt.prompt();
v = v.isNotEmpty ? v : val[key]['default'];
values[key] = v;
} else
values[key] =
await prompt.prompt(checker: (s) => s.isNotEmpty);
} else if (val[key]['type'] == 'choice') {
var chooser =
new Chooser(val[key]['choices'], message: '$desc: ');
values[key] = await chooser.choose();
}
}
}
var deps = projectPubspec.dependencies.keys.map((k) {
var dep = projectPubspec.dependencies[k];
if (dep is HostedReference)
return new MakerDependency(k, dep.versionConstraint.toString());
return null;
}).where((d) => d != null);
await depend(deps);
}
Future merge(Directory src, Directory dst, String prefix) async {
if (!await src.exists()) return;
print('Copying ${src.absolute.path} into ${dst.absolute.path}...');
if (!await dst.exists()) await dst.create(recursive: true);
await for (var entity in src.list()) {
if (entity is Directory) {
var name = p.basename(entity.path);
var newDir = new Directory.fromUri(dst.uri.resolve(name));
await merge(
entity, newDir, prefix.isEmpty ? name : '$prefix/$name');
} else if (entity is File &&
!entity.path.endsWith('angel_cli.yaml')) {
var name = p.basename(entity.path);
var target = dst.uri.resolve(name);
var targetFile = new File.fromUri(target);
bool allClear = !await targetFile.exists();
if (!allClear) {
print('The file ${entity.absolute.path} already exists.');
var p = new Prompter('Overwrite the existing file? [y/N]');
var answer = await p.prompt(
checker: (s) => s.trim() == 'y' || s.trim() == 'N');
allClear = answer == 'y';
if (allClear) await targetFile.delete();
}
if (allClear) {
try {
var path = prefix.isEmpty ? name : '$prefix/$name';
if (globs.any((g) => g.matches(path))) {
print('Rendering Mustache template from ${entity.absolute
.path} to ${targetFile.absolute.path}...');
var contents = await entity.readAsString();
var renderer = mustache.compile(contents);
var generated = renderer(values);
await targetFile.writeAsString(generated);
} else {
print(
'Copying ${entity.absolute.path} to ${targetFile.absolute.path}...');
await targetFile.parent.create(recursive: true);
await entity.copy(targetFile.absolute.path);
}
} catch (_) {
print('Failed to copy.');
}
} else {
print('Skipped ${entity.absolute.path}.');
}
}
}
}
await merge(new Directory.fromUri(packageDir.uri.resolve('files')),
Directory.current, '');
print('Successfully installed $packageName@${projectPubspec.version}.');
}
} else {
print('No add-ons were specified to be installed.');
}
}
Future<List<PubSpec>> list() async {
if (!await installRepo.exists()) {
throw 'No local add-on database exists. Run `angel install --update` first.';
} else {
List<PubSpec> repos = [];
await for (var entity in installRepo.list()) {
if (entity is Directory) {
try {
repos.add(await PubSpec.load(entity));
} catch (_) {
// Ignore failures...
}
}
}
return repos;
}
}
Future update() async {
Process git;
if (!await installRepo.exists()) {
git = await Process.start('git', [
'clone',
repo,
installRepo.absolute.path,
]);
} else {
git = await Process.start(
'git',
[
'pull',
'origin',
'master',
],
workingDirectory: installRepo.absolute.path,
);
}
git..stdout.listen(stdout.add)..stderr.listen(stderr.add);
var code = await git.exitCode;
if (code != 0) {
throw 'git exited with code $code.';
}
}
}

View file

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:http/src/base_client.dart' as http;
import 'package:pub_semver/pub_semver.dart';
final Version PACKAGE_VERSION = new Version(1, 1, 5, build: '2');
final Version PACKAGE_VERSION = new Version(1, 2, 0);
Future<Version> fetchCurrentVersion(http.BaseClient client) async {
var response =
await client.get('https://pub.dartlang.org/api/packages/angel_cli');

View file

@ -21,7 +21,10 @@ class MongoServiceGenerator extends ServiceGenerator {
@override
void applyToLibrary(LibraryBuilder library, String name, String lower) {
library.addMember(new ImportBuilder('package:mongo_dart/mongo_dart.dart'));
library.addMembers([
new ImportBuilder('package:angel_mongo/angel_mongo.dart'),
new ImportBuilder('package:mongo_dart/mongo_dart.dart'),
]);
}
@override

View file

@ -2,7 +2,7 @@ author: "Tobe O <thosakwe@gmail.com>"
description: "Command-line tools for the Angel framework."
homepage: "https://github.com/angel-dart/angel_cli"
name: "angel_cli"
version: 1.1.5+2
version: 1.2.0
dependencies:
# analyzer: "^0.29.0"
args: ^0.13.4
@ -10,9 +10,11 @@ dependencies:
console: "^2.2.3"
dart_style: ^1.0.0
glob: "^1.1.0"
homedir: ^0.0.4
http: ^0.11.3
id: "^1.0.0"
inflection: "^0.4.1"
mustache4dart: ^2.1.0
pubspec: "^0.0.14"
random_string: "^0.0.1"
recase: "^1.0.0"