├── .github └── workflows │ ├── gradle.yml │ └── publish.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── build-logic ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── jvm-publication-convention.gradle.kts │ └── publication-convention.gradle.kts ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── install-shaper.sh ├── run-cli-sample.sh ├── samples ├── kmm-component-out │ ├── news-droid │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── dev │ │ │ └── icerock │ │ │ └── shaper │ │ │ └── sample │ │ │ └── kmm │ │ │ └── news │ │ │ └── SelectorFragment.kt │ ├── news-ios │ │ ├── Classes │ │ │ └── SelectorViewController.swift │ │ └── News.podspec │ └── news │ │ ├── build.gradle.kts │ │ └── commonMain │ │ └── kotlin │ │ └── dev │ │ └── icerock │ │ └── shaper │ │ └── sample │ │ └── kmm │ │ └── news │ │ └── SelectorViewModel.kt ├── kmm-component.yaml ├── kmm-component │ ├── {{moduleName}}-droid │ │ ├── build.gradle.kts.hbs │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── {{dts packageName}} │ │ │ └── SelectorFragment.kt │ ├── {{moduleName}}-ios │ │ ├── Classes │ │ │ └── SelectorViewController.swift │ │ └── {{cap moduleName}}.podspec.hbs │ └── {{moduleName}} │ │ ├── build.gradle.kts.hbs │ │ └── commonMain │ │ └── kotlin │ │ └── {{dts packageName}} │ │ └── SelectorViewModel.kt ├── kmm-module-out │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── dev │ │ │ └── icerock │ │ │ └── shaper │ │ │ └── sample │ │ │ └── kmm │ │ │ └── auth │ │ │ └── Empty.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── dev │ │ │ └── icerock │ │ │ └── shaper │ │ │ └── sample │ │ │ └── kmm │ │ │ └── auth │ │ │ └── di │ │ │ └── AuthFactory.kt │ │ └── iosMain │ │ └── kotlin │ │ └── dev │ │ └── icerock │ │ └── shaper │ │ └── sample │ │ └── kmm │ │ └── auth │ │ └── Empty.kt ├── kmm-module.yaml └── kmm-module │ ├── AndroidManifest.xml.hbs │ ├── Empty.kt │ ├── Factory.kt.hbs │ ├── build.gradle.kts.hbs │ ├── console.output.hbs │ └── sub │ └── build.gradle.kts.hbs ├── settings.gradle.kts ├── shaper-cli ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── dev │ └── icerock │ └── tools │ └── shaper │ └── cli │ ├── ConfigOverrider.kt │ └── ShaperCli.kt └── shaper-core ├── build.gradle.kts └── src ├── main └── kotlin │ └── dev │ └── icerock │ └── tools │ └── shaper │ └── core │ ├── FileTemplateSource.kt │ ├── HandlebarsFactory.kt │ ├── PathLocator.kt │ ├── Shaper.kt │ ├── ShaperConfig.kt │ ├── StringExt.kt │ ├── TemplateClassPathLoader.kt │ ├── TemplateConfig.kt │ ├── TemplatesRepository.kt │ └── YamlConfigReader.kt └── test ├── kotlin └── dev │ └── icerock │ └── tools │ └── shaper │ └── core │ ├── ShaperTest.kt │ ├── StringExtTest.kt │ └── YamlReaderTest.kt └── resources ├── Test.kt ├── Test.kt.hbs ├── build.gradle.kts ├── build.gradle.kts.hbs ├── config.yaml ├── configForShaper.yaml ├── configWithoutFiles.yaml ├── files ├── 1yaml-test.hbs └── 2yaml-test.hbs ├── includes ├── part.hbs └── partials │ └── part2.hbs ├── second-includes └── second-psrt.hbs └── sub ├── build.gradle.kts └── build.gradle.kts.hbs /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI with Gradle 5 | 6 | on: 7 | push: 8 | branches: [ master, develop ] 9 | pull_request: 10 | branches: [ master, develop ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 11 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 11 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | - name: Build with Gradle 26 | run: ./gradlew build 27 | - name: Publish Test Report 28 | uses: mikepenz/action-junit-report@v2 29 | if: ${{ always() }} 30 | with: 31 | report_paths: '**/build/test-results/test/TEST-*.xml' 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Create release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'Version' 8 | default: '0.1.0' 9 | required: true 10 | 11 | jobs: 12 | publish: 13 | name: Publish library at mavenCentral 14 | runs-on: macos-latest 15 | env: 16 | OSSRH_USER: ${{ secrets.OSSRH_USER }} 17 | OSSRH_KEY: ${{ secrets.OSSRH_KEY }} 18 | SIGNING_KEY_ID: ${{ secrets.SIGNING_KEYID }} 19 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} 20 | SIGNING_KEY: ${{ secrets.GPG_KEY_CONTENTS }} 21 | steps: 22 | - uses: actions/checkout@v1 23 | - name: Set up JDK 11 24 | uses: actions/setup-java@v1 25 | with: 26 | java-version: 11 27 | - name: Publish 28 | run: ./gradlew publish 29 | release: 30 | name: Create release 31 | needs: publish 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Create Release 35 | id: create_release 36 | uses: actions/create-release@v1 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | commitish: ${{ github.ref }} 41 | tag_name: release/${{ github.event.inputs.version }} 42 | release_name: ${{ github.event.inputs.version }} 43 | body: "Will be filled later" 44 | draft: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | build 4 | *.iml -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shaper 2 | 3 | Kotlin library and command line tool for generation of directory with files from template (on 4 | Handlebars) and configuration. 5 | 6 | # Install 7 | 8 | `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/icerockdev/shaper/master/install-shaper.sh)"` 9 | 10 | ## Core library 11 | ```kotlin 12 | repositories { 13 | mavenCentral() 14 | } 15 | 16 | dependencies { 17 | implementation("dev.icerock.tools:shaper-core:0.5.1") 18 | } 19 | ``` 20 | 21 | # Usage 22 | 23 | ## CLI 24 | 25 | You can use generator by CLI app. Configuration stored in yaml file in this case: 26 | 27 | ```yaml 28 | globalParams: 29 | packageName: dev.icerock 30 | 31 | files: 32 | - pathTemplate: 'build.gradle.kts' 33 | contentTemplateName: build.gradle.kts 34 | templateParams: 35 | dependencies: 36 | - dep1 37 | - dep2 38 | - pathTemplate: 'src/main/kotlin/{{packagePath packageName}}/Test.kt' 39 | contentTemplateName: Test.kt 40 | templateParams: 41 | packageName: dev.icerock.test 42 | ``` 43 | 44 | To run CLI: 45 | 46 | ```shell 47 | shaper-cli --help 48 | ``` 49 | 50 | ## Core 51 | 52 | Core is kotlin library, for integration in cli-app, idea plugin, gradle plugin, etc. 53 | 54 | Using is simple: 55 | 56 | ```kotlin 57 | import dev.icerock.tools.shaper.core.TemplateConfig 58 | import dev.icerock.tools.shaper.core.Shaper 59 | 60 | // describe files configuration for generator 61 | val buildGradleFile = TemplateConfig.FileConfig( 62 | // path of output file 63 | pathTemplate = "build.gradle.kts", 64 | // name of template file used for content of file 65 | contentTemplateName = "build.gradle.kts", 66 | // params that will be passed into template 67 | templateParams = mapOf("dependencies" to listOf("dep1", "dep2")) 68 | ) 69 | val sourceCodeFile = TemplateConfig.FileConfig( 70 | // path also support templates and got all params 71 | pathTemplate = "src/main/kotlin/{{packagePath packageName}}/Test.kt", 72 | contentTemplateName = "Test.kt", 73 | templateParams = mapOf("packageName" to "dev.icerock.test") 74 | ) 75 | 76 | // describe generator config 77 | val config = TemplateConfig( 78 | // params that will be passed into template of each files. 79 | // can be overriden by params of file 80 | globalParams = mapOf("packageName" to "dev.icerock"), 81 | // list of files that should be generated 82 | files = listOf(buildGradleFile, sourceCodeFile) 83 | ) 84 | 85 | // create generator with configuration 86 | val shaper = Shaper(config) 87 | // execute generation into build/test directory 88 | shaper.execute(output = "build/test") 89 | ``` 90 | 91 | Templates will be loaded by priority: 92 | 93 | 1. search at working dir by name 94 | 2. search at template repositories from `~/.shaper/config.yaml` 95 | 2. search at resources of ClassLoader by name 96 | 97 | Here sample templates: 98 | `build.gradle.kts.hbs`: 99 | 100 | ```handlebars 101 | plugins { 102 | id("org.jetbrains.kotlin.jvm") version "1.4.30" 103 | } 104 | 105 | dependencies { 106 | {{#each dependencies}} implementation("{{this}}") 107 | {{/each}} 108 | } 109 | ``` 110 | 111 | `Test.kt.hbs`: 112 | 113 | ```handlebars 114 | package {{packageName}} 115 | 116 | class Test { 117 | } 118 | ``` 119 | 120 | ## Include/exclude files 121 | 122 | You can include/exclude files using global parameters in config file. 123 | 124 | 1. Add global parameter(s) that can have value `true` or `false`. 125 | 126 | 2. Add `{{incl ...}}` helper with the specified parameter in file path and/or file name. 127 | 128 | ```yaml 129 | globalParams: 130 | ... 131 | addTests: false 132 | addDefaultTest: false 133 | files: 134 | - pathTemplate: '{{incl addTests}}src/main/kotlin/package/{{incl addDefaultTest}}DefaultTest.kt' 135 | contentTemplateName: DefaultTest.kt 136 | ``` 137 | 138 | In this example, file `DefaultTest.kt` will be added only if both parameters `addFragment` and `addDefaultTest` are set to `true`. 139 | 140 | ## Partials 141 | 142 | For use partials you can put your `.hbs` files in `includes` directories (multi-nesting support). 143 | 144 | In config yaml file: 145 | ```yaml 146 | globalParams: 147 | ... 148 | files: 149 | ... 150 | outputs: 151 | ... 152 | includes: 153 | - partials 154 | - layouts 155 | 156 | ``` 157 | 158 | `partials/sub-partials/type.hbs` 159 | ```handlebars 160 | {{~#if (eq this.type "integer")}}Int{{else}}{{stu this.type}}{{/if}}{{#if nullableOnly}}?{{else}}{{#if this.nullable}}?{{/if~}}{{/if~}} 161 | ``` 162 | 163 | `layouts/hello.hbs` 164 | ```handlebars 165 | Hi {{name}} 166 | ``` 167 | 168 | And use the partials in templates: 169 | ```handlebars 170 | {{~#each columns}} 171 | val {{this.name}}: {{> type this nullableOnly=true}} = obj.{{this.name~}} 172 | {{~/each}} 173 | {{> hello name="Developer"}} 174 | ``` 175 | 176 | ## Helpers 177 | In config yaml file: 178 | ```yaml 179 | globalParams: 180 | items: 181 | - name: "name1" 182 | type: "type1" 183 | ... 184 | - name: "name2" 185 | type: "type2" 186 | ... 187 | ``` 188 | `filterByAllOf` filters `items` by all pair key-value (AND condition), for example: 189 | ```handlebars 190 | {{~#each (filterByAllOf items type="type1" name="name1")}} 191 | {{! there will only be items when type="type1" and name="name1"}} 192 | {{~/each}} 193 | ``` 194 | `filterByOneOf` filters `items` by one of pair key-value (OR condition), for example: 195 | ```handlebars 196 | {{~#each (filterByAllOf items type="type1" name="name2")}} 197 | {{! there will only be items when type="type1" or name="name2"}} 198 | {{~/each}} 199 | ``` 200 | `containsAllOf` checks for all pair key-value (AND condition) in `items`, for example: 201 | ```handlebars 202 | {{~#if (containsAllOf items type="type1" name="name1")}} 203 | ... 204 | {{~/if}} 205 | ``` 206 | `containsOneOf` checks for one of pair key-value (OR condition) in `items`, for example: 207 | ```handlebars 208 | {{~#if (containsOneOf items type="type1" name="name2")}} 209 | ... 210 | {{~/if}} 211 | ``` 212 | `containsKey` checks for exist key in `items`, for example: 213 | ```handlebars 214 | {{~#if (containsKey items "type")}} 215 | ... 216 | {{~/if}} 217 | ``` 218 | `containsValue` checks for exist value in `items`, for example: 219 | ```handlebars 220 | {{~#if (containsValue items "type1")}} 221 | ... 222 | {{~/if}} 223 | ``` 224 | `currentTimestamp` returns the current UNIX time, for example: 225 | ```handlebars 226 | {{currentTimestamp}} 227 | ``` 228 | `currentDateTime` returns the current date and/or time in a specified format, for example: 229 | ```handlebars 230 | {{currentDateTime "yyyy-MM-dd HH:mm:ss"}} 231 | ``` 232 | `renderPartial` render partial template like `{{> paramTemplate paramName="test"}}` and return it value, for example: 233 | ```handlebars 234 | {{~#if (eq (renderPartial "paramTemplate" paramName="test") "test")}} 235 | ... 236 | {{~/if}} 237 | ``` 238 | 239 | ## License 240 | 241 | Copyright 2021 IceRock MAG Inc 242 | 243 | Licensed under the Apache License, Version 2.0 (the "License"); 244 | you may not use this file except in compliance with the License. 245 | You may obtain a copy of the License at 246 | 247 | http://www.apache.org/licenses/LICENSE-2.0 248 | 249 | Unless required by applicable law or agreed to in writing, software 250 | distributed under the License is distributed on an "AS IS" BASIS, 251 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 252 | See the License for the specific language governing permissions and 253 | limitations under the License. 254 | -------------------------------------------------------------------------------- /build-logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | google() 8 | 9 | gradlePluginPortal() 10 | } 11 | 12 | dependencies { 13 | api("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") 14 | } 15 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/jvm-publication-convention.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | plugins { 6 | id("java") 7 | id("org.gradle.maven-publish") 8 | } 9 | 10 | java { 11 | withJavadocJar() 12 | withSourcesJar() 13 | } 14 | 15 | publishing.publications.register("mavenJava", MavenPublication::class) { 16 | from(components["java"]) 17 | } 18 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/publication-convention.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | import java.util.Base64 6 | 7 | plugins { 8 | id("org.gradle.maven-publish") 9 | id("signing") 10 | } 11 | 12 | publishing { 13 | repositories.maven("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") { 14 | name = "OSSRH" 15 | 16 | credentials { 17 | username = System.getenv("OSSRH_USER") 18 | password = System.getenv("OSSRH_KEY") 19 | } 20 | } 21 | 22 | publications.withType { 23 | // Provide artifacts information requited by Maven Central 24 | pom { 25 | name.set("Shaper") 26 | description.set("File structure generation tool from templates on Handlebars") 27 | url.set("https://github.com/icerockdev/shaper") 28 | licenses { 29 | license { 30 | name.set("Apache-2.0") 31 | distribution.set("repo") 32 | url.set("https://github.com/icerockdev/shaper/blob/master/LICENSE.md") 33 | } 34 | } 35 | 36 | developers { 37 | developer { 38 | id.set("Alex009") 39 | name.set("Aleksey Mikhailov") 40 | email.set("aleksey.mikhailov@icerockdev.com") 41 | } 42 | developer { 43 | id.set("oyakovlev") 44 | name.set("Oleg Yakovlev") 45 | email.set("oyakovlev@icerockdev.com") 46 | } 47 | developer { 48 | id.set("ATchernov") 49 | name.set("Andrey Tchernov") 50 | email.set("tchernov@icerockdev.com") 51 | } 52 | developer { 53 | id.set("RezMike") 54 | name.set("Mikhail Reznichenko") 55 | email.set("mreznichenko@icerockdev.com") 56 | } 57 | } 58 | 59 | scm { 60 | connection.set("scm:git:ssh://github.com/icerockdev/shaper.git") 61 | developerConnection.set("scm:git:ssh://github.com/icerockdev/shaper.git") 62 | url.set("https://github.com/icerockdev/shaper") 63 | } 64 | } 65 | } 66 | } 67 | 68 | 69 | signing { 70 | val signingKeyId: String? = System.getenv("SIGNING_KEY_ID") 71 | val signingPassword: String? = System.getenv("SIGNING_PASSWORD") 72 | val signingKey: String? = System.getenv("SIGNING_KEY")?.let { base64Key -> 73 | String(Base64.getDecoder().decode(base64Key)) 74 | } 75 | if (signingKeyId != null) { 76 | useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) 77 | sign(publishing.publications) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | buildscript { 6 | repositories { 7 | mavenCentral() 8 | google() 9 | 10 | gradlePluginPortal() 11 | } 12 | dependencies { 13 | classpath(":build-logic") 14 | } 15 | } 16 | 17 | subprojects { 18 | group = "dev.icerock.tools" 19 | version = "0.5.1" 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /install-shaper.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ~/.shaper && \ 2 | cd ~/.shaper && \ 3 | curl -L -s "https://github.com/icerockdev/shaper/releases/download/release%2F0.5.1/shaper-cli-0.5.1.zip" > cli.zip && \ 4 | unzip -q cli.zip && \ 5 | rm cli.zip && \ 6 | rm -rf shaper-cli || true && \ 7 | mv shaper-cli-0.5.1 shaper-cli && \ 8 | echo "repositories:" > config.yaml && \ 9 | echo 'To complete setup add into your environments: export PATH=~/.shaper/shaper-cli/bin:$PATH' 10 | echo 'After it you can call shaper by command: shaper-cli -i -o ' 11 | -------------------------------------------------------------------------------- /run-cli-sample.sh: -------------------------------------------------------------------------------- 1 | ./gradlew build 2 | cd samples 3 | java -jar ../shaper-cli/build/libs/shaper-cli-0.1.1-SNAPSHOT.jar -i $1.yaml -o $1-out -------------------------------------------------------------------------------- /samples/kmm-component-out/news-droid/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | plugin(Deps.Plugins.androidLibrary) 3 | plugin(Deps.Plugins.kotlinAndroid) 4 | } 5 | 6 | dependencies { 7 | implementation(Deps.Libs.Android.appCompat) 8 | implementation(Deps.Libs.Android.material) 9 | implementation(Deps.Libs.Android.constraintLayout) 10 | implementation(Deps.Libs.Android.recyclerView) 11 | implementation(Deps.Libs.Android.mokoMvvmViewBinding) 12 | implementation(Deps.Libs.Android.androidKtx) 13 | implementation(Deps.Libs.Android.lifecycleRuntimeKtx) 14 | 15 | implementation(project(parent.path + ":news")) 16 | } 17 | -------------------------------------------------------------------------------- /samples/kmm-component-out/news-droid/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /samples/kmm-component-out/news-droid/src/main/java/dev/icerock/shaper/sample/kmm/news/SelectorFragment.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/samples/kmm-component-out/news-droid/src/main/java/dev/icerock/shaper/sample/kmm/news/SelectorFragment.kt -------------------------------------------------------------------------------- /samples/kmm-component-out/news-ios/Classes/SelectorViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import MultiPlatformLibrary 3 | 4 | class SelectorViewController: UIViewController { 5 | } 6 | -------------------------------------------------------------------------------- /samples/kmm-component-out/news-ios/News.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'News' 3 | spec.version = '0.1.0' 4 | spec.homepage = '..' 5 | spec.source = { :git => "..." } 6 | spec.authors = 'IceRock Development' 7 | spec.license = '' 8 | spec.summary = '...' 9 | 10 | spec.module_name = "#{spec.name}" 11 | 12 | spec.ios.deployment_target = '11.0' 13 | spec.swift_version = '5' 14 | 15 | spec.source_files = "Classes/**/*.{h,m,swift}" 16 | end -------------------------------------------------------------------------------- /samples/kmm-component-out/news/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | plugin(Deps.Plugins.androidLibrary) 3 | plugin(Deps.Plugins.kotlinMultiplatform) 4 | plugin(Deps.Plugins.mobileMultiplatform) 5 | } 6 | 7 | dependencies { 8 | commonMainImplementation(Deps.Libs.MultiPlatform.ktorClient) 9 | 10 | commonTestImplementation(Deps.Libs.Tests.kotlinTestJUnit) 11 | commonTestImplementation(Deps.Libs.Tests.mokoTest) 12 | } 13 | -------------------------------------------------------------------------------- /samples/kmm-component-out/news/commonMain/kotlin/dev/icerock/shaper/sample/kmm/news/SelectorViewModel.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/samples/kmm-component-out/news/commonMain/kotlin/dev/icerock/shaper/sample/kmm/news/SelectorViewModel.kt -------------------------------------------------------------------------------- /samples/kmm-component.yaml: -------------------------------------------------------------------------------- 1 | globalParams: 2 | packageName: dev.icerock.shaper.sample.kmm.news 3 | moduleName: news 4 | 5 | files: kmm-component/ 6 | -------------------------------------------------------------------------------- /samples/kmm-component/{{moduleName}}-droid/build.gradle.kts.hbs: -------------------------------------------------------------------------------- 1 | plugins { 2 | plugin(Deps.Plugins.androidLibrary) 3 | plugin(Deps.Plugins.kotlinAndroid) 4 | } 5 | 6 | dependencies { 7 | implementation(Deps.Libs.Android.appCompat) 8 | implementation(Deps.Libs.Android.material) 9 | implementation(Deps.Libs.Android.constraintLayout) 10 | implementation(Deps.Libs.Android.recyclerView) 11 | implementation(Deps.Libs.Android.mokoMvvmViewBinding) 12 | implementation(Deps.Libs.Android.androidKtx) 13 | implementation(Deps.Libs.Android.lifecycleRuntimeKtx) 14 | 15 | implementation(project(parent.path + ":{{moduleName}}")) 16 | } 17 | -------------------------------------------------------------------------------- /samples/kmm-component/{{moduleName}}-droid/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /samples/kmm-component/{{moduleName}}-droid/src/main/java/{{dts packageName}}/SelectorFragment.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/samples/kmm-component/{{moduleName}}-droid/src/main/java/{{dts packageName}}/SelectorFragment.kt -------------------------------------------------------------------------------- /samples/kmm-component/{{moduleName}}-ios/Classes/SelectorViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import MultiPlatformLibrary 3 | 4 | class SelectorViewController: UIViewController { 5 | } 6 | -------------------------------------------------------------------------------- /samples/kmm-component/{{moduleName}}-ios/{{cap moduleName}}.podspec.hbs: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = '{{cap moduleName}}' 3 | spec.version = '0.1.0' 4 | spec.homepage = '..' 5 | spec.source = { :git => "..." } 6 | spec.authors = 'IceRock Development' 7 | spec.license = '' 8 | spec.summary = '...' 9 | 10 | spec.module_name = "#{spec.name}" 11 | 12 | spec.ios.deployment_target = '11.0' 13 | spec.swift_version = '5' 14 | 15 | spec.source_files = "Classes/**/*.{h,m,swift}" 16 | end -------------------------------------------------------------------------------- /samples/kmm-component/{{moduleName}}/build.gradle.kts.hbs: -------------------------------------------------------------------------------- 1 | plugins { 2 | plugin(Deps.Plugins.androidLibrary) 3 | plugin(Deps.Plugins.kotlinMultiplatform) 4 | plugin(Deps.Plugins.mobileMultiplatform) 5 | } 6 | 7 | dependencies { 8 | commonMainImplementation(Deps.Libs.MultiPlatform.ktorClient) 9 | 10 | commonTestImplementation(Deps.Libs.Tests.kotlinTestJUnit) 11 | commonTestImplementation(Deps.Libs.Tests.mokoTest) 12 | } 13 | -------------------------------------------------------------------------------- /samples/kmm-component/{{moduleName}}/commonMain/kotlin/{{dts packageName}}/SelectorViewModel.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/samples/kmm-component/{{moduleName}}/commonMain/kotlin/{{dts packageName}}/SelectorViewModel.kt -------------------------------------------------------------------------------- /samples/kmm-module-out/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | plugin(Deps.Plugins.androidLibrary) 3 | plugin(Deps.Plugins.kotlinMultiplatform) 4 | plugin(Deps.Plugins.mobileMultiplatform) 5 | } 6 | 7 | dependencies { 8 | androidMainImplementation(Deps.Libs.Android.lifecycle) 9 | androidMainImplementation(Deps.Libs.Android.recyclerView) 10 | 11 | commonMainImplementation(Deps.Libs.MultiPlatform.mokoMvvm.common) 12 | commonMainImplementation(Deps.Libs.MultiPlatform.mokoResources.common) 13 | commonMainImplementation(Deps.Libs.MultiPlatform.napier.common) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /samples/kmm-module-out/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /samples/kmm-module-out/src/androidMain/kotlin/dev/icerock/shaper/sample/kmm/auth/Empty.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/samples/kmm-module-out/src/androidMain/kotlin/dev/icerock/shaper/sample/kmm/auth/Empty.kt -------------------------------------------------------------------------------- /samples/kmm-module-out/src/commonMain/kotlin/dev/icerock/shaper/sample/kmm/auth/di/AuthFactory.kt: -------------------------------------------------------------------------------- 1 | package dev.icerock.shaper.sample.kmm.auth.di 2 | 3 | class AuthFactory( 4 | // TODO pass external dependencies to constructor arguments 5 | ) { 6 | // TODO here add factory methods and singleton lazies 7 | } 8 | -------------------------------------------------------------------------------- /samples/kmm-module-out/src/iosMain/kotlin/dev/icerock/shaper/sample/kmm/auth/Empty.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/samples/kmm-module-out/src/iosMain/kotlin/dev/icerock/shaper/sample/kmm/auth/Empty.kt -------------------------------------------------------------------------------- /samples/kmm-module.yaml: -------------------------------------------------------------------------------- 1 | globalParams: 2 | packageName: dev.icerock.shaper.sample.kmm.auth 3 | moduleName: Auth 4 | androidMainDeps: 5 | - lifecycle 6 | - recyclerView 7 | commonMainDeps: 8 | - mokoMvvm 9 | - mokoResources 10 | - napier 11 | config: 12 | val: test 13 | var: 1 14 | opt: 15 | first: 1.1 16 | second: two 17 | third: false 18 | 19 | files: 20 | - pathTemplate: 'build.gradle.kts' 21 | contentTemplateName: kmm-module/build.gradle.kts.hbs 22 | - pathTemplate: 'src/commonMain/kotlin/{{dts packageName}}/di/{{moduleName}}Factory.kt' 23 | contentTemplateName: kmm-module/Factory.kt.hbs 24 | - pathTemplate: 'src/androidMain/AndroidManifest.xml' 25 | contentTemplateName: kmm-module/AndroidManifest.xml.hbs 26 | - pathTemplate: 'src/androidMain/kotlin/{{dts packageName}}/Empty.kt' 27 | contentTemplateName: kmm-module/Empty.kt 28 | - pathTemplate: 'src/iosMain/kotlin/{{dts packageName}}/Empty.kt' 29 | contentTemplateName: kmm-module/Empty.kt 30 | outputs: 31 | - title: '=== Tips for feature setup ===' 32 | contentTemplateName: kmm-module/console.output.hbs -------------------------------------------------------------------------------- /samples/kmm-module/AndroidManifest.xml.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /samples/kmm-module/Empty.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icerockdev/shaper/4ec73208fcbb7133241fc275a7003d5dc375dac5/samples/kmm-module/Empty.kt -------------------------------------------------------------------------------- /samples/kmm-module/Factory.kt.hbs: -------------------------------------------------------------------------------- 1 | package {{packageName}}.di 2 | 3 | class {{moduleName}}Factory( 4 | // TODO pass external dependencies to constructor arguments 5 | ) { 6 | // TODO here add factory methods and singleton lazies 7 | } 8 | -------------------------------------------------------------------------------- /samples/kmm-module/build.gradle.kts.hbs: -------------------------------------------------------------------------------- 1 | plugins { 2 | plugin(Deps.Plugins.androidLibrary) 3 | plugin(Deps.Plugins.kotlinMultiplatform) 4 | plugin(Deps.Plugins.mobileMultiplatform) 5 | } 6 | 7 | dependencies { 8 | {{#each androidMainDeps}} androidMainImplementation(Deps.Libs.Android.{{this}}) 9 | {{/each}} 10 | {{#each commonMainDeps}} commonMainImplementation(Deps.Libs.MultiPlatform.{{this}}.common) 11 | {{/each}} 12 | } 13 | -------------------------------------------------------------------------------- /samples/kmm-module/console.output.hbs: -------------------------------------------------------------------------------- 1 | 1. Move *out folder to mpp-library/features 2 | 2. Rename it to '{{lcs moduleName}}' 3 | 3. Append next line to 'settings.gradle.kts': 4 | 5 | include(":mpp-library:feature:{{lcs moduleName}}") 6 | 7 | 4. Append next property to you Deps.kt file (should be in ./buildSrc/main/kotlin) 8 | in Modules.Feature 9 | ``` 10 | object Feature { 11 | val {{lcs moduleName}} = MultiPlatformModule( 12 | name = ":mpp-library:feature:{{lcs moduleName}}", 13 | exported = true 14 | ) 15 | ``` 16 | 17 | 5. Include feature in you project root 'build.gradle.kts': 18 | ``` 19 | val mppModules = listOf( 20 | ... 21 | //append here 22 | Deps.Modules.Feature.{{lcs moduleName}} 23 | ) 24 | ``` -------------------------------------------------------------------------------- /samples/kmm-module/sub/build.gradle.kts.hbs: -------------------------------------------------------------------------------- 1 | plugins { 2 | plugin(Deps.Plugins.androidLibrary) 3 | plugin(Deps.Plugins.kotlinMultiplatform) 4 | plugin(Deps.Plugins.mobileMultiplatform) 5 | } 6 | # Suka 7 | dependencies { 8 | {{#each androidMainDeps}} androidMainImplementation(Deps.Libs.Android.{{this}}) 9 | {{/each}} 10 | {{#each commonMainDeps}} commonMainImplementation(Deps.Libs.MultiPlatform.{{this}}.common) 11 | {{/each}} 12 | } 13 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | rootProject.name = "shaper" 6 | 7 | dependencyResolutionManagement { 8 | repositories { 9 | mavenCentral() 10 | } 11 | } 12 | 13 | includeBuild("build-logic") 14 | 15 | include("shaper-core") 16 | include("shaper-cli") -------------------------------------------------------------------------------- /shaper-cli/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | plugins { 6 | id("org.jetbrains.kotlin.jvm") 7 | id("application") 8 | } 9 | 10 | dependencies { 11 | implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6") 12 | /** github.jknack:handlebars uses a slf4j-api package which is working only with slf4j-simple 13 | * if github.jknack:handlebars version updated, slf4j-simple needs up version too **/ 14 | implementation("org.slf4j:slf4j-simple:1.7.32") 15 | implementation(project(":shaper-core")) 16 | } 17 | 18 | application { 19 | mainClass.set("dev.icerock.tools.shaper.cli.ShaperCliKt") 20 | } 21 | 22 | // export PATH=~/.shaper/shaper-cli/bin:$PATH 23 | tasks.create("install") { 24 | dependsOn(tasks.getByName("installDist")) 25 | group = "distribution" 26 | 27 | doFirst { 28 | copy { 29 | val userHome = System.getProperty("user.home") 30 | from(file("${layout.buildDirectory}/install")) 31 | into(file("$userHome/.shaper")) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /shaper-cli/src/main/kotlin/dev/icerock/tools/shaper/cli/ConfigOverrider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.cli 6 | 7 | class ConfigOverrider { 8 | fun override(input: Map): Map { 9 | return overrideMap(input) 10 | } 11 | 12 | private fun overrideMap(map: Map, path: String? = null): Map { 13 | val prefix = path?.plus(":") ?: "" 14 | return map.mapValues { 15 | overrideValue(prefix + it.key, it.value) 16 | } 17 | } 18 | 19 | private fun overrideValue(path: String, value: Any): Any { 20 | return when (value) { 21 | is String -> overrideString(path, value) 22 | is List<*> -> overrideList(path, value) 23 | is Map<*, *> -> overrideMap(map = value as Map, path = path) 24 | is Int -> overrideInt(path, value) 25 | is Double -> overrideDouble(path, value) 26 | is Boolean -> overrideBoolean(path, value) 27 | else -> value 28 | } 29 | } 30 | 31 | private fun overrideString(path: String, value: String): String { 32 | return overridePrimitive(path, value) { it } 33 | } 34 | 35 | private fun overrideBoolean(path: String, value: Boolean): Boolean { 36 | return overridePrimitive(path, value) { it == "true" } 37 | } 38 | 39 | private fun overrideInt(path: String, value: Int): Int { 40 | return overridePrimitive(path, value) { it.toInt() } 41 | } 42 | 43 | private fun overrideDouble(path: String, value: Double): Double { 44 | return overridePrimitive(path, value) { it.toDouble() } 45 | } 46 | 47 | private fun overridePrimitive(path: String, value: T, mapper: (String) -> T): T { 48 | println("Input $path value") 49 | println("Default: $value (Press enter to skip change)") 50 | val input = readLine() ?: return value 51 | val newValue = if (input.isBlank()) value else mapper(input) 52 | println("$path = $newValue\n") 53 | return newValue 54 | } 55 | 56 | // TODO #11 maybe give user option to write own list? 57 | private fun overrideList(path: String, value: List<*>): List<*> { 58 | return value.mapIndexed { index, item -> 59 | overrideValue("$path:$index", item as Any) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /shaper-cli/src/main/kotlin/dev/icerock/tools/shaper/cli/ShaperCli.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.cli 6 | 7 | import dev.icerock.tools.shaper.core.Shaper 8 | import dev.icerock.tools.shaper.core.ShaperConfig 9 | import dev.icerock.tools.shaper.core.TemplatesRepository 10 | import kotlinx.cli.ArgParser 11 | import kotlinx.cli.ArgType 12 | import kotlinx.cli.default 13 | 14 | fun main(args: Array) { 15 | val parser = ArgParser("shaper") 16 | 17 | val input: String by parser.option( 18 | type = ArgType.String, 19 | shortName = "i", 20 | description = "input configuration file", 21 | ).default("shaper.yaml") 22 | 23 | val output: String by parser.option( 24 | type = ArgType.String, 25 | shortName = "o", 26 | description = "output files directory", 27 | ).default(".") 28 | 29 | val skipOverrider: Boolean by parser.option( 30 | type = ArgType.Boolean, 31 | shortName = "s", 32 | description = "skip overrider" 33 | ).default(false) 34 | 35 | parser.parse(args) 36 | 37 | val shaperConfig = ShaperConfig.read() 38 | val templatesRepository = TemplatesRepository(shaperConfig) 39 | val config = templatesRepository.getTemplateConfig(input) 40 | 41 | val overriddenConfig = when (skipOverrider) { 42 | true -> config 43 | else -> config.copy(globalParams = ConfigOverrider().override(config.globalParams)) 44 | } 45 | 46 | val shaper = Shaper(templateConfig = overriddenConfig) 47 | val consoleResult = shaper.execute(output) 48 | println(consoleResult) 49 | } 50 | -------------------------------------------------------------------------------- /shaper-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | plugins { 6 | id("org.jetbrains.kotlin.jvm") 7 | id("publication-convention") 8 | id("jvm-publication-convention") 9 | } 10 | 11 | dependencies { 12 | implementation("com.github.jknack:handlebars:4.3.1") 13 | implementation("org.yaml:snakeyaml:2.0") 14 | 15 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.9.25") 16 | } 17 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/FileTemplateSource.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import com.github.jknack.handlebars.io.AbstractTemplateSource 8 | import java.io.File 9 | import java.nio.charset.Charset 10 | 11 | class FileTemplateSource(private val file: File, private val filename: String? = null) : AbstractTemplateSource() { 12 | override fun content(charset: Charset?): String { 13 | return file.readText(charset ?: Charsets.UTF_8) 14 | } 15 | 16 | override fun filename(): String = filename ?: file.name 17 | 18 | override fun lastModified(): Long = 0L 19 | } 20 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/HandlebarsFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import com.github.jknack.handlebars.Handlebars 8 | import com.github.jknack.handlebars.Helper 9 | import com.github.jknack.handlebars.cache.ConcurrentMapTemplateCache 10 | import java.time.LocalDateTime 11 | import java.time.format.DateTimeFormatter 12 | 13 | object HandlebarsFactory { 14 | private const val TIME_MILLIS_MULTIPLIER = 1000 15 | fun create(): Handlebars { 16 | val handlebars = Handlebars().with(ConcurrentMapTemplateCache()) 17 | 18 | handlebars.registerHelper("incl", Helper { include, _ -> 19 | if (include) "" else Shaper.NOT_INCLUDE 20 | }) 21 | 22 | handlebars.registerHelper("raw", Helper> { _, options -> 23 | options.fn() 24 | }) 25 | 26 | handlebars.registerHelper("renderPartial", Helper { context, options -> 27 | handlebars.compile(context).apply(options.context.combine(options.hash)) 28 | }) 29 | 30 | registerConditionHelpers(handlebars) 31 | registerStringHelpers(handlebars) 32 | registerArrayHelpers(handlebars) 33 | registerDateTimeHelpers(handlebars) 34 | 35 | return handlebars 36 | } 37 | 38 | private fun registerConditionHelpers(handlebars: Handlebars) { 39 | handlebars.registerHelper("eq", Helper { context, options -> 40 | context == options.params[0] 41 | }) 42 | 43 | handlebars.registerHelper("or", Helper { context, options -> 44 | context || options.params[0] as Boolean 45 | }) 46 | 47 | handlebars.registerHelper("and", Helper { context, options -> 48 | context && options.params[0] as Boolean 49 | }) 50 | } 51 | 52 | private fun registerStringHelpers(handlebars: Handlebars) { 53 | handlebars.registerHelper("dts", Helper { context, _ -> 54 | context.replace('.', '/') 55 | }) 56 | handlebars.registerHelper("lcs", Helper { context, _ -> 57 | context.lowercase() 58 | }) 59 | handlebars.registerHelper("cap", Helper { context, _ -> 60 | context.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } 61 | }) 62 | handlebars.registerHelper("ucs", Helper { context, _ -> 63 | context.uppercase() 64 | }) 65 | handlebars.registerHelper("cts", Helper { context, _ -> 66 | context.camelToSnakeCase() 67 | }) 68 | handlebars.registerHelper("stl", Helper { context, _ -> 69 | context.snakeToLowerCamelCase() 70 | }) 71 | handlebars.registerHelper("stu", Helper { context, _ -> 72 | context.snakeToUpperCamelCase() 73 | }) 74 | handlebars.registerHelper("ctwcap", Helper { context, _ -> 75 | context.snakeToUpperCamelCase().camelToCapitalizeWithWhitespace() 76 | }) 77 | } 78 | 79 | private fun registerArrayHelpers(handlebars: Handlebars) { 80 | handlebars.registerHelper("filterByAllOf", Helper>> { context, options -> 81 | if (options.hash.isEmpty()) { 82 | return@Helper arrayListOf>() 83 | } 84 | 85 | val keyValueMap = options.hash.toMap() 86 | context.filter { map: Map -> 87 | keyValueMap.filter { entry: Map.Entry -> 88 | map.containsKey(entry.key) && map[entry.key] == entry.value 89 | }.size == keyValueMap.size 90 | } 91 | }) 92 | 93 | handlebars.registerHelper("filterByOneOf", Helper>> { context, options -> 94 | if (options.hash.isEmpty()) { 95 | return@Helper arrayListOf>() 96 | } 97 | 98 | val keyValueMap = options.hash.toMap() 99 | context.filter { map: Map -> 100 | keyValueMap.filter { entry: Map.Entry -> 101 | map.containsKey(entry.key) && map[entry.key] == entry.value 102 | }.isNotEmpty() 103 | } 104 | }) 105 | 106 | handlebars.registerHelper("containsAllOf", Helper>> { context, options -> 107 | if (options.hash.isEmpty()) { 108 | return@Helper false 109 | } 110 | 111 | val keyValueMap = options.hash.toMap() 112 | context.any { map: Map -> 113 | keyValueMap.filter { entry: Map.Entry -> 114 | map.containsKey(entry.key) && map[entry.key] == entry.value 115 | }.size == keyValueMap.size 116 | } 117 | }) 118 | 119 | handlebars.registerHelper("containsOneOf", Helper>> { context, options -> 120 | if (options.hash.isEmpty()) { 121 | return@Helper false 122 | } 123 | 124 | val keyValueMap = options.hash.toMap() 125 | context.any { map: Map -> 126 | keyValueMap.filter { entry: Map.Entry -> 127 | map.containsKey(entry.key) && map[entry.key] == entry.value 128 | }.isNotEmpty() 129 | } 130 | }) 131 | 132 | handlebars.registerHelper("containsKey", Helper>> { context, options -> 133 | context.any { map: Map -> 134 | map.keys.contains(options.params[0]) 135 | } 136 | }) 137 | 138 | handlebars.registerHelper("containsValue", Helper>> { context, options -> 139 | context.any { map: Map -> 140 | map.values.contains(options.params[0]) 141 | } 142 | }) 143 | } 144 | 145 | private fun registerDateTimeHelpers(handlebars: Handlebars) { 146 | handlebars.registerHelper("currentTimestamp", Helper { _, _ -> 147 | System.currentTimeMillis() / TIME_MILLIS_MULTIPLIER 148 | }) 149 | 150 | handlebars.registerHelper("currentDateTime", Helper { context, _ -> 151 | LocalDateTime.now().format(DateTimeFormatter.ofPattern(context)) 152 | }) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/PathLocator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | object PathLocator { 8 | 9 | private val dirMapList = mutableMapOf() 10 | 11 | fun set(path: String, base: String) = dirMapList.put(path, base) 12 | 13 | fun getBaseDir(path: String) = dirMapList[path] 14 | 15 | fun getAbsolutePath(path: String) = getBaseDir(path) + "/" + path 16 | 17 | } 18 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/Shaper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import com.github.jknack.handlebars.io.FileTemplateLoader 8 | import com.github.jknack.handlebars.io.URLTemplateLoader 9 | import java.io.File 10 | import java.io.FileNotFoundException 11 | import java.io.FileWriter 12 | import java.io.StringWriter 13 | 14 | class Shaper(private val templateConfig: TemplateConfig) { 15 | 16 | fun execute(outputPath: String): String { 17 | val handlebars = HandlebarsFactory.create() 18 | 19 | val list = mutableListOf(TemplateClassPathLoader("/", "")) 20 | templateConfig.includes.forEach { includeDir -> 21 | val file = File(includeDir) 22 | 23 | if (file.exists()) { 24 | file.walkTopDown().filter { it.isDirectory }.forEach { 25 | list.add(FileTemplateLoader(it.absolutePath, ".hbs")) 26 | } 27 | } else { 28 | File( 29 | this::class.java.classLoader.getResource("$includeDir/") 30 | ?.file ?: throw FileNotFoundException(includeDir) 31 | ).walkTopDown().filter { it.isDirectory }.forEach { 32 | list.add(FileTemplateLoader(it.absolutePath, ".hbs")) 33 | } 34 | } 35 | } 36 | 37 | handlebars.with(*list.toTypedArray()) 38 | 39 | templateConfig.files.forEach { fileConfig -> 40 | val allParams = templateConfig.globalParams + fileConfig.templateParams 41 | 42 | val fileNameTemplate = handlebars.compileInline(fileConfig.pathTemplate.replace("\\", "/")) 43 | val filePath = fileNameTemplate.apply(allParams) 44 | if (filePath.contains(NOT_INCLUDE)) return@forEach 45 | 46 | val file = File(outputPath, filePath) 47 | with(file.parentFile) { 48 | if (!exists()) mkdirs() 49 | } 50 | 51 | if (fileConfig.contentTemplateName.endsWith(".hbs")) { 52 | val contentTemplate = handlebars.compile(fileConfig.contentTemplateName) 53 | FileWriter(file).use { fileWriter -> 54 | contentTemplate.apply(allParams, fileWriter) 55 | } 56 | } else { 57 | val source = File(fileConfig.contentTemplateName) 58 | source.copyTo(file, overwrite = true) 59 | } 60 | } 61 | val resultWriter = StringWriter() 62 | templateConfig.outputs.forEach { outputConfig -> 63 | resultWriter.write(outputConfig.outputTitle) 64 | resultWriter.appendLine() 65 | val allParams = templateConfig.globalParams + outputConfig.templateParams 66 | val contentTemplate = handlebars.compile(outputConfig.contentTemplateName) 67 | 68 | contentTemplate.apply(allParams, resultWriter) 69 | resultWriter.appendLine() 70 | } 71 | return resultWriter.toString() 72 | } 73 | 74 | companion object { 75 | const val NOT_INCLUDE = "{not_include}" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/ShaperConfig.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import org.yaml.snakeyaml.Yaml 8 | import java.io.File 9 | 10 | data class ShaperConfig( 11 | val templateRepositories: List 12 | ) { 13 | companion object { 14 | fun read(): ShaperConfig? { 15 | val userHome = System.getProperty("user.home") 16 | val globalConfigFile = File(userHome, ".shaper/config.yaml") 17 | if (globalConfigFile.exists().not()) return null 18 | 19 | val config: Map = globalConfigFile.inputStream().use { stream -> 20 | val yaml = Yaml() 21 | yaml.load(stream) 22 | } ?: throw IllegalArgumentException("configuration can't be read at $globalConfigFile") 23 | 24 | val repositories = config["repositories"] as? List 25 | 26 | return ShaperConfig( 27 | templateRepositories = repositories?.map { File(it) }.orEmpty() 28 | ) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/StringExt.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | private val camelRegex = "(?<=[a-zA-Z])[A-Z]".toRegex() 8 | private val snakeRegex = "_[a-zA-Z]".toRegex() 9 | 10 | internal fun String.camelToSnakeCase(): String { 11 | return camelRegex.replace(this) { 12 | "_${it.value}" 13 | }.lowercase() 14 | } 15 | 16 | internal fun String.camelToCapitalizeWithWhitespace(): String { 17 | return camelRegex.replace(this) { matchResult -> 18 | " " + matchResult.value.replaceFirstChar { 19 | if (it.isLowerCase()) it.titlecase() else it.toString() 20 | } 21 | } 22 | } 23 | 24 | internal fun String.snakeToLowerCamelCase(): String { 25 | return snakeRegex.replace(this) { 26 | it.value.replace("_", "") 27 | .uppercase() 28 | }.replaceFirstChar { it.lowercase() } 29 | } 30 | 31 | internal fun String.snakeToUpperCamelCase(): String { 32 | return this.snakeToLowerCamelCase().replaceFirstChar { 33 | if (it.isLowerCase()) it.titlecase() else it.toString() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/TemplateClassPathLoader.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import com.github.jknack.handlebars.io.TemplateSource 8 | import com.github.jknack.handlebars.io.URLTemplateLoader 9 | import com.github.jknack.handlebars.io.URLTemplateSource 10 | import java.io.File 11 | import java.io.FileNotFoundException 12 | import java.net.URL 13 | 14 | class TemplateClassPathLoader() : URLTemplateLoader() { 15 | 16 | constructor(prefix: String, suffix: String) : this() { 17 | setPrefix(prefix) 18 | setSuffix(suffix) 19 | } 20 | 21 | override fun sourceAt(location: String?): TemplateSource? { 22 | if (location == null) throw Exception("The uri is required.") 23 | 24 | val templateFile = if (PathLocator.getBaseDir(location) == null) { 25 | File(location) 26 | } else { 27 | File(PathLocator.getAbsolutePath(location)) 28 | } 29 | 30 | return if (templateFile.exists()) { 31 | FileTemplateSource(templateFile, location) 32 | } else { 33 | URLTemplateSource(location, getResource(location) ?: throw FileNotFoundException(location)) 34 | } 35 | } 36 | 37 | override fun getResource(location: String?): URL? { 38 | return this::class.java.classLoader.getResource(location) ?: this::class.java.classLoader.getResource( 39 | resolve( 40 | normalize(location) 41 | ) 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/TemplateConfig.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | data class TemplateConfig( 8 | val globalParams: Map, 9 | val files: List, 10 | val includes: List, 11 | val outputs: List 12 | ) { 13 | data class FileConfig( 14 | val pathTemplate: String, 15 | val contentTemplateName: String, 16 | val templateParams: Map 17 | ) 18 | 19 | data class OutputConfig( 20 | val outputTitle: String, 21 | val contentTemplateName: String, 22 | val templateParams: Map 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/TemplatesRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import java.io.File 8 | 9 | class TemplatesRepository( 10 | private val shaperConfig: ShaperConfig? 11 | ) { 12 | fun getTemplateConfig(templateName: String): TemplateConfig { 13 | var templateConfigFile: File = File(templateName) 14 | if (templateConfigFile.exists().not()) { 15 | templateConfigFile = shaperConfig?.templateRepositories 16 | .orEmpty() 17 | .map { File(it, templateName) } 18 | .firstOrNull { it.exists() } 19 | ?: throw IllegalArgumentException("configuration $templateName not found") 20 | } 21 | 22 | return YamlConfigReader.read(templateConfigFile) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shaper-core/src/main/kotlin/dev/icerock/tools/shaper/core/YamlConfigReader.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import org.yaml.snakeyaml.Yaml 8 | import java.io.File 9 | 10 | object YamlConfigReader { 11 | 12 | fun read(file: File): TemplateConfig { 13 | val absoluteFile = file.absoluteFile 14 | val map = readYaml(absoluteFile) 15 | return buildConfig(map = map, directory = absoluteFile.parentFile) 16 | } 17 | 18 | private fun readYaml(file: File): Map { 19 | return file.inputStream().use { stream -> 20 | val yaml = Yaml() 21 | yaml.load(stream) 22 | } ?: throw IllegalArgumentException("configuration is empty at $file") 23 | } 24 | 25 | private fun buildConfig(map: Map, directory: File): TemplateConfig { 26 | val globalParams = map["globalParams"] as? Map 27 | val files = map["files"] 28 | val includes = map["includes"] as? List 29 | 30 | val filesConfigs: List = collectFileConfig(files, directory) 31 | val outputsConfigs: List = collectOutputConfig( 32 | map["outputs"] as? List>, 33 | directory 34 | ) 35 | val includesConfigs = collectIncludeConfig(includes, directory) 36 | 37 | return TemplateConfig( 38 | globalParams = globalParams.orEmpty(), 39 | files = filesConfigs, 40 | includes = includesConfigs, 41 | outputs = outputsConfigs 42 | ) 43 | } 44 | 45 | private fun collectIncludeConfig(includes: List?, directory: File) = 46 | includes?.map { File(directory, it).absolutePath }.orEmpty() 47 | 48 | private fun collectOutputConfig( 49 | outputs: List>?, 50 | directory: File 51 | ): List { 52 | return outputs?.map { fileMap -> 53 | val templateFile = File(directory, fileMap["contentTemplateName"] as String) 54 | TemplateConfig.OutputConfig( 55 | outputTitle = fileMap["title"] as String, 56 | contentTemplateName = templateFile.path, 57 | templateParams = (fileMap["templateParams"] as? Map).orEmpty() 58 | ) 59 | }.orEmpty() 60 | } 61 | 62 | private fun collectFileConfig(files: Any?, directory: File): List { 63 | val filesList = files as? List> 64 | val filesDirectory = files as? String 65 | return filesList?.map { fileMap -> 66 | val contentTemplateName = fileMap["contentTemplateName"] as String 67 | PathLocator.set(contentTemplateName, directory.absolutePath) 68 | TemplateConfig.FileConfig( 69 | pathTemplate = fileMap["pathTemplate"] as String, 70 | contentTemplateName = contentTemplateName, 71 | templateParams = (fileMap["templateParams"] as? Map).orEmpty() 72 | ) 73 | } ?: if (filesDirectory != null) { 74 | PathLocator.set(filesDirectory, directory.absolutePath) 75 | val filesDir = File(directory, filesDirectory) 76 | filesDir.walkTopDown().filterNot { it.isDirectory }.map { 77 | TemplateConfig.FileConfig( 78 | pathTemplate = it.relativeTo(filesDir).path.removeSuffix(".hbs"), 79 | contentTemplateName = it.path, 80 | templateParams = emptyMap() 81 | ) 82 | }.toList() 83 | } else emptyList() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /shaper-core/src/test/kotlin/dev/icerock/tools/shaper/core/ShaperTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import java.io.File 8 | import java.net.URL 9 | import kotlin.test.Test 10 | import kotlin.test.assertEquals 11 | 12 | class ShaperTest { 13 | 14 | @Test 15 | fun `generation of gradle module`() { 16 | val buildGradleFile = TemplateConfig.FileConfig( 17 | pathTemplate = "build.gradle.kts", 18 | contentTemplateName = "build.gradle.kts.hbs", 19 | templateParams = mapOf("dependencies" to listOf("dep1", "dep2")) 20 | ) 21 | val sourceCodeFile = TemplateConfig.FileConfig( 22 | pathTemplate = "src/main/kotlin/{{dts packageName}}/Test.kt", 23 | contentTemplateName = "Test.kt.hbs", 24 | templateParams = mapOf("packageName" to "dev.icerock.test") 25 | ) 26 | val config = TemplateConfig( 27 | globalParams = mapOf("packageName" to "dev.icerock"), 28 | files = listOf(buildGradleFile, sourceCodeFile), 29 | includes = listOf("includes", "second-includes"), 30 | outputs = emptyList() 31 | ) 32 | val shaper = Shaper(templateConfig = config) 33 | 34 | shaper.execute("build/test") 35 | 36 | assertFileEquals( 37 | expectedResourceName = "build.gradle.kts", 38 | actualFilePath = "build/test/build.gradle.kts" 39 | ) 40 | assertFileEquals( 41 | expectedResourceName = "Test.kt", 42 | actualFilePath = "build/test/src/main/kotlin/dev/icerock/test/Test.kt" 43 | ) 44 | } 45 | 46 | @Test 47 | fun `with yaml test`() { 48 | val result = YamlConfigReader.read(File("src/test/resources/configForShaper.yaml")) 49 | 50 | val shaper = Shaper(templateConfig = result) 51 | shaper.execute("build/test") 52 | 53 | assertFileEquals( 54 | expectedResourceName = "sub/build.gradle.kts", 55 | actualFilePath = "build/test/sub/build.gradle.kts" 56 | ) 57 | 58 | assertFileEquals( 59 | expectedResourceName = "build.gradle.kts", 60 | actualFilePath = "build/test/build.gradle.kts" 61 | ) 62 | } 63 | 64 | private fun assertFileEquals(expectedResourceName: String, actualFilePath: String) { 65 | val expectedUrl: URL = this::class.java.classLoader.getResource(expectedResourceName) 66 | val actualFile = File(actualFilePath) 67 | 68 | assertEquals( 69 | expected = expectedUrl.readText(), 70 | actual = actualFile.readText() 71 | ) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /shaper-core/src/test/kotlin/dev/icerock/tools/shaper/core/StringExtTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | 6 | package dev.icerock.tools.shaper.core 7 | 8 | import kotlin.test.Test 9 | import kotlin.test.assertEquals 10 | 11 | class StringExtTest { 12 | @Test 13 | fun `camel to snake case cases`() { 14 | assertEquals( 15 | expected = "test_words_in_camel", 16 | actual = "testWordsInCamel".camelToSnakeCase() 17 | ) 18 | assertEquals( 19 | expected = "test_words_in_camel", 20 | actual = "TestWordsInCamel".camelToSnakeCase() 21 | ) 22 | } 23 | 24 | @Test 25 | fun `snake to upper camel case cases`() { 26 | assertEquals( 27 | expected = "TestWordsInCamel", 28 | actual = "test_words_in_camel".snakeToUpperCamelCase() 29 | ) 30 | } 31 | 32 | @Test 33 | fun `snake to lower camel case cases`() { 34 | assertEquals( 35 | expected = "testWordsInCamel", 36 | actual = "test_words_in_camel".snakeToLowerCamelCase() 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /shaper-core/src/test/kotlin/dev/icerock/tools/shaper/core/YamlReaderTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. 3 | */ 4 | 5 | package dev.icerock.tools.shaper.core 6 | 7 | import org.hamcrest.CoreMatchers.containsString 8 | import org.hamcrest.MatcherAssert.assertThat 9 | import java.io.File 10 | import kotlin.test.Test 11 | import kotlin.test.assertEquals 12 | 13 | class YamlReaderTest { 14 | @Test 15 | fun `read yaml with files`() { 16 | val result = YamlConfigReader.read(File("src/test/resources/config.yaml")) 17 | val array = result.globalParams["androidMainDeps"] as ArrayList 18 | val files = result.files.sortedBy { it.pathTemplate } 19 | val includes = result.includes 20 | val outputs = result.outputs 21 | 22 | assertEquals("dev.icerock.shaper.sample.kmm.auth", result.globalParams["packageName"]) 23 | assertEquals("Auth", result.globalParams["moduleName"]) 24 | assertEquals("lifecycle", array[0]) 25 | assertEquals("recyclerView", array[1]) 26 | assertEquals("build.gradle.kts", files[0].pathTemplate) 27 | assertThat( 28 | files[0].contentTemplateName, 29 | containsString("kmm-module/build.gradle.kts.hbs") 30 | ) 31 | assertEquals(0, files[0].templateParams.count()) 32 | assertEquals("src/commonMain/kotlin/{{dts packageName}}/di/{{moduleName}}Factory.kt", files[1].pathTemplate) 33 | assertThat( 34 | files[1].contentTemplateName, 35 | containsString("kmm-module/Factory.kt.hbs") 36 | ) 37 | assertEquals(0, files[1].templateParams.count()) 38 | assertThat( 39 | includes[0], 40 | containsString("shaper/shaper-core/src/test/resources/includes") 41 | ) 42 | assertEquals("=== Tips for feature setup ===", outputs[0].outputTitle) 43 | assertThat( 44 | outputs[0].contentTemplateName, 45 | containsString("kmm-module/console.output.hbs") 46 | ) 47 | assertEquals(0, outputs[0].templateParams.count()) 48 | } 49 | 50 | @Test 51 | fun `read yaml with one directory`() { 52 | val result = YamlConfigReader.read(File("src/test/resources/configWithoutFiles.yaml")) 53 | val array = result.globalParams["androidMainDeps"] as ArrayList 54 | val files = result.files.sortedBy { it.pathTemplate } 55 | val includes = result.includes 56 | val outputs = result.outputs 57 | 58 | assertEquals("dev.icerock.shaper.sample.kmm.auth", result.globalParams["packageName"]) 59 | assertEquals("Auth", result.globalParams["moduleName"]) 60 | assertEquals("lifecycle", array[0]) 61 | assertEquals("recyclerView", array[1]) 62 | assertEquals("1yaml-test", files[0].pathTemplate) 63 | assertThat( 64 | files[0].contentTemplateName, 65 | containsString("shaper/shaper-core/src/test/resources/files/1yaml-test.hbs") 66 | ) 67 | assertEquals(0, files[0].templateParams.count()) 68 | assertEquals("2yaml-test", files[1].pathTemplate) 69 | assertThat( 70 | files[1].contentTemplateName, 71 | containsString("shaper/shaper-core/src/test/resources/files/2yaml-test.hbs") 72 | ) 73 | assertEquals(0, files[1].templateParams.count()) 74 | assertThat( 75 | includes[0], 76 | containsString("shaper/shaper-core/src/test/resources/includes") 77 | ) 78 | assertThat( 79 | includes[1], 80 | containsString("shaper/shaper-core/src/test/resources/not-includes/sub-includes") 81 | ) 82 | assertEquals("=== Tips for feature setup ===", outputs[0].outputTitle) 83 | assertThat( 84 | outputs[0].contentTemplateName, 85 | containsString("shaper/shaper-core/src/test/resources/kmm-module/console.output.hbs") 86 | ) 87 | assertEquals(0, outputs[0].templateParams.count()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/Test.kt: -------------------------------------------------------------------------------- 1 | package dev.icerock.test 2 | 3 | class Test { 4 | fun hello() { 5 | println("Hi Developer!") 6 | println("Test") 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/Test.kt.hbs: -------------------------------------------------------------------------------- 1 | package {{packageName}} 2 | 3 | class Test { 4 | fun hello() { 5 | println("{{> part2}} {{> part name="Developer"}}!") 6 | println("{{> second-psrt arg="Test"}}") 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.jvm") version "1.4.30" 3 | } 4 | 5 | dependencies { 6 | implementation("dep1") 7 | implementation("dep2") 8 | 9 | } 10 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/build.gradle.kts.hbs: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.jvm") version "1.4.30" 3 | } 4 | 5 | dependencies { 6 | {{#each dependencies}} implementation("{{this}}") 7 | {{/each}} 8 | } 9 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/config.yaml: -------------------------------------------------------------------------------- 1 | globalParams: 2 | packageName: dev.icerock.shaper.sample.kmm.auth 3 | moduleName: Auth 4 | androidMainDeps: 5 | - lifecycle 6 | - recyclerView 7 | 8 | files: 9 | - pathTemplate: 'build.gradle.kts' 10 | contentTemplateName: kmm-module/build.gradle.kts.hbs 11 | - pathTemplate: 'src/commonMain/kotlin/{{dts packageName}}/di/{{moduleName}}Factory.kt' 12 | contentTemplateName: kmm-module/Factory.kt.hbs 13 | outputs: 14 | - title: '=== Tips for feature setup ===' 15 | contentTemplateName: kmm-module/console.output.hbs 16 | includes: 17 | - includes 18 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/configForShaper.yaml: -------------------------------------------------------------------------------- 1 | globalParams: 2 | packageName: dev.icerock.shaper.sample.kmm.auth 3 | moduleName: Auth 4 | androidMainDeps: 5 | - lifecycle 6 | - recyclerView 7 | dependencies: 8 | - dep1 9 | - dep2 10 | files: 11 | - pathTemplate: 'build.gradle.kts' 12 | contentTemplateName: build.gradle.kts.hbs 13 | - pathTemplate: 'sub/build.gradle.kts' 14 | contentTemplateName: sub/build.gradle.kts.hbs 15 | includes: 16 | - includes 17 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/configWithoutFiles.yaml: -------------------------------------------------------------------------------- 1 | globalParams: 2 | packageName: dev.icerock.shaper.sample.kmm.auth 3 | moduleName: Auth 4 | androidMainDeps: 5 | - lifecycle 6 | - recyclerView 7 | 8 | files: files/ 9 | outputs: 10 | - title: '=== Tips for feature setup ===' 11 | contentTemplateName: kmm-module/console.output.hbs 12 | includes: 13 | - includes 14 | - not-includes/sub-includes 15 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/files/1yaml-test.hbs: -------------------------------------------------------------------------------- 1 | Test1 2 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/files/2yaml-test.hbs: -------------------------------------------------------------------------------- 1 | Test2 2 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/includes/part.hbs: -------------------------------------------------------------------------------- 1 | {{name~}} 2 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/includes/partials/part2.hbs: -------------------------------------------------------------------------------- 1 | Hi{{#noop}}{{/noop~}} 2 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/second-includes/second-psrt.hbs: -------------------------------------------------------------------------------- 1 | {{arg~}} 2 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/sub/build.gradle.kts: -------------------------------------------------------------------------------- 1 | package sub 2 | 3 | plugins { 4 | id("org.jetbrains.kotlin.jvm") version "1.4.20" 5 | } 6 | # Sub-file {{test}} 7 | dependencies { 8 | implements("dep1") 9 | } 10 | -------------------------------------------------------------------------------- /shaper-core/src/test/resources/sub/build.gradle.kts.hbs: -------------------------------------------------------------------------------- 1 | package sub 2 | 3 | plugins { 4 | id("org.jetbrains.kotlin.jvm") version "1.4.20" 5 | } 6 | # Sub-file {{{{raw}}}}{{test}}{{{{/raw}}}} 7 | dependencies { 8 | {{~#if (or (eq "test1" "test") (eq "test2" "test2"))}} 9 | {{~#if (and (eq "test" "test") (eq "test2" "test2"))}} 10 | implements("dep1") 11 | {{/if~}} 12 | {{/if~}} 13 | {{~#if (or (eq "test1" "test") (eq "test" "test2"))}} 14 | implements("dep2") 15 | {{~/if~}} 16 | } 17 | --------------------------------------------------------------------------------