diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e6018c..063b753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [5.17.0] - 2024-01-01 + +* Metro cli improvements + * Ability to create pages in a subfolder. E.g. `metro make:page auth/login` + * Ability to create models in a subfolder. E.g. `metro make:model auth/user` + * Ability to create controllers in a subfolder. E.g. `metro make:controller auth/login` + * Update `--postman` flag for API Services. Now you can run `metro make:api_service example_api_service --postman` and it will help you create an API Service from a Postman collection. +* Update stubs +* Add `cli_dialog` as a dependency +* Update pubspec.yaml + ## [5.16.0] - 2023-12-25 * Update pubspec.yaml diff --git a/LICENSE b/LICENSE index 99cc5a1..8304f9f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Anthony Gordon +Copyright (c) 2024 Anthony Gordon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/pubspec.lock b/example/pubspec.lock index 2f93dff..b0947d8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + cli_dialog: + dependency: transitive + description: + name: cli_dialog + sha256: "47530e1c4a6190662954cefce196d35143c4ed2675ea697d6e542790bdef5641" + url: "https://pub.dev" + source: hosted + version: "0.5.0" clock: dependency: transitive description: @@ -57,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + dart_console: + dependency: transitive + description: + name: dart_console + sha256: dfa4b63eb4382325ff975fdb6b7a0db8303bb5809ee5cb4516b44153844742ed + url: "https://pub.dev" + source: hosted + version: "1.2.0" dio: dependency: transitive description: @@ -235,15 +251,15 @@ packages: path: ".." relative: true source: path - version: "5.16.0" + version: "5.17.0" nylo_support: dependency: transitive description: name: nylo_support - sha256: "8860d620bc0de49b5f0fee22d7391a6ad240fe4bc5737f1a9287bcd5d82d3e71" + sha256: "3bb7b0c16cc477d4f85217d6dae881f17f671c5d45f62063f48b5634162610ba" url: "https://pub.dev" source: hosted - version: "5.23.0" + version: "5.24.0" page_transition: dependency: transitive description: diff --git a/lib/metro/metro.dart b/lib/metro/metro.dart index 98b09ac..2200476 100644 --- a/lib/metro/metro.dart +++ b/lib/metro/metro.dart @@ -4,10 +4,12 @@ import 'dart:convert'; import 'dart:io'; import 'package:args/args.dart'; +import 'package:cli_dialog/cli_dialog.dart'; import 'package:nylo_framework/json_dart_generator/dart_code_generator.dart'; import 'package:nylo_framework/metro/stubs/config_stub.dart'; import 'package:nylo_framework/metro/stubs/interceptor_stub.dart'; import 'package:nylo_framework/metro/stubs/route_guard_stub.dart'; +import 'package:nylo_support/metro/models/metro_project_file.dart'; import 'package:nylo_support/metro/models/ny_command.dart'; import 'package:nylo_framework/metro/stubs/api_service_stub.dart'; import 'package:nylo_framework/metro/stubs/controller_stub.dart'; @@ -326,7 +328,7 @@ _makeApiService(List arguments) async { abbr: 'u', help: 'Provide the Base Url that should be used in the API service.', ); - parser.addOption( + parser.addFlag( postmanCollectionOption, abbr: 'p', help: @@ -353,24 +355,105 @@ _makeApiService(List arguments) async { } // handle postman collection - if (argResults.options.contains(postmanCollectionOption)) { - String? assetName = argResults[postmanCollectionOption]; - File file = File("public/assets/postman/$assetName"); + if (argResults.options.contains(postmanCollectionOption) && + argResults[postmanCollectionOption]) { + // find all files in a directory + final directoryPostmanCollections = + Directory("public/assets/postman/collections"); + if (!(await directoryPostmanCollections.exists())) { + MetroConsole.writeInBlack( + "Your project is missing a 'public/assets/postman/collections' directory"); + exit(1); + } + List filesCollections = + directoryPostmanCollections.listSync(recursive: false).toList(); + List fileCollectionsNames = []; + filesCollections.forEach((element) { + if (element.path.contains(".json")) { + fileCollectionsNames.add(element.path.split("/").last); + } + }); + + if (fileCollectionsNames.isEmpty) { + MetroConsole.writeInBlack( + "Your project is missing Postman collections inside the 'public/assets/postman/collections' directory"); + exit(1); + } + + final directoryPostmanEnvironment = + Directory("public/assets/postman/environments"); + if (!(await directoryPostmanEnvironment.exists())) { + MetroConsole.writeInBlack( + "Your project is missing a 'public/assets/postman/environments' directory"); + exit(1); + } + List filesEnvironment = + directoryPostmanEnvironment.listSync(recursive: false).toList(); + List fileEnvironmentNames = []; + filesEnvironment.forEach((element) { + if (element.path.contains(".json")) { + fileEnvironmentNames.add(element.path.split("/").last); + } + }); + + final dialogQuestions = CLI_Dialog(listQuestions: [ + [ + { + 'question': 'Which Postman collection would you like to use?', + 'options': fileCollectionsNames + }, + 'collection_name' + ], + [ + { + 'question': 'Which Postman environment would you like to use?', + 'options': ["None", ...fileEnvironmentNames] + }, + 'environment_name' + ], + ]).ask(); + + final dynamic collectionName = dialogQuestions['collection_name']; + final dynamic environmentName = dialogQuestions['environment_name']; + + String? assetName = collectionName; + File file = File("public/assets/postman/collections/$assetName"); if ((await file.exists()) == false) { MetroConsole.writeInRed("Cannot locate \"$assetName\""); MetroConsole.writeInBlack( - "Add \"$assetName\" to your \"/public/assets/postman/\" directory."); + "Add \"$assetName\" to your \"/public/assets/postman/collections\" directory."); exit(0); } String fileJson = await file.readAsString(); dynamic json = jsonDecode(fileJson); - // get root postman contents - File filePostman = File("postman.json"); - if ((await filePostman.exists()) == false) { - MetroConsole.writeInBlack( - "Your project is missing a 'postman.json' file. E.g.\n" + - ''' + Map postmanGlobalVars = {}; + if (environmentName != "N") { + File filePostman = + File("public/assets/postman/environments/$environmentName"); + if ((await filePostman.exists()) == false) { + MetroConsole.writeInBlack( + "File doesn't exist 'public/assets/postman/environments/$environmentName', please add your environment file from Postman."); + exit(1); + } + String jsonFilePostman = await filePostman.readAsString(); + Map jsonDecodedPostmanEnvironment = + jsonDecode(jsonFilePostman); + List postmanEnvironmentValues = + jsonDecodedPostmanEnvironment['values']; + Map newObj = {}; + postmanEnvironmentValues.forEach((value) { + newObj[value['key']] = value['value']; + }); + + postmanGlobalVars = newObj; + } else { + // get root postman contents + File filePostman = File("postman.json"); + if ((await filePostman.exists()) == false) { + MetroConsole.writeInBlack( + "Your project is missing a 'postman.json' file. E.g.\n" + + ''' { "global": { "BASE_URL": "https://nylo.dev", @@ -378,14 +461,14 @@ _makeApiService(List arguments) async { } } ''' + - "Create the file at the root of the project."); - exit(1); + "Create the file at the root of the project."); + exit(1); + } + String jsonFilePostman = await filePostman.readAsString(); + Map postmanFileContents = + Map.from(jsonDecode(jsonFilePostman)); + postmanGlobalVars = postmanFileContents['global']; } - String jsonFilePostman = await filePostman.readAsString(); - - Map postmanFileContents = - Map.from(jsonDecode(jsonFilePostman)); - Map postmanGlobalVars = postmanFileContents['global']; await _makePostmanApiService( json: json, @@ -394,6 +477,7 @@ _makeApiService(List arguments) async { hasForceFlag: hasForceFlag, baseUrlFlagValue: baseUrlFlagValue); await MetroService.runProcess("dart format lib/app/models"); + await MetroService.runProcess("dart format lib/app/networking"); exit(0); } @@ -595,9 +679,9 @@ _makePostmanApiService( } } // create model - await _createNyloModel(ReCase(modelName), + await _createNyloModel(modelName, stubModel: code, hasForceFlag: hasForceFlag); - imports.add(makeImportPathModel(ReCase(modelName).snakeCase)); + imports.add(makeImportPathModel(modelName.snakeCase)); } if (postmanItem["request"]["url"] == null) { @@ -785,13 +869,13 @@ _makeController(List arguments) async { bool? hasForceFlag = argResults[forceFlag]; MetroService.hasHelpFlag(argResults[helpFlag], parser.usage); - String className = - argResults.arguments.first.replaceAll(RegExp(r'(_?controller)'), ""); - ReCase classReCase = ReCase(className); + MetroProjectFile projectFile = MetroService.createMetroProjectFile( + argResults.arguments.first, + prefix: RegExp(r'(_?controller)')); - String stubController = controllerStub(controllerName: classReCase); + String stubController = controllerStub(controllerName: projectFile.name); - await MetroService.makeController(classReCase.snakeCase, stubController, + await MetroService.makeController(projectFile.name, stubController, forceCreate: hasForceFlag ?? false); } @@ -819,10 +903,10 @@ _makeModel(List arguments) async { bool hasJsonFlag = argResults[jsonFlag] ?? false; MetroService.hasHelpFlag(argResults[helpFlag], parser.usage); - String className = argResults.arguments.first; - ReCase classReCase = ReCase(className); + MetroProjectFile projectFile = + MetroService.createMetroProjectFile(argResults.arguments.first); - String modelName = classReCase.pascalCase; + String modelName = projectFile.name.pascalCase; String stubModel = ""; if (hasJsonFlag) { final fileName = 'nylo-model.json'; @@ -857,19 +941,28 @@ _makeModel(List arguments) async { } else { stubModel = modelStub(modelName: modelName); } - await _createNyloModel(classReCase, - stubModel: stubModel, hasForceFlag: hasForceFlag); + await _createNyloModel(projectFile.name, + stubModel: stubModel, + hasForceFlag: hasForceFlag, + creationPath: projectFile.creationPath); if (hasJsonFlag) { + String creationPath = (projectFile.creationPath != null + ? projectFile.creationPath! + "/" + : ""); await MetroService.runProcess( - "dart format lib/app/models/${className.snakeCase}.dart"); + "dart format lib/app/models/$creationPath${projectFile.name.snakeCase}.dart"); } } /// Creates a new Model -_createNyloModel(ReCase classReCase, - {required String stubModel, bool? hasForceFlag}) async { +_createNyloModel(String classReCase, + {required String stubModel, + bool? hasForceFlag, + String? creationPath}) async { await MetroService.makeModel(classReCase.snakeCase, stubModel, - forceCreate: hasForceFlag ?? false, addToConfig: true); + forceCreate: hasForceFlag ?? false, + addToConfig: true, + creationPath: creationPath); } /// Creates a new Page @@ -930,29 +1023,41 @@ _makePage(List arguments) async { exit(1); } - String className = - argResults.arguments.first.replaceAll(RegExp(r'(_?page)'), ""); - ReCase classReCase = ReCase(className); + MetroProjectFile projectFile = MetroService.createMetroProjectFile( + argResults.arguments.first, + prefix: RegExp(r'(_?page)')); if (shouldCreateController) { - String stubPageAndController = - pageWithControllerStub(className: classReCase); - await MetroService.makePage(className.snakeCase, stubPageAndController, - forceCreate: hasForceFlag ?? false, - addToRoute: true, - isInitialPage: initialPage, - isAuthPage: authPage); - - String stubController = controllerStub(controllerName: classReCase); - await MetroService.makeController(className.snakeCase, stubController, - forceCreate: hasForceFlag ?? false); + String stubPageAndController = pageWithControllerStub( + className: projectFile.name, creationPath: projectFile.creationPath); + await MetroService.makePage( + projectFile.name, + stubPageAndController, + forceCreate: hasForceFlag ?? false, + addToRoute: true, + isInitialPage: initialPage, + isAuthPage: authPage, + creationPath: projectFile.creationPath, + ); + + String stubController = controllerStub(controllerName: projectFile.name); + await MetroService.makeController( + projectFile.name, + stubController, + forceCreate: hasForceFlag ?? false, + creationPath: projectFile.creationPath, + ); } else { - String stubPage = pageStub(className: classReCase); - await MetroService.makePage(className.snakeCase, stubPage, - forceCreate: hasForceFlag ?? false, - addToRoute: true, - isInitialPage: initialPage, - isAuthPage: authPage); + String stubPage = pageStub(className: projectFile.name); + await MetroService.makePage( + projectFile.name, + stubPage, + forceCreate: hasForceFlag ?? false, + addToRoute: true, + isInitialPage: initialPage, + isAuthPage: authPage, + creationPath: projectFile.creationPath, + ); } } diff --git a/lib/metro/stubs/controller_stub.dart b/lib/metro/stubs/controller_stub.dart index 81d1353..f56d278 100644 --- a/lib/metro/stubs/controller_stub.dart +++ b/lib/metro/stubs/controller_stub.dart @@ -1,8 +1,8 @@ import 'package:recase/recase.dart'; /// This stub is used to create a new Controller. -String controllerStub({required ReCase controllerName}) => ''' -import 'controller.dart'; +String controllerStub({required String controllerName}) => ''' +import '/app/controllers/controller.dart'; import 'package:flutter/widgets.dart'; class ${controllerName.pascalCase}Controller extends Controller { diff --git a/lib/metro/stubs/network_method_stub.dart b/lib/metro/stubs/network_method_stub.dart index fae990f..2bc0859 100644 --- a/lib/metro/stubs/network_method_stub.dart +++ b/lib/metro/stubs/network_method_stub.dart @@ -16,6 +16,7 @@ String networkMethodStub({ //$method\n${urlFullPath != null ? ' /// $urlFullPath' : ''} Future<${_getType(model, isList: isList, isOptional: true)}> $methodName(${_mapParams(queryParams, dataParams)}) async => await network${_getType(model, isList: isList, returnDynamic: false, addBrackets: true)}( ${_callBackType(headers: headerParams, method: method, path: path, queryParams: queryParams, dataParams: dataParams)} + ${urlFullPath != null ? 'baseUrl: "${Uri.parse(urlFullPath).origin}"' : ''} ); '''; @@ -92,7 +93,7 @@ String _callBackType( ${headers.entries.map((e) => "\"${e.key}\": '${e.value}'").toList().join(", ")} }); return ${_requestType(method, path, queryParams, dataParams).substring(0, _requestType(method, path, queryParams, dataParams).length - 1)}; - }'''; + },'''; } String _requestType(String method, String path, diff --git a/lib/metro/stubs/page_stub.dart b/lib/metro/stubs/page_stub.dart index 8f5111a..be2f1b0 100644 --- a/lib/metro/stubs/page_stub.dart +++ b/lib/metro/stubs/page_stub.dart @@ -1,7 +1,7 @@ import 'package:recase/recase.dart'; /// This stub is used to create a new Page. -String pageStub({required ReCase className}) => ''' +String pageStub({required String className}) => ''' import 'package:flutter/material.dart'; import 'package:nylo_framework/nylo_framework.dart'; diff --git a/lib/metro/stubs/page_w_controller_stub.dart b/lib/metro/stubs/page_w_controller_stub.dart index c35b0d0..9a4193c 100644 --- a/lib/metro/stubs/page_w_controller_stub.dart +++ b/lib/metro/stubs/page_w_controller_stub.dart @@ -1,10 +1,12 @@ import 'package:recase/recase.dart'; /// This stub is used to create a new Page + Controller. -String pageWithControllerStub({required ReCase className}) => ''' +String pageWithControllerStub( + {required String className, String? creationPath}) => + ''' import 'package:flutter/material.dart'; import 'package:nylo_framework/nylo_framework.dart'; -import '/app/controllers/${className.snakeCase}_controller.dart'; +import '/app/controllers/${creationPath != null ? creationPath + "/${className.snakeCase}" : className.snakeCase}_controller.dart'; class ${className.pascalCase}Page extends NyStatefulWidget<${className.pascalCase}Controller> { static const path = '/${className.paramCase}'; diff --git a/lib/metro/stubs/postman_api_service_stub.dart b/lib/metro/stubs/postman_api_service_stub.dart index c690278..88ee8c4 100644 --- a/lib/metro/stubs/postman_api_service_stub.dart +++ b/lib/metro/stubs/postman_api_service_stub.dart @@ -14,7 +14,7 @@ class ${rc.pascalCase}ApiService extends NyApiService { ${rc.pascalCase}ApiService({BuildContext? buildContext}) : super(buildContext, decoders: modelDecoders); @override - String get baseUrl => $baseUrl; + String get baseUrl => ""; $networkMethods } diff --git a/lib/metro/stubs/route_guard_stub.dart b/lib/metro/stubs/route_guard_stub.dart index 4d53114..3949b67 100644 --- a/lib/metro/stubs/route_guard_stub.dart +++ b/lib/metro/stubs/route_guard_stub.dart @@ -17,7 +17,7 @@ class ${rc.pascalCase}RouteGuard extends NyRouteGuard { @override Future canOpen(BuildContext? context, NyArgument? data) async { // implement your check - return true; // true/false + return true; } @override diff --git a/lib/nylo_framework.dart b/lib/nylo_framework.dart index d97a4b0..b1c24ee 100644 --- a/lib/nylo_framework.dart +++ b/lib/nylo_framework.dart @@ -42,4 +42,4 @@ export 'package:nylo_support/networking/ny_base_api_service.dart'; export 'package:dio/dio.dart'; /// Nylo version -const String nyloVersion = 'v5.16.0'; +const String nyloVersion = 'v5.17.0'; diff --git a/pubspec.lock b/pubspec.lock index b40457c..12be3a6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + cli_dialog: + dependency: "direct main" + description: + name: cli_dialog + sha256: "47530e1c4a6190662954cefce196d35143c4ed2675ea697d6e542790bdef5641" + url: "https://pub.dev" + source: hosted + version: "0.5.0" clock: dependency: transitive description: @@ -57,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + dart_console: + dependency: transitive + description: + name: dart_console + sha256: dfa4b63eb4382325ff975fdb6b7a0db8303bb5809ee5cb4516b44153844742ed + url: "https://pub.dev" + source: hosted + version: "1.2.0" dio: dependency: "direct main" description: @@ -233,10 +249,10 @@ packages: dependency: "direct main" description: name: nylo_support - sha256: "8860d620bc0de49b5f0fee22d7391a6ad240fe4bc5737f1a9287bcd5d82d3e71" + sha256: "3bb7b0c16cc477d4f85217d6dae881f17f671c5d45f62063f48b5634162610ba" url: "https://pub.dev" source: hosted - version: "5.23.0" + version: "5.24.0" page_transition: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 899c2c3..db7f69b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: nylo_framework description: Micro-framework for Flutter that's built to simplify app development for Flutter projects. -version: 5.16.0 +version: 5.17.0 homepage: https://nylo.dev repository: https://github.com/nylo-core/framework/tree/5.x issue_tracker: https://github.com/nylo-core/framework/issues @@ -16,9 +16,10 @@ environment: dependencies: flutter_dotenv: ^5.1.0 - nylo_support: ^5.23.0 + nylo_support: ^5.24.0 theme_provider: ^0.6.0 page_transition: ^2.1.0 + cli_dialog: ^0.5.0 collection: ^1.17.1 args: ^2.4.2 dio: ^5.4.0