From c0bf58894ccb342feb3a2789211026f9700388bf Mon Sep 17 00:00:00 2001 From: regiostech Date: Tue, 5 Jul 2016 17:19:02 -0400 Subject: [PATCH 001/132] It begins! --- .gitignore | 74 ++++++++++++++++++++++++++++++++++++++++++++++ .idea/vcs.xml | 6 ++++ LICENSE | 21 +++++++++++++ bin/angel.dart | 7 +++++ lib/angel_cli.dart | 4 +++ lib/src/args.dart | 6 ++++ pubspec.yaml | 11 +++++++ 7 files changed, 129 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/vcs.xml create mode 100644 LICENSE create mode 100644 bin/angel.dart create mode 100644 lib/angel_cli.dart create mode 100644 lib/src/args.dart create mode 100644 pubspec.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..fa6ea1da --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Dart template +# See https://www.dartlang.org/tools/private-files.html + +# Files and directories created by pub +.buildlog +.packages +.project +.pub/ +build/ +**/packages/ + +# Files created by dart2js +# (Most Dart developers will use pub build to compile Dart, use/modify these +# rules if you intend to use dart2js directly +# Convention is to use extension '.dart.js' for Dart compiled to Javascript to +# differentiate from explicit Javascript files) +*.dart.js +*.part.js +*.js.deps +*.js.map +*.info.json + +# Directory created by dartdoc +doc/api/ + +# Don't commit pubspec lock file +# (Library packages only! Remove pattern if developing an application package) +pubspec.lock diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..63b4b681 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart new file mode 100644 index 00000000..2379afab --- /dev/null +++ b/bin/angel.dart @@ -0,0 +1,7 @@ +library angel_cli.tool; + +import 'package:angel_cli/angel_cli.dart'; + +main(List args) { + var parser = makeParser(); +} \ No newline at end of file diff --git a/lib/angel_cli.dart b/lib/angel_cli.dart new file mode 100644 index 00000000..af96721c --- /dev/null +++ b/lib/angel_cli.dart @@ -0,0 +1,4 @@ +library angel_cli; + +import 'package:args/args.dart'; +part 'src/args.dart'; \ No newline at end of file diff --git a/lib/src/args.dart b/lib/src/args.dart new file mode 100644 index 00000000..b8ac1483 --- /dev/null +++ b/lib/src/args.dart @@ -0,0 +1,6 @@ +part of angel_cli; + +ArgParser makeParser() { + var result = new ArgParser(allowTrailingOptions: true); + return result; +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..eac413f7 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,11 @@ +name: angel_cli +version: 1.0.0-dev +description: Command-line tools for the Angel framework. +author: Tobe O +homepage: https://github.com/angel-dart/angel_cli +dependencies: + args: ">=0.3.4 <1.0.0" + colorize: ">=0.1.2 <1.0.0" + console: ">=2.2.3 <3.0.0" + mustache4dart: ">=1.0.0 <3.0.0" + yaml: ">=2.0.0 <3.0.0" \ No newline at end of file From 99d2cc3c2e60c30eabd5dc351af422172fff0ff8 Mon Sep 17 00:00:00 2001 From: regiostech Date: Tue, 5 Jul 2016 17:20:01 -0400 Subject: [PATCH 002/132] Shebang --- bin/angel.dart | 2 ++ pubspec.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/bin/angel.dart b/bin/angel.dart index 2379afab..fe769e7f 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -1,3 +1,5 @@ +#!/usr/bin/env dart + library angel_cli.tool; import 'package:angel_cli/angel_cli.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index eac413f7..f1f1971a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,6 +3,8 @@ version: 1.0.0-dev description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli +executables: + angel: angel dependencies: args: ">=0.3.4 <1.0.0" colorize: ">=0.1.2 <1.0.0" From 166974ff975092d1288fd30d8d4073fd3167e6bf Mon Sep 17 00:00:00 2001 From: regiostech Date: Tue, 5 Jul 2016 17:20:45 -0400 Subject: [PATCH 003/132] README --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..5f33cd5b --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# angel_cli +Command-line tools for the Angel framework. \ No newline at end of file From 63d8359faba6eb55477a6cc5ede3c5f35b710716 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 14 Sep 2016 00:33:28 -0400 Subject: [PATCH 004/132] Lookin good --- .idea/runConfigurations/Doctor.xml | 7 ++++ .idea/runConfigurations/Service.xml | 7 ++++ .idea/runConfigurations/Show_Help.xml | 7 ++++ bin/angel.dart | 19 ++++++++-- lib/angel_cli.dart | 3 +- lib/src/args.dart | 6 ---- lib/src/commands/commands.dart | 4 +++ lib/src/commands/doctor.dart | 38 ++++++++++++++++++++ lib/src/commands/service.dart | 51 +++++++++++++++++++++++++++ pubspec.yaml | 3 +- 10 files changed, 133 insertions(+), 12 deletions(-) create mode 100644 .idea/runConfigurations/Doctor.xml create mode 100644 .idea/runConfigurations/Service.xml create mode 100644 .idea/runConfigurations/Show_Help.xml delete mode 100644 lib/src/args.dart create mode 100644 lib/src/commands/commands.dart create mode 100644 lib/src/commands/doctor.dart create mode 100644 lib/src/commands/service.dart diff --git a/.idea/runConfigurations/Doctor.xml b/.idea/runConfigurations/Doctor.xml new file mode 100644 index 00000000..bd640ddb --- /dev/null +++ b/.idea/runConfigurations/Doctor.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Service.xml b/.idea/runConfigurations/Service.xml new file mode 100644 index 00000000..525203cb --- /dev/null +++ b/.idea/runConfigurations/Service.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Show_Help.xml b/.idea/runConfigurations/Show_Help.xml new file mode 100644 index 00000000..009fef31 --- /dev/null +++ b/.idea/runConfigurations/Show_Help.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index fe769e7f..3a095590 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -1,9 +1,22 @@ #!/usr/bin/env dart - library angel_cli.tool; +import "dart:io"; +import "package:args/command_runner.dart"; import 'package:angel_cli/angel_cli.dart'; +final String DOCTOR = "doctor"; + main(List args) { - var parser = makeParser(); -} \ No newline at end of file + var runner = + new CommandRunner("angel", "Command-line tools for the Angel framework."); + + runner.addCommand(new DoctorCommand()); + runner.addCommand(new ServiceCommand()); + + return runner.run(args).then((_) {}).catchError((exc, st) { + stderr.writeln("Oops, something went wrong: $exc"); + stderr.writeln(st); + exitCode = 1; + }); +} diff --git a/lib/angel_cli.dart b/lib/angel_cli.dart index af96721c..4d3fa997 100644 --- a/lib/angel_cli.dart +++ b/lib/angel_cli.dart @@ -1,4 +1,3 @@ library angel_cli; -import 'package:args/args.dart'; -part 'src/args.dart'; \ No newline at end of file +export 'src/commands/commands.dart'; \ No newline at end of file diff --git a/lib/src/args.dart b/lib/src/args.dart deleted file mode 100644 index b8ac1483..00000000 --- a/lib/src/args.dart +++ /dev/null @@ -1,6 +0,0 @@ -part of angel_cli; - -ArgParser makeParser() { - var result = new ArgParser(allowTrailingOptions: true); - return result; -} \ No newline at end of file diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart new file mode 100644 index 00000000..f3c15b74 --- /dev/null +++ b/lib/src/commands/commands.dart @@ -0,0 +1,4 @@ +library angel_cli.commands; + +export "doctor.dart"; +export "service.dart"; \ No newline at end of file diff --git a/lib/src/commands/doctor.dart b/lib/src/commands/doctor.dart new file mode 100644 index 00000000..836ab723 --- /dev/null +++ b/lib/src/commands/doctor.dart @@ -0,0 +1,38 @@ +import "dart:convert"; +import "dart:io"; +import "package:args/command_runner.dart"; +import "package:console/console.dart"; + +class DoctorCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => "doctor"; + + @override + String get description => + "Ensures that the current system is capable of running Angel."; + + @override + run() async { + print("Checking your system for dependencies..."); + await _checkForGit(); + } + + _checkForGit() async { + try { + var git = await Process.start("git", ["--version"]); + if (await git.exitCode == 0) { + var version = await git.stdout.transform(UTF8.decoder).join(); + _pen.green(); + _pen("${Icon.CHECKMARK} Git executable found: v${version.replaceAll('git version', '').trim()}"); + _pen(); + } else + throw new Exception("Git executable exit code not 0"); + } catch (exc) { + _pen.red(); + _pen("${Icon.BALLOT_X} Git executable not found"); + _pen(); + } + } +} diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart new file mode 100644 index 00000000..c4d2196a --- /dev/null +++ b/lib/src/commands/service.dart @@ -0,0 +1,51 @@ +import "package:args/command_runner.dart"; +import "package:console/console.dart"; +import "package:mustache4dart/mustache4dart.dart"; + +class ServiceCommand extends Command { + final String CUSTOM = "Custom"; + final String MEMORY = "In-Memory"; + final String MONGO = "MongoDB"; + final TextPen _pen = new TextPen(); + + @override String get name => "service"; + + @override String get description => + "Creates a new service within the given project."; + + @override + run() async { + var name = await readInput("Name of Service (not plural): "); + var chooser = new Chooser([MONGO, MEMORY, CUSTOM], + message: "What type of service would you like to create? "); + var type = await chooser.choose(); + print("Creating $type service $name"); + + fail() { + _pen.red(); + _pen("Could not successfully create service $name."); + _pen(); + } + + String serviceSource = ""; + + if (type == MONGO) { + serviceSource = _generateMongoService(name); + } else fail(); + + print("Generated source: "); + print(serviceSource); + } + + _generateMongoService(String name) { + return ''' + import "package:angel_mongo/angel_mongo.dart"; + + class ${name}Service extends MongoService { + ${name}Service(collection):super(collection) { + print("YEET"); + } + } + '''; + } +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index f1f1971a..5faddd53 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,8 +6,9 @@ homepage: https://github.com/angel-dart/angel_cli executables: angel: angel dependencies: + analyzer: ">=0.28.1 <1.0.0" args: ">=0.3.4 <1.0.0" - colorize: ">=0.1.2 <1.0.0" console: ">=2.2.3 <3.0.0" + dart_style: "0.2.9+1 <0.3.0" mustache4dart: ">=1.0.0 <3.0.0" yaml: ">=2.0.0 <3.0.0" \ No newline at end of file From ef503d57391d1f36c7c3d84c52c6f0ea301d9bdd Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 14 Sep 2016 20:20:23 -0400 Subject: [PATCH 005/132] Bye dart style --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5faddd53..cc37953c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,6 @@ dependencies: analyzer: ">=0.28.1 <1.0.0" args: ">=0.3.4 <1.0.0" console: ">=2.2.3 <3.0.0" - dart_style: "0.2.9+1 <0.3.0" + # dart_style: "0.2.9+1 <0.3.0" mustache4dart: ">=1.0.0 <3.0.0" yaml: ">=2.0.0 <3.0.0" \ No newline at end of file From 99024ed151f20381f05f55cd3276ccd70b490d91 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 14 Sep 2016 20:21:15 -0400 Subject: [PATCH 006/132] Service formatting --- lib/src/commands/service.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index c4d2196a..57355a7a 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -39,13 +39,13 @@ class ServiceCommand extends Command { _generateMongoService(String name) { return ''' - import "package:angel_mongo/angel_mongo.dart"; +import "package:angel_mongo/angel_mongo.dart"; - class ${name}Service extends MongoService { - ${name}Service(collection):super(collection) { - print("YEET"); - } - } - '''; +class ${name}Service extends MongoService { + ${name}Service(collection):super(collection) { + print("YEET"); + } +} + '''.trim(); } } \ No newline at end of file From 12dd7542425b580e9f8bd952d2181cc614f301bc Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 14 Sep 2016 21:28:12 -0400 Subject: [PATCH 007/132] Can generate services --- .gitignore | 1 + .idea/runConfigurations/Service.xml | 1 + TODO.md | 6 +++ bin/angel.dart | 3 +- lib/src/commands/service.dart | 81 ++++++++++++++++++++++++++--- sample_project/pubspec.yaml | 5 ++ sample_project/sample_project.dart | 11 ++++ 7 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 TODO.md create mode 100644 sample_project/pubspec.yaml create mode 100644 sample_project/sample_project.dart diff --git a/.gitignore b/.gitignore index fa6ea1da..6d6d8568 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,4 @@ doc/api/ # Don't commit pubspec lock file # (Library packages only! Remove pattern if developing an application package) pubspec.lock +/sample_project/lib/src/services/ diff --git a/.idea/runConfigurations/Service.xml b/.idea/runConfigurations/Service.xml index 525203cb..58887959 100644 --- a/.idea/runConfigurations/Service.xml +++ b/.idea/runConfigurations/Service.xml @@ -2,6 +2,7 @@ \ No newline at end of file diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..b61b7e6a --- /dev/null +++ b/TODO.md @@ -0,0 +1,6 @@ +# Todo + +* `schema` +* `migrate` +* `build` +* `deploy` \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index 3a095590..dff43cd7 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -14,9 +14,8 @@ main(List args) { runner.addCommand(new DoctorCommand()); runner.addCommand(new ServiceCommand()); - return runner.run(args).then((_) {}).catchError((exc, st) { + return runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); - stderr.writeln(st); exitCode = 1; }); } diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 57355a7a..5e8a1c07 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -1,3 +1,4 @@ +import "dart:io"; import "package:args/command_runner.dart"; import "package:console/console.dart"; import "package:mustache4dart/mustache4dart.dart"; @@ -6,6 +7,8 @@ class ServiceCommand extends Command { final String CUSTOM = "Custom"; final String MEMORY = "In-Memory"; final String MONGO = "MongoDB"; + final String MONGO_TYPED = "MongoDB (typed)"; + final String TRESTLE = "Trestle"; final TextPen _pen = new TextPen(); @override String get name => "service"; @@ -16,10 +19,9 @@ class ServiceCommand extends Command { @override run() async { var name = await readInput("Name of Service (not plural): "); - var chooser = new Chooser([MONGO, MEMORY, CUSTOM], + var chooser = new Chooser([TRESTLE, MONGO, MONGO_TYPED, MEMORY, CUSTOM], message: "What type of service would you like to create? "); var type = await chooser.choose(); - print("Creating $type service $name"); fail() { _pen.red(); @@ -31,19 +33,84 @@ class ServiceCommand extends Command { if (type == MONGO) { serviceSource = _generateMongoService(name); - } else fail(); + } else if (type == MONGO_TYPED) { + _pen.blue(); + _pen("${Icon.STAR} To create a typed Mongo service, please create a schema using 'angel schema'."); + _pen(); + } else if (type == MEMORY) { + serviceSource = _generateMemoryService(name); + } else if (type == CUSTOM) { + serviceSource = _generateCustomService(name); + } else if (type == TRESTLE) { + _pen.blue(); + _pen("${Icon.STAR} Trestle services are not yet implemented. :("); + _pen(); + } else { + print("Code to generate a $type service is not yet written."); + } - print("Generated source: "); - print(serviceSource); + if (serviceSource.isEmpty) { + if (type == MONGO_TYPED) + return; + + fail(); + throw new Exception("Empty generated service code."); + } + + var servicesDir = new Directory("lib/src/services"); + var serviceFile = new File.fromUri(servicesDir.uri.resolve("${name.toLowerCase()}.dart")); + var serviceLibrary = new File.fromUri( + servicesDir.uri.resolve("services.dart")); + + if (!await servicesDir.exists()) + await servicesDir.create(recursive: true); + + await serviceFile.writeAsString(serviceSource); + await serviceLibrary.writeAsString( + "\nexport '${name.toLowerCase()}.dart';", mode: FileMode.APPEND); + + _pen.green(); + _pen("${Icon.CHECKMARK} Successfully generated service $name."); + _pen(); + } + + _generateCustomService(String name) { + return ''' +import 'package:angel_framework/angel_framework.dart'; + +class ${name}Service extends Service { + ${name}Service():super() { + // Your logic here! + } +} + '''.trim(); + } + + _generateMemoryService(String name) { + return ''' +import 'package:angel_framework/defs.dart'; +import 'package:angel_framework/angel_framework.dart'; + +/// Store in-memory instances of this class. +class $name extends MemoryModel { +} + +/// Manages [$name] in-memory. +class ${name}Service extends MemoryService<$name> { + ${name}Service():super() { + // Your logic here! + } +} + '''.trim(); } _generateMongoService(String name) { return ''' -import "package:angel_mongo/angel_mongo.dart"; +import 'package:angel_mongo/angel_mongo.dart'; class ${name}Service extends MongoService { ${name}Service(collection):super(collection) { - print("YEET"); + // Your logic here! } } '''.trim(); diff --git a/sample_project/pubspec.yaml b/sample_project/pubspec.yaml new file mode 100644 index 00000000..863d07e3 --- /dev/null +++ b/sample_project/pubspec.yaml @@ -0,0 +1,5 @@ +name: sample_project +publish_to: none +dependencies: + angel_framework: ^1.0.0-dev + angel_mongo: ^1.0.0-dev \ No newline at end of file diff --git a/sample_project/sample_project.dart b/sample_project/sample_project.dart new file mode 100644 index 00000000..e4adb20f --- /dev/null +++ b/sample_project/sample_project.dart @@ -0,0 +1,11 @@ +import "dart:async"; +import "dart:io"; +import "package:angel_framework/angel_framework.dart"; +import "package:sample_project/src/services/services.dart"; + +class SampleServer extends Angel { + @override + startServer(address, port) async { + return await super.startServer(address, port); + } +} \ No newline at end of file From a7223ee25478a841e65ab255b34920db9904460a Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 14 Sep 2016 21:29:08 -0400 Subject: [PATCH 008/132] More todos --- TODO.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index b61b7e6a..dfe4a0a0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,9 @@ # Todo +* `service` + * Add tests * `schema` * `migrate` * `build` -* `deploy` \ No newline at end of file +* `deploy` +* `test` \ No newline at end of file From 034ede4dad488f3b0406d140577946c3068a677c Mon Sep 17 00:00:00 2001 From: thosakwe Date: Thu, 15 Sep 2016 01:19:06 -0400 Subject: [PATCH 009/132] Build test for services --- .gitignore | 1 + lib/src/commands/service.dart | 88 +++++++++++++++++++ .../{sample_project.dart => angel.dart} | 2 +- sample_project/pubspec.yaml | 7 +- 4 files changed, 95 insertions(+), 3 deletions(-) rename sample_project/{sample_project.dart => angel.dart} (79%) diff --git a/.gitignore b/.gitignore index 6d6d8568..c6097d69 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,4 @@ doc/api/ # (Library packages only! Remove pattern if developing an application package) pubspec.lock /sample_project/lib/src/services/ +/sample_project/test/services/ diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 5e8a1c07..50eaef26 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -61,14 +61,21 @@ class ServiceCommand extends Command { var serviceFile = new File.fromUri(servicesDir.uri.resolve("${name.toLowerCase()}.dart")); var serviceLibrary = new File.fromUri( servicesDir.uri.resolve("services.dart")); + var testDir = new Directory("test/services"); + var testFile = new File.fromUri(testDir.uri.resolve("${name.toLowerCase()}.dart")); if (!await servicesDir.exists()) await servicesDir.create(recursive: true); + if (!await testDir.exists()) + await testDir.create(recursive: true); + await serviceFile.writeAsString(serviceSource); await serviceLibrary.writeAsString( "\nexport '${name.toLowerCase()}.dart';", mode: FileMode.APPEND); + await testFile.writeAsString(_generateTests(name, type)); + _pen.green(); _pen("${Icon.CHECKMARK} Successfully generated service $name."); _pen(); @@ -115,4 +122,85 @@ class ${name}Service extends MongoService { } '''.trim(); } + + _generateTests(String name, String type) { + return ''' +import 'dart:io'; +import 'package:angel/src/services/${name.toLowerCase()}.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:http/http.dart' as http; +import 'package:json_god/json_god.dart' as god;${_includeMongo(type)} +import 'package:test/test.dart'; + +main() { + group('$name', () { + Angel app; + http.Client client; + HttpServer server; + String url; + HookedService ${name}s;${_createDb(type)} + + setUp(() async { + app = new Angel(); + client = new http.Client();${_openDb(type)} + app.use('/${name.toLowerCase()}s', new ${name}Service(${_dbCollection(name, type)})); + ${name}s = app.service("${name.toLowerCase()}s"); + + server = await app.startServer(null, 0); + url = "http://\${server.address.host}:\${server.port}"; + }); + + tearDown(() async { + app = null; + url = null; + client.close(); + client = null; + ${name}s = null; + await server.close(force: true); + }); + + test('Index ${name.toLowerCase()}s', () async { + var indexed${name}s = await ${name}s.index(); + }); + + test('Index via REST', () async { + var response = await client.get('\$url/${name.toLowerCase()}s'); + print(god.deserialize(response.body)); + }); + }); +} + '''.trim(); + } + + _createDb(String type) { + if (type == MONGO || type == MONGO_TYPED) { + return "\n Db db;"; + } + + return ""; + } + + _dbCollection(String name, String type) { + if (type == MONGO || type == MONGO_TYPED) { + return "db.collection('${name.toLowerCase()}s')"; + } + + return ""; + } + + _includeMongo(String type) { + if (type == MONGO || type == MONGO_TYPED) { + return "\nimport 'package:mongo_dart/mongo_dart.dart';"; + } + + return ""; + } + + _openDb(String type) { + if (type == MONGO || type == MONGO_TYPED) { + return "\n await db.open();"; + } + + return ""; + } } \ No newline at end of file diff --git a/sample_project/sample_project.dart b/sample_project/angel.dart similarity index 79% rename from sample_project/sample_project.dart rename to sample_project/angel.dart index e4adb20f..67acefcf 100644 --- a/sample_project/sample_project.dart +++ b/sample_project/angel.dart @@ -1,7 +1,7 @@ import "dart:async"; import "dart:io"; import "package:angel_framework/angel_framework.dart"; -import "package:sample_project/src/services/services.dart"; +import "package:angel/src/services/services.dart"; class SampleServer extends Angel { @override diff --git a/sample_project/pubspec.yaml b/sample_project/pubspec.yaml index 863d07e3..9731a80e 100644 --- a/sample_project/pubspec.yaml +++ b/sample_project/pubspec.yaml @@ -1,5 +1,8 @@ -name: sample_project +name: angel publish_to: none dependencies: angel_framework: ^1.0.0-dev - angel_mongo: ^1.0.0-dev \ No newline at end of file + angel_mongo: ^1.0.0-dev +dev_dependencies: + http: ^0.11.3 + test: ^0.12.13 \ No newline at end of file From 57d95e71c45d1e6591f8c02cf5c6556213eea719 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 17 Sep 2016 23:56:08 -0400 Subject: [PATCH 010/132] 'init' command --- .gitignore | 1 + .idea/runConfigurations/Init.xml | 8 ++++ bin/angel.dart | 1 + lib/src/commands/commands.dart | 1 + lib/src/commands/init.dart | 76 ++++++++++++++++++++++++++++++++ sample_project/angel.dart | 11 ----- sample_project/pubspec.yaml | 8 ---- 7 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 .idea/runConfigurations/Init.xml create mode 100644 lib/src/commands/init.dart delete mode 100644 sample_project/angel.dart delete mode 100644 sample_project/pubspec.yaml diff --git a/.gitignore b/.gitignore index c6097d69..58e5c867 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,4 @@ doc/api/ pubspec.lock /sample_project/lib/src/services/ /sample_project/test/services/ +/sample_project/ diff --git a/.idea/runConfigurations/Init.xml b/.idea/runConfigurations/Init.xml new file mode 100644 index 00000000..c0740078 --- /dev/null +++ b/.idea/runConfigurations/Init.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index dff43cd7..0433165b 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -13,6 +13,7 @@ main(List args) { runner.addCommand(new DoctorCommand()); runner.addCommand(new ServiceCommand()); + runner.addCommand(new InitCommand()); return runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index f3c15b74..e6c5faee 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -1,4 +1,5 @@ library angel_cli.commands; export "doctor.dart"; +export "init.dart"; export "service.dart"; \ No newline at end of file diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart new file mode 100644 index 00000000..5cd20303 --- /dev/null +++ b/lib/src/commands/init.dart @@ -0,0 +1,76 @@ +import "dart:convert"; +import "dart:io"; +import "package:args/command_runner.dart"; +import "package:console/console.dart"; + +class InitCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => "init"; + + @override + String get description => + "Initializes a new Angel project in the current directory."; + + InitCommand() {} + + @override + run() async { + Directory projectDir = new Directory( + argResults.arguments.isEmpty ? "." : argResults.arguments[0]); + print("Creating new Angel project in ${projectDir.absolute.path}..."); + await _cloneRepo(projectDir);_pen.green(); + _pen("${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); + _pen(); + await _pubGet(); + } + + _cloneRepo(Directory projectDir) async { + try { + if (await projectDir.exists()) { + var chooser = new Chooser(["Yes", "No"], + message: + "Directory '${projectDir.absolute.path}' exists. Overwrite it? (Yes/No)"); + + if (await chooser.choose() != "Yes") + throw new Exception("Chose not to overwrite existing directory."); + await projectDir.delete(recursive: true); + } + + var git = await Process.start("git", [ + "clone", + "--depth", + "1", + "https://github.com/angel-dart/angel", + projectDir.absolute.path + ]); + + git.stdout.transform(UTF8.decoder).listen(stdout.write); + git.stderr.transform(UTF8.decoder).listen(stderr.write); + + if (await git.exitCode != 0) { + throw new Exception("Could not clone repo."); + } + + var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git")); + + if (await gitDir.exists()) + await gitDir.delete(recursive: true); + } catch (e) { + print(e); + _pen.red(); + _pen("${Icon.BALLOT_X} Could not initialize Angel project."); + _pen(); + rethrow; + } + } + + _pubGet() async { + var pub = await Process.start("pub", ["get"]); + pub.stdout.pipe(stdout); + pub.stderr.pipe(stderr); + var code = await pub.exitCode; + print("Pub process exited with code $code"); + } +} diff --git a/sample_project/angel.dart b/sample_project/angel.dart deleted file mode 100644 index 67acefcf..00000000 --- a/sample_project/angel.dart +++ /dev/null @@ -1,11 +0,0 @@ -import "dart:async"; -import "dart:io"; -import "package:angel_framework/angel_framework.dart"; -import "package:angel/src/services/services.dart"; - -class SampleServer extends Angel { - @override - startServer(address, port) async { - return await super.startServer(address, port); - } -} \ No newline at end of file diff --git a/sample_project/pubspec.yaml b/sample_project/pubspec.yaml deleted file mode 100644 index 9731a80e..00000000 --- a/sample_project/pubspec.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: angel -publish_to: none -dependencies: - angel_framework: ^1.0.0-dev - angel_mongo: ^1.0.0-dev -dev_dependencies: - http: ^0.11.3 - test: ^0.12.13 \ No newline at end of file From 699907e6eae4c71418e98be774ba5c875ede09a6 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 17 Sep 2016 23:56:44 -0400 Subject: [PATCH 011/132] +1 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index cc37953c..77432754 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev +version: 1.0.0-dev+1 description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli From 5c7b975160b2a8cdb8f07f6c110738f3aaf3da0a Mon Sep 17 00:00:00 2001 From: thosakwe Date: Mon, 19 Sep 2016 00:04:08 -0400 Subject: [PATCH 012/132] New plans, use Grinder for CLI --- TODO.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index dfe4a0a0..74835954 100644 --- a/TODO.md +++ b/TODO.md @@ -2,8 +2,6 @@ * `service` * Add tests -* `schema` -* `migrate` -* `build` +* `migration` * `deploy` -* `test` \ No newline at end of file +* Call these from Grinder script :) \ No newline at end of file From 64978a7a891b70f16a2c13bba90077e3004abdec Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 21 Sep 2016 19:47:50 -0400 Subject: [PATCH 013/132] Fixed init --- lib/src/commands/init.dart | 6 +++--- pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 5cd20303..68adc2ad 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -23,7 +23,7 @@ class InitCommand extends Command { await _cloneRepo(projectDir);_pen.green(); _pen("${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); _pen(); - await _pubGet(); + await _pubGet(projectDir); } _cloneRepo(Directory projectDir) async { @@ -66,8 +66,8 @@ class InitCommand extends Command { } } - _pubGet() async { - var pub = await Process.start("pub", ["get"]); + _pubGet(Directory projectDir) async { + var pub = await Process.start("pub", ["get"], workingDirectory: projectDir.absolute.path); pub.stdout.pipe(stdout); pub.stderr.pipe(stderr); var code = await pub.exitCode; diff --git a/pubspec.yaml b/pubspec.yaml index 77432754..ec84e242 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+1 +version: 1.0.0-dev+2 description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli From 52d96b45820c541437d1c31f7f650c02b250c2f4 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 23 Nov 2016 19:35:40 -0500 Subject: [PATCH 014/132] +3 --- lib/src/commands/service.dart | 152 ++++++++++++++++++++++++++++------ pubspec.yaml | 3 +- 2 files changed, 127 insertions(+), 28 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 50eaef26..3c562388 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -11,10 +11,11 @@ class ServiceCommand extends Command { final String TRESTLE = "Trestle"; final TextPen _pen = new TextPen(); - @override String get name => "service"; + @override + String get name => "service"; - @override String get description => - "Creates a new service within the given project."; + @override + String get description => "Creates a new service within the given project."; @override run() async { @@ -34,9 +35,9 @@ class ServiceCommand extends Command { if (type == MONGO) { serviceSource = _generateMongoService(name); } else if (type == MONGO_TYPED) { - _pen.blue(); - _pen("${Icon.STAR} To create a typed Mongo service, please create a schema using 'angel schema'."); - _pen(); + serviceSource = _generateMongoTypedService(name); + + await _generateMongoModel(name); } else if (type == MEMORY) { serviceSource = _generateMemoryService(name); } else if (type == CUSTOM) { @@ -50,32 +51,36 @@ class ServiceCommand extends Command { } if (serviceSource.isEmpty) { - if (type == MONGO_TYPED) - return; - fail(); throw new Exception("Empty generated service code."); } var servicesDir = new Directory("lib/src/services"); - var serviceFile = new File.fromUri(servicesDir.uri.resolve("${name.toLowerCase()}.dart")); - var serviceLibrary = new File.fromUri( - servicesDir.uri.resolve("services.dart")); + var serviceFile = + new File.fromUri(servicesDir.uri.resolve("${name.toLowerCase()}.dart")); + var serviceLibrary = + new File.fromUri(servicesDir.uri.resolve("services.dart")); var testDir = new Directory("test/services"); - var testFile = new File.fromUri(testDir.uri.resolve("${name.toLowerCase()}.dart")); + var testFile = new File.fromUri( + testDir.uri.resolve("${name.toLowerCase()}_test.dart")); - if (!await servicesDir.exists()) - await servicesDir.create(recursive: true); + if (!await servicesDir.exists()) await servicesDir.create(recursive: true); - if (!await testDir.exists()) - await testDir.create(recursive: true); + if (!await testDir.exists()) await testDir.create(recursive: true); await serviceFile.writeAsString(serviceSource); - await serviceLibrary.writeAsString( - "\nexport '${name.toLowerCase()}.dart';", mode: FileMode.APPEND); + await serviceLibrary.writeAsString("\nexport '${name.toLowerCase()}.dart';", + mode: FileMode.APPEND); await testFile.writeAsString(_generateTests(name, type)); + final runConfig = new File('./.idea/runConfigurations/${name}_tests.xml'); + + if (!await runConfig.exists()) { + await runConfig.create(recursive: true); + await runConfig.writeAsString(_generateRunConfiguration(name)); + } + _pen.green(); _pen("${Icon.CHECKMARK} Successfully generated service $name."); _pen(); @@ -90,7 +95,8 @@ class ${name}Service extends Service { // Your logic here! } } - '''.trim(); + ''' + .trim(); } _generateMemoryService(String name) { @@ -108,19 +114,112 @@ class ${name}Service extends MemoryService<$name> { // Your logic here! } } - '''.trim(); + ''' + .trim(); + } + + _generateMongoModel(String name) async { + final lower = name.toLowerCase(); + final file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'dart:convert'; +import 'package:angel_mongo/model.dart'; + +class $name extends Model { + String name, desc; + + $name({this.name, this.desc}); + + factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); + + factory $name.fromMap(Map data) => new $name( + name: data["name"], + desc: data["desc"]); + + Map toJson() { + return { + "name": name, + "desc": desc + }; + } +} + ''' + .trim()); } _generateMongoService(String name) { - return ''' -import 'package:angel_mongo/angel_mongo.dart'; + final lower = name.toLowerCase(); + return ''' +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:mongo_dart/mongo_dart.dart'; + +configureServer(Db db) { + return (Angel app) async { + app.use("/api/${lower}s", new ${name}Service(db.collection("${lower}s"))); + + HookedService service = app.service("api/${lower}s"); + app.container.singleton(service.inner); + }; +} + +/// Manages [$name] in the database. class ${name}Service extends MongoService { ${name}Service(collection):super(collection) { // Your logic here! } } - '''.trim(); + ''' + .trim(); + } + + _generateMongoTypedService(String name) { + final lower = name.toLowerCase(); + + return ''' +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:mongo_dart/mongo_dart.dart'; +import '../models/$lower.dart'; +export '../models/$lower.dart'; + +configureServer(Db db) { + return (Angel app) async { + app.use("/api/${lower}s", new ${name}Service(db.collection("${lower}s"))); + + HookedService service = app.service("api/${lower}s"); + app.container.singleton(service.inner); + }; +} + +/// Manages [$name] in the database. +class ${name}Service extends MongoTypedService<$name> { + ${name}Service(collection):super(collection) { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateRunConfiguration(String name) { + final lower = name.toLowerCase(); + + return ''' + + + + +''' + .trim(); } _generateTests(String name, String type) { @@ -169,7 +268,8 @@ main() { }); }); } - '''.trim(); + ''' + .trim(); } _createDb(String type) { @@ -203,4 +303,4 @@ main() { return ""; } -} \ No newline at end of file +} diff --git a/pubspec.yaml b/pubspec.yaml index ec84e242..ba1d1d51 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+2 +version: 1.0.0-dev+3 description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli @@ -9,6 +9,5 @@ dependencies: analyzer: ">=0.28.1 <1.0.0" args: ">=0.3.4 <1.0.0" console: ">=2.2.3 <3.0.0" - # dart_style: "0.2.9+1 <0.3.0" mustache4dart: ">=1.0.0 <3.0.0" yaml: ">=2.0.0 <3.0.0" \ No newline at end of file From 2a212c9d4077429f42744647f68ea41639461639 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 23 Nov 2016 19:42:29 -0500 Subject: [PATCH 015/132] +4 --- lib/src/commands/service.dart | 11 +++++++---- pubspec.yaml | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 3c562388..69a4b18c 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -58,8 +58,6 @@ class ServiceCommand extends Command { var servicesDir = new Directory("lib/src/services"); var serviceFile = new File.fromUri(servicesDir.uri.resolve("${name.toLowerCase()}.dart")); - var serviceLibrary = - new File.fromUri(servicesDir.uri.resolve("services.dart")); var testDir = new Directory("test/services"); var testFile = new File.fromUri( testDir.uri.resolve("${name.toLowerCase()}_test.dart")); @@ -69,8 +67,13 @@ class ServiceCommand extends Command { if (!await testDir.exists()) await testDir.create(recursive: true); await serviceFile.writeAsString(serviceSource); - await serviceLibrary.writeAsString("\nexport '${name.toLowerCase()}.dart';", - mode: FileMode.APPEND); + + if (type == MONGO_TYPED) { + var serviceLibrary = new File("lib/src/models/models.dart"); + await serviceLibrary.writeAsString( + "\nexport '${name.toLowerCase()}.dart';", + mode: FileMode.APPEND); + } await testFile.writeAsString(_generateTests(name, type)); diff --git a/pubspec.yaml b/pubspec.yaml index ba1d1d51..7de21a47 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+3 +version: 1.0.0-dev+4 description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli From b92ebdeb210d22ee34675865bf23f46b056b10f5 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 10 Dec 2016 13:52:14 -0500 Subject: [PATCH 016/132] +5 --- .idea/runConfigurations/Service.xml | 8 --- bin/angel.dart | 9 ++- lib/src/commands/commands.dart | 4 +- lib/src/commands/plugin.dart | 46 +++++++++++++++ lib/src/commands/service.dart | 89 +++++++---------------------- lib/src/commands/test.dart | 82 ++++++++++++++++++++++++++ pubspec.yaml | 3 +- 7 files changed, 159 insertions(+), 82 deletions(-) delete mode 100644 .idea/runConfigurations/Service.xml create mode 100644 lib/src/commands/plugin.dart create mode 100644 lib/src/commands/test.dart diff --git a/.idea/runConfigurations/Service.xml b/.idea/runConfigurations/Service.xml deleted file mode 100644 index 58887959..00000000 --- a/.idea/runConfigurations/Service.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index 0433165b..fd9e44d0 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -11,9 +11,12 @@ main(List args) { var runner = new CommandRunner("angel", "Command-line tools for the Angel framework."); - runner.addCommand(new DoctorCommand()); - runner.addCommand(new ServiceCommand()); - runner.addCommand(new InitCommand()); + runner + ..addCommand(new DoctorCommand()) + ..addCommand(new ServiceCommand()) + ..addCommand(new InitCommand()) + ..addCommand(new TestCommand()) + ..addCommand(new PluginCommand()); return runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index e6c5faee..14041531 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -2,4 +2,6 @@ library angel_cli.commands; export "doctor.dart"; export "init.dart"; -export "service.dart"; \ No newline at end of file +export "plugin.dart"; +export "service.dart"; +export "test.dart"; \ No newline at end of file diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart new file mode 100644 index 00000000..6a731cda --- /dev/null +++ b/lib/src/commands/plugin.dart @@ -0,0 +1,46 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import "package:console/console.dart"; + +class PluginCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => "plugin"; + + @override + String get description => "Creates a new plugin within the given project."; + + @override + run() async { + final name = await readInput("Name of Plugin: "), lower = name.toLowerCase(); + final testDir = new Directory("lib/src/config/plugins"); + final pluginFile = new File.fromUri( + testDir.uri.resolve("$lower.dart")); + + if (!await pluginFile.exists()) + await pluginFile.create(recursive: true); + + await pluginFile.writeAsString(_generatePlugin(lower)); + + _pen.green(); + _pen("${Icon.CHECKMARK} Successfully generated plugin $name."); + _pen(); + } + + String _generatePlugin(String name) { + + return ''' +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; + +class $name extends AngelPlugin { + @override + Future call(Angel app) async { + // Work some magic... + } +} + ''' + .trim(); + } +} diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 69a4b18c..6c4bdcf7 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -1,7 +1,6 @@ import "dart:io"; import "package:args/command_runner.dart"; import "package:console/console.dart"; -import "package:mustache4dart/mustache4dart.dart"; class ServiceCommand extends Command { final String CUSTOM = "Custom"; @@ -228,82 +227,36 @@ class ${name}Service extends MongoTypedService<$name> { _generateTests(String name, String type) { return ''' import 'dart:io'; -import 'package:angel/src/services/${name.toLowerCase()}.dart'; +import 'package:angel/angel.dart'; import 'package:angel_framework/angel_framework.dart'; -import 'package:http/http.dart' as http; -import 'package:json_god/json_god.dart' as god;${_includeMongo(type)} +import 'package:angel_test/angel_test.dart'; import 'package:test/test.dart'; -main() { - group('$name', () { - Angel app; - http.Client client; - HttpServer server; - String url; - HookedService ${name}s;${_createDb(type)} +main() async { + Angel app; + TestClient client; - setUp(() async { - app = new Angel(); - client = new http.Client();${_openDb(type)} - app.use('/${name.toLowerCase()}s', new ${name}Service(${_dbCollection(name, type)})); - ${name}s = app.service("${name.toLowerCase()}s"); + setUp(() async { + app = await createServer(); + client = await connectTo(app, saveSession: false); + }); - server = await app.startServer(null, 0); - url = "http://\${server.address.host}:\${server.port}"; - }); + tearDown(() async { + await client.close(); + app = null; + }); - tearDown(() async { - app = null; - url = null; - client.close(); - client = null; - ${name}s = null; - await server.close(force: true); - }); + test('index via REST', () async { + final response = await client.get('/api/${name.toLowerCase()}'); + expect(response, hasStatus(HttpStatus.OK)); + }); - test('Index ${name.toLowerCase()}s', () async { - var indexed${name}s = await ${name}s.index(); - }); - - test('Index via REST', () async { - var response = await client.get('\$url/${name.toLowerCase()}s'); - print(god.deserialize(response.body)); - }); + test('Index ${name.toLowerCase()}s', () async { + final ${name.toLowerCase()}s = await client.service('api/${name.toLowerCase()}').index(); + print(${name.toLowerCase()}s); }); } - ''' - .trim(); - } - _createDb(String type) { - if (type == MONGO || type == MONGO_TYPED) { - return "\n Db db;"; - } - - return ""; - } - - _dbCollection(String name, String type) { - if (type == MONGO || type == MONGO_TYPED) { - return "db.collection('${name.toLowerCase()}s')"; - } - - return ""; - } - - _includeMongo(String type) { - if (type == MONGO || type == MONGO_TYPED) { - return "\nimport 'package:mongo_dart/mongo_dart.dart';"; - } - - return ""; - } - - _openDb(String type) { - if (type == MONGO || type == MONGO_TYPED) { - return "\n await db.open();"; - } - - return ""; + '''.trim(); } } diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart new file mode 100644 index 00000000..01063e48 --- /dev/null +++ b/lib/src/commands/test.dart @@ -0,0 +1,82 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import "package:console/console.dart"; + +class TestCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => "test"; + + @override + String get description => "Creates a new test within the given project."; + + @override + run() async { + final name = await readInput("Name of Test: "), lower = name.toLowerCase(); + final testDir = new Directory("test/services"); + final testFile = new File.fromUri( + testDir.uri.resolve("${lower}_test.dart")); + + if (!await testFile.exists()) + await testFile.create(recursive: true); + + await testFile.writeAsString(_generateTest(lower)); + + final runConfig = new File('./.idea/runConfigurations/${name}_tests.xml'); + + if (!await runConfig.exists()) { + await runConfig.create(recursive: true); + await runConfig.writeAsString(_generateRunConfiguration(name)); + } + + _pen.green(); + _pen("${Icon.CHECKMARK} Successfully generated test $name."); + _pen(); + } + + _generateRunConfiguration(String name) { + final lower = name.toLowerCase(); + + return ''' + + + + +''' + .trim(); + } + + String _generateTest(String lower) { + return ''' +import 'dart:io'; +import 'package:angel/angel.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_test/angel_test.dart'; +import 'package:test/test.dart'; + +main() async { + Angel app; + TestClient client; + + setUp(() async { + app = await createServer(); + client = await connectTo(app, saveSession: false); + }); + + tearDown(() async { + await client.close(); + app = null; + }); + + test('$lower', () async { + final response = await client.get('/$lower'); + expect(response, hasStatus(HttpStatus.OK)); + }); +} + ''' + .trim(); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 7de21a47..e434f181 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+4 +version: 1.0.0-dev+5 description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli @@ -9,5 +9,4 @@ dependencies: analyzer: ">=0.28.1 <1.0.0" args: ">=0.3.4 <1.0.0" console: ">=2.2.3 <3.0.0" - mustache4dart: ">=1.0.0 <3.0.0" yaml: ">=2.0.0 <3.0.0" \ No newline at end of file From b3fe0c514610a6fa530187566028f79926a14070 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Tue, 13 Dec 2016 12:50:53 -0500 Subject: [PATCH 017/132] Added start --- .DS_Store | Bin 0 -> 6148 bytes .idea/angel_cli.iml | 26 ++++++++++++++ .idea/modules.xml | 8 +++++ README.md | 14 +++++++- bin/angel.dart | 3 +- lib/src/commands/commands.dart | 1 + lib/src/commands/start.dart | 63 +++++++++++++++++++++++++++++++++ pubspec.yaml | 2 +- 8 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 .DS_Store create mode 100644 .idea/angel_cli.iml create mode 100644 .idea/modules.xml create mode 100644 lib/src/commands/start.dart diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c2e90e8119a896a5bda0104623306eb0a7172b7d GIT binary patch literal 6148 zcmeHKO-sW-5Pj1YnxcmuJ?7}C;6I495ejOd^#{}>3Su-u>A_omhCj;xq;GbYlBD%2 zDl;(q_G8}e3hd9I)=D0wL8?2DFqh;BNKE^Ay zOlU*AM~(^CsEAK(9yGo8Mtb|aH{9VFWrwHPHF+Z}nb|pJxF$}>DB28{VUsb$Ef$Pl z9E#SwBx>Zv<;+Y0Q@|AXZ3^fSPK%B_T4@TH0;a%D0X-jLx?&Qr_UMid7VZf^9I)FN z>+a{D7;6VH30Qk%4^2Fk=&2H(7!uwN;^`buLVWGf(;;qU&J$La@IsOB^pMzvLlTcx zngXUkTY + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..5f91449b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 5f33cd5b..87776e1c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,14 @@ # angel_cli -Command-line tools for the Angel framework. \ No newline at end of file +Command-line tools for the Angel framework. + +To install: + +```bash +$ pub global activate angel_cli +``` + +And then, for information on each command: + +```bash +$ angel --help +``` \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index fd9e44d0..d16d4266 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -16,7 +16,8 @@ main(List args) { ..addCommand(new ServiceCommand()) ..addCommand(new InitCommand()) ..addCommand(new TestCommand()) - ..addCommand(new PluginCommand()); + ..addCommand(new PluginCommand()) + ..addCommand(new StartCommand()); return runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 14041531..7e82dcc3 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -4,4 +4,5 @@ export "doctor.dart"; export "init.dart"; export "plugin.dart"; export "service.dart"; +export "start.dart"; export "test.dart"; \ No newline at end of file diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart new file mode 100644 index 00000000..23ceff96 --- /dev/null +++ b/lib/src/commands/start.dart @@ -0,0 +1,63 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:yaml/yaml.dart'; + +class StartCommand extends Command { + @override + String get name => 'start'; + + @override + String get description => + 'Runs any `start` scripts, and then runs the server.'; + + StartCommand() : super() { + argParser.addFlag('production', + help: 'Starts the server in production mode.', + negatable: false, + defaultsTo: false); + } + + @override + run() async { + final pubspec = new File('pubspec.yaml'); + + if (await pubspec.exists()) { + // Run start scripts + final doc = loadYamlDocument(await pubspec.readAsString()); + final scriptsNode = doc.contents['scripts']; + + if (scriptsNode != null && scriptsNode.containsKey('start')) { + final scripts = scriptsNode['start'] is List + ? scriptsNode['start'] + : [scriptsNode['start']]; + + for (String script in scripts) { + final split = script.split(' '); + final result = await Process.run(split.first, split.skip(1).toList(), + stdoutEncoding: null, stderrEncoding: null); + final code = result.exitCode; + + stdout.add(result.stdout); + stderr.add(result.stderr); + + if (code != 0) { + throw new Exception("Command '$script' exited with code $code."); + } + } + } + } + + print('Starting server...'); + + final env = {}; + + if (argResults['production']) env['ANGEL_ENV'] = 'production'; + + final server = await Process.start(Platform.executable, ['bin/server.dart'], + environment: env); + server.stdout.pipe(stdout); + server.stderr.pipe(stderr); + + exitCode = await server.exitCode; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index e434f181..75278ac7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+5 +version: 1.0.0-dev+6 description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli From d40a20e52cb480d509c45ac5837bf3e8e1ac5fd4 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Tue, 13 Dec 2016 12:52:01 -0500 Subject: [PATCH 018/132] Create .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..3939d628 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: dart From 08e12d9f4af97b5faa89d96a549b1c8a7be8c713 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 21 Dec 2016 12:34:15 -0500 Subject: [PATCH 019/132] +7 --- lib/src/commands/service.dart | 135 +++++++++++++++++++++++++--------- lib/src/commands/start.dart | 22 +++--- lib/src/commands/test.dart | 2 +- pubspec.yaml | 2 +- 4 files changed, 110 insertions(+), 51 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 6c4bdcf7..58c35937 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -1,41 +1,41 @@ -import "dart:io"; -import "package:args/command_runner.dart"; -import "package:console/console.dart"; +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:console/console.dart'; class ServiceCommand extends Command { - final String CUSTOM = "Custom"; - final String MEMORY = "In-Memory"; - final String MONGO = "MongoDB"; - final String MONGO_TYPED = "MongoDB (typed)"; - final String TRESTLE = "Trestle"; + final String CUSTOM = 'Custom'; + final String MEMORY = 'In-Memory'; + final String MONGO = 'MongoDB'; + final String MONGO_TYPED = 'MongoDB (typed)'; + final String TRESTLE = 'Trestle'; final TextPen _pen = new TextPen(); @override - String get name => "service"; + String get name => 'service'; @override - String get description => "Creates a new service within the given project."; + String get description => 'Creates a new service within the given project.'; @override run() async { - var name = await readInput("Name of Service (not plural): "); + var name = await readInput('Name of Service (not plural): '); var chooser = new Chooser([TRESTLE, MONGO, MONGO_TYPED, MEMORY, CUSTOM], - message: "What type of service would you like to create? "); + message: 'What type of service would you like to create? '); var type = await chooser.choose(); fail() { _pen.red(); - _pen("Could not successfully create service $name."); + _pen('Could not successfully create service $name.'); _pen(); } - String serviceSource = ""; + String serviceSource = ''; if (type == MONGO) { serviceSource = _generateMongoService(name); + await _generateMemoryModel(name); } else if (type == MONGO_TYPED) { serviceSource = _generateMongoTypedService(name); - await _generateMongoModel(name); } else if (type == MEMORY) { serviceSource = _generateMemoryService(name); @@ -43,23 +43,23 @@ class ServiceCommand extends Command { serviceSource = _generateCustomService(name); } else if (type == TRESTLE) { _pen.blue(); - _pen("${Icon.STAR} Trestle services are not yet implemented. :("); + _pen('${Icon.STAR} Trestle services are not yet implemented. :('); _pen(); } else { - print("Code to generate a $type service is not yet written."); + print('Code to generate a $type service is not yet written.'); } if (serviceSource.isEmpty) { fail(); - throw new Exception("Empty generated service code."); + throw new Exception('Empty generated service code.'); } - var servicesDir = new Directory("lib/src/services"); + var servicesDir = new Directory('lib/src/services'); var serviceFile = - new File.fromUri(servicesDir.uri.resolve("${name.toLowerCase()}.dart")); - var testDir = new Directory("test/services"); + new File.fromUri(servicesDir.uri.resolve('${name.toLowerCase()}.dart')); + var testDir = new Directory('test/services'); var testFile = new File.fromUri( - testDir.uri.resolve("${name.toLowerCase()}_test.dart")); + testDir.uri.resolve('${name.toLowerCase()}_test.dart')); if (!await servicesDir.exists()) await servicesDir.create(recursive: true); @@ -67,10 +67,10 @@ class ServiceCommand extends Command { await serviceFile.writeAsString(serviceSource); - if (type == MONGO_TYPED) { - var serviceLibrary = new File("lib/src/models/models.dart"); + if (type == MONGO_TYPED || type == MEMORY) { + var serviceLibrary = new File('lib/src/models/models.dart'); await serviceLibrary.writeAsString( - "\nexport '${name.toLowerCase()}.dart';", + '\nexport "${name.toLowerCase()}.dart";', mode: FileMode.APPEND); } @@ -84,7 +84,7 @@ class ServiceCommand extends Command { } _pen.green(); - _pen("${Icon.CHECKMARK} Successfully generated service $name."); + _pen('${Icon.CHECKMARK} Successfully generated service $name.'); _pen(); } @@ -101,6 +101,44 @@ class ${name}Service extends Service { .trim(); } + _generateMemoryModel(String name) async { + final lower = name.toLowerCase(); + final file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'dart:convert'; +import 'package:angel_framework/angel_framework.dart'; + +class $name extends MemoryModel { + String name, desc; + + $name({String id, this.name, this.desc}) { + this.id = id; + } + + factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); + + factory $name.fromMap(Map data) => new $name( + id: data['id'] + name: data['name'], + desc: data['desc']); + + Map toJson() { + return { + 'id': id, + 'name': name, + 'desc': desc + }; + } +} + ''' + .trim()); + } + _generateMemoryService(String name) { return ''' import 'package:angel_framework/defs.dart'; @@ -108,6 +146,26 @@ import 'package:angel_framework/angel_framework.dart'; /// Store in-memory instances of this class. class $name extends MemoryModel { + String name, desc; + + $name({String id, this.name, this.desc}) { + this.id = id; + } + + factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); + + factory $name.fromMap(Map data) => new $name( + id: data['id'] + name: data['name'], + desc: data['desc']); + + Map toJson() { + return { + 'id': id, + 'name': name, + 'desc': desc + }; + } } /// Manages [$name] in-memory. @@ -135,18 +193,22 @@ import 'package:angel_mongo/model.dart'; class $name extends Model { String name, desc; - $name({this.name, this.desc}); + $name({String id, this.name, this.desc}) { + this.id = id; + } factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); factory $name.fromMap(Map data) => new $name( - name: data["name"], - desc: data["desc"]); + id: data['id'] + name: data['name'], + desc: data['desc']); Map toJson() { return { - "name": name, - "desc": desc + 'id': id, + 'name': name, + 'desc': desc }; } } @@ -164,9 +226,9 @@ import 'package:mongo_dart/mongo_dart.dart'; configureServer(Db db) { return (Angel app) async { - app.use("/api/${lower}s", new ${name}Service(db.collection("${lower}s"))); + app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - HookedService service = app.service("api/${lower}s"); + HookedService service = app.service('api/${lower}s'); app.container.singleton(service.inner); }; } @@ -193,9 +255,9 @@ export '../models/$lower.dart'; configureServer(Db db) { return (Angel app) async { - app.use("/api/${lower}s", new ${name}Service(db.collection("${lower}s"))); + app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - HookedService service = app.service("api/${lower}s"); + HookedService service = app.service('api/${lower}s'); app.container.singleton(service.inner); }; } @@ -257,6 +319,7 @@ main() async { }); } - '''.trim(); + ''' + .trim(); } } diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index 23ceff96..1524adab 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -27,22 +27,18 @@ class StartCommand extends Command { final scriptsNode = doc.contents['scripts']; if (scriptsNode != null && scriptsNode.containsKey('start')) { - final scripts = scriptsNode['start'] is List - ? scriptsNode['start'] - : [scriptsNode['start']]; - - for (String script in scripts) { - final split = script.split(' '); - final result = await Process.run(split.first, split.skip(1).toList(), - stdoutEncoding: null, stderrEncoding: null); - final code = result.exitCode; - - stdout.add(result.stdout); - stderr.add(result.stderr); + try { + var scripts = + await Process.start('pub', ['global', 'run', 'scripts', 'start']); + scripts.stdout.pipe(stdout); + scripts.stderr.pipe(stderr); + int code = await scripts.exitCode; if (code != 0) { - throw new Exception("Command '$script' exited with code $code."); + throw new Exception('`scripts start` failed with exit code $code.'); } + } catch (e) { + // No scripts? No problem... } } } diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index 01063e48..91f5a5e8 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -63,7 +63,7 @@ main() async { setUp(() async { app = await createServer(); - client = await connectTo(app, saveSession: false); + client = await connectTo(app); }); tearDown(() async { diff --git a/pubspec.yaml b/pubspec.yaml index 75278ac7..dd53be52 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+6 +version: 1.0.0-dev+7 description: Command-line tools for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_cli From 0790b41110532cba2759340d258b06e26c361bc1 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 21 Dec 2016 16:27:54 -0500 Subject: [PATCH 020/132] Snake --- lib/src/commands/service.dart | 39 +++++++++++++++++++---------------- pubspec.yaml | 12 ++++++----- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 58c35937..7fd9cae4 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:console/console.dart'; +import 'package:id/id.dart'; class ServiceCommand extends Command { final String CUSTOM = 'Custom'; @@ -16,6 +17,8 @@ class ServiceCommand extends Command { @override String get description => 'Creates a new service within the given project.'; + String _snake(name) => idFromString(name).snake; + @override run() async { var name = await readInput('Name of Service (not plural): '); @@ -54,12 +57,11 @@ class ServiceCommand extends Command { throw new Exception('Empty generated service code.'); } + var lower = _snake(name); var servicesDir = new Directory('lib/src/services'); - var serviceFile = - new File.fromUri(servicesDir.uri.resolve('${name.toLowerCase()}.dart')); + var serviceFile = new File.fromUri(servicesDir.uri.resolve('$lower.dart')); var testDir = new Directory('test/services'); - var testFile = new File.fromUri( - testDir.uri.resolve('${name.toLowerCase()}_test.dart')); + var testFile = new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); if (!await servicesDir.exists()) await servicesDir.create(recursive: true); @@ -69,14 +71,13 @@ class ServiceCommand extends Command { if (type == MONGO_TYPED || type == MEMORY) { var serviceLibrary = new File('lib/src/models/models.dart'); - await serviceLibrary.writeAsString( - '\nexport "${name.toLowerCase()}.dart";', + await serviceLibrary.writeAsString("\nexport '$lower.dart';", mode: FileMode.APPEND); } await testFile.writeAsString(_generateTests(name, type)); - final runConfig = new File('./.idea/runConfigurations/${name}_tests.xml'); + var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); if (!await runConfig.exists()) { await runConfig.create(recursive: true); @@ -102,8 +103,8 @@ class ${name}Service extends Service { } _generateMemoryModel(String name) async { - final lower = name.toLowerCase(); - final file = new File('lib/src/models/$lower.dart'); + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); @@ -179,8 +180,8 @@ class ${name}Service extends MemoryService<$name> { } _generateMongoModel(String name) async { - final lower = name.toLowerCase(); - final file = new File('lib/src/models/$lower.dart'); + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); @@ -217,7 +218,7 @@ class $name extends Model { } _generateMongoService(String name) { - final lower = name.toLowerCase(); + var lower = _snake(name); return ''' import 'package:angel_framework/angel_framework.dart'; @@ -244,7 +245,7 @@ class ${name}Service extends MongoService { } _generateMongoTypedService(String name) { - final lower = name.toLowerCase(); + var lower = _snake(name); return ''' import 'package:angel_framework/angel_framework.dart'; @@ -273,7 +274,7 @@ class ${name}Service extends MongoTypedService<$name> { } _generateRunConfiguration(String name) { - final lower = name.toLowerCase(); + var lower = _snake(name); return ''' @@ -287,6 +288,8 @@ class ${name}Service extends MongoTypedService<$name> { } _generateTests(String name, String type) { + var lower = _snake(name); + return ''' import 'dart:io'; import 'package:angel/angel.dart'; @@ -309,13 +312,13 @@ main() async { }); test('index via REST', () async { - final response = await client.get('/api/${name.toLowerCase()}'); + var response = await client.get('/api/${lower}s'); expect(response, hasStatus(HttpStatus.OK)); }); - test('Index ${name.toLowerCase()}s', () async { - final ${name.toLowerCase()}s = await client.service('api/${name.toLowerCase()}').index(); - print(${name.toLowerCase()}s); + test('Index ${lower}s', () async { + var ${lower}s = await client.service('api/${lower}s').index(); + print(${lower}s); }); } diff --git a/pubspec.yaml b/pubspec.yaml index dd53be52..1ec8a3c1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,14 @@ name: angel_cli -version: 1.0.0-dev+7 +version: 1.0.0-dev+8 description: Command-line tools for the Angel framework. +environment: + sdk: ">=1.19.0" author: Tobe O homepage: https://github.com/angel-dart/angel_cli executables: angel: angel dependencies: - analyzer: ">=0.28.1 <1.0.0" - args: ">=0.3.4 <1.0.0" - console: ">=2.2.3 <3.0.0" - yaml: ">=2.0.0 <3.0.0" \ No newline at end of file + args: ^0.13.7 + id: ^1.0.0 + console: ^2.2.3 + yaml: ^2.0.0 \ No newline at end of file From f25c40280d1f647ef05ab41676641eef3eaac16f Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 21 Dec 2016 16:33:07 -0500 Subject: [PATCH 021/132] 9 --- lib/src/commands/service.dart | 32 ++++++-------------------------- pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 7fd9cae4..7e76d97d 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -124,7 +124,7 @@ class $name extends MemoryModel { factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); factory $name.fromMap(Map data) => new $name( - id: data['id'] + id: data['id'], name: data['name'], desc: data['desc']); @@ -141,33 +141,13 @@ class $name extends MemoryModel { } _generateMemoryService(String name) { + var lower = _snake(name); + return ''' import 'package:angel_framework/defs.dart'; import 'package:angel_framework/angel_framework.dart'; - -/// Store in-memory instances of this class. -class $name extends MemoryModel { - String name, desc; - - $name({String id, this.name, this.desc}) { - this.id = id; - } - - factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); - - factory $name.fromMap(Map data) => new $name( - id: data['id'] - name: data['name'], - desc: data['desc']); - - Map toJson() { - return { - 'id': id, - 'name': name, - 'desc': desc - }; - } -} +import '../models/$lower.dart'; +export '../models/$lower.dart'; /// Manages [$name] in-memory. class ${name}Service extends MemoryService<$name> { @@ -201,7 +181,7 @@ class $name extends Model { factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); factory $name.fromMap(Map data) => new $name( - id: data['id'] + id: data['id'], name: data['name'], desc: data['desc']); diff --git a/pubspec.yaml b/pubspec.yaml index 1ec8a3c1..0b55f7d3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+8 +version: 1.0.0-dev+9 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From a179a5d47faabfe3a4dfd185e29b0c617c15e2de Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 21 Dec 2016 16:35:48 -0500 Subject: [PATCH 022/132] 10 --- lib/src/commands/service.dart | 1 - pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 7e76d97d..7990f82c 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -144,7 +144,6 @@ class $name extends MemoryModel { var lower = _snake(name); return ''' -import 'package:angel_framework/defs.dart'; import 'package:angel_framework/angel_framework.dart'; import '../models/$lower.dart'; export '../models/$lower.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 0b55f7d3..896a2c59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+9 +version: 1.0.0-dev+10 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From 4195927822d68931b0391ef022bee61698f81dfc Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 21 Dec 2016 16:37:45 -0500 Subject: [PATCH 023/132] 11 --- lib/src/commands/service.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 7990f82c..02e50107 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -282,7 +282,7 @@ main() async { setUp(() async { app = await createServer(); - client = await connectTo(app, saveSession: false); + client = await connectTo(app; }); tearDown(() async { diff --git a/pubspec.yaml b/pubspec.yaml index 896a2c59..5a55a928 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+10 +version: 1.0.0-dev+11 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From 6b36b314b73f2c86db55e8ee88798ec43c891b00 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 23 Dec 2016 23:42:55 -0500 Subject: [PATCH 024/132] key --- bin/angel.dart | 1 + lib/src/commands/commands.dart | 1 + lib/src/commands/init.dart | 23 +++++++++++--- lib/src/commands/key.dart | 37 ++++++++++++++++++++++ lib/src/commands/start.dart | 58 +++++++++++++++++++++++++++++----- pubspec.yaml | 7 ++-- 6 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 lib/src/commands/key.dart diff --git a/bin/angel.dart b/bin/angel.dart index d16d4266..f07a31f5 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -13,6 +13,7 @@ main(List args) { runner ..addCommand(new DoctorCommand()) + ..addCommand(new KeyCommand()) ..addCommand(new ServiceCommand()) ..addCommand(new InitCommand()) ..addCommand(new TestCommand()) diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 7e82dcc3..1cda4c4c 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -1,6 +1,7 @@ library angel_cli.commands; export "doctor.dart"; +export "key.dart"; export "init.dart"; export "plugin.dart"; export "service.dart"; diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 68adc2ad..856b0eb0 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -2,8 +2,11 @@ import "dart:convert"; import "dart:io"; import "package:args/command_runner.dart"; import "package:console/console.dart"; +import 'package:random_string/random_string.dart' as rs; +import 'key.dart'; class InitCommand extends Command { + final KeyCommand _key = new KeyCommand(); final TextPen _pen = new TextPen(); @override @@ -20,10 +23,20 @@ class InitCommand extends Command { Directory projectDir = new Directory( argResults.arguments.isEmpty ? "." : argResults.arguments[0]); print("Creating new Angel project in ${projectDir.absolute.path}..."); - await _cloneRepo(projectDir);_pen.green(); - _pen("${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); + await _cloneRepo(projectDir); + _pen.green(); + _pen( + "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); _pen(); await _pubGet(projectDir); + var secret = rs.randomAlphaNumeric(32); + print('Generated new JWT secret: $secret'); + await _key.changeSecret( + new File.fromUri(projectDir.uri.resolve('config/default.yaml')), + secret); + await _key.changeSecret( + new File.fromUri(projectDir.uri.resolve('config/production.yaml')), + secret); } _cloneRepo(Directory projectDir) async { @@ -55,8 +68,7 @@ class InitCommand extends Command { var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git")); - if (await gitDir.exists()) - await gitDir.delete(recursive: true); + if (await gitDir.exists()) await gitDir.delete(recursive: true); } catch (e) { print(e); _pen.red(); @@ -67,7 +79,8 @@ class InitCommand extends Command { } _pubGet(Directory projectDir) async { - var pub = await Process.start("pub", ["get"], workingDirectory: projectDir.absolute.path); + var pub = await Process.start("pub", ["get"], + workingDirectory: projectDir.absolute.path); pub.stdout.pipe(stdout); pub.stderr.pipe(stderr); var code = await pub.exitCode; diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart new file mode 100644 index 00000000..5f311e33 --- /dev/null +++ b/lib/src/commands/key.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:random_string/random_string.dart' as rs; + +class KeyCommand extends Command { + @override + String get name => 'key'; + + @override + String get description => 'Generates a new `angel_auth`key.'; + + @override + run() async { + var secret = rs.randomAlphaNumeric(32); + print('Generated new JWT secret: $secret'); + await changeSecret(new File('config/default.yaml'), secret); + await changeSecret(new File('config/production.yaml'), secret); + } + + changeSecret(File file, String secret) async { + if (await file.exists()) { + var sink = await file.openWrite(); + + await for (var chunk + in await file.openRead().transform(UTF8.decoder)) { + var lines = chunk.split('\n'); + + for (String line in lines) { + if (line.contains('jwt_secret:')) { + sink.writeln('jwt_secret: $secret'); + } else sink.writeln(line); + } + } + } + } +} diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index 1524adab..9950a2e1 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -1,7 +1,11 @@ import 'dart:io'; import 'package:args/command_runner.dart'; +import 'package:watcher/watcher.dart'; import 'package:yaml/yaml.dart'; +Process server; +bool watching = false; + class StartCommand extends Command { @override String get name => 'start'; @@ -11,14 +15,38 @@ class StartCommand extends Command { 'Runs any `start` scripts, and then runs the server.'; StartCommand() : super() { - argParser.addFlag('production', - help: 'Starts the server in production mode.', - negatable: false, - defaultsTo: false); + argParser + ..addFlag('production', + help: 'Starts the server in production mode.', + negatable: false, + defaultsTo: false) + ..addFlag('watch', + abbr: 'w', + help: 'Restart the server on file changes.', + defaultsTo: true); } @override run() async { + if (argResults['watch']) { + new DirectoryWatcher('bin').events.listen((_) async => start()); + new DirectoryWatcher('config').events.listen((_) async => start()); + new DirectoryWatcher('lib').events.listen((_) async => start()); + } + + return await start(); + } + + start() async { + bool isNew = true; + if (server != null) { + isNew = false; + + if (!server.kill()) { + throw new Exception('Could not kill existing server process.'); + } + } + final pubspec = new File('pubspec.yaml'); if (await pubspec.exists()) { @@ -43,16 +71,30 @@ class StartCommand extends Command { } } - print('Starting server...'); + if (isNew) + print('Starting server...'); + else + print('Changes detected - restarting server...'); final env = {}; if (argResults['production']) env['ANGEL_ENV'] = 'production'; - final server = await Process.start(Platform.executable, ['bin/server.dart'], + server = await Process.start(Platform.executable, ['bin/server.dart'], environment: env); - server.stdout.pipe(stdout); - server.stderr.pipe(stderr); + + try { + if (isNew) { + server.stdout.pipe(stdout); + server.stderr.pipe(stderr); + } + } catch (e) { + print(e); + } + + if (!isNew) { + print('Successfully restarted server.'); + } exitCode = await server.exitCode; } diff --git a/pubspec.yaml b/pubspec.yaml index 5a55a928..78bbc2db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+11 +version: 1.0.0-dev+12 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" @@ -9,6 +9,9 @@ executables: angel: angel dependencies: args: ^0.13.7 - id: ^1.0.0 console: ^2.2.3 + glob: ^1.1.0 + id: ^1.0.0 + random_string: ^0.0.1 + watcher: ^0.9.7 yaml: ^2.0.0 \ No newline at end of file From d4d55d6a8f5fa3ba7c9b3fc2b604cf1c0f19d675 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 23 Dec 2016 23:45:37 -0500 Subject: [PATCH 025/132] 13 --- lib/src/commands/key.dart | 11 ++++++++--- pubspec.yaml | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index 5f311e33..41524e8e 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -20,18 +20,23 @@ class KeyCommand extends Command { changeSecret(File file, String secret) async { if (await file.exists()) { + bool foundSecret = false; var sink = await file.openWrite(); - await for (var chunk - in await file.openRead().transform(UTF8.decoder)) { + await for (var chunk in await file.openRead().transform(UTF8.decoder)) { var lines = chunk.split('\n'); for (String line in lines) { if (line.contains('jwt_secret:')) { + foundSecret = true; sink.writeln('jwt_secret: $secret'); - } else sink.writeln(line); + } else + sink.writeln(line); } } + + if (!foundSecret) sink.writeln('jwt:secret: $secret'); + await sink.close(); } } } diff --git a/pubspec.yaml b/pubspec.yaml index 78bbc2db..78527b67 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+12 +version: 1.0.0-dev+13 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From e98a94640a01bfff62216878a69417f2c5f59d1c Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 23 Dec 2016 23:48:17 -0500 Subject: [PATCH 026/132] 14 --- lib/src/commands/key.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index 41524e8e..b25f5340 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -21,7 +21,7 @@ class KeyCommand extends Command { changeSecret(File file, String secret) async { if (await file.exists()) { bool foundSecret = false; - var sink = await file.openWrite(); + var sink = await file.openWrite(mode: FileMode.APPEND); await for (var chunk in await file.openRead().transform(UTF8.decoder)) { var lines = chunk.split('\n'); From 8d3329eb0a26033e275892b4ef4c9a764f3ab60d Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 23 Dec 2016 23:48:33 -0500 Subject: [PATCH 027/132] 14 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 78527b67..185977ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+13 +version: 1.0.0-dev+14 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From f9554a176d8dedac7bfa33a1e2dd4a512c1d880d Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 24 Dec 2016 00:24:40 -0500 Subject: [PATCH 028/132] Key done --- lib/src/commands/key.dart | 20 +++----------------- pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index b25f5340..9ca570e5 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -20,23 +20,9 @@ class KeyCommand extends Command { changeSecret(File file, String secret) async { if (await file.exists()) { - bool foundSecret = false; - var sink = await file.openWrite(mode: FileMode.APPEND); - - await for (var chunk in await file.openRead().transform(UTF8.decoder)) { - var lines = chunk.split('\n'); - - for (String line in lines) { - if (line.contains('jwt_secret:')) { - foundSecret = true; - sink.writeln('jwt_secret: $secret'); - } else - sink.writeln(line); - } - } - - if (!foundSecret) sink.writeln('jwt:secret: $secret'); - await sink.close(); + var contents = await file.readAsString(); + contents = contents.replaceAll(new RegExp(r'jwt_secret:[^\n]+\n?'), ''); + await file.writeAsString(contents.trim() + '\njwt_secret: $secret'); } } } diff --git a/pubspec.yaml b/pubspec.yaml index 185977ec..80f82d27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+14 +version: 1.0.0-dev+15 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From 994a4cf97b4e7c7fdf342aa645a08ec8136b8a69 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 31 Dec 2016 11:39:00 -0500 Subject: [PATCH 029/132] Fixed --- lib/src/commands/init.dart | 22 +++++++++-- lib/src/commands/service.dart | 72 +++++++++++++++++------------------ pubspec.yaml | 2 +- 3 files changed, 55 insertions(+), 41 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 856b0eb0..fe1024b5 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -29,6 +29,7 @@ class InitCommand extends Command { "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); _pen(); await _pubGet(projectDir); + await _preBuild(projectDir); var secret = rs.randomAlphaNumeric(32); print('Generated new JWT secret: $secret'); await _key.changeSecret( @@ -59,8 +60,8 @@ class InitCommand extends Command { projectDir.absolute.path ]); - git.stdout.transform(UTF8.decoder).listen(stdout.write); - git.stderr.transform(UTF8.decoder).listen(stderr.write); + stdout.addStream(git.stdout); + stderr.addStream(git.stderr); if (await git.exitCode != 0) { throw new Exception("Could not clone repo."); @@ -78,11 +79,24 @@ class InitCommand extends Command { } } + _preBuild(Directory projectDir) async { + // Run build + var build = await Process.start(Platform.executable, ['tool/build.dart'], + workingDirectory: projectDir.absolute.path); + + stdout.addStream(build.stdout); + stderr.addStream(build.stderr); + + var buildCode = await build.exitCode; + + if (buildCode != 0) throw new Exception('Failed to pre-build resources.'); + } + _pubGet(Directory projectDir) async { var pub = await Process.start("pub", ["get"], workingDirectory: projectDir.absolute.path); - pub.stdout.pipe(stdout); - pub.stderr.pipe(stderr); + stdout.addStream(pub.stdout); + stderr.addStream(pub.stderr); var code = await pub.exitCode; print("Pub process exited with code $code"); } diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 02e50107..c550a58e 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -36,12 +36,12 @@ class ServiceCommand extends Command { if (type == MONGO) { serviceSource = _generateMongoService(name); - await _generateMemoryModel(name); } else if (type == MONGO_TYPED) { serviceSource = _generateMongoTypedService(name); await _generateMongoModel(name); } else if (type == MEMORY) { serviceSource = _generateMemoryService(name); + await _generateMemoryModel(name); } else if (type == CUSTOM) { serviceSource = _generateCustomService(name); } else if (type == TRESTLE) { @@ -113,28 +113,23 @@ library angel.models.$lower; import 'dart:convert'; import 'package:angel_framework/angel_framework.dart'; +import 'package:source_gen/generators/json_serializable.dart'; -class $name extends MemoryModel { - String name, desc; +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends MemoryModel with _\$${name}SerializerMixin { + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}fromJson(json); $name({String id, this.name, this.desc}) { this.id = id; } - - factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); - - factory $name.fromMap(Map data) => new $name( - id: data['id'], - name: data['name'], - desc: data['desc']); - - Map toJson() { - return { - 'id': id, - 'name': name, - 'desc': desc - }; - } } ''' .trim()); @@ -169,28 +164,23 @@ library angel.models.$lower; import 'dart:convert'; import 'package:angel_mongo/model.dart'; +import 'package:source_gen/generators/json_serializable.dart'; -class $name extends Model { - String name, desc; +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}fromJson(json); $name({String id, this.name, this.desc}) { this.id = id; } - - factory $name.fromJson(String json) => new $name.fromMap(JSON.decode(json)); - - factory $name.fromMap(Map data) => new $name( - id: data['id'], - name: data['name'], - desc: data['desc']); - - Map toJson() { - return { - 'id': id, - 'name': name, - 'desc': desc - }; - } } ''' .trim()); @@ -202,14 +192,24 @@ class $name extends Model { return ''' import 'package:angel_framework/angel_framework.dart'; import 'package:angel_mongo/angel_mongo.dart'; +import 'package:angel_validate/angel_validate.dart'; import 'package:mongo_dart/mongo_dart.dart'; +final Validator ${lower}Schema = new Validator({ + 'name*': [isString, isNotEmpty], + 'desc*': [isString, isNotEmpty] +}); + configureServer(Db db) { return (Angel app) async { app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); HookedService service = app.service('api/${lower}s'); app.container.singleton(service.inner); + + service + ..beforeCreate.listen(validateEvent(${lower}Schema)) + ..beforeUpdate.listen(validateEvent(${lower}Schema)); }; } @@ -282,7 +282,7 @@ main() async { setUp(() async { app = await createServer(); - client = await connectTo(app; + client = await connectTo(app); }); tearDown(() async { diff --git a/pubspec.yaml b/pubspec.yaml index 80f82d27..cac7195c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+15 +version: 1.0.0-dev+16 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From 07d48ca35e4e82f357d9232b785355b9fb8c07f2 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 31 Dec 2016 11:50:35 -0500 Subject: [PATCH 030/132] 16 --- lib/src/commands/init.dart | 31 ++++++++++++++++--------------- lib/src/commands/key.dart | 1 - lib/src/commands/service.dart | 11 +++++++++++ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index fe1024b5..73fb543a 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -1,4 +1,3 @@ -import "dart:convert"; import "dart:io"; import "package:args/command_runner.dart"; import "package:console/console.dart"; @@ -29,7 +28,7 @@ class InitCommand extends Command { "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); _pen(); await _pubGet(projectDir); - await _preBuild(projectDir); + await preBuild(projectDir); var secret = rs.randomAlphaNumeric(32); print('Generated new JWT secret: $secret'); await _key.changeSecret( @@ -79,19 +78,6 @@ class InitCommand extends Command { } } - _preBuild(Directory projectDir) async { - // Run build - var build = await Process.start(Platform.executable, ['tool/build.dart'], - workingDirectory: projectDir.absolute.path); - - stdout.addStream(build.stdout); - stderr.addStream(build.stderr); - - var buildCode = await build.exitCode; - - if (buildCode != 0) throw new Exception('Failed to pre-build resources.'); - } - _pubGet(Directory projectDir) async { var pub = await Process.start("pub", ["get"], workingDirectory: projectDir.absolute.path); @@ -101,3 +87,18 @@ class InitCommand extends Command { print("Pub process exited with code $code"); } } + +preBuild(Directory projectDir) async { + // Run build + print('Pre-building resources...'); + + var build = await Process.start(Platform.executable, ['tool/build.dart'], + workingDirectory: projectDir.absolute.path); + + stdout.addStream(build.stdout); + stderr.addStream(build.stderr); + + var buildCode = await build.exitCode; + + if (buildCode != 0) throw new Exception('Failed to pre-build resources.'); +} diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index 9ca570e5..93fff9fa 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:random_string/random_string.dart' as rs; diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index c550a58e..4ab13fc6 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:console/console.dart'; import 'package:id/id.dart'; +import 'init.dart' show preBuild; class ServiceCommand extends Command { final String CUSTOM = 'Custom'; @@ -73,6 +74,7 @@ class ServiceCommand extends Command { var serviceLibrary = new File('lib/src/models/models.dart'); await serviceLibrary.writeAsString("\nexport '$lower.dart';", mode: FileMode.APPEND); + await preBuild(Directory.current); } await testFile.writeAsString(_generateTests(name, type)); @@ -119,6 +121,10 @@ part '$lower.g.dart'; @JsonSerializable() class $name extends MemoryModel with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + @JsonKey('name') String name; @@ -170,6 +176,10 @@ part '$lower.g.dart'; @JsonSerializable() class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + @JsonKey('name') String name; @@ -193,6 +203,7 @@ class $name extends Model with _\$${name}SerializerMixin { import 'package:angel_framework/angel_framework.dart'; import 'package:angel_mongo/angel_mongo.dart'; import 'package:angel_validate/angel_validate.dart'; +import 'package:angel_validate/server.dart'; import 'package:mongo_dart/mongo_dart.dart'; final Validator ${lower}Schema = new Validator({ From 6e2030fdf439b24865c88f8cd272bf6ba915c5da Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 31 Dec 2016 13:19:51 -0500 Subject: [PATCH 031/132] 17 --- lib/src/commands/init.dart | 7 +++++-- lib/src/commands/key.dart | 5 ++++- pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 73fb543a..fede5e22 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -30,10 +30,13 @@ class InitCommand extends Command { await _pubGet(projectDir); await preBuild(projectDir); var secret = rs.randomAlphaNumeric(32); - print('Generated new JWT secret: $secret'); + print('Generated new development JWT secret: $secret'); await _key.changeSecret( new File.fromUri(projectDir.uri.resolve('config/default.yaml')), secret); + + secret = rs.randomAlphaNumeric(32); + print('Generated new production JWT secret: $secret'); await _key.changeSecret( new File.fromUri(projectDir.uri.resolve('config/production.yaml')), secret); @@ -91,7 +94,7 @@ class InitCommand extends Command { preBuild(Directory projectDir) async { // Run build print('Pre-building resources...'); - + var build = await Process.start(Platform.executable, ['tool/build.dart'], workingDirectory: projectDir.absolute.path); diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index 93fff9fa..56b922d7 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -12,8 +12,11 @@ class KeyCommand extends Command { @override run() async { var secret = rs.randomAlphaNumeric(32); - print('Generated new JWT secret: $secret'); + print('Generated new development JWT secret: $secret'); await changeSecret(new File('config/default.yaml'), secret); + + secret = rs.randomAlphaNumeric(32); + print('Generated new production JWT secret: $secret'); await changeSecret(new File('config/production.yaml'), secret); } diff --git a/pubspec.yaml b/pubspec.yaml index cac7195c..fc31d272 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+16 +version: 1.0.0-dev+17 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From a3ee18e8c937ed9de6aa0e817647eaa000ccfb0a Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 31 Dec 2016 13:24:36 -0500 Subject: [PATCH 032/132] 18 --- lib/src/commands/service.dart | 6 ++---- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 4ab13fc6..8325e571 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -113,7 +113,6 @@ class ${name}Service extends Service { await file.writeAsString(''' library angel.models.$lower; -import 'dart:convert'; import 'package:angel_framework/angel_framework.dart'; import 'package:source_gen/generators/json_serializable.dart'; @@ -131,7 +130,7 @@ class $name extends MemoryModel with _\$${name}SerializerMixin { @JsonKey('desc') String desc; - factory $name.fromJson(Map json) => _\$${name}fromJson(json); + factory $name.fromJson(Map json) => _\$${name}FromJson(json); $name({String id, this.name, this.desc}) { this.id = id; @@ -168,7 +167,6 @@ class ${name}Service extends MemoryService<$name> { await file.writeAsString(''' library angel.models.$lower; -import 'dart:convert'; import 'package:angel_mongo/model.dart'; import 'package:source_gen/generators/json_serializable.dart'; @@ -186,7 +184,7 @@ class $name extends Model with _\$${name}SerializerMixin { @JsonKey('desc') String desc; - factory $name.fromJson(Map json) => _\$${name}fromJson(json); + factory $name.fromJson(Map json) => _\$${name}FromJson(json); $name({String id, this.name, this.desc}) { this.id = id; diff --git a/pubspec.yaml b/pubspec.yaml index fc31d272..cec3a79a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_cli -version: 1.0.0-dev+17 +version: 1.0.0-dev+18 description: Command-line tools for the Angel framework. environment: sdk: ">=1.19.0" From 8ad5fff04cac75720ed5a75d80c017a50818d372 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 1 Feb 2017 17:56:04 -0500 Subject: [PATCH 033/132] 19 --- bin/angel.dart | 5 +- lib/angel_cli.dart | 2 +- lib/src/commands/commands.dart | 1 + lib/src/commands/key.dart | 2 +- lib/src/commands/rename.dart | 136 +++++++++++++++++++++++++++++++++ lib/src/commands/service.dart | 8 +- lib/src/commands/start.dart | 23 ++++-- pubspec.yaml | 34 +++++---- 8 files changed, 179 insertions(+), 32 deletions(-) create mode 100644 lib/src/commands/rename.dart diff --git a/bin/angel.dart b/bin/angel.dart index f07a31f5..f8919c4c 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -1,5 +1,5 @@ #!/usr/bin/env dart -library angel_cli.tool; +library demon.tool; import "dart:io"; import "package:args/command_runner.dart"; @@ -18,7 +18,8 @@ main(List args) { ..addCommand(new InitCommand()) ..addCommand(new TestCommand()) ..addCommand(new PluginCommand()) - ..addCommand(new StartCommand()); + ..addCommand(new StartCommand()) + ..addCommand(new RenameCommand()); return runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/lib/angel_cli.dart b/lib/angel_cli.dart index 4d3fa997..3a879ac2 100644 --- a/lib/angel_cli.dart +++ b/lib/angel_cli.dart @@ -1,3 +1,3 @@ -library angel_cli; +library demon; export 'src/commands/commands.dart'; \ No newline at end of file diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 1cda4c4c..bccb48f1 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -4,6 +4,7 @@ export "doctor.dart"; export "key.dart"; export "init.dart"; export "plugin.dart"; +export "rename.dart"; export "service.dart"; export "start.dart"; export "test.dart"; \ No newline at end of file diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index 56b922d7..f2a66f7a 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -24,7 +24,7 @@ class KeyCommand extends Command { if (await file.exists()) { var contents = await file.readAsString(); contents = contents.replaceAll(new RegExp(r'jwt_secret:[^\n]+\n?'), ''); - await file.writeAsString(contents.trim() + '\njwt_secret: $secret'); + await file.writeAsString(contents.trim() + '\njwt_secret: "$secret"'); } } } diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart new file mode 100644 index 00000000..cfa9beea --- /dev/null +++ b/lib/src/commands/rename.dart @@ -0,0 +1,136 @@ +import 'dart:io'; +import 'package:analyzer/analyzer.dart'; +import 'package:args/command_runner.dart'; +import 'package:console/console.dart'; +import 'package:pubspec/pubspec.dart'; + +class RenameCommand extends Command { + @override + String get name => 'rename'; + + @override + String get description => 'Renames the current project.'; + + @override + String get invocation => '$name '; + + @override + run() async { + String newName; + + if (argResults.rest.isNotEmpty) + newName = argResults.rest.first; + else { + var p = new Prompter('Enter new project name: '); + newName = await p.prompt(checker: (String str) => str.isNotEmpty); + } + + var ch = new Chooser(['Yes', 'No'], + message: 'Rename the project to `$newName`? '); + var choice = await ch.choose(); + + if (choice == 'Yes') { + print('Renaming project to `$newName`...'); + var pubspecFile = + new File.fromUri(Directory.current.uri.resolve('pubspec.yaml')); + + if (!await pubspecFile.exists()) { + throw new Exception('No pubspec.yaml found in current directory.'); + } else { + var pubspec = await PubSpec.load(Directory.current); + var oldName = pubspec.name; + var newPubspec = + new PubSpec.fromJson(pubspec.toJson()..['name'] = newName); + await newPubspec.save(Directory.current); + await renameDartFiles(oldName, newName); + print('Now running `pub get`...'); + var pub = await Process.start('pub', ['get']); + stdout.addStream(pub.stdout); + stderr.addStream(pub.stderr); + await pub.exitCode; + } + } + } + + renameDartFiles(String oldName, String newName) async { + var entry = + new File.fromUri(Directory.current.uri.resolve('lib/$oldName.dart')); + + if (await entry.exists()) { + await entry.rename('lib/$newName.dart'); + print('Renaming library file `${entry.absolute.path}`...'); + } + + await for (FileSystemEntity file + in Directory.current.list(recursive: true)) { + if (file is File && file.path.endsWith('.dart')) { + var contents = await file.readAsString(); + var ast = parseCompilationUnit(contents); + var visitor = new RenamingVisitor(oldName, newName) + ..visitCompilationUnit(ast); + + if (visitor.replace.isNotEmpty) { + visitor.replace.forEach((range, replacement) { + if (range.first is int) { + contents = + contents.replaceRange(range.first, range.last, replacement); + } else if (range.first is String) { + contents = contents.replaceAll(range.first, replacement); + } + }); + + await file.writeAsString(contents); + print('Updated file `${file.absolute.path}`.'); + } + } + } + } +} + +class RenamingVisitor extends RecursiveAstVisitor { + final String oldName, newName; + final Map replace = {}; + + RenamingVisitor(this.oldName, this.newName); + + String updateUri(String uri) { + if (uri == 'package:$oldName/$oldName.dart') { + return 'package:$newName/$newName.dart'; + } else if (uri.startsWith('package:$oldName/')) { + return 'package:$newName/' + uri.replaceFirst('package:$oldName/', ''); + } else + return uri; + } + + @override + visitExportDirective(ExportDirective ctx) { + var uri = ctx.uri.stringValue, updated = updateUri(uri); + if (uri != updated) replace[[uri]] = updated; + } + + @override + visitImportDirective(ImportDirective ctx) { + var uri = ctx.uri.stringValue, updated = updateUri(uri); + if (uri != updated) replace[[uri]] = updated; + } + + @override + visitLibraryDirective(LibraryDirective ctx) { + var name = ctx.name.name; + + if (name.startsWith(oldName)) { + replace[[ctx.offset, ctx.end]] = + 'library ' + name.replaceFirst(oldName, newName) + ';'; + } + } + + @override + visitPartOfDirective(PartOfDirective ctx) { + var name = ctx.libraryName.name; + + if (name.startsWith(oldName)) { + replace[[ctx.offset, ctx.end]] = + 'part of ' + name.replaceFirst(oldName, newName) + ';'; + } + } +} diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 8325e571..d238edef 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -132,9 +132,7 @@ class $name extends MemoryModel with _\$${name}SerializerMixin { factory $name.fromJson(Map json) => _\$${name}FromJson(json); - $name({String id, this.name, this.desc}) { - this.id = id; - } + $name({this.id, this.name, this.desc}); } ''' .trim()); @@ -186,9 +184,7 @@ class $name extends Model with _\$${name}SerializerMixin { factory $name.fromJson(Map json) => _\$${name}FromJson(json); - $name({String id, this.name, this.desc}) { - this.id = id; - } + $name({this.id, this.name, this.desc}); } ''' .trim()); diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index 9950a2e1..c340ed7a 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -16,6 +16,10 @@ class StartCommand extends Command { StartCommand() : super() { argParser + ..addFlag('multi', + help: 'Starts bin/multi_server.dart, instead of the standard server.', + negatable: false, + defaultsTo: false) ..addFlag('production', help: 'Starts the server in production mode.', negatable: false, @@ -58,8 +62,8 @@ class StartCommand extends Command { try { var scripts = await Process.start('pub', ['global', 'run', 'scripts', 'start']); - scripts.stdout.pipe(stdout); - scripts.stderr.pipe(stderr); + stdout.addStream(scripts.stdout); + stderr.addStream(scripts.stderr); int code = await scripts.exitCode; if (code != 0) { @@ -80,20 +84,27 @@ class StartCommand extends Command { if (argResults['production']) env['ANGEL_ENV'] = 'production'; - server = await Process.start(Platform.executable, ['bin/server.dart'], + server = await Process.start( + Platform.executable, + [ + argResults['multi'] == true + ? 'bin/multi_server.dart' + : 'bin/server.dart' + ], environment: env); try { if (isNew) { - server.stdout.pipe(stdout); - server.stderr.pipe(stderr); + stdout.addStream(server.stdout); + stderr.addStream(server.stderr); } } catch (e) { print(e); } if (!isNew) { - print('Successfully restarted server.'); + print( + '${new DateTime.now().toIso8601String()}Successfully restarted server.'); } exitCode = await server.exitCode; diff --git a/pubspec.yaml b/pubspec.yaml index cec3a79a..ae02f9ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,19 @@ -name: angel_cli -version: 1.0.0-dev+18 -description: Command-line tools for the Angel framework. -environment: +author: "Tobe O " +description: "Command-line tools for the Angel framework." +homepage: "https://github.com/angel-dart/angel_cli" +name: "angel_cli" +version: "1.0.0-dev+19" +dependencies: + analyzer: "^0.29.0" + args: "^0.13.7" + console: "^2.2.3" + glob: "^1.1.0" + id: "^1.0.0" + pubspec: "^0.0.14" + random_string: "^0.0.1" + watcher: "^0.9.7" + yaml: "^2.0.0" +environment: sdk: ">=1.19.0" -author: Tobe O -homepage: https://github.com/angel-dart/angel_cli -executables: - angel: angel -dependencies: - args: ^0.13.7 - console: ^2.2.3 - glob: ^1.1.0 - id: ^1.0.0 - random_string: ^0.0.1 - watcher: ^0.9.7 - yaml: ^2.0.0 \ No newline at end of file +executables: + angel: "angel" From 9afc4eb546044fbbc8582b113ff4eb9342f5091a Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 1 Feb 2017 18:34:31 -0500 Subject: [PATCH 034/132] 20 --- lib/src/commands/start.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index c340ed7a..04dc19df 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -104,7 +104,7 @@ class StartCommand extends Command { if (!isNew) { print( - '${new DateTime.now().toIso8601String()}Successfully restarted server.'); + '${new DateTime.now().toIso8601String()}: Successfully restarted server.'); } exitCode = await server.exitCode; diff --git a/pubspec.yaml b/pubspec.yaml index ae02f9ee..3c5279fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.0-dev+19" +version: "1.0.0-dev+20" dependencies: analyzer: "^0.29.0" args: "^0.13.7" From f07211328f818de07bb6b0de9d881cc4b4254664 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sun, 19 Feb 2017 07:54:29 -0500 Subject: [PATCH 035/132] 1.0.0 --- lib/src/commands/service.dart | 94 +++++++++++++++++++++++++++++++++-- pubspec.yaml | 3 +- 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index d238edef..ad970320 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -2,13 +2,17 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:console/console.dart'; import 'package:id/id.dart'; +import 'package:recase/recase.dart'; import 'init.dart' show preBuild; class ServiceCommand extends Command { final String CUSTOM = 'Custom'; final String MEMORY = 'In-Memory'; + final String MEMORY_JSON = 'In-Memory (serialized via `source_gen`)'; final String MONGO = 'MongoDB'; final String MONGO_TYPED = 'MongoDB (typed)'; + final String MONGO_TYPED_JSON = + 'MongoDB (typed, serialized via `source_gen`)'; final String TRESTLE = 'Trestle'; final TextPen _pen = new TextPen(); @@ -23,7 +27,7 @@ class ServiceCommand extends Command { @override run() async { var name = await readInput('Name of Service (not plural): '); - var chooser = new Chooser([TRESTLE, MONGO, MONGO_TYPED, MEMORY, CUSTOM], + var chooser = new Chooser([MONGO, MONGO_TYPED, MEMORY, CUSTOM], message: 'What type of service would you like to create? '); var type = await chooser.choose(); @@ -40,9 +44,19 @@ class ServiceCommand extends Command { } else if (type == MONGO_TYPED) { serviceSource = _generateMongoTypedService(name); await _generateMongoModel(name); + await _generateValidator(name); + } else if (type == MONGO_TYPED_JSON) { + serviceSource = _generateMongoTypedService(name); + await _generateMongoModelJson(name); + await _generateValidator(name); } else if (type == MEMORY) { serviceSource = _generateMemoryService(name); await _generateMemoryModel(name); + await _generateValidator(name); + } else if (type == MEMORY_JSON) { + serviceSource = _generateMemoryService(name); + await _generateMemoryModelJson(name); + await _generateValidator(name); } else if (type == CUSTOM) { serviceSource = _generateCustomService(name); } else if (type == TRESTLE) { @@ -91,6 +105,21 @@ class ServiceCommand extends Command { _pen(); } + _generateValidator(String name) async { + var rc = new ReCase(name); + var file = new File('lib/src/validators/${rc.snakeCase}.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +import 'package:angel_validate/angel_validate.dart'; + +final Validator CREATE_${rc.constantCase} = + new Validator({'name*': isString, 'desc*': isString}); + ''' + .trim()); + } + _generateCustomService(String name) { return ''' import 'package:angel_framework/angel_framework.dart'; @@ -113,13 +142,35 @@ class ${name}Service extends Service { await file.writeAsString(''' library angel.models.$lower; -import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/common.dart'; + +class $name extends Model { + String name, desc; + + $name({String id, this.name, this.desc}) { + this.id = id; + } +} + ''' + .trim()); + } + + _generateMemoryModelJson(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; import 'package:source_gen/generators/json_serializable.dart'; part '$lower.g.dart'; @JsonSerializable() -class $name extends MemoryModel with _\$${name}SerializerMixin { +class $name extends Model with _\$${name}SerializerMixin { @JsonKey('id') @override String id; @@ -139,7 +190,8 @@ class $name extends MemoryModel with _\$${name}SerializerMixin { } _generateMemoryService(String name) { - var lower = _snake(name); + var rc = new ReCase(name); + var lower = rc.snakeCase; return ''' import 'package:angel_framework/angel_framework.dart'; @@ -170,6 +222,40 @@ import 'package:source_gen/generators/json_serializable.dart'; part '$lower.g.dart'; +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}FromJson(json); + + $name({this.id, this.name, this.desc}); +} + ''' + .trim()); + } + + _generateMongoModelJson(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; +import 'package:source_gen/generators/json_serializable.dart'; + +part '$lower.g.dart'; + @JsonSerializable() class $name extends Model with _\$${name}SerializerMixin { @JsonKey('id') diff --git a/pubspec.yaml b/pubspec.yaml index 3c5279fc..3d466f53 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.0-dev+20" +version: "1.0.0" dependencies: analyzer: "^0.29.0" args: "^0.13.7" @@ -11,6 +11,7 @@ dependencies: id: "^1.0.0" pubspec: "^0.0.14" random_string: "^0.0.1" + recase: "^1.0.0" watcher: "^0.9.7" yaml: "^2.0.0" environment: From 80a31575aa8d8d113f86817dceaebb265faaee79 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 22 Feb 2017 21:48:08 -0500 Subject: [PATCH 036/132] 1.0.1 --- lib/src/commands/service.dart | 427 ++++++------------ .../commands/service_generators/custom.dart | 24 + .../service_generators/generator.dart | 24 + lib/src/commands/service_generators/map.dart | 15 + .../commands/service_generators/mongo.dart | 29 ++ .../commands/service_generators/rethink.dart | 31 ++ .../service_generators.dart | 5 + lib/src/commands/service_old.dart | 398 ++++++++++++++++ pubspec.yaml | 4 +- 9 files changed, 662 insertions(+), 295 deletions(-) create mode 100644 lib/src/commands/service_generators/custom.dart create mode 100644 lib/src/commands/service_generators/generator.dart create mode 100644 lib/src/commands/service_generators/map.dart create mode 100644 lib/src/commands/service_generators/mongo.dart create mode 100644 lib/src/commands/service_generators/rethink.dart create mode 100644 lib/src/commands/service_generators/service_generators.dart create mode 100644 lib/src/commands/service_old.dart diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index ad970320..73090211 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -1,19 +1,20 @@ import 'dart:io'; import 'package:args/command_runner.dart'; +import 'package:code_builder/code_builder.dart'; import 'package:console/console.dart'; -import 'package:id/id.dart'; +import 'package:inflection/inflection.dart'; import 'package:recase/recase.dart'; +import 'service_generators/service_generators.dart'; import 'init.dart' show preBuild; +const List GENERATORS = const [ + const MapServiceGenerator(), + const MongoServiceGenerator(), + const RethinkServiceGenerator(), + const CustomServiceGenerator() +]; + class ServiceCommand extends Command { - final String CUSTOM = 'Custom'; - final String MEMORY = 'In-Memory'; - final String MEMORY_JSON = 'In-Memory (serialized via `source_gen`)'; - final String MONGO = 'MongoDB'; - final String MONGO_TYPED = 'MongoDB (typed)'; - final String MONGO_TYPED_JSON = - 'MongoDB (typed, serialized via `source_gen`)'; - final String TRESTLE = 'Trestle'; final TextPen _pen = new TextPen(); @override @@ -22,130 +23,146 @@ class ServiceCommand extends Command { @override String get description => 'Creates a new service within the given project.'; - String _snake(name) => idFromString(name).snake; - @override run() async { var name = await readInput('Name of Service (not plural): '); - var chooser = new Chooser([MONGO, MONGO_TYPED, MEMORY, CUSTOM], + var chooser = new Chooser( + GENERATORS.map((g) => g.name).toList(), message: 'What type of service would you like to create? '); var type = await chooser.choose(); - fail() { - _pen.red(); - _pen('Could not successfully create service $name.'); - _pen(); - } + print('Wrap this service in a TypedService? (slight performance cost)'); + chooser = new Chooser(['Yes', 'No']); + var typed = (await chooser.choose()) == 'Yes'; - String serviceSource = ''; + var generator = + GENERATORS.firstWhere((g) => g.name == type, orElse: () => null); - if (type == MONGO) { - serviceSource = _generateMongoService(name); - } else if (type == MONGO_TYPED) { - serviceSource = _generateMongoTypedService(name); - await _generateMongoModel(name); - await _generateValidator(name); - } else if (type == MONGO_TYPED_JSON) { - serviceSource = _generateMongoTypedService(name); - await _generateMongoModelJson(name); - await _generateValidator(name); - } else if (type == MEMORY) { - serviceSource = _generateMemoryService(name); - await _generateMemoryModel(name); - await _generateValidator(name); - } else if (type == MEMORY_JSON) { - serviceSource = _generateMemoryService(name); - await _generateMemoryModelJson(name); - await _generateValidator(name); - } else if (type == CUSTOM) { - serviceSource = _generateCustomService(name); - } else if (type == TRESTLE) { + if (generator == null) { _pen.blue(); - _pen('${Icon.STAR} Trestle services are not yet implemented. :('); + _pen('${Icon.STAR} \'$type\' services are not yet implemented. :('); _pen(); } else { - print('Code to generate a $type service is not yet written.'); + var rc = new ReCase(name); + name = rc.pascalCase; + var lower = rc.snakeCase; + var servicesDir = new Directory('lib/src/services'); + var serviceFile = + new File.fromUri(servicesDir.uri.resolve('$lower.dart')); + var testDir = new Directory('test/services'); + var testFile = + new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); + + if (!await servicesDir.exists()) + await servicesDir.create(recursive: true); + if (!await testDir.exists()) await testDir.create(recursive: true); + + await serviceFile + .writeAsString(_generateService(generator, name, lower, typed)); + await testFile.writeAsString(_generateTests(lower, type)); + + var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); + + if (!await runConfig.exists()) { + await runConfig.create(recursive: true); + await runConfig.writeAsString(_generateRunConfiguration(name, lower)); + } + + if (generator.createsModel == true) { + await _generateModel(name, lower); + } + + if (generator.createsValidator == true) { + await _generateValidator(lower, rc.constantCase); + } + + if (generator.exportedInServiceLibrary == true) { + var serviceLibrary = new File('lib/src/models/models.dart'); + await serviceLibrary.writeAsString("\nexport '$lower.dart';", + mode: FileMode.APPEND); + } + + if (generator.shouldRunBuild == true) { + await preBuild(Directory.current); + } + + _pen.green(); + _pen('${Icon.CHECKMARK} Successfully generated service $name.'); + _pen(); } - - if (serviceSource.isEmpty) { - fail(); - throw new Exception('Empty generated service code.'); - } - - var lower = _snake(name); - var servicesDir = new Directory('lib/src/services'); - var serviceFile = new File.fromUri(servicesDir.uri.resolve('$lower.dart')); - var testDir = new Directory('test/services'); - var testFile = new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); - - if (!await servicesDir.exists()) await servicesDir.create(recursive: true); - - if (!await testDir.exists()) await testDir.create(recursive: true); - - await serviceFile.writeAsString(serviceSource); - - if (type == MONGO_TYPED || type == MEMORY) { - var serviceLibrary = new File('lib/src/models/models.dart'); - await serviceLibrary.writeAsString("\nexport '$lower.dart';", - mode: FileMode.APPEND); - await preBuild(Directory.current); - } - - await testFile.writeAsString(_generateTests(name, type)); - - var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); - - if (!await runConfig.exists()) { - await runConfig.create(recursive: true); - await runConfig.writeAsString(_generateRunConfiguration(name)); - } - - _pen.green(); - _pen('${Icon.CHECKMARK} Successfully generated service $name.'); - _pen(); } - _generateValidator(String name) async { - var rc = new ReCase(name); - var file = new File('lib/src/validators/${rc.snakeCase}.dart'); + String _generateService( + ServiceGenerator generator, String name, String lower, bool typed) { + var lib = new LibraryBuilder(); - if (!await file.exists()) await file.createSync(recursive: true); + /* + import 'package:angel_framework/angel_framework.dart'; + import '../models/$lower.dart'; + export '../models/$lower.dart'; + */ + lib.addMember( + new ImportBuilder('package:angel_framework/angel_framework.dart')); + generator.applyToLibrary(lib, name, lower); - await file.writeAsString(''' -import 'package:angel_validate/angel_validate.dart'; + if (generator.createsModel == true) { + lib + ..addMember(new ImportBuilder('../models/$lower.dart')) + ..addMember(new ExportBuilder('../models/$lower.dart')); + } -final Validator CREATE_${rc.constantCase} = - new Validator({'name*': isString, 'desc*': isString}); - ''' - .trim()); + // configureServer() {} + var configureServer = new MethodBuilder('configureServer', + returnType: new TypeBuilder('AngelConfigurer')); + generator.applyToConfigureServer(configureServer, name, lower); + + // return (Angel app) async {} + var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync) + ..addPositional(parameter('app', [new TypeBuilder('Angel')])); + generator.beforeService(closure, name, lower); + + // app.use('/api/todos', new MapService()); + var service = generator.createInstance(closure, name, lower); + + if (typed == true) { + service = + new TypeBuilder('TypedService', genericTypes: [new TypeBuilder(name)]) + .newInstance([service]); + } + + closure.addStatement(reference('app') + .invoke('use', [literal('/api/${pluralize(lower)}'), service])); + + if (generator.injectsSingleton == true) { + closure.addStatement(varField('service', + value: reference('app') + .invoke('service', [literal('/api/${pluralize(lower)}')]).castAs( + new TypeBuilder('HookedService')))); + closure.addStatement(reference('app') + .property('container') + .invoke('singleton', [reference('service').property('inner')])); + } + + configureServer.addStatement(closure.asReturn()); + + lib.addMember(configureServer); + + return prettyToSource(lib.buildAst()); } - _generateCustomService(String name) { - return ''' -import 'package:angel_framework/angel_framework.dart'; - -class ${name}Service extends Service { - ${name}Service():super() { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateMemoryModel(String name) async { - var lower = _snake(name); + _generateModel(String name, String lower) async { var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' -library angel.models.$lower; import 'package:angel_framework/common.dart'; class $name extends Model { - String name, desc; + String name; + + String desc; $name({String id, this.name, this.desc}) { this.id = id; @@ -155,197 +172,21 @@ class $name extends Model { .trim()); } - _generateMemoryModelJson(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); + _generateValidator(String lower, String constantCase) async { + var file = new File('lib/src/validators/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_framework/common.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMemoryService(String name) { - var rc = new ReCase(name); - var lower = rc.snakeCase; - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import '../models/$lower.dart'; -export '../models/$lower.dart'; - -/// Manages [$name] in-memory. -class ${name}Service extends MemoryService<$name> { - ${name}Service():super() { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateMongoModel(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_mongo/model.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMongoModelJson(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_framework/common.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMongoService(String name) { - var lower = _snake(name); - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_mongo/angel_mongo.dart'; import 'package:angel_validate/angel_validate.dart'; -import 'package:angel_validate/server.dart'; -import 'package:mongo_dart/mongo_dart.dart'; -final Validator ${lower}Schema = new Validator({ - 'name*': [isString, isNotEmpty], - 'desc*': [isString, isNotEmpty] -}); - -configureServer(Db db) { - return (Angel app) async { - app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - - HookedService service = app.service('api/${lower}s'); - app.container.singleton(service.inner); - - service - ..beforeCreate.listen(validateEvent(${lower}Schema)) - ..beforeUpdate.listen(validateEvent(${lower}Schema)); - }; -} - -/// Manages [$name] in the database. -class ${name}Service extends MongoService { - ${name}Service(collection):super(collection) { - // Your logic here! - } -} +final Validator CREATE_$constantCase = + new Validator({'name*': isString, 'desc*': isString}); ''' - .trim(); + .trim()); } - _generateMongoTypedService(String name) { - var lower = _snake(name); - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_mongo/angel_mongo.dart'; -import 'package:mongo_dart/mongo_dart.dart'; -import '../models/$lower.dart'; -export '../models/$lower.dart'; - -configureServer(Db db) { - return (Angel app) async { - app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - - HookedService service = app.service('api/${lower}s'); - app.container.singleton(service.inner); - }; -} - -/// Manages [$name] in the database. -class ${name}Service extends MongoTypedService<$name> { - ${name}Service(collection):super(collection) { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateRunConfiguration(String name) { - var lower = _snake(name); - + _generateRunConfiguration(String name, String lower) { return ''' @@ -357,9 +198,7 @@ class ${name}Service extends MongoTypedService<$name> { .trim(); } - _generateTests(String name, String type) { - var lower = _snake(name); - + _generateTests(String lower, String type) { return ''' import 'dart:io'; import 'package:angel/angel.dart'; @@ -382,13 +221,13 @@ main() async { }); test('index via REST', () async { - var response = await client.get('/api/${lower}s'); + var response = await client.get('/api/${pluralize(lower)}'); expect(response, hasStatus(HttpStatus.OK)); }); - test('Index ${lower}s', () async { - var ${lower}s = await client.service('api/${lower}s').index(); - print(${lower}s); + test('Index ${pluralize(lower)}', () async { + var ${pluralize(lower)} = await client.service('api/${pluralize(lower)}').index(); + print(${pluralize(lower)}); }); } diff --git a/lib/src/commands/service_generators/custom.dart b/lib/src/commands/service_generators/custom.dart new file mode 100644 index 00000000..7d1f7f90 --- /dev/null +++ b/lib/src/commands/service_generators/custom.dart @@ -0,0 +1,24 @@ +import 'package:code_builder/code_builder.dart'; +import 'generator.dart'; + +class CustomServiceGenerator extends ServiceGenerator { + @override + bool get createsModel => false; + + @override + bool get createsValidator => false; + + const CustomServiceGenerator() : super('Custom'); + + @override + void applyToLibrary(LibraryBuilder library, String name, String lower) { + var clazz = new ClassBuilder('${name}Service', asExtends: new TypeBuilder('Service')); + library.addMember(clazz); + } + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('${name}Service').newInstance([]); + } +} diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart new file mode 100644 index 00000000..280962eb --- /dev/null +++ b/lib/src/commands/service_generators/generator.dart @@ -0,0 +1,24 @@ +import 'package:code_builder/code_builder.dart'; + +class ServiceGenerator { + final String name; + + const ServiceGenerator(this.name); + + bool get createsModel => true; + bool get createsValidator => true; + bool get exportedInServiceLibrary => true; + bool get injectsSingleton => true; + bool get shouldRunBuild => false; + + void applyToLibrary(LibraryBuilder library, String name, String lower) {} + + void beforeService(MethodBuilder methodBuilder, String name, String lower) {} + + void applyToConfigureServer( + MethodBuilder configureServer, String name, String lower) {} + + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) => + literal(null); +} diff --git a/lib/src/commands/service_generators/map.dart b/lib/src/commands/service_generators/map.dart new file mode 100644 index 00000000..d0f7eb44 --- /dev/null +++ b/lib/src/commands/service_generators/map.dart @@ -0,0 +1,15 @@ +import 'generator.dart'; +import 'package:code_builder/code_builder.dart'; + +class MapServiceGenerator extends ServiceGenerator { + @override + bool get createsModel => false; + + const MapServiceGenerator() : super('In-Memory'); + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('MapService').newInstance([]); + } +} diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart new file mode 100644 index 00000000..1e9d1fd5 --- /dev/null +++ b/lib/src/commands/service_generators/mongo.dart @@ -0,0 +1,29 @@ +import 'generator.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:inflection/inflection.dart'; + +class MongoServiceGenerator extends ServiceGenerator { + const MongoServiceGenerator() : super('MongoDB'); + + @override + void applyToConfigureServer( + MethodBuilder configureServer, String name, String lower) { + configureServer.addPositional(parameter('db', [new TypeBuilder('Db')])); + } + + @override + void applyToLibrary(LibraryBuilder library, String name, String lower) { + library.addMembers([ + 'package:angel_mongo/angel_mongo.dart', + 'package:mongo_dart/mongo_dart.dart' + ].map((str) => new ImportBuilder(str))); + } + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('MongoService').newInstance([ + reference('db').invoke('collection', [literal(pluralize(lower))]) + ]); + } +} diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart new file mode 100644 index 00000000..80c8646c --- /dev/null +++ b/lib/src/commands/service_generators/rethink.dart @@ -0,0 +1,31 @@ +import 'generator.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:inflection/inflection.dart'; + +class RethinkServiceGenerator extends ServiceGenerator { + const RethinkServiceGenerator() : super('RethinkDB'); + + @override + void applyToConfigureServer( + MethodBuilder configureServer, String name, String lower) { + configureServer + ..addPositional(parameter('connection', [new TypeBuilder('Connection')])) + ..addPositional(parameter('r', [new TypeBuilder('Rethinkdb')])); + } + + @override + void applyToLibrary(LibraryBuilder library, String name, String lower) { + library.addMembers([ + 'package:angel_rethink/angel_rethink.dart', + 'package:rethinkdb_driver/rethinkdb_driver.dart' + ].map((str) => new ImportBuilder(str))); + } + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('RethinkService').newInstance([ + reference('r').invoke('table', [literal(pluralize(lower))]) + ]); + } +} diff --git a/lib/src/commands/service_generators/service_generators.dart b/lib/src/commands/service_generators/service_generators.dart new file mode 100644 index 00000000..760cd927 --- /dev/null +++ b/lib/src/commands/service_generators/service_generators.dart @@ -0,0 +1,5 @@ +export 'custom.dart'; +export 'generator.dart'; +export 'map.dart'; +export 'mongo.dart'; +export 'rethink.dart'; \ No newline at end of file diff --git a/lib/src/commands/service_old.dart b/lib/src/commands/service_old.dart new file mode 100644 index 00000000..ad970320 --- /dev/null +++ b/lib/src/commands/service_old.dart @@ -0,0 +1,398 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:console/console.dart'; +import 'package:id/id.dart'; +import 'package:recase/recase.dart'; +import 'init.dart' show preBuild; + +class ServiceCommand extends Command { + final String CUSTOM = 'Custom'; + final String MEMORY = 'In-Memory'; + final String MEMORY_JSON = 'In-Memory (serialized via `source_gen`)'; + final String MONGO = 'MongoDB'; + final String MONGO_TYPED = 'MongoDB (typed)'; + final String MONGO_TYPED_JSON = + 'MongoDB (typed, serialized via `source_gen`)'; + final String TRESTLE = 'Trestle'; + final TextPen _pen = new TextPen(); + + @override + String get name => 'service'; + + @override + String get description => 'Creates a new service within the given project.'; + + String _snake(name) => idFromString(name).snake; + + @override + run() async { + var name = await readInput('Name of Service (not plural): '); + var chooser = new Chooser([MONGO, MONGO_TYPED, MEMORY, CUSTOM], + message: 'What type of service would you like to create? '); + var type = await chooser.choose(); + + fail() { + _pen.red(); + _pen('Could not successfully create service $name.'); + _pen(); + } + + String serviceSource = ''; + + if (type == MONGO) { + serviceSource = _generateMongoService(name); + } else if (type == MONGO_TYPED) { + serviceSource = _generateMongoTypedService(name); + await _generateMongoModel(name); + await _generateValidator(name); + } else if (type == MONGO_TYPED_JSON) { + serviceSource = _generateMongoTypedService(name); + await _generateMongoModelJson(name); + await _generateValidator(name); + } else if (type == MEMORY) { + serviceSource = _generateMemoryService(name); + await _generateMemoryModel(name); + await _generateValidator(name); + } else if (type == MEMORY_JSON) { + serviceSource = _generateMemoryService(name); + await _generateMemoryModelJson(name); + await _generateValidator(name); + } else if (type == CUSTOM) { + serviceSource = _generateCustomService(name); + } else if (type == TRESTLE) { + _pen.blue(); + _pen('${Icon.STAR} Trestle services are not yet implemented. :('); + _pen(); + } else { + print('Code to generate a $type service is not yet written.'); + } + + if (serviceSource.isEmpty) { + fail(); + throw new Exception('Empty generated service code.'); + } + + var lower = _snake(name); + var servicesDir = new Directory('lib/src/services'); + var serviceFile = new File.fromUri(servicesDir.uri.resolve('$lower.dart')); + var testDir = new Directory('test/services'); + var testFile = new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); + + if (!await servicesDir.exists()) await servicesDir.create(recursive: true); + + if (!await testDir.exists()) await testDir.create(recursive: true); + + await serviceFile.writeAsString(serviceSource); + + if (type == MONGO_TYPED || type == MEMORY) { + var serviceLibrary = new File('lib/src/models/models.dart'); + await serviceLibrary.writeAsString("\nexport '$lower.dart';", + mode: FileMode.APPEND); + await preBuild(Directory.current); + } + + await testFile.writeAsString(_generateTests(name, type)); + + var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); + + if (!await runConfig.exists()) { + await runConfig.create(recursive: true); + await runConfig.writeAsString(_generateRunConfiguration(name)); + } + + _pen.green(); + _pen('${Icon.CHECKMARK} Successfully generated service $name.'); + _pen(); + } + + _generateValidator(String name) async { + var rc = new ReCase(name); + var file = new File('lib/src/validators/${rc.snakeCase}.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +import 'package:angel_validate/angel_validate.dart'; + +final Validator CREATE_${rc.constantCase} = + new Validator({'name*': isString, 'desc*': isString}); + ''' + .trim()); + } + + _generateCustomService(String name) { + return ''' +import 'package:angel_framework/angel_framework.dart'; + +class ${name}Service extends Service { + ${name}Service():super() { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateMemoryModel(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; + +class $name extends Model { + String name, desc; + + $name({String id, this.name, this.desc}) { + this.id = id; + } +} + ''' + .trim()); + } + + _generateMemoryModelJson(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; +import 'package:source_gen/generators/json_serializable.dart'; + +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}FromJson(json); + + $name({this.id, this.name, this.desc}); +} + ''' + .trim()); + } + + _generateMemoryService(String name) { + var rc = new ReCase(name); + var lower = rc.snakeCase; + + return ''' +import 'package:angel_framework/angel_framework.dart'; +import '../models/$lower.dart'; +export '../models/$lower.dart'; + +/// Manages [$name] in-memory. +class ${name}Service extends MemoryService<$name> { + ${name}Service():super() { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateMongoModel(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_mongo/model.dart'; +import 'package:source_gen/generators/json_serializable.dart'; + +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}FromJson(json); + + $name({this.id, this.name, this.desc}); +} + ''' + .trim()); + } + + _generateMongoModelJson(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; +import 'package:source_gen/generators/json_serializable.dart'; + +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}FromJson(json); + + $name({this.id, this.name, this.desc}); +} + ''' + .trim()); + } + + _generateMongoService(String name) { + var lower = _snake(name); + + return ''' +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:angel_validate/angel_validate.dart'; +import 'package:angel_validate/server.dart'; +import 'package:mongo_dart/mongo_dart.dart'; + +final Validator ${lower}Schema = new Validator({ + 'name*': [isString, isNotEmpty], + 'desc*': [isString, isNotEmpty] +}); + +configureServer(Db db) { + return (Angel app) async { + app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); + + HookedService service = app.service('api/${lower}s'); + app.container.singleton(service.inner); + + service + ..beforeCreate.listen(validateEvent(${lower}Schema)) + ..beforeUpdate.listen(validateEvent(${lower}Schema)); + }; +} + +/// Manages [$name] in the database. +class ${name}Service extends MongoService { + ${name}Service(collection):super(collection) { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateMongoTypedService(String name) { + var lower = _snake(name); + + return ''' +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:mongo_dart/mongo_dart.dart'; +import '../models/$lower.dart'; +export '../models/$lower.dart'; + +configureServer(Db db) { + return (Angel app) async { + app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); + + HookedService service = app.service('api/${lower}s'); + app.container.singleton(service.inner); + }; +} + +/// Manages [$name] in the database. +class ${name}Service extends MongoTypedService<$name> { + ${name}Service(collection):super(collection) { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateRunConfiguration(String name) { + var lower = _snake(name); + + return ''' + + + + +''' + .trim(); + } + + _generateTests(String name, String type) { + var lower = _snake(name); + + return ''' +import 'dart:io'; +import 'package:angel/angel.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_test/angel_test.dart'; +import 'package:test/test.dart'; + +main() async { + Angel app; + TestClient client; + + setUp(() async { + app = await createServer(); + client = await connectTo(app); + }); + + tearDown(() async { + await client.close(); + app = null; + }); + + test('index via REST', () async { + var response = await client.get('/api/${lower}s'); + expect(response, hasStatus(HttpStatus.OK)); + }); + + test('Index ${lower}s', () async { + var ${lower}s = await client.service('api/${lower}s').index(); + print(${lower}s); + }); +} + + ''' + .trim(); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 3d466f53..87f1192a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,13 +2,15 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.0" +version: "1.0.1" dependencies: analyzer: "^0.29.0" args: "^0.13.7" + code_builder: "^1.0.0-alpha" console: "^2.2.3" glob: "^1.1.0" id: "^1.0.0" + inflection: "^0.4.1" pubspec: "^0.0.14" random_string: "^0.0.1" recase: "^1.0.0" From 6cd3b65a0c89faa61b5976dda35cde103dc39c5f Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 1 Mar 2017 22:26:11 -0500 Subject: [PATCH 037/132] 1.0.2 --- lib/src/commands/init.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index fede5e22..a8d35a6e 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -28,7 +28,7 @@ class InitCommand extends Command { "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); _pen(); await _pubGet(projectDir); - await preBuild(projectDir); + // await preBuild(projectDir); var secret = rs.randomAlphaNumeric(32); print('Generated new development JWT secret: $secret'); await _key.changeSecret( diff --git a/pubspec.yaml b/pubspec.yaml index 87f1192a..3baa982b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.1" +version: "1.0.2" dependencies: analyzer: "^0.29.0" args: "^0.13.7" From 1db250ca1640ea4fcb203529bb1fec6b3f3e0a17 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 1 Mar 2017 22:31:00 -0500 Subject: [PATCH 038/132] 1.0.3 --- lib/src/commands/service.dart | 20 +++++++++++++------ .../commands/service_generators/mongo.dart | 5 +---- pubspec.yaml | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 73090211..aa1bf75a 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -3,6 +3,7 @@ import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import 'package:console/console.dart'; import 'package:inflection/inflection.dart'; +import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; import 'service_generators/service_generators.dart'; import 'init.dart' show preBuild; @@ -25,6 +26,7 @@ class ServiceCommand extends Command { @override run() async { + var pubspec = await PubSpec.load(Directory.current); var name = await readInput('Name of Service (not plural): '); var chooser = new Chooser( GENERATORS.map((g) => g.name).toList(), @@ -102,7 +104,7 @@ class ServiceCommand extends Command { export '../models/$lower.dart'; */ lib.addMember( - new ImportBuilder('package:angel_framework/angel_framework.dart')); + new ImportBuilder('package:angel_common/angel_common.dart')); generator.applyToLibrary(lib, name, lower); if (generator.createsModel == true) { @@ -180,8 +182,14 @@ class $name extends Model { await file.writeAsString(''' import 'package:angel_validate/angel_validate.dart'; -final Validator CREATE_$constantCase = - new Validator({'name*': isString, 'desc*': isString}); +final Validator $constantCase = new Validator({ + 'name': [isString, isNotEmpty], + 'desc': [isString, isNotEmpty] +}); + +final Validator CREATE_$constantCase = $constantCase.extend({}) + ..requiredFields.addAll(['name', 'desc']); + ''' .trim()); } @@ -198,11 +206,11 @@ final Validator CREATE_$constantCase = .trim(); } - _generateTests(String lower, String type) { + _generateTests(PubSpec pubspec, String lower, String type) { return ''' import 'dart:io'; -import 'package:angel/angel.dart'; -import 'package:angel_framework/angel_framework.dart'; +import 'package:${pubspec.name}/${pubspec.name}.dart'; +import 'package:angel_common/angel_common.dart'; import 'package:angel_test/angel_test.dart'; import 'package:test/test.dart'; diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index 1e9d1fd5..af15f740 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -13,10 +13,7 @@ class MongoServiceGenerator extends ServiceGenerator { @override void applyToLibrary(LibraryBuilder library, String name, String lower) { - library.addMembers([ - 'package:angel_mongo/angel_mongo.dart', - 'package:mongo_dart/mongo_dart.dart' - ].map((str) => new ImportBuilder(str))); + library.addMember(new ImportBuilder('package:mongo_dart/mongo_dart.dart')); } @override diff --git a/pubspec.yaml b/pubspec.yaml index 3baa982b..bbe944d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.2" +version: "1.0.3" dependencies: analyzer: "^0.29.0" args: "^0.13.7" From b3232b03afaf947ae2301765ed3fa4b6bb4869eb Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 3 Mar 2017 23:14:39 -0500 Subject: [PATCH 039/132] 1.0.4 --- lib/src/commands/init.dart | 6 ++++ lib/src/commands/rename.dart | 52 +++++++++++++++++------------------ lib/src/commands/service.dart | 4 +-- pubspec.yaml | 2 +- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index a8d35a6e..3d775f12 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -2,7 +2,9 @@ import "dart:io"; import "package:args/command_runner.dart"; import "package:console/console.dart"; import 'package:random_string/random_string.dart' as rs; +import 'package:path/path.dart' as p; import 'key.dart'; +import 'rename.dart'; class InitCommand extends Command { final KeyCommand _key = new KeyCommand(); @@ -40,6 +42,10 @@ class InitCommand extends Command { await _key.changeSecret( new File.fromUri(projectDir.uri.resolve('config/production.yaml')), secret); + + var name = p.basenameWithoutExtension(projectDir.path); + print('Renaming project from "angel" to "$name"...'); + await renameDartFiles(projectDir, 'angel', name); } _cloneRepo(Directory projectDir) async { diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index cfa9beea..89019528 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -42,7 +42,7 @@ class RenameCommand extends Command { var newPubspec = new PubSpec.fromJson(pubspec.toJson()..['name'] = newName); await newPubspec.save(Directory.current); - await renameDartFiles(oldName, newName); + await renameDartFiles(Directory.current, oldName, newName); print('Now running `pub get`...'); var pub = await Process.start('pub', ['get']); stdout.addStream(pub.stdout); @@ -51,37 +51,35 @@ class RenameCommand extends Command { } } } +} - renameDartFiles(String oldName, String newName) async { - var entry = - new File.fromUri(Directory.current.uri.resolve('lib/$oldName.dart')); +renameDartFiles(Directory dir, String oldName, String newName) async { + var entry = new File.fromUri(dir.uri.resolve('lib/$oldName.dart')); - if (await entry.exists()) { - await entry.rename('lib/$newName.dart'); - print('Renaming library file `${entry.absolute.path}`...'); - } + if (await entry.exists()) { + await entry.rename('lib/$newName.dart'); + print('Renaming library file `${entry.absolute.path}`...'); + } - await for (FileSystemEntity file - in Directory.current.list(recursive: true)) { - if (file is File && file.path.endsWith('.dart')) { - var contents = await file.readAsString(); - var ast = parseCompilationUnit(contents); - var visitor = new RenamingVisitor(oldName, newName) - ..visitCompilationUnit(ast); + await for (FileSystemEntity file in dir.list(recursive: true)) { + if (file is File && file.path.endsWith('.dart')) { + var contents = await file.readAsString(); + var ast = parseCompilationUnit(contents); + var visitor = new RenamingVisitor(oldName, newName) + ..visitCompilationUnit(ast); - if (visitor.replace.isNotEmpty) { - visitor.replace.forEach((range, replacement) { - if (range.first is int) { - contents = - contents.replaceRange(range.first, range.last, replacement); - } else if (range.first is String) { - contents = contents.replaceAll(range.first, replacement); - } - }); + if (visitor.replace.isNotEmpty) { + visitor.replace.forEach((range, replacement) { + if (range.first is int) { + contents = + contents.replaceRange(range.first, range.last, replacement); + } else if (range.first is String) { + contents = contents.replaceAll(range.first, replacement); + } + }); - await file.writeAsString(contents); - print('Updated file `${file.absolute.path}`.'); - } + await file.writeAsString(contents); + print('Updated file `${file.absolute.path}`.'); } } } diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index aa1bf75a..71f2945f 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -61,7 +61,7 @@ class ServiceCommand extends Command { await serviceFile .writeAsString(_generateService(generator, name, lower, typed)); - await testFile.writeAsString(_generateTests(lower, type)); + await testFile.writeAsString(_generateTests(pubspec, lower)); var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); @@ -206,7 +206,7 @@ final Validator CREATE_$constantCase = $constantCase.extend({}) .trim(); } - _generateTests(PubSpec pubspec, String lower, String type) { + _generateTests(PubSpec pubspec, String lower) { return ''' import 'dart:io'; import 'package:${pubspec.name}/${pubspec.name}.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index bbe944d2..12e0e1d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.3" +version: "1.0.4" dependencies: analyzer: "^0.29.0" args: "^0.13.7" From 058e2fee3971be32f0a3cfe6348fb37414c89e06 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 3 Mar 2017 23:16:15 -0500 Subject: [PATCH 040/132] 1.0.4 --- lib/src/commands/test.dart | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index 91f5a5e8..5bc9c046 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; +import 'package:pubspec/pubspec.dart'; class TestCommand extends Command { final TextPen _pen = new TextPen(); @@ -15,13 +16,13 @@ class TestCommand extends Command { run() async { final name = await readInput("Name of Test: "), lower = name.toLowerCase(); final testDir = new Directory("test/services"); - final testFile = new File.fromUri( - testDir.uri.resolve("${lower}_test.dart")); + final testFile = + new File.fromUri(testDir.uri.resolve("${lower}_test.dart")); - if (!await testFile.exists()) - await testFile.create(recursive: true); + if (!await testFile.exists()) await testFile.create(recursive: true); - await testFile.writeAsString(_generateTest(lower)); + await testFile.writeAsString( + _generateTest(await PubSpec.load(Directory.current), lower)); final runConfig = new File('./.idea/runConfigurations/${name}_tests.xml'); @@ -49,10 +50,10 @@ class TestCommand extends Command { .trim(); } - String _generateTest(String lower) { + String _generateTest(PubSpec pubspec, String lower) { return ''' import 'dart:io'; -import 'package:angel/angel.dart'; +import 'package:${pubspec.name}/${pubspec.name}.dart'; import 'package:angel_framework/angel_framework.dart'; import 'package:angel_test/angel_test.dart'; import 'package:test/test.dart'; From 886dd32167cd65471c970461ea137ff1e9ddf3c7 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 4 Mar 2017 16:34:49 -0500 Subject: [PATCH 041/132] 1.0.5 --- lib/src/commands/init.dart | 11 +++++---- lib/src/commands/rename.dart | 23 +++++++++++++++---- .../service_generators/generator.dart | 2 +- lib/src/commands/start.dart | 15 +++++++----- pubspec.yaml | 2 +- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 3d775f12..9a84bfac 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -25,11 +25,6 @@ class InitCommand extends Command { argResults.arguments.isEmpty ? "." : argResults.arguments[0]); print("Creating new Angel project in ${projectDir.absolute.path}..."); await _cloneRepo(projectDir); - _pen.green(); - _pen( - "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); - _pen(); - await _pubGet(projectDir); // await preBuild(projectDir); var secret = rs.randomAlphaNumeric(32); print('Generated new development JWT secret: $secret'); @@ -45,7 +40,13 @@ class InitCommand extends Command { var name = p.basenameWithoutExtension(projectDir.path); print('Renaming project from "angel" to "$name"...'); + await renamePubspec(projectDir, 'angel', name); await renameDartFiles(projectDir, 'angel', name); + _pen.green(); + _pen( + "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); + _pen(); + await _pubGet(projectDir); } _cloneRepo(Directory projectDir) async { diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index 89019528..1eee74aa 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -39,9 +39,7 @@ class RenameCommand extends Command { } else { var pubspec = await PubSpec.load(Directory.current); var oldName = pubspec.name; - var newPubspec = - new PubSpec.fromJson(pubspec.toJson()..['name'] = newName); - await newPubspec.save(Directory.current); + await renamePubspec(Directory.current, oldName, newName); await renameDartFiles(Directory.current, oldName, newName); print('Now running `pub get`...'); var pub = await Process.start('pub', ['get']); @@ -53,11 +51,28 @@ class RenameCommand extends Command { } } +renamePubspec(Directory dir, String oldName, String newName) async { + var pubspec = await PubSpec.load(dir); + var newPubspec = new PubSpec.fromJson(pubspec.toJson()..['name'] = newName); + await newPubspec.save(dir); +} + renameDartFiles(Directory dir, String oldName, String newName) async { + // Try to replace MongoDB URL + var defaultYaml = new File.fromUri(dir.uri.resolve('config/default.yaml')); + + if (await defaultYaml.exists()) { + print('Changing MongoDB URL in file "${defaultYaml.absolute.path}"...'); + var contents = await defaultYaml.readAsString(); + contents = contents.replaceAll('mongodb://localhost:27017/$oldName', + 'mongodb://localhost:27017/$newName'); + await defaultYaml.writeAsString(contents); + } + var entry = new File.fromUri(dir.uri.resolve('lib/$oldName.dart')); if (await entry.exists()) { - await entry.rename('lib/$newName.dart'); + await entry.rename(dir.uri.resolve('lib/$newName.dart').toFilePath()); print('Renaming library file `${entry.absolute.path}`...'); } diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart index 280962eb..79ccc4f8 100644 --- a/lib/src/commands/service_generators/generator.dart +++ b/lib/src/commands/service_generators/generator.dart @@ -8,7 +8,7 @@ class ServiceGenerator { bool get createsModel => true; bool get createsValidator => true; bool get exportedInServiceLibrary => true; - bool get injectsSingleton => true; + bool get injectsSingleton => false; bool get shouldRunBuild => false; void applyToLibrary(LibraryBuilder library, String name, String lower) {} diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index 04dc19df..02ac0c00 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:watcher/watcher.dart'; @@ -62,8 +63,8 @@ class StartCommand extends Command { try { var scripts = await Process.start('pub', ['global', 'run', 'scripts', 'start']); - stdout.addStream(scripts.stdout); - stderr.addStream(scripts.stderr); + listen(scripts.stdout, stdout); + listen(scripts.stderr, stderr); int code = await scripts.exitCode; if (code != 0) { @@ -94,10 +95,8 @@ class StartCommand extends Command { environment: env); try { - if (isNew) { - stdout.addStream(server.stdout); - stderr.addStream(server.stderr); - } + listen(server.stdout, stdout); + listen(server.stderr, stderr); } catch (e) { print(e); } @@ -110,3 +109,7 @@ class StartCommand extends Command { exitCode = await server.exitCode; } } + +void listen(Stream> stream, IOSink sink) { + stream.listen(sink.add); +} diff --git a/pubspec.yaml b/pubspec.yaml index 12e0e1d1..a544d703 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.4" +version: "1.0.5" dependencies: analyzer: "^0.29.0" args: "^0.13.7" From f849ce9a83d33c2caf331d7d0df9320355ca17e0 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Thu, 9 Mar 2017 22:39:24 -0500 Subject: [PATCH 042/132] 1.0.6 --- README.md | 7 ++++++- lib/src/commands/service_generators/rethink.dart | 2 +- pubspec.yaml | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 87776e1c..70aab43d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # angel_cli Command-line tools for the Angel framework. +Includes functionality such as: +* Project scaffolding +* Generating service models, plugins, tests and more +* Renaming projects +* Starting server with live reloading To install: @@ -11,4 +16,4 @@ And then, for information on each command: ```bash $ angel --help -``` \ No newline at end of file +``` diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 80c8646c..267e1776 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -17,7 +17,7 @@ class RethinkServiceGenerator extends ServiceGenerator { void applyToLibrary(LibraryBuilder library, String name, String lower) { library.addMembers([ 'package:angel_rethink/angel_rethink.dart', - 'package:rethinkdb_driver/rethinkdb_driver.dart' + 'package:rethinkdb_driver2/rethinkdb_driver2.dart' ].map((str) => new ImportBuilder(str))); } diff --git a/pubspec.yaml b/pubspec.yaml index a544d703..ff5a8ab5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,11 +2,11 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.5" +version: "1.0.6" dependencies: - analyzer: "^0.29.0" + # analyzer: "^0.29.0" args: "^0.13.7" - code_builder: "^1.0.0-alpha" + code_builder: "^1.0.0-beta" console: "^2.2.3" glob: "^1.1.0" id: "^1.0.0" From afca3b0752c7381b397aa25e3cb1c8ccfe691faf Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 24 Mar 2017 16:25:50 -0400 Subject: [PATCH 043/132] 1.0.7 --- bin/angel.dart | 24 ++++++- lib/pubspec.update.g.dart | 26 +++++++ lib/src/commands/init.dart | 71 ++++++++++++++++--- lib/src/commands/plugin.dart | 18 ++--- lib/src/commands/service.dart | 21 +++--- .../commands/service_generators/rethink.dart | 1 + lib/src/commands/test.dart | 4 +- pubspec.yaml | 6 +- tool/build.dart | 4 ++ tool/phases.dart | 6 ++ tool/watch.dart | 4 ++ 11 files changed, 154 insertions(+), 31 deletions(-) create mode 100644 lib/pubspec.update.g.dart create mode 100644 tool/build.dart create mode 100644 tool/phases.dart create mode 100644 tool/watch.dart diff --git a/bin/angel.dart b/bin/angel.dart index f8919c4c..8288adf6 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -4,10 +4,13 @@ library demon.tool; import "dart:io"; import "package:args/command_runner.dart"; import 'package:angel_cli/angel_cli.dart'; +import 'package:angel_cli/pubspec.update.g.dart'; +import 'package:console/console.dart'; +import 'package:http/http.dart' as http; final String DOCTOR = "doctor"; -main(List args) { +main(List args) async { var runner = new CommandRunner("angel", "Command-line tools for the Angel framework."); @@ -21,7 +24,24 @@ main(List args) { ..addCommand(new StartCommand()) ..addCommand(new RenameCommand()); - return runner.run(args).then((_) {}).catchError((exc) { + stdout.write('Checking for update... '); + var client = new http.Client(); + var update = await checkForUpdate(client); + client.close(); + + if (update != null) { + stdout.writeln(); + var pen = new TextPen(); + pen.cyan(); + pen.text( + 'ATTENTION: There is a new version of the Angel CLI available (version $update).'); + pen.text('\nTo update, run `pub global activate angel_cli`.'); + pen(); + stdout.writeln(); + } else + stdout.writeln('No update available.'); + + return await runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); exitCode = 1; }); diff --git a/lib/pubspec.update.g.dart b/lib/pubspec.update.g.dart new file mode 100644 index 00000000..b79370e3 --- /dev/null +++ b/lib/pubspec.update.g.dart @@ -0,0 +1,26 @@ +import 'dart:async'; +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, 0, 6); +Future fetchCurrentVersion(http.BaseClient client) async { + var response = + await client.get('https://pub.dartlang.org/api/packages/angel_cli'); + var json = JSON.decode(response.body) as Map; + if (!(json.containsKey('latest')) || + !(json['latest'].containsKey('pubspec')) || + !(json['latest']['pubspec'].containsKey('version'))) { + throw new StateError( + 'GET https://pub.dartlang.org/api/packages/angel_cli returned an invalid pub API response.'); + } + return new Version.parse(json['latest']['pubspec']['version']); +} + +Future checkForUpdate(http.BaseClient client) async { + var current = await fetchCurrentVersion(client); + if (PACKAGE_VERSION.compareTo(current) < 0) { + return current; + } + return null; +} diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 9a84bfac..744ca6d3 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -17,7 +17,9 @@ class InitCommand extends Command { String get description => "Initializes a new Angel project in the current directory."; - InitCommand() {} + InitCommand() { + argParser.addFlag('pub-get', defaultsTo: true); + } @override run() async { @@ -38,15 +40,60 @@ class InitCommand extends Command { new File.fromUri(projectDir.uri.resolve('config/production.yaml')), secret); - var name = p.basenameWithoutExtension(projectDir.path); + var name = p.basenameWithoutExtension( + projectDir.absolute.uri.normalizePath().toFilePath()); print('Renaming project from "angel" to "$name"...'); await renamePubspec(projectDir, 'angel', name); await renameDartFiles(projectDir, 'angel', name); + + if (argResults['pub-get'] != false) { + print('Now running pub get...'); + await _pubGet(projectDir); + } + _pen.green(); - _pen( - "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); + _pen("${Icon.CHECKMARK} Successfully initialized Angel project."); _pen(); - await _pubGet(projectDir); + _pen + ..reset() + ..text('\nCongratulations! You are ready to start developing with Angel!') + ..text('\nTo start the server (with file watching), run ') + ..magenta() + ..text('`angel start`') + ..normal() + ..text(' in your terminal.') + ..text('\nHappy coding!') + ..call(); + } + + _deleteRecursive(FileSystemEntity entity, [bool self = true]) async { + if (entity is Directory) { + await for (var entity in entity.list(recursive: true)) { + try { + await _deleteRecursive(entity); + } catch (e) {} + } + + try { + if (self != false) await entity.delete(recursive: true); + } catch (e) {} + } else if (entity is File) { + try { + await entity.delete(recursive: true); + } catch (e) {} + } else if (entity is Link) { + var path = await entity.resolveSymbolicLinks(); + var stat = await FileStat.stat(path); + + switch (stat.type) { + case FileSystemEntityType.DIRECTORY: + return await _deleteRecursive(new Directory(path)); + case FileSystemEntityType.FILE: + return await _deleteRecursive(new File(path)); + default: + break; + } + } } _cloneRepo(Directory projectDir) async { @@ -54,11 +101,16 @@ class InitCommand extends Command { if (await projectDir.exists()) { var chooser = new Chooser(["Yes", "No"], message: - "Directory '${projectDir.absolute.path}' exists. Overwrite it? (Yes/No)"); + "Directory '${projectDir.absolute.path}' already exists. Overwrite it? (Yes/No)"); if (await chooser.choose() != "Yes") throw new Exception("Chose not to overwrite existing directory."); - await projectDir.delete(recursive: true); + else if (projectDir.absolute.uri.normalizePath().toFilePath() != + Directory.current.absolute.uri.normalizePath().toFilePath()) + await projectDir.delete(recursive: true); + else { + await _deleteRecursive(projectDir, false); + } } var git = await Process.start("git", [ @@ -89,7 +141,10 @@ class InitCommand extends Command { } _pubGet(Directory projectDir) async { - var pub = await Process.start("pub", ["get"], + var exec = new File(Platform.resolvedExecutable); + var pubPath = exec.parent.uri.resolve('pub').path; + print('Running pub at "$pubPath"...'); + var pub = await Process.start(pubPath, ["get"], workingDirectory: projectDir.absolute.path); stdout.addStream(pub.stdout); stderr.addStream(pub.stderr); diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart index 6a731cda..fdadffe2 100644 --- a/lib/src/commands/plugin.dart +++ b/lib/src/commands/plugin.dart @@ -1,6 +1,8 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; class PluginCommand extends Command { final TextPen _pen = new TextPen(); @@ -13,24 +15,24 @@ class PluginCommand extends Command { @override run() async { - final name = await readInput("Name of Plugin: "), lower = name.toLowerCase(); + var pubspec = await PubSpec.load(Directory.current); + final name = await readInput("Name of Plugin: "), + lower = new ReCase(name).snakeCase; final testDir = new Directory("lib/src/config/plugins"); - final pluginFile = new File.fromUri( - testDir.uri.resolve("$lower.dart")); + final pluginFile = new File.fromUri(testDir.uri.resolve("$lower.dart")); - if (!await pluginFile.exists()) - await pluginFile.create(recursive: true); + if (!await pluginFile.exists()) await pluginFile.create(recursive: true); - await pluginFile.writeAsString(_generatePlugin(lower)); + await pluginFile.writeAsString(_generatePlugin(pubspec, name, lower)); _pen.green(); _pen("${Icon.CHECKMARK} Successfully generated plugin $name."); _pen(); } - String _generatePlugin(String name) { - + String _generatePlugin(PubSpec pubspec, String name, String lower) { return ''' +library ${pubspec.name}.plugins.$lower; import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 71f2945f..dda6e6fb 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -71,11 +71,11 @@ class ServiceCommand extends Command { } if (generator.createsModel == true) { - await _generateModel(name, lower); + await _generateModel(pubspec, name, lower); } if (generator.createsValidator == true) { - await _generateValidator(lower, rc.constantCase); + await _generateValidator(pubspec, lower, rc.constantCase); } if (generator.exportedInServiceLibrary == true) { @@ -152,34 +152,33 @@ class ServiceCommand extends Command { return prettyToSource(lib.buildAst()); } - _generateModel(String name, String lower) async { + _generateModel(PubSpec pubspec, String name, String lower) async { var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' - +library ${pubspec.name}.models.$lower; import 'package:angel_framework/common.dart'; class $name extends Model { - String name; - - String desc; + @override + String id; + String name, desc; - $name({String id, this.name, this.desc}) { - this.id = id; - } + $name({this.id, this.name, this.desc}); } ''' .trim()); } - _generateValidator(String lower, String constantCase) async { + _generateValidator(PubSpec pubspec, String lower, String constantCase) async { var file = new File('lib/src/validators/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' +library ${pubspec.name}.models.$lower; import 'package:angel_validate/angel_validate.dart'; final Validator $constantCase = new Validator({ diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 267e1776..71d6acc5 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -25,6 +25,7 @@ class RethinkServiceGenerator extends ServiceGenerator { ExpressionBuilder createInstance( MethodBuilder methodBuilder, String name, String lower) { return new TypeBuilder('RethinkService').newInstance([ + reference('connection'), reference('r').invoke('table', [literal(pluralize(lower))]) ]); } diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index 5bc9c046..47c022e0 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; class TestCommand extends Command { final TextPen _pen = new TextPen(); @@ -14,7 +15,8 @@ class TestCommand extends Command { @override run() async { - final name = await readInput("Name of Test: "), lower = name.toLowerCase(); + final name = await readInput("Name of Test: "), + lower = new ReCase(name).snakeCase; final testDir = new Directory("test/services"); final testFile = new File.fromUri(testDir.uri.resolve("${lower}_test.dart")); diff --git a/pubspec.yaml b/pubspec.yaml index ff5a8ab5..b4e736a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,13 +2,14 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.6" +version: "1.0.7" dependencies: # analyzer: "^0.29.0" args: "^0.13.7" code_builder: "^1.0.0-beta" console: "^2.2.3" glob: "^1.1.0" + http: ^0.11.3 id: "^1.0.0" inflection: "^0.4.1" pubspec: "^0.0.14" @@ -16,6 +17,9 @@ dependencies: recase: "^1.0.0" watcher: "^0.9.7" yaml: "^2.0.0" +dev_dependencies: + build_runner: ^0.3.0 + check_for_update: ^1.0.0 environment: sdk: ">=1.19.0" executables: diff --git a/tool/build.dart b/tool/build.dart new file mode 100644 index 00000000..c07165fd --- /dev/null +++ b/tool/build.dart @@ -0,0 +1,4 @@ +import 'package:build_runner/build_runner.dart'; +import 'phases.dart'; + +main() => build(phaseGroup, deleteFilesByDefault: true); diff --git a/tool/phases.dart b/tool/phases.dart new file mode 100644 index 00000000..1f5fbf61 --- /dev/null +++ b/tool/phases.dart @@ -0,0 +1,6 @@ +import 'package:build_runner/build_runner.dart'; +import 'package:check_for_update/builder.dart'; + +final PhaseGroup phaseGroup = new PhaseGroup.singleAction( + new CheckForUpdateBuilder(subDirectory: 'lib'), + new InputSet('angel_cli', const ['pubspec.yaml'])); diff --git a/tool/watch.dart b/tool/watch.dart new file mode 100644 index 00000000..cd2ce7db --- /dev/null +++ b/tool/watch.dart @@ -0,0 +1,4 @@ +import 'package:build_runner/build_runner.dart'; +import 'phases.dart'; + +main() => watch(phaseGroup, deleteFilesByDefault: true); From ac181b2242bcb20b2584d4f178d965c0b708bfb8 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 24 Mar 2017 16:27:01 -0400 Subject: [PATCH 044/132] 1.0.7 --- bin/angel.dart | 2 +- lib/pubspec.dart | 1 + lib/{ => src}/pubspec.update.g.dart | 2 +- tool/phases.dart | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 lib/pubspec.dart rename lib/{ => src}/pubspec.update.g.dart (94%) diff --git a/bin/angel.dart b/bin/angel.dart index 8288adf6..7ced0bef 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -4,7 +4,7 @@ library demon.tool; import "dart:io"; import "package:args/command_runner.dart"; import 'package:angel_cli/angel_cli.dart'; -import 'package:angel_cli/pubspec.update.g.dart'; +import 'package:angel_cli/pubspec.dart'; import 'package:console/console.dart'; import 'package:http/http.dart' as http; diff --git a/lib/pubspec.dart b/lib/pubspec.dart new file mode 100644 index 00000000..874a4c7a --- /dev/null +++ b/lib/pubspec.dart @@ -0,0 +1 @@ +export 'src/pubspec.update.g.dart'; \ No newline at end of file diff --git a/lib/pubspec.update.g.dart b/lib/src/pubspec.update.g.dart similarity index 94% rename from lib/pubspec.update.g.dart rename to lib/src/pubspec.update.g.dart index b79370e3..7ea21bad 100644 --- a/lib/pubspec.update.g.dart +++ b/lib/src/pubspec.update.g.dart @@ -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, 0, 6); +final Version PACKAGE_VERSION = new Version(1, 0, 7); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/tool/phases.dart b/tool/phases.dart index 1f5fbf61..9e82fe33 100644 --- a/tool/phases.dart +++ b/tool/phases.dart @@ -2,5 +2,5 @@ import 'package:build_runner/build_runner.dart'; import 'package:check_for_update/builder.dart'; final PhaseGroup phaseGroup = new PhaseGroup.singleAction( - new CheckForUpdateBuilder(subDirectory: 'lib'), + new CheckForUpdateBuilder(subDirectory: 'lib/src'), new InputSet('angel_cli', const ['pubspec.yaml'])); From 0daa67f510b6db4148ec33889ea4263efdaf23a5 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 25 Mar 2017 18:33:28 -0400 Subject: [PATCH 045/132] 1.0.8 --- bin/angel.dart | 33 +++++++++++++++++++-------------- pubspec.yaml | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/bin/angel.dart b/bin/angel.dart index 7ced0bef..6a8ae5ee 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -25,21 +25,26 @@ main(List args) async { ..addCommand(new RenameCommand()); stdout.write('Checking for update... '); - var client = new http.Client(); - var update = await checkForUpdate(client); - client.close(); - if (update != null) { - stdout.writeln(); - var pen = new TextPen(); - pen.cyan(); - pen.text( - 'ATTENTION: There is a new version of the Angel CLI available (version $update).'); - pen.text('\nTo update, run `pub global activate angel_cli`.'); - pen(); - stdout.writeln(); - } else - stdout.writeln('No update available.'); + try { + var client = new http.Client(); + var update = await checkForUpdate(client); + client.close(); + + if (update != null) { + stdout.writeln(); + var pen = new TextPen(); + pen.cyan(); + pen.text( + 'ATTENTION: There is a new version of the Angel CLI available (version $update).'); + pen.text('\nTo update, run `pub global activate angel_cli`.'); + pen(); + stdout.writeln(); + } else + stdout.writeln('No update available.'); + } catch (e) { + stdout.writeln('Failed to check for update.'); + } return await runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/pubspec.yaml b/pubspec.yaml index b4e736a5..a642a60a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.7" +version: "1.0.8" dependencies: # analyzer: "^0.29.0" args: "^0.13.7" From cff49aa94a935e3e74c337e166738fc8728641d7 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 25 Mar 2017 18:39:31 -0400 Subject: [PATCH 046/132] 1.0.9 --- lib/src/commands/plugin.dart | 3 ++- lib/src/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart index fdadffe2..dd1e0c56 100644 --- a/lib/src/commands/plugin.dart +++ b/lib/src/commands/plugin.dart @@ -32,7 +32,8 @@ class PluginCommand extends Command { String _generatePlugin(PubSpec pubspec, String name, String lower) { return ''' -library ${pubspec.name}.plugins.$lower; +library ${pubspec.name}.config.plugins.$lower; + import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; diff --git a/lib/src/pubspec.update.g.dart b/lib/src/pubspec.update.g.dart index 7ea21bad..0bd08f21 100644 --- a/lib/src/pubspec.update.g.dart +++ b/lib/src/pubspec.update.g.dart @@ -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, 0, 7); +final Version PACKAGE_VERSION = new Version(1, 0, 9); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index a642a60a..63c7451e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.8" +version: "1.0.9" dependencies: # analyzer: "^0.29.0" args: "^0.13.7" From 8b71a2603ddf287cfc4adde6c7c848cd9d1ba837 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Mon, 10 Apr 2017 09:42:17 -0400 Subject: [PATCH 047/132] override dates --- lib/src/commands/service.dart | 4 +++- lib/src/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index dda6e6fb..f65e293e 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -165,8 +165,10 @@ class $name extends Model { @override String id; String name, desc; + @override + DateTime createdAt, updatedAt; - $name({this.id, this.name, this.desc}); + $name({this.id, this.name, this.desc, this.createdAt, this.updatedAt}); } ''' .trim()); diff --git a/lib/src/pubspec.update.g.dart b/lib/src/pubspec.update.g.dart index 0bd08f21..0f2c34a3 100644 --- a/lib/src/pubspec.update.g.dart +++ b/lib/src/pubspec.update.g.dart @@ -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, 0, 9); +final Version PACKAGE_VERSION = new Version(1, 0, 10); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index 63c7451e..8706e718 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.9" +version: 1.0.10 dependencies: # analyzer: "^0.29.0" args: "^0.13.7" From 1675cbd701cd4bb3a8985451473ab94bf6752878 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 26 Apr 2017 09:21:25 -0400 Subject: [PATCH 048/132] Bugfixes --- .idea/angel_cli.iml | 2 +- lib/src/commands/init.dart | 3 +++ lib/src/commands/service.dart | 4 ++-- lib/src/commands/service_generators/map.dart | 4 ++-- .../commands/service_generators/mongo.dart | 3 +++ .../commands/service_generators/rethink.dart | 3 +++ lib/src/commands/start.dart | 20 ++++++++++++++++--- pubspec.yaml | 2 +- 8 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.idea/angel_cli.iml b/.idea/angel_cli.iml index e673c03e..0a2188d2 100644 --- a/.idea/angel_cli.iml +++ b/.idea/angel_cli.iml @@ -6,7 +6,6 @@ - @@ -17,6 +16,7 @@ + diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 744ca6d3..59f435b0 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -6,6 +6,8 @@ import 'package:path/path.dart' as p; import 'key.dart'; import 'rename.dart'; +final RegExp _leadingSlashes = new RegExp(r'^/+'); + class InitCommand extends Command { final KeyCommand _key = new KeyCommand(); final TextPen _pen = new TextPen(); @@ -143,6 +145,7 @@ class InitCommand extends Command { _pubGet(Directory projectDir) async { var exec = new File(Platform.resolvedExecutable); var pubPath = exec.parent.uri.resolve('pub').path; + if (Platform.isWindows) pubPath = pubPath.replaceAll(_leadingSlashes, ''); print('Running pub at "$pubPath"...'); var pub = await Process.start(pubPath, ["get"], workingDirectory: projectDir.absolute.path); diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index f65e293e..c5c682f7 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -107,7 +107,7 @@ class ServiceCommand extends Command { new ImportBuilder('package:angel_common/angel_common.dart')); generator.applyToLibrary(lib, name, lower); - if (generator.createsModel == true) { + if (generator.createsModel == true || typed) { lib ..addMember(new ImportBuilder('../models/$lower.dart')) ..addMember(new ExportBuilder('../models/$lower.dart')); @@ -180,7 +180,7 @@ class $name extends Model { if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' -library ${pubspec.name}.models.$lower; +library ${pubspec.name}.validtors.$lower; import 'package:angel_validate/angel_validate.dart'; final Validator $constantCase = new Validator({ diff --git a/lib/src/commands/service_generators/map.dart b/lib/src/commands/service_generators/map.dart index d0f7eb44..ec7b1499 100644 --- a/lib/src/commands/service_generators/map.dart +++ b/lib/src/commands/service_generators/map.dart @@ -2,11 +2,11 @@ import 'generator.dart'; import 'package:code_builder/code_builder.dart'; class MapServiceGenerator extends ServiceGenerator { + const MapServiceGenerator() : super('In-Memory'); + @override bool get createsModel => false; - const MapServiceGenerator() : super('In-Memory'); - @override ExpressionBuilder createInstance( MethodBuilder methodBuilder, String name, String lower) { diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index af15f740..16e7c363 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -5,6 +5,9 @@ import 'package:inflection/inflection.dart'; class MongoServiceGenerator extends ServiceGenerator { const MongoServiceGenerator() : super('MongoDB'); + @override + bool get createsModel => false; + @override void applyToConfigureServer( MethodBuilder configureServer, String name, String lower) { diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 71d6acc5..22d0bd64 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -5,6 +5,9 @@ import 'package:inflection/inflection.dart'; class RethinkServiceGenerator extends ServiceGenerator { const RethinkServiceGenerator() : super('RethinkDB'); + @override + bool get createsModel => false; + @override void applyToConfigureServer( MethodBuilder configureServer, String name, String lower) { diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index 02ac0c00..f9af79bc 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -34,9 +34,23 @@ class StartCommand extends Command { @override run() async { if (argResults['watch']) { - new DirectoryWatcher('bin').events.listen((_) async => start()); - new DirectoryWatcher('config').events.listen((_) async => start()); - new DirectoryWatcher('lib').events.listen((_) async => start()); + try { + new DirectoryWatcher('bin').events.listen((_) async => start()); + } catch (e) { + // Fail silently... + } + + try { + new DirectoryWatcher('config').events.listen((_) async => start()); + } catch (e) { + // Fail silently... + } + + try { + new DirectoryWatcher('lib').events.listen((_) async => start()); + } catch (e) { + // Fail silently... + } } return await start(); diff --git a/pubspec.yaml b/pubspec.yaml index 8706e718..bbb0ff9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.0.10 +version: 1.0.11 dependencies: # analyzer: "^0.29.0" args: "^0.13.7" From 40b155fb328f8fe67a342f14793b715150941f59 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 26 Apr 2017 09:22:03 -0400 Subject: [PATCH 049/132] Version --- lib/src/pubspec.update.g.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/pubspec.update.g.dart b/lib/src/pubspec.update.g.dart index 0f2c34a3..5e85f7e3 100644 --- a/lib/src/pubspec.update.g.dart +++ b/lib/src/pubspec.update.g.dart @@ -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, 0, 10); +final Version PACKAGE_VERSION = new Version(1, 0, 11); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); From 0df215730011ac40fb35fd022c16fbf17cd29ceb Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 3 Jun 2017 22:19:44 -0400 Subject: [PATCH 050/132] 1.0.12 --- .idea/angel_cli.iml | 4 +++- .idea/runConfigurations/build_dart.xml | 7 +++++++ lib/src/commands/init.dart | 10 ++++++++-- lib/src/commands/start.dart | 3 ++- lib/src/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 6 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 .idea/runConfigurations/build_dart.xml diff --git a/.idea/angel_cli.iml b/.idea/angel_cli.iml index 0a2188d2..0f3fa76d 100644 --- a/.idea/angel_cli.iml +++ b/.idea/angel_cli.iml @@ -9,10 +9,12 @@ + + @@ -20,7 +22,7 @@ - + \ No newline at end of file diff --git a/.idea/runConfigurations/build_dart.xml b/.idea/runConfigurations/build_dart.xml new file mode 100644 index 00000000..1bf4884c --- /dev/null +++ b/.idea/runConfigurations/build_dart.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 59f435b0..7b01a35e 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -142,10 +142,16 @@ class InitCommand extends Command { } } - _pubGet(Directory projectDir) async { + static String resolvePub() { var exec = new File(Platform.resolvedExecutable); var pubPath = exec.parent.uri.resolve('pub').path; - if (Platform.isWindows) pubPath = pubPath.replaceAll(_leadingSlashes, ''); + if (Platform.isWindows) pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat'; + pubPath = Uri.decodeFull(pubPath); + return pubPath; + } + + _pubGet(Directory projectDir) async { + var pubPath = resolvePub(); print('Running pub at "$pubPath"...'); var pub = await Process.start(pubPath, ["get"], workingDirectory: projectDir.absolute.path); diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index f9af79bc..3d1580a8 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:watcher/watcher.dart'; import 'package:yaml/yaml.dart'; +import 'init.dart'; Process server; bool watching = false; @@ -76,7 +77,7 @@ class StartCommand extends Command { if (scriptsNode != null && scriptsNode.containsKey('start')) { try { var scripts = - await Process.start('pub', ['global', 'run', 'scripts', 'start']); + await Process.start(InitCommand.resolvePub(), ['global', 'run', 'scripts', 'start']); listen(scripts.stdout, stdout); listen(scripts.stderr, stderr); int code = await scripts.exitCode; diff --git a/lib/src/pubspec.update.g.dart b/lib/src/pubspec.update.g.dart index 5e85f7e3..04cc7783 100644 --- a/lib/src/pubspec.update.g.dart +++ b/lib/src/pubspec.update.g.dart @@ -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, 0, 11); +final Version PACKAGE_VERSION = new Version(1, 0, 12); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index bbb0ff9e..a499da46 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.0.11 +version: 1.0.12 dependencies: # analyzer: "^0.29.0" args: "^0.13.7" From 1af44cab90b070766d39f0e4f64dc1e97fbeeb5f Mon Sep 17 00:00:00 2001 From: thosakwe Date: Tue, 6 Jun 2017 08:23:28 -0400 Subject: [PATCH 051/132] Encourage hot reload --- lib/src/commands/start.dart | 12 ++++++++++-- lib/src/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index 3d1580a8..b37a665b 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -34,6 +34,14 @@ class StartCommand extends Command { @override run() async { + stderr + ..writeln( + 'WARNING: `angel start` is now deprecated, in favor of `package:angel_hot`.') + ..writeln( + 'This new alternative supports hot reloading, which is faster and more reliable.') + ..writeln() + ..writeln('Find it on Pub: https://pub.dartlang.org/packages/angel_hot'); + if (argResults['watch']) { try { new DirectoryWatcher('bin').events.listen((_) async => start()); @@ -76,8 +84,8 @@ class StartCommand extends Command { if (scriptsNode != null && scriptsNode.containsKey('start')) { try { - var scripts = - await Process.start(InitCommand.resolvePub(), ['global', 'run', 'scripts', 'start']); + var scripts = await Process.start( + InitCommand.resolvePub(), ['global', 'run', 'scripts', 'start']); listen(scripts.stdout, stdout); listen(scripts.stderr, stderr); int code = await scripts.exitCode; diff --git a/lib/src/pubspec.update.g.dart b/lib/src/pubspec.update.g.dart index 04cc7783..507249b4 100644 --- a/lib/src/pubspec.update.g.dart +++ b/lib/src/pubspec.update.g.dart @@ -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, 0, 12); +final Version PACKAGE_VERSION = new Version(1, 1, 0); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index a499da46..0fac6821 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.0.12 +version: 1.1.0 dependencies: # analyzer: "^0.29.0" args: "^0.13.7" From dea34baf4ef9649d7cf96a737c78cdc04c4b1a52 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 14 Jun 2017 21:13:03 -0400 Subject: [PATCH 052/132] 1.1.1 --- .idea/runConfigurations/Controller.xml | 8 +++ .idea/runConfigurations/Update.xml | 8 +++ bin/angel.dart | 31 ++------- lib/angel_cli.dart | 2 +- lib/pubspec.dart | 1 - lib/src/commands/commands.dart | 4 +- lib/src/commands/controller.dart | 66 +++++++++++++++++++ lib/src/commands/init.dart | 12 +++- lib/src/{ => commands}/pubspec.update.g.dart | 2 +- lib/src/commands/service.dart | 5 +- .../service_generators/file_service.dart | 26 ++++++++ .../service_generators.dart | 1 + lib/src/commands/test.dart | 8 +-- lib/src/commands/update.dart | 66 +++++++++++++++++++ pubspec.yaml | 6 +- tool/phases.dart | 2 +- 16 files changed, 203 insertions(+), 45 deletions(-) create mode 100644 .idea/runConfigurations/Controller.xml create mode 100644 .idea/runConfigurations/Update.xml delete mode 100644 lib/pubspec.dart create mode 100644 lib/src/commands/controller.dart rename lib/src/{ => commands}/pubspec.update.g.dart (94%) create mode 100644 lib/src/commands/service_generators/file_service.dart create mode 100644 lib/src/commands/update.dart diff --git a/.idea/runConfigurations/Controller.xml b/.idea/runConfigurations/Controller.xml new file mode 100644 index 00000000..38315109 --- /dev/null +++ b/.idea/runConfigurations/Controller.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Update.xml b/.idea/runConfigurations/Update.xml new file mode 100644 index 00000000..75cab2d3 --- /dev/null +++ b/.idea/runConfigurations/Update.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index 6a8ae5ee..ee16c084 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -1,12 +1,9 @@ #!/usr/bin/env dart -library demon.tool; +library angel_cli.tool; import "dart:io"; import "package:args/command_runner.dart"; import 'package:angel_cli/angel_cli.dart'; -import 'package:angel_cli/pubspec.dart'; -import 'package:console/console.dart'; -import 'package:http/http.dart' as http; final String DOCTOR = "doctor"; @@ -15,6 +12,7 @@ main(List args) async { new CommandRunner("angel", "Command-line tools for the Angel framework."); runner + ..addCommand(new ControllerCommand()) ..addCommand(new DoctorCommand()) ..addCommand(new KeyCommand()) ..addCommand(new ServiceCommand()) @@ -22,29 +20,8 @@ main(List args) async { ..addCommand(new TestCommand()) ..addCommand(new PluginCommand()) ..addCommand(new StartCommand()) - ..addCommand(new RenameCommand()); - - stdout.write('Checking for update... '); - - try { - var client = new http.Client(); - var update = await checkForUpdate(client); - client.close(); - - if (update != null) { - stdout.writeln(); - var pen = new TextPen(); - pen.cyan(); - pen.text( - 'ATTENTION: There is a new version of the Angel CLI available (version $update).'); - pen.text('\nTo update, run `pub global activate angel_cli`.'); - pen(); - stdout.writeln(); - } else - stdout.writeln('No update available.'); - } catch (e) { - stdout.writeln('Failed to check for update.'); - } + ..addCommand(new RenameCommand()) + ..addCommand(new UpdateCommand()); return await runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/lib/angel_cli.dart b/lib/angel_cli.dart index 3a879ac2..4d3fa997 100644 --- a/lib/angel_cli.dart +++ b/lib/angel_cli.dart @@ -1,3 +1,3 @@ -library demon; +library angel_cli; export 'src/commands/commands.dart'; \ No newline at end of file diff --git a/lib/pubspec.dart b/lib/pubspec.dart deleted file mode 100644 index 874a4c7a..00000000 --- a/lib/pubspec.dart +++ /dev/null @@ -1 +0,0 @@ -export 'src/pubspec.update.g.dart'; \ No newline at end of file diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index bccb48f1..43ab86ed 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -1,5 +1,6 @@ library angel_cli.commands; +export 'controller.dart'; export "doctor.dart"; export "key.dart"; export "init.dart"; @@ -7,4 +8,5 @@ export "plugin.dart"; export "rename.dart"; export "service.dart"; export "start.dart"; -export "test.dart"; \ No newline at end of file +export "test.dart"; +export 'update.dart'; \ No newline at end of file diff --git a/lib/src/commands/controller.dart b/lib/src/commands/controller.dart new file mode 100644 index 00000000..fbf5a06d --- /dev/null +++ b/lib/src/commands/controller.dart @@ -0,0 +1,66 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:code_builder/code_builder.dart'; +import "package:console/console.dart"; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; + +class ControllerCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => "controller"; + + @override + String get description => + "Creates a new controller within the given project."; + + @override + run() async { + final name = await readInput("Name of Controller: "), + recase = new ReCase(name), + lower = recase.snakeCase; + final controllersDir = new Directory("lib/src/routes/controllers"); + final controllerFile = + new File.fromUri(controllersDir.uri.resolve("$lower.dart")); + + if (!await controllerFile.exists()) + await controllerFile.create(recursive: true); + + await controllerFile.writeAsString( + _generateController(await PubSpec.load(Directory.current), recase)); + + _pen.green(); + _pen("${Icon.CHECKMARK} Successfully generated controller $name."); + _pen(); + } + + NewInstanceBuilder _expose(String path) => new TypeBuilder('Expose') + .constInstance([], namedArguments: {'path': literal(path)}); + + String _generateController(PubSpec pubspec, ReCase recase) { + var lower = recase.snakeCase; + var lib = new LibraryBuilder('${pubspec.name}.routes.controllers.$lower'); + lib.addDirective( + new ImportBuilder('package:angel_common/angel_common.dart')); + + var clazz = new ClassBuilder('${recase.pascalCase}Controller', + asExtends: new TypeBuilder('Controller')); + + // Add @Expose() + clazz.addAnnotation(_expose('/$lower')); + + // Add + // @Expose(path: '/') + // String foo() => 'bar'; + + var meth = new MethodBuilder('foo', + returns: literal('bar'), returnType: new TypeBuilder('String')); + meth.addAnnotation(_expose('/')); + clazz.addMethod(meth); + + lib.addMember(clazz); + + return prettyToSource(lib.buildAst()); + } +} diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 7b01a35e..69917acb 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -64,7 +64,14 @@ class InitCommand extends Command { ..text('`angel start`') ..normal() ..text(' in your terminal.') - ..text('\nHappy coding!') + ..text('\n\nFind more documentation about Angel:') + ..text('\n * https://angel-dart.github.io') + ..text('\n * https://github.com/angel-dart/angel/wiki') + ..text( + '\n * https://www.youtube.com/playlist?list=PLl3P3tmiT-frEV50VdH_cIrA2YqIyHkkY') + ..text('\n * https://medium.com/the-angel-framework') + ..text('\n * https://dart.academy/tag/angel') + ..text('\n\nHappy coding!') ..call(); } @@ -145,7 +152,8 @@ class InitCommand extends Command { static String resolvePub() { var exec = new File(Platform.resolvedExecutable); var pubPath = exec.parent.uri.resolve('pub').path; - if (Platform.isWindows) pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat'; + if (Platform.isWindows) + pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat'; pubPath = Uri.decodeFull(pubPath); return pubPath; } diff --git a/lib/src/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart similarity index 94% rename from lib/src/pubspec.update.g.dart rename to lib/src/commands/pubspec.update.g.dart index 507249b4..9d141f37 100644 --- a/lib/src/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 0); +final Version PACKAGE_VERSION = new Version(1, 1, 1); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index c5c682f7..0e7a00f1 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -10,6 +10,7 @@ import 'init.dart' show preBuild; const List GENERATORS = const [ const MapServiceGenerator(), + const FileServiceGenerator(), const MongoServiceGenerator(), const RethinkServiceGenerator(), const CustomServiceGenerator() @@ -70,7 +71,7 @@ class ServiceCommand extends Command { await runConfig.writeAsString(_generateRunConfiguration(name, lower)); } - if (generator.createsModel == true) { + if (generator.createsModel == true || typed == true) { await _generateModel(pubspec, name, lower); } @@ -78,7 +79,7 @@ class ServiceCommand extends Command { await _generateValidator(pubspec, lower, rc.constantCase); } - if (generator.exportedInServiceLibrary == true) { + if (generator.exportedInServiceLibrary == true || typed == true) { var serviceLibrary = new File('lib/src/models/models.dart'); await serviceLibrary.writeAsString("\nexport '$lower.dart';", mode: FileMode.APPEND); diff --git a/lib/src/commands/service_generators/file_service.dart b/lib/src/commands/service_generators/file_service.dart new file mode 100644 index 00000000..7778e2d9 --- /dev/null +++ b/lib/src/commands/service_generators/file_service.dart @@ -0,0 +1,26 @@ +import 'generator.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:inflection/inflection.dart'; + +class FileServiceGenerator extends ServiceGenerator { + const FileServiceGenerator() : super('Persistent JSON File'); + + @override + bool get createsModel => false; + + @override + void applyToLibrary(LibraryBuilder library, String name, String lower) { + library.addMember(new ImportBuilder('dart:io')); + library.addMember(new ImportBuilder( + 'package:angel_file_service/angel_file_service.dart')); + } + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('JsonFileService').newInstance([ + new TypeBuilder('File') + .newInstance([literal(pluralize(lower) + '_db.json')]) + ]); + } +} diff --git a/lib/src/commands/service_generators/service_generators.dart b/lib/src/commands/service_generators/service_generators.dart index 760cd927..658cc16b 100644 --- a/lib/src/commands/service_generators/service_generators.dart +++ b/lib/src/commands/service_generators/service_generators.dart @@ -1,4 +1,5 @@ export 'custom.dart'; +export 'file_service.dart'; export 'generator.dart'; export 'map.dart'; export 'mongo.dart'; diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index 47c022e0..fdb5dee7 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -61,18 +61,14 @@ import 'package:angel_test/angel_test.dart'; import 'package:test/test.dart'; main() async { - Angel app; TestClient client; setUp(() async { - app = await createServer(); + var app = await createServer(); client = await connectTo(app); }); - tearDown(() async { - await client.close(); - app = null; - }); + tearDown(() => client.close()); test('$lower', () async { final response = await client.get('/$lower'); diff --git a/lib/src/commands/update.dart b/lib/src/commands/update.dart new file mode 100644 index 00000000..8f17b579 --- /dev/null +++ b/lib/src/commands/update.dart @@ -0,0 +1,66 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:console/console.dart'; +import 'package:http/http.dart' as http; +import 'init.dart'; +import 'pubspec.update.g.dart'; + +class UpdateCommand extends Command { + @override + String get name => 'update'; + + @override + String get description => 'Updates the Angel CLI, if an update is available.'; + + @override + run() async { + stdout.write('Checking for update... '); + + try { + var client = new http.Client(); + var update = await checkForUpdate(client); + client.close(); + + if (update != null) { + stdout.writeln(); + var pen = new TextPen(); + pen.cyan(); + pen.text( + 'ATTENTION: There is a new version of the Angel CLI available (version $update).'); + pen.call(); + var prompt = new Chooser(['Yes', 'No']); + print('Update now?'); + var choice = await prompt.choose(); + + if (choice != 'Yes') { + pen.reset(); + pen.cyan(); + pen.text( + 'When you are ready to update, run `pub global activate angel_cli`.'); + pen(); + stdout.writeln(); + } else { + var pubPath = InitCommand.resolvePub(); + print('Running `pub global activate` using $pubPath...'); + var p = + await Process.start(pubPath, ['global', 'activate', 'angel_cli']); + p.stderr.listen(stderr.add); + p.stdout.listen(stdout.add); + var exitCode = await p.exitCode; + + if (exitCode != 0) + throw 'Pub terminated with a non-zero exit code.'; + else { + pen.reset(); + pen.green(); + pen("${Icon.CHECKMARK} Successfully updated the Angel CLI to version $update.\n"); + pen(); + } + } + } else + stdout.writeln('No update available.'); + } catch (e) { + stdout.writeln('Failed to check for update.'); + } + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 0fac6821..0f44f331 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,11 +2,11 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.1.0 +version: 1.1.1 dependencies: # analyzer: "^0.29.0" - args: "^0.13.7" - code_builder: "^1.0.0-beta" + args: ^0.13.4 + code_builder: ^1.0.0 console: "^2.2.3" glob: "^1.1.0" http: ^0.11.3 diff --git a/tool/phases.dart b/tool/phases.dart index 9e82fe33..94bcdbf6 100644 --- a/tool/phases.dart +++ b/tool/phases.dart @@ -2,5 +2,5 @@ import 'package:build_runner/build_runner.dart'; import 'package:check_for_update/builder.dart'; final PhaseGroup phaseGroup = new PhaseGroup.singleAction( - new CheckForUpdateBuilder(subDirectory: 'lib/src'), + new CheckForUpdateBuilder(subDirectory: 'lib/src/commands'), new InputSet('angel_cli', const ['pubspec.yaml'])); From cb075a4dc056bb7bc46d29cc22c0c58251df826c Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 14 Jun 2017 21:15:14 -0400 Subject: [PATCH 053/132] 1.1.2 --- lib/src/commands/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 9d141f37..fd4e05e2 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 1); +final Version PACKAGE_VERSION = new Version(1, 1, 2); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index 0f44f331..fc2c35c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.1.1 +version: 1.1.2 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From 2414886c2a28688d4c1decf688fc97fc24997aea Mon Sep 17 00:00:00 2001 From: thosakwe Date: Thu, 15 Jun 2017 16:43:16 -0400 Subject: [PATCH 054/132] Multiple boilerplates --- lib/src/commands/init.dart | 40 ++++++++++++++++++++------ lib/src/commands/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 69917acb..6de7436c 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -122,13 +122,14 @@ class InitCommand extends Command { } } - var git = await Process.start("git", [ - "clone", - "--depth", - "1", - "https://github.com/angel-dart/angel", - projectDir.absolute.path - ]); + print('Choose a project type before continuing:'); + var boilerplateChooser = new Chooser(ALL_BOILERPLATES); + var boilerplate = await boilerplateChooser.choose(); + + print( + 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); + var git = await Process.start("git", + ["clone", "--depth", "1", boilerplate.url, projectDir.absolute.path]); stdout.addStream(git.stdout); stderr.addStream(git.stderr); @@ -138,7 +139,6 @@ class InitCommand extends Command { } var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git")); - if (await gitDir.exists()) await gitDir.delete(recursive: true); } catch (e) { print(e); @@ -184,3 +184,27 @@ preBuild(Directory projectDir) async { if (buildCode != 0) throw new Exception('Failed to pre-build resources.'); } + +const BoilerplateInfo FULL_APPLICATION_BOILERPLATE = const BoilerplateInfo( + 'Full Application', + 'A complete project including authentication, multi-threading, and more.', + 'https://github.com/angel-dart/angel.git'); + +const BoilerplateInfo LIGHT_BOILERPLATE = const BoilerplateInfo( + 'Light', + 'Minimal starting point for new users', + 'https://github.com/angel-dart/boilerplate_light.git'); + +const List ALL_BOILERPLATES = const [ + FULL_APPLICATION_BOILERPLATE, + LIGHT_BOILERPLATE +]; + +class BoilerplateInfo { + final String name, description, url; + + const BoilerplateInfo(this.name, this.description, this.url); + + @override + String toString() => '$name ($description)'; +} diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index fd4e05e2..4207fb77 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 2); +final Version PACKAGE_VERSION = new Version(1, 1, 3); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index fc2c35c2..b7677786 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.1.2 +version: 1.1.3 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From 6f5b4624b434149d9f071bd314506bc5c5585c9b Mon Sep 17 00:00:00 2001 From: thosakwe Date: Thu, 15 Jun 2017 22:57:35 -0400 Subject: [PATCH 055/132] 1.1.4 --- .idea/runConfigurations/Version.xml | 7 +++++++ bin/angel.dart | 3 ++- lib/src/commands/commands.dart | 3 ++- lib/src/commands/init.dart | 12 +----------- lib/src/commands/pub.dart | 13 +++++++++++++ lib/src/commands/pubspec.update.g.dart | 2 +- lib/src/commands/rename.dart | 3 ++- lib/src/commands/start.dart | 4 ++-- lib/src/commands/update.dart | 4 ++-- lib/src/commands/version.dart | 13 +++++++++++++ pubspec.yaml | 2 +- 11 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 .idea/runConfigurations/Version.xml create mode 100644 lib/src/commands/pub.dart create mode 100644 lib/src/commands/version.dart diff --git a/.idea/runConfigurations/Version.xml b/.idea/runConfigurations/Version.xml new file mode 100644 index 00000000..cd70052b --- /dev/null +++ b/.idea/runConfigurations/Version.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index ee16c084..97f470fb 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -21,7 +21,8 @@ main(List args) async { ..addCommand(new PluginCommand()) ..addCommand(new StartCommand()) ..addCommand(new RenameCommand()) - ..addCommand(new UpdateCommand()); + ..addCommand(new UpdateCommand()) + ..addCommand(new VersionCommand()); return await runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 43ab86ed..4264689c 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -9,4 +9,5 @@ export "rename.dart"; export "service.dart"; export "start.dart"; export "test.dart"; -export 'update.dart'; \ No newline at end of file +export 'update.dart'; +export 'version.dart'; \ No newline at end of file diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 6de7436c..a7b3f6f5 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -4,10 +4,9 @@ import "package:console/console.dart"; import 'package:random_string/random_string.dart' as rs; import 'package:path/path.dart' as p; import 'key.dart'; +import 'pub.dart'; import 'rename.dart'; -final RegExp _leadingSlashes = new RegExp(r'^/+'); - class InitCommand extends Command { final KeyCommand _key = new KeyCommand(); final TextPen _pen = new TextPen(); @@ -149,15 +148,6 @@ class InitCommand extends Command { } } - static String resolvePub() { - var exec = new File(Platform.resolvedExecutable); - var pubPath = exec.parent.uri.resolve('pub').path; - if (Platform.isWindows) - pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat'; - pubPath = Uri.decodeFull(pubPath); - return pubPath; - } - _pubGet(Directory projectDir) async { var pubPath = resolvePub(); print('Running pub at "$pubPath"...'); diff --git a/lib/src/commands/pub.dart b/lib/src/commands/pub.dart new file mode 100644 index 00000000..113c8b33 --- /dev/null +++ b/lib/src/commands/pub.dart @@ -0,0 +1,13 @@ +import 'dart:io'; + +final RegExp _leadingSlashes = new RegExp(r'^/+'); + + + String resolvePub() { +var exec = new File(Platform.resolvedExecutable); +var pubPath = exec.parent.uri.resolve('pub').path; +if (Platform.isWindows) +pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat'; +pubPath = Uri.decodeFull(pubPath); +return pubPath; +} \ No newline at end of file diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 4207fb77..0aa8ac8f 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 3); +final Version PACKAGE_VERSION = new Version(1, 1, 4); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index 1eee74aa..c3544e76 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -3,6 +3,7 @@ import 'package:analyzer/analyzer.dart'; import 'package:args/command_runner.dart'; import 'package:console/console.dart'; import 'package:pubspec/pubspec.dart'; +import 'pub.dart'; class RenameCommand extends Command { @override @@ -42,7 +43,7 @@ class RenameCommand extends Command { await renamePubspec(Directory.current, oldName, newName); await renameDartFiles(Directory.current, oldName, newName); print('Now running `pub get`...'); - var pub = await Process.start('pub', ['get']); + var pub = await Process.start(resolvePub(), ['get']); stdout.addStream(pub.stdout); stderr.addStream(pub.stderr); await pub.exitCode; diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index b37a665b..f1fac21a 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:watcher/watcher.dart'; import 'package:yaml/yaml.dart'; -import 'init.dart'; +import 'pub.dart'; Process server; bool watching = false; @@ -85,7 +85,7 @@ class StartCommand extends Command { if (scriptsNode != null && scriptsNode.containsKey('start')) { try { var scripts = await Process.start( - InitCommand.resolvePub(), ['global', 'run', 'scripts', 'start']); + resolvePub(), ['global', 'run', 'scripts', 'start']); listen(scripts.stdout, stdout); listen(scripts.stderr, stderr); int code = await scripts.exitCode; diff --git a/lib/src/commands/update.dart b/lib/src/commands/update.dart index 8f17b579..5eadffc4 100644 --- a/lib/src/commands/update.dart +++ b/lib/src/commands/update.dart @@ -2,8 +2,8 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:console/console.dart'; import 'package:http/http.dart' as http; -import 'init.dart'; import 'pubspec.update.g.dart'; +import 'pub.dart'; class UpdateCommand extends Command { @override @@ -40,7 +40,7 @@ class UpdateCommand extends Command { pen(); stdout.writeln(); } else { - var pubPath = InitCommand.resolvePub(); + var pubPath = resolvePub(); print('Running `pub global activate` using $pubPath...'); var p = await Process.start(pubPath, ['global', 'activate', 'angel_cli']); diff --git a/lib/src/commands/version.dart b/lib/src/commands/version.dart new file mode 100644 index 00000000..4d265991 --- /dev/null +++ b/lib/src/commands/version.dart @@ -0,0 +1,13 @@ +import 'package:args/command_runner.dart'; +import 'pubspec.update.g.dart'; + +class VersionCommand extends Command { + @override + String get name => 'version'; + + @override + String get description => 'Prints the currently-installed version of the Angel CLI.'; + + @override + run() => print('Angel CLI version $PACKAGE_VERSION'); +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index b7677786..ab99f0a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.1.3 +version: 1.1.4 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From c351f0ed14395b4430ead504b83313e787f30a26 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 4 Aug 2017 11:42:07 -0400 Subject: [PATCH 056/132] Formatting --- analysis_options.yaml | 2 ++ lib/src/commands/init.dart | 19 ++++++---- lib/src/commands/plugin.dart | 4 ++- lib/src/commands/pub.dart | 17 +++++---- lib/src/commands/pubspec.update.g.dart | 2 +- lib/src/commands/rename.dart | 8 +++-- lib/src/commands/service.dart | 50 +++++++++++++------------- lib/src/commands/start.dart | 2 +- lib/src/commands/test.dart | 5 +-- pubspec.yaml | 4 ++- 10 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..518eb901 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true \ No newline at end of file diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index a7b3f6f5..57e1a8e3 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -122,7 +122,7 @@ class InitCommand extends Command { } print('Choose a project type before continuing:'); - var boilerplateChooser = new Chooser(ALL_BOILERPLATES); + var boilerplateChooser = new Chooser(allBoilerplates); var boilerplate = await boilerplateChooser.choose(); print( @@ -175,19 +175,24 @@ preBuild(Directory projectDir) async { if (buildCode != 0) throw new Exception('Failed to pre-build resources.'); } -const BoilerplateInfo FULL_APPLICATION_BOILERPLATE = const BoilerplateInfo( +const BoilerplateInfo fullApplicationBoilerplate = const BoilerplateInfo( 'Full Application', 'A complete project including authentication, multi-threading, and more.', 'https://github.com/angel-dart/angel.git'); -const BoilerplateInfo LIGHT_BOILERPLATE = const BoilerplateInfo( +const BoilerplateInfo lightBoilerplate = const BoilerplateInfo( 'Light', - 'Minimal starting point for new users', + 'Minimal starting point for new users.', 'https://github.com/angel-dart/boilerplate_light.git'); -const List ALL_BOILERPLATES = const [ - FULL_APPLICATION_BOILERPLATE, - LIGHT_BOILERPLATE +const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( + 'ORM', + "A starting point for applications that use Angel's ORM.", + 'https://github.com/angel-dart/boilerplate_orm.git'); + +const List allBoilerplates = const [ + fullApplicationBoilerplate, + lightBoilerplate ]; class BoilerplateInfo { diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart index dd1e0c56..2ded982f 100644 --- a/lib/src/commands/plugin.dart +++ b/lib/src/commands/plugin.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; +import 'package:dart_style/dart_style.dart'; import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; @@ -23,7 +24,8 @@ class PluginCommand extends Command { if (!await pluginFile.exists()) await pluginFile.create(recursive: true); - await pluginFile.writeAsString(_generatePlugin(pubspec, name, lower)); + await pluginFile.writeAsString( + new DartFormatter().format(_generatePlugin(pubspec, name, lower))); _pen.green(); _pen("${Icon.CHECKMARK} Successfully generated plugin $name."); diff --git a/lib/src/commands/pub.dart b/lib/src/commands/pub.dart index 113c8b33..1f3a74ce 100644 --- a/lib/src/commands/pub.dart +++ b/lib/src/commands/pub.dart @@ -2,12 +2,11 @@ import 'dart:io'; final RegExp _leadingSlashes = new RegExp(r'^/+'); - - String resolvePub() { -var exec = new File(Platform.resolvedExecutable); -var pubPath = exec.parent.uri.resolve('pub').path; -if (Platform.isWindows) -pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat'; -pubPath = Uri.decodeFull(pubPath); -return pubPath; -} \ No newline at end of file +String resolvePub() { + var exec = new File(Platform.resolvedExecutable); + var pubPath = exec.parent.uri.resolve('pub').path; + if (Platform.isWindows) + pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat'; + pubPath = Uri.decodeFull(pubPath); + return pubPath; +} diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 0aa8ac8f..7e5762c3 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 4); +final Version PACKAGE_VERSION = new Version(1, 1, 5); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index c3544e76..2c90d961 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:analyzer/analyzer.dart'; import 'package:args/command_runner.dart'; import 'package:console/console.dart'; +import 'package:dart_style/dart_style.dart'; import 'package:pubspec/pubspec.dart'; import 'pub.dart'; @@ -43,7 +44,9 @@ class RenameCommand extends Command { await renamePubspec(Directory.current, oldName, newName); await renameDartFiles(Directory.current, oldName, newName); print('Now running `pub get`...'); - var pub = await Process.start(resolvePub(), ['get']); + var pubPath = resolvePub(); + print('Pub path: $pubPath'); + var pub = await Process.start(pubPath, ['get']); stdout.addStream(pub.stdout); stderr.addStream(pub.stderr); await pub.exitCode; @@ -77,6 +80,7 @@ renameDartFiles(Directory dir, String oldName, String newName) async { print('Renaming library file `${entry.absolute.path}`...'); } + var fmt = new DartFormatter(); await for (FileSystemEntity file in dir.list(recursive: true)) { if (file is File && file.path.endsWith('.dart')) { var contents = await file.readAsString(); @@ -94,7 +98,7 @@ renameDartFiles(Directory dir, String oldName, String newName) async { } }); - await file.writeAsString(contents); + await file.writeAsString(fmt.format(contents)); print('Updated file `${file.absolute.path}`.'); } } diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 0e7a00f1..1db296e7 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import 'package:console/console.dart'; +import 'package:dart_style/dart_style.dart'; import 'package:inflection/inflection.dart'; import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; @@ -60,9 +61,11 @@ class ServiceCommand extends Command { await servicesDir.create(recursive: true); if (!await testDir.exists()) await testDir.create(recursive: true); + var fmt = new DartFormatter(); + await serviceFile .writeAsString(_generateService(generator, name, lower, typed)); - await testFile.writeAsString(_generateTests(pubspec, lower)); + await testFile.writeAsString(_generateTests(pubspec, lower, fmt)); var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); @@ -72,11 +75,11 @@ class ServiceCommand extends Command { } if (generator.createsModel == true || typed == true) { - await _generateModel(pubspec, name, lower); + await _generateModel(pubspec, name, lower, fmt); } if (generator.createsValidator == true) { - await _generateValidator(pubspec, lower, rc.constantCase); + await _generateValidator(pubspec, lower, rc, fmt); } if (generator.exportedInServiceLibrary == true || typed == true) { @@ -104,8 +107,7 @@ class ServiceCommand extends Command { import '../models/$lower.dart'; export '../models/$lower.dart'; */ - lib.addMember( - new ImportBuilder('package:angel_common/angel_common.dart')); + lib.addMember(new ImportBuilder('package:angel_common/angel_common.dart')); generator.applyToLibrary(lib, name, lower); if (generator.createsModel == true || typed) { @@ -153,47 +155,46 @@ class ServiceCommand extends Command { return prettyToSource(lib.buildAst()); } - _generateModel(PubSpec pubspec, String name, String lower) async { + _generateModel( + PubSpec pubspec, String name, String lower, DartFormatter fmt) async { var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); - await file.writeAsString(''' + await file.writeAsString(fmt.format(''' library ${pubspec.name}.models.$lower; -import 'package:angel_framework/common.dart'; +import 'package:angel_model/angel_model.dart'; class $name extends Model { @override String id; - String name, desc; + String name, description; @override DateTime createdAt, updatedAt; - $name({this.id, this.name, this.desc, this.createdAt, this.updatedAt}); + $name({this.id, this.name, this.description, this.createdAt, this.updatedAt}); } - ''' - .trim()); + ''')); } - _generateValidator(PubSpec pubspec, String lower, String constantCase) async { + _generateValidator( + PubSpec pubspec, String lower, ReCase rc, DartFormatter fmt) async { var file = new File('lib/src/validators/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); - await file.writeAsString(''' + await file.writeAsString(fmt.format(''' library ${pubspec.name}.validtors.$lower; import 'package:angel_validate/angel_validate.dart'; -final Validator $constantCase = new Validator({ +final Validator ${rc.camelCase} = new Validator({ 'name': [isString, isNotEmpty], - 'desc': [isString, isNotEmpty] + 'description': [isString, isNotEmpty] }); -final Validator CREATE_$constantCase = $constantCase.extend({}) - ..requiredFields.addAll(['name', 'desc']); - - ''' - .trim()); +final Validator create${rc.pascalCase} = ${rc.camelCase}.extend({}) + ..requiredFields.addAll(['name', 'description']); + ''')); } _generateRunConfiguration(String name, String lower) { @@ -208,8 +209,8 @@ final Validator CREATE_$constantCase = $constantCase.extend({}) .trim(); } - _generateTests(PubSpec pubspec, String lower) { - return ''' + _generateTests(PubSpec pubspec, String lower, DartFormatter fmt) { + return fmt.format(''' import 'dart:io'; import 'package:${pubspec.name}/${pubspec.name}.dart'; import 'package:angel_common/angel_common.dart'; @@ -241,7 +242,6 @@ main() async { }); } - ''' - .trim(); + '''); } } diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index f1fac21a..eae1456c 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -80,7 +80,7 @@ class StartCommand extends Command { if (await pubspec.exists()) { // Run start scripts final doc = loadYamlDocument(await pubspec.readAsString()); - final scriptsNode = doc.contents['scripts']; + final scriptsNode = doc.contents.value['scripts']; if (scriptsNode != null && scriptsNode.containsKey('start')) { try { diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index fdb5dee7..b74da8fd 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; +import 'package:dart_style/dart_style.dart'; import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; @@ -23,8 +24,8 @@ class TestCommand extends Command { if (!await testFile.exists()) await testFile.create(recursive: true); - await testFile.writeAsString( - _generateTest(await PubSpec.load(Directory.current), lower)); + await testFile.writeAsString(new DartFormatter() + .format(_generateTest(await PubSpec.load(Directory.current), lower))); final runConfig = new File('./.idea/runConfigurations/${name}_tests.xml'); diff --git a/pubspec.yaml b/pubspec.yaml index ab99f0a4..7b443518 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,12 +2,13 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.1.4 +version: 1.1.5 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 code_builder: ^1.0.0 console: "^2.2.3" + dart_style: ^1.0.0 glob: "^1.1.0" http: ^0.11.3 id: "^1.0.0" @@ -18,6 +19,7 @@ dependencies: watcher: "^0.9.7" yaml: "^2.0.0" dev_dependencies: + build: ^0.7.0 build_runner: ^0.3.0 check_for_update: ^1.0.0 environment: From 7d2696f1f6d98fc527a817ee1a7b781aef99b9b4 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Mon, 7 Aug 2017 21:45:37 -0400 Subject: [PATCH 057/132] v1.1.5 --- .idea/vcs.xml | 6 - CHANGELOG.md | 14 ++ bin/angel.dart | 1 + lib/src/commands/commands.dart | 1 + lib/src/commands/controller.dart | 3 + lib/src/commands/deprecated.dart | 10 ++ lib/src/commands/init.dart | 5 +- lib/src/commands/make.dart | 22 +++ lib/src/commands/make/controller.dart | 105 ++++++++++++ lib/src/commands/make/maker.dart | 58 +++++++ lib/src/commands/make/model.dart | 161 ++++++++++++++++++ lib/src/commands/make/plugin.dart | 74 ++++++++ lib/src/commands/make/service.dart | 126 ++++++++++++++ lib/src/commands/make/test.dart | 115 +++++++++++++ lib/src/commands/plugin.dart | 3 + lib/src/commands/rename.dart | 16 +- lib/src/commands/service.dart | 15 +- .../service_generators/file_service.dart | 7 +- .../service_generators/generator.dart | 14 ++ .../commands/service_generators/mongo.dart | 5 + .../commands/service_generators/rethink.dart | 5 + .../service_generators.dart | 19 ++- lib/src/commands/start.dart | 3 + lib/src/commands/test.dart | 3 + 24 files changed, 760 insertions(+), 31 deletions(-) delete mode 100644 .idea/vcs.xml create mode 100644 CHANGELOG.md create mode 100644 lib/src/commands/deprecated.dart create mode 100644 lib/src/commands/make.dart create mode 100644 lib/src/commands/make/controller.dart create mode 100644 lib/src/commands/make/maker.dart create mode 100644 lib/src/commands/make/model.dart create mode 100644 lib/src/commands/make/plugin.dart create mode 100644 lib/src/commands/make/service.dart create mode 100644 lib/src/commands/make/test.dart diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ac03088e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# 1.1.5 +Deprecated several commands, in favor of the `make` +command: +* `controller` +* `plugin` +* `service` +* `test` + +The `rename` command will now replace *all* occurrences +of the old project names with the new one in `config/` +YAML files, and also operates on the glob `config/**/*.yaml`. + +Changed the call to run `angel start` to run `dart bin/server.dart` instead, after an +`init` command. \ No newline at end of file diff --git a/bin/angel.dart b/bin/angel.dart index 97f470fb..90aa8cd0 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -22,6 +22,7 @@ main(List args) async { ..addCommand(new StartCommand()) ..addCommand(new RenameCommand()) ..addCommand(new UpdateCommand()) + ..addCommand(new MakeCommand()) ..addCommand(new VersionCommand()); return await runner.run(args).then((_) {}).catchError((exc) { diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 4264689c..d5360558 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -4,6 +4,7 @@ export 'controller.dart'; export "doctor.dart"; export "key.dart"; export "init.dart"; +export "make.dart"; export "plugin.dart"; export "rename.dart"; export "service.dart"; diff --git a/lib/src/commands/controller.dart b/lib/src/commands/controller.dart index fbf5a06d..ce9458e6 100644 --- a/lib/src/commands/controller.dart +++ b/lib/src/commands/controller.dart @@ -4,6 +4,7 @@ import 'package:code_builder/code_builder.dart'; import "package:console/console.dart"; import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; +import 'deprecated.dart'; class ControllerCommand extends Command { final TextPen _pen = new TextPen(); @@ -17,6 +18,8 @@ class ControllerCommand extends Command { @override run() async { + warnDeprecated(this.name, _pen); + final name = await readInput("Name of Controller: "), recase = new ReCase(name), lower = recase.snakeCase; diff --git a/lib/src/commands/deprecated.dart b/lib/src/commands/deprecated.dart new file mode 100644 index 00000000..992da105 --- /dev/null +++ b/lib/src/commands/deprecated.dart @@ -0,0 +1,10 @@ +import 'package:console/console.dart'; + +void warnDeprecated(String command, [TextPen pen]) { + pen ??= new TextPen(); + pen + ..yellow() + ..call('The `$command` command is deprecated, and will be removed by v1.2.0.') + ..call() + ..reset(); +} diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 57e1a8e3..c854f249 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -60,7 +60,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('`angel start`') + ..text('`dart bin/server.dart`') ..normal() ..text(' in your terminal.') ..text('\n\nFind more documentation about Angel:') @@ -192,7 +192,8 @@ const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( const List allBoilerplates = const [ fullApplicationBoilerplate, - lightBoilerplate + lightBoilerplate, + ormBoilerplate ]; class BoilerplateInfo { diff --git a/lib/src/commands/make.dart b/lib/src/commands/make.dart new file mode 100644 index 00000000..0a71c5be --- /dev/null +++ b/lib/src/commands/make.dart @@ -0,0 +1,22 @@ +import 'package:args/command_runner.dart'; +import 'make/controller.dart'; +import 'make/model.dart'; +import 'make/plugin.dart'; +import 'make/service.dart'; +import 'make/test.dart'; + +class MakeCommand extends Command { + @override + String get name => 'make'; + + @override + String get description => 'Generates common code for your project, such as projects and controllers.'; + + MakeCommand() { + addSubcommand(new ControllerCommand()); + addSubcommand(new ModelCommand()); + addSubcommand(new PluginCommand()); + addSubcommand(new TestCommand()); + addSubcommand(new ServiceCommand()); + } +} \ No newline at end of file diff --git a/lib/src/commands/make/controller.dart b/lib/src/commands/make/controller.dart new file mode 100644 index 00000000..ae36bd80 --- /dev/null +++ b/lib/src/commands/make/controller.dart @@ -0,0 +1,105 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:code_builder/dart/core.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:console/console.dart'; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; +import 'maker.dart'; + +class ControllerCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => 'controller'; + + @override + String get description => 'Generates a controller class.'; + + ControllerCommand() { + argParser + ..addFlag('websocket', + abbr: 'w', + help: + 'Generates a WebSocketController, instead of an HTTP controller.', + negatable: false) + ..addOption('name', + abbr: 'n', help: 'Specifies a name for the model class.') + ..addOption('output-dir', + help: 'Specifies a directory to create the controller class in.', + defaultsTo: 'lib/src/controllers'); + } + + @override + run() async { + var pubspec = await PubSpec.load(Directory.current); + String name; + if (argResults.wasParsed('name')) name = argResults['name']; + + if (name?.isNotEmpty != true) { + var p = new Prompter('Name of Controller class: '); + name = await p.prompt(checker: (s) => s.isNotEmpty); + } + + List deps = [ + const MakerDependency('angel_framework', '^1.0.0') + ]; + + var rc = new ReCase(name); + var controllerLib = + new LibraryBuilder('${pubspec.name}.src.controllers.${rc.snakeCase}'); + + if (argResults['websocket']) { + deps.add(const MakerDependency('angel_websocket', '^1.0.0')); + controllerLib.addDirective( + new ImportBuilder('package:angel_websocket/server.dart')); + } else + controllerLib.addDirective( + new ImportBuilder('package:angel_framework/angel_framework.dart')); + + TypeBuilder parentType = new TypeBuilder( + argResults['websocket'] ? 'WebSocketController' : 'Controller'); + ClassBuilder clazz = + new ClassBuilder('${rc.pascalCase}Controller', asExtends: parentType); + controllerLib.addMember(clazz); + + if (argResults['websocket']) { + var meth = new MethodBuilder('hello', returnType: lib$core.$void); + meth.addAnnotation(new TypeBuilder('ExposeWs') + .constInstance([literal('get_${rc.snakeCase}')])); + meth.addPositional( + parameter('socket', [new TypeBuilder('WebSocketContext')])); + meth.addStatement(reference('socket').invoke('send', [ + literal('got_${rc.snakeCase}'), + map({'message': literal('Hello, world!')}) + ])); + clazz.addMethod(meth); + } else { + clazz.addAnnotation(new TypeBuilder('Expose') + .constInstance([literal('/${rc.snakeCase}')])); + + var meth = new MethodBuilder('hello', + returnType: lib$core.String, returns: literal('Hello, world!')); + meth.addAnnotation( + new TypeBuilder('Expose').constInstance([literal('/')])); + clazz.addMethod(meth); + } + + var outputDir = new Directory.fromUri( + Directory.current.uri.resolve(argResults['output-dir'])); + var controllerFile = + new File.fromUri(outputDir.uri.resolve('${rc.snakeCase}.dart')); + if (!await controllerFile.exists()) + await controllerFile.create(recursive: true); + await controllerFile + .writeAsString(prettyToSource(controllerLib.buildAst())); + _pen + ..green() + ..call( + '${Icon.CHECKMARK} Created controller file "${controllerFile.absolute.path}".') + ..call() + ..reset(); + + if (deps.isNotEmpty) await depend(deps); + } +} diff --git a/lib/src/commands/make/maker.dart b/lib/src/commands/make/maker.dart new file mode 100644 index 00000000..bbee58b4 --- /dev/null +++ b/lib/src/commands/make/maker.dart @@ -0,0 +1,58 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:pubspec/pubspec.dart'; +import 'package:pub_semver/pub_semver.dart'; +import '../pub.dart'; + +class MakerDependency { + final String name, version; + final bool dev; + const MakerDependency(this.name, this.version, {this.dev: false}); +} + +Future depend(Iterable deps) async { + var pubspec = await PubSpec.load(Directory.current); + Map newDeps = {}, newDevDeps = {}; + + for (var dep in deps) { + var isPresent = false; + if (dep.dev) + isPresent = pubspec.devDependencies.containsKey(dep.name); + else + isPresent = pubspec.dependencies.containsKey(dep.name); + + if (!isPresent) { + print('Installing ${dep.name}@${dep.version}...'); + + if (dep.dev) + newDevDeps[dep.name] = + new HostedReference(new VersionConstraint.parse(dep.version)); + else + newDeps[dep.name] = + new HostedReference(new VersionConstraint.parse(dep.version)); + } + + if (newDeps.isNotEmpty || newDevDeps.isNotEmpty) { + var newPubspec = pubspec.copy( + dependencies: + new Map.from(pubspec.dependencies) + ..addAll(newDeps), + devDependencies: + new Map.from(pubspec.devDependencies) + ..addAll(newDevDeps)); + + await newPubspec.save(Directory.current); + var pubPath = resolvePub(); + + print('Now running `$pubPath get`...'); + + var pubGet = await Process.start(pubPath, ['get']); + pubGet.stdout.listen(stdout.add); + pubGet.stderr.listen(stderr.add); + + var code = await pubGet.exitCode; + + if (code != 0) throw 'pub get terminated with exit code $code'; + } + } +} diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart new file mode 100644 index 00000000..208678af --- /dev/null +++ b/lib/src/commands/make/model.dart @@ -0,0 +1,161 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:code_builder/dart/core.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:console/console.dart'; +import 'package:inflection/inflection.dart'; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; +import 'maker.dart'; + +class ModelCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => 'model'; + + @override + String get description => 'Generates a model class.'; + + ModelCommand() { + argParser + ..addFlag('migration', + abbr: 'm', + help: 'Generate an angel_orm migration file.', + negatable: false) + ..addFlag('orm', help: 'Generate angel_orm code.', negatable: false) + ..addFlag('serializable', + help: 'Generate angel_serialize annotations.', defaultsTo: true) + ..addOption('name', + abbr: 'n', help: 'Specifies a name for the model class.') + ..addOption('output-dir', + help: 'Specifies a directory to create the model class in.', + defaultsTo: 'lib/src/models') + ..addOption('migration-dir', + help: 'Specifies a directory to create the migration class in.', + defaultsTo: 'tool/migrations'); + } + + @override + run() async { + var pubspec = await PubSpec.load(Directory.current); + String name; + if (argResults.wasParsed('name')) name = argResults['name']; + + if (name?.isNotEmpty != true) { + var p = new Prompter('Name of Model class: '); + name = await p.prompt(checker: (s) => s.isNotEmpty); + } + + List deps = [ + const MakerDependency('angel_framework', '^1.0.0'), + const MakerDependency('angel_model', '^1.0.0'), + ]; + + var rc = new ReCase(name); + var modelLib = + new LibraryBuilder('${pubspec.name}.src.models.${rc.snakeCase}'); + modelLib.addDirective( + new ImportBuilder('package:angel_model/angel_model.dart')); + + var needsSerialize = argResults['serializable'] || argResults['orm']; + + if (needsSerialize) { + modelLib.addDirective( + new ImportBuilder('package:angel_serialize/angel_serialize.dart')); + deps.add(const MakerDependency('angel_serialize', '^1.0.0-alpha')); + } + + if (argResults['orm']) { + modelLib + .addDirective(new ImportBuilder('package:angel_orm/angel_orm.dart')); + deps.add(const MakerDependency('angel_orm', '^1.0.0-alpha')); + } + + var modelClazz = new ClassBuilder( + needsSerialize ? '_${rc.pascalCase}' : rc.pascalCase, + asExtends: new TypeBuilder('Model')); + modelLib.addMember(modelClazz); + + if (needsSerialize) { + modelLib.addDirective(new PartBuilder('${rc.snakeCase}.g.dart')); + modelClazz.addAnnotation(reference('serializable')); + } + + if (argResults['orm']) { + modelClazz.addAnnotation(reference('orm')); + } + + // Save model file + var outputDir = new Directory.fromUri( + Directory.current.uri.resolve(argResults['output-dir'])); + var modelFile = + new File.fromUri(outputDir.uri.resolve('${rc.snakeCase}.dart')); + if (!await modelFile.exists()) await modelFile.create(recursive: true); + await modelFile.writeAsString(prettyToSource(modelLib.buildAst())); + _pen + ..green() + ..call( + '${Icon.CHECKMARK} Created model file "${modelFile.absolute.path}".') + ..call() + ..reset(); + + if (argResults['migration']) { + deps.add( + const MakerDependency('angel_migration', '^1.0.0-alpha', dev: true)); + + var migrationLib = new LibraryBuilder() + ..addDirective( + new ImportBuilder('package:angel_migration/angel_migration.dart')); + var migrationClazz = new ClassBuilder('${rc.pascalCase}Migration', + asExtends: new TypeBuilder('Migration')); + migrationLib.addMember(migrationClazz); + var tableName = pluralize(rc.snakeCase); + + // up() + var up = new MethodBuilder('up', returnType: lib$core.$void); + migrationClazz.addMethod(up); + up.addAnnotation(lib$core.override); + up.addPositional(parameter('schema', [new TypeBuilder('Schema')])); + + // (table) { ... } + var callback = new MethodBuilder.closure(); + callback.addPositional(parameter('table')); + + var cascade = reference('table').cascade((table) => [ + table.invoke('serial', [literal('id')]).invoke('primaryKey', []), + table.invoke('date', [literal('created_at')]), + table.invoke('date', [literal('updated_at')]) + ]); + callback.addStatement(cascade); + + up.addStatement(reference('schema').invoke('create', [callback])); + + // down() + var down = new MethodBuilder('down', returnType: lib$core.$void); + migrationClazz.addMethod(down); + down.addAnnotation(lib$core.override); + down.addPositional(parameter('schema', [new TypeBuilder('Schema')])); + down.addStatement( + reference('schema').invoke('drop', [literal(tableName)])); + + // Save migration file + var migrationDir = new Directory.fromUri( + Directory.current.uri.resolve(argResults['migration-dir'])); + var migrationFile = + new File.fromUri(migrationDir.uri.resolve('${rc.snakeCase}.dart')); + if (!await migrationFile.exists()) + await migrationFile.create(recursive: true); + await migrationFile + .writeAsString(prettyToSource(migrationLib.buildAst())); + _pen + ..green() + ..call( + '${Icon.CHECKMARK} Created migration file "${migrationFile.absolute.path}".') + ..call() + ..reset(); + } + + if (deps.isNotEmpty) await depend(deps); + } +} diff --git a/lib/src/commands/make/plugin.dart b/lib/src/commands/make/plugin.dart new file mode 100644 index 00000000..b6c1183f --- /dev/null +++ b/lib/src/commands/make/plugin.dart @@ -0,0 +1,74 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import "package:console/console.dart"; +import 'package:dart_style/dart_style.dart'; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; +import 'maker.dart'; + +class PluginCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => "plugin"; + + @override + String get description => "Creates a new plug-in within the given project."; + + PluginCommand() { + argParser + ..addOption('name', + abbr: 'n', help: 'Specifies a name for the plug-in class.') + ..addOption('output-dir', + help: 'Specifies a directory to create the plug-in class in.', + defaultsTo: 'lib/src/config/plugins'); + } + + @override + run() async { + var pubspec = await PubSpec.load(Directory.current); + String name; + if (argResults.wasParsed('name')) name = argResults['name']; + + if (name?.isNotEmpty != true) { + var p = new Prompter('Name of Controller class: '); + name = await p.prompt(checker: (s) => s.isNotEmpty); + } + + List deps = [ + const MakerDependency('angel_framework', '^1.0.0') + ]; + + var rc = new ReCase(name); + final pluginDir = new Directory.fromUri( + Directory.current.uri.resolve(argResults['output-dir'])); + final pluginFile = + new File.fromUri(pluginDir.uri.resolve("${rc.snakeCase}.dart")); + if (!await pluginFile.exists()) await pluginFile.create(recursive: true); + await pluginFile.writeAsString( + new DartFormatter().format(_generatePlugin(pubspec, rc))); + + if (deps.isNotEmpty) await depend(deps); + + _pen.green(); + _pen( + '${Icon.CHECKMARK} Successfully generated plug-in file "${pluginFile.absolute.path}".'); + _pen(); + } + + String _generatePlugin(PubSpec pubspec, ReCase rc) { + return ''' +library ${pubspec.name}.src.config.plugins.${rc.snakeCase}; + +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; + +class ${rc.pascalCase}Plugin extends AngelPlugin { + @override + Future call(Angel app) async { + // Work some magic... + } +} + '''; + } +} diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart new file mode 100644 index 00000000..62707fd4 --- /dev/null +++ b/lib/src/commands/make/service.dart @@ -0,0 +1,126 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:console/console.dart'; +import 'package:inflection/inflection.dart'; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; +import '../service_generators/service_generators.dart'; +import 'maker.dart'; + +class ServiceCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => 'service'; + + @override + String get description => 'Generates an Angel service.'; + + ServiceCommand() { + argParser + ..addFlag('typed', + abbr: 't', + help: 'Wrap the generated service in a `TypedService` instance.', + negatable: false) + ..addOption('name', + abbr: 'n', help: 'Specifies a name for the service file.') + ..addOption('output-dir', + help: 'Specifies a directory to create the service file.', + defaultsTo: 'lib/src/services'); + } + + @override + run() async { + var pubspec = await PubSpec.load(Directory.current); + String name; + if (argResults.wasParsed('name')) name = argResults['name']; + + if (name?.isNotEmpty != true) { + var p = new Prompter('Name of Service: '); + name = await p.prompt(checker: (s) => s.isNotEmpty); + } + + List deps = [ + const MakerDependency('angel_framework', '^1.0.0') + ]; + + var rc = new ReCase(name); + var serviceLib = + new LibraryBuilder('${pubspec.name}.src.services.${rc.snakeCase}'); + + ServiceGenerator generator; + + var chooser = new Chooser( + serviceGenerators.map((g) => g.name).toList(), + message: 'What type of service would you like to create? '); + var type = await chooser.choose(); + + generator = + serviceGenerators.firstWhere((g) => g.name == type, orElse: () => null); + + if (generator == null) { + _pen.red(); + _pen('${Icon.BALLOT_X} \'$type\' services are not yet implemented. :('); + _pen(); + throw 'Unrecognized service type: "$type".'; + } + + for (var dep in generator.dependencies) { + if (!deps.any((d) => d.name == dep.name)) deps.add(dep); + } + + if (generator.goesFirst) { + generator.applyToLibrary(serviceLib, name, rc.snakeCase); + serviceLib.addMember( + new ImportBuilder('package:angel_framework/angel_framework.dart')); + } else { + serviceLib.addMember( + new ImportBuilder('package:angel_framework/angel_framework.dart')); + generator.applyToLibrary(serviceLib, name, rc.snakeCase); + } + + if (argResults['typed']) { + serviceLib + ..addMember(new ImportBuilder('../models/${rc.snakeCase}.dart')); + } + + // configureServer() {} + var configureServer = new MethodBuilder('configureServer', + returnType: new TypeBuilder('AngelConfigurer')); + generator.applyToConfigureServer(configureServer, name, rc.snakeCase); + + // return (Angel app) async {} + var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync) + ..addPositional(parameter('app', [new TypeBuilder('Angel')])); + generator.beforeService(closure, name, rc.snakeCase); + + // app.use('/api/todos', new MapService()); + var service = generator.createInstance(closure, name, rc.snakeCase); + + if (argResults['typed']) { + service = new TypeBuilder('TypedService', + genericTypes: [new TypeBuilder(rc.pascalCase)]) + .newInstance([service]); + } + + closure.addStatement(reference('app') + .invoke('use', [literal('/api/${pluralize(rc.snakeCase)}'), service])); + configureServer.addStatement(closure.asReturn()); + serviceLib.addMember(configureServer); + + final outputDir = new Directory.fromUri( + Directory.current.uri.resolve(argResults['output-dir'])); + final serviceFile = + new File.fromUri(outputDir.uri.resolve("${rc.snakeCase}.dart")); + if (!await serviceFile.exists()) await serviceFile.create(recursive: true); + await serviceFile.writeAsString(prettyToSource(serviceLib.buildAst())); + + _pen.green(); + _pen( + '${Icon.CHECKMARK} Successfully generated service file "${serviceFile.absolute.path}".'); + _pen(); + + if (deps.isNotEmpty) await depend(deps); + } +} diff --git a/lib/src/commands/make/test.dart b/lib/src/commands/make/test.dart new file mode 100644 index 00000000..417e6eb5 --- /dev/null +++ b/lib/src/commands/make/test.dart @@ -0,0 +1,115 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import "package:console/console.dart"; +import 'package:dart_style/dart_style.dart'; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; +import 'maker.dart'; + +class TestCommand extends Command { + final TextPen _pen = new TextPen(); + + @override + String get name => "test"; + + @override + String get description => "Creates a new test within the given project."; + + TestCommand() { + argParser + ..addFlag('run-configuration', + help: 'Generate a run configuration for JetBrains IDE\'s.', + defaultsTo: true) + ..addOption('name', + abbr: 'n', help: 'Specifies a name for the plug-in class.') + ..addOption('output-dir', + help: 'Specifies a directory to create the plug-in class in.', + defaultsTo: 'test'); + } + + @override + run() async { + var pubspec = await PubSpec.load(Directory.current); + String name; + if (argResults.wasParsed('name')) name = argResults['name']; + + if (name?.isNotEmpty != true) { + var p = new Prompter('Name of Test: '); + name = await p.prompt(checker: (s) => s.isNotEmpty); + } + + List deps = [ + const MakerDependency('angel_framework', '^1.0.0'), + const MakerDependency('angel_test', '^1.0.0', dev: true), + const MakerDependency('test', '^0.12.0', dev: true), + ]; + + var rc = new ReCase(name); + final testDir = new Directory.fromUri( + Directory.current.uri.resolve(argResults['output-dir'])); + final testFile = + new File.fromUri(testDir.uri.resolve("${rc.snakeCase}_test.dart")); + if (!await testFile.exists()) await testFile.create(recursive: true); + await testFile + .writeAsString(new DartFormatter().format(_generateTest(pubspec, rc))); + + if (deps.isNotEmpty) await depend(deps); + + _pen.green(); + _pen( + '${Icon.CHECKMARK} Successfully generated test file "${testFile.absolute.path}".'); + _pen(); + + if (argResults['run-configuration']) { + final runConfig = new File.fromUri(Directory.current.uri + .resolve('.idea/runConfigurations/${name}_Tests.xml')); + + if (!await runConfig.exists()) await runConfig.create(recursive: true); + await runConfig.writeAsString(_generateRunConfiguration(name, rc)); + + _pen.reset(); + _pen.green(); + _pen( + '${Icon.CHECKMARK} Successfully generated run configuration "$name Tests" at "${runConfig.absolute.path}".'); + _pen(); + } + } + + _generateRunConfiguration(String name, ReCase rc) { + return ''' + + + + +''' + .trim(); + } + + String _generateTest(PubSpec pubspec, ReCase rc) { + return ''' +import 'dart:io'; +import 'package:${pubspec.name}/${pubspec.name}.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_test/angel_test.dart'; +import 'package:test/test.dart'; + +main() async { + TestClient client; + + setUp(() async { + var app = await createServer(); + client = await connectTo(app); + }); + + tearDown(() => client.close()); + + test('${rc.snakeCase}', () async { + final response = await client.get('/${rc.snakeCase}'); + expect(response, hasStatus(HttpStatus.OK)); + }); +} + '''; + } +} diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart index 2ded982f..49d5fc10 100644 --- a/lib/src/commands/plugin.dart +++ b/lib/src/commands/plugin.dart @@ -4,6 +4,7 @@ import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; +import 'deprecated.dart'; class PluginCommand extends Command { final TextPen _pen = new TextPen(); @@ -16,6 +17,8 @@ class PluginCommand extends Command { @override run() async { + warnDeprecated(this.name, _pen); + var pubspec = await PubSpec.load(Directory.current); final name = await readInput("Name of Plugin: "), lower = new ReCase(name).snakeCase; diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index 2c90d961..f64c4047 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -3,6 +3,7 @@ import 'package:analyzer/analyzer.dart'; import 'package:args/command_runner.dart'; import 'package:console/console.dart'; import 'package:dart_style/dart_style.dart'; +import 'package:glob/glob.dart'; import 'package:pubspec/pubspec.dart'; import 'pub.dart'; @@ -63,14 +64,15 @@ renamePubspec(Directory dir, String oldName, String newName) async { renameDartFiles(Directory dir, String oldName, String newName) async { // Try to replace MongoDB URL - var defaultYaml = new File.fromUri(dir.uri.resolve('config/default.yaml')); + var configGlob = new Glob('config/**/*.yaml'); - if (await defaultYaml.exists()) { - print('Changing MongoDB URL in file "${defaultYaml.absolute.path}"...'); - var contents = await defaultYaml.readAsString(); - contents = contents.replaceAll('mongodb://localhost:27017/$oldName', - 'mongodb://localhost:27017/$newName'); - await defaultYaml.writeAsString(contents); + await for (var yamlFile in configGlob.list(root: dir.absolute.path)) { + if (yamlFile is File) { + print('Replacing occurrences of "$oldName" with "$newName" in file "${yamlFile.absolute.path}"...'); + var contents = await yamlFile.readAsString(); + contents = contents.replaceAll(oldName, newName); + await yamlFile.writeAsString(contents); + } } var entry = new File.fromUri(dir.uri.resolve('lib/$oldName.dart')); diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 1db296e7..9fd28165 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -7,16 +7,9 @@ import 'package:inflection/inflection.dart'; import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; import 'service_generators/service_generators.dart'; +import 'deprecated.dart'; import 'init.dart' show preBuild; -const List GENERATORS = const [ - const MapServiceGenerator(), - const FileServiceGenerator(), - const MongoServiceGenerator(), - const RethinkServiceGenerator(), - const CustomServiceGenerator() -]; - class ServiceCommand extends Command { final TextPen _pen = new TextPen(); @@ -28,10 +21,12 @@ class ServiceCommand extends Command { @override run() async { + warnDeprecated(this.name, _pen); + var pubspec = await PubSpec.load(Directory.current); var name = await readInput('Name of Service (not plural): '); var chooser = new Chooser( - GENERATORS.map((g) => g.name).toList(), + serviceGenerators.map((g) => g.name).toList(), message: 'What type of service would you like to create? '); var type = await chooser.choose(); @@ -40,7 +35,7 @@ class ServiceCommand extends Command { var typed = (await chooser.choose()) == 'Yes'; var generator = - GENERATORS.firstWhere((g) => g.name == type, orElse: () => null); + serviceGenerators.firstWhere((g) => g.name == type, orElse: () => null); if (generator == null) { _pen.blue(); diff --git a/lib/src/commands/service_generators/file_service.dart b/lib/src/commands/service_generators/file_service.dart index 7778e2d9..d536cbc8 100644 --- a/lib/src/commands/service_generators/file_service.dart +++ b/lib/src/commands/service_generators/file_service.dart @@ -1,12 +1,17 @@ import 'generator.dart'; import 'package:code_builder/code_builder.dart'; import 'package:inflection/inflection.dart'; +import '../make/maker.dart'; class FileServiceGenerator extends ServiceGenerator { const FileServiceGenerator() : super('Persistent JSON File'); @override - bool get createsModel => false; + List get dependencies => + const [const MakerDependency('angel_file_service', '^1.0.0')]; + + @override + bool get goesFirst => true; @override void applyToLibrary(LibraryBuilder library, String name, String lower) { diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart index 79ccc4f8..970bb0fa 100644 --- a/lib/src/commands/service_generators/generator.dart +++ b/lib/src/commands/service_generators/generator.dart @@ -1,16 +1,30 @@ import 'package:code_builder/code_builder.dart'; +import '../make/maker.dart'; class ServiceGenerator { final String name; const ServiceGenerator(this.name); + List get dependencies => []; + + @deprecated bool get createsModel => true; + + @deprecated bool get createsValidator => true; + + @deprecated bool get exportedInServiceLibrary => true; + + @deprecated bool get injectsSingleton => false; + + @deprecated bool get shouldRunBuild => false; + bool get goesFirst => false; + void applyToLibrary(LibraryBuilder library, String name, String lower) {} void beforeService(MethodBuilder methodBuilder, String name, String lower) {} diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index 16e7c363..c81153ef 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -1,10 +1,15 @@ import 'generator.dart'; import 'package:code_builder/code_builder.dart'; import 'package:inflection/inflection.dart'; +import '../make/maker.dart'; class MongoServiceGenerator extends ServiceGenerator { const MongoServiceGenerator() : super('MongoDB'); + @override + List get dependencies => + const [const MakerDependency('angel_mongo', '^1.0.0')]; + @override bool get createsModel => false; diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 22d0bd64..17c55b44 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -1,10 +1,15 @@ import 'generator.dart'; import 'package:code_builder/code_builder.dart'; import 'package:inflection/inflection.dart'; +import '../make/maker.dart'; class RethinkServiceGenerator extends ServiceGenerator { const RethinkServiceGenerator() : super('RethinkDB'); + @override + List get dependencies => + const [const MakerDependency('angel_rethink', '^1.0.0')]; + @override bool get createsModel => false; diff --git a/lib/src/commands/service_generators/service_generators.dart b/lib/src/commands/service_generators/service_generators.dart index 658cc16b..0dfd685e 100644 --- a/lib/src/commands/service_generators/service_generators.dart +++ b/lib/src/commands/service_generators/service_generators.dart @@ -1,6 +1,15 @@ -export 'custom.dart'; -export 'file_service.dart'; +import 'custom.dart'; +import 'file_service.dart'; +import 'generator.dart'; +import 'map.dart'; +import 'mongo.dart'; +import 'rethink.dart'; export 'generator.dart'; -export 'map.dart'; -export 'mongo.dart'; -export 'rethink.dart'; \ No newline at end of file + +const List serviceGenerators = const [ + const MapServiceGenerator(), + const FileServiceGenerator(), + const MongoServiceGenerator(), + const RethinkServiceGenerator(), + const CustomServiceGenerator() +]; \ No newline at end of file diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart index eae1456c..696ebf09 100644 --- a/lib/src/commands/start.dart +++ b/lib/src/commands/start.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:watcher/watcher.dart'; import 'package:yaml/yaml.dart'; +import 'deprecated.dart'; import 'pub.dart'; Process server; @@ -34,6 +35,8 @@ class StartCommand extends Command { @override run() async { + warnDeprecated(this.name); + stderr ..writeln( 'WARNING: `angel start` is now deprecated, in favor of `package:angel_hot`.') diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index b74da8fd..b519b62e 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -4,6 +4,7 @@ import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; import 'package:pubspec/pubspec.dart'; import 'package:recase/recase.dart'; +import 'deprecated.dart'; class TestCommand extends Command { final TextPen _pen = new TextPen(); @@ -16,6 +17,8 @@ class TestCommand extends Command { @override run() async { + warnDeprecated(this.name, _pen); + final name = await readInput("Name of Test: "), lower = new ReCase(name).snakeCase; final testDir = new Directory("test/services"); From ade8725c1e0a502d9c23ea80a7a9f83ce1f7cfef Mon Sep 17 00:00:00 2001 From: thosakwe Date: Mon, 7 Aug 2017 22:06:28 -0400 Subject: [PATCH 058/132] migration dep --- lib/src/commands/make/model.dart | 2 +- lib/src/commands/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 208678af..3d4af783 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -102,7 +102,7 @@ class ModelCommand extends Command { if (argResults['migration']) { deps.add( - const MakerDependency('angel_migration', '^1.0.0-alpha', dev: true)); + const MakerDependency('angel_migration', '^1.0.0-alpha')); var migrationLib = new LibraryBuilder() ..addDirective( diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 7e5762c3..3ef37657 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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); +final Version PACKAGE_VERSION = new Version(1, 1, 5, build: '1'); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index 7b443518..15eab6e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.1.5 +version: 1.1.5+1 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From 7c27a329e56b7f9b7f8a9f1cf7808dc157380664 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Mon, 7 Aug 2017 22:08:52 -0400 Subject: [PATCH 059/132] fix migration --- lib/src/commands/make/model.dart | 18 +++++++++--------- lib/src/commands/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 3d4af783..9b544420 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -101,8 +101,7 @@ class ModelCommand extends Command { ..reset(); if (argResults['migration']) { - deps.add( - const MakerDependency('angel_migration', '^1.0.0-alpha')); + deps.add(const MakerDependency('angel_migration', '^1.0.0-alpha')); var migrationLib = new LibraryBuilder() ..addDirective( @@ -129,7 +128,8 @@ class ModelCommand extends Command { ]); callback.addStatement(cascade); - up.addStatement(reference('schema').invoke('create', [callback])); + up.addStatement( + reference('schema').invoke('create', [literal(tableName), callback])); // down() var down = new MethodBuilder('down', returnType: lib$core.$void); @@ -148,12 +148,12 @@ class ModelCommand extends Command { await migrationFile.create(recursive: true); await migrationFile .writeAsString(prettyToSource(migrationLib.buildAst())); - _pen - ..green() - ..call( - '${Icon.CHECKMARK} Created migration file "${migrationFile.absolute.path}".') - ..call() - ..reset(); + _pen + ..green() + ..call( + '${Icon.CHECKMARK} Created migration file "${migrationFile.absolute.path}".') + ..call() + ..reset(); } if (deps.isNotEmpty) await depend(deps); diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 3ef37657..7cf40e16 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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: '1'); +final Version PACKAGE_VERSION = new Version(1, 1, 5, build: '2'); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index 15eab6e8..8c31aa70 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.1.5+1 +version: 1.1.5+2 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From bff1f6aed1b6ffebf10137bb19f48b37cf40b1fa Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 19 Oct 2017 21:06:15 -0400 Subject: [PATCH 060/132] 1.2.0 --- .idea/angel_cli.iml | 10 - bin/angel.dart | 1 + lib/src/commands/commands.dart | 1 + lib/src/commands/init.dart | 67 +++-- lib/src/commands/install.dart | 245 ++++++++++++++++++ lib/src/commands/pubspec.update.g.dart | 2 +- .../commands/service_generators/mongo.dart | 5 +- pubspec.yaml | 4 +- 8 files changed, 306 insertions(+), 29 deletions(-) create mode 100644 lib/src/commands/install.dart diff --git a/.idea/angel_cli.iml b/.idea/angel_cli.iml index 0f3fa76d..78848ff9 100644 --- a/.idea/angel_cli.iml +++ b/.idea/angel_cli.iml @@ -4,21 +4,11 @@ - - - - - - - - - - diff --git a/bin/angel.dart b/bin/angel.dart index 90aa8cd0..ccb0b146 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -17,6 +17,7 @@ main(List args) async { ..addCommand(new KeyCommand()) ..addCommand(new ServiceCommand()) ..addCommand(new InitCommand()) + ..addCommand(new InstallCommand()) ..addCommand(new TestCommand()) ..addCommand(new PluginCommand()) ..addCommand(new StartCommand()) diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index d5360558..28534228 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -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"; diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index c854f249..cb5480a9 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -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(allBoilerplates); + var boilerplateChooser = new Chooser( + 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 allBoilerplates = const [ +const List 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 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)'; diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart new file mode 100644 index 00000000..3f2721cd --- /dev/null +++ b/lib/src/commands/install.dart @@ -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 values = { + 'project_name': pubspec.name, + 'pubspec': pubspec, + }; + + List 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; + + // 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; + + 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() async { + if (!await installRepo.exists()) { + throw 'No local add-on database exists. Run `angel install --update` first.'; + } else { + List 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.'; + } + } +} diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 7cf40e16..89067c01 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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 fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index c81153ef..eb956d42 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -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 diff --git a/pubspec.yaml b/pubspec.yaml index 8c31aa70..3da27681 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " 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" From 2854b28ec2b56f574ef9871f2e57bf3b21b55cf6 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 19 Oct 2017 21:08:40 -0400 Subject: [PATCH 061/132] +1 --- lib/src/commands/install.dart | 21 +++++++++++++++++---- lib/src/commands/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index 3f2721cd..b2097c6b 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -121,12 +121,25 @@ class InstallCommand extends Command { } } - var deps = projectPubspec.dependencies.keys.map((k) { - var dep = projectPubspec.dependencies[k]; + 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) + .toList(); + + deps.addAll(projectPubspec.devDependencies.keys.map((k) { + var dep = projectPubspec.devDependencies[k]; if (dep is HostedReference) - return new MakerDependency(k, dep.versionConstraint.toString()); + return new MakerDependency(k, dep.versionConstraint.toString(), + dev: true); return null; - }).where((d) => d != null); + }).where((d) => d != null)); + await depend(deps); } diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 89067c01..a4c53ba2 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 2, 0); +final Version PACKAGE_VERSION = new Version(1, 2, 0, build: '1'); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index 3da27681..50231b9b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.2.0 +version: 1.2.0+1 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From 3f4728bb37cee589d2679d7488ae87bc364f5965 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 20 Oct 2017 02:29:23 -0400 Subject: [PATCH 062/132] +3 --- lib/src/commands/install.dart | 45 +++++++++++++------------- lib/src/commands/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index b2097c6b..689e2393 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -69,6 +69,7 @@ class InstallCommand extends Command { if (!await packageDir.exists()) throw 'No add-on named "$packageName" is installed. You might need to run `angel install --update`.'; + print('Installing $packageName...'); Map values = { 'project_name': pubspec.name, @@ -77,9 +78,30 @@ class InstallCommand extends Command { List globs = []; + var projectPubspec = await PubSpec.load(packageDir); + 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) + .toList(); + + deps.addAll(projectPubspec.devDependencies.keys.map((k) { + var dep = projectPubspec.devDependencies[k]; + if (dep is HostedReference) + return new MakerDependency(k, dep.versionConstraint.toString(), + dev: true); + return null; + }).where((d) => d != null)); + + await depend(deps); + 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(); @@ -120,27 +142,6 @@ class InstallCommand extends Command { } } } - - 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) - .toList(); - - deps.addAll(projectPubspec.devDependencies.keys.map((k) { - var dep = projectPubspec.devDependencies[k]; - if (dep is HostedReference) - return new MakerDependency(k, dep.versionConstraint.toString(), - dev: true); - return null; - }).where((d) => d != null)); - - await depend(deps); } Future merge(Directory src, Directory dst, String prefix) async { diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index a4c53ba2..b4406c45 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 2, 0, build: '1'); +final Version PACKAGE_VERSION = new Version(1, 2, 0, build: '3'); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index 50231b9b..9cebff70 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.2.0+1 +version: 1.2.0+3 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From 3450aa31683e23d332febf4b2675b61fe057e1c7 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 20 Oct 2017 02:31:36 -0400 Subject: [PATCH 063/132] +4 --- lib/src/commands/pubspec.update.g.dart | 2 +- lib/src/commands/update.dart | 4 +++- pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index b4406c45..52eefc08 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 2, 0, build: '3'); +final Version PACKAGE_VERSION = new Version(1, 2, 0, build: '4'); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/lib/src/commands/update.dart b/lib/src/commands/update.dart index 5eadffc4..d0e5c21c 100644 --- a/lib/src/commands/update.dart +++ b/lib/src/commands/update.dart @@ -59,8 +59,10 @@ class UpdateCommand extends Command { } } else stdout.writeln('No update available.'); - } catch (e) { + } catch (e, st) { stdout.writeln('Failed to check for update.'); + stderr.writeln(e); + stderr.writeln(st); } } } diff --git a/pubspec.yaml b/pubspec.yaml index 9cebff70..f06dd758 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.2.0+3 +version: 1.2.0+4 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From 77c3f0806378e3f1b5498bc3bffeaf19ebd98231 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 20 Oct 2017 10:32:10 -0400 Subject: [PATCH 064/132] wipe --- .DS_Store | Bin 6148 -> 6148 bytes lib/src/commands/install.dart | 2 +- lib/src/commands/pubspec.update.g.dart | 2 +- pubspec.yaml | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.DS_Store b/.DS_Store index c2e90e8119a896a5bda0104623306eb0a7172b7d..5d196f037d56ca0b78267e0adb31afd97123cb69 100644 GIT binary patch delta 31 ncmZoMXfc@J&&a+pU^gQp`(z%bMVp^7DY8y%u-MGb@s}R}pr#5i delta 56 zcmZoMXfc@J&&aVcU^gQp$7CL+MbbG8`3%Vn*`7K1$w@i+Nem1O0zm8r#4|QeWU^!3 J%+B$b9{{mN4v7E& diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index 689e2393..b75d8fa4 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -47,7 +47,7 @@ class InstallCommand extends Command { @override run() async { if (argResults['wipe']) { - if (await installRepo.exists()) await installRepo.delete(); + if (await installRepo.exists()) await installRepo.delete(recursive: true); } else if (argResults['list']) { var addons = await list(); print('${addons.length} add-on(s) installed:'); diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart index 52eefc08..07f80537 100644 --- a/lib/src/commands/pubspec.update.g.dart +++ b/lib/src/commands/pubspec.update.g.dart @@ -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, 2, 0, build: '4'); +final Version PACKAGE_VERSION = new Version(1, 2, 0, build: '5'); Future fetchCurrentVersion(http.BaseClient client) async { var response = await client.get('https://pub.dartlang.org/api/packages/angel_cli'); diff --git a/pubspec.yaml b/pubspec.yaml index f06dd758..ca7b9426 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: 1.2.0+4 +version: 1.2.0+5 dependencies: # analyzer: "^0.29.0" args: ^0.13.4 From 0a59aa77dfe4c09755a7dcf59cc2391d3f1ba5b8 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 17:47:49 -0400 Subject: [PATCH 065/132] New generators for Controller + Model --- .idea/angel_cli.iml | 1 + .idea/runConfigurations/build_dart.xml | 7 - analysis_options.yaml | 3 +- lib/src/commands/controller.dart | 6 +- lib/src/commands/install.dart | 12 +- lib/src/commands/make/controller.dart | 117 +++++++------- lib/src/commands/make/maker.dart | 96 ++++++++---- lib/src/commands/make/model.dart | 205 +++++++++++++++---------- lib/src/commands/make/plugin.dart | 6 +- lib/src/commands/make/service.dart | 4 +- lib/src/commands/make/test.dart | 6 +- lib/src/commands/plugin.dart | 6 +- lib/src/commands/pubspec.update.g.dart | 26 ---- lib/src/commands/rename.dart | 8 +- lib/src/commands/service.dart | 10 +- lib/src/commands/test.dart | 6 +- lib/src/util.dart | 18 +++ pubspec.yaml | 51 +++--- tool/build.dart | 4 - tool/phases.dart | 6 - tool/watch.dart | 4 - 21 files changed, 328 insertions(+), 274 deletions(-) delete mode 100644 .idea/runConfigurations/build_dart.xml delete mode 100644 lib/src/commands/pubspec.update.g.dart create mode 100644 lib/src/util.dart delete mode 100644 tool/build.dart delete mode 100644 tool/phases.dart delete mode 100644 tool/watch.dart diff --git a/.idea/angel_cli.iml b/.idea/angel_cli.iml index 78848ff9..6118a7ae 100644 --- a/.idea/angel_cli.iml +++ b/.idea/angel_cli.iml @@ -2,6 +2,7 @@ + diff --git a/.idea/runConfigurations/build_dart.xml b/.idea/runConfigurations/build_dart.xml deleted file mode 100644 index 1bf4884c..00000000 --- a/.idea/runConfigurations/build_dart.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index 518eb901..eae1e42a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,2 +1,3 @@ analyzer: - strong-mode: true \ No newline at end of file + strong-mode: + implicit-casts: false \ No newline at end of file diff --git a/lib/src/commands/controller.dart b/lib/src/commands/controller.dart index ce9458e6..995aba29 100644 --- a/lib/src/commands/controller.dart +++ b/lib/src/commands/controller.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import "package:console/console.dart"; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import 'deprecated.dart'; @@ -31,7 +31,7 @@ class ControllerCommand extends Command { await controllerFile.create(recursive: true); await controllerFile.writeAsString( - _generateController(await PubSpec.load(Directory.current), recase)); + _generateController(await Pubspec.load(Directory.current), recase)); _pen.green(); _pen("${Icon.CHECKMARK} Successfully generated controller $name."); @@ -41,7 +41,7 @@ class ControllerCommand extends Command { NewInstanceBuilder _expose(String path) => new TypeBuilder('Expose') .constInstance([], namedArguments: {'path': literal(path)}); - String _generateController(PubSpec pubspec, ReCase recase) { + String _generateController(Pubspec pubspec, ReCase recase) { var lower = recase.snakeCase; var lib = new LibraryBuilder('${pubspec.name}.routes.controllers.$lower'); lib.addDirective( diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index b75d8fa4..b81a6008 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -6,7 +6,7 @@ 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:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart' as yaml; import 'make/maker.dart'; @@ -61,7 +61,7 @@ class InstallCommand extends Command { if (!await installRepo.exists()) throw 'No local add-on database exists. Run `angel install --update` first.'; - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await Pubspec.load(Directory.current); for (var packageName in argResults.rest) { var packageDir = @@ -78,7 +78,7 @@ class InstallCommand extends Command { List globs = []; - var projectPubspec = await PubSpec.load(packageDir); + var projectPubspec = await Pubspec.load(packageDir); var deps = projectPubspec.dependencies.keys .map((k) { var dep = projectPubspec.dependencies[k]; @@ -207,16 +207,16 @@ class InstallCommand extends Command { } } - Future> list() async { + Future> list() async { if (!await installRepo.exists()) { throw 'No local add-on database exists. Run `angel install --update` first.'; } else { - List repos = []; + List repos = []; await for (var entity in installRepo.list()) { if (entity is Directory) { try { - repos.add(await PubSpec.load(entity)); + repos.add(await Pubspec.load(entity)); } catch (_) { // Ignore failures... } diff --git a/lib/src/commands/make/controller.dart b/lib/src/commands/make/controller.dart index ae36bd80..428c9e68 100644 --- a/lib/src/commands/make/controller.dart +++ b/lib/src/commands/make/controller.dart @@ -1,15 +1,14 @@ import 'dart:io'; import 'package:args/command_runner.dart'; -import 'package:code_builder/dart/core.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:console/console.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:dart_style/dart_style.dart'; +import 'package:io/ansi.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:recase/recase.dart'; +import '../../util.dart'; import 'maker.dart'; class ControllerCommand extends Command { - final TextPen _pen = new TextPen(); - @override String get name => 'controller'; @@ -32,73 +31,83 @@ class ControllerCommand extends Command { @override run() async { - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await loadPubspec(); String name; - if (argResults.wasParsed('name')) name = argResults['name']; + if (argResults.wasParsed('name')) name = argResults['name'] as String; if (name?.isNotEmpty != true) { - var p = new Prompter('Name of Controller class: '); - name = await p.prompt(checker: (s) => s.isNotEmpty); + name = prompts.get('Name of controller class'); } List deps = [ const MakerDependency('angel_framework', '^1.0.0') ]; + // ${pubspec.name}.src.models.${rc.snakeCase} + var rc = new ReCase(name); - var controllerLib = - new LibraryBuilder('${pubspec.name}.src.controllers.${rc.snakeCase}'); + var controllerLib = new Library((controllerLib) { + if (argResults['websocket'] as bool) { + deps.add(const MakerDependency('angel_websocket', '^1.0.0')); + controllerLib.directives + .add(new Directive.import('package:angel_websocket/server.dart')); + } else { + controllerLib.directives.add(new Directive.import( + 'package:angel_framework/angel_framework.dart')); + } - if (argResults['websocket']) { - deps.add(const MakerDependency('angel_websocket', '^1.0.0')); - controllerLib.addDirective( - new ImportBuilder('package:angel_websocket/server.dart')); - } else - controllerLib.addDirective( - new ImportBuilder('package:angel_framework/angel_framework.dart')); + controllerLib.body.add(new Class((clazz) { + clazz + ..name = '${rc.pascalCase}Controller' + ..extend = refer(argResults['websocket'] as bool + ? 'WebSocketController' + : 'Controller'); - TypeBuilder parentType = new TypeBuilder( - argResults['websocket'] ? 'WebSocketController' : 'Controller'); - ClassBuilder clazz = - new ClassBuilder('${rc.pascalCase}Controller', asExtends: parentType); - controllerLib.addMember(clazz); - - if (argResults['websocket']) { - var meth = new MethodBuilder('hello', returnType: lib$core.$void); - meth.addAnnotation(new TypeBuilder('ExposeWs') - .constInstance([literal('get_${rc.snakeCase}')])); - meth.addPositional( - parameter('socket', [new TypeBuilder('WebSocketContext')])); - meth.addStatement(reference('socket').invoke('send', [ - literal('got_${rc.snakeCase}'), - map({'message': literal('Hello, world!')}) - ])); - clazz.addMethod(meth); - } else { - clazz.addAnnotation(new TypeBuilder('Expose') - .constInstance([literal('/${rc.snakeCase}')])); - - var meth = new MethodBuilder('hello', - returnType: lib$core.String, returns: literal('Hello, world!')); - meth.addAnnotation( - new TypeBuilder('Expose').constInstance([literal('/')])); - clazz.addMethod(meth); - } + if (argResults['websocket'] as bool) { + clazz.methods.add(new Method((meth) { + meth + ..name = 'hello' + ..returns = refer('void') + ..annotations.add(refer('ExposeWs') + .constInstance([literal('get_${rc.snakeCase}')])) + ..requiredParameters.add(new Parameter((b) => b + ..name = 'socket' + ..type = refer('WebSocketContext'))) + ..body = new Block((block) { + block.addExpression(refer('socket').property('send').call([ + literal('got_${rc.snakeCase}'), + literalMap({'message': literal('Hello, world!')}), + ])); + }); + })); + } else { + clazz + ..annotations.add( + refer('Expose').constInstance([literal('/${rc.snakeCase}')])) + ..methods.add(new Method((meth) { + meth + ..name = 'hello' + ..returns = refer('String') + ..body = literal('Hello, world').returned.statement + ..annotations.add(refer('Expose').constInstance([ + literal('/'), + ])); + })); + } + })); + }); var outputDir = new Directory.fromUri( - Directory.current.uri.resolve(argResults['output-dir'])); + Directory.current.uri.resolve(argResults['output-dir'] as String)); var controllerFile = new File.fromUri(outputDir.uri.resolve('${rc.snakeCase}.dart')); if (!await controllerFile.exists()) await controllerFile.create(recursive: true); - await controllerFile - .writeAsString(prettyToSource(controllerLib.buildAst())); - _pen - ..green() - ..call( - '${Icon.CHECKMARK} Created controller file "${controllerFile.absolute.path}".') - ..call() - ..reset(); + await controllerFile.writeAsString(new DartFormatter() + .format(controllerLib.accept(new DartEmitter()).toString())); + + print(green.wrap( + '$checkmark Created controller file "${controllerFile.absolute.path}"')); if (deps.isNotEmpty) await depend(deps); } diff --git a/lib/src/commands/make/maker.dart b/lib/src/commands/make/maker.dart index bbee58b4..3e9e832b 100644 --- a/lib/src/commands/make/maker.dart +++ b/lib/src/commands/make/maker.dart @@ -1,18 +1,21 @@ import 'dart:async'; import 'dart:io'; -import 'package:pubspec/pubspec.dart'; +import 'package:io/ansi.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:pub_semver/pub_semver.dart'; import '../pub.dart'; +import '../../util.dart'; class MakerDependency { final String name, version; final bool dev; + const MakerDependency(this.name, this.version, {this.dev: false}); } Future depend(Iterable deps) async { - var pubspec = await PubSpec.load(Directory.current); - Map newDeps = {}, newDevDeps = {}; + var pubspec = await loadPubspec(); + var missing = []; for (var dep in deps) { var isPresent = false; @@ -22,37 +25,62 @@ Future depend(Iterable deps) async { isPresent = pubspec.dependencies.containsKey(dep.name); if (!isPresent) { - print('Installing ${dep.name}@${dep.version}...'); - - if (dep.dev) - newDevDeps[dep.name] = - new HostedReference(new VersionConstraint.parse(dep.version)); - else - newDeps[dep.name] = - new HostedReference(new VersionConstraint.parse(dep.version)); - } - - if (newDeps.isNotEmpty || newDevDeps.isNotEmpty) { - var newPubspec = pubspec.copy( - dependencies: - new Map.from(pubspec.dependencies) - ..addAll(newDeps), - devDependencies: - new Map.from(pubspec.devDependencies) - ..addAll(newDevDeps)); - - await newPubspec.save(Directory.current); - var pubPath = resolvePub(); - - print('Now running `$pubPath get`...'); - - var pubGet = await Process.start(pubPath, ['get']); - pubGet.stdout.listen(stdout.add); - pubGet.stderr.listen(stderr.add); - - var code = await pubGet.exitCode; - - if (code != 0) throw 'pub get terminated with exit code $code'; +// TODO: https://github.com/dart-lang/pubspec_parse/issues/17: +// print('Installing ${dep.name}@${dep.version}...'); +// +// if (dep.dev) { +// pubspec.devDependencies[dep.name] = new HostedDependency( +// version: new VersionConstraint.parse(dep.version), +// ); +// } else { +// pubspec.dependencies[dep.name] = new HostedDependency( +// version: new VersionConstraint.parse(dep.version), +// ); +// } } } + + missing.sort((a, b) { + if (!a.dev) { + if (b.dev) { + return -1; + } else { + return 0; + } + } else { + if (b.dev) { + return 0; + } else { + return 1; + } + } + }); + + if (missing.isNotEmpty) { + print(yellow.wrap(missing.length == 1 + ? 'You are missing one dependency:' + : 'You are missing ${missing.length} dependencies:')); + print('\n'); + + for (var dep in missing) { + var m = ' * ${dep.name}@${dep.version}'; + if (dep.dev) m += ' (dev dependency)'; + print(yellow.wrap(m)); + } + } + +// if (isPresent) { +// TODO: https://github.com/dart-lang/pubspec_parse/issues/17 +// await savePubspec(pubspec); +// var pubPath = resolvePub(); +// +// print('Now running `$pubPath get`...'); +// +// var pubGet = await Process.start(pubPath, ['get']); +// pubGet.stdout.listen(stdout.add); +// pubGet.stderr.listen(stderr.add); +// +// var code = await pubGet.exitCode; +// +// if (code != 0) throw 'pub get terminated with exit code $code'; } diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 9b544420..899ced3a 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -1,16 +1,15 @@ import 'dart:io'; import 'package:args/command_runner.dart'; -import 'package:code_builder/dart/core.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:console/console.dart'; +import 'package:dart_style/dart_style.dart'; import 'package:inflection/inflection.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:io/ansi.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:recase/recase.dart'; +import '../../util.dart'; import 'maker.dart'; class ModelCommand extends Command { - final TextPen _pen = new TextPen(); - @override String get name => 'model'; @@ -38,13 +37,12 @@ class ModelCommand extends Command { @override run() async { - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await loadPubspec(); String name; - if (argResults.wasParsed('name')) name = argResults['name']; + if (argResults.wasParsed('name')) name = argResults['name'] as String; if (name?.isNotEmpty != true) { - var p = new Prompter('Name of Model class: '); - name = await p.prompt(checker: (s) => s.isNotEmpty); + name = prompts.get('Name of model class'); } List deps = [ @@ -53,107 +51,148 @@ class ModelCommand extends Command { ]; var rc = new ReCase(name); - var modelLib = - new LibraryBuilder('${pubspec.name}.src.models.${rc.snakeCase}'); - modelLib.addDirective( - new ImportBuilder('package:angel_model/angel_model.dart')); - var needsSerialize = argResults['serializable'] || argResults['orm']; + var modelLib = new Library((modelLib) { + modelLib.directives + .add(new Directive.import('package:angel_model/angel_model.dart')); - if (needsSerialize) { - modelLib.addDirective( - new ImportBuilder('package:angel_serialize/angel_serialize.dart')); - deps.add(const MakerDependency('angel_serialize', '^1.0.0-alpha')); - } + var needsSerialize = + argResults['serializable'] as bool || argResults['orm'] as bool; - if (argResults['orm']) { - modelLib - .addDirective(new ImportBuilder('package:angel_orm/angel_orm.dart')); - deps.add(const MakerDependency('angel_orm', '^1.0.0-alpha')); - } + if (needsSerialize) { + modelLib.directives.add(new Directive.import( + 'package:angel_serialize/angel_serialize.dart')); + deps.add(const MakerDependency('angel_serialize', '^2.0.0')); + } - var modelClazz = new ClassBuilder( - needsSerialize ? '_${rc.pascalCase}' : rc.pascalCase, - asExtends: new TypeBuilder('Model')); - modelLib.addMember(modelClazz); + if (argResults['orm'] as bool) { + modelLib.directives + .add(new Directive.import('package:angel_orm/angel_orm.dart')); + deps.add(const MakerDependency('angel_orm', '^1.0.0-alpha')); + } - if (needsSerialize) { - modelLib.addDirective(new PartBuilder('${rc.snakeCase}.g.dart')); - modelClazz.addAnnotation(reference('serializable')); - } + modelLib.body.add(new Class((modelClazz) { + modelClazz + ..name = needsSerialize ? '_${rc.pascalCase}' : rc.pascalCase + ..extend = refer('Model'); - if (argResults['orm']) { - modelClazz.addAnnotation(reference('orm')); - } + if (needsSerialize) { + // TODO: Add parts + // modelLib.addDirective(new PartBuilder('${rc.snakeCase}.g.dart')); + modelClazz.annotations.add(refer('serializable')); + } + + if (argResults['orm'] as bool) { + modelClazz.annotations.add(refer('orm')); + } + })); + }); // Save model file var outputDir = new Directory.fromUri( - Directory.current.uri.resolve(argResults['output-dir'])); + Directory.current.uri.resolve(argResults['output-dir'] as String)); var modelFile = new File.fromUri(outputDir.uri.resolve('${rc.snakeCase}.dart')); if (!await modelFile.exists()) await modelFile.create(recursive: true); - await modelFile.writeAsString(prettyToSource(modelLib.buildAst())); - _pen - ..green() - ..call( - '${Icon.CHECKMARK} Created model file "${modelFile.absolute.path}".') - ..call() - ..reset(); - if (argResults['migration']) { + await modelFile.writeAsString(new DartFormatter() + .format(modelLib.accept(new DartEmitter()).toString())); + + print(green + .wrap('$checkmark Created model file "${modelFile.absolute.path}".')); + + if (argResults['migration'] as bool) { deps.add(const MakerDependency('angel_migration', '^1.0.0-alpha')); - var migrationLib = new LibraryBuilder() - ..addDirective( - new ImportBuilder('package:angel_migration/angel_migration.dart')); - var migrationClazz = new ClassBuilder('${rc.pascalCase}Migration', - asExtends: new TypeBuilder('Migration')); - migrationLib.addMember(migrationClazz); - var tableName = pluralize(rc.snakeCase); + var migrationLib = new Library((migrationLib) { + migrationLib + ..directives.add(new Directive.import( + 'package:angel_migration.dart/angel_migration.dart')) + ..body.add(new Class((migrationClazz) { + migrationClazz + ..name = '${rc.pascalCase}Migration' + ..extend = refer('Migration'); - // up() - var up = new MethodBuilder('up', returnType: lib$core.$void); - migrationClazz.addMethod(up); - up.addAnnotation(lib$core.override); - up.addPositional(parameter('schema', [new TypeBuilder('Schema')])); + var tableName = pluralize(rc.snakeCase); - // (table) { ... } - var callback = new MethodBuilder.closure(); - callback.addPositional(parameter('table')); + // up() + migrationClazz.methods.add(new Method((up) { + up + ..name = 'up' + ..returns = refer('void') + ..annotations.add(refer('override')) + ..requiredParameters.add(new Parameter((b) => b + ..name = 'schema' + ..type = refer('Schema'))) + ..body = new Block((block) { + // (table) { ... } + var callback = new Method((callback) { + callback + ..requiredParameters + .add(new Parameter((b) => b..name = 'table')) + ..body = new Block((block) { + var table = refer('table'); - var cascade = reference('table').cascade((table) => [ - table.invoke('serial', [literal('id')]).invoke('primaryKey', []), - table.invoke('date', [literal('created_at')]), - table.invoke('date', [literal('updated_at')]) - ]); - callback.addStatement(cascade); + block.addExpression( + (table.property('serial').call([literal('id')])) + .property('primaryKey') + .call([]), + ); - up.addStatement( - reference('schema').invoke('create', [literal(tableName), callback])); + block.addExpression( + table.property('date').call([ + literal('created_at'), + ]), + ); - // down() - var down = new MethodBuilder('down', returnType: lib$core.$void); - migrationClazz.addMethod(down); - down.addAnnotation(lib$core.override); - down.addPositional(parameter('schema', [new TypeBuilder('Schema')])); - down.addStatement( - reference('schema').invoke('drop', [literal(tableName)])); + block.addExpression( + table.property('date').call([ + literal('updated_at'), + ]), + ); + }); + }); + + block.addExpression(refer('schema').property('create').call([ + literal(tableName), + callback.closure, + ])); + }); + })); + + // down() + migrationClazz.methods.add(new Method((down) { + down + ..name = 'down' + ..returns = refer('void') + ..annotations.add(refer('override')) + ..requiredParameters.add(new Parameter((b) => b + ..name = 'schema' + ..type = refer('Schema'))) + ..body = new Block((block) { + block.addExpression( + refer('schema').property('drop').call([ + literal(tableName), + ]), + ); + }); + })); + })); + }); // Save migration file var migrationDir = new Directory.fromUri( - Directory.current.uri.resolve(argResults['migration-dir'])); + Directory.current.uri.resolve(argResults['migration-dir'] as String)); var migrationFile = new File.fromUri(migrationDir.uri.resolve('${rc.snakeCase}.dart')); if (!await migrationFile.exists()) await migrationFile.create(recursive: true); - await migrationFile - .writeAsString(prettyToSource(migrationLib.buildAst())); - _pen - ..green() - ..call( - '${Icon.CHECKMARK} Created migration file "${migrationFile.absolute.path}".') - ..call() - ..reset(); + + await migrationFile.writeAsString(new DartFormatter() + .format(migrationLib.accept(new DartEmitter()).toString())); + + print(green.wrap( + '$checkmark Created migration file "${migrationFile.absolute.path}".')); } if (deps.isNotEmpty) await depend(deps); diff --git a/lib/src/commands/make/plugin.dart b/lib/src/commands/make/plugin.dart index b6c1183f..6547d95a 100644 --- a/lib/src/commands/make/plugin.dart +++ b/lib/src/commands/make/plugin.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import 'maker.dart'; @@ -26,7 +26,7 @@ class PluginCommand extends Command { @override run() async { - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await Pubspec.load(Directory.current); String name; if (argResults.wasParsed('name')) name = argResults['name']; @@ -56,7 +56,7 @@ class PluginCommand extends Command { _pen(); } - String _generatePlugin(PubSpec pubspec, ReCase rc) { + String _generatePlugin(Pubspec pubspec, ReCase rc) { return ''' library ${pubspec.name}.src.config.plugins.${rc.snakeCase}; diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart index 62707fd4..c2f7489a 100644 --- a/lib/src/commands/make/service.dart +++ b/lib/src/commands/make/service.dart @@ -3,7 +3,7 @@ import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import 'package:console/console.dart'; import 'package:inflection/inflection.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import '../service_generators/service_generators.dart'; import 'maker.dart'; @@ -32,7 +32,7 @@ class ServiceCommand extends Command { @override run() async { - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await Pubspec.load(Directory.current); String name; if (argResults.wasParsed('name')) name = argResults['name']; diff --git a/lib/src/commands/make/test.dart b/lib/src/commands/make/test.dart index 417e6eb5..fd15f0fa 100644 --- a/lib/src/commands/make/test.dart +++ b/lib/src/commands/make/test.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import 'maker.dart'; @@ -29,7 +29,7 @@ class TestCommand extends Command { @override run() async { - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await Pubspec.load(Directory.current); String name; if (argResults.wasParsed('name')) name = argResults['name']; @@ -87,7 +87,7 @@ class TestCommand extends Command { .trim(); } - String _generateTest(PubSpec pubspec, ReCase rc) { + String _generateTest(Pubspec pubspec, ReCase rc) { return ''' import 'dart:io'; import 'package:${pubspec.name}/${pubspec.name}.dart'; diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart index 49d5fc10..d8facda9 100644 --- a/lib/src/commands/plugin.dart +++ b/lib/src/commands/plugin.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import 'deprecated.dart'; @@ -19,7 +19,7 @@ class PluginCommand extends Command { run() async { warnDeprecated(this.name, _pen); - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await Pubspec.load(Directory.current); final name = await readInput("Name of Plugin: "), lower = new ReCase(name).snakeCase; final testDir = new Directory("lib/src/config/plugins"); @@ -35,7 +35,7 @@ class PluginCommand extends Command { _pen(); } - String _generatePlugin(PubSpec pubspec, String name, String lower) { + String _generatePlugin(Pubspec pubspec, String name, String lower) { return ''' library ${pubspec.name}.config.plugins.$lower; diff --git a/lib/src/commands/pubspec.update.g.dart b/lib/src/commands/pubspec.update.g.dart deleted file mode 100644 index 07f80537..00000000 --- a/lib/src/commands/pubspec.update.g.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'dart:async'; -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, 2, 0, build: '5'); -Future fetchCurrentVersion(http.BaseClient client) async { - var response = - await client.get('https://pub.dartlang.org/api/packages/angel_cli'); - var json = JSON.decode(response.body) as Map; - if (!(json.containsKey('latest')) || - !(json['latest'].containsKey('pubspec')) || - !(json['latest']['pubspec'].containsKey('version'))) { - throw new StateError( - 'GET https://pub.dartlang.org/api/packages/angel_cli returned an invalid pub API response.'); - } - return new Version.parse(json['latest']['pubspec']['version']); -} - -Future checkForUpdate(http.BaseClient client) async { - var current = await fetchCurrentVersion(client); - if (PACKAGE_VERSION.compareTo(current) < 0) { - return current; - } - return null; -} diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index f64c4047..b0c42912 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -4,7 +4,7 @@ import 'package:args/command_runner.dart'; import 'package:console/console.dart'; import 'package:dart_style/dart_style.dart'; import 'package:glob/glob.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'pub.dart'; class RenameCommand extends Command { @@ -40,7 +40,7 @@ class RenameCommand extends Command { if (!await pubspecFile.exists()) { throw new Exception('No pubspec.yaml found in current directory.'); } else { - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await Pubspec.load(Directory.current); var oldName = pubspec.name; await renamePubspec(Directory.current, oldName, newName); await renameDartFiles(Directory.current, oldName, newName); @@ -57,8 +57,8 @@ class RenameCommand extends Command { } renamePubspec(Directory dir, String oldName, String newName) async { - var pubspec = await PubSpec.load(dir); - var newPubspec = new PubSpec.fromJson(pubspec.toJson()..['name'] = newName); + var pubspec = await Pubspec.load(dir); + var newPubspec = new Pubspec.fromJson(pubspec.toJson()..['name'] = newName); await newPubspec.save(dir); } diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 9fd28165..ed60431d 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -4,7 +4,7 @@ import 'package:code_builder/code_builder.dart'; import 'package:console/console.dart'; import 'package:dart_style/dart_style.dart'; import 'package:inflection/inflection.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import 'service_generators/service_generators.dart'; import 'deprecated.dart'; @@ -23,7 +23,7 @@ class ServiceCommand extends Command { run() async { warnDeprecated(this.name, _pen); - var pubspec = await PubSpec.load(Directory.current); + var pubspec = await Pubspec.load(Directory.current); var name = await readInput('Name of Service (not plural): '); var chooser = new Chooser( serviceGenerators.map((g) => g.name).toList(), @@ -151,7 +151,7 @@ class ServiceCommand extends Command { } _generateModel( - PubSpec pubspec, String name, String lower, DartFormatter fmt) async { + Pubspec pubspec, String name, String lower, DartFormatter fmt) async { var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); @@ -173,7 +173,7 @@ class $name extends Model { } _generateValidator( - PubSpec pubspec, String lower, ReCase rc, DartFormatter fmt) async { + Pubspec pubspec, String lower, ReCase rc, DartFormatter fmt) async { var file = new File('lib/src/validators/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); @@ -204,7 +204,7 @@ final Validator create${rc.pascalCase} = ${rc.camelCase}.extend({}) .trim(); } - _generateTests(PubSpec pubspec, String lower, DartFormatter fmt) { + _generateTests(Pubspec pubspec, String lower, DartFormatter fmt) { return fmt.format(''' import 'dart:io'; import 'package:${pubspec.name}/${pubspec.name}.dart'; diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index b519b62e..d973a85c 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; -import 'package:pubspec/pubspec.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import 'deprecated.dart'; @@ -28,7 +28,7 @@ class TestCommand extends Command { if (!await testFile.exists()) await testFile.create(recursive: true); await testFile.writeAsString(new DartFormatter() - .format(_generateTest(await PubSpec.load(Directory.current), lower))); + .format(_generateTest(await Pubspec.load(Directory.current), lower))); final runConfig = new File('./.idea/runConfigurations/${name}_tests.xml'); @@ -56,7 +56,7 @@ class TestCommand extends Command { .trim(); } - String _generateTest(PubSpec pubspec, String lower) { + String _generateTest(Pubspec pubspec, String lower) { return ''' import 'dart:io'; import 'package:${pubspec.name}/${pubspec.name}.dart'; diff --git a/lib/src/util.dart b/lib/src/util.dart new file mode 100644 index 00000000..b1ea7ef7 --- /dev/null +++ b/lib/src/util.dart @@ -0,0 +1,18 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:io/ansi.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:yamlicious/yamlicious.dart'; + +final String checkmark = ansiOutputEnabled ? '\u2713' : '[Success]'; + +Future loadPubspec() { + var file = new File('pubspec.yaml'); + return file + .readAsString() + .then((yaml) => new Pubspec.parse(yaml, sourceUrl: file.uri)); +} + +Future savePubspec(Pubspec pubspec) async { + var text = toYamlString(pubspec); +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index ca7b9426..b2f905a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,30 +1,35 @@ -author: "Tobe O " -description: "Command-line tools for the Angel framework." -homepage: "https://github.com/angel-dart/angel_cli" -name: "angel_cli" -version: 1.2.0+5 +author: Tobe O +description: Command-line tools for the Angel framework. +homepage: https://github.com/angel-dart/angel_cli +name: angel_cli +version: 1.3.0 dependencies: - # analyzer: "^0.29.0" - args: ^0.13.4 - code_builder: ^1.0.0 - console: "^2.2.3" + # analyzer: ^0.29.0 + args: ^1.0.0 + code_builder: ^3.0.0 + #console: ^2.2.3 + dart2_constant: ^1.0.0 dart_style: ^1.0.0 - glob: "^1.1.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" - watcher: "^0.9.7" - yaml: "^2.0.0" + id: ^1.0.0 + io: ^0.3.2 + inflection: ^0.4.1 + #mustache4dart: ^2.1.0 + path: ^1.0.0 + prompts: ^1.0.0 + pubspec_parse: ^0.1.2 + random_string: ^0.0.1 + recase: ^1.0.0 + watcher: ^0.9.7 + yaml: ^2.0.0 + yamlicious: ^0.0.5 dev_dependencies: - build: ^0.7.0 - build_runner: ^0.3.0 - check_for_update: ^1.0.0 + #build: ^0.7.0 + #build_runner: ^0.3.0 + #check_for_update: ^1.0.0 environment: - sdk: ">=1.19.0" + sdk: ">=2.0.0-dev <3.0.0" executables: - angel: "angel" + angel: angel diff --git a/tool/build.dart b/tool/build.dart deleted file mode 100644 index c07165fd..00000000 --- a/tool/build.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'package:build_runner/build_runner.dart'; -import 'phases.dart'; - -main() => build(phaseGroup, deleteFilesByDefault: true); diff --git a/tool/phases.dart b/tool/phases.dart deleted file mode 100644 index 94bcdbf6..00000000 --- a/tool/phases.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:build_runner/build_runner.dart'; -import 'package:check_for_update/builder.dart'; - -final PhaseGroup phaseGroup = new PhaseGroup.singleAction( - new CheckForUpdateBuilder(subDirectory: 'lib/src/commands'), - new InputSet('angel_cli', const ['pubspec.yaml'])); diff --git a/tool/watch.dart b/tool/watch.dart deleted file mode 100644 index cd2ce7db..00000000 --- a/tool/watch.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'package:build_runner/build_runner.dart'; -import 'phases.dart'; - -main() => watch(phaseGroup, deleteFilesByDefault: true); From 9fd92753470706a0df5ea63f0fd81e2e0254e1fc Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 17:50:19 -0400 Subject: [PATCH 066/132] Update plugin gen --- lib/src/commands/make/plugin.dart | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/src/commands/make/plugin.dart b/lib/src/commands/make/plugin.dart index 6547d95a..37807505 100644 --- a/lib/src/commands/make/plugin.dart +++ b/lib/src/commands/make/plugin.dart @@ -1,14 +1,14 @@ import 'dart:io'; import 'package:args/command_runner.dart'; -import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; +import 'package:io/ansi.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; +import '../../util.dart'; import 'maker.dart'; class PluginCommand extends Command { - final TextPen _pen = new TextPen(); - @override String get name => "plugin"; @@ -26,13 +26,12 @@ class PluginCommand extends Command { @override run() async { - var pubspec = await Pubspec.load(Directory.current); + var pubspec = await loadPubspec(); String name; - if (argResults.wasParsed('name')) name = argResults['name']; + if (argResults.wasParsed('name')) name = argResults['name'] as String; if (name?.isNotEmpty != true) { - var p = new Prompter('Name of Controller class: '); - name = await p.prompt(checker: (s) => s.isNotEmpty); + name = prompts.get('Name of plug-in class'); } List deps = [ @@ -41,7 +40,7 @@ class PluginCommand extends Command { var rc = new ReCase(name); final pluginDir = new Directory.fromUri( - Directory.current.uri.resolve(argResults['output-dir'])); + Directory.current.uri.resolve(argResults['output-dir'] as String)); final pluginFile = new File.fromUri(pluginDir.uri.resolve("${rc.snakeCase}.dart")); if (!await pluginFile.exists()) await pluginFile.create(recursive: true); @@ -50,10 +49,8 @@ class PluginCommand extends Command { if (deps.isNotEmpty) await depend(deps); - _pen.green(); - _pen( - '${Icon.CHECKMARK} Successfully generated plug-in file "${pluginFile.absolute.path}".'); - _pen(); + print(green.wrap( + '$checkmark Successfully generated plug-in file "${pluginFile.absolute.path}".')); } String _generatePlugin(Pubspec pubspec, ReCase rc) { From 4c9e1c78ce440513adf4fcc9d73116362433c893 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 17:50:33 -0400 Subject: [PATCH 067/132] Ignore .dart_tool --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 58e5c867..0d7438ab 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,4 @@ pubspec.lock /sample_project/lib/src/services/ /sample_project/test/services/ /sample_project/ +.dart_tool \ No newline at end of file From c676bb18fd5fb4dc496136a33a97e306beb874d6 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:02:56 -0400 Subject: [PATCH 068/132] Update service generator --- lib/src/commands/make/service.dart | 138 ++++++++++-------- .../service_generators/generator.dart | 6 +- 2 files changed, 77 insertions(+), 67 deletions(-) diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart index c2f7489a..e1a742a6 100644 --- a/lib/src/commands/make/service.dart +++ b/lib/src/commands/make/service.dart @@ -1,16 +1,17 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:console/console.dart'; +import 'package:dart_style/dart_style.dart'; import 'package:inflection/inflection.dart'; +import 'package:io/ansi.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import '../service_generators/service_generators.dart'; +import '../../util.dart'; import 'maker.dart'; class ServiceCommand extends Command { - final TextPen _pen = new TextPen(); - @override String get name => 'service'; @@ -32,94 +33,103 @@ class ServiceCommand extends Command { @override run() async { - var pubspec = await Pubspec.load(Directory.current); + var pubspec = await loadPubspec(); String name; - if (argResults.wasParsed('name')) name = argResults['name']; + if (argResults.wasParsed('name')) name = argResults['name'] as String; if (name?.isNotEmpty != true) { - var p = new Prompter('Name of Service: '); - name = await p.prompt(checker: (s) => s.isNotEmpty); + name = prompts.get('Name of service'); } List deps = [ const MakerDependency('angel_framework', '^1.0.0') ]; + // '${pubspec.name}.src.services.${rc.snakeCase}' var rc = new ReCase(name); - var serviceLib = - new LibraryBuilder('${pubspec.name}.src.services.${rc.snakeCase}'); + var serviceLib = new Library((serviceLib) { + var generator = prompts.choose( + 'Choose which type of service to create', serviceGenerators); - ServiceGenerator generator; +// if (generator == null) { +// _pen.red(); +// _pen('${Icon.BALLOT_X} \'$type\' services are not yet implemented. :('); +// _pen(); +// throw 'Unrecognized service type: "$type".'; +// } - var chooser = new Chooser( - serviceGenerators.map((g) => g.name).toList(), - message: 'What type of service would you like to create? '); - var type = await chooser.choose(); + for (var dep in generator.dependencies) { + if (!deps.any((d) => d.name == dep.name)) deps.add(dep); + } - generator = - serviceGenerators.firstWhere((g) => g.name == type, orElse: () => null); + if (generator.goesFirst) { + generator.applyToLibrary(serviceLib, name, rc.snakeCase); + serviceLib.directives.add(new Directive.import( + 'package:angel_framework/angel_framework.dart')); + } else { + serviceLib.directives.add(new Directive.import( + 'package:angel_framework/angel_framework.dart')); + generator.applyToLibrary(serviceLib, name, rc.snakeCase); + } - if (generator == null) { - _pen.red(); - _pen('${Icon.BALLOT_X} \'$type\' services are not yet implemented. :('); - _pen(); - throw 'Unrecognized service type: "$type".'; - } + if (argResults['typed'] as bool) { + serviceLib.directives + .add(new Directive.import('../models/${rc.snakeCase}.dart')); + } - for (var dep in generator.dependencies) { - if (!deps.any((d) => d.name == dep.name)) deps.add(dep); - } + // configureServer() {} + serviceLib.body.add(new Method((configureServer) { + configureServer + ..name = 'configureServer' + ..returns = refer('AngelConfigurer'); - if (generator.goesFirst) { - generator.applyToLibrary(serviceLib, name, rc.snakeCase); - serviceLib.addMember( - new ImportBuilder('package:angel_framework/angel_framework.dart')); - } else { - serviceLib.addMember( - new ImportBuilder('package:angel_framework/angel_framework.dart')); - generator.applyToLibrary(serviceLib, name, rc.snakeCase); - } + configureServer.body = new Block((block) { + generator.applyToConfigureServer( + configureServer, block, name, rc.snakeCase); - if (argResults['typed']) { - serviceLib - ..addMember(new ImportBuilder('../models/${rc.snakeCase}.dart')); - } + // return (Angel app) async {} + var closure = new Method((closure) { + closure + ..modifier = MethodModifier.async + ..requiredParameters.add(new Parameter((b) => b + ..name = 'app' + ..type = refer('Angel'))); + closure.body = new Block((block) { + generator.beforeService(block, name, rc.snakeCase); - // configureServer() {} - var configureServer = new MethodBuilder('configureServer', - returnType: new TypeBuilder('AngelConfigurer')); - generator.applyToConfigureServer(configureServer, name, rc.snakeCase); + // app.use('/api/todos', new MapService()); + var service = + generator.createInstance(closure, name, rc.snakeCase); - // return (Angel app) async {} - var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync) - ..addPositional(parameter('app', [new TypeBuilder('Angel')])); - generator.beforeService(closure, name, rc.snakeCase); + if (argResults['typed'] as bool) { + var tb = new TypeReference((b) => b + ..symbol = 'TypedService' + ..types.add(refer(rc.pascalCase))); + service = tb.newInstance([service]); + } - // app.use('/api/todos', new MapService()); - var service = generator.createInstance(closure, name, rc.snakeCase); + block.addExpression(refer('app').property('use').call([ + literal('/api/${pluralize(rc.snakeCase)}'), + service, + ])); + }); + }); - if (argResults['typed']) { - service = new TypeBuilder('TypedService', - genericTypes: [new TypeBuilder(rc.pascalCase)]) - .newInstance([service]); - } - - closure.addStatement(reference('app') - .invoke('use', [literal('/api/${pluralize(rc.snakeCase)}'), service])); - configureServer.addStatement(closure.asReturn()); - serviceLib.addMember(configureServer); + block.addExpression(closure.closure.returned); + }); + })); + }); final outputDir = new Directory.fromUri( - Directory.current.uri.resolve(argResults['output-dir'])); + Directory.current.uri.resolve(argResults['output-dir'] as String)); final serviceFile = new File.fromUri(outputDir.uri.resolve("${rc.snakeCase}.dart")); if (!await serviceFile.exists()) await serviceFile.create(recursive: true); - await serviceFile.writeAsString(prettyToSource(serviceLib.buildAst())); + await serviceFile.writeAsString(new DartFormatter() + .format(serviceLib.accept(new DartEmitter()).toString())); - _pen.green(); - _pen( - '${Icon.CHECKMARK} Successfully generated service file "${serviceFile.absolute.path}".'); - _pen(); + print(green.wrap( + '$checkmark Successfully generated service file "${serviceFile.absolute.path}".')); if (deps.isNotEmpty) await depend(deps); } diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart index 970bb0fa..23ec15e8 100644 --- a/lib/src/commands/service_generators/generator.dart +++ b/lib/src/commands/service_generators/generator.dart @@ -27,12 +27,12 @@ class ServiceGenerator { void applyToLibrary(LibraryBuilder library, String name, String lower) {} - void beforeService(MethodBuilder methodBuilder, String name, String lower) {} + void beforeService(BlockBuilder builder, String name, String lower) {} void applyToConfigureServer( - MethodBuilder configureServer, String name, String lower) {} + MethodBuilder configureServer, BlockBuilder block, String name, String lower) {} - ExpressionBuilder createInstance( + Expression createInstance( MethodBuilder methodBuilder, String name, String lower) => literal(null); } From 00075b07cd8f5e2f37e822e65d91667b7eb49ffc Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:04:33 -0400 Subject: [PATCH 069/132] Update test generator --- lib/src/commands/make/test.dart | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/src/commands/make/test.dart b/lib/src/commands/make/test.dart index fd15f0fa..c7efb890 100644 --- a/lib/src/commands/make/test.dart +++ b/lib/src/commands/make/test.dart @@ -1,14 +1,14 @@ import 'dart:io'; import 'package:args/command_runner.dart'; -import "package:console/console.dart"; import 'package:dart_style/dart_style.dart'; +import 'package:io/ansi.dart'; +import 'package:prompts/prompts.dart' as prompter; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; +import '../../util.dart'; import 'maker.dart'; class TestCommand extends Command { - final TextPen _pen = new TextPen(); - @override String get name => "test"; @@ -29,13 +29,12 @@ class TestCommand extends Command { @override run() async { - var pubspec = await Pubspec.load(Directory.current); + var pubspec = await loadPubspec(); String name; - if (argResults.wasParsed('name')) name = argResults['name']; + if (argResults.wasParsed('name')) name = argResults['name'] as String; if (name?.isNotEmpty != true) { - var p = new Prompter('Name of Test: '); - name = await p.prompt(checker: (s) => s.isNotEmpty); + name = prompter.get('Name of test'); } List deps = [ @@ -46,7 +45,7 @@ class TestCommand extends Command { var rc = new ReCase(name); final testDir = new Directory.fromUri( - Directory.current.uri.resolve(argResults['output-dir'])); + Directory.current.uri.resolve(argResults['output-dir'] as String)); final testFile = new File.fromUri(testDir.uri.resolve("${rc.snakeCase}_test.dart")); if (!await testFile.exists()) await testFile.create(recursive: true); @@ -55,27 +54,22 @@ class TestCommand extends Command { if (deps.isNotEmpty) await depend(deps); - _pen.green(); - _pen( - '${Icon.CHECKMARK} Successfully generated test file "${testFile.absolute.path}".'); - _pen(); + print(green.wrap( + '$checkmark Successfully generated test file "${testFile.absolute.path}".')); - if (argResults['run-configuration']) { + if (argResults['run-configuration'] as bool) { final runConfig = new File.fromUri(Directory.current.uri .resolve('.idea/runConfigurations/${name}_Tests.xml')); if (!await runConfig.exists()) await runConfig.create(recursive: true); await runConfig.writeAsString(_generateRunConfiguration(name, rc)); - _pen.reset(); - _pen.green(); - _pen( - '${Icon.CHECKMARK} Successfully generated run configuration "$name Tests" at "${runConfig.absolute.path}".'); - _pen(); + print(green.wrap( + '$checkmark Successfully generated run configuration "$name Tests" at "${runConfig.absolute.path}".')); } } - _generateRunConfiguration(String name, ReCase rc) { + String _generateRunConfiguration(String name, ReCase rc) { return ''' From 96f2955e94c58ec752247b2614872c1236c6655f Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:06:08 -0400 Subject: [PATCH 070/132] Update custom service generator --- lib/src/commands/service_generators/custom.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/src/commands/service_generators/custom.dart b/lib/src/commands/service_generators/custom.dart index 7d1f7f90..a1de5f32 100644 --- a/lib/src/commands/service_generators/custom.dart +++ b/lib/src/commands/service_generators/custom.dart @@ -12,13 +12,16 @@ class CustomServiceGenerator extends ServiceGenerator { @override void applyToLibrary(LibraryBuilder library, String name, String lower) { - var clazz = new ClassBuilder('${name}Service', asExtends: new TypeBuilder('Service')); - library.addMember(clazz); + library.body.add(new Class((clazz) { + clazz + ..name = '${name}Service' + ..extend = refer('Service'); + })); } @override - ExpressionBuilder createInstance( + Expression createInstance( MethodBuilder methodBuilder, String name, String lower) { - return new TypeBuilder('${name}Service').newInstance([]); + return refer('${name}Service').newInstance([]); } } From fda5f944690bed8da53fb0a3ad6b3ad0a97077a9 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:08:29 -0400 Subject: [PATCH 071/132] Update file service generator --- .../service_generators/file_service.dart | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/src/commands/service_generators/file_service.dart b/lib/src/commands/service_generators/file_service.dart index d536cbc8..7ff54579 100644 --- a/lib/src/commands/service_generators/file_service.dart +++ b/lib/src/commands/service_generators/file_service.dart @@ -14,18 +14,29 @@ class FileServiceGenerator extends ServiceGenerator { bool get goesFirst => true; @override - void applyToLibrary(LibraryBuilder library, String name, String lower) { - library.addMember(new ImportBuilder('dart:io')); - library.addMember(new ImportBuilder( - 'package:angel_file_service/angel_file_service.dart')); + void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, + String name, String lower) { + configureServer.requiredParameters.add(new Parameter((b) => b + ..name = 'dbDirectory' + ..type = refer('Directory'))); } @override - ExpressionBuilder createInstance( + void applyToLibrary(LibraryBuilder library, String name, String lower) { + library.directives.addAll([ + new Directive.import( + 'package:angel_file_service/angel_file_service.dart'), + new Directive.import('package:file/file.dart'), + ]); + } + + @override + Expression createInstance( MethodBuilder methodBuilder, String name, String lower) { - return new TypeBuilder('JsonFileService').newInstance([ - new TypeBuilder('File') - .newInstance([literal(pluralize(lower) + '_db.json')]) + return refer('JsonFileService').newInstance([ + refer('dbDirectory') + .property('childFile') + .call([literal(pluralize(lower) + '_db.json')]) ]); } } From 0d378633accf6fc2b103a02bcca70056ea087774 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:08:48 -0400 Subject: [PATCH 072/132] Update map service generator --- lib/src/commands/service_generators/map.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/service_generators/map.dart b/lib/src/commands/service_generators/map.dart index ec7b1499..4b1dec7e 100644 --- a/lib/src/commands/service_generators/map.dart +++ b/lib/src/commands/service_generators/map.dart @@ -8,8 +8,8 @@ class MapServiceGenerator extends ServiceGenerator { bool get createsModel => false; @override - ExpressionBuilder createInstance( + Expression createInstance( MethodBuilder methodBuilder, String name, String lower) { - return new TypeBuilder('MapService').newInstance([]); + return refer('MapService').newInstance([]); } } From e5ecbeef3dbcabb308f0e16dc68b9187b60192d4 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:12:12 -0400 Subject: [PATCH 073/132] Update rethink service generator --- lib/src/commands/controller.dart | 8 +++--- lib/src/commands/service.dart | 20 +++++++------- .../commands/service_generators/mongo.dart | 20 +++++++------- .../commands/service_generators/rethink.dart | 27 +++++++++++-------- lib/src/util.dart | 5 ++-- pubspec.yaml | 2 +- 6 files changed, 45 insertions(+), 37 deletions(-) diff --git a/lib/src/commands/controller.dart b/lib/src/commands/controller.dart index 995aba29..fd3ff04d 100644 --- a/lib/src/commands/controller.dart +++ b/lib/src/commands/controller.dart @@ -38,17 +38,17 @@ class ControllerCommand extends Command { _pen(); } - NewInstanceBuilder _expose(String path) => new TypeBuilder('Expose') + NewInstanceBuilder _expose(String path) => refer('Expose') .constInstance([], namedArguments: {'path': literal(path)}); String _generateController(Pubspec pubspec, ReCase recase) { var lower = recase.snakeCase; var lib = new LibraryBuilder('${pubspec.name}.routes.controllers.$lower'); lib.addDirective( - new ImportBuilder('package:angel_common/angel_common.dart')); + new Directive.import('package:angel_common/angel_common.dart')); var clazz = new ClassBuilder('${recase.pascalCase}Controller', - asExtends: new TypeBuilder('Controller')); + asExtends: refer('Controller')); // Add @Expose() clazz.addAnnotation(_expose('/$lower')); @@ -58,7 +58,7 @@ class ControllerCommand extends Command { // String foo() => 'bar'; var meth = new MethodBuilder('foo', - returns: literal('bar'), returnType: new TypeBuilder('String')); + returns: literal('bar'), returnType: refer('String')); meth.addAnnotation(_expose('/')); clazz.addMethod(meth); diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index ed60431d..4e058078 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -102,23 +102,23 @@ class ServiceCommand extends Command { import '../models/$lower.dart'; export '../models/$lower.dart'; */ - lib.addMember(new ImportBuilder('package:angel_common/angel_common.dart')); + lib.addMember(new Directive.import('package:angel_common/angel_common.dart')); generator.applyToLibrary(lib, name, lower); if (generator.createsModel == true || typed) { lib - ..addMember(new ImportBuilder('../models/$lower.dart')) + ..addMember(new Directive.import('../models/$lower.dart')) ..addMember(new ExportBuilder('../models/$lower.dart')); } // configureServer() {} var configureServer = new MethodBuilder('configureServer', - returnType: new TypeBuilder('AngelConfigurer')); + returnType: refer('AngelConfigurer')); generator.applyToConfigureServer(configureServer, name, lower); // return (Angel app) async {} var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync) - ..addPositional(parameter('app', [new TypeBuilder('Angel')])); + ..addPositional(parameter('app', [refer('Angel')])); generator.beforeService(closure, name, lower); // app.use('/api/todos', new MapService()); @@ -126,21 +126,21 @@ class ServiceCommand extends Command { if (typed == true) { service = - new TypeBuilder('TypedService', genericTypes: [new TypeBuilder(name)]) + refer('TypedService', genericTypes: [refer(name)]) .newInstance([service]); } - closure.addStatement(reference('app') + closure.addStatement(refer('app') .invoke('use', [literal('/api/${pluralize(lower)}'), service])); if (generator.injectsSingleton == true) { closure.addStatement(varField('service', - value: reference('app') + value: refer('app') .invoke('service', [literal('/api/${pluralize(lower)}')]).castAs( - new TypeBuilder('HookedService')))); - closure.addStatement(reference('app') + refer('HookedService')))); + closure.addStatement(refer('app') .property('container') - .invoke('singleton', [reference('service').property('inner')])); + .invoke('singleton', [refer('service').property('inner')])); } configureServer.addStatement(closure.asReturn()); diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index eb956d42..f6b70444 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -14,24 +14,26 @@ class MongoServiceGenerator extends ServiceGenerator { bool get createsModel => false; @override - void applyToConfigureServer( - MethodBuilder configureServer, String name, String lower) { - configureServer.addPositional(parameter('db', [new TypeBuilder('Db')])); + void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, + String name, String lower) { + configureServer.requiredParameters.add(new Parameter((b) => b + ..name = 'db' + ..type = refer('Db'))); } @override void applyToLibrary(LibraryBuilder library, String name, String lower) { - library.addMembers([ - new ImportBuilder('package:angel_mongo/angel_mongo.dart'), - new ImportBuilder('package:mongo_dart/mongo_dart.dart'), + library.directives.addAll([ + new Directive.import('package:angel_mongo/angel_mongo.dart'), + new Directive.import('package:mongo_dart/mongo_dart.dart'), ]); } @override - ExpressionBuilder createInstance( + Expression createInstance( MethodBuilder methodBuilder, String name, String lower) { - return new TypeBuilder('MongoService').newInstance([ - reference('db').invoke('collection', [literal(pluralize(lower))]) + return refer('MongoService').newInstance([ + refer('db').property('collection').call([literal(pluralize(lower))]) ]); } } diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 17c55b44..dced10e6 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -14,27 +14,32 @@ class RethinkServiceGenerator extends ServiceGenerator { bool get createsModel => false; @override - void applyToConfigureServer( - MethodBuilder configureServer, String name, String lower) { - configureServer - ..addPositional(parameter('connection', [new TypeBuilder('Connection')])) - ..addPositional(parameter('r', [new TypeBuilder('Rethinkdb')])); + void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, + String name, String lower) { + configureServer.requiredParameters.addAll([ + new Parameter((b) => b + ..name = 'connection' + ..type = refer('Connection')), + new Parameter((b) => b + ..name = 'r' + ..type = refer('Rethinkdb')), + ]); } @override void applyToLibrary(LibraryBuilder library, String name, String lower) { - library.addMembers([ + library.directives.addAll([ 'package:angel_rethink/angel_rethink.dart', 'package:rethinkdb_driver2/rethinkdb_driver2.dart' - ].map((str) => new ImportBuilder(str))); + ].map((str) => new Directive.import(str))); } @override - ExpressionBuilder createInstance( + Expression createInstance( MethodBuilder methodBuilder, String name, String lower) { - return new TypeBuilder('RethinkService').newInstance([ - reference('connection'), - reference('r').invoke('table', [literal(pluralize(lower))]) + return refer('RethinkService').newInstance([ + refer('connection'), + refer('r').property('table').call([literal(pluralize(lower))]) ]); } } diff --git a/lib/src/util.dart b/lib/src/util.dart index b1ea7ef7..8ca561c7 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:io/ansi.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:yamlicious/yamlicious.dart'; +//import 'package:yamlicious/yamlicious.dart'; final String checkmark = ansiOutputEnabled ? '\u2713' : '[Success]'; @@ -14,5 +14,6 @@ Future loadPubspec() { } Future savePubspec(Pubspec pubspec) async { - var text = toYamlString(pubspec); + // TODO: Save pubspec for real? + //var text = toYamlString(pubspec); } \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index b2f905a5..27327240 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: recase: ^1.0.0 watcher: ^0.9.7 yaml: ^2.0.0 - yamlicious: ^0.0.5 + #yamlicious: ^0.0.5 dev_dependencies: #build: ^0.7.0 #build_runner: ^0.3.0 From df0632d0004953ddccfab0cf3ac96f4da30adda9 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:13:32 -0400 Subject: [PATCH 074/132] Remove deprecated commands --- lib/src/commands/commands.dart | 5 - lib/src/commands/controller.dart | 69 ------ lib/src/commands/deprecated.dart | 10 - lib/src/commands/plugin.dart | 54 ---- lib/src/commands/service.dart | 242 ------------------ lib/src/commands/service_old.dart | 398 ------------------------------ lib/src/commands/start.dart | 141 ----------- lib/src/commands/test.dart | 85 ------- 8 files changed, 1004 deletions(-) delete mode 100644 lib/src/commands/controller.dart delete mode 100644 lib/src/commands/deprecated.dart delete mode 100644 lib/src/commands/plugin.dart delete mode 100644 lib/src/commands/service.dart delete mode 100644 lib/src/commands/service_old.dart delete mode 100644 lib/src/commands/start.dart delete mode 100644 lib/src/commands/test.dart diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 28534228..70a559de 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -1,15 +1,10 @@ library angel_cli.commands; -export 'controller.dart'; export "doctor.dart"; export "key.dart"; export "init.dart"; export "install.dart"; export "make.dart"; -export "plugin.dart"; export "rename.dart"; -export "service.dart"; -export "start.dart"; -export "test.dart"; export 'update.dart'; export 'version.dart'; \ No newline at end of file diff --git a/lib/src/commands/controller.dart b/lib/src/commands/controller.dart deleted file mode 100644 index fd3ff04d..00000000 --- a/lib/src/commands/controller.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'dart:io'; -import 'package:args/command_runner.dart'; -import 'package:code_builder/code_builder.dart'; -import "package:console/console.dart"; -import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:recase/recase.dart'; -import 'deprecated.dart'; - -class ControllerCommand extends Command { - final TextPen _pen = new TextPen(); - - @override - String get name => "controller"; - - @override - String get description => - "Creates a new controller within the given project."; - - @override - run() async { - warnDeprecated(this.name, _pen); - - final name = await readInput("Name of Controller: "), - recase = new ReCase(name), - lower = recase.snakeCase; - final controllersDir = new Directory("lib/src/routes/controllers"); - final controllerFile = - new File.fromUri(controllersDir.uri.resolve("$lower.dart")); - - if (!await controllerFile.exists()) - await controllerFile.create(recursive: true); - - await controllerFile.writeAsString( - _generateController(await Pubspec.load(Directory.current), recase)); - - _pen.green(); - _pen("${Icon.CHECKMARK} Successfully generated controller $name."); - _pen(); - } - - NewInstanceBuilder _expose(String path) => refer('Expose') - .constInstance([], namedArguments: {'path': literal(path)}); - - String _generateController(Pubspec pubspec, ReCase recase) { - var lower = recase.snakeCase; - var lib = new LibraryBuilder('${pubspec.name}.routes.controllers.$lower'); - lib.addDirective( - new Directive.import('package:angel_common/angel_common.dart')); - - var clazz = new ClassBuilder('${recase.pascalCase}Controller', - asExtends: refer('Controller')); - - // Add @Expose() - clazz.addAnnotation(_expose('/$lower')); - - // Add - // @Expose(path: '/') - // String foo() => 'bar'; - - var meth = new MethodBuilder('foo', - returns: literal('bar'), returnType: refer('String')); - meth.addAnnotation(_expose('/')); - clazz.addMethod(meth); - - lib.addMember(clazz); - - return prettyToSource(lib.buildAst()); - } -} diff --git a/lib/src/commands/deprecated.dart b/lib/src/commands/deprecated.dart deleted file mode 100644 index 992da105..00000000 --- a/lib/src/commands/deprecated.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:console/console.dart'; - -void warnDeprecated(String command, [TextPen pen]) { - pen ??= new TextPen(); - pen - ..yellow() - ..call('The `$command` command is deprecated, and will be removed by v1.2.0.') - ..call() - ..reset(); -} diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart deleted file mode 100644 index d8facda9..00000000 --- a/lib/src/commands/plugin.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'dart:io'; -import 'package:args/command_runner.dart'; -import "package:console/console.dart"; -import 'package:dart_style/dart_style.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:recase/recase.dart'; -import 'deprecated.dart'; - -class PluginCommand extends Command { - final TextPen _pen = new TextPen(); - - @override - String get name => "plugin"; - - @override - String get description => "Creates a new plugin within the given project."; - - @override - run() async { - warnDeprecated(this.name, _pen); - - var pubspec = await Pubspec.load(Directory.current); - final name = await readInput("Name of Plugin: "), - lower = new ReCase(name).snakeCase; - final testDir = new Directory("lib/src/config/plugins"); - final pluginFile = new File.fromUri(testDir.uri.resolve("$lower.dart")); - - if (!await pluginFile.exists()) await pluginFile.create(recursive: true); - - await pluginFile.writeAsString( - new DartFormatter().format(_generatePlugin(pubspec, name, lower))); - - _pen.green(); - _pen("${Icon.CHECKMARK} Successfully generated plugin $name."); - _pen(); - } - - String _generatePlugin(Pubspec pubspec, String name, String lower) { - return ''' -library ${pubspec.name}.config.plugins.$lower; - -import 'dart:async'; -import 'package:angel_framework/angel_framework.dart'; - -class $name extends AngelPlugin { - @override - Future call(Angel app) async { - // Work some magic... - } -} - ''' - .trim(); - } -} diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart deleted file mode 100644 index 4e058078..00000000 --- a/lib/src/commands/service.dart +++ /dev/null @@ -1,242 +0,0 @@ -import 'dart:io'; -import 'package:args/command_runner.dart'; -import 'package:code_builder/code_builder.dart'; -import 'package:console/console.dart'; -import 'package:dart_style/dart_style.dart'; -import 'package:inflection/inflection.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:recase/recase.dart'; -import 'service_generators/service_generators.dart'; -import 'deprecated.dart'; -import 'init.dart' show preBuild; - -class ServiceCommand extends Command { - final TextPen _pen = new TextPen(); - - @override - String get name => 'service'; - - @override - String get description => 'Creates a new service within the given project.'; - - @override - run() async { - warnDeprecated(this.name, _pen); - - var pubspec = await Pubspec.load(Directory.current); - var name = await readInput('Name of Service (not plural): '); - var chooser = new Chooser( - serviceGenerators.map((g) => g.name).toList(), - message: 'What type of service would you like to create? '); - var type = await chooser.choose(); - - print('Wrap this service in a TypedService? (slight performance cost)'); - chooser = new Chooser(['Yes', 'No']); - var typed = (await chooser.choose()) == 'Yes'; - - var generator = - serviceGenerators.firstWhere((g) => g.name == type, orElse: () => null); - - if (generator == null) { - _pen.blue(); - _pen('${Icon.STAR} \'$type\' services are not yet implemented. :('); - _pen(); - } else { - var rc = new ReCase(name); - name = rc.pascalCase; - var lower = rc.snakeCase; - var servicesDir = new Directory('lib/src/services'); - var serviceFile = - new File.fromUri(servicesDir.uri.resolve('$lower.dart')); - var testDir = new Directory('test/services'); - var testFile = - new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); - - if (!await servicesDir.exists()) - await servicesDir.create(recursive: true); - if (!await testDir.exists()) await testDir.create(recursive: true); - - var fmt = new DartFormatter(); - - await serviceFile - .writeAsString(_generateService(generator, name, lower, typed)); - await testFile.writeAsString(_generateTests(pubspec, lower, fmt)); - - var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); - - if (!await runConfig.exists()) { - await runConfig.create(recursive: true); - await runConfig.writeAsString(_generateRunConfiguration(name, lower)); - } - - if (generator.createsModel == true || typed == true) { - await _generateModel(pubspec, name, lower, fmt); - } - - if (generator.createsValidator == true) { - await _generateValidator(pubspec, lower, rc, fmt); - } - - if (generator.exportedInServiceLibrary == true || typed == true) { - var serviceLibrary = new File('lib/src/models/models.dart'); - await serviceLibrary.writeAsString("\nexport '$lower.dart';", - mode: FileMode.APPEND); - } - - if (generator.shouldRunBuild == true) { - await preBuild(Directory.current); - } - - _pen.green(); - _pen('${Icon.CHECKMARK} Successfully generated service $name.'); - _pen(); - } - } - - String _generateService( - ServiceGenerator generator, String name, String lower, bool typed) { - var lib = new LibraryBuilder(); - - /* - import 'package:angel_framework/angel_framework.dart'; - import '../models/$lower.dart'; - export '../models/$lower.dart'; - */ - lib.addMember(new Directive.import('package:angel_common/angel_common.dart')); - generator.applyToLibrary(lib, name, lower); - - if (generator.createsModel == true || typed) { - lib - ..addMember(new Directive.import('../models/$lower.dart')) - ..addMember(new ExportBuilder('../models/$lower.dart')); - } - - // configureServer() {} - var configureServer = new MethodBuilder('configureServer', - returnType: refer('AngelConfigurer')); - generator.applyToConfigureServer(configureServer, name, lower); - - // return (Angel app) async {} - var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync) - ..addPositional(parameter('app', [refer('Angel')])); - generator.beforeService(closure, name, lower); - - // app.use('/api/todos', new MapService()); - var service = generator.createInstance(closure, name, lower); - - if (typed == true) { - service = - refer('TypedService', genericTypes: [refer(name)]) - .newInstance([service]); - } - - closure.addStatement(refer('app') - .invoke('use', [literal('/api/${pluralize(lower)}'), service])); - - if (generator.injectsSingleton == true) { - closure.addStatement(varField('service', - value: refer('app') - .invoke('service', [literal('/api/${pluralize(lower)}')]).castAs( - refer('HookedService')))); - closure.addStatement(refer('app') - .property('container') - .invoke('singleton', [refer('service').property('inner')])); - } - - configureServer.addStatement(closure.asReturn()); - - lib.addMember(configureServer); - - return prettyToSource(lib.buildAst()); - } - - _generateModel( - Pubspec pubspec, String name, String lower, DartFormatter fmt) async { - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(fmt.format(''' -library ${pubspec.name}.models.$lower; -import 'package:angel_model/angel_model.dart'; - -class $name extends Model { - @override - String id; - String name, description; - @override - DateTime createdAt, updatedAt; - - $name({this.id, this.name, this.description, this.createdAt, this.updatedAt}); -} - ''')); - } - - _generateValidator( - Pubspec pubspec, String lower, ReCase rc, DartFormatter fmt) async { - var file = new File('lib/src/validators/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(fmt.format(''' -library ${pubspec.name}.validtors.$lower; -import 'package:angel_validate/angel_validate.dart'; - -final Validator ${rc.camelCase} = new Validator({ - 'name': [isString, isNotEmpty], - 'description': [isString, isNotEmpty] -}); - -final Validator create${rc.pascalCase} = ${rc.camelCase}.extend({}) - ..requiredFields.addAll(['name', 'description']); - ''')); - } - - _generateRunConfiguration(String name, String lower) { - return ''' - - - - -''' - .trim(); - } - - _generateTests(Pubspec pubspec, String lower, DartFormatter fmt) { - return fmt.format(''' -import 'dart:io'; -import 'package:${pubspec.name}/${pubspec.name}.dart'; -import 'package:angel_common/angel_common.dart'; -import 'package:angel_test/angel_test.dart'; -import 'package:test/test.dart'; - -main() async { - Angel app; - TestClient client; - - setUp(() async { - app = await createServer(); - client = await connectTo(app); - }); - - tearDown(() async { - await client.close(); - app = null; - }); - - test('index via REST', () async { - var response = await client.get('/api/${pluralize(lower)}'); - expect(response, hasStatus(HttpStatus.OK)); - }); - - test('Index ${pluralize(lower)}', () async { - var ${pluralize(lower)} = await client.service('api/${pluralize(lower)}').index(); - print(${pluralize(lower)}); - }); -} - - '''); - } -} diff --git a/lib/src/commands/service_old.dart b/lib/src/commands/service_old.dart deleted file mode 100644 index ad970320..00000000 --- a/lib/src/commands/service_old.dart +++ /dev/null @@ -1,398 +0,0 @@ -import 'dart:io'; -import 'package:args/command_runner.dart'; -import 'package:console/console.dart'; -import 'package:id/id.dart'; -import 'package:recase/recase.dart'; -import 'init.dart' show preBuild; - -class ServiceCommand extends Command { - final String CUSTOM = 'Custom'; - final String MEMORY = 'In-Memory'; - final String MEMORY_JSON = 'In-Memory (serialized via `source_gen`)'; - final String MONGO = 'MongoDB'; - final String MONGO_TYPED = 'MongoDB (typed)'; - final String MONGO_TYPED_JSON = - 'MongoDB (typed, serialized via `source_gen`)'; - final String TRESTLE = 'Trestle'; - final TextPen _pen = new TextPen(); - - @override - String get name => 'service'; - - @override - String get description => 'Creates a new service within the given project.'; - - String _snake(name) => idFromString(name).snake; - - @override - run() async { - var name = await readInput('Name of Service (not plural): '); - var chooser = new Chooser([MONGO, MONGO_TYPED, MEMORY, CUSTOM], - message: 'What type of service would you like to create? '); - var type = await chooser.choose(); - - fail() { - _pen.red(); - _pen('Could not successfully create service $name.'); - _pen(); - } - - String serviceSource = ''; - - if (type == MONGO) { - serviceSource = _generateMongoService(name); - } else if (type == MONGO_TYPED) { - serviceSource = _generateMongoTypedService(name); - await _generateMongoModel(name); - await _generateValidator(name); - } else if (type == MONGO_TYPED_JSON) { - serviceSource = _generateMongoTypedService(name); - await _generateMongoModelJson(name); - await _generateValidator(name); - } else if (type == MEMORY) { - serviceSource = _generateMemoryService(name); - await _generateMemoryModel(name); - await _generateValidator(name); - } else if (type == MEMORY_JSON) { - serviceSource = _generateMemoryService(name); - await _generateMemoryModelJson(name); - await _generateValidator(name); - } else if (type == CUSTOM) { - serviceSource = _generateCustomService(name); - } else if (type == TRESTLE) { - _pen.blue(); - _pen('${Icon.STAR} Trestle services are not yet implemented. :('); - _pen(); - } else { - print('Code to generate a $type service is not yet written.'); - } - - if (serviceSource.isEmpty) { - fail(); - throw new Exception('Empty generated service code.'); - } - - var lower = _snake(name); - var servicesDir = new Directory('lib/src/services'); - var serviceFile = new File.fromUri(servicesDir.uri.resolve('$lower.dart')); - var testDir = new Directory('test/services'); - var testFile = new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); - - if (!await servicesDir.exists()) await servicesDir.create(recursive: true); - - if (!await testDir.exists()) await testDir.create(recursive: true); - - await serviceFile.writeAsString(serviceSource); - - if (type == MONGO_TYPED || type == MEMORY) { - var serviceLibrary = new File('lib/src/models/models.dart'); - await serviceLibrary.writeAsString("\nexport '$lower.dart';", - mode: FileMode.APPEND); - await preBuild(Directory.current); - } - - await testFile.writeAsString(_generateTests(name, type)); - - var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); - - if (!await runConfig.exists()) { - await runConfig.create(recursive: true); - await runConfig.writeAsString(_generateRunConfiguration(name)); - } - - _pen.green(); - _pen('${Icon.CHECKMARK} Successfully generated service $name.'); - _pen(); - } - - _generateValidator(String name) async { - var rc = new ReCase(name); - var file = new File('lib/src/validators/${rc.snakeCase}.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -import 'package:angel_validate/angel_validate.dart'; - -final Validator CREATE_${rc.constantCase} = - new Validator({'name*': isString, 'desc*': isString}); - ''' - .trim()); - } - - _generateCustomService(String name) { - return ''' -import 'package:angel_framework/angel_framework.dart'; - -class ${name}Service extends Service { - ${name}Service():super() { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateMemoryModel(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_framework/common.dart'; - -class $name extends Model { - String name, desc; - - $name({String id, this.name, this.desc}) { - this.id = id; - } -} - ''' - .trim()); - } - - _generateMemoryModelJson(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_framework/common.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMemoryService(String name) { - var rc = new ReCase(name); - var lower = rc.snakeCase; - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import '../models/$lower.dart'; -export '../models/$lower.dart'; - -/// Manages [$name] in-memory. -class ${name}Service extends MemoryService<$name> { - ${name}Service():super() { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateMongoModel(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_mongo/model.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMongoModelJson(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_framework/common.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMongoService(String name) { - var lower = _snake(name); - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_mongo/angel_mongo.dart'; -import 'package:angel_validate/angel_validate.dart'; -import 'package:angel_validate/server.dart'; -import 'package:mongo_dart/mongo_dart.dart'; - -final Validator ${lower}Schema = new Validator({ - 'name*': [isString, isNotEmpty], - 'desc*': [isString, isNotEmpty] -}); - -configureServer(Db db) { - return (Angel app) async { - app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - - HookedService service = app.service('api/${lower}s'); - app.container.singleton(service.inner); - - service - ..beforeCreate.listen(validateEvent(${lower}Schema)) - ..beforeUpdate.listen(validateEvent(${lower}Schema)); - }; -} - -/// Manages [$name] in the database. -class ${name}Service extends MongoService { - ${name}Service(collection):super(collection) { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateMongoTypedService(String name) { - var lower = _snake(name); - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_mongo/angel_mongo.dart'; -import 'package:mongo_dart/mongo_dart.dart'; -import '../models/$lower.dart'; -export '../models/$lower.dart'; - -configureServer(Db db) { - return (Angel app) async { - app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - - HookedService service = app.service('api/${lower}s'); - app.container.singleton(service.inner); - }; -} - -/// Manages [$name] in the database. -class ${name}Service extends MongoTypedService<$name> { - ${name}Service(collection):super(collection) { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateRunConfiguration(String name) { - var lower = _snake(name); - - return ''' - - - - -''' - .trim(); - } - - _generateTests(String name, String type) { - var lower = _snake(name); - - return ''' -import 'dart:io'; -import 'package:angel/angel.dart'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_test/angel_test.dart'; -import 'package:test/test.dart'; - -main() async { - Angel app; - TestClient client; - - setUp(() async { - app = await createServer(); - client = await connectTo(app); - }); - - tearDown(() async { - await client.close(); - app = null; - }); - - test('index via REST', () async { - var response = await client.get('/api/${lower}s'); - expect(response, hasStatus(HttpStatus.OK)); - }); - - test('Index ${lower}s', () async { - var ${lower}s = await client.service('api/${lower}s').index(); - print(${lower}s); - }); -} - - ''' - .trim(); - } -} diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart deleted file mode 100644 index 696ebf09..00000000 --- a/lib/src/commands/start.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:args/command_runner.dart'; -import 'package:watcher/watcher.dart'; -import 'package:yaml/yaml.dart'; -import 'deprecated.dart'; -import 'pub.dart'; - -Process server; -bool watching = false; - -class StartCommand extends Command { - @override - String get name => 'start'; - - @override - String get description => - 'Runs any `start` scripts, and then runs the server.'; - - StartCommand() : super() { - argParser - ..addFlag('multi', - help: 'Starts bin/multi_server.dart, instead of the standard server.', - negatable: false, - defaultsTo: false) - ..addFlag('production', - help: 'Starts the server in production mode.', - negatable: false, - defaultsTo: false) - ..addFlag('watch', - abbr: 'w', - help: 'Restart the server on file changes.', - defaultsTo: true); - } - - @override - run() async { - warnDeprecated(this.name); - - stderr - ..writeln( - 'WARNING: `angel start` is now deprecated, in favor of `package:angel_hot`.') - ..writeln( - 'This new alternative supports hot reloading, which is faster and more reliable.') - ..writeln() - ..writeln('Find it on Pub: https://pub.dartlang.org/packages/angel_hot'); - - if (argResults['watch']) { - try { - new DirectoryWatcher('bin').events.listen((_) async => start()); - } catch (e) { - // Fail silently... - } - - try { - new DirectoryWatcher('config').events.listen((_) async => start()); - } catch (e) { - // Fail silently... - } - - try { - new DirectoryWatcher('lib').events.listen((_) async => start()); - } catch (e) { - // Fail silently... - } - } - - return await start(); - } - - start() async { - bool isNew = true; - if (server != null) { - isNew = false; - - if (!server.kill()) { - throw new Exception('Could not kill existing server process.'); - } - } - - final pubspec = new File('pubspec.yaml'); - - if (await pubspec.exists()) { - // Run start scripts - final doc = loadYamlDocument(await pubspec.readAsString()); - final scriptsNode = doc.contents.value['scripts']; - - if (scriptsNode != null && scriptsNode.containsKey('start')) { - try { - var scripts = await Process.start( - resolvePub(), ['global', 'run', 'scripts', 'start']); - listen(scripts.stdout, stdout); - listen(scripts.stderr, stderr); - int code = await scripts.exitCode; - - if (code != 0) { - throw new Exception('`scripts start` failed with exit code $code.'); - } - } catch (e) { - // No scripts? No problem... - } - } - } - - if (isNew) - print('Starting server...'); - else - print('Changes detected - restarting server...'); - - final env = {}; - - if (argResults['production']) env['ANGEL_ENV'] = 'production'; - - server = await Process.start( - Platform.executable, - [ - argResults['multi'] == true - ? 'bin/multi_server.dart' - : 'bin/server.dart' - ], - environment: env); - - try { - listen(server.stdout, stdout); - listen(server.stderr, stderr); - } catch (e) { - print(e); - } - - if (!isNew) { - print( - '${new DateTime.now().toIso8601String()}: Successfully restarted server.'); - } - - exitCode = await server.exitCode; - } -} - -void listen(Stream> stream, IOSink sink) { - stream.listen(sink.add); -} diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart deleted file mode 100644 index d973a85c..00000000 --- a/lib/src/commands/test.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'dart:io'; -import 'package:args/command_runner.dart'; -import "package:console/console.dart"; -import 'package:dart_style/dart_style.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:recase/recase.dart'; -import 'deprecated.dart'; - -class TestCommand extends Command { - final TextPen _pen = new TextPen(); - - @override - String get name => "test"; - - @override - String get description => "Creates a new test within the given project."; - - @override - run() async { - warnDeprecated(this.name, _pen); - - final name = await readInput("Name of Test: "), - lower = new ReCase(name).snakeCase; - final testDir = new Directory("test/services"); - final testFile = - new File.fromUri(testDir.uri.resolve("${lower}_test.dart")); - - if (!await testFile.exists()) await testFile.create(recursive: true); - - await testFile.writeAsString(new DartFormatter() - .format(_generateTest(await Pubspec.load(Directory.current), lower))); - - final runConfig = new File('./.idea/runConfigurations/${name}_tests.xml'); - - if (!await runConfig.exists()) { - await runConfig.create(recursive: true); - await runConfig.writeAsString(_generateRunConfiguration(name)); - } - - _pen.green(); - _pen("${Icon.CHECKMARK} Successfully generated test $name."); - _pen(); - } - - _generateRunConfiguration(String name) { - final lower = name.toLowerCase(); - - return ''' - - - - -''' - .trim(); - } - - String _generateTest(Pubspec pubspec, String lower) { - return ''' -import 'dart:io'; -import 'package:${pubspec.name}/${pubspec.name}.dart'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_test/angel_test.dart'; -import 'package:test/test.dart'; - -main() async { - TestClient client; - - setUp(() async { - var app = await createServer(); - client = await connectTo(app); - }); - - tearDown(() => client.close()); - - test('$lower', () async { - final response = await client.get('/$lower'); - expect(response, hasStatus(HttpStatus.OK)); - }); -} - ''' - .trim(); - } -} From 1cc9e0b83a6aa16f9dd8acc9945768c35a66fd32 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:16:14 -0400 Subject: [PATCH 075/132] Update Doctor command --- lib/src/commands/doctor.dart | 14 +++++--------- lib/src/util.dart | 2 ++ pubspec.yaml | 7 ------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/src/commands/doctor.dart b/lib/src/commands/doctor.dart index 836ab723..ab45e515 100644 --- a/lib/src/commands/doctor.dart +++ b/lib/src/commands/doctor.dart @@ -1,10 +1,10 @@ import "dart:convert"; import "dart:io"; import "package:args/command_runner.dart"; -import "package:console/console.dart"; +import 'package:io/ansi.dart'; +import '../util.dart'; class DoctorCommand extends Command { - final TextPen _pen = new TextPen(); @override String get name => "doctor"; @@ -23,16 +23,12 @@ class DoctorCommand extends Command { try { var git = await Process.start("git", ["--version"]); if (await git.exitCode == 0) { - var version = await git.stdout.transform(UTF8.decoder).join(); - _pen.green(); - _pen("${Icon.CHECKMARK} Git executable found: v${version.replaceAll('git version', '').trim()}"); - _pen(); + var version = await git.stdout.transform(utf8.decoder).join(); + print(green.wrap("$checkmark Git executable found: v${version.replaceAll('git version', '').trim()}")) } else throw new Exception("Git executable exit code not 0"); } catch (exc) { - _pen.red(); - _pen("${Icon.BALLOT_X} Git executable not found"); - _pen(); + print(red.wrap("$ballot Git executable not found")); } } } diff --git a/lib/src/util.dart b/lib/src/util.dart index 8ca561c7..588e3ad3 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -6,6 +6,8 @@ import 'package:pubspec_parse/pubspec_parse.dart'; final String checkmark = ansiOutputEnabled ? '\u2713' : '[Success]'; +final String ballot = ansiOutputEnabled ? '\u2717' : '[Failure]'; + Future loadPubspec() { var file = new File('pubspec.yaml'); return file diff --git a/pubspec.yaml b/pubspec.yaml index 27327240..6d62c7e5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,8 +7,6 @@ dependencies: # analyzer: ^0.29.0 args: ^1.0.0 code_builder: ^3.0.0 - #console: ^2.2.3 - dart2_constant: ^1.0.0 dart_style: ^1.0.0 glob: ^1.1.0 homedir: ^0.0.4 @@ -16,7 +14,6 @@ dependencies: id: ^1.0.0 io: ^0.3.2 inflection: ^0.4.1 - #mustache4dart: ^2.1.0 path: ^1.0.0 prompts: ^1.0.0 pubspec_parse: ^0.1.2 @@ -25,10 +22,6 @@ dependencies: watcher: ^0.9.7 yaml: ^2.0.0 #yamlicious: ^0.0.5 -dev_dependencies: - #build: ^0.7.0 - #build_runner: ^0.3.0 - #check_for_update: ^1.0.0 environment: sdk: ">=2.0.0-dev <3.0.0" executables: From d0d05e4eaae98aff2db30aa3c418f706e8d6b8a7 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:27:21 -0400 Subject: [PATCH 076/132] Update init command --- lib/src/commands/doctor.dart | 4 +- lib/src/commands/init.dart | 114 ++++++++++++++--------------------- 2 files changed, 48 insertions(+), 70 deletions(-) diff --git a/lib/src/commands/doctor.dart b/lib/src/commands/doctor.dart index ab45e515..b435b6f0 100644 --- a/lib/src/commands/doctor.dart +++ b/lib/src/commands/doctor.dart @@ -5,7 +5,6 @@ import 'package:io/ansi.dart'; import '../util.dart'; class DoctorCommand extends Command { - @override String get name => "doctor"; @@ -24,7 +23,8 @@ class DoctorCommand extends Command { var git = await Process.start("git", ["--version"]); if (await git.exitCode == 0) { var version = await git.stdout.transform(utf8.decoder).join(); - print(green.wrap("$checkmark Git executable found: v${version.replaceAll('git version', '').trim()}")) + print(green.wrap( + "$checkmark Git executable found: v${version.replaceAll('git version', '').trim()}")); } else throw new Exception("Git executable exit code not 0"); } catch (exc) { diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index cb5480a9..c7da329e 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -1,15 +1,17 @@ +import 'dart:async'; import "dart:io"; import "package:args/command_runner.dart"; -import "package:console/console.dart"; -import 'package:random_string/random_string.dart' as rs; +import 'package:io/ansi.dart'; import 'package:path/path.dart' as p; +import 'package:prompts/prompts.dart' as prompts; +import 'package:random_string/random_string.dart' as rs; +import '../util.dart'; import 'key.dart'; import 'pub.dart'; import 'rename.dart'; class InitCommand extends Command { final KeyCommand _key = new KeyCommand(); - final TextPen _pen = new TextPen(); @override String get name => "init"; @@ -19,14 +21,7 @@ class InitCommand extends Command { "Initializes a new Angel project in the current directory."; InitCommand() { - 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, - ); + argParser..addFlag('pub-get', defaultsTo: true); } @override @@ -59,26 +54,27 @@ class InitCommand extends Command { await _pubGet(projectDir); } - _pen.green(); - _pen("${Icon.CHECKMARK} Successfully initialized Angel project."); - _pen(); - _pen - ..reset() - ..text('\nCongratulations! You are ready to start developing with Angel!') - ..text('\nTo start the server (with file watching), run ') - ..magenta() - ..text(argResults['legacy'] ? '`dart bin/server.dart`' : '`dart bin/dev.dart`') - ..normal() - ..text(' in your terminal.') - ..text('\n\nFind more documentation about Angel:') - ..text('\n * https://angel-dart.github.io') - ..text('\n * https://github.com/angel-dart/angel/wiki') - ..text( - '\n * https://www.youtube.com/playlist?list=PLl3P3tmiT-frEV50VdH_cIrA2YqIyHkkY') - ..text('\n * https://medium.com/the-angel-framework') - ..text('\n * https://dart.academy/tag/angel') - ..text('\n\nHappy coding!') - ..call(); + print(green.wrap("$checkmark Successfully initialized Angel project.")); + + stdout + ..writeln() + ..writeln( + 'Congratulations! You are ready to start developing with Angel!') + ..write('To start the server (with ') + ..write(cyan.wrap('hot-reloading')) + ..write('), run ') + ..write(magenta.wrap('`dart --observe bin/dev.dart`')) + ..writeln(' in your terminal') + ..writeln() + ..writeln('Find more documentation about Angel:') + ..writeln(' * https://angel-dart.github.io') + ..writeln(' * https://github.com/angel-dart/angel/wiki') + ..writeln( + ' * https://www.youtube.com/playlist?list=PLl3P3tmiT-frEV50VdH_cIrA2YqIyHkkY') + ..writeln(' * https://medium.com/the-angel-framework') + ..writeln(' * https://dart.academy/tag/angel') + ..writeln() + ..writeln('Happy coding!'); } _deleteRecursive(FileSystemEntity entity, [bool self = true]) async { @@ -101,9 +97,9 @@ class InitCommand extends Command { var stat = await FileStat.stat(path); switch (stat.type) { - case FileSystemEntityType.DIRECTORY: + case FileSystemEntityType.directory: return await _deleteRecursive(new Directory(path)); - case FileSystemEntityType.FILE: + case FileSystemEntityType.file: return await _deleteRecursive(new File(path)); default: break; @@ -114,11 +110,10 @@ class InitCommand extends Command { _cloneRepo(Directory projectDir) async { try { if (await projectDir.exists()) { - var chooser = new Chooser(["Yes", "No"], - message: - "Directory '${projectDir.absolute.path}' already exists. Overwrite it? (Yes/No)"); + var shouldDelete = prompts.getBool( + "Directory '${projectDir.absolute.path}' already exists. Overwrite it?"); - if (await chooser.choose() != "Yes") + if (shouldDelete) throw new Exception("Chose not to overwrite existing directory."); else if (projectDir.absolute.uri.normalizePath().toFilePath() != Directory.current.absolute.uri.normalizePath().toFilePath()) @@ -129,10 +124,9 @@ class InitCommand extends Command { } print('Choose a project type before continuing:'); - var boilerplateChooser = new Chooser( - argResults['legacy'] ? legacyBoilerplates : boilerplates, - ); - var boilerplate = await boilerplateChooser.choose(); + + var boilerplate = prompts.choose( + 'Choose a project type before continuing', boilerplates); print( 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); @@ -157,13 +151,14 @@ class InitCommand extends Command { } } + if (boilerplate.needsPrebuild) { + await preBuild(projectDir).catchError((_) => null); + } + var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git")); if (await gitDir.exists()) await gitDir.delete(recursive: true); } catch (e) { - print(e); - _pen.red(); - _pen("${Icon.BALLOT_X} Could not initialize Angel project."); - _pen(); + print(red.wrap("$ballot Could not initialize Angel project.")); rethrow; } } @@ -180,11 +175,11 @@ class InitCommand extends Command { } } -preBuild(Directory projectDir) async { +Future preBuild(Directory projectDir) async { // Run build - print('Pre-building resources...'); + print('Running `pub run build_runner build`...'); - var build = await Process.start(Platform.executable, ['tool/build.dart'], + var build = await Process.start(resolvePub(), ['run', 'build'], workingDirectory: projectDir.absolute.path); stdout.addStream(build.stdout); @@ -195,31 +190,12 @@ preBuild(Directory projectDir) async { if (buildCode != 0) throw new Exception('Failed to pre-build resources.'); } -const BoilerplateInfo fullApplicationBoilerplate = const BoilerplateInfo( - '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', -); - const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( 'ORM', "A starting point for applications that use Angel's ORM.", 'https://github.com/angel-dart/boilerplate_orm.git', ); -const List legacyBoilerplates = const [ - fullApplicationBoilerplate, - lightBoilerplate, - ormBoilerplate -]; - const BoilerplateInfo basicBoilerplate = const BoilerplateInfo( 'Basic', 'Minimal starting point - A simple server with only a few additional packages.', @@ -233,8 +209,10 @@ const List boilerplates = const [ class BoilerplateInfo { final String name, description, url, ref; + final bool needsPrebuild; - const BoilerplateInfo(this.name, this.description, this.url, {this.ref}); + const BoilerplateInfo(this.name, this.description, this.url, + {this.ref, this.needsPrebuild: false}); @override String toString() => '$name ($description)'; From 7a5a2d840149af9a5f1c4b43b2871db5a6432eea Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:27:54 -0400 Subject: [PATCH 077/132] Remove all references to dead commands --- bin/angel.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bin/angel.dart b/bin/angel.dart index ccb0b146..8f066e1b 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -12,15 +12,10 @@ main(List args) async { new CommandRunner("angel", "Command-line tools for the Angel framework."); runner - ..addCommand(new ControllerCommand()) ..addCommand(new DoctorCommand()) ..addCommand(new KeyCommand()) - ..addCommand(new ServiceCommand()) ..addCommand(new InitCommand()) ..addCommand(new InstallCommand()) - ..addCommand(new TestCommand()) - ..addCommand(new PluginCommand()) - ..addCommand(new StartCommand()) ..addCommand(new RenameCommand()) ..addCommand(new UpdateCommand()) ..addCommand(new MakeCommand()) From 9dca056e5224207d7afd5373e971024bfb6db610 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:35:45 -0400 Subject: [PATCH 078/132] Update install command --- lib/src/commands/install.dart | 66 +++++++++++++---------------------- lib/src/util.dart | 7 ++-- pubspec.yaml | 1 + 3 files changed, 29 insertions(+), 45 deletions(-) diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index b81a6008..50746b7d 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -1,13 +1,15 @@ 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:io/ansi.dart'; import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as p; +import 'package:prompts/prompts.dart' as prompts; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart' as yaml; +import '../util.dart'; import 'make/maker.dart'; class InstallCommand extends Command { @@ -46,22 +48,22 @@ class InstallCommand extends Command { @override run() async { - if (argResults['wipe']) { + if (argResults['wipe'] as bool) { if (await installRepo.exists()) await installRepo.delete(recursive: true); - } else if (argResults['list']) { + } else if (argResults['list'] as bool) { 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']) { + } else if (argResults['update'] as bool) { 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); + var pubspec = await loadPubspec(); for (var packageName in argResults.rest) { var packageDir = @@ -78,23 +80,21 @@ class InstallCommand extends Command { List globs = []; - var projectPubspec = await Pubspec.load(packageDir); + var projectPubspec = await loadPubspec(packageDir); 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; - }) + var dep = projectPubspec.dependencies[k]; + if (dep is HostedDependency) + return new MakerDependency(k, dep.version.toString()); + return null; + }) .where((d) => d != null) .toList(); deps.addAll(projectPubspec.devDependencies.keys.map((k) { var dep = projectPubspec.devDependencies[k]; - if (dep is HostedReference) - return new MakerDependency(k, dep.versionConstraint.toString(), - dev: true); + if (dep is HostedDependency) + return new MakerDependency(k, dep.version.toString(), dev: true); return null; }).where((d) => d != null)); @@ -110,7 +110,8 @@ class InstallCommand extends Command { // Loads globs if (cfg['templates'] is List) { - globs.addAll(cfg['templates'].map((p) => new Glob(p))); + globs.addAll( + (cfg['templates'] as List).map((p) => new Glob(p.toString()))); } if (cfg['values'] is Map) { @@ -120,25 +121,9 @@ class InstallCommand extends Command { 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); + values[key] = prompts.get(desc.toString(), defaultsTo: val[key]['default']?.toString()); } else if (val[key]['type'] == 'choice') { - var chooser = - new Chooser(val[key]['choices'], message: '$desc: '); - values[key] = await chooser.choose(); + values[key] = prompts.choose(desc.toString(), val[key]['choices'] as Iterable); } } } @@ -164,10 +149,7 @@ class InstallCommand extends Command { 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'; + allClear = prompts.getBool('Overwrite the existing file?'); if (allClear) await targetFile.delete(); } @@ -176,12 +158,12 @@ class InstallCommand extends Command { 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}...'); + 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); + await targetFile.writeAsString(generated.toString()); } else { print( 'Copying ${entity.absolute.path} to ${targetFile.absolute.path}...'); @@ -216,7 +198,7 @@ class InstallCommand extends Command { await for (var entity in installRepo.list()) { if (entity is Directory) { try { - repos.add(await Pubspec.load(entity)); + repos.add(await loadPubspec(entity)); } catch (_) { // Ignore failures... } diff --git a/lib/src/util.dart b/lib/src/util.dart index 588e3ad3..e40b0d29 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -8,8 +8,9 @@ final String checkmark = ansiOutputEnabled ? '\u2713' : '[Success]'; final String ballot = ansiOutputEnabled ? '\u2717' : '[Failure]'; -Future loadPubspec() { - var file = new File('pubspec.yaml'); +Future loadPubspec([Directory directory]) { + directory ??= Directory.current; + var file = new File.fromUri(directory.uri.resolve('pubspec.yaml')); return file .readAsString() .then((yaml) => new Pubspec.parse(yaml, sourceUrl: file.uri)); @@ -18,4 +19,4 @@ Future loadPubspec() { Future savePubspec(Pubspec pubspec) async { // TODO: Save pubspec for real? //var text = toYamlString(pubspec); -} \ No newline at end of file +} diff --git a/pubspec.yaml b/pubspec.yaml index 6d62c7e5..06546a9f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: id: ^1.0.0 io: ^0.3.2 inflection: ^0.4.1 + mustache4dart: ^2.0.0 path: ^1.0.0 prompts: ^1.0.0 pubspec_parse: ^0.1.2 From 997344488c870c90b6b7fa60ba3e249e8eb5a65e Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:36:10 -0400 Subject: [PATCH 079/132] Remove update+version commands --- bin/angel.dart | 4 +-- lib/src/commands/update.dart | 68 ----------------------------------- lib/src/commands/version.dart | 13 ------- 3 files changed, 1 insertion(+), 84 deletions(-) delete mode 100644 lib/src/commands/update.dart delete mode 100644 lib/src/commands/version.dart diff --git a/bin/angel.dart b/bin/angel.dart index 8f066e1b..cc11c968 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -17,9 +17,7 @@ main(List args) async { ..addCommand(new InitCommand()) ..addCommand(new InstallCommand()) ..addCommand(new RenameCommand()) - ..addCommand(new UpdateCommand()) - ..addCommand(new MakeCommand()) - ..addCommand(new VersionCommand()); + ..addCommand(new MakeCommand()); return await runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); diff --git a/lib/src/commands/update.dart b/lib/src/commands/update.dart deleted file mode 100644 index d0e5c21c..00000000 --- a/lib/src/commands/update.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:io'; -import 'package:args/command_runner.dart'; -import 'package:console/console.dart'; -import 'package:http/http.dart' as http; -import 'pubspec.update.g.dart'; -import 'pub.dart'; - -class UpdateCommand extends Command { - @override - String get name => 'update'; - - @override - String get description => 'Updates the Angel CLI, if an update is available.'; - - @override - run() async { - stdout.write('Checking for update... '); - - try { - var client = new http.Client(); - var update = await checkForUpdate(client); - client.close(); - - if (update != null) { - stdout.writeln(); - var pen = new TextPen(); - pen.cyan(); - pen.text( - 'ATTENTION: There is a new version of the Angel CLI available (version $update).'); - pen.call(); - var prompt = new Chooser(['Yes', 'No']); - print('Update now?'); - var choice = await prompt.choose(); - - if (choice != 'Yes') { - pen.reset(); - pen.cyan(); - pen.text( - 'When you are ready to update, run `pub global activate angel_cli`.'); - pen(); - stdout.writeln(); - } else { - var pubPath = resolvePub(); - print('Running `pub global activate` using $pubPath...'); - var p = - await Process.start(pubPath, ['global', 'activate', 'angel_cli']); - p.stderr.listen(stderr.add); - p.stdout.listen(stdout.add); - var exitCode = await p.exitCode; - - if (exitCode != 0) - throw 'Pub terminated with a non-zero exit code.'; - else { - pen.reset(); - pen.green(); - pen("${Icon.CHECKMARK} Successfully updated the Angel CLI to version $update.\n"); - pen(); - } - } - } else - stdout.writeln('No update available.'); - } catch (e, st) { - stdout.writeln('Failed to check for update.'); - stderr.writeln(e); - stderr.writeln(st); - } - } -} diff --git a/lib/src/commands/version.dart b/lib/src/commands/version.dart deleted file mode 100644 index 4d265991..00000000 --- a/lib/src/commands/version.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:args/command_runner.dart'; -import 'pubspec.update.g.dart'; - -class VersionCommand extends Command { - @override - String get name => 'version'; - - @override - String get description => 'Prints the currently-installed version of the Angel CLI.'; - - @override - run() => print('Angel CLI version $PACKAGE_VERSION'); -} \ No newline at end of file From 7080f80ee7cb5b14834a836d37d46ff4e2505e95 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:36:25 -0400 Subject: [PATCH 080/132] Remove update+version commands --- lib/src/commands/commands.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 70a559de..08595fbc 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -5,6 +5,4 @@ export "key.dart"; export "init.dart"; export "install.dart"; export "make.dart"; -export "rename.dart"; -export 'update.dart'; -export 'version.dart'; \ No newline at end of file +export "rename.dart"; \ No newline at end of file From 2aeeafeb93fda0389b6bcd330a62497e9c9046d6 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:42:38 -0400 Subject: [PATCH 081/132] Update rename command --- lib/src/commands/rename.dart | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index b0c42912..59b4c8d6 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -1,10 +1,12 @@ import 'dart:io'; import 'package:analyzer/analyzer.dart'; import 'package:args/command_runner.dart'; -import 'package:console/console.dart'; import 'package:dart_style/dart_style.dart'; import 'package:glob/glob.dart'; +import 'package:io/ansi.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:pubspec_parse/pubspec_parse.dart'; +import '../util.dart'; import 'pub.dart'; class RenameCommand extends Command { @@ -24,15 +26,12 @@ class RenameCommand extends Command { if (argResults.rest.isNotEmpty) newName = argResults.rest.first; else { - var p = new Prompter('Enter new project name: '); - newName = await p.prompt(checker: (String str) => str.isNotEmpty); + newName = prompts.get('Rename project to'); } - var ch = new Chooser(['Yes', 'No'], - message: 'Rename the project to `$newName`? '); - var choice = await ch.choose(); + var choice = prompts.getBool('Rename the project to `$newName`?'); - if (choice == 'Yes') { + if (choice) { print('Renaming project to `$newName`...'); var pubspecFile = new File.fromUri(Directory.current.uri.resolve('pubspec.yaml')); @@ -40,7 +39,7 @@ class RenameCommand extends Command { if (!await pubspecFile.exists()) { throw new Exception('No pubspec.yaml found in current directory.'); } else { - var pubspec = await Pubspec.load(Directory.current); + var pubspec = await loadPubspec(); var oldName = pubspec.name; await renamePubspec(Directory.current, oldName, newName); await renameDartFiles(Directory.current, oldName, newName); @@ -57,9 +56,13 @@ class RenameCommand extends Command { } renamePubspec(Directory dir, String oldName, String newName) async { - var pubspec = await Pubspec.load(dir); - var newPubspec = new Pubspec.fromJson(pubspec.toJson()..['name'] = newName); - await newPubspec.save(dir); +// var pubspec = await loadPubspec(dir); + print(cyan.wrap('Renaming your project to `$newName.`')); + print(cyan + .wrap('Note that this does not actually modify your `pubspec.yaml`.')); +// TODO: https://github.com/dart-lang/pubspec_parse/issues/17 +// var newPubspec = new Pubspec.fromJson(pubspec.toJson()..['name'] = newName); +// await newPubspec.save(dir); } renameDartFiles(Directory dir, String oldName, String newName) async { @@ -68,7 +71,8 @@ renameDartFiles(Directory dir, String oldName, String newName) async { await for (var yamlFile in configGlob.list(root: dir.absolute.path)) { if (yamlFile is File) { - print('Replacing occurrences of "$oldName" with "$newName" in file "${yamlFile.absolute.path}"...'); + print( + 'Replacing occurrences of "$oldName" with "$newName" in file "${yamlFile.absolute.path}"...'); var contents = await yamlFile.readAsString(); contents = contents.replaceAll(oldName, newName); await yamlFile.writeAsString(contents); @@ -93,10 +97,10 @@ renameDartFiles(Directory dir, String oldName, String newName) async { if (visitor.replace.isNotEmpty) { visitor.replace.forEach((range, replacement) { if (range.first is int) { - contents = - contents.replaceRange(range.first, range.last, replacement); + contents = contents.replaceRange( + range.first as int, range.last as int, replacement); } else if (range.first is String) { - contents = contents.replaceAll(range.first, replacement); + contents = contents.replaceAll(range.first as Pattern, replacement); } }); From f05fbfad5eb3af699ffba04366a7a50d8873f249 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:43:10 -0400 Subject: [PATCH 082/132] Formatting --- lib/angel_cli.dart | 2 +- lib/src/commands/commands.dart | 2 +- lib/src/commands/install.dart | 6 ++++-- lib/src/commands/make.dart | 5 +++-- lib/src/commands/service_generators/generator.dart | 4 ++-- lib/src/commands/service_generators/service_generators.dart | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/angel_cli.dart b/lib/angel_cli.dart index 4d3fa997..843a6734 100644 --- a/lib/angel_cli.dart +++ b/lib/angel_cli.dart @@ -1,3 +1,3 @@ library angel_cli; -export 'src/commands/commands.dart'; \ No newline at end of file +export 'src/commands/commands.dart'; diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 08595fbc..3cd8fdb7 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -5,4 +5,4 @@ export "key.dart"; export "init.dart"; export "install.dart"; export "make.dart"; -export "rename.dart"; \ No newline at end of file +export "rename.dart"; diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index 50746b7d..2cff7b95 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -121,9 +121,11 @@ class InstallCommand extends Command { var desc = val[key]['description'] ?? key; if (val[key]['type'] == 'prompt') { - values[key] = prompts.get(desc.toString(), defaultsTo: val[key]['default']?.toString()); + values[key] = prompts.get(desc.toString(), + defaultsTo: val[key]['default']?.toString()); } else if (val[key]['type'] == 'choice') { - values[key] = prompts.choose(desc.toString(), val[key]['choices'] as Iterable); + values[key] = prompts.choose( + desc.toString(), val[key]['choices'] as Iterable); } } } diff --git a/lib/src/commands/make.dart b/lib/src/commands/make.dart index 0a71c5be..6f091e07 100644 --- a/lib/src/commands/make.dart +++ b/lib/src/commands/make.dart @@ -10,7 +10,8 @@ class MakeCommand extends Command { String get name => 'make'; @override - String get description => 'Generates common code for your project, such as projects and controllers.'; + String get description => + 'Generates common code for your project, such as projects and controllers.'; MakeCommand() { addSubcommand(new ControllerCommand()); @@ -19,4 +20,4 @@ class MakeCommand extends Command { addSubcommand(new TestCommand()); addSubcommand(new ServiceCommand()); } -} \ No newline at end of file +} diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart index 23ec15e8..2e816ae5 100644 --- a/lib/src/commands/service_generators/generator.dart +++ b/lib/src/commands/service_generators/generator.dart @@ -29,8 +29,8 @@ class ServiceGenerator { void beforeService(BlockBuilder builder, String name, String lower) {} - void applyToConfigureServer( - MethodBuilder configureServer, BlockBuilder block, String name, String lower) {} + void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, + String name, String lower) {} Expression createInstance( MethodBuilder methodBuilder, String name, String lower) => diff --git a/lib/src/commands/service_generators/service_generators.dart b/lib/src/commands/service_generators/service_generators.dart index 0dfd685e..813adc18 100644 --- a/lib/src/commands/service_generators/service_generators.dart +++ b/lib/src/commands/service_generators/service_generators.dart @@ -12,4 +12,4 @@ const List serviceGenerators = const [ const MongoServiceGenerator(), const RethinkServiceGenerator(), const CustomServiceGenerator() -]; \ No newline at end of file +]; From 63e63e438e61838b2d83abdce3197473a3f77997 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:47:24 -0400 Subject: [PATCH 083/132] Roll custom random string for Dart 2 --- lib/src/commands/init.dart | 2 +- lib/src/commands/key.dart | 2 +- lib/src/random_string.dart | 15 +++++++++++++++ pubspec.yaml | 4 ++-- 4 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 lib/src/random_string.dart diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index c7da329e..4ce56e9d 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -4,7 +4,7 @@ import "package:args/command_runner.dart"; import 'package:io/ansi.dart'; import 'package:path/path.dart' as p; import 'package:prompts/prompts.dart' as prompts; -import 'package:random_string/random_string.dart' as rs; +import '../random_string.dart' as rs; import '../util.dart'; import 'key.dart'; import 'pub.dart'; diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index f2a66f7a..296be621 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -1,6 +1,6 @@ import 'dart:io'; import 'package:args/command_runner.dart'; -import 'package:random_string/random_string.dart' as rs; +import '../random_string.dart' as rs; class KeyCommand extends Command { @override diff --git a/lib/src/random_string.dart b/lib/src/random_string.dart new file mode 100644 index 00000000..c089e870 --- /dev/null +++ b/lib/src/random_string.dart @@ -0,0 +1,15 @@ +import 'dart:math'; + +const String _valid = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; +final Random _rnd = new Random.secure(); + +String randomAlphaNumeric(int length) { + var b = new StringBuffer(); + + for (int i = 0; i < length; i++) { + b.writeCharCode(_valid.codeUnitAt(_rnd.nextInt(_valid.length))); + } + + return b.toString(); +} diff --git a/pubspec.yaml b/pubspec.yaml index 06546a9f..15cde314 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://github.com/angel-dart/angel_cli name: angel_cli version: 1.3.0 dependencies: - # analyzer: ^0.29.0 + analyzer: ">=0.32.0" args: ^1.0.0 code_builder: ^3.0.0 dart_style: ^1.0.0 @@ -18,7 +18,7 @@ dependencies: path: ^1.0.0 prompts: ^1.0.0 pubspec_parse: ^0.1.2 - random_string: ^0.0.1 + quiver: ">=0.29.0" recase: ^1.0.0 watcher: ^0.9.7 yaml: ^2.0.0 From 0d0c0539d0c53269ebedd93332425ef7172a8492 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:48:12 -0400 Subject: [PATCH 084/132] Fix typo in key command --- lib/src/commands/key.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/commands/key.dart b/lib/src/commands/key.dart index 296be621..cab986f8 100644 --- a/lib/src/commands/key.dart +++ b/lib/src/commands/key.dart @@ -7,7 +7,7 @@ class KeyCommand extends Command { String get name => 'key'; @override - String get description => 'Generates a new `angel_auth`key.'; + String get description => 'Generates a new `angel_auth` key.'; @override run() async { From 1c9aa76d89cfecd6e1a2c9ecfa814177e1872b5b Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:49:17 -0400 Subject: [PATCH 085/132] Don't use ORM boilerplate --- lib/src/commands/init.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 4ce56e9d..ac25ed0d 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -125,8 +125,9 @@ class InitCommand extends Command { print('Choose a project type before continuing:'); - var boilerplate = prompts.choose( - 'Choose a project type before continuing', boilerplates); + var boilerplate = basicBoilerplate; +// var boilerplate = prompts.choose( +// 'Choose a project type before continuing', boilerplates); print( 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); From 5eb15fcd0650327a5a977d354b1c90e89b4f710d Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:54:38 -0400 Subject: [PATCH 086/132] Handle String thrown differently --- bin/angel.dart | 8 ++++++-- lib/src/commands/init.dart | 4 ++-- lib/src/commands/rename.dart | 17 +++++++++++++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/bin/angel.dart b/bin/angel.dart index cc11c968..41e6e57d 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -20,7 +20,11 @@ main(List args) async { ..addCommand(new MakeCommand()); return await runner.run(args).then((_) {}).catchError((exc) { - stderr.writeln("Oops, something went wrong: $exc"); - exitCode = 1; + if (exc is String) { + stdout.writeln(exc); + } else { + stderr.writeln("Oops, something went wrong: $exc"); + exitCode = 1; + } }); } diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index ac25ed0d..c6569baf 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -113,8 +113,8 @@ class InitCommand extends Command { var shouldDelete = prompts.getBool( "Directory '${projectDir.absolute.path}' already exists. Overwrite it?"); - if (shouldDelete) - throw new Exception("Chose not to overwrite existing directory."); + if (!shouldDelete) + throw "Chose not to overwrite existing directory."; else if (projectDir.absolute.uri.normalizePath().toFilePath() != Directory.current.absolute.uri.normalizePath().toFilePath()) await projectDir.delete(recursive: true); diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index 59b4c8d6..fee799eb 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -58,8 +58,21 @@ class RenameCommand extends Command { renamePubspec(Directory dir, String oldName, String newName) async { // var pubspec = await loadPubspec(dir); print(cyan.wrap('Renaming your project to `$newName.`')); - print(cyan - .wrap('Note that this does not actually modify your `pubspec.yaml`.')); + + var pubspecFile = new File.fromUri(dir.uri.resolve('pubspec.yaml')); + + if (await pubspecFile.exists()) { + var contents = await pubspecFile.readAsString(), oldContents = contents; + contents = + contents.replaceAll(new RegExp('name:\\s*$oldName'), 'name: $newName'); + + if (contents != oldContents) { + await pubspecFile.writeAsString(contents); + } + } + +// print(cyan +// .wrap('Note that this does not actually modify your `pubspec.yaml`.')); // TODO: https://github.com/dart-lang/pubspec_parse/issues/17 // var newPubspec = new Pubspec.fromJson(pubspec.toJson()..['name'] = newName); // await newPubspec.save(dir); From 96b9668ad5ba998d4f7216251958763c8c1f0437 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 18:58:53 -0400 Subject: [PATCH 087/132] Correct typo in init command --- lib/src/commands/init.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index c6569baf..f89a544e 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -64,7 +64,7 @@ class InitCommand extends Command { ..write(cyan.wrap('hot-reloading')) ..write('), run ') ..write(magenta.wrap('`dart --observe bin/dev.dart`')) - ..writeln(' in your terminal') + ..writeln(' in your terminal.') ..writeln() ..writeln('Find more documentation about Angel:') ..writeln(' * https://angel-dart.github.io') @@ -159,7 +159,9 @@ class InitCommand extends Command { var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git")); if (await gitDir.exists()) await gitDir.delete(recursive: true); } catch (e) { - print(red.wrap("$ballot Could not initialize Angel project.")); + if (e is! String) { + print(red.wrap("$ballot Could not initialize Angel project.")); + } rethrow; } } From dca32896eb8b88928a0cb565abf71ebef6edb854 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:06:48 -0400 Subject: [PATCH 088/132] Remove problematic Map casts --- bin/angel.dart | 9 ++++++++- lib/src/commands/install.dart | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bin/angel.dart b/bin/angel.dart index 41e6e57d..f50713ba 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -11,6 +11,9 @@ main(List args) async { var runner = new CommandRunner("angel", "Command-line tools for the Angel framework."); + runner.argParser + .addFlag('verbose', help: 'Print verbose output', negatable: false); + runner ..addCommand(new DoctorCommand()) ..addCommand(new KeyCommand()) @@ -19,12 +22,16 @@ main(List args) async { ..addCommand(new RenameCommand()) ..addCommand(new MakeCommand()); - return await runner.run(args).then((_) {}).catchError((exc) { + return await runner.run(args).then((_) {}).catchError((exc, st) { if (exc is String) { stdout.writeln(exc); } else { stderr.writeln("Oops, something went wrong: $exc"); exitCode = 1; + + if (args.contains('--verbose')) { + stderr.writeln(st); + } } }); } diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index 2cff7b95..bc13e11f 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -73,7 +73,7 @@ class InstallCommand extends Command { throw 'No add-on named "$packageName" is installed. You might need to run `angel install --update`.'; print('Installing $packageName...'); - Map values = { + Map values = { 'project_name': pubspec.name, 'pubspec': pubspec, }; @@ -106,7 +106,7 @@ class InstallCommand extends Command { if (await promptFile.exists()) { var contents = await promptFile.readAsString(); var y = yaml.loadYamlDocument(contents); - var cfg = y.contents.value as Map; + var cfg = y.contents.value as Map; // Loads globs if (cfg['templates'] is List) { @@ -115,7 +115,7 @@ class InstallCommand extends Command { } if (cfg['values'] is Map) { - var val = cfg['values'] as Map; + var val = cfg['values'] as Map; for (var key in val.keys) { var desc = val[key]['description'] ?? key; From ea44b4a7fd6291650498f6c47aa5cd66741235bc Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:13:32 -0400 Subject: [PATCH 089/132] Screenshot --- README.md | 7 +++++-- bin/angel.dart | 20 +++++++++++++++++--- screenshots/screenshot.png | Bin 0 -> 84903 bytes 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 screenshots/screenshot.png diff --git a/README.md b/README.md index 70aab43d..3b96938f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # angel_cli + +![Screenshot of Terminal](screenshots/screenshot.png) + Command-line tools for the Angel framework. Includes functionality such as: * Project scaffolding * Generating service models, plugins, tests and more * Renaming projects -* Starting server with live reloading +* Much more... To install: @@ -15,5 +18,5 @@ $ pub global activate angel_cli And then, for information on each command: ```bash -$ angel --help +$ angel help ``` diff --git a/bin/angel.dart b/bin/angel.dart index f50713ba..2c510a07 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -8,11 +8,16 @@ import 'package:angel_cli/angel_cli.dart'; final String DOCTOR = "doctor"; main(List args) async { - var runner = - new CommandRunner("angel", "Command-line tools for the Angel framework."); + var runner = new CommandRunner( + "angel", + asciiArt.trim() + + '\n\n' + + "Command-line tools for the Angel framework." + + '\n\n' + + 'https://angel-dart.github.io'); runner.argParser - .addFlag('verbose', help: 'Print verbose output', negatable: false); + .addFlag('verbose', help: 'Print verbose output.', negatable: false); runner ..addCommand(new DoctorCommand()) @@ -35,3 +40,12 @@ main(List args) async { } }); } + +const String asciiArt = ''' +____________ ________________________ +___ |__ | / /_ ____/__ ____/__ / +__ /| |_ |/ /_ / __ __ __/ __ / +_ ___ | /| / / /_/ / _ /___ _ /___ +/_/ |_/_/ |_/ \____/ /_____/ /_____/ + +'''; diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..fe8c9652e90163723a8df8bbba0757f07f1e244d GIT binary patch literal 84903 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 tcFC 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 Date: Sat, 14 Jul 2018 19:16:18 -0400 Subject: [PATCH 090/132] Update test boilerplate --- lib/src/commands/make/test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/commands/make/test.dart b/lib/src/commands/make/test.dart index c7efb890..68af780f 100644 --- a/lib/src/commands/make/test.dart +++ b/lib/src/commands/make/test.dart @@ -84,7 +84,7 @@ class TestCommand extends Command { String _generateTest(Pubspec pubspec, ReCase rc) { return ''' import 'dart:io'; -import 'package:${pubspec.name}/${pubspec.name}.dart'; +import 'package:${pubspec.name}/${pubspec.name}.dart' as ${pubspec.name}; import 'package:angel_framework/angel_framework.dart'; import 'package:angel_test/angel_test.dart'; import 'package:test/test.dart'; @@ -93,7 +93,8 @@ main() async { TestClient client; setUp(() async { - var app = await createServer(); + var app = new Angel(); + await app.configure(${pubspec.name}.configureServer); client = await connectTo(app); }); From 7d6884bd8d5110a895ceb4f92308c5eadd373429 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:16:36 -0400 Subject: [PATCH 091/132] Update test boilerplate --- lib/src/commands/make/test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/commands/make/test.dart b/lib/src/commands/make/test.dart index 68af780f..d95db4ff 100644 --- a/lib/src/commands/make/test.dart +++ b/lib/src/commands/make/test.dart @@ -102,7 +102,7 @@ main() async { test('${rc.snakeCase}', () async { final response = await client.get('/${rc.snakeCase}'); - expect(response, hasStatus(HttpStatus.OK)); + expect(response, hasStatus(HttpStatus.ok)); }); } '''; From d7038c7c68f5e530e585361326aac409cb80a724 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:18:53 -0400 Subject: [PATCH 092/132] Update controller boilerplate --- lib/src/commands/make/controller.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/commands/make/controller.dart b/lib/src/commands/make/controller.dart index 428c9e68..1ec1845e 100644 --- a/lib/src/commands/make/controller.dart +++ b/lib/src/commands/make/controller.dart @@ -26,7 +26,7 @@ class ControllerCommand extends Command { abbr: 'n', help: 'Specifies a name for the model class.') ..addOption('output-dir', help: 'Specifies a directory to create the controller class in.', - defaultsTo: 'lib/src/controllers'); + defaultsTo: 'lib/src/routes/controllers'); } @override @@ -69,7 +69,7 @@ class ControllerCommand extends Command { ..name = 'hello' ..returns = refer('void') ..annotations.add(refer('ExposeWs') - .constInstance([literal('get_${rc.snakeCase}')])) + .newInstance([literal('get_${rc.snakeCase}')])) ..requiredParameters.add(new Parameter((b) => b ..name = 'socket' ..type = refer('WebSocketContext'))) @@ -82,14 +82,14 @@ class ControllerCommand extends Command { })); } else { clazz - ..annotations.add( - refer('Expose').constInstance([literal('/${rc.snakeCase}')])) + ..annotations + .add(refer('Expose').newInstance([literal('/${rc.snakeCase}')])) ..methods.add(new Method((meth) { meth ..name = 'hello' ..returns = refer('String') ..body = literal('Hello, world').returned.statement - ..annotations.add(refer('Expose').constInstance([ + ..annotations.add(refer('Expose').newInstance([ literal('/'), ])); })); From d6568c53a093aa259291c9573a70d10c9cc06c65 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:20:28 -0400 Subject: [PATCH 093/132] Fix annotations --- lib/src/commands/make/controller.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/commands/make/controller.dart b/lib/src/commands/make/controller.dart index 1ec1845e..58a1d250 100644 --- a/lib/src/commands/make/controller.dart +++ b/lib/src/commands/make/controller.dart @@ -69,7 +69,7 @@ class ControllerCommand extends Command { ..name = 'hello' ..returns = refer('void') ..annotations.add(refer('ExposeWs') - .newInstance([literal('get_${rc.snakeCase}')])) + .call([literal('get_${rc.snakeCase}')])) ..requiredParameters.add(new Parameter((b) => b ..name = 'socket' ..type = refer('WebSocketContext'))) @@ -83,13 +83,13 @@ class ControllerCommand extends Command { } else { clazz ..annotations - .add(refer('Expose').newInstance([literal('/${rc.snakeCase}')])) + .add(refer('Expose').call([literal('/${rc.snakeCase}')])) ..methods.add(new Method((meth) { meth ..name = 'hello' ..returns = refer('String') ..body = literal('Hello, world').returned.statement - ..annotations.add(refer('Expose').newInstance([ + ..annotations.add(refer('Expose').call([ literal('/'), ])); })); From c199e56e141603fa798effeb031418c88a80bfe7 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:26:05 -0400 Subject: [PATCH 094/132] Update plugin boilerplate --- lib/src/commands/make/controller.dart | 13 +++++++++++-- lib/src/commands/make/plugin.dart | 7 +++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/src/commands/make/controller.dart b/lib/src/commands/make/controller.dart index 58a1d250..f5746d64 100644 --- a/lib/src/commands/make/controller.dart +++ b/lib/src/commands/make/controller.dart @@ -64,12 +64,21 @@ class ControllerCommand extends Command { : 'Controller'); if (argResults['websocket'] as bool) { + // XController(AngelWebSocket ws) : super(ws); + clazz.constructors.add(new Constructor((b) { + b + ..requiredParameters.add(new Parameter((b) => b + ..name = 'ws' + ..type = refer('AngelWebSocket'))) + ..initializers.add(new Code('super(ws)')); + })); + clazz.methods.add(new Method((meth) { meth ..name = 'hello' ..returns = refer('void') - ..annotations.add(refer('ExposeWs') - .call([literal('get_${rc.snakeCase}')])) + ..annotations + .add(refer('ExposeWs').call([literal('get_${rc.snakeCase}')])) ..requiredParameters.add(new Parameter((b) => b ..name = 'socket' ..type = refer('WebSocketContext'))) diff --git a/lib/src/commands/make/plugin.dart b/lib/src/commands/make/plugin.dart index 37807505..d9424359 100644 --- a/lib/src/commands/make/plugin.dart +++ b/lib/src/commands/make/plugin.dart @@ -60,11 +60,10 @@ library ${pubspec.name}.src.config.plugins.${rc.snakeCase}; import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; -class ${rc.pascalCase}Plugin extends AngelPlugin { - @override - Future call(Angel app) async { +AngelConfigurer ${rc.camelCase}() { + return (Angel app) async { // Work some magic... - } + }; } '''; } From ac273baa0693d19c2a7d5cea8b5998452387b2f8 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:29:34 -0400 Subject: [PATCH 095/132] Update model boilerplate --- lib/src/commands/make/model.dart | 1 + lib/src/commands/make/plugin.dart | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 899ced3a..e0a4c7ba 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -73,6 +73,7 @@ class ModelCommand extends Command { modelLib.body.add(new Class((modelClazz) { modelClazz + ..abstract = true ..name = needsSerialize ? '_${rc.pascalCase}' : rc.pascalCase ..extend = refer('Model'); diff --git a/lib/src/commands/make/plugin.dart b/lib/src/commands/make/plugin.dart index d9424359..cfccbfe9 100644 --- a/lib/src/commands/make/plugin.dart +++ b/lib/src/commands/make/plugin.dart @@ -57,7 +57,6 @@ class PluginCommand extends Command { return ''' library ${pubspec.name}.src.config.plugins.${rc.snakeCase}; -import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; AngelConfigurer ${rc.camelCase}() { From 29252ee91557d6366bc73e2e4656da299bbca3f5 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:30:56 -0400 Subject: [PATCH 096/132] Update service generator toString --- lib/src/commands/service_generators/generator.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart index 2e816ae5..43b573f5 100644 --- a/lib/src/commands/service_generators/generator.dart +++ b/lib/src/commands/service_generators/generator.dart @@ -35,4 +35,7 @@ class ServiceGenerator { Expression createInstance( MethodBuilder methodBuilder, String name, String lower) => literal(null); + + @override + String toString() => name; } From 5e5d6177030999b18487ba1640e729052d98e3c8 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:47:25 -0400 Subject: [PATCH 097/132] More flexibility for service gen --- lib/src/commands/make/service.dart | 8 ++++---- lib/src/commands/service_generators/custom.dart | 4 ++-- .../service_generators/file_service.dart | 16 +++++++++++----- .../commands/service_generators/generator.dart | 15 ++++++++++----- lib/src/commands/service_generators/map.dart | 4 ++-- lib/src/commands/service_generators/mongo.dart | 12 ++++++++---- lib/src/commands/service_generators/rethink.dart | 12 ++++++++---- 7 files changed, 45 insertions(+), 26 deletions(-) diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart index e1a742a6..72b2b3b0 100644 --- a/lib/src/commands/make/service.dart +++ b/lib/src/commands/make/service.dart @@ -85,7 +85,7 @@ class ServiceCommand extends Command { configureServer.body = new Block((block) { generator.applyToConfigureServer( - configureServer, block, name, rc.snakeCase); + serviceLib, configureServer, block, name, rc.snakeCase); // return (Angel app) async {} var closure = new Method((closure) { @@ -95,11 +95,11 @@ class ServiceCommand extends Command { ..name = 'app' ..type = refer('Angel'))); closure.body = new Block((block) { - generator.beforeService(block, name, rc.snakeCase); + generator.beforeService(serviceLib, block, name, rc.snakeCase); // app.use('/api/todos', new MapService()); - var service = - generator.createInstance(closure, name, rc.snakeCase); + var service = generator.createInstance( + serviceLib, closure, name, rc.snakeCase); if (argResults['typed'] as bool) { var tb = new TypeReference((b) => b diff --git a/lib/src/commands/service_generators/custom.dart b/lib/src/commands/service_generators/custom.dart index a1de5f32..d4134602 100644 --- a/lib/src/commands/service_generators/custom.dart +++ b/lib/src/commands/service_generators/custom.dart @@ -20,8 +20,8 @@ class CustomServiceGenerator extends ServiceGenerator { } @override - Expression createInstance( - MethodBuilder methodBuilder, String name, String lower) { + Expression createInstance(LibraryBuilder library, MethodBuilder methodBuilder, + String name, String lower) { return refer('${name}Service').newInstance([]); } } diff --git a/lib/src/commands/service_generators/file_service.dart b/lib/src/commands/service_generators/file_service.dart index 7ff54579..278562d0 100644 --- a/lib/src/commands/service_generators/file_service.dart +++ b/lib/src/commands/service_generators/file_service.dart @@ -14,8 +14,12 @@ class FileServiceGenerator extends ServiceGenerator { bool get goesFirst => true; @override - void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, - String name, String lower) { + void applyToConfigureServer( + LibraryBuilder library, + MethodBuilder configureServer, + BlockBuilder block, + String name, + String lower) { configureServer.requiredParameters.add(new Parameter((b) => b ..name = 'dbDirectory' ..type = refer('Directory'))); @@ -26,13 +30,15 @@ class FileServiceGenerator extends ServiceGenerator { library.directives.addAll([ new Directive.import( 'package:angel_file_service/angel_file_service.dart'), - new Directive.import('package:file/file.dart'), ]); } @override - Expression createInstance( - MethodBuilder methodBuilder, String name, String lower) { + Expression createInstance(LibraryBuilder library, MethodBuilder methodBuilder, + String name, String lower) { + library.directives.addAll([ + new Directive.import('package:file/file.dart'), + ]); return refer('JsonFileService').newInstance([ refer('dbDirectory') .property('childFile') diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart index 43b573f5..63289581 100644 --- a/lib/src/commands/service_generators/generator.dart +++ b/lib/src/commands/service_generators/generator.dart @@ -27,13 +27,18 @@ class ServiceGenerator { void applyToLibrary(LibraryBuilder library, String name, String lower) {} - void beforeService(BlockBuilder builder, String name, String lower) {} + void beforeService(LibraryBuilder library, BlockBuilder builder, String name, + String lower) {} - void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, - String name, String lower) {} + void applyToConfigureServer( + LibraryBuilder library, + MethodBuilder configureServer, + BlockBuilder block, + String name, + String lower) {} - Expression createInstance( - MethodBuilder methodBuilder, String name, String lower) => + Expression createInstance(LibraryBuilder library, MethodBuilder methodBuilder, + String name, String lower) => literal(null); @override diff --git a/lib/src/commands/service_generators/map.dart b/lib/src/commands/service_generators/map.dart index 4b1dec7e..545961df 100644 --- a/lib/src/commands/service_generators/map.dart +++ b/lib/src/commands/service_generators/map.dart @@ -8,8 +8,8 @@ class MapServiceGenerator extends ServiceGenerator { bool get createsModel => false; @override - Expression createInstance( - MethodBuilder methodBuilder, String name, String lower) { + Expression createInstance(LibraryBuilder library, MethodBuilder methodBuilder, + String name, String lower) { return refer('MapService').newInstance([]); } } diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index f6b70444..9386f28f 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -14,8 +14,12 @@ class MongoServiceGenerator extends ServiceGenerator { bool get createsModel => false; @override - void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, - String name, String lower) { + void applyToConfigureServer( + LibraryBuilder library, + MethodBuilder configureServer, + BlockBuilder block, + String name, + String lower) { configureServer.requiredParameters.add(new Parameter((b) => b ..name = 'db' ..type = refer('Db'))); @@ -30,8 +34,8 @@ class MongoServiceGenerator extends ServiceGenerator { } @override - Expression createInstance( - MethodBuilder methodBuilder, String name, String lower) { + Expression createInstance(LibraryBuilder library, MethodBuilder methodBuilder, + String name, String lower) { return refer('MongoService').newInstance([ refer('db').property('collection').call([literal(pluralize(lower))]) ]); diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index dced10e6..f36e7f12 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -14,8 +14,12 @@ class RethinkServiceGenerator extends ServiceGenerator { bool get createsModel => false; @override - void applyToConfigureServer(MethodBuilder configureServer, BlockBuilder block, - String name, String lower) { + void applyToConfigureServer( + LibraryBuilder library, + MethodBuilder configureServer, + BlockBuilder block, + String name, + String lower) { configureServer.requiredParameters.addAll([ new Parameter((b) => b ..name = 'connection' @@ -35,8 +39,8 @@ class RethinkServiceGenerator extends ServiceGenerator { } @override - Expression createInstance( - MethodBuilder methodBuilder, String name, String lower) { + Expression createInstance(LibraryBuilder library, MethodBuilder methodBuilder, + String name, String lower) { return refer('RethinkService').newInstance([ refer('connection'), refer('r').property('table').call([literal(pluralize(lower))]) From af2c00be83159be615820fd93c8f2a433f90a681 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:48:41 -0400 Subject: [PATCH 098/132] Correct rethink --- lib/src/commands/service_generators/rethink.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index f36e7f12..93dc65f1 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -34,7 +34,7 @@ class RethinkServiceGenerator extends ServiceGenerator { void applyToLibrary(LibraryBuilder library, String name, String lower) { library.directives.addAll([ 'package:angel_rethink/angel_rethink.dart', - 'package:rethinkdb_driver2/rethinkdb_driver2.dart' + 'package:rethinkdb_driver/rethinkdb_driver.dart' ].map((str) => new Directive.import(str))); } From 4e9d35ab96cb9e3d65379d68dbf398e814348553 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sat, 14 Jul 2018 19:50:08 -0400 Subject: [PATCH 099/132] 1.3.0 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac03088e..1a943443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.3.0 +* Focus on Dart2 from here on out. +* Update `code_builder`. +* More changes... + # 1.1.5 Deprecated several commands, in favor of the `make` command: From 4bf3308beb03e704fb2b9b2e3c34747f77828c69 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sun, 15 Jul 2018 00:11:38 -0400 Subject: [PATCH 100/132] Basic deployment commands --- CHANGELOG.md | 3 ++ bin/angel.dart | 1 + lib/src/commands/commands.dart | 1 + lib/src/commands/deploy.dart | 17 ++++++++ lib/src/commands/deploy/nginx.dart | 52 +++++++++++++++++++++++++ lib/src/commands/deploy/systemd.dart | 58 ++++++++++++++++++++++++++++ lib/src/commands/make/model.dart | 3 +- pubspec.yaml | 2 +- 8 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 lib/src/commands/deploy.dart create mode 100644 lib/src/commands/deploy/nginx.dart create mode 100644 lib/src/commands/deploy/systemd.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a943443..e5e625a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.3.1 +* Add `deploy nginx` and `deploy systemd`. + # 1.3.0 * Focus on Dart2 from here on out. * Update `code_builder`. diff --git a/bin/angel.dart b/bin/angel.dart index 2c510a07..bdeddff4 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -20,6 +20,7 @@ main(List args) async { .addFlag('verbose', help: 'Print verbose output.', negatable: false); runner + ..addCommand(new DeployCommand()) ..addCommand(new DoctorCommand()) ..addCommand(new KeyCommand()) ..addCommand(new InitCommand()) diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart index 3cd8fdb7..a6db230d 100644 --- a/lib/src/commands/commands.dart +++ b/lib/src/commands/commands.dart @@ -1,5 +1,6 @@ library angel_cli.commands; +export "deploy.dart"; export "doctor.dart"; export "key.dart"; export "init.dart"; diff --git a/lib/src/commands/deploy.dart b/lib/src/commands/deploy.dart new file mode 100644 index 00000000..92b5f82e --- /dev/null +++ b/lib/src/commands/deploy.dart @@ -0,0 +1,17 @@ +import 'package:args/command_runner.dart'; +import 'deploy/nginx.dart'; +import 'deploy/systemd.dart'; + +class DeployCommand extends Command { + @override + String get name => 'deploy'; + + @override + String get description => + 'Generates scaffolding + helper functionality for deploying servers. Run this in your project root.'; + + DeployCommand() { + addSubcommand(new NginxCommand()); + addSubcommand(new SystemdCommand()); + } +} diff --git a/lib/src/commands/deploy/nginx.dart b/lib/src/commands/deploy/nginx.dart new file mode 100644 index 00000000..638f70ac --- /dev/null +++ b/lib/src/commands/deploy/nginx.dart @@ -0,0 +1,52 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:io/ansi.dart'; +import 'package:path/path.dart' as p; +import '../../util.dart'; + +class NginxCommand extends Command { + @override + String get name => 'nginx'; + + @override + String get description => + 'Generates a NGINX configuration for a reverse proxy + static server.'; + + NginxCommand() { + argParser.addOption('out', + abbr: 'o', + help: + 'An optional output file to write to; otherwise prints to stdout.'); + } + + @override + run() async { + var webPath = p.join(p.current, 'web'); + var nginxText = ''' +server { + listen 80 default_server; + root ${p.absolute(webPath)}; # Set to your static files directory + + location / { + try_files \$uri @proxy; # Try to serve static files; fallback to proxied Angel server + } + + location @proxy { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; # Important, do not omit + } +} + ''' + .trim(); + + if (!argResults.wasParsed('out')) { + print(nginxText); + } else { + var file = new File(argResults['out'] as String); + await file.create(recursive: true); + await file.writeAsString(nginxText); + print(green.wrap( + "$checkmark Successfully generated nginx configuration in '${file.path}'.")); + } + } +} diff --git a/lib/src/commands/deploy/systemd.dart b/lib/src/commands/deploy/systemd.dart new file mode 100644 index 00000000..4d4114bd --- /dev/null +++ b/lib/src/commands/deploy/systemd.dart @@ -0,0 +1,58 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:io/ansi.dart'; +import 'package:path/path.dart' as p; +import '../../util.dart'; + +class SystemdCommand extends Command { + @override + String get name => 'systemd'; + + @override + String get description => + 'Generates a systemd service to continuously run your server.'; + + SystemdCommand() { + argParser + ..addOption('user', + abbr: 'u', + defaultsTo: 'web', + help: 'The name of the unprivileged account to run the server as.') + ..addOption('out', + abbr: 'o', + help: + 'An optional output file to write to; otherwise prints to stdout.'); + } + + @override + run() async { + var projectPath = p.absolute(p.current); + var pubspec = await loadPubspec(); + var user = argResults['user']; + var systemdText = ''' +[Unit] +Description=`${pubspec.name}` server + +[Service] +Environment=ANGEL_ENV=production +User=$user # Name of unprivileged `$user` user +WorkingDirectory=$projectPath # Path to `${pubspec.name}` project +ExecStart=${Platform.resolvedExecutable} bin/prod.dart +Restart=always # Restart process on crash + +[Install] +WantedBy=multi-user.target + ''' + .trim(); + + if (!argResults.wasParsed('out')) { + print(systemdText); + } else { + var file = new File(argResults['out'] as String); + await file.create(recursive: true); + await file.writeAsString(systemdText); + print(green.wrap( + "$checkmark Successfully generated systemd service in '${file.path}'.")); + } + } +} diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index e0a4c7ba..010752e6 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -46,7 +46,6 @@ class ModelCommand extends Command { } List deps = [ - const MakerDependency('angel_framework', '^1.0.0'), const MakerDependency('angel_model', '^1.0.0'), ]; @@ -63,6 +62,8 @@ class ModelCommand extends Command { modelLib.directives.add(new Directive.import( 'package:angel_serialize/angel_serialize.dart')); deps.add(const MakerDependency('angel_serialize', '^2.0.0')); + deps.add(const MakerDependency('angel_serialize_generator', '^2.0.0')); + deps.add(const MakerDependency('build_runner', '">=0.7.0 <0.10.0"')); } if (argResults['orm'] as bool) { diff --git a/pubspec.yaml b/pubspec.yaml index 15cde314..e4bc78a1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 1.3.0 +version: 1.3.1 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 63df555639769cdfd7948a5dc46a0e9a351d7954 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sun, 15 Jul 2018 00:45:01 -0400 Subject: [PATCH 101/132] Part directive --- CHANGELOG.md | 3 +++ lib/src/commands/make/model.dart | 6 +++++- pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5e625a3..a04a14f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.3.2 +* Restore `part` directives in generated models. + # 1.3.1 * Add `deploy nginx` and `deploy systemd`. diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 010752e6..ffefd685 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -72,6 +72,11 @@ class ModelCommand extends Command { deps.add(const MakerDependency('angel_orm', '^1.0.0-alpha')); } + modelLib.body.addAll([ + new Code('part ${rc.snakeCase}.g.dart'), + new Code('part ${rc.snakeCase}.serializer.g.dart'), + ]); + modelLib.body.add(new Class((modelClazz) { modelClazz ..abstract = true @@ -79,7 +84,6 @@ class ModelCommand extends Command { ..extend = refer('Model'); if (needsSerialize) { - // TODO: Add parts // modelLib.addDirective(new PartBuilder('${rc.snakeCase}.g.dart')); modelClazz.annotations.add(refer('serializable')); } diff --git a/pubspec.yaml b/pubspec.yaml index e4bc78a1..b1544b66 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 1.3.1 +version: 1.3.2 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From bd8dc2c53f393ace2a55579d50b9f7445d71054f Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sun, 15 Jul 2018 00:49:18 -0400 Subject: [PATCH 102/132] 1.3.3 --- CHANGELOG.md | 5 ++++- lib/src/commands/make/model.dart | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a04a14f0..7d2d4324 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.3.3 +* Fix a small typo in the model generator. + # 1.3.2 * Restore `part` directives in generated models. @@ -22,4 +25,4 @@ of the old project names with the new one in `config/` YAML files, and also operates on the glob `config/**/*.yaml`. Changed the call to run `angel start` to run `dart bin/server.dart` instead, after an -`init` command. \ No newline at end of file +`init` command. diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index ffefd685..7053d411 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -73,8 +73,8 @@ class ModelCommand extends Command { } modelLib.body.addAll([ - new Code('part ${rc.snakeCase}.g.dart'), - new Code('part ${rc.snakeCase}.serializer.g.dart'), + new Code('part ${rc.snakeCase}.g.dart;'), + new Code('part ${rc.snakeCase}.serializer.g.dart;'), ]); modelLib.body.add(new Class((modelClazz) { diff --git a/pubspec.yaml b/pubspec.yaml index b1544b66..f27ae411 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 1.3.2 +version: 1.3.3 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From b3379ee418ea9792bf6596b489e0ac9aacc8d0a6 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sun, 15 Jul 2018 00:51:23 -0400 Subject: [PATCH 103/132] 1.3.4 --- CHANGELOG.md | 3 +++ lib/src/commands/make/model.dart | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d2d4324..3ac3dfca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.3.4 +* Fix another typo. + # 1.3.3 * Fix a small typo in the model generator. diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 7053d411..f4b33038 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -73,8 +73,8 @@ class ModelCommand extends Command { } modelLib.body.addAll([ - new Code('part ${rc.snakeCase}.g.dart;'), - new Code('part ${rc.snakeCase}.serializer.g.dart;'), + new Code("part '${rc.snakeCase}.g.dart';"), + new Code("part '${rc.snakeCase}.serializer.g.dart';"), ]); modelLib.body.add(new Class((modelClazz) { diff --git a/pubspec.yaml b/pubspec.yaml index f27ae411..9e453811 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 1.3.3 +version: 1.3.4 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From d6e12c24eb1998485d1f7b1653d186b642b9c83a Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:18:43 -0400 Subject: [PATCH 104/132] Bump to 1.4.0 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 9e453811..67a3dd07 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 1.3.4 +version: 1.4.0 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 5d808150dbdca590321717259fef1ba134c38241 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:22:53 -0400 Subject: [PATCH 105/132] Add legacy and 2.x boilerplates --- lib/src/commands/init.dart | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index f89a544e..372b7e75 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -125,9 +125,9 @@ class InitCommand extends Command { print('Choose a project type before continuing:'); - var boilerplate = basicBoilerplate; -// var boilerplate = prompts.choose( -// 'Choose a project type before continuing', boilerplates); + //var boilerplate = basicBoilerplate; + var boilerplate = prompts.choose( + 'Choose a project type before continuing', boilerplates); print( 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); @@ -200,14 +200,22 @@ const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( ); const BoilerplateInfo basicBoilerplate = const BoilerplateInfo( - 'Basic', - 'Minimal starting point - A simple server with only a few additional packages.', + 'Basic', + 'Minimal starting point for Angel 2.x - A simple server with only a few additional packages.', + 'https://github.com/angel-dart/angel.git', + ref: '2.x'); + +const BoilerplateInfo legacyBoilerplate = const BoilerplateInfo( + 'Legacy', + 'Minimal starting point for applications running Angel 1.1.x.', 'https://github.com/angel-dart/angel.git', + ref: '1.1.x', ); const List boilerplates = const [ basicBoilerplate, - ormBoilerplate, + legacyBoilerplate, + //ormBoilerplate, ]; class BoilerplateInfo { From 95ccb89fb6bb3e1562256f475e257fed4b4a0d4c Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:23:41 -0400 Subject: [PATCH 106/132] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac3dfca..d0e565db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.4.0 +* `init` can now produce either 1.x or 2.x projects. + # 1.3.4 * Fix another typo. From 68b56872bd78062e23c7d6c74e339f040083256e Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:28:53 -0400 Subject: [PATCH 107/132] Use single-branch clone for unique ref on boilerplate --- lib/src/commands/init.dart | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 372b7e75..a1da0f67 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -131,8 +131,30 @@ class InitCommand extends Command { print( 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); - var git = await Process.start("git", - ["clone", "--depth", "1", boilerplate.url, projectDir.absolute.path]); + + Process git; + + if (boilerplate.ref == null) { + git = await Process.start("git", [ + "clone", + "--depth", + "1", + boilerplate.url, + projectDir.absolute.path + ]); + } else { + // git clone --single-branch -b branch host:/dir.git + git = await Process.start("git", [ + "clone", + "--depth", + "1", + "--single-branch", + "-b", + boilerplate.ref, + boilerplate.url, + projectDir.absolute.path + ]); + } stdout.addStream(git.stdout); stderr.addStream(git.stderr); @@ -141,8 +163,10 @@ class InitCommand extends Command { throw new Exception("Could not clone repo."); } + /* if (boilerplate.ref != null) { - git = await Process.start("git", ["checkout", boilerplate.ref]); + git = await Process + .start("git", ["checkout", 'origin/${boilerplate.ref}']); stdout.addStream(git.stdout); stderr.addStream(git.stderr); @@ -151,6 +175,7 @@ class InitCommand extends Command { throw new Exception("Could not checkout branch ${boilerplate.ref}."); } } + */ if (boilerplate.needsPrebuild) { await preBuild(projectDir).catchError((_) => null); From d24c444f45622baf5b3e2ceea67e657530687628 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:30:12 -0400 Subject: [PATCH 108/132] Remove homedir --- lib/src/commands/install.dart | 1 - lib/src/util.dart | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index bc13e11f..691de76a 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:glob/glob.dart'; -import 'package:homedir/homedir.dart'; import 'package:io/ansi.dart'; import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as p; diff --git a/lib/src/util.dart b/lib/src/util.dart index e40b0d29..09ca3a59 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -8,6 +8,11 @@ final String checkmark = ansiOutputEnabled ? '\u2713' : '[Success]'; final String ballot = ansiOutputEnabled ? '\u2717' : '[Failure]'; +String get homeDirPath => + Platform.environment['HOME'] ?? Platform.environment['USERPROFILE']; + +Directory get homeDir => new Directory(homeDirPath); + Future loadPubspec([Directory directory]) { directory ??= Directory.current; var file = new File.fromUri(directory.uri.resolve('pubspec.yaml')); From 7db282d15b7debe01aa29404411630f2573c523f Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:34:59 -0400 Subject: [PATCH 109/132] Bump to 2.0 --- CHANGELOG.md | 3 ++- pubspec.yaml | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0e565db..0080c08a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ -# 1.4.0 +# 2.0.0 * `init` can now produce either 1.x or 2.x projects. +* Fixed deps for compatibility with Dart2 stable. # 1.3.4 * Fix another typo. diff --git a/pubspec.yaml b/pubspec.yaml index 67a3dd07..42a7d196 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,17 +9,16 @@ dependencies: code_builder: ^3.0.0 dart_style: ^1.0.0 glob: ^1.1.0 - homedir: ^0.0.4 http: ^0.11.3 - id: ^1.0.0 io: ^0.3.2 - inflection: ^0.4.1 - mustache4dart: ^2.0.0 + inflection: # Note, no new update has been published, even though I sent a PR... + git: https://github.com/gmosx/dart-inflection.git + mustache4dart: ^3.0.0-dev.1.0 path: ^1.0.0 prompts: ^1.0.0 pubspec_parse: ^0.1.2 quiver: ">=0.29.0" - recase: ^1.0.0 + recase: ^2.0.0 watcher: ^0.9.7 yaml: ^2.0.0 #yamlicious: ^0.0.5 From 1158feb81dce97e5f44071270ab8834d778cf007 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:38:41 -0400 Subject: [PATCH 110/132] Bump spec --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 42a7d196..f9f565c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 1.4.0 +version: 2.0.0 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 8a89f380e751e66dcb06fe7e6581052539aaa1c4 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:54:59 -0400 Subject: [PATCH 111/132] systemd deployment can install directly --- lib/src/commands/deploy/systemd.dart | 30 +++++++++++++++++++++++++++- lib/src/util.dart | 23 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/src/commands/deploy/systemd.dart b/lib/src/commands/deploy/systemd.dart index 4d4114bd..28c496a8 100644 --- a/lib/src/commands/deploy/systemd.dart +++ b/lib/src/commands/deploy/systemd.dart @@ -14,6 +14,8 @@ class SystemdCommand extends Command { SystemdCommand() { argParser + ..addOption('install', + abbr: 'i', help: 'A name to install this service as on the system.') ..addOption('user', abbr: 'u', defaultsTo: 'web', @@ -45,8 +47,34 @@ WantedBy=multi-user.target ''' .trim(); - if (!argResults.wasParsed('out')) { + if (!argResults.wasParsed('out') && !argResults.wasParsed('install')) { print(systemdText); + } else if (argResults.wasParsed('install')) { + var systemdPath = argResults.wasParsed('out') + ? argResults['out'] as String + : p.join('etc', 'systemd', 'system'); + var serviceFilename = p.join(systemdPath, + p.setExtension(argResults['install'] as String, '.service')); + var file = new File(serviceFilename); + await file.create(recursive: true); + await file.writeAsString(systemdText); + print(green.wrap( + "$checkmark Successfully generated systemd service in '${file.path}'.")); + + // sudo systemctl daemon-reload + if (await runCommand('sudo', ['systemctl', 'daemon-reload'])) { + // sudo service start + if (await runCommand('sudo', [ + 'service', + p.basenameWithoutExtension(serviceFilename), + 'start' + ])) { + } else { + print(red.wrap('$ballot Failed to install service system-wide.')); + } + } else { + print(red.wrap('$ballot Failed to install service system-wide.')); + } } else { var file = new File(argResults['out'] as String); await file.create(recursive: true); diff --git a/lib/src/util.dart b/lib/src/util.dart index 09ca3a59..f145d2a8 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -25,3 +25,26 @@ Future savePubspec(Pubspec pubspec) async { // TODO: Save pubspec for real? //var text = toYamlString(pubspec); } + +Future runCommand(String exec, List args) async { + var s = '$exec ${args.join(' ')}'.trim(); + stdout.write(darkGray.wrap('Running `$s`... ')); + + try { + var p = await Process.start(exec, args); + var code = await p.exitCode; + + if (code == 0) { + print(green.wrap(checkmark)); + return true; + } else { + print(red.wrap(ballot)); + await stdout.addStream(p.stdout); + await stderr.addStream(p.stderr); + return false; + } + } catch (e) { + print(red.wrap('$ballot Failed to run process.')); + return false; + } +} From a126332e29a09739467a9fe80f0483fd45d1ffc2 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 4 Oct 2018 11:57:18 -0400 Subject: [PATCH 112/132] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0080c08a..8dd9de37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.0.1 +* `deploy systemd` now has an `--install` option, where you can immediately +spawn the service. + # 2.0.0 * `init` can now produce either 1.x or 2.x projects. * Fixed deps for compatibility with Dart2 stable. From 30990fc63ef9d32e46ae055927e88b6cbacd2878 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 31 Dec 2018 11:09:46 -0500 Subject: [PATCH 113/132] fully deprecate install command --- CHANGELOG.md | 3 +++ lib/src/commands/install.dart | 6 ++++++ pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dd9de37..9a1c82e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.1.0 +* Deprecate `angel install`. + # 2.0.1 * `deploy systemd` now has an `--install` option, where you can immediately spawn the service. diff --git a/lib/src/commands/install.dart b/lib/src/commands/install.dart index 691de76a..d7a9ec37 100644 --- a/lib/src/commands/install.dart +++ b/lib/src/commands/install.dart @@ -47,6 +47,12 @@ class InstallCommand extends Command { @override run() async { + print(yellow.wrap( + 'WARNING: The `install` command is no longer considered necessary, and has been deprecated.\n' + 'Expect it to be removed in an upcoming release.\n\n' + 'See here: https://github.com/angel-dart/install.git\n\n' + 'To stop seeing this, downgrade to `package:angel_cli@<=2.0.0`.')); + if (argResults['wipe'] as bool) { if (await installRepo.exists()) await installRepo.delete(recursive: true); } else if (argResults['list'] as bool) { diff --git a/pubspec.yaml b/pubspec.yaml index f9f565c2..6c7377cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.0.0 +version: 2.1.0 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 27879f8b1994b746190ca2d8b8656b6f5bcb5611 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 31 Dec 2018 11:14:18 -0500 Subject: [PATCH 114/132] remove 1.x boilerplate --- CHANGELOG.md | 3 + lib/src/commands/init.dart | 16 ++-- lib/src/commands/rename.dart | 7 ++ sample-project/.dockerignore | 10 ++ sample-project/.gitignore | 90 ++++++++++++++++++ sample-project/.idea/angel.iml | 16 ++++ sample-project/.idea/misc.xml | 28 ++++++ sample-project/.idea/modules.xml | 8 ++ .../.idea/runConfigurations/dev_dart.xml | 8 ++ .../.idea/runConfigurations/prod_dart.xml | 11 +++ sample-project/.vscode/launch.json | 19 ++++ sample-project/.vscode/tasks.json | 27 ++++++ sample-project/Dockerfile | 14 +++ sample-project/LICENSE | 21 ++++ sample-project/README.md | 67 +++++++++++++ sample-project/analysis_options.yaml | 4 + sample-project/bin/dev.dart | 26 +++++ sample-project/bin/prod.dart | 4 + sample-project/config/default.yaml | 5 + sample-project/config/development.yaml | 2 + sample-project/config/production.yaml | 3 + sample-project/lib/sample_project.dart | 20 ++++ sample-project/lib/src/config/config.dart | 32 +++++++ .../lib/src/config/plugins/plugins.dart | 8 ++ .../src/routes/controllers/controllers.dart | 8 ++ sample-project/lib/src/routes/routes.dart | 61 ++++++++++++ sample-project/lib/src/services/services.dart | 14 +++ sample-project/pubspec.yaml | 20 ++++ sample-project/test/all_test.dart | 43 +++++++++ sample-project/views/error.jael | 5 + sample-project/views/hello.jael | 5 + sample-project/views/layout.jael | 17 ++++ sample-project/web/css/site.css | 27 ++++++ sample-project/web/images/favicon.png | Bin 0 -> 2853 bytes sample-project/web/robots.txt | 2 + 35 files changed, 643 insertions(+), 8 deletions(-) create mode 100644 sample-project/.dockerignore create mode 100644 sample-project/.gitignore create mode 100644 sample-project/.idea/angel.iml create mode 100644 sample-project/.idea/misc.xml create mode 100644 sample-project/.idea/modules.xml create mode 100644 sample-project/.idea/runConfigurations/dev_dart.xml create mode 100644 sample-project/.idea/runConfigurations/prod_dart.xml create mode 100644 sample-project/.vscode/launch.json create mode 100644 sample-project/.vscode/tasks.json create mode 100644 sample-project/Dockerfile create mode 100644 sample-project/LICENSE create mode 100644 sample-project/README.md create mode 100644 sample-project/analysis_options.yaml create mode 100644 sample-project/bin/dev.dart create mode 100644 sample-project/bin/prod.dart create mode 100644 sample-project/config/default.yaml create mode 100644 sample-project/config/development.yaml create mode 100644 sample-project/config/production.yaml create mode 100644 sample-project/lib/sample_project.dart create mode 100644 sample-project/lib/src/config/config.dart create mode 100644 sample-project/lib/src/config/plugins/plugins.dart create mode 100644 sample-project/lib/src/routes/controllers/controllers.dart create mode 100644 sample-project/lib/src/routes/routes.dart create mode 100644 sample-project/lib/src/services/services.dart create mode 100644 sample-project/pubspec.yaml create mode 100644 sample-project/test/all_test.dart create mode 100644 sample-project/views/error.jael create mode 100644 sample-project/views/hello.jael create mode 100644 sample-project/views/layout.jael create mode 100644 sample-project/web/css/site.css create mode 100644 sample-project/web/images/favicon.png create mode 100644 sample-project/web/robots.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a1c82e6..b3c221c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # 2.1.0 * Deprecate `angel install`. +* Rename projects using `snake_case`. +* `init` now fetches from `master`. +* Remove the `1.x` option. # 2.0.1 * `deploy systemd` now has an `--install` option, where you can immediately diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index a1da0f67..4766d390 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -4,6 +4,7 @@ import "package:args/command_runner.dart"; import 'package:io/ansi.dart'; import 'package:path/path.dart' as p; import 'package:prompts/prompts.dart' as prompts; +import 'package:recase/recase.dart'; import '../random_string.dart' as rs; import '../util.dart'; import 'key.dart'; @@ -45,6 +46,7 @@ class InitCommand extends Command { var name = p.basenameWithoutExtension( projectDir.absolute.uri.normalizePath().toFilePath()); + name = ReCase(name).snakeCase; print('Renaming project from "angel" to "$name"...'); await renamePubspec(projectDir, 'angel', name); await renameDartFiles(projectDir, 'angel', name); @@ -123,11 +125,10 @@ class InitCommand extends Command { } } - print('Choose a project type before continuing:'); - - //var boilerplate = basicBoilerplate; - var boilerplate = prompts.choose( - 'Choose a project type before continuing', boilerplates); + var boilerplate = basicBoilerplate; + // print('Choose a project type before continuing:'); + // var boilerplate = prompts.choose( + // 'Choose a project type before continuing', boilerplates); print( 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); @@ -227,8 +228,7 @@ const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( const BoilerplateInfo basicBoilerplate = const BoilerplateInfo( 'Basic', 'Minimal starting point for Angel 2.x - A simple server with only a few additional packages.', - 'https://github.com/angel-dart/angel.git', - ref: '2.x'); + 'https://github.com/angel-dart/angel.git'); const BoilerplateInfo legacyBoilerplate = const BoilerplateInfo( 'Legacy', @@ -239,7 +239,7 @@ const BoilerplateInfo legacyBoilerplate = const BoilerplateInfo( const List boilerplates = const [ basicBoilerplate, - legacyBoilerplate, + //legacyBoilerplate, //ormBoilerplate, ]; diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index fee799eb..98a3752c 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -139,6 +139,13 @@ class RenamingVisitor extends RecursiveAstVisitor { return uri; } + @override + visitSimpleStringLiteral(SimpleStringLiteral node) { + if (node.value == '{{$oldName}}') { + replace[[node.value]] = newName; + } + } + @override visitExportDirective(ExportDirective ctx) { var uri = ctx.uri.stringValue, updated = updateUri(uri); diff --git a/sample-project/.dockerignore b/sample-project/.dockerignore new file mode 100644 index 00000000..faf23bd1 --- /dev/null +++ b/sample-project/.dockerignore @@ -0,0 +1,10 @@ +.dart_tool +.idea +.pub +.vscode +logs/ +test/ +build/ +.analysis-options +.packages +*.g.dart \ No newline at end of file diff --git a/sample-project/.gitignore b/sample-project/.gitignore new file mode 100644 index 00000000..d88b0dfb --- /dev/null +++ b/sample-project/.gitignore @@ -0,0 +1,90 @@ +# Created by .ignore support plugin (hsz.mobi) +### Dart template +# See https://www.dartlang.org/tools/private-files.html + +# source_gen +.dart_tool + +# Files and directories created by pub +.buildlog +.packages +.project +.pub/ +.scripts-bin/ +build/ +**/packages/ + +# Files created by dart2js +# (Most Dart developers will use pub build to compile Dart, use/modify these +# rules if you intend to use dart2js directly +# Convention is to use extension '.dart.js' for Dart compiled to Javascript to +# differentiate from explicit Javascript files) +*.dart.js +*.part.js +*.js.deps +*.js.map +*.info.json + +# Directory created by dartdoc +doc/api/ + +# Don't commit pubspec lock file +# (Library packages only! Remove pattern if developing an application package) +pubspec.lock +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### VSCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +logs/ +*.pem +.DS_Store +server_log.txt diff --git a/sample-project/.idea/angel.iml b/sample-project/.idea/angel.iml new file mode 100644 index 00000000..eae13016 --- /dev/null +++ b/sample-project/.idea/angel.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample-project/.idea/misc.xml b/sample-project/.idea/misc.xml new file mode 100644 index 00000000..c65900a0 --- /dev/null +++ b/sample-project/.idea/misc.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + General + + + XPath + + + + + AngularJS + + + + + + \ No newline at end of file diff --git a/sample-project/.idea/modules.xml b/sample-project/.idea/modules.xml new file mode 100644 index 00000000..b0343846 --- /dev/null +++ b/sample-project/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/sample-project/.idea/runConfigurations/dev_dart.xml b/sample-project/.idea/runConfigurations/dev_dart.xml new file mode 100644 index 00000000..418187ff --- /dev/null +++ b/sample-project/.idea/runConfigurations/dev_dart.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/sample-project/.idea/runConfigurations/prod_dart.xml b/sample-project/.idea/runConfigurations/prod_dart.xml new file mode 100644 index 00000000..e93c56a5 --- /dev/null +++ b/sample-project/.idea/runConfigurations/prod_dart.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/sample-project/.vscode/launch.json b/sample-project/.vscode/launch.json new file mode 100644 index 00000000..ab26e089 --- /dev/null +++ b/sample-project/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Development Server", + "type": "dart", + "request": "launch", + "program": "${workspaceRoot}/bin/dev.dart", + "checkedMode": true + }, + { + "name": "Production Server", + "type": "dart", + "request": "launch", + "program": "${workspaceRoot}/bin/prod.dart", + "checkedMode": true + } + ] +} \ No newline at end of file diff --git a/sample-project/.vscode/tasks.json b/sample-project/.vscode/tasks.json new file mode 100644 index 00000000..0479cc0b --- /dev/null +++ b/sample-project/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "pub", + "isShellCommand": true, + "echoCommand": true, + "showOutput": "always", + "tasks": [ + { + "taskName": "pub:build", + "suppressTaskName": true, + "args": [ + "build" + ] + }, + { + "taskName": "pub:serve", + "showOutput": "silent", + "suppressTaskName": true, + "isBackground": true, + "args": [ + "serve" + ] + } + ] +} \ No newline at end of file diff --git a/sample-project/Dockerfile b/sample-project/Dockerfile new file mode 100644 index 00000000..f9056deb --- /dev/null +++ b/sample-project/Dockerfile @@ -0,0 +1,14 @@ +FROM google/dart:2.0 + +COPY ./ ./ + +# Install dependencies, pre-build +RUN pub get + +# Optionally build generaed sources. +# RUN pub run build_runner build + +# Set environment, start server +ENV ANGEL_ENV=production +EXPOSE 3000 +CMD dart bin/prod.dart \ No newline at end of file diff --git a/sample-project/LICENSE b/sample-project/LICENSE new file mode 100644 index 00000000..52a1469a --- /dev/null +++ b/sample-project/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 angel-dart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sample-project/README.md b/sample-project/README.md new file mode 100644 index 00000000..aff56f62 --- /dev/null +++ b/sample-project/README.md @@ -0,0 +1,67 @@ +[![The Angel Framework](https://angel-dart.github.io/assets/images/logo.png)](https://angel-dart.github.io) + +[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/angel_dart/discussion) +[![Pub](https://img.shields.io/pub/v/angel_framework.svg)](https://pub.dartlang.org/packages/angel_framework) +[![Build status](https://travis-ci.org/angel-dart/framework.svg?branch=master)](https://travis-ci.org/angel-dart/framework) +![License](https://img.shields.io/github/license/angel-dart/framework.svg) + +**A batteries-included, full-stack Web server framework for Dart.** + +----- + +[Wiki (in-depth documentation)](https://github.com/angel-dart/angel/wiki) + +[API Documentation](http://www.dartdocs.org/documentation/angel_common/latest) + +[Roadmap](https://github.com/angel-dart/roadmap/blob/master/ROADMAP.md) + +[File an Issue](https://github.com/angel-dart/roadmap/issues) + +[Awesome Angel :fire:](https://github.com/angel-dart/awesome-angel) + + + +Like what you see? Please lend us a star! :star: + +## Newest Tutorials +* [Dependency Injection Patterns with Angel 2](https://thosakwe.com/dependency-injection-patterns-in-angel-2/) +* [Angel 2.0.0 is Almost Here - What it Means for You](https://thosakwe.com/new-features-coming-to-angel-in-version-2-0-0/) +* [GraphQL is coming to Angel (and Dart)](https://thosakwe.com/graphql-is-coming-to-angel-and-dart/) + +## Installation & Setup +*Having errors with a fresh Angel installation? See [here](https://angel-dart.gitbook.io/angel/the-basics/installation) for help.* + +Once you have [Dart](https://www.dartlang.org/) installed, bootstrapping a project is as simple as running a few shell commands: + +Install the [Angel CLI](https://github.com/angel-dart/cli): + +```bash +pub global activate angel_cli +``` + +Bootstrap a project: + +```bash +angel init hello +``` + +You can even have your server run and be *hot-reloaded* on file changes: + +```bash +dart --observe bin/dev.dart +``` + +Next, check out the [detailed documentation](https://angel-dart.gitbook.io/angel) to learn to flesh out your project. + +## Features +With features like the following, Angel is the all-in-one framework you should choose to build your next project: +* [Advanced, Modular Routing](https://github.com/angel-dart/route) +* [Middleware](https://angel-dart.gitbook.io/angel/the-basics/middleware) +* [Dependency Injection](https://angel-dart.gitbook.io/angel/the-basics/dependency-injection) +* [Strongly-typed ORM](https://github.com/angel-dart/orm) +* And [much more](https://github.com/angel-dart)... + +## Basic Example +Examples and complete projects can be found here: + +https://github.com/angel-dart/examples-v2 diff --git a/sample-project/analysis_options.yaml b/sample-project/analysis_options.yaml new file mode 100644 index 00000000..a4f33350 --- /dev/null +++ b/sample-project/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:pedantic/analysis_options.yaml +analyzer: + strong-mode: + implicit-casts: false diff --git a/sample-project/bin/dev.dart b/sample-project/bin/dev.dart new file mode 100644 index 00000000..9347bc87 --- /dev/null +++ b/sample-project/bin/dev.dart @@ -0,0 +1,26 @@ +import 'dart:io'; +import 'package:sample_project/src/pretty_logging.dart'; +import 'package:sample_project/sample_project.dart'; +import 'package:angel_container/mirrors.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_hot/angel_hot.dart'; +import 'package:logging/logging.dart'; + +main() async { + // Watch the config/ and web/ directories for changes, and hot-reload the server. + var hot = new HotReloader(() async { + var app = new Angel(reflector: MirrorsReflector()); + await app.configure(configureServer); + hierarchicalLoggingEnabled = true; + app.logger = new Logger('angel'); + var sub = app.logger.onRecord.listen(prettyLog); + app.shutdownHooks.add((_) => sub.cancel()); + return app; + }, [ + new Directory('config'), + new Directory('lib'), + ]); + + var server = await hot.startServer('127.0.0.1', 3000); + print('Listening at http://${server.address.address}:${server.port}'); +} diff --git a/sample-project/bin/prod.dart b/sample-project/bin/prod.dart new file mode 100644 index 00000000..fdaf2e12 --- /dev/null +++ b/sample-project/bin/prod.dart @@ -0,0 +1,4 @@ +import 'package:sample_project/sample_project.dart'; +import 'package:angel_production/angel_production.dart'; + +main(List args) => Runner('angel', configureServer).run(args); diff --git a/sample-project/config/default.yaml b/sample-project/config/default.yaml new file mode 100644 index 00000000..8f6fea0d --- /dev/null +++ b/sample-project/config/default.yaml @@ -0,0 +1,5 @@ +# Default server configuration. +host: 127.0.0.1 +mongo_db: mongodb://localhost:27017/angel +port: 3000 +jwt_secret: "IIeewWFKTdCJIT5zLh09J2gFKmfpgh0J" \ No newline at end of file diff --git a/sample-project/config/development.yaml b/sample-project/config/development.yaml new file mode 100644 index 00000000..4bed71e2 --- /dev/null +++ b/sample-project/config/development.yaml @@ -0,0 +1,2 @@ +# Development-only server configuration. +debug: true \ No newline at end of file diff --git a/sample-project/config/production.yaml b/sample-project/config/production.yaml new file mode 100644 index 00000000..a6ff83f2 --- /dev/null +++ b/sample-project/config/production.yaml @@ -0,0 +1,3 @@ +# Production-only server configuration +debug: false +jwt_secret: "DJ3JnBDBS6UcnbAGwPQb3m7YWxja7KLZ" \ No newline at end of file diff --git a/sample-project/lib/sample_project.dart b/sample-project/lib/sample_project.dart new file mode 100644 index 00000000..0da05718 --- /dev/null +++ b/sample-project/lib/sample_project.dart @@ -0,0 +1,20 @@ +library sample_project; + +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:file/local.dart'; +import 'src/config/config.dart' as configuration; +import 'src/routes/routes.dart' as routes; +import 'src/services/services.dart' as services; + +/// Configures the server instance. +Future configureServer(Angel app) async { + // Grab a handle to the file system, so that we can do things like + // serve static files. + var fs = const LocalFileSystem(); + + // Set up our application, using the plug-ins defined with this project. + await app.configure(configuration.configureServer(fs)); + await app.configure(services.configureServer); + await app.configure(routes.configureServer(fs)); +} diff --git a/sample-project/lib/src/config/config.dart b/sample-project/lib/src/config/config.dart new file mode 100644 index 00000000..e7b8c562 --- /dev/null +++ b/sample-project/lib/src/config/config.dart @@ -0,0 +1,32 @@ +library sample_project.src.config; + +import 'package:angel_configuration/angel_configuration.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_jael/angel_jael.dart'; +import 'package:file/file.dart'; +import 'plugins/plugins.dart' as plugins; + +/// This is a perfect place to include configuration and load plug-ins. +AngelConfigurer configureServer(FileSystem fileSystem) { + return (Angel app) async { + // Load configuration from the `config/` directory. + // + // See: https://github.com/angel-dart/configuration + await app.configure(configuration(fileSystem)); + + // Configure our application to render Jael templates from the `views/` directory. + // + // See: https://github.com/angel-dart/jael + await app.configure(jael(fileSystem.directory('views'))); + + // Apply another plug-ins, i.e. ones that *you* have written. + // + // Typically, the plugins in `lib/src/config/plugins/plugins.dart` are plug-ins + // that add functionality specific to your application. + // + // If you write a plug-in that you plan to use again, or are + // using one created by the community, include it in + // `lib/src/config/config.dart`. + await plugins.configureServer(app); + }; +} diff --git a/sample-project/lib/src/config/plugins/plugins.dart b/sample-project/lib/src/config/plugins/plugins.dart new file mode 100644 index 00000000..5e6d9ea3 --- /dev/null +++ b/sample-project/lib/src/config/plugins/plugins.dart @@ -0,0 +1,8 @@ +library sample_project.src.config.plugins; + +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; + +Future configureServer(Angel app) async { + // Include any plugins you have made here. +} diff --git a/sample-project/lib/src/routes/controllers/controllers.dart b/sample-project/lib/src/routes/controllers/controllers.dart new file mode 100644 index 00000000..1f10cc1a --- /dev/null +++ b/sample-project/lib/src/routes/controllers/controllers.dart @@ -0,0 +1,8 @@ +library sample_project.src.routes.controllers; + +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; + +Future configureServer(Angel app) async { + /// Controllers will not function unless wired to the application! +} diff --git a/sample-project/lib/src/routes/routes.dart b/sample-project/lib/src/routes/routes.dart new file mode 100644 index 00000000..8953bd40 --- /dev/null +++ b/sample-project/lib/src/routes/routes.dart @@ -0,0 +1,61 @@ +library sample_project.src.routes; + +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_static/angel_static.dart'; +import 'package:file/file.dart'; +import 'controllers/controllers.dart' as controllers; + +/// Put your app routes here! +/// +/// See the wiki for information about routing, requests, and responses: +/// * https://github.com/angel-dart/angel/wiki/Basic-Routing +/// * https://github.com/angel-dart/angel/wiki/Requests-&-Responses +AngelConfigurer configureServer(FileSystem fileSystem) { + return (Angel app) async { + // Typically, you want to mount controllers first, after any global middleware. + await app.configure(controllers.configureServer); + + // Render `views/hello.jl` when a user visits the application root. + app.get('/', (req, res) => res.render('hello')); + + // Mount static server at web in development. + // The `CachingVirtualDirectory` variant of `VirtualDirectory` also sends `Cache-Control` headers. + // + // In production, however, prefer serving static files through NGINX or a + // similar reverse proxy. + // + // Read the following two sources for documentation: + // * https://medium.com/the-angel-framework/serving-static-files-with-the-angel-framework-2ddc7a2b84ae + // * https://github.com/angel-dart/static + if (!app.isProduction) { + var vDir = VirtualDirectory( + app, + fileSystem, + source: fileSystem.directory('web'), + ); + app.fallback(vDir.handleRequest); + } + + // Throw a 404 if no route matched the request. + app.fallback((req, res) => throw AngelHttpException.notFound()); + + // Set our application up to handle different errors. + // + // Read the following for documentation: + // * https://github.com/angel-dart/angel/wiki/Error-Handling + + var oldErrorHandler = app.errorHandler; + app.errorHandler = (e, req, res) async { + if (!req.accepts('text/html')) + return await oldErrorHandler(e, req, res); + else { + if (e.statusCode == 404) { + return await res + .render('error', {'message': 'No file exists at ${req.uri}.'}); + } + + return await res.render('error', {'message': e.message}); + } + }; + }; +} diff --git a/sample-project/lib/src/services/services.dart b/sample-project/lib/src/services/services.dart new file mode 100644 index 00000000..68f1123e --- /dev/null +++ b/sample-project/lib/src/services/services.dart @@ -0,0 +1,14 @@ +library sample_project.services; + +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; + +/// Configure our application to use *services*. +/// Services must be wired to the app via `app.use`. +/// +/// They provide many benefits, such as instant REST API generation, +/// and respond to both REST and WebSockets. +/// +/// Read more here: +/// https://github.com/angel-dart/angel/wiki/Service-Basics +Future configureServer(Angel app) async {} diff --git a/sample-project/pubspec.yaml b/sample-project/pubspec.yaml new file mode 100644 index 00000000..a1c5aa38 --- /dev/null +++ b/sample-project/pubspec.yaml @@ -0,0 +1,20 @@ +name: sample_project +description: An app that's going to be amazing pretty soon. +publish_to: none # Ensure we don't accidentally publish our private code! ;) +environment: + sdk: '>=2.0.0-dev <3.0.0' +homepage: https://github.com/angel-dart/angel +dependencies: + angel_auth: ^2.0.0-alpha # Supports stateless authentication via JWT + angel_configuration: ^2.0.0 # Loads application configuration, along with support for .env files. + angel_framework: ^2.0.0-alpha # The core server library. + angel_jael: ^2.0.0 # Server-side templating engine + angel_production: ^1.0.0-alpha + angel_static: ^2.0.0-alpha # Static file server + angel_validate: ^2.0.0-alpha # Allows for validation of input data +dev_dependencies: + angel_hot: ^2.0.0 # Hot-reloading support. :) + angel_test: ^2.0.0-alpha # Utilities for testing Angel servers. + io: ^0.3.2 + pedantic: ^1.0.0 + test: ^1.0.0 diff --git a/sample-project/test/all_test.dart b/sample-project/test/all_test.dart new file mode 100644 index 00000000..06bec6e5 --- /dev/null +++ b/sample-project/test/all_test.dart @@ -0,0 +1,43 @@ +import 'package:sample_project/sample_project.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_test/angel_test.dart'; +import 'package:test/test.dart'; + +// Angel also includes facilities to make testing easier. +// +// `package:angel_test` ships a client that can test +// both plain HTTP and WebSockets. +// +// Tests do not require your server to actually be mounted on a port, +// so they will run faster than they would in other frameworks, where you +// would have to first bind a socket, and then account for network latency. +// +// See the documentation here: +// https://github.com/angel-dart/test +// +// If you are unfamiliar with Dart's advanced testing library, you can read up +// here: +// https://github.com/dart-lang/test + +main() async { + TestClient client; + + setUp(() async { + var app = Angel(); + await app.configure(configureServer); + + client = await connectTo(app); + }); + + tearDown(() async { + await client.close(); + }); + + test('index returns 200', () async { + // Request a resource at the given path. + var response = await client.get('/'); + + // Expect a 200 response. + expect(response, hasStatus(200)); + }); +} diff --git a/sample-project/views/error.jael b/sample-project/views/error.jael new file mode 100644 index 00000000..1e93df46 --- /dev/null +++ b/sample-project/views/error.jael @@ -0,0 +1,5 @@ + + +
{{ message }}
+
+
\ No newline at end of file diff --git a/sample-project/views/hello.jael b/sample-project/views/hello.jael new file mode 100644 index 00000000..7ca6b8af --- /dev/null +++ b/sample-project/views/hello.jael @@ -0,0 +1,5 @@ + + +
Angel
+
+
\ No newline at end of file diff --git a/sample-project/views/layout.jael b/sample-project/views/layout.jael new file mode 100644 index 00000000..ea0714bc --- /dev/null +++ b/sample-project/views/layout.jael @@ -0,0 +1,17 @@ + + + + {{ title ?? 'Angel' }} + + + + + + +
+
+ +
+
+ + \ No newline at end of file diff --git a/sample-project/web/css/site.css b/sample-project/web/css/site.css new file mode 100644 index 00000000..9e40b8de --- /dev/null +++ b/sample-project/web/css/site.css @@ -0,0 +1,27 @@ +html, body { + height: 100%; +} + +body { + margin: 0; + padding: 0; + width: 100%; + display: table; + font-weight: 100; + font-family: 'Lato', sans-serif; +} + +.container { + text-align: center; + display: table-cell; + vertical-align: middle; +} + +.content { + text-align: center; + display: inline-block; +} + +.title { + font-size: 96px; +} \ No newline at end of file diff --git a/sample-project/web/images/favicon.png b/sample-project/web/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8be3ad791e165bad029dcf6f9c23c6aeb1c66fc9 GIT binary patch literal 2853 zcmaJ@XIN9&77aZCK}sm{R6^)24G0jWB>{;P1JV=}E+LR8DI@_xk)T8cMMQAKL2<-U zP>>>6p9mNklrl2lC`Ay1g7i8geSE=D=lyuT``vrbKKom1?Q-^ylS%g3p{%H@2m*nW zNghN$$?CHHf#oItQhMwg$+C&(7R=ktisB^z94g2)k`+OPkr+TU)sG59CdM{Xw}U`3 zhIId6Ua+?pj>2LffOQOl&tOZ~AkcPaJ{zDMr1D@9)Mz@>5kCE}9uA{NI>Li&y;0t5 z0+mMhNa9d;C;9kOk`7WFBH_+Xu`jIdSR5+M7GZ5gL7~wwEEa*bvBhH1mM{zojX|O$FV+fegR{lrFm|vn4_p$B z6B&i`Bf5WyCD}Q`X*?bqheReMBp?#35iCwL67AsNu&#l@SV<68+(af1;9D`d245A3 zR4#=>XY=SRCTv|1h+xI>9O05o|1N>S{wB-herc1WVMso}Mxqg@^^(2^muBdJBP)9eJ&bD|9&p^ z-_`rVjr@Kt4&UV>CCMPykM_Tg`m0MaKkMeV!If;jjXsqrnRkw4uy>9=^8kSq@<~Kj ze|~RY2tCF>0Geuj``Wc@CtoSw0$(9t^>x*k)>RW^gs=g~74q2ww-Et9yW3ok9X;#T zc*+!h-IwhuTVxJ{Nx!W22u(XiB`TU~n&CU{w()%yjT5t34?b8ej*;p%6h4rcm*xwH z_Tw+~-s_S!HBl>8pM%^}J+PWYUewuTDcem_EK(Muik`J=<6bFd$i(M*(!7Ir&^`(L z(n_JgpLXhqbj;q1(ikvZrE+pdH}SersE7TF%6zZc#neK`8l-<_&|A1q_=9jGT7R2E zHX9*ESdz`fJkf&d-83H|7|;Y1#kgAgm1OkgN8`e=9^CMisKrKz;M0B-LZ8%%*{TRUZ1znhmn^`A^1^)tvNWjL)i`)2Ujm z3|>PGMbtTTj`ZksTeXG(iGVVEg&Zu_)utIM@QrI z0BBcRQ$m+Uw(3V0wK}uQGGZr%uKhAb&?nK8S4})P$(;*~TXVIY?`HdFg~<>-jpz8N zkqgqP#-W30wC9fxOa|&)D7T(G(7#X)xNY9D^~E8*!y&e5UI{C|$N=wqMo|u#HtJ)V zP3XM(Oz;d?e0i#;J>69A_fz#}*t$@T8ldlCJ(VwQFAkR4?G(bEJRl0k2c6_UZ@dql z;%qa{%ef_#7CFwuDQE6>YWt~t@X+d`mUh!BrH!w5o>#4CAHn zWvOQy7Z=crUe8_%0e)cm)Qa(Ywb*^LjaoMC_r^91&DeY4&OH7h93P*Xu4(Ds(Frc= zJw6_NxaUs*t7L)8Lv;+kiH4Q%&!M_la#=3@zltXyGmpJ)#y~Y{ay1|(8W>32%slQU zts44n(;@Mvqb|mw`tq-$$8n&t+ofje@3u8W*iUyuS}R1+d4vq%0E*EX$=RCR`U?&QXn=iyMgYJ)p3^z{9lPt5DaC2q&yy_Pirq84Np}XYLzZG>U8#O5{jk9Y_7qlZ(#gfBJdL?B}eRsVL>_| z&u$9mo%#Dej68s!O%Ex^RrTMM(VVCch3O+zD?pyyH%^-j)tgEJ>grV~D&noKXS=G% z#mZ;Eov)Li$X`)(>mw(}Z-y;aYMZyZH`JjlDu*E=Tn#m`FH{ALY(tkwlPeVCq5f|-6 z`@Dj>kZzsO$i#w~>$lBXhSxM^pmY4^n*}dUZ5oT1^A#5B&iHg24(!_DF?B4<-&}C{ ze&q$G>0i&s}x#CC8CymXahHP8sYX`vIw^+fS3AZb6%ID#KTG{&Jj=Te4q^h z1BIeKpiUug+9Gsq<88qdKCw8>SM;)ShW6)=1dQfWju=bY-R+%g4kO%OZq4f4r69Us zny=JkJuWE6kPE>5rOmn*dS(cA<&!o*bmk$!m9e)uZ!a4LrKf0J<~CS3eO!B7nr?(Z zRm*u@s(I9$tK_fQkhvpMD@81$X|YCbP&Or`TgIa5hwn&tFHEzf%Gw_^>Gs!l*7`ND zC+i0~tMlAZf-8fqL|IOGS}0V{HNPDkW(@yVpX<=ubBwJG$F&~Hr_3qKYYw;~?bB-N zs`PANENO2`+1fsv@uRdn z(7hnN^1R#Ke3QKclMqPT zPfur~nA~o|DbGMbfkIx_TTbqwUuH=b=8mLZ8u7@4fFfJG`DOOhy@#4kMM-(#tD#%w zk8l3Nb}-5}jhousk%(POIvwa7K%RBu<^$(>(H zR%BvjWJdxQRg1?E8+R0NwDa1l;I7O#y}(Gnid!Ygs6;1Ln@Lj{)T^LfZ`R?xvV7gm jx#x-C!eb$k0wom4?$QJ5X&YAe`o)#x=0mK&hadS5nCG_x literal 0 HcmV?d00001 diff --git a/sample-project/web/robots.txt b/sample-project/web/robots.txt new file mode 100644 index 00000000..f328961c --- /dev/null +++ b/sample-project/web/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /admin \ No newline at end of file From 5775c2ff1a71451ccfe1ce8d9e2009beb5e73531 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 31 Dec 2018 11:29:40 -0500 Subject: [PATCH 115/132] 2.1.0 --- .gitignore | 2 + CHANGELOG.md | 3 + lib/src/commands/init.dart | 5 +- lib/src/commands/make.dart | 2 + lib/src/commands/make/migration.dart | 133 ++++++++++++++++++ lib/src/commands/make/model.dart | 122 ++++------------ sample-project/.dockerignore | 10 -- sample-project/.gitignore | 90 ------------ sample-project/.idea/angel.iml | 16 --- sample-project/.idea/misc.xml | 28 ---- sample-project/.idea/modules.xml | 8 -- .../.idea/runConfigurations/dev_dart.xml | 8 -- .../.idea/runConfigurations/prod_dart.xml | 11 -- sample-project/.vscode/launch.json | 19 --- sample-project/.vscode/tasks.json | 27 ---- sample-project/Dockerfile | 14 -- sample-project/LICENSE | 21 --- sample-project/README.md | 67 --------- sample-project/analysis_options.yaml | 4 - sample-project/bin/dev.dart | 26 ---- sample-project/bin/prod.dart | 4 - sample-project/config/default.yaml | 5 - sample-project/config/development.yaml | 2 - sample-project/config/production.yaml | 3 - sample-project/lib/sample_project.dart | 20 --- sample-project/lib/src/config/config.dart | 32 ----- .../lib/src/config/plugins/plugins.dart | 8 -- .../src/routes/controllers/controllers.dart | 8 -- sample-project/lib/src/routes/routes.dart | 61 -------- sample-project/lib/src/services/services.dart | 14 -- sample-project/pubspec.yaml | 20 --- sample-project/test/all_test.dart | 43 ------ sample-project/views/error.jael | 5 - sample-project/views/hello.jael | 5 - sample-project/views/layout.jael | 17 --- sample-project/web/css/site.css | 27 ---- sample-project/web/images/favicon.png | Bin 2853 -> 0 bytes sample-project/web/robots.txt | 2 - 38 files changed, 166 insertions(+), 726 deletions(-) create mode 100644 lib/src/commands/make/migration.dart delete mode 100644 sample-project/.dockerignore delete mode 100644 sample-project/.gitignore delete mode 100644 sample-project/.idea/angel.iml delete mode 100644 sample-project/.idea/misc.xml delete mode 100644 sample-project/.idea/modules.xml delete mode 100644 sample-project/.idea/runConfigurations/dev_dart.xml delete mode 100644 sample-project/.idea/runConfigurations/prod_dart.xml delete mode 100644 sample-project/.vscode/launch.json delete mode 100644 sample-project/.vscode/tasks.json delete mode 100644 sample-project/Dockerfile delete mode 100644 sample-project/LICENSE delete mode 100644 sample-project/README.md delete mode 100644 sample-project/analysis_options.yaml delete mode 100644 sample-project/bin/dev.dart delete mode 100644 sample-project/bin/prod.dart delete mode 100644 sample-project/config/default.yaml delete mode 100644 sample-project/config/development.yaml delete mode 100644 sample-project/config/production.yaml delete mode 100644 sample-project/lib/sample_project.dart delete mode 100644 sample-project/lib/src/config/config.dart delete mode 100644 sample-project/lib/src/config/plugins/plugins.dart delete mode 100644 sample-project/lib/src/routes/controllers/controllers.dart delete mode 100644 sample-project/lib/src/routes/routes.dart delete mode 100644 sample-project/lib/src/services/services.dart delete mode 100644 sample-project/pubspec.yaml delete mode 100644 sample-project/test/all_test.dart delete mode 100644 sample-project/views/error.jael delete mode 100644 sample-project/views/hello.jael delete mode 100644 sample-project/views/layout.jael delete mode 100644 sample-project/web/css/site.css delete mode 100644 sample-project/web/images/favicon.png delete mode 100644 sample-project/web/robots.txt diff --git a/.gitignore b/.gitignore index 0d7438ab..b0dcebde 100644 --- a/.gitignore +++ b/.gitignore @@ -75,4 +75,6 @@ pubspec.lock /sample_project/lib/src/services/ /sample_project/test/services/ /sample_project/ +sample_project/ +sample-project .dart_tool \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c221c5..6a021635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ * Rename projects using `snake_case`. * `init` now fetches from `master`. * Remove the `1.x` option. +* Add `make migration` command. +* Replace `{{oldName}}` in the `rename` command. +* `pub get` now runs with `inheritStdio`. # 2.0.1 * `deploy systemd` now has an `--install` option, where you can immediately diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 4766d390..cc8fcf16 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -196,9 +196,8 @@ class InitCommand extends Command { var pubPath = resolvePub(); print('Running pub at "$pubPath"...'); var pub = await Process.start(pubPath, ["get"], - workingDirectory: projectDir.absolute.path); - stdout.addStream(pub.stdout); - stderr.addStream(pub.stderr); + workingDirectory: projectDir.absolute.path, + mode: ProcessStartMode.inheritStdio); var code = await pub.exitCode; print("Pub process exited with code $code"); } diff --git a/lib/src/commands/make.dart b/lib/src/commands/make.dart index 6f091e07..219fedf1 100644 --- a/lib/src/commands/make.dart +++ b/lib/src/commands/make.dart @@ -1,5 +1,6 @@ import 'package:args/command_runner.dart'; import 'make/controller.dart'; +import 'make/migration.dart'; import 'make/model.dart'; import 'make/plugin.dart'; import 'make/service.dart'; @@ -15,6 +16,7 @@ class MakeCommand extends Command { MakeCommand() { addSubcommand(new ControllerCommand()); + addSubcommand(new MigrationCommand()); addSubcommand(new ModelCommand()); addSubcommand(new PluginCommand()); addSubcommand(new TestCommand()); diff --git a/lib/src/commands/make/migration.dart b/lib/src/commands/make/migration.dart new file mode 100644 index 00000000..507540f5 --- /dev/null +++ b/lib/src/commands/make/migration.dart @@ -0,0 +1,133 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; +import 'package:inflection/inflection.dart'; +import 'package:io/ansi.dart'; +import 'package:prompts/prompts.dart' as prompts; +import 'package:recase/recase.dart'; +import '../../util.dart'; +import 'maker.dart'; + +class MigrationCommand extends Command { + @override + String get name => 'migration'; + + @override + String get description => 'Generates a migration class.'; + + MigrationCommand() { + argParser + ..addOption('name', + abbr: 'n', help: 'Specifies a name for the model class.') + ..addOption('output-dir', + help: 'Specifies a directory to create the migration class in.', + defaultsTo: 'tool/migrations'); + } + + @override + FutureOr run() async { + String name; + if (argResults.wasParsed('name')) name = argResults['name'] as String; + + if (name?.isNotEmpty != true) { + name = prompts.get('Name of model class'); + } + + var deps = [const MakerDependency('angel_migration', '^1.0.0-alpha')]; + var rc = new ReCase(name); + + var migrationLib = new Library((migrationLib) { + migrationLib + ..directives.add(new Directive.import( + 'package:angel_migration.dart/angel_migration.dart')) + ..body.add(new Class((migrationClazz) { + migrationClazz + ..name = '${rc.pascalCase}Migration' + ..extend = refer('Migration'); + + var tableName = pluralize(rc.snakeCase); + + // up() + migrationClazz.methods.add(new Method((up) { + up + ..name = 'up' + ..returns = refer('void') + ..annotations.add(refer('override')) + ..requiredParameters.add(new Parameter((b) => b + ..name = 'schema' + ..type = refer('Schema'))) + ..body = new Block((block) { + // (table) { ... } + var callback = new Method((callback) { + callback + ..requiredParameters + .add(new Parameter((b) => b..name = 'table')) + ..body = new Block((block) { + var table = refer('table'); + + block.addExpression( + (table.property('serial').call([literal('id')])) + .property('primaryKey') + .call([]), + ); + + block.addExpression( + table.property('date').call([ + literal('created_at'), + ]), + ); + + block.addExpression( + table.property('date').call([ + literal('updated_at'), + ]), + ); + }); + }); + + block.addExpression(refer('schema').property('create').call([ + literal(tableName), + callback.closure, + ])); + }); + })); + + // down() + migrationClazz.methods.add(new Method((down) { + down + ..name = 'down' + ..returns = refer('void') + ..annotations.add(refer('override')) + ..requiredParameters.add(new Parameter((b) => b + ..name = 'schema' + ..type = refer('Schema'))) + ..body = new Block((block) { + block.addExpression( + refer('schema').property('drop').call([ + literal(tableName), + ]), + ); + }); + })); + })); + }); + + // Save migration file + var migrationDir = new Directory.fromUri( + Directory.current.uri.resolve(argResults['output-dir'] as String)); + var migrationFile = + new File.fromUri(migrationDir.uri.resolve('${rc.snakeCase}.dart')); + if (!await migrationFile.exists()) + await migrationFile.create(recursive: true); + + await migrationFile.writeAsString(new DartFormatter() + .format(migrationLib.accept(new DartEmitter()).toString())); + + print(green.wrap( + '$checkmark Created migration file "${migrationFile.absolute.path}".')); + + await depend(deps); + } +} diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index f4b33038..0775025b 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import 'package:dart_style/dart_style.dart'; -import 'package:inflection/inflection.dart'; import 'package:io/ansi.dart'; import 'package:prompts/prompts.dart' as prompts; import 'package:recase/recase.dart'; @@ -21,7 +20,7 @@ class ModelCommand extends Command { ..addFlag('migration', abbr: 'm', help: 'Generate an angel_orm migration file.', - negatable: false) + defaultsTo: true) ..addFlag('orm', help: 'Generate angel_orm code.', negatable: false) ..addFlag('serializable', help: 'Generate angel_serialize annotations.', defaultsTo: true) @@ -37,7 +36,6 @@ class ModelCommand extends Command { @override run() async { - var pubspec = await loadPubspec(); String name; if (argResults.wasParsed('name')) name = argResults['name'] as String; @@ -52,6 +50,12 @@ class ModelCommand extends Command { var rc = new ReCase(name); var modelLib = new Library((modelLib) { + if (argResults['migration'] as bool) { + modelLib.directives.addAll([ + new Directive.import('package:angel_migration/angel_migration.dart'), + ]); + } + modelLib.directives .add(new Directive.import('package:angel_model/angel_model.dart')); @@ -67,14 +71,14 @@ class ModelCommand extends Command { } if (argResults['orm'] as bool) { - modelLib.directives - .add(new Directive.import('package:angel_orm/angel_orm.dart')); + modelLib.directives.addAll([ + new Directive.import('package:angel_orm/angel_orm.dart'), + ]); deps.add(const MakerDependency('angel_orm', '^1.0.0-alpha')); } modelLib.body.addAll([ new Code("part '${rc.snakeCase}.g.dart';"), - new Code("part '${rc.snakeCase}.serializer.g.dart';"), ]); modelLib.body.add(new Class((modelClazz) { @@ -89,7 +93,12 @@ class ModelCommand extends Command { } if (argResults['orm'] as bool) { - modelClazz.annotations.add(refer('orm')); + if (argResults['migration'] as bool) { + modelClazz.annotations.add(refer('orm')); + } else { + modelClazz.annotations.add(refer('Orm') + .newInstance([], {'generateMigration': literalFalse})); + } } })); }); @@ -108,97 +117,14 @@ class ModelCommand extends Command { .wrap('$checkmark Created model file "${modelFile.absolute.path}".')); if (argResults['migration'] as bool) { - deps.add(const MakerDependency('angel_migration', '^1.0.0-alpha')); - - var migrationLib = new Library((migrationLib) { - migrationLib - ..directives.add(new Directive.import( - 'package:angel_migration.dart/angel_migration.dart')) - ..body.add(new Class((migrationClazz) { - migrationClazz - ..name = '${rc.pascalCase}Migration' - ..extend = refer('Migration'); - - var tableName = pluralize(rc.snakeCase); - - // up() - migrationClazz.methods.add(new Method((up) { - up - ..name = 'up' - ..returns = refer('void') - ..annotations.add(refer('override')) - ..requiredParameters.add(new Parameter((b) => b - ..name = 'schema' - ..type = refer('Schema'))) - ..body = new Block((block) { - // (table) { ... } - var callback = new Method((callback) { - callback - ..requiredParameters - .add(new Parameter((b) => b..name = 'table')) - ..body = new Block((block) { - var table = refer('table'); - - block.addExpression( - (table.property('serial').call([literal('id')])) - .property('primaryKey') - .call([]), - ); - - block.addExpression( - table.property('date').call([ - literal('created_at'), - ]), - ); - - block.addExpression( - table.property('date').call([ - literal('updated_at'), - ]), - ); - }); - }); - - block.addExpression(refer('schema').property('create').call([ - literal(tableName), - callback.closure, - ])); - }); - })); - - // down() - migrationClazz.methods.add(new Method((down) { - down - ..name = 'down' - ..returns = refer('void') - ..annotations.add(refer('override')) - ..requiredParameters.add(new Parameter((b) => b - ..name = 'schema' - ..type = refer('Schema'))) - ..body = new Block((block) { - block.addExpression( - refer('schema').property('drop').call([ - literal(tableName), - ]), - ); - }); - })); - })); - }); - - // Save migration file - var migrationDir = new Directory.fromUri( - Directory.current.uri.resolve(argResults['migration-dir'] as String)); - var migrationFile = - new File.fromUri(migrationDir.uri.resolve('${rc.snakeCase}.dart')); - if (!await migrationFile.exists()) - await migrationFile.create(recursive: true); - - await migrationFile.writeAsString(new DartFormatter() - .format(migrationLib.accept(new DartEmitter()).toString())); - - print(green.wrap( - '$checkmark Created migration file "${migrationFile.absolute.path}".')); + await runner.run([ + 'make', + 'migration', + '-n', + name, + '--output-dir', + argResults['migration-dir'] as String, + ]); } if (deps.isNotEmpty) await depend(deps); diff --git a/sample-project/.dockerignore b/sample-project/.dockerignore deleted file mode 100644 index faf23bd1..00000000 --- a/sample-project/.dockerignore +++ /dev/null @@ -1,10 +0,0 @@ -.dart_tool -.idea -.pub -.vscode -logs/ -test/ -build/ -.analysis-options -.packages -*.g.dart \ No newline at end of file diff --git a/sample-project/.gitignore b/sample-project/.gitignore deleted file mode 100644 index d88b0dfb..00000000 --- a/sample-project/.gitignore +++ /dev/null @@ -1,90 +0,0 @@ -# Created by .ignore support plugin (hsz.mobi) -### Dart template -# See https://www.dartlang.org/tools/private-files.html - -# source_gen -.dart_tool - -# Files and directories created by pub -.buildlog -.packages -.project -.pub/ -.scripts-bin/ -build/ -**/packages/ - -# Files created by dart2js -# (Most Dart developers will use pub build to compile Dart, use/modify these -# rules if you intend to use dart2js directly -# Convention is to use extension '.dart.js' for Dart compiled to Javascript to -# differentiate from explicit Javascript files) -*.dart.js -*.part.js -*.js.deps -*.js.map -*.info.json - -# Directory created by dartdoc -doc/api/ - -# Don't commit pubspec lock file -# (Library packages only! Remove pattern if developing an application package) -pubspec.lock -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/workspace.xml -.idea/tasks.xml -.idea/dictionaries -.idea/vcs.xml -.idea/jsLibraryMappings.xml - -# Sensitive or high-churn files: -.idea/dataSources.ids -.idea/dataSources.xml -.idea/dataSources.local.xml -.idea/sqlDataSources.xml -.idea/dynamic.xml -.idea/uiDesigner.xml - -# Gradle: -.idea/gradle.xml -.idea/libraries - -# Mongo Explorer plugin: -.idea/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### VSCode template -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -logs/ -*.pem -.DS_Store -server_log.txt diff --git a/sample-project/.idea/angel.iml b/sample-project/.idea/angel.iml deleted file mode 100644 index eae13016..00000000 --- a/sample-project/.idea/angel.iml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample-project/.idea/misc.xml b/sample-project/.idea/misc.xml deleted file mode 100644 index c65900a0..00000000 --- a/sample-project/.idea/misc.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - General - - - XPath - - - - - AngularJS - - - - - - \ No newline at end of file diff --git a/sample-project/.idea/modules.xml b/sample-project/.idea/modules.xml deleted file mode 100644 index b0343846..00000000 --- a/sample-project/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/sample-project/.idea/runConfigurations/dev_dart.xml b/sample-project/.idea/runConfigurations/dev_dart.xml deleted file mode 100644 index 418187ff..00000000 --- a/sample-project/.idea/runConfigurations/dev_dart.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/sample-project/.idea/runConfigurations/prod_dart.xml b/sample-project/.idea/runConfigurations/prod_dart.xml deleted file mode 100644 index e93c56a5..00000000 --- a/sample-project/.idea/runConfigurations/prod_dart.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample-project/.vscode/launch.json b/sample-project/.vscode/launch.json deleted file mode 100644 index ab26e089..00000000 --- a/sample-project/.vscode/launch.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Development Server", - "type": "dart", - "request": "launch", - "program": "${workspaceRoot}/bin/dev.dart", - "checkedMode": true - }, - { - "name": "Production Server", - "type": "dart", - "request": "launch", - "program": "${workspaceRoot}/bin/prod.dart", - "checkedMode": true - } - ] -} \ No newline at end of file diff --git a/sample-project/.vscode/tasks.json b/sample-project/.vscode/tasks.json deleted file mode 100644 index 0479cc0b..00000000 --- a/sample-project/.vscode/tasks.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "0.1.0", - "command": "pub", - "isShellCommand": true, - "echoCommand": true, - "showOutput": "always", - "tasks": [ - { - "taskName": "pub:build", - "suppressTaskName": true, - "args": [ - "build" - ] - }, - { - "taskName": "pub:serve", - "showOutput": "silent", - "suppressTaskName": true, - "isBackground": true, - "args": [ - "serve" - ] - } - ] -} \ No newline at end of file diff --git a/sample-project/Dockerfile b/sample-project/Dockerfile deleted file mode 100644 index f9056deb..00000000 --- a/sample-project/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM google/dart:2.0 - -COPY ./ ./ - -# Install dependencies, pre-build -RUN pub get - -# Optionally build generaed sources. -# RUN pub run build_runner build - -# Set environment, start server -ENV ANGEL_ENV=production -EXPOSE 3000 -CMD dart bin/prod.dart \ No newline at end of file diff --git a/sample-project/LICENSE b/sample-project/LICENSE deleted file mode 100644 index 52a1469a..00000000 --- a/sample-project/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 angel-dart - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/sample-project/README.md b/sample-project/README.md deleted file mode 100644 index aff56f62..00000000 --- a/sample-project/README.md +++ /dev/null @@ -1,67 +0,0 @@ -[![The Angel Framework](https://angel-dart.github.io/assets/images/logo.png)](https://angel-dart.github.io) - -[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/angel_dart/discussion) -[![Pub](https://img.shields.io/pub/v/angel_framework.svg)](https://pub.dartlang.org/packages/angel_framework) -[![Build status](https://travis-ci.org/angel-dart/framework.svg?branch=master)](https://travis-ci.org/angel-dart/framework) -![License](https://img.shields.io/github/license/angel-dart/framework.svg) - -**A batteries-included, full-stack Web server framework for Dart.** - ------ - -[Wiki (in-depth documentation)](https://github.com/angel-dart/angel/wiki) - -[API Documentation](http://www.dartdocs.org/documentation/angel_common/latest) - -[Roadmap](https://github.com/angel-dart/roadmap/blob/master/ROADMAP.md) - -[File an Issue](https://github.com/angel-dart/roadmap/issues) - -[Awesome Angel :fire:](https://github.com/angel-dart/awesome-angel) - - - -Like what you see? Please lend us a star! :star: - -## Newest Tutorials -* [Dependency Injection Patterns with Angel 2](https://thosakwe.com/dependency-injection-patterns-in-angel-2/) -* [Angel 2.0.0 is Almost Here - What it Means for You](https://thosakwe.com/new-features-coming-to-angel-in-version-2-0-0/) -* [GraphQL is coming to Angel (and Dart)](https://thosakwe.com/graphql-is-coming-to-angel-and-dart/) - -## Installation & Setup -*Having errors with a fresh Angel installation? See [here](https://angel-dart.gitbook.io/angel/the-basics/installation) for help.* - -Once you have [Dart](https://www.dartlang.org/) installed, bootstrapping a project is as simple as running a few shell commands: - -Install the [Angel CLI](https://github.com/angel-dart/cli): - -```bash -pub global activate angel_cli -``` - -Bootstrap a project: - -```bash -angel init hello -``` - -You can even have your server run and be *hot-reloaded* on file changes: - -```bash -dart --observe bin/dev.dart -``` - -Next, check out the [detailed documentation](https://angel-dart.gitbook.io/angel) to learn to flesh out your project. - -## Features -With features like the following, Angel is the all-in-one framework you should choose to build your next project: -* [Advanced, Modular Routing](https://github.com/angel-dart/route) -* [Middleware](https://angel-dart.gitbook.io/angel/the-basics/middleware) -* [Dependency Injection](https://angel-dart.gitbook.io/angel/the-basics/dependency-injection) -* [Strongly-typed ORM](https://github.com/angel-dart/orm) -* And [much more](https://github.com/angel-dart)... - -## Basic Example -Examples and complete projects can be found here: - -https://github.com/angel-dart/examples-v2 diff --git a/sample-project/analysis_options.yaml b/sample-project/analysis_options.yaml deleted file mode 100644 index a4f33350..00000000 --- a/sample-project/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false diff --git a/sample-project/bin/dev.dart b/sample-project/bin/dev.dart deleted file mode 100644 index 9347bc87..00000000 --- a/sample-project/bin/dev.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'dart:io'; -import 'package:sample_project/src/pretty_logging.dart'; -import 'package:sample_project/sample_project.dart'; -import 'package:angel_container/mirrors.dart'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_hot/angel_hot.dart'; -import 'package:logging/logging.dart'; - -main() async { - // Watch the config/ and web/ directories for changes, and hot-reload the server. - var hot = new HotReloader(() async { - var app = new Angel(reflector: MirrorsReflector()); - await app.configure(configureServer); - hierarchicalLoggingEnabled = true; - app.logger = new Logger('angel'); - var sub = app.logger.onRecord.listen(prettyLog); - app.shutdownHooks.add((_) => sub.cancel()); - return app; - }, [ - new Directory('config'), - new Directory('lib'), - ]); - - var server = await hot.startServer('127.0.0.1', 3000); - print('Listening at http://${server.address.address}:${server.port}'); -} diff --git a/sample-project/bin/prod.dart b/sample-project/bin/prod.dart deleted file mode 100644 index fdaf2e12..00000000 --- a/sample-project/bin/prod.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'package:sample_project/sample_project.dart'; -import 'package:angel_production/angel_production.dart'; - -main(List args) => Runner('angel', configureServer).run(args); diff --git a/sample-project/config/default.yaml b/sample-project/config/default.yaml deleted file mode 100644 index 8f6fea0d..00000000 --- a/sample-project/config/default.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Default server configuration. -host: 127.0.0.1 -mongo_db: mongodb://localhost:27017/angel -port: 3000 -jwt_secret: "IIeewWFKTdCJIT5zLh09J2gFKmfpgh0J" \ No newline at end of file diff --git a/sample-project/config/development.yaml b/sample-project/config/development.yaml deleted file mode 100644 index 4bed71e2..00000000 --- a/sample-project/config/development.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# Development-only server configuration. -debug: true \ No newline at end of file diff --git a/sample-project/config/production.yaml b/sample-project/config/production.yaml deleted file mode 100644 index a6ff83f2..00000000 --- a/sample-project/config/production.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Production-only server configuration -debug: false -jwt_secret: "DJ3JnBDBS6UcnbAGwPQb3m7YWxja7KLZ" \ No newline at end of file diff --git a/sample-project/lib/sample_project.dart b/sample-project/lib/sample_project.dart deleted file mode 100644 index 0da05718..00000000 --- a/sample-project/lib/sample_project.dart +++ /dev/null @@ -1,20 +0,0 @@ -library sample_project; - -import 'dart:async'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:file/local.dart'; -import 'src/config/config.dart' as configuration; -import 'src/routes/routes.dart' as routes; -import 'src/services/services.dart' as services; - -/// Configures the server instance. -Future configureServer(Angel app) async { - // Grab a handle to the file system, so that we can do things like - // serve static files. - var fs = const LocalFileSystem(); - - // Set up our application, using the plug-ins defined with this project. - await app.configure(configuration.configureServer(fs)); - await app.configure(services.configureServer); - await app.configure(routes.configureServer(fs)); -} diff --git a/sample-project/lib/src/config/config.dart b/sample-project/lib/src/config/config.dart deleted file mode 100644 index e7b8c562..00000000 --- a/sample-project/lib/src/config/config.dart +++ /dev/null @@ -1,32 +0,0 @@ -library sample_project.src.config; - -import 'package:angel_configuration/angel_configuration.dart'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_jael/angel_jael.dart'; -import 'package:file/file.dart'; -import 'plugins/plugins.dart' as plugins; - -/// This is a perfect place to include configuration and load plug-ins. -AngelConfigurer configureServer(FileSystem fileSystem) { - return (Angel app) async { - // Load configuration from the `config/` directory. - // - // See: https://github.com/angel-dart/configuration - await app.configure(configuration(fileSystem)); - - // Configure our application to render Jael templates from the `views/` directory. - // - // See: https://github.com/angel-dart/jael - await app.configure(jael(fileSystem.directory('views'))); - - // Apply another plug-ins, i.e. ones that *you* have written. - // - // Typically, the plugins in `lib/src/config/plugins/plugins.dart` are plug-ins - // that add functionality specific to your application. - // - // If you write a plug-in that you plan to use again, or are - // using one created by the community, include it in - // `lib/src/config/config.dart`. - await plugins.configureServer(app); - }; -} diff --git a/sample-project/lib/src/config/plugins/plugins.dart b/sample-project/lib/src/config/plugins/plugins.dart deleted file mode 100644 index 5e6d9ea3..00000000 --- a/sample-project/lib/src/config/plugins/plugins.dart +++ /dev/null @@ -1,8 +0,0 @@ -library sample_project.src.config.plugins; - -import 'dart:async'; -import 'package:angel_framework/angel_framework.dart'; - -Future configureServer(Angel app) async { - // Include any plugins you have made here. -} diff --git a/sample-project/lib/src/routes/controllers/controllers.dart b/sample-project/lib/src/routes/controllers/controllers.dart deleted file mode 100644 index 1f10cc1a..00000000 --- a/sample-project/lib/src/routes/controllers/controllers.dart +++ /dev/null @@ -1,8 +0,0 @@ -library sample_project.src.routes.controllers; - -import 'dart:async'; -import 'package:angel_framework/angel_framework.dart'; - -Future configureServer(Angel app) async { - /// Controllers will not function unless wired to the application! -} diff --git a/sample-project/lib/src/routes/routes.dart b/sample-project/lib/src/routes/routes.dart deleted file mode 100644 index 8953bd40..00000000 --- a/sample-project/lib/src/routes/routes.dart +++ /dev/null @@ -1,61 +0,0 @@ -library sample_project.src.routes; - -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_static/angel_static.dart'; -import 'package:file/file.dart'; -import 'controllers/controllers.dart' as controllers; - -/// Put your app routes here! -/// -/// See the wiki for information about routing, requests, and responses: -/// * https://github.com/angel-dart/angel/wiki/Basic-Routing -/// * https://github.com/angel-dart/angel/wiki/Requests-&-Responses -AngelConfigurer configureServer(FileSystem fileSystem) { - return (Angel app) async { - // Typically, you want to mount controllers first, after any global middleware. - await app.configure(controllers.configureServer); - - // Render `views/hello.jl` when a user visits the application root. - app.get('/', (req, res) => res.render('hello')); - - // Mount static server at web in development. - // The `CachingVirtualDirectory` variant of `VirtualDirectory` also sends `Cache-Control` headers. - // - // In production, however, prefer serving static files through NGINX or a - // similar reverse proxy. - // - // Read the following two sources for documentation: - // * https://medium.com/the-angel-framework/serving-static-files-with-the-angel-framework-2ddc7a2b84ae - // * https://github.com/angel-dart/static - if (!app.isProduction) { - var vDir = VirtualDirectory( - app, - fileSystem, - source: fileSystem.directory('web'), - ); - app.fallback(vDir.handleRequest); - } - - // Throw a 404 if no route matched the request. - app.fallback((req, res) => throw AngelHttpException.notFound()); - - // Set our application up to handle different errors. - // - // Read the following for documentation: - // * https://github.com/angel-dart/angel/wiki/Error-Handling - - var oldErrorHandler = app.errorHandler; - app.errorHandler = (e, req, res) async { - if (!req.accepts('text/html')) - return await oldErrorHandler(e, req, res); - else { - if (e.statusCode == 404) { - return await res - .render('error', {'message': 'No file exists at ${req.uri}.'}); - } - - return await res.render('error', {'message': e.message}); - } - }; - }; -} diff --git a/sample-project/lib/src/services/services.dart b/sample-project/lib/src/services/services.dart deleted file mode 100644 index 68f1123e..00000000 --- a/sample-project/lib/src/services/services.dart +++ /dev/null @@ -1,14 +0,0 @@ -library sample_project.services; - -import 'dart:async'; -import 'package:angel_framework/angel_framework.dart'; - -/// Configure our application to use *services*. -/// Services must be wired to the app via `app.use`. -/// -/// They provide many benefits, such as instant REST API generation, -/// and respond to both REST and WebSockets. -/// -/// Read more here: -/// https://github.com/angel-dart/angel/wiki/Service-Basics -Future configureServer(Angel app) async {} diff --git a/sample-project/pubspec.yaml b/sample-project/pubspec.yaml deleted file mode 100644 index a1c5aa38..00000000 --- a/sample-project/pubspec.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: sample_project -description: An app that's going to be amazing pretty soon. -publish_to: none # Ensure we don't accidentally publish our private code! ;) -environment: - sdk: '>=2.0.0-dev <3.0.0' -homepage: https://github.com/angel-dart/angel -dependencies: - angel_auth: ^2.0.0-alpha # Supports stateless authentication via JWT - angel_configuration: ^2.0.0 # Loads application configuration, along with support for .env files. - angel_framework: ^2.0.0-alpha # The core server library. - angel_jael: ^2.0.0 # Server-side templating engine - angel_production: ^1.0.0-alpha - angel_static: ^2.0.0-alpha # Static file server - angel_validate: ^2.0.0-alpha # Allows for validation of input data -dev_dependencies: - angel_hot: ^2.0.0 # Hot-reloading support. :) - angel_test: ^2.0.0-alpha # Utilities for testing Angel servers. - io: ^0.3.2 - pedantic: ^1.0.0 - test: ^1.0.0 diff --git a/sample-project/test/all_test.dart b/sample-project/test/all_test.dart deleted file mode 100644 index 06bec6e5..00000000 --- a/sample-project/test/all_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:sample_project/sample_project.dart'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_test/angel_test.dart'; -import 'package:test/test.dart'; - -// Angel also includes facilities to make testing easier. -// -// `package:angel_test` ships a client that can test -// both plain HTTP and WebSockets. -// -// Tests do not require your server to actually be mounted on a port, -// so they will run faster than they would in other frameworks, where you -// would have to first bind a socket, and then account for network latency. -// -// See the documentation here: -// https://github.com/angel-dart/test -// -// If you are unfamiliar with Dart's advanced testing library, you can read up -// here: -// https://github.com/dart-lang/test - -main() async { - TestClient client; - - setUp(() async { - var app = Angel(); - await app.configure(configureServer); - - client = await connectTo(app); - }); - - tearDown(() async { - await client.close(); - }); - - test('index returns 200', () async { - // Request a resource at the given path. - var response = await client.get('/'); - - // Expect a 200 response. - expect(response, hasStatus(200)); - }); -} diff --git a/sample-project/views/error.jael b/sample-project/views/error.jael deleted file mode 100644 index 1e93df46..00000000 --- a/sample-project/views/error.jael +++ /dev/null @@ -1,5 +0,0 @@ - - -
{{ message }}
-
-
\ No newline at end of file diff --git a/sample-project/views/hello.jael b/sample-project/views/hello.jael deleted file mode 100644 index 7ca6b8af..00000000 --- a/sample-project/views/hello.jael +++ /dev/null @@ -1,5 +0,0 @@ - - -
Angel
-
-
\ No newline at end of file diff --git a/sample-project/views/layout.jael b/sample-project/views/layout.jael deleted file mode 100644 index ea0714bc..00000000 --- a/sample-project/views/layout.jael +++ /dev/null @@ -1,17 +0,0 @@ - - - - {{ title ?? 'Angel' }} - - - - - - -
-
- -
-
- - \ No newline at end of file diff --git a/sample-project/web/css/site.css b/sample-project/web/css/site.css deleted file mode 100644 index 9e40b8de..00000000 --- a/sample-project/web/css/site.css +++ /dev/null @@ -1,27 +0,0 @@ -html, body { - height: 100%; -} - -body { - margin: 0; - padding: 0; - width: 100%; - display: table; - font-weight: 100; - font-family: 'Lato', sans-serif; -} - -.container { - text-align: center; - display: table-cell; - vertical-align: middle; -} - -.content { - text-align: center; - display: inline-block; -} - -.title { - font-size: 96px; -} \ No newline at end of file diff --git a/sample-project/web/images/favicon.png b/sample-project/web/images/favicon.png deleted file mode 100644 index 8be3ad791e165bad029dcf6f9c23c6aeb1c66fc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2853 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 diff --git a/sample-project/web/robots.txt b/sample-project/web/robots.txt deleted file mode 100644 index f328961c..00000000 --- a/sample-project/web/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: /admin \ No newline at end of file From ffef695da8512c7c4f92d858b64063d1eaf2a1c4 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 31 Dec 2018 11:36:16 -0500 Subject: [PATCH 116/132] 2.1.0 done --- lib/src/commands/init.dart | 45 +++++++++++++++----------------- lib/src/commands/make/model.dart | 9 ++++--- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index cc8fcf16..9915891c 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -136,30 +136,29 @@ class InitCommand extends Command { Process git; if (boilerplate.ref == null) { - git = await Process.start("git", [ - "clone", - "--depth", - "1", - boilerplate.url, - projectDir.absolute.path - ]); + git = await Process.start( + "git", + ["clone", "--depth", "1", boilerplate.url, projectDir.absolute.path], + mode: ProcessStartMode.inheritStdio, + ); } else { // git clone --single-branch -b branch host:/dir.git - git = await Process.start("git", [ - "clone", - "--depth", - "1", - "--single-branch", - "-b", - boilerplate.ref, - boilerplate.url, - projectDir.absolute.path - ]); + git = await Process.start( + "git", + [ + "clone", + "--depth", + "1", + "--single-branch", + "-b", + boilerplate.ref, + boilerplate.url, + projectDir.absolute.path + ], + mode: ProcessStartMode.inheritStdio, + ); } - stdout.addStream(git.stdout); - stderr.addStream(git.stderr); - if (await git.exitCode != 0) { throw new Exception("Could not clone repo."); } @@ -208,10 +207,8 @@ Future preBuild(Directory projectDir) async { print('Running `pub run build_runner build`...'); var build = await Process.start(resolvePub(), ['run', 'build'], - workingDirectory: projectDir.absolute.path); - - stdout.addStream(build.stdout); - stderr.addStream(build.stderr); + workingDirectory: projectDir.absolute.path, + mode: ProcessStartMode.inheritStdio); var buildCode = await build.exitCode; diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 0775025b..8f5cf4db 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -59,8 +59,9 @@ class ModelCommand extends Command { modelLib.directives .add(new Directive.import('package:angel_model/angel_model.dart')); - var needsSerialize = - argResults['serializable'] as bool || argResults['orm'] as bool; + var needsSerialize = argResults['serializable'] as bool || + argResults['orm'] as bool || + argResults['migration'] as bool; if (needsSerialize) { modelLib.directives.add(new Directive.import( @@ -70,7 +71,7 @@ class ModelCommand extends Command { deps.add(const MakerDependency('build_runner', '">=0.7.0 <0.10.0"')); } - if (argResults['orm'] as bool) { + if (argResults['orm'] as bool || argResults['migration'] as bool) { modelLib.directives.addAll([ new Directive.import('package:angel_orm/angel_orm.dart'), ]); @@ -92,7 +93,7 @@ class ModelCommand extends Command { modelClazz.annotations.add(refer('serializable')); } - if (argResults['orm'] as bool) { + if (argResults['orm'] as bool || argResults['migration'] as bool) { if (argResults['migration'] as bool) { modelClazz.annotations.add(refer('orm')); } else { From bd5a48098c005a12c75493dc0137982a06994c33 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 31 Dec 2018 11:37:34 -0500 Subject: [PATCH 117/132] remove unused --- lib/src/commands/make/maker.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/src/commands/make/maker.dart b/lib/src/commands/make/maker.dart index 3e9e832b..25b82795 100644 --- a/lib/src/commands/make/maker.dart +++ b/lib/src/commands/make/maker.dart @@ -1,9 +1,5 @@ import 'dart:async'; -import 'dart:io'; import 'package:io/ansi.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:pub_semver/pub_semver.dart'; -import '../pub.dart'; import '../../util.dart'; class MakerDependency { From d82f4d5fb16a573e9cdc105db18870ac181a0af6 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 31 Dec 2018 12:29:53 -0500 Subject: [PATCH 118/132] 2.1.1 --- CHANGELOG.md | 3 +++ lib/src/commands/rename.dart | 13 ++++--------- pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a021635..c6606793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.1.1 +* Edit the way `rename` runs, leaving no corner unturned. + # 2.1.0 * Deprecate `angel install`. * Rename projects using `snake_case`. diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index 98a3752c..d0213b8b 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -113,7 +113,7 @@ renameDartFiles(Directory dir, String oldName, String newName) async { contents = contents.replaceRange( range.first as int, range.last as int, replacement); } else if (range.first is String) { - contents = contents.replaceAll(range.first as Pattern, replacement); + contents = contents.replaceAll(range.first as String, replacement); } }); @@ -128,7 +128,9 @@ class RenamingVisitor extends RecursiveAstVisitor { final String oldName, newName; final Map replace = {}; - RenamingVisitor(this.oldName, this.newName); + RenamingVisitor(this.oldName, this.newName) { + replace[['{{$oldName}}']] = newName; + } String updateUri(String uri) { if (uri == 'package:$oldName/$oldName.dart') { @@ -139,13 +141,6 @@ class RenamingVisitor extends RecursiveAstVisitor { return uri; } - @override - visitSimpleStringLiteral(SimpleStringLiteral node) { - if (node.value == '{{$oldName}}') { - replace[[node.value]] = newName; - } - } - @override visitExportDirective(ExportDirective ctx) { var uri = ctx.uri.stringValue, updated = updateUri(uri); diff --git a/pubspec.yaml b/pubspec.yaml index 6c7377cf..f1888ddf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.0 +version: 2.1.1 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 0e56cc808c6a15e544eaf2ad578ccc8cdbe13a9d Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 31 Dec 2018 13:27:34 -0500 Subject: [PATCH 119/132] 2.1.2 --- CHANGELOG.md | 3 +++ lib/src/commands/make/model.dart | 3 ++- lib/src/commands/rename.dart | 4 +++- pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6606793..3ad85a97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.1.2 +* No migrations-by-default. + # 2.1.1 * Edit the way `rename` runs, leaving no corner unturned. diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 8f5cf4db..ceda14dc 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -20,7 +20,8 @@ class ModelCommand extends Command { ..addFlag('migration', abbr: 'm', help: 'Generate an angel_orm migration file.', - defaultsTo: true) + defaultsTo: false, + negatable: false) ..addFlag('orm', help: 'Generate angel_orm code.', negatable: false) ..addFlag('serializable', help: 'Generate angel_serialize annotations.', defaultsTo: true) diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index d0213b8b..b62597e7 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -5,7 +5,7 @@ import 'package:dart_style/dart_style.dart'; import 'package:glob/glob.dart'; import 'package:io/ansi.dart'; import 'package:prompts/prompts.dart' as prompts; -import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:recase/recase.dart'; import '../util.dart'; import 'pub.dart'; @@ -29,6 +29,8 @@ class RenameCommand extends Command { newName = prompts.get('Rename project to'); } + newName = new Recase(newName).snakeCase; + var choice = prompts.getBool('Rename the project to `$newName`?'); if (choice) { diff --git a/pubspec.yaml b/pubspec.yaml index f1888ddf..89010430 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.1 +version: 2.1.2 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From eb82f173a66e2d05a8adadd65d8ff641ceddb957 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 9 Jan 2019 12:46:12 -0500 Subject: [PATCH 120/132] 2.1.3 --- CHANGELOG.md | 4 ++++ lib/src/commands/init.dart | 12 +++++++++--- lib/src/commands/make/model.dart | 4 ++-- lib/src/commands/rename.dart | 2 +- pubspec.yaml | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad85a97..9f2607c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.1.3 +* Fix generation of ORM models. +* A `--project-name` to `init` command. + # 2.1.2 * No migrations-by-default. diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 9915891c..53e355a4 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -22,7 +22,10 @@ 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) + ..addOption('project-name', + abbr: 'n', help: 'The name for this project.'); } @override @@ -44,8 +47,11 @@ class InitCommand extends Command { new File.fromUri(projectDir.uri.resolve('config/production.yaml')), secret); - var name = p.basenameWithoutExtension( - projectDir.absolute.uri.normalizePath().toFilePath()); + var name = argResults.wasParsed('project-name') + ? argResults['project-name'] as String + : p.basenameWithoutExtension( + projectDir.absolute.uri.normalizePath().toFilePath()); + name = ReCase(name).snakeCase; print('Renaming project from "angel" to "$name"...'); await renamePubspec(projectDir, 'angel', name); diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index ceda14dc..f40b3d93 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -98,8 +98,8 @@ class ModelCommand extends Command { if (argResults['migration'] as bool) { modelClazz.annotations.add(refer('orm')); } else { - modelClazz.annotations.add(refer('Orm') - .newInstance([], {'generateMigration': literalFalse})); + modelClazz.annotations.add( + refer('Orm').call([], {'generateMigration': literalFalse})); } } })); diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index b62597e7..553f3595 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -29,7 +29,7 @@ class RenameCommand extends Command { newName = prompts.get('Rename project to'); } - newName = new Recase(newName).snakeCase; + newName = new ReCase(newName).snakeCase; var choice = prompts.getBool('Rename the project to `$newName`?'); diff --git a/pubspec.yaml b/pubspec.yaml index 89010430..ecfea27d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.2 +version: 2.1.3 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From d2b1082435f0dfc7ab774f6a00c0badc0c2558ee Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 11 Jan 2019 19:12:06 -0500 Subject: [PATCH 121/132] 2.1.4 --- CHANGELOG.md | 4 ++++ lib/src/commands/init.dart | 13 +++++++------ lib/src/commands/make/model.dart | 20 +++----------------- pubspec.yaml | 2 +- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2607c2..322936d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.1.4 +* The `migration` argument to `model` just emits an annotation now. +* Add the ORM boilerplate. + # 2.1.3 * Fix generation of ORM models. * A `--project-name` to `init` command. diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 53e355a4..a7f4e852 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -131,10 +131,10 @@ class InitCommand extends Command { } } - var boilerplate = basicBoilerplate; - // print('Choose a project type before continuing:'); - // var boilerplate = prompts.choose( - // 'Choose a project type before continuing', boilerplates); + // var boilerplate = basicBoilerplate; + print('Choose a project type before continuing:'); + var boilerplate = prompts.choose( + 'Choose a project type before continuing', boilerplates); print( 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); @@ -224,7 +224,8 @@ Future preBuild(Directory projectDir) async { const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( 'ORM', "A starting point for applications that use Angel's ORM.", - 'https://github.com/angel-dart/boilerplate_orm.git', + 'https://github.com/angel-dart/angel.git', + ref: 'orm', ); const BoilerplateInfo basicBoilerplate = const BoilerplateInfo( @@ -242,7 +243,7 @@ const BoilerplateInfo legacyBoilerplate = const BoilerplateInfo( const List boilerplates = const [ basicBoilerplate, //legacyBoilerplate, - //ormBoilerplate, + ormBoilerplate, ]; class BoilerplateInfo { diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index f40b3d93..45ebc1b8 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -19,8 +19,8 @@ class ModelCommand extends Command { argParser ..addFlag('migration', abbr: 'm', - help: 'Generate an angel_orm migration file.', - defaultsTo: false, + help: 'Generate migrations when running `build_runner`.', + defaultsTo: true, negatable: false) ..addFlag('orm', help: 'Generate angel_orm code.', negatable: false) ..addFlag('serializable', @@ -29,10 +29,7 @@ class ModelCommand extends Command { abbr: 'n', help: 'Specifies a name for the model class.') ..addOption('output-dir', help: 'Specifies a directory to create the model class in.', - defaultsTo: 'lib/src/models') - ..addOption('migration-dir', - help: 'Specifies a directory to create the migration class in.', - defaultsTo: 'tool/migrations'); + defaultsTo: 'lib/src/models'); } @override @@ -118,17 +115,6 @@ class ModelCommand extends Command { print(green .wrap('$checkmark Created model file "${modelFile.absolute.path}".')); - if (argResults['migration'] as bool) { - await runner.run([ - 'make', - 'migration', - '-n', - name, - '--output-dir', - argResults['migration-dir'] as String, - ]); - } - if (deps.isNotEmpty) await depend(deps); } } diff --git a/pubspec.yaml b/pubspec.yaml index ecfea27d..cd125d7d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.3 +version: 2.1.4 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 449a71100ef6ab6b8f9fa2f1c7ade187dfd4a965 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 11 Jan 2019 19:49:11 -0500 Subject: [PATCH 122/132] Patch rename --- CHANGELOG.md | 3 +++ lib/src/commands/rename.dart | 10 ++++++---- pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 322936d6..e7aa4d83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.1.4+1 +* Patch `part of 'path'` renames. + # 2.1.4 * The `migration` argument to `model` just emits an annotation now. * Add the ORM boilerplate. diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index 553f3595..e95586f6 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -167,11 +167,13 @@ class RenamingVisitor extends RecursiveAstVisitor { @override visitPartOfDirective(PartOfDirective ctx) { - var name = ctx.libraryName.name; + if (ctx.libraryName != null) { + var name = ctx.libraryName.name; - if (name.startsWith(oldName)) { - replace[[ctx.offset, ctx.end]] = - 'part of ' + name.replaceFirst(oldName, newName) + ';'; + if (name.startsWith(oldName)) { + replace[[ctx.offset, ctx.end]] = + 'part of ' + name.replaceFirst(oldName, newName) + ';'; + } } } } diff --git a/pubspec.yaml b/pubspec.yaml index cd125d7d..e79ae8ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.4 +version: 2.1.4+1 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 589c40415cbc15a043941e612d497930f231c00a Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 25 Jan 2019 11:02:38 -0500 Subject: [PATCH 123/132] 2.1.5 --- CHANGELOG.md | 4 ++++ lib/src/commands/init.dart | 14 ++++++++++++++ lib/src/commands/make/model.dart | 7 ++++--- lib/src/commands/rename.dart | 20 ++++++++++++-------- pubspec.yaml | 2 +- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7aa4d83..f2972a52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.1.5 +* Add `shared` boilerplates. +* Remove uncecessary `angel_model` imports. + # 2.1.4+1 * Patch `part of 'path'` renames. diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index a7f4e852..099b4214 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -240,10 +240,24 @@ const BoilerplateInfo legacyBoilerplate = const BoilerplateInfo( ref: '1.1.x', ); +const BoilerplateInfo sharedBoilerplate = const BoilerplateInfo( + 'Shared', + 'Holds common models and files shared across multiple Dart projects.', + 'https://github.com/angel-dart/boilerplate_shared.git'); + +const BoilerplateInfo sharedOrmBoilerplate = const BoilerplateInfo( + 'Shared (ORM)', + 'Holds common models and files shared across multiple Dart projects.', + 'https://github.com/angel-dart/boilerplate_shared.git', + ref: 'orm', +); + const List boilerplates = const [ basicBoilerplate, //legacyBoilerplate, ormBoilerplate, + sharedBoilerplate, + sharedOrmBoilerplate, ]; class BoilerplateInfo { diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 45ebc1b8..68f4bef7 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -54,9 +54,6 @@ class ModelCommand extends Command { ]); } - modelLib.directives - .add(new Directive.import('package:angel_model/angel_model.dart')); - var needsSerialize = argResults['serializable'] as bool || argResults['orm'] as bool || argResults['migration'] as bool; @@ -67,6 +64,10 @@ class ModelCommand extends Command { deps.add(const MakerDependency('angel_serialize', '^2.0.0')); deps.add(const MakerDependency('angel_serialize_generator', '^2.0.0')); deps.add(const MakerDependency('build_runner', '">=0.7.0 <0.10.0"')); + } else { + modelLib.directives + .add(new Directive.import('package:angel_model/angel_model.dart')); + deps.add(const MakerDependency('angel_model', '^1.0.0')); } if (argResults['orm'] as bool || argResults['migration'] as bool) { diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart index e95586f6..ae11b5e5 100644 --- a/lib/src/commands/rename.dart +++ b/lib/src/commands/rename.dart @@ -81,18 +81,22 @@ renamePubspec(Directory dir, String oldName, String newName) async { } renameDartFiles(Directory dir, String oldName, String newName) async { + if (!await dir.exists()) return; + // Try to replace MongoDB URL var configGlob = new Glob('config/**/*.yaml'); - await for (var yamlFile in configGlob.list(root: dir.absolute.path)) { - if (yamlFile is File) { - print( - 'Replacing occurrences of "$oldName" with "$newName" in file "${yamlFile.absolute.path}"...'); - var contents = await yamlFile.readAsString(); - contents = contents.replaceAll(oldName, newName); - await yamlFile.writeAsString(contents); + try { + await for (var yamlFile in configGlob.list(root: dir.absolute.path)) { + if (yamlFile is File) { + print( + 'Replacing occurrences of "$oldName" with "$newName" in file "${yamlFile.absolute.path}"...'); + var contents = await yamlFile.readAsString(); + contents = contents.replaceAll(oldName, newName); + await yamlFile.writeAsString(contents); + } } - } + } catch (_) {} var entry = new File.fromUri(dir.uri.resolve('lib/$oldName.dart')); diff --git a/pubspec.yaml b/pubspec.yaml index e79ae8ec..45fc4847 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.4+1 +version: 2.1.5 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 From 88cb3648cdd4c20bd4811323eda1d0e0e98eaa16 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 17 Apr 2019 15:10:27 -0400 Subject: [PATCH 124/132] Use inflection2 --- lib/src/commands/make/migration.dart | 2 +- lib/src/commands/make/service.dart | 2 +- lib/src/commands/service_generators/file_service.dart | 2 +- lib/src/commands/service_generators/mongo.dart | 2 +- lib/src/commands/service_generators/rethink.dart | 2 +- pubspec.yaml | 5 ++--- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/src/commands/make/migration.dart b/lib/src/commands/make/migration.dart index 507540f5..58200996 100644 --- a/lib/src/commands/make/migration.dart +++ b/lib/src/commands/make/migration.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import 'package:dart_style/dart_style.dart'; -import 'package:inflection/inflection.dart'; +import 'package:inflection2/inflection2.dart'; import 'package:io/ansi.dart'; import 'package:prompts/prompts.dart' as prompts; import 'package:recase/recase.dart'; diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart index 72b2b3b0..2d99e6d9 100644 --- a/lib/src/commands/make/service.dart +++ b/lib/src/commands/make/service.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import 'package:dart_style/dart_style.dart'; -import 'package:inflection/inflection.dart'; +import 'package:inflection2/inflection2.dart'; import 'package:io/ansi.dart'; import 'package:prompts/prompts.dart' as prompts; import 'package:pubspec_parse/pubspec_parse.dart'; diff --git a/lib/src/commands/service_generators/file_service.dart b/lib/src/commands/service_generators/file_service.dart index 278562d0..673fe12d 100644 --- a/lib/src/commands/service_generators/file_service.dart +++ b/lib/src/commands/service_generators/file_service.dart @@ -1,6 +1,6 @@ import 'generator.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:inflection/inflection.dart'; +import 'package:inflection2/inflection2.dart'; import '../make/maker.dart'; class FileServiceGenerator extends ServiceGenerator { diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index 9386f28f..bb6a45f4 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -1,6 +1,6 @@ import 'generator.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:inflection/inflection.dart'; +import 'package:inflection2/inflection2.dart'; import '../make/maker.dart'; class MongoServiceGenerator extends ServiceGenerator { diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 93dc65f1..313cbc2d 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -1,6 +1,6 @@ import 'generator.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:inflection/inflection.dart'; +import 'package:inflection2/inflection2.dart'; import '../make/maker.dart'; class RethinkServiceGenerator extends ServiceGenerator { diff --git a/pubspec.yaml b/pubspec.yaml index 45fc4847..3b22b6c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.5 +version: 2.1.5+1 dependencies: analyzer: ">=0.32.0" args: ^1.0.0 @@ -11,8 +11,7 @@ dependencies: glob: ^1.1.0 http: ^0.11.3 io: ^0.3.2 - inflection: # Note, no new update has been published, even though I sent a PR... - git: https://github.com/gmosx/dart-inflection.git + inflection2: ^0.4.2 mustache4dart: ^3.0.0-dev.1.0 path: ^1.0.0 prompts: ^1.0.0 From f3dcbacb8b8799ba3188f3055517411577202f40 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 17 Apr 2019 15:12:00 -0400 Subject: [PATCH 125/132] 2.1.5+1 --- CHANGELOG.md | 3 +++ example/main.dart | 3 +++ pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 example/main.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index f2972a52..6fd025e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.1.5+1 +* Update to `inflection2`. + # 2.1.5 * Add `shared` boilerplates. * Remove uncecessary `angel_model` imports. diff --git a/example/main.dart b/example/main.dart new file mode 100644 index 00000000..ffecc0d0 --- /dev/null +++ b/example/main.dart @@ -0,0 +1,3 @@ +void main() { + // This package isn't usable from code. +} diff --git a/pubspec.yaml b/pubspec.yaml index 3b22b6c4..fd0cec60 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ author: Tobe O -description: Command-line tools for the Angel framework. +description: Command-line tools for the Angel framework, including scaffolding. homepage: https://github.com/angel-dart/angel_cli name: angel_cli version: 2.1.5+1 From 6ec93f2296c4a4a990cd6a279476355a51a8682d Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 17 Apr 2019 16:11:14 -0400 Subject: [PATCH 126/132] Bump des --- pubspec.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index fd0cec60..a548fb3c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,9 +2,9 @@ author: Tobe O description: Command-line tools for the Angel framework, including scaffolding. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.5+1 +version: 2.1.5+2 dependencies: - analyzer: ">=0.32.0" + analyzer: ">=0.32.0 <2.0.0" args: ^1.0.0 code_builder: ^3.0.0 dart_style: ^1.0.0 @@ -16,7 +16,7 @@ dependencies: path: ^1.0.0 prompts: ^1.0.0 pubspec_parse: ^0.1.2 - quiver: ">=0.29.0" + quiver: ^2.0.0 recase: ^2.0.0 watcher: ^0.9.7 yaml: ^2.0.0 From cf1b813578a1f12d94ccd6d04162ccc9c07bc753 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 29 Apr 2019 13:41:35 -0400 Subject: [PATCH 127/132] 2.1.6 --- CHANGELOG.md | 5 +++++ bin/angel.dart | 9 ++++++--- lib/src/commands/init.dart | 8 ++++++++ lib/src/commands/make/model.dart | 1 - pubspec.yaml | 5 +++-- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd025e5..bf20d590 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 2.1.6 +* Fix a bug where models always defaulted to ORM. +* Add GraphQL boilerplate. +* Automatically restore terminal colors on shutdown. + # 2.1.5+1 * Update to `inflection2`. diff --git a/bin/angel.dart b/bin/angel.dart index bdeddff4..43c62d82 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -4,6 +4,7 @@ library angel_cli.tool; import "dart:io"; import "package:args/command_runner.dart"; import 'package:angel_cli/angel_cli.dart'; +import 'package:io/ansi.dart'; final String DOCTOR = "doctor"; @@ -28,17 +29,19 @@ main(List args) async { ..addCommand(new RenameCommand()) ..addCommand(new MakeCommand()); - return await runner.run(args).then((_) {}).catchError((exc, st) { + return await runner.run(args).catchError((exc, st) { if (exc is String) { stdout.writeln(exc); } else { stderr.writeln("Oops, something went wrong: $exc"); - exitCode = 1; - if (args.contains('--verbose')) { stderr.writeln(st); } } + + exitCode = 1; + }).whenComplete(() { + stdout.write(resetAll.wrap('')); }); } diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 099b4214..6e809774 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -221,6 +221,13 @@ Future preBuild(Directory projectDir) async { if (buildCode != 0) throw new Exception('Failed to pre-build resources.'); } +const BoilerplateInfo graphQLBoilerplate = const BoilerplateInfo( + 'GraphQL', + "A starting point for GraphQL API servers.", + 'https://github.com/angel-dart/angel.git', + ref: 'graphql', +); + const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( 'ORM', "A starting point for applications that use Angel's ORM.", @@ -256,6 +263,7 @@ const List boilerplates = const [ basicBoilerplate, //legacyBoilerplate, ormBoilerplate, + graphQLBoilerplate, sharedBoilerplate, sharedOrmBoilerplate, ]; diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 68f4bef7..16762581 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -20,7 +20,6 @@ class ModelCommand extends Command { ..addFlag('migration', abbr: 'm', help: 'Generate migrations when running `build_runner`.', - defaultsTo: true, negatable: false) ..addFlag('orm', help: 'Generate angel_orm code.', negatable: false) ..addFlag('serializable', diff --git a/pubspec.yaml b/pubspec.yaml index a548fb3c..27a1bde6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,14 +2,14 @@ author: Tobe O description: Command-line tools for the Angel framework, including scaffolding. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.5+2 +version: 2.1.6 dependencies: analyzer: ">=0.32.0 <2.0.0" args: ^1.0.0 code_builder: ^3.0.0 dart_style: ^1.0.0 glob: ^1.1.0 - http: ^0.11.3 + http: ^0.12.0 io: ^0.3.2 inflection2: ^0.4.2 mustache4dart: ^3.0.0-dev.1.0 @@ -18,6 +18,7 @@ dependencies: pubspec_parse: ^0.1.2 quiver: ^2.0.0 recase: ^2.0.0 + shutdown: ^0.4.0 watcher: ^0.9.7 yaml: ^2.0.0 #yamlicious: ^0.0.5 From 427f81ee4954170585ade36d3b3f1ad7f418852c Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 17 Jul 2019 11:18:49 -0400 Subject: [PATCH 128/132] Update to 2.1.7 --- CHANGELOG.md | 8 +++ lib/src/commands/init.dart | 2 +- lib/src/commands/make/controller.dart | 4 +- lib/src/commands/make/maker.dart | 49 +++++++++---------- lib/src/commands/make/migration.dart | 2 +- lib/src/commands/make/model.dart | 28 ++++++----- lib/src/commands/make/plugin.dart | 2 +- lib/src/commands/make/service.dart | 2 +- lib/src/commands/make/test.dart | 6 +-- .../service_generators/file_service.dart | 2 +- .../commands/service_generators/mongo.dart | 2 +- .../commands/service_generators/rethink.dart | 2 +- pubspec.yaml | 2 +- 13 files changed, 60 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf20d590..8816e2db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 2.1.7 +* Fix a bug where `ArgResults.arguments` was used in `init` instead of the +intended `ArgResults.rest`. +* Stop including `package:angel_model` imports in `make model`. +* Update dependencies in `make` commands. +* Fix `make model` to generate ORM + migration by default. +* Fix `MakerDependency` logic to print missing dependencies. + # 2.1.6 * Fix a bug where models always defaulted to ORM. * Add GraphQL boilerplate. diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 6e809774..2676c2cd 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -31,7 +31,7 @@ class InitCommand extends Command { @override run() async { Directory projectDir = new Directory( - argResults.arguments.isEmpty ? "." : argResults.arguments[0]); + argResults.rest.isEmpty ? "." : argResults.rest[0]); print("Creating new Angel project in ${projectDir.absolute.path}..."); await _cloneRepo(projectDir); // await preBuild(projectDir); diff --git a/lib/src/commands/make/controller.dart b/lib/src/commands/make/controller.dart index f5746d64..209aa401 100644 --- a/lib/src/commands/make/controller.dart +++ b/lib/src/commands/make/controller.dart @@ -40,7 +40,7 @@ class ControllerCommand extends Command { } List deps = [ - const MakerDependency('angel_framework', '^1.0.0') + const MakerDependency('angel_framework', '^2.0.0') ]; // ${pubspec.name}.src.models.${rc.snakeCase} @@ -48,7 +48,7 @@ class ControllerCommand extends Command { var rc = new ReCase(name); var controllerLib = new Library((controllerLib) { if (argResults['websocket'] as bool) { - deps.add(const MakerDependency('angel_websocket', '^1.0.0')); + deps.add(const MakerDependency('angel_websocket', '^2.0.0')); controllerLib.directives .add(new Directive.import('package:angel_websocket/server.dart')); } else { diff --git a/lib/src/commands/make/maker.dart b/lib/src/commands/make/maker.dart index 25b82795..38c57121 100644 --- a/lib/src/commands/make/maker.dart +++ b/lib/src/commands/make/maker.dart @@ -2,11 +2,14 @@ import 'dart:async'; import 'package:io/ansi.dart'; import '../../util.dart'; -class MakerDependency { +class MakerDependency implements Comparable { final String name, version; final bool dev; const MakerDependency(this.name, this.version, {this.dev: false}); + + @override + int compareTo(MakerDependency other) => name.compareTo(other.name); } Future depend(Iterable deps) async { @@ -21,6 +24,7 @@ Future depend(Iterable deps) async { isPresent = pubspec.dependencies.containsKey(dep.name); if (!isPresent) { + missing.add(dep); // TODO: https://github.com/dart-lang/pubspec_parse/issues/17: // print('Installing ${dep.name}@${dep.version}...'); // @@ -36,33 +40,28 @@ Future depend(Iterable deps) async { } } - missing.sort((a, b) { - if (!a.dev) { - if (b.dev) { - return -1; - } else { - return 0; - } - } else { - if (b.dev) { - return 0; - } else { - return 1; + var missingDeps = deps.where((d) => !d.dev).toList()..sort(); + var missingDevDeps = deps.where((d) => d.dev).toList()..sort(); + + if (missingDeps.isNotEmpty || missingDevDeps.isNotEmpty) { + var totalCount = missingDeps.length + missingDevDeps.length; + print(yellow.wrap(totalCount == 1 + ? 'You are missing one dependency.' + : 'You are missing ${missing.length} dependencies.')); + print(yellow.wrap( + 'Update your `pubspec.yaml` to add the following dependencies:\n')); + + void printMissing(String type, Iterable deps) { + if (deps.isNotEmpty) { + print(yellow.wrap(' $type:')); + for (var dep in deps) { + print(yellow.wrap(' ${dep.name}: ${dep.version}')); + } } } - }); - if (missing.isNotEmpty) { - print(yellow.wrap(missing.length == 1 - ? 'You are missing one dependency:' - : 'You are missing ${missing.length} dependencies:')); - print('\n'); - - for (var dep in missing) { - var m = ' * ${dep.name}@${dep.version}'; - if (dep.dev) m += ' (dev dependency)'; - print(yellow.wrap(m)); - } + printMissing('dependencies', missingDeps); + printMissing('dev_dependencies', missingDevDeps); } // if (isPresent) { diff --git a/lib/src/commands/make/migration.dart b/lib/src/commands/make/migration.dart index 58200996..a5792b0c 100644 --- a/lib/src/commands/make/migration.dart +++ b/lib/src/commands/make/migration.dart @@ -35,7 +35,7 @@ class MigrationCommand extends Command { name = prompts.get('Name of model class'); } - var deps = [const MakerDependency('angel_migration', '^1.0.0-alpha')]; + var deps = [const MakerDependency('angel_migration', '^2.0.0')]; var rc = new ReCase(name); var migrationLib = new Library((migrationLib) { diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart index 16762581..04e964c8 100644 --- a/lib/src/commands/make/model.dart +++ b/lib/src/commands/make/model.dart @@ -20,7 +20,7 @@ class ModelCommand extends Command { ..addFlag('migration', abbr: 'm', help: 'Generate migrations when running `build_runner`.', - negatable: false) + defaultsTo: true) ..addFlag('orm', help: 'Generate angel_orm code.', negatable: false) ..addFlag('serializable', help: 'Generate angel_serialize annotations.', defaultsTo: true) @@ -47,33 +47,35 @@ class ModelCommand extends Command { var rc = new ReCase(name); var modelLib = new Library((modelLib) { - if (argResults['migration'] as bool) { + if (argResults['orm'] as bool && argResults['migration'] as bool) { modelLib.directives.addAll([ new Directive.import('package:angel_migration/angel_migration.dart'), ]); } - var needsSerialize = argResults['serializable'] as bool || - argResults['orm'] as bool || - argResults['migration'] as bool; + var needsSerialize = + argResults['serializable'] as bool || argResults['orm'] as bool; + // argResults['migration'] as bool; if (needsSerialize) { modelLib.directives.add(new Directive.import( 'package:angel_serialize/angel_serialize.dart')); deps.add(const MakerDependency('angel_serialize', '^2.0.0')); deps.add(const MakerDependency('angel_serialize_generator', '^2.0.0')); - deps.add(const MakerDependency('build_runner', '">=0.7.0 <0.10.0"')); - } else { - modelLib.directives - .add(new Directive.import('package:angel_model/angel_model.dart')); - deps.add(const MakerDependency('angel_model', '^1.0.0')); + deps.add(const MakerDependency('build_runner', '^1.0.0')); } - if (argResults['orm'] as bool || argResults['migration'] as bool) { + // else { + // modelLib.directives + // .add(new Directive.import('package:angel_model/angel_model.dart')); + // deps.add(const MakerDependency('angel_model', '^1.0.0')); + // } + + if (argResults['orm'] as bool) { modelLib.directives.addAll([ new Directive.import('package:angel_orm/angel_orm.dart'), ]); - deps.add(const MakerDependency('angel_orm', '^1.0.0-alpha')); + deps.add(const MakerDependency('angel_orm', '^2.0.0')); } modelLib.body.addAll([ @@ -91,7 +93,7 @@ class ModelCommand extends Command { modelClazz.annotations.add(refer('serializable')); } - if (argResults['orm'] as bool || argResults['migration'] as bool) { + if (argResults['orm'] as bool) { if (argResults['migration'] as bool) { modelClazz.annotations.add(refer('orm')); } else { diff --git a/lib/src/commands/make/plugin.dart b/lib/src/commands/make/plugin.dart index cfccbfe9..4f979777 100644 --- a/lib/src/commands/make/plugin.dart +++ b/lib/src/commands/make/plugin.dart @@ -35,7 +35,7 @@ class PluginCommand extends Command { } List deps = [ - const MakerDependency('angel_framework', '^1.0.0') + const MakerDependency('angel_framework', '^2.0.0') ]; var rc = new ReCase(name); diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart index 2d99e6d9..a21d07f1 100644 --- a/lib/src/commands/make/service.dart +++ b/lib/src/commands/make/service.dart @@ -42,7 +42,7 @@ class ServiceCommand extends Command { } List deps = [ - const MakerDependency('angel_framework', '^1.0.0') + const MakerDependency('angel_framework', '^2.0.0') ]; // '${pubspec.name}.src.services.${rc.snakeCase}' diff --git a/lib/src/commands/make/test.dart b/lib/src/commands/make/test.dart index d95db4ff..32adcd7f 100644 --- a/lib/src/commands/make/test.dart +++ b/lib/src/commands/make/test.dart @@ -38,9 +38,9 @@ class TestCommand extends Command { } List deps = [ - const MakerDependency('angel_framework', '^1.0.0'), - const MakerDependency('angel_test', '^1.0.0', dev: true), - const MakerDependency('test', '^0.12.0', dev: true), + const MakerDependency('angel_framework', '^2.0.0'), + const MakerDependency('angel_test', '^2.0.0', dev: true), + const MakerDependency('test', '^1.0.0', dev: true), ]; var rc = new ReCase(name); diff --git a/lib/src/commands/service_generators/file_service.dart b/lib/src/commands/service_generators/file_service.dart index 673fe12d..39e1086e 100644 --- a/lib/src/commands/service_generators/file_service.dart +++ b/lib/src/commands/service_generators/file_service.dart @@ -8,7 +8,7 @@ class FileServiceGenerator extends ServiceGenerator { @override List get dependencies => - const [const MakerDependency('angel_file_service', '^1.0.0')]; + const [const MakerDependency('angel_file_service', '^2.0.0')]; @override bool get goesFirst => true; diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart index bb6a45f4..d8d2f0d3 100644 --- a/lib/src/commands/service_generators/mongo.dart +++ b/lib/src/commands/service_generators/mongo.dart @@ -8,7 +8,7 @@ class MongoServiceGenerator extends ServiceGenerator { @override List get dependencies => - const [const MakerDependency('angel_mongo', '^1.0.0')]; + const [const MakerDependency('angel_mongo', '^2.0.0')]; @override bool get createsModel => false; diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 313cbc2d..57059844 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -8,7 +8,7 @@ class RethinkServiceGenerator extends ServiceGenerator { @override List get dependencies => - const [const MakerDependency('angel_rethink', '^1.0.0')]; + const [const MakerDependency('angel_rethink', '^2.0.0')]; @override bool get createsModel => false; diff --git a/pubspec.yaml b/pubspec.yaml index 27a1bde6..d024c561 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework, including scaffolding. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.6 +version: 2.1.7 dependencies: analyzer: ">=0.32.0 <2.0.0" args: ^1.0.0 From 0e63ee9e7727cb477f59baad6d72e124bf1150ec Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 17 Jul 2019 11:21:35 -0400 Subject: [PATCH 129/132] Heavy checkmark --- lib/src/util.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/util.dart b/lib/src/util.dart index f145d2a8..46d48fd0 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -4,7 +4,7 @@ import 'package:io/ansi.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; //import 'package:yamlicious/yamlicious.dart'; -final String checkmark = ansiOutputEnabled ? '\u2713' : '[Success]'; +final String checkmark = ansiOutputEnabled ? '\u2714' : '[Success]'; final String ballot = ansiOutputEnabled ? '\u2717' : '[Failure]'; From ecdfacce27c075d7d5011d8537cd69f53715358d Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 17 Jul 2019 11:56:52 -0400 Subject: [PATCH 130/132] Store boilerplates offline --- lib/src/commands/init.dart | 87 ++++++++++++++++---------------- lib/src/commands/make/maker.dart | 11 ++-- lib/src/util.dart | 20 ++++++++ 3 files changed, 69 insertions(+), 49 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 2676c2cd..f3c3296a 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -30,8 +30,8 @@ class InitCommand extends Command { @override run() async { - Directory projectDir = new Directory( - argResults.rest.isEmpty ? "." : argResults.rest[0]); + Directory projectDir = + new Directory(argResults.rest.isEmpty ? "." : argResults.rest[0]); print("Creating new Angel project in ${projectDir.absolute.path}..."); await _cloneRepo(projectDir); // await preBuild(projectDir); @@ -136,52 +136,51 @@ class InitCommand extends Command { var boilerplate = prompts.choose( 'Choose a project type before continuing', boilerplates); - print( - 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); - - Process git; - - if (boilerplate.ref == null) { - git = await Process.start( - "git", - ["clone", "--depth", "1", boilerplate.url, projectDir.absolute.path], - mode: ProcessStartMode.inheritStdio, - ); - } else { - // git clone --single-branch -b branch host:/dir.git - git = await Process.start( - "git", - [ - "clone", - "--depth", - "1", - "--single-branch", - "-b", - boilerplate.ref, - boilerplate.url, - projectDir.absolute.path - ], - mode: ProcessStartMode.inheritStdio, - ); - } - - if (await git.exitCode != 0) { - throw new Exception("Could not clone repo."); - } - - /* - if (boilerplate.ref != null) { - git = await Process - .start("git", ["checkout", 'origin/${boilerplate.ref}']); - - stdout.addStream(git.stdout); - stderr.addStream(git.stderr); + // Ultimately, we want a clone of every boilerplate locally on the system. + var boilerplateRootDir = Directory(p.join(angelDir.path, 'boilerplates')); + var boilerplateDir = Directory(p.join(boilerplateRootDir.path, + p.basenameWithoutExtension(boilerplate.url))); + await boilerplateRootDir.create(recursive: true); + // If there is no clone existing, clone it. + if (!await boilerplateDir.exists()) { + print( + 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); + var git = await Process.start( + "git", + [ + "clone", + "--depth", + "1", + boilerplate.url, + boilerplateDir.absolute.path + ], + mode: ProcessStartMode.inheritStdio); if (await git.exitCode != 0) { - throw new Exception("Could not checkout branch ${boilerplate.ref}."); + throw new Exception("Could not clone repo."); } } - */ + + // Next, check out the given branch. + var branch = boilerplate.ref ?? 'master'; + var git = await Process.start("git", ["checkout", 'origin/$branch'], + mode: ProcessStartMode.inheritStdio, + workingDirectory: boilerplateDir.absolute.path); + if (await git.exitCode != 0) { + throw new Exception("Could not checkout branch $branch."); + } + + // Next, pull from git. + git = await Process.start("git", ["pull", 'origin/$branch'], + mode: ProcessStartMode.inheritStdio, + workingDirectory: boilerplateDir.absolute.path); + if (await git.exitCode != 0) { + throw new Exception( + "Update of $branch failed. Attempting to continue with existing contents."); + } + + // Next, just copy everything into the given directory. + await copyDirectory(boilerplateDir, projectDir); if (boilerplate.needsPrebuild) { await preBuild(projectDir).catchError((_) => null); diff --git a/lib/src/commands/make/maker.dart b/lib/src/commands/make/maker.dart index 38c57121..09bf1bd9 100644 --- a/lib/src/commands/make/maker.dart +++ b/lib/src/commands/make/maker.dart @@ -40,14 +40,14 @@ Future depend(Iterable deps) async { } } - var missingDeps = deps.where((d) => !d.dev).toList()..sort(); - var missingDevDeps = deps.where((d) => d.dev).toList()..sort(); + var missingDeps = missing.where((d) => !d.dev).toList()..sort(); + var missingDevDeps = missing.where((d) => d.dev).toList()..sort(); + var totalCount = missingDeps.length + missingDevDeps.length; - if (missingDeps.isNotEmpty || missingDevDeps.isNotEmpty) { - var totalCount = missingDeps.length + missingDevDeps.length; + if (totalCount > 0) { print(yellow.wrap(totalCount == 1 ? 'You are missing one dependency.' - : 'You are missing ${missing.length} dependencies.')); + : 'You are missing $totalCount dependencies.')); print(yellow.wrap( 'Update your `pubspec.yaml` to add the following dependencies:\n')); @@ -62,6 +62,7 @@ Future depend(Iterable deps) async { printMissing('dependencies', missingDeps); printMissing('dev_dependencies', missingDevDeps); + print('\n'); } // if (isPresent) { diff --git a/lib/src/util.dart b/lib/src/util.dart index 46d48fd0..e57e41f3 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; import 'package:io/ansi.dart'; +import 'package:path/path.dart' as p; import 'package:pubspec_parse/pubspec_parse.dart'; //import 'package:yamlicious/yamlicious.dart'; @@ -13,6 +14,8 @@ String get homeDirPath => Directory get homeDir => new Directory(homeDirPath); +Directory get angelDir => Directory(p.join(homeDir.path, '.angel')); + Future loadPubspec([Directory directory]) { directory ??= Directory.current; var file = new File.fromUri(directory.uri.resolve('pubspec.yaml')); @@ -21,6 +24,23 @@ Future loadPubspec([Directory directory]) { .then((yaml) => new Pubspec.parse(yaml, sourceUrl: file.uri)); } +// From: https://gist.github.com/tobischw/98dcd2563eec9a2a87bda8299055358a +Future copyDirectory(Directory source, Directory destination) async { + await for (var entity in source.list(recursive: false)) { + if (entity is Directory) { + var newDirectory = + Directory(p.join(destination.absolute.path, p.basename(entity.path))); + print('Copying dir "${entity.path}" -> "${newDirectory.path}..."'); + await newDirectory.create(); + await copyDirectory(entity.absolute, newDirectory); + } else if (entity is File) { + var newPath = p.join(destination.path, p.basename(entity.path)); + print('Copying file "${entity.path}" -> "$newPath"'); + await entity.copy(newPath); + } + } +} + Future savePubspec(Pubspec pubspec) async { // TODO: Save pubspec for real? //var text = toYamlString(pubspec); From efce4496fc339593f5385f134d15b59e5216dcd1 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 17 Jul 2019 12:53:11 -0400 Subject: [PATCH 131/132] Offline downloading --- lib/src/commands/init.dart | 91 ++++++++++++++++++++++++++++---------- lib/src/util.dart | 10 ++++- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index f3c3296a..7dafb866 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -23,6 +23,10 @@ class InitCommand extends Command { InitCommand() { argParser + ..addFlag('offline', + help: + 'Disable online fetching of boilerplates. Also disables `pub-get`.', + negatable: false) ..addFlag('pub-get', defaultsTo: true) ..addOption('project-name', abbr: 'n', help: 'The name for this project.'); @@ -57,7 +61,7 @@ class InitCommand extends Command { await renamePubspec(projectDir, 'angel', name); await renameDartFiles(projectDir, 'angel', name); - if (argResults['pub-get'] != false) { + if (argResults['pub-get'] != false && argResults['offline'] == false) { print('Now running pub get...'); await _pubGet(projectDir); } @@ -116,6 +120,8 @@ class InitCommand extends Command { } _cloneRepo(Directory projectDir) async { + Directory boilerplateDir; + try { if (await projectDir.exists()) { var shouldDelete = prompts.getBool( @@ -138,15 +144,29 @@ class InitCommand extends Command { // Ultimately, we want a clone of every boilerplate locally on the system. var boilerplateRootDir = Directory(p.join(angelDir.path, 'boilerplates')); - var boilerplateDir = Directory(p.join(boilerplateRootDir.path, - p.basenameWithoutExtension(boilerplate.url))); + var boilerplateBasename = p.basenameWithoutExtension(boilerplate.url); + if (boilerplate.ref != null) boilerplateBasename += '.${boilerplate.ref}'; + boilerplateDir = + Directory(p.join(boilerplateRootDir.path, boilerplateBasename)); await boilerplateRootDir.create(recursive: true); + var branch = boilerplate.ref ?? 'master'; + // If there is no clone existing, clone it. if (!await boilerplateDir.exists()) { + if (argResults['offline'] as bool) { + throw Exception( + '--offline was selected, but the "${boilerplate.name}" boilerplate has not yet been downloaded.'); + } + print( 'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...'); - var git = await Process.start( + Process git; + + if (boilerplate.ref == null) { + print(darkGray.wrap( + '\$ git clone --depth 1 ${boilerplate.url} ${boilerplateDir.absolute.path}')); + git = await Process.start( "git", [ "clone", @@ -155,28 +175,46 @@ class InitCommand extends Command { boilerplate.url, boilerplateDir.absolute.path ], - mode: ProcessStartMode.inheritStdio); + mode: ProcessStartMode.inheritStdio, + ); + } else { + // git clone --single-branch -b branch host:/dir.git + print(darkGray.wrap( + '\$ git clone --depth 1 --single-branch -b ${boilerplate.ref} ${boilerplate.url} ${boilerplateDir.absolute.path}')); + git = await Process.start( + "git", + [ + "clone", + "--depth", + "1", + "--single-branch", + "-b", + boilerplate.ref, + boilerplate.url, + boilerplateDir.absolute.path + ], + mode: ProcessStartMode.inheritStdio, + ); + } + if (await git.exitCode != 0) { throw new Exception("Could not clone repo."); } } - // Next, check out the given branch. - var branch = boilerplate.ref ?? 'master'; - var git = await Process.start("git", ["checkout", 'origin/$branch'], - mode: ProcessStartMode.inheritStdio, - workingDirectory: boilerplateDir.absolute.path); - if (await git.exitCode != 0) { - throw new Exception("Could not checkout branch $branch."); - } - - // Next, pull from git. - git = await Process.start("git", ["pull", 'origin/$branch'], - mode: ProcessStartMode.inheritStdio, - workingDirectory: boilerplateDir.absolute.path); - if (await git.exitCode != 0) { - throw new Exception( - "Update of $branch failed. Attempting to continue with existing contents."); + // Otherwise, pull from git. + else if (!(argResults['offline'] as bool)) { + print(darkGray.wrap('\$ git pull origin $branch')); + var git = await Process.start("git", ['pull', 'origin', '$branch'], + mode: ProcessStartMode.inheritStdio, + workingDirectory: boilerplateDir.absolute.path); + if (await git.exitCode != 0) { + print(yellow.wrap( + "Update of $branch failed. Attempting to continue with existing contents.")); + } + } else { + print(darkGray.wrap( + 'Using existing contents of "${boilerplate.name}" boilerplate.')); } // Next, just copy everything into the given directory. @@ -189,6 +227,8 @@ class InitCommand extends Command { var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git")); if (await gitDir.exists()) await gitDir.delete(recursive: true); } catch (e) { + await boilerplateDir.delete(recursive: true).catchError((_) => null); + if (e is! String) { print(red.wrap("$ballot Could not initialize Angel project.")); } @@ -198,7 +238,8 @@ class InitCommand extends Command { _pubGet(Directory projectDir) async { var pubPath = resolvePub(); - print('Running pub at "$pubPath"...'); + print(darkGray.wrap('Running pub at "$pubPath"...')); + print(darkGray.wrap('\$ $pubPath get')); var pub = await Process.start(pubPath, ["get"], workingDirectory: projectDir.absolute.path, mode: ProcessStartMode.inheritStdio); @@ -209,9 +250,11 @@ class InitCommand extends Command { Future preBuild(Directory projectDir) async { // Run build - print('Running `pub run build_runner build`...'); + // print('Running `pub run build_runner build`...'); + print(darkGray.wrap('\$ pub run build_runner build')); - var build = await Process.start(resolvePub(), ['run', 'build'], + var build = await Process.start( + resolvePub(), ['run', 'build_runner', 'build'], workingDirectory: projectDir.absolute.path, mode: ProcessStartMode.inheritStdio); diff --git a/lib/src/util.dart b/lib/src/util.dart index e57e41f3..af6f838a 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -26,19 +26,25 @@ Future loadPubspec([Directory directory]) { // From: https://gist.github.com/tobischw/98dcd2563eec9a2a87bda8299055358a Future copyDirectory(Directory source, Directory destination) async { + // if (!topLevel) stdout.write('\r'); + // print(darkGray + // .wrap('Copying dir "${source.path}" -> "${destination.path}..."')); + await for (var entity in source.list(recursive: false)) { + if (p.basename(entity.path) == '.git') continue; if (entity is Directory) { var newDirectory = Directory(p.join(destination.absolute.path, p.basename(entity.path))); - print('Copying dir "${entity.path}" -> "${newDirectory.path}..."'); await newDirectory.create(); await copyDirectory(entity.absolute, newDirectory); } else if (entity is File) { var newPath = p.join(destination.path, p.basename(entity.path)); - print('Copying file "${entity.path}" -> "$newPath"'); + // print(darkGray.wrap('\rCopying file "${entity.path}" -> "$newPath"')); await entity.copy(newPath); } } + + // print('\rCopied "${source.path}" -> "${destination.path}.'); } Future savePubspec(Pubspec pubspec) async { From 63e5c99c96809070c18af0c3803141d0436dd992 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 7 Aug 2019 03:43:19 -0400 Subject: [PATCH 132/132] Fix `init` bug --- CHANGELOG.md | 4 ++++ lib/src/util.dart | 3 ++- pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8816e2db..7e3bf35f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.1.7+1 +* Fix a bug where new directories were not being created in +`init`. + # 2.1.7 * Fix a bug where `ArgResults.arguments` was used in `init` instead of the intended `ArgResults.rest`. diff --git a/lib/src/util.dart b/lib/src/util.dart index af6f838a..a294d40c 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -35,11 +35,12 @@ Future copyDirectory(Directory source, Directory destination) async { if (entity is Directory) { var newDirectory = Directory(p.join(destination.absolute.path, p.basename(entity.path))); - await newDirectory.create(); + await newDirectory.create(recursive: true); await copyDirectory(entity.absolute, newDirectory); } else if (entity is File) { var newPath = p.join(destination.path, p.basename(entity.path)); // print(darkGray.wrap('\rCopying file "${entity.path}" -> "$newPath"')); + await File(newPath).create(recursive: true); await entity.copy(newPath); } } diff --git a/pubspec.yaml b/pubspec.yaml index d024c561..34ab6e6c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ author: Tobe O description: Command-line tools for the Angel framework, including scaffolding. homepage: https://github.com/angel-dart/angel_cli name: angel_cli -version: 2.1.7 +version: 2.1.7+1 dependencies: analyzer: ">=0.32.0 <2.0.0" args: ^1.0.0