├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── images ├── CreateViews.gif ├── CreateWidgets.gif ├── InitializeArchitectureMobile.gif ├── InitializeArchitectureResponsive.gif └── flutter.png ├── package-lock.json ├── package.json ├── src ├── dart_snippets │ ├── architecture │ │ ├── base.ts │ │ ├── base_model.ts │ │ ├── base_service.ts │ │ ├── locator.ts │ │ ├── logger.ts │ │ ├── main.ts │ │ └── router.ts │ ├── views │ │ ├── desktop.ts │ │ ├── mobile.ts │ │ ├── tablet.ts │ │ ├── view.ts │ │ └── view_model.ts │ └── widgets │ │ ├── desktop.ts │ │ ├── mobile.ts │ │ ├── tablet.ts │ │ ├── view_model.ts │ │ └── widget.ts ├── extension.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── extension.test.ts │ │ └── index.ts └── utils │ ├── architecture.ts │ ├── config_json.ts │ ├── file_system_manager.ts │ ├── router_constants.ts │ ├── router_json.ts │ ├── utils.ts │ ├── view_file.ts │ ├── vs_code_actions.ts │ ├── widget_file.ts │ └── yaml_helper.ts ├── tsconfig.json ├── tslint.json └── vsc-extension-quickstart.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.vscode-typescript-tslint-plugin" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "${defaultBuildTask}" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "${defaultBuildTask}" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "flutter-stacked-architecture-generator" extension will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.0.3] - 2020-10-07 9 | 10 | - Minor bug fix for dependencies 11 | 12 | ## [1.0.2] - 2020-10-07 13 | 14 | - Minor bug fix for dependencies 15 | 16 | ## [1.0.1] - 2020-10-07 17 | 18 | - Minor bug fix for dependencies 19 | 20 | ## [1.0.0] - 2020-10-05 21 | 22 | - Initial release 23 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at madhukesh@digimogo.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 madhukesh_048 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Flutter Extensions](https://img.shields.io/badge/Flutter-grey?style=flat-square&logo=flutter&logoColor=blue)](https://flutter.dev) 2 | [![GitHub](https://img.shields.io/github/license/digiMoGo/Flutter-Stacked-VSCode-Extension)](https://raw.githubusercontent.com/digiMoGo/Flutter-Stacked-VSCode-Extension/master/LICENSE) 3 | 4 | 5 | # Flutter Stacked Architecture Generator 6 | 7 | VS Code Extension to work with [Stacked Extension](https://pub.dev/packages/stacked) developed by [FilledStacks](https://www.filledstacks.com) 8 | 9 | ## Extension Settings 10 | 11 | 1. Initialize Architecture - ```stackedExtension.initializeArchitecture``` 12 | 2. Create Views - ```stackedExtension.createViews``` 13 | 3. Create Widgets - ```stackedExtension.createWidget``` 14 | 4. Regenerate Routes - ```stackedExtension.regenerateRoutes``` 15 | 16 | ### Initialise Architecture 17 | 18 | - Running this command creates the boilerplate 19 | - Can create two kinds 20 | - Responsive Views 21 | - Mobile Views 22 | 23 | 1. Mobile Views Directory Tree 24 | ``` 25 | . 26 | ├── README.md 27 | ├── android 28 | ├── ios 29 | ├── lib 30 | │   ├── core 31 | │   │   ├── base 32 | │   │   │   ├── base_model.dart 33 | │   │   │   └── base_service.dart 34 | │   │   ├── locator.dart 35 | │   │   ├── logger.dart 36 | │   │   ├── models 37 | │   │   ├── router.dart 38 | │   │   ├── router.json 39 | │   │   ├── router_constants.dart 40 | │   │   └── services 41 | │   ├── main.dart 42 | │   ├── theme 43 | │   ├── views 44 | │   │   └── splash 45 | │   │   ├── splash_view.dart 46 | │   │   └── splash_view_model.dart 47 | │   └── widgets 48 | │   ├── dumb_widgets 49 | │   └── smart_widgets 50 | ├── myapp.iml 51 | ├── pubspec.lock 52 | ├── pubspec.yaml 53 | ├── stackedConfig.json 54 | ├── test 55 | │   └── widget_test.dart 56 | └── web 57 | ``` 58 | 59 | ![Mobile](images/InitializeArchitectureMobile.gif) 60 | 61 | 2. Responsive Views Directory Tree 62 | ``` 63 | . 64 | ├── README.md 65 | ├── android 66 | ├── ios 67 | ├── lib 68 | │   ├── core 69 | │   │   ├── base 70 | │   │   │   ├── base_model.dart 71 | │   │   │   └── base_service.dart 72 | │   │   ├── locator.dart 73 | │   │   ├── logger.dart 74 | │   │   ├── models 75 | │   │   ├── router.dart 76 | │   │   ├── router.json 77 | │   │   ├── router_constants.dart 78 | │   │   └── services 79 | │   ├── main.dart 80 | │   ├── theme 81 | │   ├── views 82 | │   │   └── splash 83 | │   │   ├── splash_desktop.dart 84 | │   │   ├── splash_mobile.dart 85 | │   │   ├── splash_tablet.dart 86 | │   │   ├── splash_view.dart 87 | │   │   └── splash_view_model.dart 88 | │   └── widgets 89 | │   ├── dumb_widgets 90 | │   └── smart_widgets 91 | ├── myapp.iml 92 | ├── pubspec.lock 93 | ├── pubspec.yaml 94 | ├── stackedConfig.json 95 | ├── test 96 | │   └── widget_test.dart 97 | └── web 98 | ``` 99 | 100 | ![Responsive](images/InitializeArchitectureResponsive.gif) 101 | 102 | The command adds the following packages to the pubsec.yml file 103 | 104 | ```yaml 105 | get_it: ^4.0.4 106 | logger: ^0.9.2 107 | stacked: ^1.7.6 108 | stacked_services: ^0.5.4+2 109 | responsive_builder: ^0.2.0+2 110 | equatable: ^1.2.4 111 | ``` 112 | 113 | ### Create Views 114 | 115 | - Running this command creates the views based on the architecture selected in Initialise Architecture Command (i.e. Responsive or Mobile views) 116 | - The views are created in the views folder based on the path provided 117 | - The path may include subfolders and this folders will be created if they do not exist 118 | - The view will be added in router 119 | 120 | ![Views](images/CreateViews.gif) 121 | 122 | ### Create Widgets 123 | 124 | - Running this command will generate widgets in ```lib/widgets``` folder 125 | - Widgets can be of two types 126 | - Dumb Widgets: Widgets without a view model 127 | - Smart Widgets: Widgets with a view model 128 | ![Widgets](images/CreateWidgets.gif) 129 | 130 | ### Upcoming Features 131 | 1. Generate Services 132 | 2. Generate Models 133 | 134 | ### Contributors 135 | 136 | 1. [Ajil Oomen](https://github.com/ajilo297) 137 | 2. [Madhukesh D](https://github.com/madhukesh048) 138 | 3. [Jugal D Wadhwa](https://github.com/jugalw13) -------------------------------------------------------------------------------- /images/CreateViews.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiMoGo/Flutter-Stacked-VSCode-Extension/65eb96f15ca53e7cb2d8e1568164d4cc03a3296c/images/CreateViews.gif -------------------------------------------------------------------------------- /images/CreateWidgets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiMoGo/Flutter-Stacked-VSCode-Extension/65eb96f15ca53e7cb2d8e1568164d4cc03a3296c/images/CreateWidgets.gif -------------------------------------------------------------------------------- /images/InitializeArchitectureMobile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiMoGo/Flutter-Stacked-VSCode-Extension/65eb96f15ca53e7cb2d8e1568164d4cc03a3296c/images/InitializeArchitectureMobile.gif -------------------------------------------------------------------------------- /images/InitializeArchitectureResponsive.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiMoGo/Flutter-Stacked-VSCode-Extension/65eb96f15ca53e7cb2d8e1568164d4cc03a3296c/images/InitializeArchitectureResponsive.gif -------------------------------------------------------------------------------- /images/flutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiMoGo/Flutter-Stacked-VSCode-Extension/65eb96f15ca53e7cb2d8e1568164d4cc03a3296c/images/flutter.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter-stacked-architecture-generator", 3 | "version": "1.0.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | } 32 | }, 33 | "@types/glob": { 34 | "version": "7.1.3", 35 | "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", 36 | "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", 37 | "dev": true, 38 | "requires": { 39 | "@types/minimatch": "*", 40 | "@types/node": "*" 41 | } 42 | }, 43 | "@types/js-yaml": { 44 | "version": "3.12.5", 45 | "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz", 46 | "integrity": "sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww==", 47 | "dev": true 48 | }, 49 | "@types/lodash": { 50 | "version": "4.14.161", 51 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.161.tgz", 52 | "integrity": "sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==", 53 | "dev": true 54 | }, 55 | "@types/minimatch": { 56 | "version": "3.0.3", 57 | "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", 58 | "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", 59 | "dev": true 60 | }, 61 | "@types/mocha": { 62 | "version": "5.2.7", 63 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", 64 | "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", 65 | "dev": true 66 | }, 67 | "@types/node": { 68 | "version": "12.12.54", 69 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.54.tgz", 70 | "integrity": "sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w==", 71 | "dev": true 72 | }, 73 | "@types/shelljs": { 74 | "version": "0.8.8", 75 | "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.8.tgz", 76 | "integrity": "sha512-lD3LWdg6j8r0VRBFahJVaxoW0SIcswxKaFUrmKl33RJVeeoNYQAz4uqCJ5Z6v4oIBOsC5GozX+I5SorIKiTcQA==", 77 | "dev": true, 78 | "requires": { 79 | "@types/glob": "*", 80 | "@types/node": "*" 81 | } 82 | }, 83 | "@types/vscode": { 84 | "version": "1.48.0", 85 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.48.0.tgz", 86 | "integrity": "sha512-sZJKzsJz1gSoFXcOJWw3fnKl2sseUgZmvB4AJZS+Fea+bC/jfGPVhmFL/FfQHld/TKtukVONsmoD3Pkyx9iadg==", 87 | "dev": true 88 | }, 89 | "agent-base": { 90 | "version": "4.3.0", 91 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", 92 | "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", 93 | "dev": true, 94 | "requires": { 95 | "es6-promisify": "^5.0.0" 96 | } 97 | }, 98 | "ansi-colors": { 99 | "version": "3.2.3", 100 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", 101 | "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", 102 | "dev": true 103 | }, 104 | "ansi-regex": { 105 | "version": "3.0.0", 106 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 107 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 108 | "dev": true 109 | }, 110 | "ansi-styles": { 111 | "version": "3.2.1", 112 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 113 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 114 | "dev": true, 115 | "requires": { 116 | "color-convert": "^1.9.0" 117 | } 118 | }, 119 | "argparse": { 120 | "version": "1.0.10", 121 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 122 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 123 | "requires": { 124 | "sprintf-js": "~1.0.2" 125 | } 126 | }, 127 | "balanced-match": { 128 | "version": "1.0.0", 129 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 130 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 131 | }, 132 | "brace-expansion": { 133 | "version": "1.1.11", 134 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 135 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 136 | "requires": { 137 | "balanced-match": "^1.0.0", 138 | "concat-map": "0.0.1" 139 | } 140 | }, 141 | "browser-stdout": { 142 | "version": "1.3.1", 143 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 144 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 145 | "dev": true 146 | }, 147 | "builtin-modules": { 148 | "version": "1.1.1", 149 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 150 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 151 | "dev": true 152 | }, 153 | "camelcase": { 154 | "version": "5.3.1", 155 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 156 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 157 | "dev": true 158 | }, 159 | "chalk": { 160 | "version": "2.4.2", 161 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 162 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 163 | "dev": true, 164 | "requires": { 165 | "ansi-styles": "^3.2.1", 166 | "escape-string-regexp": "^1.0.5", 167 | "supports-color": "^5.3.0" 168 | }, 169 | "dependencies": { 170 | "supports-color": { 171 | "version": "5.5.0", 172 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 173 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 174 | "dev": true, 175 | "requires": { 176 | "has-flag": "^3.0.0" 177 | } 178 | } 179 | } 180 | }, 181 | "cliui": { 182 | "version": "5.0.0", 183 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 184 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 185 | "dev": true, 186 | "requires": { 187 | "string-width": "^3.1.0", 188 | "strip-ansi": "^5.2.0", 189 | "wrap-ansi": "^5.1.0" 190 | }, 191 | "dependencies": { 192 | "ansi-regex": { 193 | "version": "4.1.0", 194 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 195 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 196 | "dev": true 197 | }, 198 | "string-width": { 199 | "version": "3.1.0", 200 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 201 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 202 | "dev": true, 203 | "requires": { 204 | "emoji-regex": "^7.0.1", 205 | "is-fullwidth-code-point": "^2.0.0", 206 | "strip-ansi": "^5.1.0" 207 | } 208 | }, 209 | "strip-ansi": { 210 | "version": "5.2.0", 211 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 212 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 213 | "dev": true, 214 | "requires": { 215 | "ansi-regex": "^4.1.0" 216 | } 217 | } 218 | } 219 | }, 220 | "color-convert": { 221 | "version": "1.9.3", 222 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 223 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 224 | "dev": true, 225 | "requires": { 226 | "color-name": "1.1.3" 227 | } 228 | }, 229 | "color-name": { 230 | "version": "1.1.3", 231 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 232 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 233 | "dev": true 234 | }, 235 | "commander": { 236 | "version": "2.20.3", 237 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 238 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 239 | "dev": true 240 | }, 241 | "concat-map": { 242 | "version": "0.0.1", 243 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 244 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 245 | }, 246 | "debug": { 247 | "version": "3.2.6", 248 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 249 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 250 | "dev": true, 251 | "requires": { 252 | "ms": "^2.1.1" 253 | } 254 | }, 255 | "decamelize": { 256 | "version": "1.2.0", 257 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 258 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 259 | "dev": true 260 | }, 261 | "define-properties": { 262 | "version": "1.1.3", 263 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 264 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 265 | "dev": true, 266 | "requires": { 267 | "object-keys": "^1.0.12" 268 | } 269 | }, 270 | "diff": { 271 | "version": "3.5.0", 272 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 273 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 274 | "dev": true 275 | }, 276 | "emoji-regex": { 277 | "version": "7.0.3", 278 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 279 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 280 | "dev": true 281 | }, 282 | "es-abstract": { 283 | "version": "1.17.6", 284 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", 285 | "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", 286 | "dev": true, 287 | "requires": { 288 | "es-to-primitive": "^1.2.1", 289 | "function-bind": "^1.1.1", 290 | "has": "^1.0.3", 291 | "has-symbols": "^1.0.1", 292 | "is-callable": "^1.2.0", 293 | "is-regex": "^1.1.0", 294 | "object-inspect": "^1.7.0", 295 | "object-keys": "^1.1.1", 296 | "object.assign": "^4.1.0", 297 | "string.prototype.trimend": "^1.0.1", 298 | "string.prototype.trimstart": "^1.0.1" 299 | } 300 | }, 301 | "es-to-primitive": { 302 | "version": "1.2.1", 303 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 304 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 305 | "dev": true, 306 | "requires": { 307 | "is-callable": "^1.1.4", 308 | "is-date-object": "^1.0.1", 309 | "is-symbol": "^1.0.2" 310 | } 311 | }, 312 | "es6-promise": { 313 | "version": "4.2.8", 314 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 315 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", 316 | "dev": true 317 | }, 318 | "es6-promisify": { 319 | "version": "5.0.0", 320 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 321 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 322 | "dev": true, 323 | "requires": { 324 | "es6-promise": "^4.0.3" 325 | } 326 | }, 327 | "escape-string-regexp": { 328 | "version": "1.0.5", 329 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 330 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 331 | "dev": true 332 | }, 333 | "esprima": { 334 | "version": "4.0.1", 335 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 336 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 337 | }, 338 | "find-up": { 339 | "version": "3.0.0", 340 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 341 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 342 | "dev": true, 343 | "requires": { 344 | "locate-path": "^3.0.0" 345 | } 346 | }, 347 | "flat": { 348 | "version": "4.1.0", 349 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", 350 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", 351 | "dev": true, 352 | "requires": { 353 | "is-buffer": "~2.0.3" 354 | } 355 | }, 356 | "fs.realpath": { 357 | "version": "1.0.0", 358 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 359 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 360 | }, 361 | "function-bind": { 362 | "version": "1.1.1", 363 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 364 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 365 | "dev": true 366 | }, 367 | "get-caller-file": { 368 | "version": "2.0.5", 369 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 370 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 371 | "dev": true 372 | }, 373 | "glob": { 374 | "version": "7.1.6", 375 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 376 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 377 | "requires": { 378 | "fs.realpath": "^1.0.0", 379 | "inflight": "^1.0.4", 380 | "inherits": "2", 381 | "minimatch": "^3.0.4", 382 | "once": "^1.3.0", 383 | "path-is-absolute": "^1.0.0" 384 | } 385 | }, 386 | "growl": { 387 | "version": "1.10.5", 388 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 389 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 390 | "dev": true 391 | }, 392 | "has": { 393 | "version": "1.0.3", 394 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 395 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 396 | "dev": true, 397 | "requires": { 398 | "function-bind": "^1.1.1" 399 | } 400 | }, 401 | "has-flag": { 402 | "version": "3.0.0", 403 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 404 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 405 | "dev": true 406 | }, 407 | "has-symbols": { 408 | "version": "1.0.1", 409 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 410 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", 411 | "dev": true 412 | }, 413 | "he": { 414 | "version": "1.2.0", 415 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 416 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 417 | "dev": true 418 | }, 419 | "http-proxy-agent": { 420 | "version": "2.1.0", 421 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", 422 | "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", 423 | "dev": true, 424 | "requires": { 425 | "agent-base": "4", 426 | "debug": "3.1.0" 427 | }, 428 | "dependencies": { 429 | "debug": { 430 | "version": "3.1.0", 431 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 432 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 433 | "dev": true, 434 | "requires": { 435 | "ms": "2.0.0" 436 | } 437 | }, 438 | "ms": { 439 | "version": "2.0.0", 440 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 441 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 442 | "dev": true 443 | } 444 | } 445 | }, 446 | "https-proxy-agent": { 447 | "version": "2.2.4", 448 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", 449 | "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", 450 | "dev": true, 451 | "requires": { 452 | "agent-base": "^4.3.0", 453 | "debug": "^3.1.0" 454 | } 455 | }, 456 | "inflight": { 457 | "version": "1.0.6", 458 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 459 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 460 | "requires": { 461 | "once": "^1.3.0", 462 | "wrappy": "1" 463 | } 464 | }, 465 | "inherits": { 466 | "version": "2.0.4", 467 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 468 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 469 | }, 470 | "interpret": { 471 | "version": "1.4.0", 472 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", 473 | "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" 474 | }, 475 | "is-buffer": { 476 | "version": "2.0.4", 477 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", 478 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", 479 | "dev": true 480 | }, 481 | "is-callable": { 482 | "version": "1.2.0", 483 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", 484 | "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", 485 | "dev": true 486 | }, 487 | "is-date-object": { 488 | "version": "1.0.2", 489 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", 490 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", 491 | "dev": true 492 | }, 493 | "is-fullwidth-code-point": { 494 | "version": "2.0.0", 495 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 496 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 497 | "dev": true 498 | }, 499 | "is-regex": { 500 | "version": "1.1.1", 501 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", 502 | "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", 503 | "dev": true, 504 | "requires": { 505 | "has-symbols": "^1.0.1" 506 | } 507 | }, 508 | "is-symbol": { 509 | "version": "1.0.3", 510 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", 511 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", 512 | "dev": true, 513 | "requires": { 514 | "has-symbols": "^1.0.1" 515 | } 516 | }, 517 | "isexe": { 518 | "version": "2.0.0", 519 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 520 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 521 | "dev": true 522 | }, 523 | "js-tokens": { 524 | "version": "4.0.0", 525 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 526 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 527 | "dev": true 528 | }, 529 | "js-yaml": { 530 | "version": "3.14.0", 531 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 532 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 533 | "requires": { 534 | "argparse": "^1.0.7", 535 | "esprima": "^4.0.0" 536 | } 537 | }, 538 | "locate-path": { 539 | "version": "3.0.0", 540 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 541 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 542 | "dev": true, 543 | "requires": { 544 | "p-locate": "^3.0.0", 545 | "path-exists": "^3.0.0" 546 | } 547 | }, 548 | "lodash": { 549 | "version": "4.17.20", 550 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 551 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 552 | }, 553 | "log-symbols": { 554 | "version": "2.2.0", 555 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 556 | "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 557 | "dev": true, 558 | "requires": { 559 | "chalk": "^2.0.1" 560 | } 561 | }, 562 | "minimatch": { 563 | "version": "3.0.4", 564 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 565 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 566 | "requires": { 567 | "brace-expansion": "^1.1.7" 568 | } 569 | }, 570 | "minimist": { 571 | "version": "1.2.5", 572 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 573 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 574 | "dev": true 575 | }, 576 | "mkdirp": { 577 | "version": "0.5.4", 578 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", 579 | "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", 580 | "dev": true, 581 | "requires": { 582 | "minimist": "^1.2.5" 583 | } 584 | }, 585 | "mocha": { 586 | "version": "6.2.3", 587 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", 588 | "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", 589 | "dev": true, 590 | "requires": { 591 | "ansi-colors": "3.2.3", 592 | "browser-stdout": "1.3.1", 593 | "debug": "3.2.6", 594 | "diff": "3.5.0", 595 | "escape-string-regexp": "1.0.5", 596 | "find-up": "3.0.0", 597 | "glob": "7.1.3", 598 | "growl": "1.10.5", 599 | "he": "1.2.0", 600 | "js-yaml": "3.13.1", 601 | "log-symbols": "2.2.0", 602 | "minimatch": "3.0.4", 603 | "mkdirp": "0.5.4", 604 | "ms": "2.1.1", 605 | "node-environment-flags": "1.0.5", 606 | "object.assign": "4.1.0", 607 | "strip-json-comments": "2.0.1", 608 | "supports-color": "6.0.0", 609 | "which": "1.3.1", 610 | "wide-align": "1.1.3", 611 | "yargs": "13.3.2", 612 | "yargs-parser": "13.1.2", 613 | "yargs-unparser": "1.6.0" 614 | }, 615 | "dependencies": { 616 | "glob": { 617 | "version": "7.1.3", 618 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 619 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 620 | "dev": true, 621 | "requires": { 622 | "fs.realpath": "^1.0.0", 623 | "inflight": "^1.0.4", 624 | "inherits": "2", 625 | "minimatch": "^3.0.4", 626 | "once": "^1.3.0", 627 | "path-is-absolute": "^1.0.0" 628 | } 629 | }, 630 | "js-yaml": { 631 | "version": "3.13.1", 632 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 633 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 634 | "dev": true, 635 | "requires": { 636 | "argparse": "^1.0.7", 637 | "esprima": "^4.0.0" 638 | } 639 | } 640 | } 641 | }, 642 | "ms": { 643 | "version": "2.1.1", 644 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 645 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 646 | "dev": true 647 | }, 648 | "node-environment-flags": { 649 | "version": "1.0.5", 650 | "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", 651 | "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", 652 | "dev": true, 653 | "requires": { 654 | "object.getownpropertydescriptors": "^2.0.3", 655 | "semver": "^5.7.0" 656 | } 657 | }, 658 | "object-inspect": { 659 | "version": "1.8.0", 660 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", 661 | "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", 662 | "dev": true 663 | }, 664 | "object-keys": { 665 | "version": "1.1.1", 666 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 667 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 668 | "dev": true 669 | }, 670 | "object.assign": { 671 | "version": "4.1.0", 672 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 673 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 674 | "dev": true, 675 | "requires": { 676 | "define-properties": "^1.1.2", 677 | "function-bind": "^1.1.1", 678 | "has-symbols": "^1.0.0", 679 | "object-keys": "^1.0.11" 680 | } 681 | }, 682 | "object.getownpropertydescriptors": { 683 | "version": "2.1.0", 684 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", 685 | "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", 686 | "dev": true, 687 | "requires": { 688 | "define-properties": "^1.1.3", 689 | "es-abstract": "^1.17.0-next.1" 690 | } 691 | }, 692 | "once": { 693 | "version": "1.4.0", 694 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 695 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 696 | "requires": { 697 | "wrappy": "1" 698 | } 699 | }, 700 | "p-limit": { 701 | "version": "2.3.0", 702 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 703 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 704 | "dev": true, 705 | "requires": { 706 | "p-try": "^2.0.0" 707 | } 708 | }, 709 | "p-locate": { 710 | "version": "3.0.0", 711 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 712 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 713 | "dev": true, 714 | "requires": { 715 | "p-limit": "^2.0.0" 716 | } 717 | }, 718 | "p-try": { 719 | "version": "2.2.0", 720 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 721 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 722 | "dev": true 723 | }, 724 | "path-exists": { 725 | "version": "3.0.0", 726 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 727 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 728 | "dev": true 729 | }, 730 | "path-is-absolute": { 731 | "version": "1.0.1", 732 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 733 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 734 | }, 735 | "path-parse": { 736 | "version": "1.0.6", 737 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 738 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 739 | }, 740 | "rechoir": { 741 | "version": "0.6.2", 742 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 743 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", 744 | "requires": { 745 | "resolve": "^1.1.6" 746 | } 747 | }, 748 | "require-directory": { 749 | "version": "2.1.1", 750 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 751 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 752 | "dev": true 753 | }, 754 | "require-main-filename": { 755 | "version": "2.0.0", 756 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 757 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 758 | "dev": true 759 | }, 760 | "resolve": { 761 | "version": "1.17.0", 762 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 763 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 764 | "requires": { 765 | "path-parse": "^1.0.6" 766 | } 767 | }, 768 | "rimraf": { 769 | "version": "2.7.1", 770 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 771 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 772 | "dev": true, 773 | "requires": { 774 | "glob": "^7.1.3" 775 | } 776 | }, 777 | "semver": { 778 | "version": "5.7.1", 779 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 780 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 781 | "dev": true 782 | }, 783 | "set-blocking": { 784 | "version": "2.0.0", 785 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 786 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 787 | "dev": true 788 | }, 789 | "shelljs": { 790 | "version": "0.8.4", 791 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", 792 | "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", 793 | "requires": { 794 | "glob": "^7.0.0", 795 | "interpret": "^1.0.0", 796 | "rechoir": "^0.6.2" 797 | } 798 | }, 799 | "sprintf-js": { 800 | "version": "1.0.3", 801 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 802 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 803 | }, 804 | "string-width": { 805 | "version": "2.1.1", 806 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 807 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 808 | "dev": true, 809 | "requires": { 810 | "is-fullwidth-code-point": "^2.0.0", 811 | "strip-ansi": "^4.0.0" 812 | } 813 | }, 814 | "string.prototype.trimend": { 815 | "version": "1.0.1", 816 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", 817 | "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", 818 | "dev": true, 819 | "requires": { 820 | "define-properties": "^1.1.3", 821 | "es-abstract": "^1.17.5" 822 | } 823 | }, 824 | "string.prototype.trimstart": { 825 | "version": "1.0.1", 826 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", 827 | "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", 828 | "dev": true, 829 | "requires": { 830 | "define-properties": "^1.1.3", 831 | "es-abstract": "^1.17.5" 832 | } 833 | }, 834 | "strip-ansi": { 835 | "version": "4.0.0", 836 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 837 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 838 | "dev": true, 839 | "requires": { 840 | "ansi-regex": "^3.0.0" 841 | } 842 | }, 843 | "strip-json-comments": { 844 | "version": "2.0.1", 845 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 846 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 847 | "dev": true 848 | }, 849 | "supports-color": { 850 | "version": "6.0.0", 851 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", 852 | "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", 853 | "dev": true, 854 | "requires": { 855 | "has-flag": "^3.0.0" 856 | } 857 | }, 858 | "tslib": { 859 | "version": "1.13.0", 860 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 861 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", 862 | "dev": true 863 | }, 864 | "tslint": { 865 | "version": "5.20.1", 866 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", 867 | "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", 868 | "dev": true, 869 | "requires": { 870 | "@babel/code-frame": "^7.0.0", 871 | "builtin-modules": "^1.1.1", 872 | "chalk": "^2.3.0", 873 | "commander": "^2.12.1", 874 | "diff": "^4.0.1", 875 | "glob": "^7.1.1", 876 | "js-yaml": "^3.13.1", 877 | "minimatch": "^3.0.4", 878 | "mkdirp": "^0.5.1", 879 | "resolve": "^1.3.2", 880 | "semver": "^5.3.0", 881 | "tslib": "^1.8.0", 882 | "tsutils": "^2.29.0" 883 | }, 884 | "dependencies": { 885 | "diff": { 886 | "version": "4.0.2", 887 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 888 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 889 | "dev": true 890 | } 891 | } 892 | }, 893 | "tsutils": { 894 | "version": "2.29.0", 895 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", 896 | "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", 897 | "dev": true, 898 | "requires": { 899 | "tslib": "^1.8.1" 900 | } 901 | }, 902 | "typescript": { 903 | "version": "3.9.7", 904 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", 905 | "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==" 906 | }, 907 | "vscode-test": { 908 | "version": "1.4.0", 909 | "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.4.0.tgz", 910 | "integrity": "sha512-Jt7HNGvSE0+++Tvtq5wc4hiXLIr2OjDShz/gbAfM/mahQpy4rKBnmOK33D+MR67ATWviQhl+vpmU3p/qwSH/Pg==", 911 | "dev": true, 912 | "requires": { 913 | "http-proxy-agent": "^2.1.0", 914 | "https-proxy-agent": "^2.2.4", 915 | "rimraf": "^2.6.3" 916 | } 917 | }, 918 | "which": { 919 | "version": "1.3.1", 920 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 921 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 922 | "dev": true, 923 | "requires": { 924 | "isexe": "^2.0.0" 925 | } 926 | }, 927 | "which-module": { 928 | "version": "2.0.0", 929 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 930 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 931 | "dev": true 932 | }, 933 | "wide-align": { 934 | "version": "1.1.3", 935 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 936 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 937 | "dev": true, 938 | "requires": { 939 | "string-width": "^1.0.2 || 2" 940 | } 941 | }, 942 | "wrap-ansi": { 943 | "version": "5.1.0", 944 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 945 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 946 | "dev": true, 947 | "requires": { 948 | "ansi-styles": "^3.2.0", 949 | "string-width": "^3.0.0", 950 | "strip-ansi": "^5.0.0" 951 | }, 952 | "dependencies": { 953 | "ansi-regex": { 954 | "version": "4.1.0", 955 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 956 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 957 | "dev": true 958 | }, 959 | "string-width": { 960 | "version": "3.1.0", 961 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 962 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 963 | "dev": true, 964 | "requires": { 965 | "emoji-regex": "^7.0.1", 966 | "is-fullwidth-code-point": "^2.0.0", 967 | "strip-ansi": "^5.1.0" 968 | } 969 | }, 970 | "strip-ansi": { 971 | "version": "5.2.0", 972 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 973 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 974 | "dev": true, 975 | "requires": { 976 | "ansi-regex": "^4.1.0" 977 | } 978 | } 979 | } 980 | }, 981 | "wrappy": { 982 | "version": "1.0.2", 983 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 984 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 985 | }, 986 | "y18n": { 987 | "version": "4.0.0", 988 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 989 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 990 | "dev": true 991 | }, 992 | "yargs": { 993 | "version": "13.3.2", 994 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 995 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 996 | "dev": true, 997 | "requires": { 998 | "cliui": "^5.0.0", 999 | "find-up": "^3.0.0", 1000 | "get-caller-file": "^2.0.1", 1001 | "require-directory": "^2.1.1", 1002 | "require-main-filename": "^2.0.0", 1003 | "set-blocking": "^2.0.0", 1004 | "string-width": "^3.0.0", 1005 | "which-module": "^2.0.0", 1006 | "y18n": "^4.0.0", 1007 | "yargs-parser": "^13.1.2" 1008 | }, 1009 | "dependencies": { 1010 | "ansi-regex": { 1011 | "version": "4.1.0", 1012 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1013 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1014 | "dev": true 1015 | }, 1016 | "string-width": { 1017 | "version": "3.1.0", 1018 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1019 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1020 | "dev": true, 1021 | "requires": { 1022 | "emoji-regex": "^7.0.1", 1023 | "is-fullwidth-code-point": "^2.0.0", 1024 | "strip-ansi": "^5.1.0" 1025 | } 1026 | }, 1027 | "strip-ansi": { 1028 | "version": "5.2.0", 1029 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1030 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1031 | "dev": true, 1032 | "requires": { 1033 | "ansi-regex": "^4.1.0" 1034 | } 1035 | } 1036 | } 1037 | }, 1038 | "yargs-parser": { 1039 | "version": "13.1.2", 1040 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 1041 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 1042 | "dev": true, 1043 | "requires": { 1044 | "camelcase": "^5.0.0", 1045 | "decamelize": "^1.2.0" 1046 | } 1047 | }, 1048 | "yargs-unparser": { 1049 | "version": "1.6.0", 1050 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", 1051 | "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", 1052 | "dev": true, 1053 | "requires": { 1054 | "flat": "^4.1.0", 1055 | "lodash": "^4.17.15", 1056 | "yargs": "^13.3.0" 1057 | } 1058 | } 1059 | } 1060 | } 1061 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter-stacked-architecture-generator", 3 | "displayName": "Flutter Stacked Architecture Generator", 4 | "description": "VsCode extension to generate boilerplate code when using FilledStacks Stacked Architecture", 5 | "version": "1.0.3", 6 | "engines": { 7 | "vscode": "^1.48.0" 8 | }, 9 | "categories": [ 10 | "Other" 11 | ], 12 | "keywords": [ 13 | "Flutter", 14 | "FilledStacks", 15 | "MVVM", 16 | "Dart", 17 | "Stacked" 18 | ], 19 | "publisher": "digiMoGo", 20 | "icon": "images/flutter.png", 21 | "author": { 22 | "email": "azure@digimogo.com", 23 | "name": "digiMoGo", 24 | "url": "https://github.com/digiMoGo" 25 | }, 26 | "license": "MIT", 27 | "contributors": [ 28 | { 29 | "name": "Ajil Oommen", 30 | "email": "ajilo297@gmail.com", 31 | "url": "https://github.com/ajilo297" 32 | }, 33 | { 34 | "name": "Madhukesh D", 35 | "email": "madhukesh04@gmail.com", 36 | "url": "https://github.com/madhukesh048" 37 | }, 38 | { 39 | "name": "Jugal Wadhwa", 40 | "email": "jugaldeepak@gmail.com", 41 | "url": "https://github.com/jugalw13" 42 | } 43 | ], 44 | "repository": { 45 | "url": "https://github.com/digiMoGo/Flutter-Stacked-VSCode-Extension" 46 | }, 47 | "activationEvents": [ 48 | "onCommand:stackedExtension.regenerateRoutes", 49 | "onCommand:stackedExtension.createViews", 50 | "onCommand:stackedExtension.initializeArchitecture", 51 | "onCommand:stackedExtension.createWidget" 52 | ], 53 | "main": "./out/extension.js", 54 | "contributes": { 55 | "commands": [ 56 | { 57 | "command": "stackedExtension.createViews", 58 | "title": "Create Views", 59 | "category": "Stacked Architecture" 60 | }, 61 | { 62 | "command": "stackedExtension.regenerateRoutes", 63 | "title": "Regenerate Routes", 64 | "category": "Stacked Architecture" 65 | }, 66 | { 67 | "command": "stackedExtension.initializeArchitecture", 68 | "title": "Initialize Architecture", 69 | "category": "Stacked Architecture" 70 | }, 71 | { 72 | "command": "stackedExtension.createWidget", 73 | "title": "Create Widgets", 74 | "category": "Stacked Architecture" 75 | } 76 | ] 77 | }, 78 | "scripts": { 79 | "vscode:prepublish": "npm run compile", 80 | "compile": "tsc -p ./", 81 | "watch": "tsc -watch -p ./", 82 | "pretest": "npm run compile", 83 | "test": "node ./out/test/runTest.js" 84 | }, 85 | "devDependencies": { 86 | "@types/glob": "^7.1.1", 87 | "@types/js-yaml": "^3.12.1", 88 | "@types/lodash": "^4.14.149", 89 | "@types/mocha": "^5.2.7", 90 | "@types/node": "^12.11.7", 91 | "@types/shelljs": "^0.8.6", 92 | "@types/vscode": "^1.48.0", 93 | "glob": "^7.1.5", 94 | "mocha": "^6.2.2", 95 | "tslint": "^5.20.1", 96 | "vscode-test": "^1.2.2" 97 | }, 98 | "dependencies": { 99 | "js-yaml": "^3.14.0", 100 | "lodash": "^4.17.20", 101 | "shelljs": "^0.8.4", 102 | "typescript": "^3.9.7" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/dart_snippets/architecture/base.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "lodash"; 2 | 3 | export abstract class Base { 4 | 5 | constructor(private fileName: string, private classSuffix?: string) { } 6 | 7 | get className(): string { 8 | return this.getClassName(this.fileName, this.classSuffix); 9 | } 10 | 11 | private getClassName(fileName: string, suffix?: string): string { 12 | let camelCaseString = _.camelCase(fileName); 13 | let className = this.convertStringToUpperCamelCase(camelCaseString); 14 | if (suffix === undefined) { return className; } 15 | return className += suffix; 16 | } 17 | 18 | private convertStringToUpperCamelCase(fileName: string): string { 19 | let camelCaseString = _.camelCase(fileName); 20 | return _.upperFirst(camelCaseString); 21 | } 22 | } -------------------------------------------------------------------------------- /src/dart_snippets/architecture/base_model.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from './base'; 3 | 4 | export class BaseModel extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix?: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `import 'package:equatable/equatable.dart'; 12 | 13 | abstract class BaseModel with EquatableMixin { 14 | Map toMap(); 15 | Map get userValues; 16 | Map get userKeys; 17 | 18 | @override 19 | String toString() => toMap().toString(); 20 | } 21 | `; 22 | } 23 | 24 | get dartString(): string { 25 | return this._dartString; 26 | } 27 | } -------------------------------------------------------------------------------- /src/dart_snippets/architecture/base_service.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from './base'; 3 | 4 | export class BaseService extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix?: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `import 'package:logger/logger.dart'; 12 | 13 | import '../logger.dart'; 14 | 15 | class BaseService { 16 | Logger log; 17 | BaseService({String title}) { 18 | log = getLogger(title ?? this.runtimeType.toString()); 19 | } 20 | } 21 | `; 22 | } 23 | 24 | get dartString(): string { 25 | return this._dartString; 26 | } 27 | } -------------------------------------------------------------------------------- /src/dart_snippets/architecture/locator.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from './base'; 3 | 4 | export class Locator extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix?: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `import 'package:get_it/get_it.dart'; 12 | import 'package:logger/logger.dart'; 13 | import 'package:stacked_services/stacked_services.dart'; 14 | 15 | import 'logger.dart'; 16 | 17 | final GetIt locator = GetIt.instance; 18 | 19 | class LocatorInjector { 20 | static Future setUpLocator() async { 21 | Logger log = getLogger('Locator Injector'); 22 | log.d('Registering Navigation Service'); 23 | locator.registerLazySingleton(() => NavigationService()); 24 | log.d('Registering Dialog Service'); 25 | locator.registerLazySingleton(() => DialogService()); 26 | log.d('Registering Snackbar Service'); 27 | locator.registerLazySingleton(() => SnackbarService()); 28 | } 29 | } 30 | `; 31 | } 32 | 33 | get dartString(): string { 34 | return this._dartString; 35 | } 36 | } -------------------------------------------------------------------------------- /src/dart_snippets/architecture/logger.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from './base'; 3 | 4 | export class Logger extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix?: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `import 'package:logger/logger.dart'; 12 | 13 | class SimpleLogPrinter extends LogPrinter { 14 | static int counter = 0; 15 | 16 | final String className; 17 | 18 | final Map levelColors = { 19 | Level.verbose: AnsiColor.fg(250), 20 | Level.debug: AnsiColor.fg(92), 21 | Level.info: AnsiColor.fg(12), 22 | Level.warning: AnsiColor.fg(214), 23 | Level.error: AnsiColor.fg(196), 24 | Level.wtf: AnsiColor.fg(199), 25 | }; 26 | 27 | SimpleLogPrinter(this.className); 28 | 29 | @override 30 | List log(LogEvent event) { 31 | String message = event.message; 32 | AnsiColor color = levelColors[event.level]; 33 | String className = this.className; 34 | SimpleLogPrinter.counter += 1; 35 | int sequenceNumber = SimpleLogPrinter.counter; 36 | 37 | return [color('$sequenceNumber. [$className]: $message')]; 38 | } 39 | } 40 | 41 | Logger getLogger(String className) { 42 | return Logger( 43 | printer: SimpleLogPrinter( 44 | className, 45 | ), 46 | ); 47 | } 48 | `; 49 | } 50 | 51 | get dartString(): string { 52 | return this._dartString; 53 | } 54 | } -------------------------------------------------------------------------------- /src/dart_snippets/architecture/main.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from './base'; 3 | 4 | export class Main extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, initialRouteName: string, suffix?: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `import 'package:flutter/material.dart'; 12 | import 'package:stacked_services/stacked_services.dart'; 13 | 14 | import 'core/locator.dart'; 15 | import 'core/router_constants.dart'; 16 | import 'core/router.dart' as router; 17 | 18 | void main() async { 19 | await LocatorInjector.setUpLocator(); 20 | runApp(MyApp()); 21 | } 22 | 23 | class MyApp extends StatelessWidget { 24 | @override 25 | Widget build(BuildContext context) { 26 | return MaterialApp( 27 | navigatorKey: locator().navigatorKey, 28 | onGenerateRoute: router.Router.generateRoute, 29 | initialRoute: ${initialRouteName}ViewRoute, 30 | ); 31 | } 32 | } 33 | `; 34 | } 35 | 36 | get dartString(): string { 37 | return this._dartString; 38 | } 39 | } -------------------------------------------------------------------------------- /src/dart_snippets/architecture/router.ts: -------------------------------------------------------------------------------- 1 | import _ = require("lodash"); 2 | import * as path from 'path'; 3 | import { Base } from "./base"; 4 | import { FileSystemManager } from "../../utils/file_system_manager"; 5 | import { IRouteObject } from "../../utils/router_json"; 6 | import { VsCodeActions } from "../../utils/vs_code_actions"; 7 | 8 | export class Router extends Base { 9 | private _dartString: string; 10 | private initialPath: string; 11 | 12 | constructor(fileName: string, private routes: Array, suffix?: string) { 13 | super(fileName, suffix); 14 | this.initialPath = suffix === undefined ? '../../' : `package:${suffix}`; 15 | this._dartString = '// [ This is an auto generated file ]\n\n'; 16 | this._dartString += `import 'package:flutter/material.dart'; 17 | import '${this.initialPath}/core/router_constants.dart';\n\n`; 18 | } 19 | 20 | public create() { 21 | 22 | this.routes.forEach((value, index) => { 23 | this._dartString += `import '${this.initialPath}/${value.file_path}' as view${index};\n`; 24 | }); 25 | 26 | this._dartString += `\nclass Router { 27 | static Route generateRoute(RouteSettings settings) { 28 | switch (settings.name) {\n`; 29 | 30 | this.routes.forEach((value, index) => { 31 | this._dartString += ` case ${this.lowerCamelCased(value.route_name)}:\n`; 32 | this._dartString += ` return MaterialPageRoute(builder: (_) => view${index}.${value.view_name}());\n`; 33 | }); 34 | 35 | this._dartString += ` default: 36 | return MaterialPageRoute( 37 | builder: (_) => Scaffold( 38 | body: Center( 39 | child: Text('No route defined for \${settings.name}'), 40 | ), 41 | ), 42 | ); 43 | } 44 | } 45 | }`; 46 | 47 | const root_path = VsCodeActions.rootPath; 48 | const json_path = path.join(root_path, 'lib', 'core'); 49 | FileSystemManager.createFile(json_path, 'router.dart', this._dartString); 50 | 51 | } 52 | 53 | private lowerCamelCased(value: string): string { 54 | return _.camelCase(value); 55 | } 56 | } -------------------------------------------------------------------------------- /src/dart_snippets/views/desktop.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from '../architecture/base'; 3 | 4 | export class Desktop extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string) { 9 | super(fileName, suffix); 10 | 11 | let classPrefixList: string[] = this.className.split('Desktop'); 12 | let classPrefix: string | undefined; 13 | if (!_.isEmpty(classPrefixList)) { classPrefix = _.first(classPrefixList); } 14 | 15 | this._dartString = `part of ${fileName}_view; 16 | 17 | class _${this.className} extends StatelessWidget { 18 | final ${classPrefix}ViewModel viewModel; 19 | 20 | _${this.className} (this.viewModel); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Scaffold( 25 | body: Center( 26 | child: Text('${this.className}'), 27 | ), 28 | ); 29 | } 30 | } 31 | `; 32 | } 33 | 34 | get dartString(): string { 35 | return this._dartString; 36 | } 37 | } -------------------------------------------------------------------------------- /src/dart_snippets/views/mobile.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from '../architecture/base'; 3 | 4 | export class Mobile extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string) { 9 | super(fileName, suffix); 10 | 11 | let classPrefixList: string[] = this.className.split('Mobile'); 12 | let classPrefix: string | undefined; 13 | if (!_.isEmpty(classPrefixList)) { classPrefix = _.first(classPrefixList); } 14 | 15 | this._dartString = `part of ${fileName}_view; 16 | 17 | class _${this.className} extends StatelessWidget { 18 | final ${classPrefix}ViewModel viewModel; 19 | 20 | _${this.className} (this.viewModel); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Scaffold( 25 | body: Center( 26 | child: Text('${this.className}'), 27 | ), 28 | ); 29 | } 30 | } 31 | `; 32 | } 33 | 34 | get dartString(): string { 35 | return this._dartString; 36 | } 37 | } -------------------------------------------------------------------------------- /src/dart_snippets/views/tablet.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from '../architecture/base'; 3 | 4 | export class Tablet extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string) { 9 | super(fileName, suffix); 10 | 11 | let classPrefixList: string[] = this.className.split('Tablet'); 12 | let classPrefix: string | undefined; 13 | if (!_.isEmpty(classPrefixList)) { classPrefix = _.first(classPrefixList); } 14 | 15 | this._dartString = `part of ${fileName}_view; 16 | 17 | class _${this.className} extends StatelessWidget { 18 | final ${classPrefix}ViewModel viewModel; 19 | 20 | _${this.className} (this.viewModel); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Scaffold( 25 | body: Center( 26 | child: Text('${this.className}'), 27 | ), 28 | ); 29 | } 30 | } 31 | `; 32 | } 33 | 34 | get dartString(): string { 35 | return this._dartString; 36 | } 37 | } -------------------------------------------------------------------------------- /src/dart_snippets/views/view.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from '../architecture/base'; 3 | import { TYPE_OF_ARCHITECTURE } from '../../utils/utils'; 4 | 5 | export class View extends Base { 6 | 7 | private _dartString: string; 8 | 9 | constructor(fileName: string, suffix: string, typeOfArchitecture: TYPE_OF_ARCHITECTURE) { 10 | super(fileName, suffix); 11 | 12 | let classPrefixList: string[] = this.className.split('View'); 13 | let classPrefix: string | undefined; 14 | if (!_.isEmpty(classPrefixList)) { classPrefix = _.first(classPrefixList); } 15 | 16 | switch (typeOfArchitecture) { 17 | case TYPE_OF_ARCHITECTURE.Responsive: this._dartString = this.responsiveDartString(fileName, classPrefix); 18 | break; 19 | case TYPE_OF_ARCHITECTURE.Mobile: this._dartString = this.mobileDartString(fileName, classPrefix); 20 | break; 21 | default: this._dartString = this.mobileDartString(fileName, classPrefix); 22 | break; 23 | } 24 | } 25 | 26 | get dartString(): string { 27 | return this._dartString; 28 | } 29 | 30 | private mobileDartString(fileName: string, classPrefix?: string): string { 31 | return `import 'package:flutter/material.dart'; 32 | import 'package:stacked/stacked.dart'; 33 | import '${fileName}_view_model.dart'; 34 | 35 | class ${classPrefix}View extends StatelessWidget { 36 | @override 37 | Widget build(BuildContext context) { 38 | return ViewModelBuilder<${classPrefix}ViewModel>.reactive( 39 | builder: (BuildContext context, ${classPrefix}ViewModel viewModel, Widget _) { 40 | return Scaffold( 41 | appBar: AppBar(), 42 | body: Center( 43 | child: Text('${classPrefix} View'), 44 | ), 45 | ); 46 | }, 47 | viewModelBuilder: () => ${classPrefix}ViewModel(), 48 | ); 49 | } 50 | } 51 | `; 52 | } 53 | 54 | private responsiveDartString(fileName: string, classPrefix?: string): string { 55 | return `library ${fileName}_view; 56 | 57 | import 'package:flutter/material.dart'; 58 | import 'package:stacked/stacked.dart'; 59 | import 'package:responsive_builder/responsive_builder.dart'; 60 | import '${fileName}_view_model.dart'; 61 | 62 | part '${fileName}_mobile.dart'; 63 | part '${fileName}_tablet.dart'; 64 | part '${fileName}_desktop.dart'; 65 | 66 | class ${this.className} extends StatelessWidget { 67 | @override 68 | Widget build(BuildContext context) { 69 | return ViewModelBuilder<${classPrefix}ViewModel>.reactive( 70 | viewModelBuilder: () => ${classPrefix}ViewModel(), 71 | onModelReady: (viewModel) { 72 | // Do something once your viewModel is initialized 73 | }, 74 | builder: (BuildContext context, ${classPrefix}ViewModel viewModel, Widget child) { 75 | return ScreenTypeLayout( 76 | mobile: _${classPrefix}Mobile(viewModel), 77 | desktop: _${classPrefix}Desktop(viewModel), 78 | tablet: _${classPrefix}Tablet(viewModel), 79 | ); 80 | } 81 | ); 82 | } 83 | } 84 | `; 85 | } 86 | } -------------------------------------------------------------------------------- /src/dart_snippets/views/view_model.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "lodash"; 2 | import { Base } from "../architecture/base"; 3 | import { TYPE_OF_VIEWMODEL } from "../../utils/utils"; 4 | 5 | export class ViewModel extends Base { 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string, private typeOfViewModel: TYPE_OF_VIEWMODEL, private projectName?: string) { 9 | 10 | super(fileName, suffix); 11 | let initialPath = this.projectName === undefined ? '../../' : `package:${this.projectName}/`; 12 | this._dartString = this.viewModelDartString(this.typeOfViewModel, initialPath); 13 | } 14 | 15 | get dartString(): string { 16 | return this._dartString; 17 | } 18 | 19 | private viewModelDartString(typeOfViewModel: TYPE_OF_VIEWMODEL, initialPath: string): string { 20 | switch (typeOfViewModel) { 21 | case TYPE_OF_VIEWMODEL.BaseViewModel: return this.baseViewModelDartString(initialPath); 22 | case TYPE_OF_VIEWMODEL.FutureViewModel: return this.futureViewModelDartString(initialPath); 23 | case TYPE_OF_VIEWMODEL.StreamViewModel: return this.streamViewModelDartString(initialPath); 24 | case TYPE_OF_VIEWMODEL.MultipleFutureViewModel: return this.multipleFutureViewModelDartString(initialPath); 25 | case TYPE_OF_VIEWMODEL.MultipleStreamViewModel: return this.multipleStreamViewModelDartString(initialPath); 26 | case TYPE_OF_VIEWMODEL.ReactiveViewModel: return this.reactiveViewModelDartString(initialPath); 27 | case TYPE_OF_VIEWMODEL.IndexTrackingViewModel: return this.indexTrackingViewModelDartString(initialPath); 28 | default: return this.baseViewModelDartString(initialPath); 29 | } 30 | } 31 | 32 | private baseViewModelDartString(initialPath: string): string { 33 | return `import 'package:logger/logger.dart'; 34 | import 'package:stacked/stacked.dart'; 35 | import '${initialPath}core/logger.dart'; 36 | 37 | class ${this.className} extends BaseViewModel { 38 | Logger log; 39 | 40 | ${this.className}() { 41 | this.log = getLogger(this.runtimeType.toString()); 42 | } 43 | } 44 | `; 45 | } 46 | 47 | private futureViewModelDartString(initialPath: string): string { 48 | return `import 'package:logger/logger.dart'; 49 | import 'package:stacked/stacked.dart'; 50 | import '${initialPath}core/logger.dart'; 51 | 52 | class ${this.className} extends FutureViewModel { 53 | Logger log; 54 | 55 | ${this.className}() { 56 | log = getLogger(this.runtimeType.toString()); 57 | } 58 | 59 | @override 60 | Future futureToRun() { 61 | // TODO: implement futureToRun 62 | throw UnimplementedError(); 63 | } 64 | } 65 | `; 66 | } 67 | 68 | private streamViewModelDartString(initialPath: string): string { 69 | return `import 'package:logger/logger.dart'; 70 | import 'package:stacked/stacked.dart'; 71 | import '${initialPath}core/logger.dart'; 72 | 73 | class ${this.className} extends StreamViewModel { 74 | Logger log; 75 | 76 | ${this.className}() { 77 | log = getLogger(this.runtimeType.toString()); 78 | } 79 | 80 | @override 81 | // TODO: implement stream 82 | Stream get stream => throw UnimplementedError(); 83 | } 84 | `; 85 | } 86 | 87 | private multipleFutureViewModelDartString(initialPath: string): string { 88 | return `import 'package:logger/logger.dart'; 89 | import 'package:stacked/stacked.dart'; 90 | import '${initialPath}core/logger.dart'; 91 | 92 | class ${this.className} extends MultipleFutureViewModel { 93 | Logger log; 94 | 95 | ${this.className}() { 96 | log = getLogger(this.runtimeType.toString()); 97 | } 98 | 99 | @override 100 | // TODO: implement futuresMap 101 | Map get futuresMap => throw UnimplementedError(); 102 | } 103 | `; 104 | } 105 | 106 | private multipleStreamViewModelDartString(initialPath: string): string { 107 | return `import 'package:logger/logger.dart'; 108 | import 'package:stacked/stacked.dart'; 109 | import '${initialPath}core/logger.dart'; 110 | 111 | class ${this.className} extends MultipleStreamViewModel { 112 | Logger log; 113 | 114 | ${this.className}() { 115 | log = getLogger(this.runtimeType.toString()); 116 | } 117 | 118 | @override 119 | // TODO: implement streamsMap 120 | Map get streamsMap => throw UnimplementedError(); 121 | } 122 | `; 123 | } 124 | 125 | private reactiveViewModelDartString(initialPath: string): string { 126 | return `import 'package:logger/logger.dart'; 127 | import 'package:stacked/stacked.dart'; 128 | import 'package:counter_stacked/core/logger.dart'; 129 | 130 | class HomeViewModel extends ReactiveViewModel { 131 | Logger log; 132 | 133 | HomeViewModel() { 134 | log = getLogger(this.runtimeType.toString()); 135 | } 136 | 137 | @override 138 | // TODO: implement reactiveServices 139 | List get reactiveServices => throw UnimplementedError(); 140 | } 141 | `; 142 | } 143 | 144 | private indexTrackingViewModelDartString(initialPath: string): string { 145 | return `import 'package:logger/logger.dart'; 146 | import 'package:stacked/stacked.dart'; 147 | import '${initialPath}core/logger.dart'; 148 | 149 | class ${this.className} extends IndexTrackingViewModel { 150 | Logger log; 151 | 152 | ${this.className}() { 153 | log = getLogger(this.runtimeType.toString()); 154 | } 155 | } 156 | `; 157 | } 158 | } -------------------------------------------------------------------------------- /src/dart_snippets/widgets/desktop.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import {Base} from '../architecture/base'; 3 | 4 | export class Desktop extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `part of ${fileName}_widget; 12 | class _${this.className} extends StatelessWidget { 13 | @override 14 | Widget build(BuildContext context) { 15 | return Center( 16 | child: Text('${fileName}_desktop'), 17 | ); 18 | } 19 | }`; 20 | } 21 | 22 | get dartString(): string { 23 | return this._dartString; 24 | } 25 | } -------------------------------------------------------------------------------- /src/dart_snippets/widgets/mobile.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from '../architecture/base'; 3 | 4 | export class Mobile extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `part of ${fileName}_widget; 12 | 13 | class _${this.className} extends StatelessWidget { 14 | @override 15 | Widget build(BuildContext context) { 16 | return Center( 17 | child: Text('${fileName}_mobile'), 18 | ); 19 | } 20 | }`; 21 | } 22 | 23 | get dartString(): string { 24 | return this._dartString; 25 | } 26 | } -------------------------------------------------------------------------------- /src/dart_snippets/widgets/tablet.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from '../architecture/base'; 3 | 4 | export class Tablet extends Base { 5 | 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string) { 9 | super(fileName, suffix); 10 | 11 | this._dartString = `part of ${fileName}_widget; 12 | 13 | class _${this.className} extends StatelessWidget { 14 | @override 15 | Widget build(BuildContext context) { 16 | return Center( 17 | child: Text('${fileName}_tablet'), 18 | ); 19 | } 20 | }`; 21 | } 22 | 23 | get dartString(): string { 24 | return this._dartString; 25 | } 26 | } -------------------------------------------------------------------------------- /src/dart_snippets/widgets/view_model.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "lodash"; 2 | import { Base } from "../architecture/base"; 3 | import { TYPE_OF_VIEWMODEL } from "../../utils/utils"; 4 | 5 | export class ViewModel extends Base { 6 | private _dartString: string; 7 | 8 | constructor(fileName: string, suffix: string, private typeOfViewModel: TYPE_OF_VIEWMODEL, private projectName?: string) { 9 | 10 | super(fileName, suffix); 11 | let initialPath = this.projectName === undefined ? '../../../' : `package:${this.projectName}/`; 12 | this._dartString = this.viewModelDartString(typeOfViewModel, initialPath); 13 | } 14 | 15 | get dartString(): string { 16 | return this._dartString; 17 | } 18 | 19 | private viewModelDartString(typeOfViewModel: TYPE_OF_VIEWMODEL, initialPath: string): string { 20 | switch (typeOfViewModel) { 21 | case TYPE_OF_VIEWMODEL.BaseViewModel: return this.baseViewModelDartString(initialPath); 22 | case TYPE_OF_VIEWMODEL.FutureViewModel: return this.futureViewModelDartString(initialPath); 23 | case TYPE_OF_VIEWMODEL.StreamViewModel: return this.streamViewModelDartString(initialPath); 24 | case TYPE_OF_VIEWMODEL.MultipleFutureViewModel: return this.multipleFutureViewModelDartString(initialPath); 25 | case TYPE_OF_VIEWMODEL.MultipleStreamViewModel: return this.multipleStreamViewModelDartString(initialPath); 26 | case TYPE_OF_VIEWMODEL.ReactiveViewModel: return this.reactiveViewModelDartString(initialPath); 27 | case TYPE_OF_VIEWMODEL.IndexTrackingViewModel: return this.indexTrackingViewModelDartString(initialPath); 28 | default: return this.baseViewModelDartString(initialPath); 29 | } 30 | } 31 | 32 | private baseViewModelDartString(initialPath: string): string { 33 | return `import 'package:logger/logger.dart'; 34 | import 'package:stacked/stacked.dart'; 35 | import '${initialPath}core/logger.dart'; 36 | 37 | class ${this.className} extends BaseViewModel { 38 | Logger log; 39 | 40 | ${this.className}() { 41 | this.log = getLogger(this.runtimeType.toString()); 42 | } 43 | } 44 | `; 45 | } 46 | 47 | private futureViewModelDartString(initialPath: string): string { 48 | return `import 'package:logger/logger.dart'; 49 | import 'package:stacked/stacked.dart'; 50 | import '${initialPath}core/logger.dart'; 51 | 52 | class ${this.className} extends FutureViewModel { 53 | Logger log; 54 | 55 | ${this.className}() { 56 | log = getLogger(this.runtimeType.toString()); 57 | } 58 | 59 | @override 60 | Future futureToRun() { 61 | // TODO: implement futureToRun 62 | throw UnimplementedError(); 63 | } 64 | } 65 | `; 66 | } 67 | 68 | private streamViewModelDartString(initialPath: string): string { 69 | return `import 'package:logger/logger.dart'; 70 | import 'package:stacked/stacked.dart'; 71 | import '${initialPath}core/logger.dart'; 72 | 73 | class ${this.className} extends StreamViewModel { 74 | Logger log; 75 | 76 | ${this.className}() { 77 | log = getLogger(this.runtimeType.toString()); 78 | } 79 | 80 | @override 81 | // TODO: implement stream 82 | Stream get stream => throw UnimplementedError(); 83 | } 84 | `; 85 | } 86 | 87 | private multipleFutureViewModelDartString(initialPath: string): string { 88 | return `import 'package:logger/logger.dart'; 89 | import 'package:stacked/stacked.dart'; 90 | import '${initialPath}core/logger.dart'; 91 | 92 | class ${this.className} extends MultipleFutureViewModel { 93 | Logger log; 94 | 95 | ${this.className}() { 96 | log = getLogger(this.runtimeType.toString()); 97 | } 98 | 99 | @override 100 | // TODO: implement futuresMap 101 | Map get futuresMap => throw UnimplementedError(); 102 | } 103 | `; 104 | } 105 | 106 | private multipleStreamViewModelDartString(initialPath: string): string { 107 | return `import 'package:logger/logger.dart'; 108 | import 'package:stacked/stacked.dart'; 109 | import '${initialPath}core/logger.dart'; 110 | 111 | class ${this.className} extends MultipleStreamViewModel { 112 | Logger log; 113 | 114 | ${this.className}() { 115 | log = getLogger(this.runtimeType.toString()); 116 | } 117 | 118 | @override 119 | // TODO: implement streamsMap 120 | Map get streamsMap => throw UnimplementedError(); 121 | } 122 | `; 123 | } 124 | 125 | private reactiveViewModelDartString(initialPath: string): string { 126 | return `import 'package:logger/logger.dart'; 127 | import 'package:stacked/stacked.dart'; 128 | import 'package:counter_stacked/core/logger.dart'; 129 | 130 | class HomeViewModel extends ReactiveViewModel { 131 | Logger log; 132 | 133 | HomeViewModel() { 134 | log = getLogger(this.runtimeType.toString()); 135 | } 136 | 137 | @override 138 | // TODO: implement reactiveServices 139 | List get reactiveServices => throw UnimplementedError(); 140 | } 141 | `; 142 | } 143 | 144 | private indexTrackingViewModelDartString(initialPath: string): string { 145 | return `import 'package:logger/logger.dart'; 146 | import 'package:stacked/stacked.dart'; 147 | import '${initialPath}core/logger.dart'; 148 | 149 | class ${this.className} extends IndexTrackingViewModel { 150 | Logger log; 151 | 152 | ${this.className}() { 153 | log = getLogger(this.runtimeType.toString()); 154 | } 155 | } 156 | `; 157 | } 158 | } -------------------------------------------------------------------------------- /src/dart_snippets/widgets/widget.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Base } from '../architecture/base'; 3 | import { TYPE_OF_ARCHITECTURE, TYPE_OF_WIDGET } from '../../utils/utils'; 4 | 5 | export class Widget extends Base { 6 | 7 | private _dartString: string; 8 | 9 | constructor(fileName: string, suffix: string, typeOfArchitecture: TYPE_OF_ARCHITECTURE, typeOfWidget: TYPE_OF_WIDGET) { 10 | super(fileName, suffix); 11 | 12 | let classPrefixList: string[] = this.className.split('Widget'); 13 | let classPrefix: string | undefined; 14 | if (!_.isEmpty(classPrefixList)) { classPrefix = _.first(classPrefixList); } 15 | 16 | if (typeOfArchitecture === TYPE_OF_ARCHITECTURE.Responsive && typeOfWidget === TYPE_OF_WIDGET.Smart) { 17 | this._dartString = this.responsiveAndSmartWidgetDartString(fileName, classPrefix); 18 | } else if (typeOfArchitecture === TYPE_OF_ARCHITECTURE.Responsive && typeOfWidget === TYPE_OF_WIDGET.Dumb) { 19 | this._dartString = this.responsiveAndDumbWidgetDartString(fileName, classPrefix); 20 | } else if (typeOfArchitecture === TYPE_OF_ARCHITECTURE.Mobile && typeOfWidget === TYPE_OF_WIDGET.Smart) { 21 | this._dartString = this.mobileAndSmartWidgetDartString(fileName, classPrefix); 22 | } else if (typeOfArchitecture === TYPE_OF_ARCHITECTURE.Mobile && typeOfWidget === TYPE_OF_WIDGET.Dumb) { 23 | this._dartString = this.mobileAndDumbWidgetDartString(); 24 | } else { 25 | this._dartString = this.mobileAndDumbWidgetDartString(); 26 | } 27 | } 28 | 29 | get dartString(): string { 30 | return this._dartString; 31 | } 32 | 33 | private mobileAndDumbWidgetDartString() { 34 | return `import 'package:flutter/material.dart'; 35 | 36 | class ${this.className} extends StatelessWidget { 37 | @override 38 | Widget build(BuildContext context) { 39 | return Center( 40 | child: Text('${this.className}'), 41 | ); 42 | } 43 | } 44 | `; 45 | } 46 | 47 | private mobileAndSmartWidgetDartString(fileName: string, classPrefix?: string): string { 48 | return `import 'package:flutter/material.dart'; 49 | import 'package:stacked/stacked.dart'; 50 | import '${fileName}_view_model.dart'; 51 | 52 | class ${this.className} extends StatelessWidget { 53 | @override 54 | Widget build(BuildContext context) { 55 | return ViewModelBuilder<${classPrefix}ViewModel>.reactive( 56 | builder: (BuildContext context, ${classPrefix}ViewModel viewModel, Widget _) { 57 | return Center( 58 | child: Text('${classPrefix} View'), 59 | ); 60 | }, 61 | viewModelBuilder: () => ${classPrefix}ViewModel(), 62 | ); 63 | } 64 | } 65 | `; 66 | } 67 | 68 | private responsiveAndDumbWidgetDartString(fileName: string, classPrefix?: string) { 69 | return `library ${fileName}_widget; 70 | 71 | import 'package:responsive_builder/responsive_builder.dart'; 72 | import 'package:flutter/material.dart'; 73 | 74 | part '${fileName}_mobile.dart'; 75 | part '${fileName}_tablet.dart'; 76 | part '${fileName}_desktop.dart'; 77 | 78 | class ${this.className} extends StatelessWidget { 79 | @override 80 | Widget build(BuildContext context) { 81 | return ScreenTypeLayout( 82 | mobile: _${classPrefix}Mobile(), 83 | desktop: _${classPrefix}Desktop(), 84 | tablet: _${classPrefix}Tablet(), 85 | ); 86 | } 87 | } 88 | `; 89 | } 90 | 91 | private responsiveAndSmartWidgetDartString(fileName: string, classPrefix?: string): string { 92 | return `library ${fileName}_widget; 93 | 94 | import 'package:responsive_builder/responsive_builder.dart'; 95 | import 'package:flutter/material.dart'; 96 | import 'package:stacked/stacked.dart'; 97 | import '${fileName}_view_model.dart'; 98 | 99 | part '${fileName}_mobile.dart'; 100 | part '${fileName}_tablet.dart'; 101 | part '${fileName}_desktop.dart'; 102 | 103 | class ${this.className} extends StatelessWidget { 104 | @override 105 | Widget build(BuildContext context) { 106 | return ViewModelBuilder<${classPrefix}ViewModel>.reactive( 107 | builder: (BuildContext context, ${classPrefix}ViewModel viewModel, Widget _) { 108 | return ScreenTypeLayout( 109 | mobile: _${classPrefix}Mobile(), 110 | desktop: _${classPrefix}Desktop(), 111 | tablet: _${classPrefix}Tablet(), 112 | ); 113 | }, 114 | viewModelBuilder: () => ${classPrefix}ViewModel(), 115 | ); 116 | } 117 | } 118 | `; 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | import * as vscode from 'vscode'; 3 | import * as path from "path"; 4 | import * as _ from "lodash"; 5 | import { FileSystemManager } from './utils/file_system_manager'; 6 | import { VsCodeActions } from './utils/vs_code_actions'; 7 | import { Utils, TYPE_OF_ARCHITECTURE, TYPE_OF_VIEWMODEL, TYPE_OF_WIDGET } from './utils/utils'; 8 | import { Architecture } from './utils/architecture'; 9 | import { ViewFile } from './utils/view_file'; 10 | import { WidgetFile } from './utils/widget_file'; 11 | import { RouterJSON } from './utils/router_json'; 12 | import { ConfigJSON } from './utils/config_json'; 13 | import { type } from 'os'; 14 | 15 | export function activate(context: vscode.ExtensionContext) { 16 | 17 | let regenerateDisposable = vscode.commands.registerCommand('stackedExtension.regenerateRoutes', async () => { 18 | if (!FileSystemManager.isFlutterProject()) { 19 | VsCodeActions.showErrorMessage('Missing pubspec'); 20 | return; 21 | } 22 | 23 | VsCodeActions.showInformationMessage('Regenerating router and router constants'); 24 | RouterJSON.generateFiles(); 25 | }); 26 | 27 | let initializeDisposable = vscode.commands.registerCommand('stackedExtension.initializeArchitecture', async () => { 28 | if (!FileSystemManager.isFlutterProject()) { 29 | VsCodeActions.showErrorMessage('Missing pubspec'); 30 | return; 31 | } 32 | 33 | let rootPath = VsCodeActions.rootPath; 34 | if (_.isUndefined(rootPath)) { return; } 35 | 36 | ConfigJSON.createJSONFile(); 37 | 38 | let typeOfArchitecture = await inputTypeOfArchitecture(); 39 | if (typeOfArchitecture === undefined) { 40 | return; 41 | } 42 | 43 | let inputString = await VsCodeActions.getInputString('Enter Class Name', async (value) => { 44 | if (value.length === 0) { 45 | return 'Enter a Class Name'; 46 | } 47 | 48 | if (value.toLowerCase() === 'view') { 49 | return 'View is not a Valid Class Name'; 50 | } 51 | 52 | return undefined; 53 | }); 54 | 55 | if (inputString.length === 0 || inputString.toLowerCase() === 'view') { 56 | console.warn("activate: inputString length is 0"); 57 | VsCodeActions.showErrorMessage("Invalid name for file"); 58 | return; 59 | } 60 | 61 | console.debug(`inputString: { ${inputString} }`); 62 | 63 | let nameArray = inputString.trim().split('/'); 64 | let folders: string[] = []; 65 | if (nameArray.length > 1) { 66 | let folderList = nameArray.splice(0, nameArray.length - 1).map(element => { return element; }); 67 | console.debug(`folderlist: { ${folderList} }`); 68 | folders = folderList; 69 | } 70 | 71 | let formattedInputString = _.last(nameArray); 72 | if (formattedInputString === undefined) { 73 | console.error('formattedInputString is undefined'); 74 | return; 75 | } 76 | let fileName = Utils.processFileName(formattedInputString); 77 | console.debug(`activate: fileName: ${fileName}`); 78 | 79 | let typeOfViewModel = await inputTypeOfViewModel(); 80 | if (typeOfViewModel === undefined) { 81 | VsCodeActions.showErrorMessage("Type of ViewModel not selected"); 82 | return; 83 | } 84 | 85 | new Architecture(path.join(rootPath, 'lib')).init(fileName); 86 | new ViewFile(rootPath, fileName, typeOfArchitecture, typeOfViewModel).createViews(); 87 | }); 88 | 89 | let viewDisposable = vscode.commands.registerCommand('stackedExtension.createViews', async () => { 90 | if (!FileSystemManager.isFlutterProject()) { 91 | VsCodeActions.showErrorMessage('Missing pubspec'); 92 | return; 93 | } 94 | 95 | let typeOfArchitecture = await inputTypeOfArchitecture(); 96 | if (typeOfArchitecture === undefined) { 97 | return; 98 | } 99 | 100 | let inputString = await VsCodeActions.getInputString('Enter Class Name', async (value) => { 101 | if (value.length === 0) { 102 | return 'Enter a Class Name'; 103 | } 104 | 105 | if (value.toLowerCase() === 'view') { 106 | return 'View is not a Valid Class Name'; 107 | } 108 | 109 | return undefined; 110 | }); 111 | 112 | if (inputString.length === 0 || inputString.toLowerCase() === 'view') { 113 | console.warn("activate: inputString length is 0"); 114 | VsCodeActions.showErrorMessage("Invalid name for file"); 115 | return; 116 | } 117 | 118 | console.debug(`inputString: { ${inputString} }`); 119 | 120 | let nameArray = inputString.trim().split('/'); 121 | let folders: string[] = []; 122 | if (nameArray.length > 1) { 123 | let folderList = nameArray.splice(0, nameArray.length - 1).map(element => { return element; }); 124 | console.debug(`folderlist: { ${folderList} }`); 125 | folders = folderList; 126 | } 127 | 128 | let formattedInputString = _.last(nameArray); 129 | if (formattedInputString === undefined) { 130 | console.error('formattedInputString is undefined'); 131 | return; 132 | } 133 | let fileName = Utils.processFileName(formattedInputString); 134 | console.debug(`activate: fileName: ${fileName}`); 135 | 136 | let typeOfViewModel = await inputTypeOfViewModel(); 137 | if (typeOfViewModel === undefined) { 138 | VsCodeActions.showErrorMessage("Type of ViewModel not selected"); 139 | return; 140 | } 141 | 142 | let rootPath = VsCodeActions.rootPath; 143 | if (rootPath === undefined) { return; } 144 | 145 | new ViewFile(rootPath, fileName, typeOfArchitecture, typeOfViewModel, folders).createViews(); 146 | }); 147 | 148 | let widgetDisposable = vscode.commands.registerCommand('stackedExtension.createWidget', async () => { 149 | if (!FileSystemManager.isFlutterProject()) { 150 | VsCodeActions.showErrorMessage('Missing pubspec'); 151 | return; 152 | } 153 | 154 | let typeOfArchitecture = await inputTypeOfArchitecture(); 155 | if (typeOfArchitecture === undefined) { 156 | return; 157 | } 158 | 159 | let typeOfWidget = await inputTypeOfWidget(); 160 | if (typeOfWidget === undefined) { 161 | VsCodeActions.showErrorMessage("Type of Widget not selected"); 162 | return; 163 | } 164 | 165 | let inputString = await VsCodeActions.getInputString('Enter class name', async (value) => { 166 | if (value.length === 0) { 167 | return 'Enter class name'; 168 | } 169 | if (value.toLowerCase() === 'widget') { 170 | return 'Widget is not a valid class name'; 171 | } 172 | return undefined; 173 | }); 174 | 175 | if (inputString.length === 0 || inputString.toLowerCase() === 'widget') { 176 | console.warn("activate: inputString length is 0"); 177 | VsCodeActions.showErrorMessage("Invalid name for file"); 178 | return; 179 | } 180 | 181 | let fileName = Utils.processFileName(inputString.trim()); 182 | console.debug(`activate: fileName: ${fileName}`); 183 | 184 | let typeOfViewModel; 185 | if (typeOfWidget === TYPE_OF_WIDGET.Smart) { 186 | typeOfViewModel = await inputTypeOfViewModel(); 187 | if (typeOfViewModel === undefined) { 188 | VsCodeActions.showErrorMessage("Type of ViewModel not selected"); 189 | return; 190 | } 191 | } 192 | 193 | let rootPath = VsCodeActions.rootPath; 194 | if (rootPath === undefined) { return; } 195 | 196 | new WidgetFile(rootPath, fileName, typeOfArchitecture, typeOfWidget, typeOfViewModel).createWidgets(); 197 | }); 198 | 199 | context.subscriptions.push(initializeDisposable); 200 | context.subscriptions.push(viewDisposable); 201 | context.subscriptions.push(widgetDisposable); 202 | context.subscriptions.push(regenerateDisposable); 203 | 204 | async function inputTypeOfArchitecture(): Promise { 205 | let typeOfArchitecturePreference = ConfigJSON.readTypeOfArchitecture(); 206 | 207 | if (typeOfArchitecturePreference === undefined) { 208 | let typeOfArch = await VsCodeActions.getInputDropdown(Utils.TYPE_OF_ARCHITECTURE); 209 | if (typeOfArch === undefined) { 210 | console.warn("undefined"); 211 | VsCodeActions.showErrorMessage('Type of Architecture not selected'); 212 | return; 213 | } 214 | ConfigJSON.updateTypeOfArchitecture(typeOfArch); 215 | typeOfArchitecturePreference = typeOfArch; 216 | } 217 | return Utils.convertResponsiveToEnum(typeOfArchitecturePreference); 218 | } 219 | 220 | async function inputTypeOfViewModel(): Promise { 221 | let viewModelType = await VsCodeActions.getInputDropdown(Utils.TYPE_OF_VIEWMODEL); 222 | return Utils.convertViewModelToEnum(viewModelType); 223 | } 224 | 225 | async function inputTypeOfWidget(): Promise { 226 | let widgetType = await VsCodeActions.getInputDropdown(Utils.TYPE_OF_WIDGET); 227 | return Utils.convertWidgetToEnum(widgetType); 228 | } 229 | } 230 | 231 | export function deactivate() { 232 | console.debug('Flutter Stacked Generator: Deactivated'); 233 | } 234 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.equal(-1, [1, 2, 3].indexOf(5)); 13 | assert.equal(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | }); 10 | mocha.useColors(true); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | e(err); 34 | } 35 | }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/utils/architecture.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as _ from "lodash"; 3 | 4 | import { FileSystemManager } from './file_system_manager'; 5 | import { WriteFileOptions } from 'fs'; 6 | import { Utils } from './utils'; 7 | import { YamlHelper } from './yaml_helper'; 8 | import { BaseModel } from '../dart_snippets/architecture/base_model'; 9 | import { BaseService } from '../dart_snippets/architecture/base_service'; 10 | import { Logger } from '../dart_snippets/architecture/logger'; 11 | import { Locator } from '../dart_snippets/architecture/locator'; 12 | import { Main } from '../dart_snippets/architecture/main'; 13 | 14 | export class Architecture { 15 | 16 | constructor(private rootPath: string) { } 17 | 18 | public init(fileName: string) { 19 | this.initCore(); 20 | this.initTheme(); 21 | this.initViews(); 22 | this.initWidgets(); 23 | 24 | YamlHelper.initializeWithDependencies(); 25 | let initialRoute = _.camelCase(fileName); 26 | this.createExistingFile(this.rootPath, 'main.dart', new Main('main.dart', initialRoute).dartString); 27 | } 28 | 29 | private initCore() { 30 | let corePath = path.join(this.rootPath, 'core'); 31 | this.initBase(corePath); 32 | this.initCoreFiles(corePath); 33 | this.initServices(corePath); 34 | this.initModels(corePath); 35 | } 36 | 37 | private initBase(corePath: string) { 38 | let basePath = path.join(corePath, 'base'); 39 | 40 | let folderCreated = FileSystemManager.createFolder(basePath); 41 | if (!folderCreated) { return; } 42 | this.createFile(basePath, 'base_model.dart', new BaseModel('base_model.dart').dartString); 43 | this.createFile(basePath, 'base_service.dart', new BaseService('base_service.dart').dartString); 44 | } 45 | 46 | private initCoreFiles(corePath: string) { 47 | this.createFile(corePath, 'locator.dart', new Locator('locator.dart').dartString); 48 | this.createFile(corePath, 'logger.dart', new Logger('logger.dart').dartString); 49 | } 50 | 51 | private initServices(corePath: string) { 52 | let servicesPath = path.join(corePath, 'services'); 53 | 54 | let folderCreated = FileSystemManager.createFolder(servicesPath); 55 | if (!folderCreated) { return; } 56 | } 57 | 58 | private initModels(corePath: string) { 59 | let modelsPath = path.join(corePath, 'models'); 60 | let folderCreated = FileSystemManager.createFolder(modelsPath); 61 | console.debug(`FolderCreated: ${folderCreated}`); 62 | } 63 | 64 | private initTheme() { 65 | let themePath = path.join(this.rootPath, 'theme'); 66 | let folderCreated = FileSystemManager.createFolder(themePath); 67 | console.debug(`FolderCreated: ${folderCreated}`); 68 | } 69 | 70 | private initViews() { 71 | let viewsPath = path.join(this.rootPath, 'views'); 72 | let folderCreated = FileSystemManager.createFolder(viewsPath); 73 | console.debug(`FolderCreated: ${folderCreated}`); 74 | } 75 | 76 | private initWidgets() { 77 | let widgetsPath = path.join(this.rootPath, 'widgets'); 78 | let widgetsFolderCreated = FileSystemManager.createFolder(widgetsPath); 79 | let dumbWidgetsPath = path.join(widgetsPath, 'dumb_widgets'); 80 | let dumbWidgetFolderCreated = FileSystemManager.createFolder(dumbWidgetsPath); 81 | let smartWidgetsPath = path.join(widgetsPath, 'smart_widgets'); 82 | let smartWidgetFolderCreated = FileSystemManager.createFolder(smartWidgetsPath); 83 | console.debug(`FolderCreated: ${widgetsFolderCreated}`); 84 | console.debug(`FolderCreated: ${dumbWidgetFolderCreated}`); 85 | console.debug(`FolderCreated: ${smartWidgetFolderCreated}`); 86 | } 87 | 88 | private createFile(pathValue: string, fileName: string, data: string, options?: WriteFileOptions) { 89 | if (FileSystemManager.doesFileExist(pathValue, fileName)) { 90 | console.error(`${fileName} already exists`); 91 | return; 92 | } 93 | 94 | FileSystemManager.createFile(pathValue, fileName, data); 95 | // Utils.openFile(path.join(pathValue, fileName)); 96 | } 97 | 98 | private createExistingFile(pathValue: string, fileName: string, data: string, options?: WriteFileOptions) { 99 | FileSystemManager.createFile(pathValue, fileName, data); 100 | Utils.openFile(path.join(pathValue, fileName)); 101 | } 102 | } -------------------------------------------------------------------------------- /src/utils/config_json.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as _ from "lodash"; 3 | import { FileSystemManager } from "./file_system_manager"; 4 | import { VsCodeActions } from "./vs_code_actions"; 5 | 6 | export class ConfigJSON { 7 | 8 | constructor() { } 9 | 10 | public static createJSONFile() { 11 | const root_path = VsCodeActions.rootPath; 12 | const file_data = FileSystemManager.readFileAsString(root_path, 'stackedConfig.json'); 13 | if (file_data === undefined) { 14 | FileSystemManager.createFile(root_path, 'stackedConfig.json', JSON.stringify({}, null, 4)); 15 | return; 16 | } 17 | let data = file_data.trim(); 18 | if (data === "") { 19 | FileSystemManager.createFile(root_path, 'stackedConfig.json', JSON.stringify({}, null, 4)); 20 | return; 21 | } 22 | let json: Config = JSON.parse(data); 23 | console.log(json); 24 | FileSystemManager.createFile(root_path, 'stackedConfig.json', JSON.stringify(json, null, 4)); 25 | } 26 | 27 | public static readTypeOfArchitecture(): string | undefined { 28 | const root_path = VsCodeActions.rootPath; 29 | const file_data = FileSystemManager.readFileAsString(root_path, 'stackedConfig.json'); 30 | 31 | let json: Config = {}; 32 | 33 | if (file_data === undefined) { 34 | console.debug('stackedConfig.json not found'); 35 | console.debug('Creating stackedConfig.json'); 36 | } else { 37 | json = JSON.parse(file_data) ?? {}; 38 | } 39 | 40 | return json.typeOfArchitecture; 41 | } 42 | 43 | public static updateTypeOfArchitecture(typeOfArch: string) { 44 | const root_path = VsCodeActions.rootPath; 45 | const file_data = FileSystemManager.readFileAsString(root_path, 'stackedConfig.json'); 46 | 47 | let json: Config = {}; 48 | 49 | if (file_data === undefined) { 50 | console.debug('stackedConfig.json not found'); 51 | console.debug('Creating stackedConfig.json'); 52 | } else { 53 | json = JSON.parse(file_data) ?? {}; 54 | } 55 | 56 | json.typeOfArchitecture = typeOfArch; 57 | FileSystemManager.createFile(root_path, 'stackedConfig.json', JSON.stringify(json, null, 4)); 58 | } 59 | } 60 | 61 | 62 | export interface Config { 63 | typeOfArchitecture?: string; 64 | } -------------------------------------------------------------------------------- /src/utils/file_system_manager.ts: -------------------------------------------------------------------------------- 1 | import { WriteFileOptions, writeFileSync, existsSync, readFile, readFileSync } from "fs"; 2 | import * as path from 'path'; 3 | import { Utils } from './utils'; 4 | import * as shell from "shelljs"; 5 | import { VsCodeActions } from "./vs_code_actions"; 6 | import { YamlHelper } from "./yaml_helper"; 7 | 8 | export class FileSystemManager { 9 | public static createFile(pathValue: string, fileName: string, data: string) { 10 | let filePath = path.join(pathValue, fileName); 11 | writeFileSync(filePath, data); 12 | // Utils.openFile(filePath); 13 | } 14 | 15 | public static createFolder(pathValue: string): boolean { 16 | if (!existsSync(pathValue)) { 17 | try { 18 | shell.mkdir('-p', pathValue); 19 | 20 | } catch (error) { 21 | console.error(`Unable to create folder: ${error}`); 22 | return false; 23 | } 24 | } 25 | return true; 26 | } 27 | 28 | public static doesFileExist(filePath: string, fileName: string): boolean { 29 | return existsSync(path.join(filePath, fileName)); 30 | } 31 | 32 | public static readFileAsString(filePath: string, fileName: string): string | undefined { 33 | if (!this.doesFileExist(filePath, fileName)) { return undefined; } 34 | let fileBuffer = readFileSync(path.join(filePath, fileName)); 35 | let fileData = fileBuffer.toString(); 36 | return fileData; 37 | } 38 | 39 | public static isFlutterProject(): boolean { 40 | let rootPath = VsCodeActions.rootPath; 41 | if (!existsSync(path.join(rootPath, 'pubspec.yaml'))) { 42 | VsCodeActions.showErrorMessage('Pubspec.yaml not found'); 43 | return false; 44 | } 45 | let errorMessage = YamlHelper.isValidFlutterPubspec(); 46 | console.error(errorMessage); 47 | if (errorMessage !== undefined) { 48 | VsCodeActions.showErrorMessage(errorMessage); 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | } -------------------------------------------------------------------------------- /src/utils/router_constants.ts: -------------------------------------------------------------------------------- 1 | import _ = require("lodash"); 2 | import * as path from 'path'; 3 | import { FileSystemManager } from "./file_system_manager"; 4 | import { VsCodeActions } from "./vs_code_actions"; 5 | 6 | export class RouterConstants { 7 | constructor(private routeList: Array) { } 8 | 9 | public create() { 10 | let dartString = '// [ This is an auto generated file ]\n\n'; 11 | this.routeList.forEach((value) => { 12 | dartString += `const String ${this.lowerCamelCased(value)} = '${this.lowerCamelCased(value)}';\n`; 13 | }); 14 | 15 | const root_path = VsCodeActions.rootPath; 16 | const json_path = path.join(root_path, 'lib', 'core'); 17 | FileSystemManager.createFile(json_path, 'router_constants.dart', dartString); 18 | } 19 | 20 | private lowerCamelCased(value: string): string { 21 | return _.camelCase(value); 22 | } 23 | } -------------------------------------------------------------------------------- /src/utils/router_json.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as _ from 'lodash'; 3 | import { VsCodeActions } from './vs_code_actions'; 4 | import { FileSystemManager } from './file_system_manager'; 5 | import { RouterConstants } from './router_constants'; 6 | import { Router } from '../dart_snippets/architecture/router'; 7 | import { YamlHelper } from './yaml_helper'; 8 | 9 | export class RouterJSON { 10 | 11 | constructor(private fileName: string, private folders?: string[]) { } 12 | 13 | public async addRoute() { 14 | const root_path = VsCodeActions.rootPath; 15 | const json_path = path.join(root_path, 'lib', 'core'); 16 | const file_data = FileSystemManager.readFileAsString(json_path, 'router.json'); 17 | 18 | let json: IRouterJSON = {}; 19 | if (file_data === undefined) { 20 | console.debug('router.json not found'); 21 | console.debug('Creating router.json'); 22 | } else { 23 | json = JSON.parse(file_data) ?? {}; 24 | } 25 | 26 | const last_updated: string = new Date().toUTCString(); 27 | json.last_updated = last_updated; 28 | 29 | const routes = json.routes ?? []; 30 | const pathValue = this.pathValue.replace(/\\/g, "/"); 31 | console.log("pathValue: " + pathValue); 32 | 33 | let routeName = `${this.snakeCasedFileName}ViewRoute`; 34 | let suffix: number | undefined; 35 | 36 | while (this.checkIfSameRouteExists(routeName + (suffix ?? ''), routes)) { 37 | if (_.isUndefined(suffix)) { 38 | suffix = 0; 39 | } else { 40 | suffix += 1; 41 | } 42 | } 43 | 44 | if (suffix !== undefined) { 45 | routeName += suffix; 46 | } 47 | 48 | routes.push({ 49 | 'file_path': `${pathValue}/${this.snakeCasedFileName}_view.dart`, 50 | 'route_name': routeName, 51 | 'view_name': this.pascalCasedFileName + 'View', 52 | }); 53 | 54 | json.routes = routes; 55 | this.saveJson(json_path, json); 56 | 57 | RouterJSON.generateFiles(); 58 | } 59 | 60 | public static generateFiles() { 61 | const root_path = VsCodeActions.rootPath; 62 | const json_path = path.join(root_path, 'lib', 'core'); 63 | const file_data = FileSystemManager.readFileAsString(json_path, 'router.json'); 64 | 65 | let json: IRouterJSON = {}; 66 | if (file_data === undefined) { 67 | console.debug('router.json not found'); 68 | console.debug('Creating router.json'); 69 | } else { 70 | json = JSON.parse(file_data) ?? {}; 71 | } 72 | 73 | const last_updated: string = new Date().toUTCString(); 74 | json.last_updated = last_updated; 75 | 76 | const routes = json.routes ?? []; 77 | new RouterConstants(routes.map((value) => value.route_name)).create(); 78 | new Router('router.dart', routes, YamlHelper.getProjectName()).create(); 79 | } 80 | 81 | private saveJson(json_path: string, file: Object) { 82 | FileSystemManager.createFile(json_path, 'router.json', JSON.stringify(file, null, 4)); 83 | } 84 | 85 | private checkIfSameRouteExists(routeName: string, routes: Array): boolean { 86 | for (const value of routes) { 87 | console.log(`value: ${value.route_name} === routeName: ${routeName}`); 88 | if (value.route_name === routeName) { return true; } 89 | } 90 | return false; 91 | } 92 | 93 | private get pathValue(): string { 94 | if (this.folders === undefined) { 95 | return path.join( 96 | 'views', 97 | this.snakeCasedFileName 98 | ); 99 | } 100 | return path.join('views', ...this.folders, this.snakeCasedFileName); 101 | } 102 | 103 | private get snakeCasedFileName(): string { 104 | let snakeCasedFileName = _.snakeCase(this.fileName); 105 | return snakeCasedFileName; 106 | } 107 | 108 | private get upperSnakeCasedFileName(): string { 109 | return _.upperFirst(this.snakeCasedFileName); 110 | } 111 | 112 | private get pascalCasedFileName(): string { 113 | return _.upperFirst(_.camelCase(this.fileName)); 114 | } 115 | } 116 | 117 | export interface IRouterJSON { 118 | last_updated?: string; 119 | routes?: Array; 120 | } 121 | 122 | export interface IRouteObject { 123 | file_path: string; 124 | view_name: string; 125 | route_name: string; 126 | } -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import _ = require('lodash'); 3 | 4 | export class Utils { 5 | public static readonly ARCHITECTURE = 'Architecture'; 6 | public static readonly TYPE_OF_ARCHITECTURE = ['Responsive', 'Mobile']; 7 | public static readonly TYPE_OF_VIEWMODEL = ['BaseViewModel', 'FutureViewModel', 'StreamViewModel', 'MultipleFutureViewModel', 'MultipleStreamViewModel', 'ReactiveViewModel', 'IndexTrackingViewModel']; 8 | public static readonly TYPE_OF_WIDGET = ['Smart Widget', 'Dumb Widget']; 9 | 10 | public static isValidClassName(className: string): string | undefined { 11 | if (className.length === 0) { 12 | return "File name should have atleast 1 character"; 13 | } 14 | if (className.toLowerCase() === "view") { 15 | return "View is not a valid file name"; 16 | } 17 | 18 | if (className.toLowerCase() === "widget") { 19 | return "Widget is not a valid file name"; 20 | } 21 | 22 | if ( 23 | !className 24 | .substring(0, 1) 25 | .match(new RegExp("([a-zA-Z$][w$]*.)*[a-zA-Z_$][w$]*")) 26 | ) { 27 | return "Invalid class name format"; 28 | } 29 | return undefined; 30 | } 31 | 32 | public static openFile(filePath: string) { 33 | console.info(`openFile: ${filePath}`); 34 | let openPath = vscode.Uri.file(filePath); 35 | 36 | vscode.workspace.openTextDocument(openPath).then((document) => { 37 | vscode.window.showTextDocument(document); 38 | }); 39 | } 40 | 41 | public static processFileName(fileName: string): string { 42 | 43 | if (fileName.length < 4) { 44 | return fileName; 45 | } 46 | fileName = _.lowerCase(fileName); 47 | 48 | let viewFileName = fileName 49 | .substring(fileName.length - 4, fileName.length) 50 | .toLowerCase(); 51 | 52 | let widgetFileName = fileName 53 | .substring(fileName.length - 6, fileName.length) 54 | .toLowerCase(); 55 | 56 | 57 | 58 | if (viewFileName === "view") { 59 | let truncatedFileName = fileName.substring(0, fileName.length - 4); 60 | return truncatedFileName.trim(); 61 | } 62 | 63 | if (widgetFileName === "widget") { 64 | let truncatedFileName = fileName.substring(0, fileName.length - 6); 65 | console.debug('Widget testing'); 66 | return truncatedFileName.trim(); 67 | } 68 | 69 | return fileName.trim(); 70 | } 71 | 72 | public static convertResponsiveToEnum(architectureType?: string): TYPE_OF_ARCHITECTURE { 73 | switch (architectureType) { 74 | case 'Mobile': return TYPE_OF_ARCHITECTURE.Mobile; 75 | case 'Responsive': return TYPE_OF_ARCHITECTURE.Responsive; 76 | default: return TYPE_OF_ARCHITECTURE.Mobile; 77 | } 78 | } 79 | 80 | public static convertViewModelToEnum(viewModelType?: string): TYPE_OF_VIEWMODEL | undefined { 81 | switch (viewModelType) { 82 | case 'BaseViewModel': return TYPE_OF_VIEWMODEL.BaseViewModel; 83 | case 'FutureViewModel': return TYPE_OF_VIEWMODEL.FutureViewModel; 84 | case 'StreamViewModel': return TYPE_OF_VIEWMODEL.StreamViewModel; 85 | case 'MultipleFutureViewModel': return TYPE_OF_VIEWMODEL.MultipleFutureViewModel; 86 | case 'MultipleStreamViewModel': return TYPE_OF_VIEWMODEL.MultipleStreamViewModel; 87 | case 'ReactiveViewModel': return TYPE_OF_VIEWMODEL.ReactiveViewModel; 88 | case 'IndexTrackingViewModel': return TYPE_OF_VIEWMODEL.IndexTrackingViewModel; 89 | default: return undefined; 90 | } 91 | } 92 | 93 | public static convertWidgetToEnum(widgetTpye?: string): TYPE_OF_WIDGET | undefined { 94 | switch(widgetTpye) { 95 | case 'Smart Widget': return TYPE_OF_WIDGET.Smart; 96 | case 'Dumb Widget': return TYPE_OF_WIDGET.Dumb; 97 | default: return undefined; 98 | } 99 | } 100 | } 101 | 102 | export enum TYPE_OF_ARCHITECTURE { 103 | Mobile, 104 | Responsive, 105 | } 106 | 107 | export enum TYPE_OF_VIEWMODEL { 108 | BaseViewModel, 109 | FutureViewModel, 110 | StreamViewModel, 111 | MultipleFutureViewModel, 112 | MultipleStreamViewModel, 113 | ReactiveViewModel, 114 | IndexTrackingViewModel, 115 | } 116 | 117 | export enum TYPE_OF_WIDGET { 118 | Smart, 119 | Dumb 120 | } 121 | -------------------------------------------------------------------------------- /src/utils/view_file.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as _ from 'lodash'; 3 | import { existsSync } from 'fs'; 4 | import { FileSystemManager } from './file_system_manager'; 5 | import { YamlHelper } from './yaml_helper'; 6 | import { TYPE_OF_ARCHITECTURE, TYPE_OF_VIEWMODEL } from './utils'; 7 | import { View } from '../dart_snippets/views/view'; 8 | import { Mobile } from '../dart_snippets/views/mobile'; 9 | import { Tablet } from '../dart_snippets/views/tablet'; 10 | import { Desktop } from '../dart_snippets/views/desktop'; 11 | import { ViewModel } from '../dart_snippets/views/view_model'; 12 | import { RouterJSON } from './router_json'; 13 | 14 | export class ViewFile { 15 | 16 | constructor(private rootPath: string, private fileName: string, private typeOfArchitecture: TYPE_OF_ARCHITECTURE, private typeOfViewModel: TYPE_OF_VIEWMODEL, private folders?: string[]) { 17 | console.debug(`ViewFile(rootPath: ${rootPath}, fileName: ${fileName})`); 18 | let folderCreated = FileSystemManager.createFolder(this.pathValue); 19 | if (!folderCreated) { return; } 20 | } 21 | 22 | public createViews() { 23 | switch (this.typeOfArchitecture) { 24 | case TYPE_OF_ARCHITECTURE.Responsive: 25 | this.createResponsiveViews(); 26 | break; 27 | case TYPE_OF_ARCHITECTURE.Mobile: 28 | this.createMobileViews(); 29 | break; 30 | default: 31 | this.createMobileViews(); 32 | break; 33 | } 34 | 35 | new RouterJSON(this.fileName, this.folders).addRoute(); 36 | } 37 | 38 | private createMobileViews() { 39 | this.createFiles(`${this.snakeCasedFileName}_view.dart`, new View(this.snakeCasedFileName, 'View', TYPE_OF_ARCHITECTURE.Mobile).dartString); 40 | this.createFiles(`${this.snakeCasedFileName}_view_model.dart`, new ViewModel(this.snakeCasedFileName, 'ViewModel', this.typeOfViewModel, YamlHelper.getProjectName()).dartString); 41 | } 42 | 43 | private createResponsiveViews() { 44 | this.createFiles(`${this.snakeCasedFileName}_view.dart`, new View(this.snakeCasedFileName, 'View', TYPE_OF_ARCHITECTURE.Responsive).dartString); 45 | this.createFiles(`${this.snakeCasedFileName}_mobile.dart`, new Mobile(this.snakeCasedFileName, 'Mobile').dartString); 46 | this.createFiles(`${this.snakeCasedFileName}_desktop.dart`, new Desktop(this.snakeCasedFileName, 'Desktop').dartString); 47 | this.createFiles(`${this.snakeCasedFileName}_tablet.dart`, new Tablet(this.snakeCasedFileName, 'Tablet').dartString); 48 | this.createFiles(`${this.snakeCasedFileName}_view_model.dart`, new ViewModel(this.snakeCasedFileName, 'ViewModel', this.typeOfViewModel, YamlHelper.getProjectName()).dartString); 49 | } 50 | 51 | private get snakeCasedFileName(): string { 52 | let snakeCasedFileName = _.snakeCase(this.fileName); 53 | return snakeCasedFileName; 54 | } 55 | 56 | private get pathValue(): string { 57 | if (this.folders === undefined) { 58 | return path.join( 59 | this.rootPath, 60 | 'lib', 61 | 'views', 62 | this.snakeCasedFileName 63 | ); 64 | } 65 | return path.join(this.rootPath, 'lib', 'views', ...this.folders, this.snakeCasedFileName); 66 | } 67 | 68 | private createFiles(fileName: string, data: string) { 69 | if (existsSync(path.join(this.pathValue, this.snakeCasedFileName))) { 70 | console.warn(`${fileName} already exists`); 71 | return; 72 | } 73 | 74 | FileSystemManager.createFile(this.pathValue, fileName, data); 75 | } 76 | } -------------------------------------------------------------------------------- /src/utils/vs_code_actions.ts: -------------------------------------------------------------------------------- 1 | import { window, InputBoxOptions, workspace, WorkspaceConfiguration } from 'vscode'; 2 | import * as _ from 'lodash'; 3 | 4 | export class VsCodeActions { 5 | 6 | /** 7 | * Accepts an input string in the current Vscode context 8 | * @param placeHolder The placeholder string to display in the input box 9 | * @param validateInput The function to validate if the text entered in the input box is of a valid format or not 10 | */ 11 | public static async getInputString(placeHolder?: string, validateInput?: InputBoxOptions["validateInput"]): Promise { 12 | let input = await window.showInputBox({ 13 | placeHolder: placeHolder, 14 | validateInput: validateInput, 15 | }); 16 | if (input === undefined) { 17 | return ""; 18 | } 19 | return input; 20 | } 21 | 22 | public static async getInputDropdown(placeHolder: string[]): Promise { 23 | let input = await window.showQuickPick(placeHolder); 24 | return input; 25 | } 26 | 27 | /** 28 | * Get the root path of the current context 29 | */ 30 | public static get rootPath(): string { 31 | let rootPath = workspace.rootPath; 32 | if (_.isUndefined(rootPath)) { return ''; } 33 | return rootPath; 34 | } 35 | 36 | /** 37 | * Display an error message in the current VsCode context 38 | * @param message The message to display 39 | */ 40 | public static showErrorMessage(message: string) { 41 | window.showErrorMessage(message); 42 | } 43 | 44 | /** 45 | * Display an information message in the current VsCode context 46 | * @param message The message to display 47 | */ 48 | public static showInformationMessage(message: string) { 49 | window.showInformationMessage(message); 50 | } 51 | 52 | public static getEditorConfiguration(): WorkspaceConfiguration { 53 | let configuration = workspace.getConfiguration('editor'); 54 | return configuration; 55 | 56 | } 57 | 58 | public static getUserPreference() { 59 | 60 | } 61 | } -------------------------------------------------------------------------------- /src/utils/widget_file.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as _ from 'lodash'; 3 | import { existsSync } from 'fs'; 4 | import { FileSystemManager } from './file_system_manager'; 5 | import { TYPE_OF_ARCHITECTURE, TYPE_OF_WIDGET, TYPE_OF_VIEWMODEL } from './utils'; 6 | import { Widget } from '../dart_snippets/widgets/widget'; 7 | import { Mobile } from '../dart_snippets/widgets/mobile'; 8 | import { Desktop } from '../dart_snippets/widgets/desktop'; 9 | import { Tablet } from '../dart_snippets/widgets/tablet'; 10 | import { ViewModel } from '../dart_snippets/widgets/view_model'; 11 | import { YamlHelper } from './yaml_helper'; 12 | 13 | export class WidgetFile { 14 | 15 | constructor(private rootPath: string, private fileName: string, private typeOfArchitecture: TYPE_OF_ARCHITECTURE, private typeOfWidget: TYPE_OF_WIDGET, private typeOfViewModel?: TYPE_OF_VIEWMODEL) { 16 | console.debug(`WidgetFile(rootPath: ${rootPath}, fileName: ${fileName})`); 17 | let folderCreated = FileSystemManager.createFolder(this.pathValue); 18 | if (!folderCreated) { return; } 19 | } 20 | 21 | public createWidgets() { 22 | switch (this.typeOfArchitecture) { 23 | case TYPE_OF_ARCHITECTURE.Responsive: 24 | this.createResponsiveWidget(); 25 | break; 26 | case TYPE_OF_ARCHITECTURE.Mobile: 27 | this.createMobileWidget(); 28 | break; 29 | default: 30 | this.createMobileWidget(); 31 | break; 32 | } 33 | } 34 | 35 | private createMobileWidget() { 36 | this.createFiles(`${this.snakeCasedFileName}_widget.dart`, new Widget(this.snakeCasedFileName, 'Widget', TYPE_OF_ARCHITECTURE.Mobile, this.typeOfWidget).dartString); 37 | if (this.typeOfWidget === TYPE_OF_WIDGET.Smart && this.typeOfViewModel !== undefined) { 38 | this.createFiles(`${this.snakeCasedFileName}_view_model.dart`, new ViewModel(this.snakeCasedFileName, 'ViewModel', this.typeOfViewModel, YamlHelper.getProjectName()).dartString); 39 | } 40 | } 41 | 42 | private createResponsiveWidget() { 43 | this.createFiles(`${this.snakeCasedFileName}_widget.dart`, new Widget(this.snakeCasedFileName, 'Widget', TYPE_OF_ARCHITECTURE.Responsive, this.typeOfWidget).dartString); 44 | this.createFiles(`${this.snakeCasedFileName}_mobile.dart`, new Mobile(this.snakeCasedFileName, 'Mobile').dartString); 45 | this.createFiles(`${this.snakeCasedFileName}_desktop.dart`, new Desktop(this.snakeCasedFileName, 'Desktop').dartString); 46 | this.createFiles(`${this.snakeCasedFileName}_tablet.dart`, new Tablet(this.snakeCasedFileName, 'Tablet').dartString); 47 | if (this.typeOfWidget === TYPE_OF_WIDGET.Smart && this.typeOfViewModel !== undefined) { 48 | this.createFiles(`${this.snakeCasedFileName}_view_model.dart`, new ViewModel(this.snakeCasedFileName, 'ViewModel', this.typeOfViewModel, YamlHelper.getProjectName()).dartString); 49 | } 50 | } 51 | 52 | private get snakeCasedFileName(): string { 53 | let snakeCasedFileName = _.snakeCase(this.fileName); 54 | console.debug(`get snakeCasedFileName: ${snakeCasedFileName}`); 55 | return snakeCasedFileName; 56 | } 57 | 58 | private get pathValue(): string { 59 | return this.typeOfWidget === TYPE_OF_WIDGET.Smart ? path.join(this.rootPath, 'lib', 'widgets', 'smart_widgets', this.snakeCasedFileName) : path.join(this.rootPath, 'lib', 'widgets', 'dumb_widgets', this.snakeCasedFileName); 60 | } 61 | 62 | private createFiles(fileName: string, data: string) { 63 | if (existsSync(path.join(this.pathValue, this.snakeCasedFileName))) { 64 | console.warn(`${fileName} already exists`); 65 | return; 66 | } 67 | 68 | FileSystemManager.createFile(this.pathValue, fileName, data); 69 | } 70 | } -------------------------------------------------------------------------------- /src/utils/yaml_helper.ts: -------------------------------------------------------------------------------- 1 | import * as yaml from 'js-yaml'; 2 | import { VsCodeActions } from './vs_code_actions'; 3 | import { FileSystemManager } from './file_system_manager'; 4 | import * as _ from 'lodash'; 5 | 6 | export class YamlHelper { 7 | 8 | public static initializeWithDependencies() { 9 | this.upgradeDartVersion(); 10 | this.addDependencyToPubspec('get_it', '4.0.4'); 11 | this.addDependencyToPubspec('logger', '0.9.2'); 12 | this.addDependencyToPubspec('stacked', '1.7.6'); 13 | this.addDependencyToPubspec('stacked_services', '0.5.4+2'); 14 | this.addDependencyToPubspec('responsive_builder', '0.2.0+2'); 15 | this.addDependencyToPubspec('equatable', '1.2.4'); 16 | this.addAssetComment(); 17 | } 18 | 19 | public static isValidFlutterPubspec(): string | undefined { 20 | let json = this.getPubspecJsonFile(); 21 | if (json === undefined) { return 'Invalid Pubspec format'; } 22 | let object = JSON.parse(json); 23 | 24 | if (object['environment'] === undefined) { 25 | return 'No environment definition found'; 26 | } 27 | if (object['dependencies'] === undefined) { 28 | return 'Definition for dependencies not found'; 29 | } 30 | if (object['dependencies']['flutter'] === undefined) { 31 | return 'Definition for FLutter in dependencies not found'; 32 | } 33 | return undefined; 34 | } 35 | 36 | public static getProjectName(): string | undefined { 37 | let json = this.getPubspecJsonFile(); 38 | if (json === undefined) { return undefined; } 39 | let object = JSON.parse(json); 40 | 41 | return object['name']; 42 | } 43 | 44 | private static addDependencyToPubspec(module: string, version?: string) { 45 | let json = this.getPubspecJsonFile(); 46 | if (json === undefined) { return; } 47 | let object = JSON.parse(json); 48 | object['dependencies'][module] = `^${version}`; 49 | let modifiedString = JSON.stringify(object); 50 | // console.debug(`addDependencyToPubspec: modifiledString: ${modifiedString}`); 51 | let updatedYaml = this.toYAML(modifiedString); 52 | if (updatedYaml === undefined) { 53 | return; 54 | } 55 | this.overwritePubspecFile(updatedYaml); 56 | } 57 | 58 | private static upgradeDartVersion() { 59 | let json = this.getPubspecJsonFile(); 60 | if (json === undefined) { return; } 61 | let object = JSON.parse(json); 62 | object['environment']['sdk'] = '>=2.3.0 <3.0.0'; 63 | let modifiedString = JSON.stringify(object); 64 | console.debug(`upgradeDartVersion: modifiledString: ${modifiedString}`); 65 | let updatedYaml = this.toYAML(modifiedString); 66 | if (updatedYaml === undefined) { 67 | return; 68 | } 69 | this.overwritePubspecFile(updatedYaml); 70 | } 71 | 72 | private static addAssetComment() { 73 | let json = this.getPubspecJsonFile(); 74 | if (json === undefined) { return; } 75 | let object = JSON.parse(json); 76 | let modifiedString = JSON.stringify(object); 77 | let updatedYaml = this.toYAML(modifiedString); 78 | if (updatedYaml === undefined) { 79 | return; 80 | } 81 | updatedYaml += `\n\n # To add assets to your application, add an assets section, like this: 82 | # assets: 83 | # - images/a_dot_burr.jpeg 84 | # - images/a_dot_ham.jpeg 85 | 86 | # An image asset can refer to one or more resolution-specific "variants", see 87 | # https://flutter.dev/assets-and-images/#resolution-aware. 88 | 89 | # For details regarding adding assets from package dependencies, see 90 | # https://flutter.dev/assets-and-images/#from-packages 91 | 92 | # To add custom fonts to your application, add a fonts section here, 93 | # in this "flutter" section. Each entry in this list should have a 94 | # "family" key with the font family name, and a "fonts" key with a 95 | # list giving the asset and other descriptors for the font. For 96 | # example: 97 | # fonts: 98 | # - family: Schyler 99 | # fonts: 100 | # - asset: fonts/Schyler-Regular.ttf 101 | # - asset: fonts/Schyler-Italic.ttf 102 | # style: italic 103 | # - family: Trajan Pro 104 | # fonts: 105 | # - asset: fonts/TrajanPro.ttf 106 | # - asset: fonts/TrajanPro_Bold.ttf 107 | # weight: 700 108 | # 109 | # For details regarding fonts from package dependencies, 110 | # see https://flutter.dev/custom-fonts/#from-packages`; 111 | 112 | this.overwritePubspecFile(updatedYaml); 113 | } 114 | 115 | private static getPubspecJsonFile(): string | undefined { 116 | let rootPath = VsCodeActions.rootPath; 117 | let fileData = FileSystemManager.readFileAsString(rootPath, 'pubspec.yaml'); 118 | if (fileData === undefined) { 119 | console.debug('Pubspec.yaml not found'); 120 | return undefined; 121 | } 122 | let data = YamlHelper.toJSON(fileData); 123 | return data; 124 | } 125 | 126 | private static overwritePubspecFile(data: string) { 127 | FileSystemManager.createFile(VsCodeActions.rootPath, 'pubspec.yaml', data); 128 | } 129 | 130 | private static toYAML(text: string): string | undefined { 131 | let json; 132 | try { 133 | // console.debug(`toYAML: ${text}`); 134 | json = JSON.parse(text); 135 | } catch (e) { 136 | VsCodeActions.showErrorMessage('Could not parse the selection as JSON.'); 137 | console.error(e); 138 | return undefined; 139 | } 140 | return yaml.safeDump(json, { indent: this.getIndent() }); 141 | } 142 | 143 | private static toJSON(text: string) { 144 | let json; 145 | try { 146 | // console.debug(`toJSON: ${text}`); 147 | json = yaml.safeLoad(text, { schema: yaml.JSON_SCHEMA }); 148 | } catch (e) { 149 | VsCodeActions.showErrorMessage('Could not parse the selection as YAML.'); 150 | console.error(e); 151 | return; 152 | } 153 | return JSON.stringify(json, null, this.getIndent()); 154 | } 155 | 156 | private static getIndent(): number { 157 | const editorCfg = VsCodeActions.getEditorConfiguration(); 158 | if (editorCfg && editorCfg.get('insertSpaces')) { 159 | const tabSize = editorCfg.get('tabSize'); 160 | if (tabSize && typeof tabSize === 'number') { 161 | return tabSize; 162 | } 163 | } 164 | return 2; 165 | } 166 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": ["es6"], 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "strict": true 10 | }, 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } 13 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "semicolon": [ 9 | true, 10 | "always" 11 | ], 12 | "triple-equals": true 13 | }, 14 | "defaultSeverity": "warning" 15 | } 16 | -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file in which you declare your extension and command. 7 | * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. 11 | 12 | ## Get up and running straight away 13 | 14 | * Press `F5` to open a new window with your extension loaded. 15 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 16 | * Set breakpoints in your code inside `src/extension.ts` to debug your extension. 17 | * Find output from your extension in the debug console. 18 | 19 | ## Make changes 20 | 21 | * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. 22 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 23 | 24 | 25 | ## Explore the API 26 | 27 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. 28 | 29 | ## Run tests 30 | 31 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. 32 | * Press `F5` to run the tests in a new window with your extension loaded. 33 | * See the output of the test result in the debug console. 34 | * Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder. 35 | * The provided test runner will only consider files matching the name pattern `**.test.ts`. 36 | * You can create folders inside the `test` folder to structure your tests any way you want. 37 | 38 | ## Go further 39 | 40 | * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). 41 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. 42 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). 43 | --------------------------------------------------------------------------------