├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── redirect.md │ └── template.md └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg ├── pre-commit └── skip.js ├── .lintstagedrc.json ├── .npmrc ├── .prettierrc.json ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── REUSE.toml ├── generators └── app │ └── index.js ├── package-lock.json ├── package.json └── test ├── README.md ├── basic.js ├── generator-ui5-test-v4 ├── generators │ ├── app │ │ ├── index.js │ │ └── templates │ │ │ └── testFileA.md │ └── hiddenSub │ │ ├── index.js │ │ └── templates │ │ └── testFileB.md └── package.json └── generator-ui5-test-v5 ├── generators ├── app │ ├── index.js │ └── templates │ │ └── testFileC.md └── hiddenSub │ ├── index.js │ └── templates │ └── testFileD.md └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | # We recommend you to keep these unchanged 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | quote_type = double 14 | 15 | # Change these settings to your own preference 16 | indent_style = tab 17 | tab_width = 2 18 | 19 | [*.{yaml,yml,md,json}] 20 | indent_size = 2 21 | indent_style = space 22 | 23 | [*.md] 24 | trim_trailing_whitespace = false 25 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | plugin-generators/ 2 | test/ 3 | node_modules/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "node": true, 5 | "es6": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2022, 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | "block-scoped-var": 1, 13 | "keyword-spacing": 2, 14 | "space-unary-ops": 2, 15 | "camelcase": 1, 16 | "no-warning-comments": 1, 17 | "no-debugger": 2, 18 | "default-case": 1, 19 | "no-unused-vars": 2, 20 | "no-trailing-spaces": 2, 21 | "semi": [1, "always"], 22 | "quotes": [1, "double"], 23 | "key-spacing": [ 24 | 1, 25 | { 26 | "beforeColon": false 27 | } 28 | ], 29 | "comma-spacing": [ 30 | 1, 31 | { 32 | "before": false, 33 | "after": true 34 | } 35 | ], 36 | "no-shadow": 2, 37 | "no-irregular-whitespace": 2 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/redirect.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Issue for an Easy-UI5 plugin 3 | about: You can find links to the plugins here. Please open the issue there if incorrect files were generated or the project doesn't run as expected. 4 | assignees: IObert 5 | --- 6 | 7 | I'm sorry to hear that easy-ui5 generated broken files. Please report this issue directly in the repository for the plugin that you used. You can find all plugins in the [UI5 Community organization](https://github.com/ui5-community/). 8 | 9 | Here are a few direct links to some plugins: 10 | 11 | - [UI5 Project](https://github.com/ui5-community/generator-ui5-project) 12 | - [UI5 Library](https://github.com/ui5-community/generator-ui5-library) 13 | - [UI5 Control](https://github.com/ui5-community/generator-ui5-control) 14 | - [SAPUI5 Fiori Launchpad Plugin](https://github.com/ui5-community/generator-ui5-flp-plugin) 15 | - [UI5 App](https://github.com/ui5-community/generator-ui5-app) 16 | - [UI5 TypeScript App](https://github.com/ui5-community/generator-ui5-ts-app) 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Issue for the Easy-UI5 shell 3 | about: Only use this template if there is a problem during the prompting phase or with the file generation. Use the other template if incorrect files were generated. 4 | --- 5 | 6 | ## Easy-Ui5 version: 7 | 8 | > Run `yo easy-ui5 --plugins` to print all relevant version strings 9 | 10 | ## OS/version: 11 | 12 | ## Browser/version (+device/version): 13 | 14 | ## Steps to reproduce the problem: 15 | 16 | 1. Step 1 17 | 2. Step 2 18 | 3. Step 3 19 | 20 | ## What is the expected result? 21 | 22 | ## What happens instead? 23 | 24 | ## Any other information? (attach screenshot if possible) 25 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Run test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | run-test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | version: [18, 20] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-node@v4 18 | with: 19 | node-version: ${{ matrix.version }} 20 | registry-url: https://registry.npmjs.org/ 21 | 22 | - run: git config --global user.email "cicd@example.com" && git config --global user.name "Your Name" 23 | - run: npm install 24 | - run: npm test 25 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Create Release 4 | 5 | on: 6 | push: 7 | tags: 8 | - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 9 | 10 | jobs: 11 | publish-npm: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v1 16 | with: 17 | node-version: 18 18 | registry-url: https://registry.npmjs.org/ 19 | 20 | - run: npm install 21 | - run: npm test 22 | - run: npm publish 23 | env: 24 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 25 | 26 | create-release: 27 | needs: publish-npm 28 | name: Create Release 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout code 32 | uses: actions/checkout@v2 33 | - name: Create Release 34 | id: create_release 35 | uses: actions/create-release@v1 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | with: 39 | tag_name: ${{ github.ref }} 40 | release_name: Release ${{ github.ref }} 41 | draft: false 42 | prerelease: false 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore node_modules 2 | node_modules/ 3 | 4 | # Ignore plugins 5 | /plugins/* 6 | 7 | # Ignore test files 8 | /test/_/* 9 | 10 | # Ignore PNPM and YARN lock files 11 | pnpm-lock.yaml 12 | yarn.lock 13 | 14 | # Ignore Mac files 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run hooks:commit-msg -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run hooks:pre-commit -------------------------------------------------------------------------------- /.husky/skip.js: -------------------------------------------------------------------------------- 1 | if (process.env.HUSKY_SKIP) { 2 | process.exit(0); 3 | } else { 4 | process.exit(1); 5 | } 6 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generators/**/*.js": "eslint" 3 | } 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": false, 3 | "printWidth": 200, 4 | "endOfLine": "lf", 5 | "tabWidth": 2, 6 | "useTabs": true, 7 | "overrides": [ 8 | { 9 | "files": ["*.yaml", "*.yml", "*.md", "*.json"], 10 | "options": { 11 | "useTabs": false, 12 | "tabWidth": 2 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "args": ["../", "--ghAuthToken=", "--ghOrg=ui5-community"], 9 | "console": "integratedTerminal", 10 | "internalConsoleOptions": "neverOpen", 11 | "name": "Yeoman generator", 12 | "program": "~/.homebrew/lib/node_modules/yo/lib/cli.js", 13 | "request": "launch", 14 | "skipFiles": ["/**"], 15 | "type": "pwa-node", 16 | "cwd": "${workspaceFolder}/test" 17 | }, 18 | { 19 | "args": ["../", "--ghAuthToken=", "--ghOrg=ui5-community", "--addGhOrg=yeoman"], 20 | "console": "integratedTerminal", 21 | "internalConsoleOptions": "neverOpen", 22 | "name": "Yeoman generator (addGhOrg)", 23 | "program": "~/.homebrew/lib/node_modules/yo/lib/cli.js", 24 | "request": "launch", 25 | "skipFiles": ["/**"], 26 | "type": "pwa-node", 27 | "cwd": "${workspaceFolder}/test" 28 | }, 29 | { 30 | "args": ["../", "ts-app", "--ghAuthToken=", "--ghOrg=ui5-community", "--addGhOrg=yeoman"], 31 | "console": "integratedTerminal", 32 | "internalConsoleOptions": "neverOpen", 33 | "name": "Yeoman generator ts-app (addGhOrg)", 34 | "program": "~/.homebrew/lib/node_modules/yo/lib/cli.js", 35 | "request": "launch", 36 | "skipFiles": ["/**"], 37 | "type": "pwa-node", 38 | "cwd": "${workspaceFolder}/test" 39 | }, 40 | { 41 | "type": "node", 42 | "request": "launch", 43 | "name": "Mocha All", 44 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 45 | "args": ["--timeout", "999999", "--colors", "${workspaceFolder}/test"], 46 | "console": "integratedTerminal", 47 | "internalConsoleOptions": "neverOpen" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [3.8.1](https://github.com/SAP/generator-easy-ui5/compare/v3.7.0...v3.8.1) (2024-03-09) 2 | 3 | ### Bug Fixes 4 | 5 | - ensure proper support for corporate proxies ([#143](https://github.com/SAP/generator-easy-ui5/issues/143)) ([c99ab14](https://github.com/SAP/generator-easy-ui5/commit/c99ab14049e3b9dbe05fdca70674d1717e1544bd)), closes [#142](https://github.com/SAP/generator-easy-ui5/issues/142) 6 | 7 | ### Features 8 | 9 | - support nesting of generators ([#141](https://github.com/SAP/generator-easy-ui5/issues/141)) ([afd9173](https://github.com/SAP/generator-easy-ui5/commit/afd9173ad8b2450281b00147d3b071d3bb989656)) 10 | 11 | # [3.7.0](https://github.com/SAP/generator-easy-ui5/compare/v3.5.1...v3.7.0) (2023-11-29) 12 | 13 | ### Bug Fixes 14 | 15 | - also include embedded subgens ([86912a8](https://github.com/SAP/generator-easy-ui5/commit/86912a8cb4736ea8950696de14ce20e87056246f)) 16 | - await Environment.lookup ([#132](https://github.com/SAP/generator-easy-ui5/issues/132)) ([#133](https://github.com/SAP/generator-easy-ui5/issues/133)) ([9f614d2](https://github.com/SAP/generator-easy-ui5/commit/9f614d21d8f22019de579aa03406806cb533d5a6)) 17 | - ignore execution of scripts to avoid husky run ([#129](https://github.com/SAP/generator-easy-ui5/issues/129)) ([3da8d22](https://github.com/SAP/generator-easy-ui5/commit/3da8d221659fe80bfd8f0a5d10275d2736ee9125)) 18 | - just use npm to install subgen dependencies ([#130](https://github.com/SAP/generator-easy-ui5/issues/130)) ([6697913](https://github.com/SAP/generator-easy-ui5/commit/6697913e03b2a188e2389ab777bb751d02fbc59b)) 19 | - omit devDependencies for plugin generators ([#125](https://github.com/SAP/generator-easy-ui5/issues/125)) ([ffda0c5](https://github.com/SAP/generator-easy-ui5/commit/ffda0c5048543bdb29956bdde01730af4fee74fb)) 20 | - removal of the outdated templates must not fail ([#126](https://github.com/SAP/generator-easy-ui5/issues/126)) ([f196dac](https://github.com/SAP/generator-easy-ui5/commit/f196dac643a947c26081437b08673151f6536523)) 21 | - sort subgen list, remove threshold, fix permission issues ([#123](https://github.com/SAP/generator-easy-ui5/issues/123)) ([c5dd321](https://github.com/SAP/generator-easy-ui5/commit/c5dd3218a26870a61722a9675a81831cc8af50e5)), closes [#122](https://github.com/SAP/generator-easy-ui5/issues/122) [#118](https://github.com/SAP/generator-easy-ui5/issues/118) [#117](https://github.com/SAP/generator-easy-ui5/issues/117) [#111](https://github.com/SAP/generator-easy-ui5/issues/111) 22 | 23 | ### Features 24 | 25 | - support for coporate proxy / switch to esm / update deps ([#124](https://github.com/SAP/generator-easy-ui5/issues/124)) ([bf95254](https://github.com/SAP/generator-easy-ui5/commit/bf95254694025f3d22e5af37bb610ba7d2a0e215)), closes [#79](https://github.com/SAP/generator-easy-ui5/issues/79) 26 | - update the min nodejs version to 18 ([06385f4](https://github.com/SAP/generator-easy-ui5/commit/06385f44d4de6b31f3cb25e6cd2a1bd74117b685)) 27 | 28 | ## [3.5.1](https://github.com/SAP/generator-easy-ui5/compare/v3.2.0...v3.5.1) (2022-09-10) 29 | 30 | ### Bug Fixes 31 | 32 | - enable for Yeoman UI usage ([03c2e38](https://github.com/SAP/generator-easy-ui5/commit/03c2e38af4eebe108d6076710b74d8aaf7c31d8d)) 33 | - **postinstall:** allow usage of NPM config for ghAuthToken to avoid rate limit ([#104](https://github.com/SAP/generator-easy-ui5/issues/104)) ([371d7fb](https://github.com/SAP/generator-easy-ui5/commit/371d7fbc82b8a59cef896197b99239b505b3cd90)), closes [#100](https://github.com/SAP/generator-easy-ui5/issues/100) 34 | - **postinstall:** avoid non-existing node_modules ([#98](https://github.com/SAP/generator-easy-ui5/issues/98)) ([f93c7c9](https://github.com/SAP/generator-easy-ui5/commit/f93c7c99b9e5df7af801a085afe72be85e462007)) 35 | - usage of proper gh org for logging and download ([#94](https://github.com/SAP/generator-easy-ui5/issues/94)) ([a88d4db](https://github.com/SAP/generator-easy-ui5/commit/a88d4dbb0a1f5ed3cf60f7ed0297c651222a83fb)) 36 | - use the org name of the selected generator for downloading the corresponding repo ([d9ca4dc](https://github.com/SAP/generator-easy-ui5/commit/d9ca4dc3170e0507199799d2e14db327cba4d871)) 37 | 38 | ### Features 39 | 40 | - allow ghAuthToken as NPM configuration ([#103](https://github.com/SAP/generator-easy-ui5/issues/103)) ([5e3d928](https://github.com/SAP/generator-easy-ui5/commit/5e3d92807e881d0ade80251bc8cc1bdde85142be)) 41 | - allow to run easy-ui5 embedded ([#99](https://github.com/SAP/generator-easy-ui5/issues/99)) ([f4952c4](https://github.com/SAP/generator-easy-ui5/commit/f4952c442c9563a51c48b8edc25843f097fce4c4)) 42 | - offline support, generator from repo, bestofui5 test ([#110](https://github.com/SAP/generator-easy-ui5/issues/110)) ([70e9012](https://github.com/SAP/generator-easy-ui5/commit/70e9012d85bee0c2ac2dadfe3ca9cac3d297ce84)) 43 | 44 | # [3.2.0](https://github.com/SAP/generator-easy-ui5/compare/v3.1.5...v3.2.0) (2022-02-03) 45 | 46 | ### Bug Fixes 47 | 48 | - freeze version of colors.js in package.json ([#85](https://github.com/SAP/generator-easy-ui5/issues/85)) ([6b497b6](https://github.com/SAP/generator-easy-ui5/commit/6b497b6c05748b8f67617c6399a11f8e8d850d48)) 49 | - use homedir for plugin-generators avoid EACCESS ([e326676](https://github.com/SAP/generator-easy-ui5/commit/e326676458f439f9ac01498381059229a897fa61)), closes [#84](https://github.com/SAP/generator-easy-ui5/issues/84) 50 | 51 | ### Features 52 | 53 | - Add support for user repositories ([6b7efa6](https://github.com/SAP/generator-easy-ui5/commit/6b7efa63414c31d76a53ee1b069557c527077f39)) 54 | - support additional GH org via NPM config ([0d33197](https://github.com/SAP/generator-easy-ui5/commit/0d33197098e010858d1ea7a0e4b172d5d6a5aa22)) 55 | 56 | ## [3.1.5](https://github.com/SAP/generator-easy-ui5/compare/v3.1.4...v3.1.5) (2022-01-10) 57 | 58 | ### Bug Fixes 59 | 60 | - re-enable sub-generator update check ([c464bd1](https://github.com/SAP/generator-easy-ui5/commit/c464bd11d2cf32006fd7f42ea8f15a736cb10271)) 61 | 62 | ## [3.1.4](https://github.com/SAP/generator-easy-ui5/compare/v3.1.3...v3.1.4) (2021-12-07) 63 | 64 | ### Bug Fixes 65 | 66 | - disable autoinstall of Yeoman 5.x ([de6fcaf](https://github.com/SAP/generator-easy-ui5/commit/de6fcafd164734a9e3fbbab599b7376a49fffe89)) 67 | 68 | ## [3.1.3](https://github.com/SAP/generator-easy-ui5/compare/v3.1.2...v3.1.3) (2021-11-25) 69 | 70 | ## [3.1.2](https://github.com/SAP/generator-easy-ui5/compare/v3.1.1...v3.1.2) (2021-11-23) 71 | 72 | ## [3.1.1](https://github.com/SAP/generator-easy-ui5/compare/v3.1.0...v3.1.1) (2021-11-23) 73 | 74 | # [3.1.0](https://github.com/SAP/generator-easy-ui5/compare/v3.0.3...v3.1.0) (2021-11-23) 75 | 76 | ## [3.0.3](https://github.com/SAP/generator-easy-ui5/compare/v3.0.2...v3.0.3) (2021-11-15) 77 | 78 | ## [3.0.2](https://github.com/SAP/generator-easy-ui5/compare/v3.0.1...v3.0.2) (2021-11-15) 79 | 80 | ## [3.0.1](https://github.com/SAP/generator-easy-ui5/compare/v2.4.6...v3.0.1) (2021-05-10) 81 | 82 | ## [2.4.6](https://github.com/SAP/generator-easy-ui5/compare/2.4.6...v2.4.6) (2021-02-10) 83 | 84 | ### Bug Fixes 85 | 86 | - uiveri5 reporters ([f2e2d6d](https://github.com/SAP/generator-easy-ui5/commit/f2e2d6dae71dc580ee0d15c42baafe06ae983d4e)) 87 | 88 | ## [2.4.5](https://github.com/SAP/generator-easy-ui5/compare/2.4.5...v2.4.5) (2021-01-29) 89 | 90 | ### Bug Fixes 91 | 92 | - opa test namespace and folder ([fb166b7](https://github.com/SAP/generator-easy-ui5/commit/fb166b7df06b7cd465366726ed484890ad389d96)) 93 | 94 | ## [2.4.4](https://github.com/SAP/generator-easy-ui5/compare/2.4.4...v2.4.4) (2021-01-25) 95 | 96 | ## [2.4.3](https://github.com/SAP/generator-easy-ui5/compare/2.4.3...v2.4.3) (2021-01-08) 97 | 98 | ## [2.4.2](https://github.com/SAP/generator-easy-ui5/compare/2.4.2...v2.4.2) (2020-12-18) 99 | 100 | ## [2.4.1](https://github.com/SAP/generator-easy-ui5/compare/v2.4.0...v2.4.1) (2020-12-18) 101 | 102 | # [2.4.0](https://github.com/SAP/generator-easy-ui5/compare/v2.3.0...v2.4.0) (2020-12-10) 103 | 104 | ### Features 105 | 106 | - add wdi5 as test framework ([e63ce2e](https://github.com/SAP/generator-easy-ui5/commit/e63ce2eb2ed9cc23840cb303d01fc4e7d23f2c11)) 107 | 108 | # [2.3.0](https://github.com/SAP/generator-easy-ui5/compare/v2.2.4...v2.3.0) (2020-11-25) 109 | 110 | ## [2.2.4](https://github.com/SAP/generator-easy-ui5/compare/v2.2.3...v2.2.4) (2020-11-10) 111 | 112 | ## [2.2.3](https://github.com/SAP/generator-easy-ui5/compare/v2.2.2...v2.2.3) (2020-11-05) 113 | 114 | ## [2.2.2](https://github.com/SAP/generator-easy-ui5/compare/v2.2.1...v2.2.2) (2020-10-28) 115 | 116 | ## [2.2.1](https://github.com/SAP/generator-easy-ui5/compare/v2.2.0...v2.2.1) (2020-10-23) 117 | 118 | # [2.2.0](https://github.com/SAP/generator-easy-ui5/compare/v2.1.7...v2.2.0) (2020-10-16) 119 | 120 | ## [2.1.7](https://github.com/SAP/generator-easy-ui5/compare/v2.1.6...v2.1.7) (2020-09-10) 121 | 122 | ## [2.1.6](https://github.com/SAP/generator-easy-ui5/compare/v2.1.5...v2.1.6) (2020-08-24) 123 | 124 | ## [2.1.5](https://github.com/SAP/generator-easy-ui5/compare/v2.1.4...v2.1.5) (2020-08-24) 125 | 126 | ## [2.1.4](https://github.com/SAP/generator-easy-ui5/compare/v2.1.3...v2.1.4) (2020-08-06) 127 | 128 | ## [2.1.3](https://github.com/SAP/generator-easy-ui5/compare/v2.1.2...v2.1.3) (2020-08-03) 129 | 130 | ## [2.1.2](https://github.com/SAP/generator-easy-ui5/compare/v2.1.1...v2.1.2) (2020-06-29) 131 | 132 | ## [2.1.1](https://github.com/SAP/generator-easy-ui5/compare/v2.1.0...v2.1.1) (2020-06-18) 133 | 134 | # [2.1.0](https://github.com/SAP/generator-easy-ui5/compare/v2.0.1...v2.1.0) (2020-06-15) 135 | 136 | ## [2.0.1](https://github.com/SAP/generator-easy-ui5/compare/v2.0.0...v2.0.1) (2020-06-09) 137 | 138 | # [2.0.0](https://github.com/SAP/generator-easy-ui5/compare/v1.3.7...v2.0.0) (2020-06-08) 139 | 140 | ## [1.3.7](https://github.com/SAP/generator-easy-ui5/compare/v1.3.6...v1.3.7) (2020-05-06) 141 | 142 | ## [1.3.6](https://github.com/SAP/generator-easy-ui5/compare/v1.3.5...v1.3.6) (2020-05-06) 143 | 144 | ## [1.3.5](https://github.com/SAP/generator-easy-ui5/compare/v1.3.4...v1.3.5) (2020-04-30) 145 | 146 | ## [1.3.4](https://github.com/SAP/generator-easy-ui5/compare/v1.3.3...v1.3.4) (2020-04-29) 147 | 148 | ## [1.3.3](https://github.com/SAP/generator-easy-ui5/compare/v1.3.2...v1.3.3) (2020-04-14) 149 | 150 | ## [1.3.2](https://github.com/SAP/generator-easy-ui5/compare/v1.3.1...v1.3.2) (2020-04-02) 151 | 152 | ### Bug Fixes 153 | 154 | - **app:** Fix lint errors ([c4df165](https://github.com/SAP/generator-easy-ui5/commit/c4df165e35b319aedfc932ac37d2593c0fa5e526)) 155 | - **formatter:** Fix formatter ([4f637c8](https://github.com/SAP/generator-easy-ui5/commit/4f637c81e2a916a0067d36f8d214a447a7a62212)) 156 | 157 | ### Features 158 | 159 | - **BaseController:** Adding the BaseController and formatter ([e51a66b](https://github.com/SAP/generator-easy-ui5/commit/e51a66bfcd8dea90fd27c7684264e1202bde3e47)) 160 | - **BaseController:** Fix lint and activate install again after creation ([8697b1b](https://github.com/SAP/generator-easy-ui5/commit/8697b1bdbcc3f82bb8eb5f0e9e750b37f0d44d8f)) 161 | 162 | ## [1.3.1](https://github.com/SAP/generator-easy-ui5/compare/v1.3.0...v1.3.1) (2020-03-20) 163 | 164 | # [1.3.0](https://github.com/SAP/generator-easy-ui5/compare/v1.2.0...v1.3.0) (2020-03-11) 165 | 166 | # [1.2.0](https://github.com/SAP/generator-easy-ui5/compare/v1.1.1...v1.2.0) (2020-02-13) 167 | 168 | ## [1.1.1](https://github.com/SAP/generator-easy-ui5/compare/v1.0.3...v1.1.1) (2019-12-13) 169 | 170 | ## [1.0.3](https://github.com/SAP/generator-easy-ui5/compare/v1.0.2...v1.0.3) (2019-12-04) 171 | 172 | ## [1.0.2](https://github.com/SAP/generator-easy-ui5/compare/v1.0.1...v1.0.2) (2019-11-08) 173 | 174 | ## [1.0.1](https://github.com/SAP/generator-easy-ui5/compare/v1.0.0...v1.0.1) (2019-11-06) 175 | 176 | # [1.0.0](https://github.com/SAP/generator-easy-ui5/compare/v0.3.4...v1.0.0) (2019-10-30) 177 | 178 | ## [0.3.4](https://github.com/SAP/generator-easy-ui5/compare/v0.3.3...v0.3.4) (2019-10-24) 179 | 180 | ## [0.3.3](https://github.com/SAP/generator-easy-ui5/compare/v0.3.2...v0.3.3) (2019-10-23) 181 | 182 | ## [0.3.2](https://github.com/SAP/generator-easy-ui5/compare/v0.3.1...v0.3.2) (2019-10-23) 183 | 184 | ## [0.3.1](https://github.com/SAP/generator-easy-ui5/compare/v2.0.2...v0.3.1) (2019-10-23) 185 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Easy UI5 2 | 3 | We welcome any type of contribution (code contributions, pull requests, issues) to this easy-ui5 generator equally. 4 | 5 | ## Analyze Issues 6 | 7 | Analyzing issue reports can be a lot of effort. Any help is welcome! Go to the Github [issue tracker](https://github.com/SAP/generator-easy-ui5/issues?q=is%3Aopen) and find an open issue which needs additional work or a bugfix. 8 | 9 | Additional work may be further information, or a minimized example or gist, or it might be a hint that helps understanding the issue. Maybe you can even find and contribute a bugfix? 10 | 11 | ## Report an Issue 12 | 13 | If you find a bug - behavior of easy-ui5 code contradicting its specification - you are welcome to report it. We can only handle well-reported, actual bugs, so please follow the guidelines below before reporting the issue to the Github [issue tracker](https://github.com/SAP/generator-easy-ui5/issues) 14 | 15 | ### Quick Checklist for Bug Reports 16 | 17 | Issue report checklist: 18 | 19 | - Real, current bug 20 | - No duplicate 21 | - Reproducible 22 | - Good summary 23 | - Well-documented 24 | - Minimal example 25 | - Use the [template](https://github.com/SAP/generator-easy-ui5/issues/new) 26 | 27 | ### Issue Reporting Disclaimer 28 | 29 | We want to improve the quality of easy-ui5 and good bug reports are welcome! But our capacity is limited, so we cannot handle questions or consultation requests and we cannot afford to ask for required details. So we reserve the right to close or to not process insufficient bug reports in favor of those which are very cleanly documented and easy to reproduce. Even though we would like to solve each well-documented issue, there is always the chance that it won't happen - remember: easy-ui5 is Open Source and comes without warranty. 30 | 31 | Bug report analysis support is very welcome! (e.g. pre-analysis or proposing solutions) 32 | 33 | ## Contributing Code 34 | 35 | ### General Remarks 36 | 37 | You are welcome to contribute code to the easy-ui5 generator in order to fix bugs or to implement new features. 38 | 39 | My **overall vision for this project** is to support basic scaffolding for multiple runtimes/deployment scenarios. I don’t want to go down the road of providing different templates (List Report, Master-detail page etc.) because this will increase the complexity of maintaining the project and makes it more complicated to use. It makes sense to have these features but this should be done by a dedicated development team then. When you look for these features, please check out the [SAP Fiori Tools - Extension Pack](https://marketplace.visualstudio.com/items?itemName=SAPSE.sap-ux-fiori-tools-extension-pack) actually provides a generator that can be used to create Fiori Elements apps that can be published to ABAP systems and SAP BTP. The team is working hard to add more templates in the future. 40 | 41 | **Not all proposed contributions can be accepted**. Some features may just fit a third-party add-on better. The code must match the overall direction of the easy-ui5 generator and improve it. So there should be some "bang for the byte". For most bug fixes this is a given, but a major feature implementation first needs to be discussed with one of the committers. Possibly, one who touched the related code or module recently. The more effort you invest, the better you should clarify in advance whether the contribution will match the project's direction. The best way would be to just open an enhancement ticket in the issue tracker to discuss the feature you plan to implement (make it clear that you intend to contribute). We will then forward the proposal to the respective code owner. This avoids disappointment. 42 | 43 | ### Contributing with AI-generated code 44 | 45 | As artificial intelligence evolves, AI-generated code is becoming valuable for many software projects, including open-source initiatives. While we recognize the potential benefits of incorporating AI-generated content into our open-source projects there are certain requirements that need to be reflected and adhered to when making contributions. 46 | 47 | Please see our [guideline for AI-generated code contributions to SAP Open Source Software Projects](https://github.com/SAP/.github/blob/main/CONTRIBUTING_USING_GENAI.md) for these requirements. 48 | 49 | ## Developer Certificate of Origin (DCO) 50 | 51 | Due to legal reasons, contributors will be asked to accept a DCO before they submit the first pull request to this projects, this happens in an automated fashion during the submission process. SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/). 52 | 53 | ## How to contribute - the Process 54 | 55 | Make sure the change would be welcome (e.g. a bugfix or a useful feature); best do so by proposing it in a GitHub issue 56 | 57 | 1. Create a branch forking the generator-easy-ui5 repository and do your change. 58 | 59 | 2. Commit (with a commit message following the [conventional-commit](https://www.conventionalcommits.org/) syntax) and push your changes on that branch 60 | 61 | 3. If your change fixes an issue reported at GitHub, add a [keyword](https://help.github.com/articles/closing-issues-using-keywords/) like `fix ` to the commit message: 62 | 63 | 4. Create a Pull Request to this repository 64 | 65 | 5. Wait for our code review and approval, possibly enhancing your change on request 66 | 67 | Note that the developers also have their regular duties, so depending on the required effort for reviewing, testing and clarification this may take a while 68 | 69 | 6. Once the change has been approved we will inform you in a comment 70 | 71 | ## How to release - the Process 72 | 73 | :warning: NPM > v8.x must be used for the release process! 74 | 75 | First, make sure that you pull the latest state of the GitHub repository and then proceed with the following steps: 76 | 77 | 1. Update the version: `npm version patch|minor|major` 78 | 79 | 2. Push the new commit and tag: `git push && git push --tags` 80 | 81 | A GitHub action will do the needful once the new tag has been pushed. 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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. 202 | -------------------------------------------------------------------------------- /LICENSES/Apache-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, 6 | AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, and distribution 13 | as defined by Sections 1 through 9 of this document. 14 | 15 | 16 | 17 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 18 | owner that is granting the License. 19 | 20 | 21 | 22 | "Legal Entity" shall mean the union of the acting entity and all other entities 23 | that control, are controlled by, or are under common control with that entity. 24 | For the purposes of this definition, "control" means (i) the power, direct 25 | or indirect, to cause the direction or management of such entity, whether 26 | by contract or otherwise, or (ii) ownership of fifty percent (50%) or more 27 | of the outstanding shares, or (iii) beneficial ownership of such entity. 28 | 29 | 30 | 31 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions 32 | granted by this License. 33 | 34 | 35 | 36 | "Source" form shall mean the preferred form for making modifications, including 37 | but not limited to software source code, documentation source, and configuration 38 | files. 39 | 40 | 41 | 42 | "Object" form shall mean any form resulting from mechanical transformation 43 | or translation of a Source form, including but not limited to compiled object 44 | code, generated documentation, and conversions to other media types. 45 | 46 | 47 | 48 | "Work" shall mean the work of authorship, whether in Source or Object form, 49 | made available under the License, as indicated by a copyright notice that 50 | is included in or attached to the work (an example is provided in the Appendix 51 | below). 52 | 53 | 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object form, 56 | that is based on (or derived from) the Work and for which the editorial revisions, 57 | annotations, elaborations, or other modifications represent, as a whole, an 58 | original work of authorship. For the purposes of this License, Derivative 59 | Works shall not include works that remain separable from, or merely link (or 60 | bind by name) to the interfaces of, the Work and Derivative Works thereof. 61 | 62 | 63 | 64 | "Contribution" shall mean any work of authorship, including the original version 65 | of the Work and any modifications or additions to that Work or Derivative 66 | Works thereof, that is intentionally submitted to Licensor for inclusion in 67 | the Work by the copyright owner or by an individual or Legal Entity authorized 68 | to submit on behalf of the copyright owner. For the purposes of this definition, 69 | "submitted" means any form of electronic, verbal, or written communication 70 | sent to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, and 72 | issue tracking systems that are managed by, or on behalf of, the Licensor 73 | for the purpose of discussing and improving the Work, but excluding communication 74 | that is conspicuously marked or otherwise designated in writing by the copyright 75 | owner as "Not a Contribution." 76 | 77 | 78 | 79 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 80 | of whom a Contribution has been received by Licensor and subsequently incorporated 81 | within the Work. 82 | 83 | 2. Grant of Copyright License. Subject to the terms and conditions of this 84 | License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 85 | no-charge, royalty-free, irrevocable copyright license to reproduce, prepare 86 | Derivative Works of, publicly display, publicly perform, sublicense, and distribute 87 | the Work and such Derivative Works in Source or Object form. 88 | 89 | 3. Grant of Patent License. Subject to the terms and conditions of this License, 90 | each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 91 | no-charge, royalty-free, irrevocable (except as stated in this section) patent 92 | license to make, have made, use, offer to sell, sell, import, and otherwise 93 | transfer the Work, where such license applies only to those patent claims 94 | licensable by such Contributor that are necessarily infringed by their Contribution(s) 95 | alone or by combination of their Contribution(s) with the Work to which such 96 | Contribution(s) was submitted. If You institute patent litigation against 97 | any entity (including a cross-claim or counterclaim in a lawsuit) alleging 98 | that the Work or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses granted to You 100 | under this License for that Work shall terminate as of the date such litigation 101 | is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the Work or 104 | Derivative Works thereof in any medium, with or without modifications, and 105 | in Source or Object form, provided that You meet the following conditions: 106 | 107 | (a) You must give any other recipients of the Work or Derivative Works a copy 108 | of this License; and 109 | 110 | (b) You must cause any modified files to carry prominent notices stating that 111 | You changed the files; and 112 | 113 | (c) You must retain, in the Source form of any Derivative Works that You distribute, 114 | all copyright, patent, trademark, and attribution notices from the Source 115 | form of the Work, excluding those notices that do not pertain to any part 116 | of the Derivative Works; and 117 | 118 | (d) If the Work includes a "NOTICE" text file as part of its distribution, 119 | then any Derivative Works that You distribute must include a readable copy 120 | of the attribution notices contained within such NOTICE file, excluding those 121 | notices that do not pertain to any part of the Derivative Works, in at least 122 | one of the following places: within a NOTICE text file distributed as part 123 | of the Derivative Works; within the Source form or documentation, if provided 124 | along with the Derivative Works; or, within a display generated by the Derivative 125 | Works, if and wherever such third-party notices normally appear. The contents 126 | of the NOTICE file are for informational purposes only and do not modify the 127 | License. You may add Your own attribution notices within Derivative Works 128 | that You distribute, alongside or as an addendum to the NOTICE text from the 129 | Work, provided that such additional attribution notices cannot be construed 130 | as modifying the License. 131 | 132 | You may add Your own copyright statement to Your modifications and may provide 133 | additional or different license terms and conditions for use, reproduction, 134 | or distribution of Your modifications, or for any such Derivative Works as 135 | a whole, provided Your use, reproduction, and distribution of the Work otherwise 136 | complies with the conditions stated in this License. 137 | 138 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 139 | Contribution intentionally submitted for inclusion in the Work by You to the 140 | Licensor shall be under the terms and conditions of this License, without 141 | any additional terms or conditions. Notwithstanding the above, nothing herein 142 | shall supersede or modify the terms of any separate license agreement you 143 | may have executed with Licensor regarding such Contributions. 144 | 145 | 6. Trademarks. This License does not grant permission to use the trade names, 146 | trademarks, service marks, or product names of the Licensor, except as required 147 | for reasonable and customary use in describing the origin of the Work and 148 | reproducing the content of the NOTICE file. 149 | 150 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to 151 | in writing, Licensor provides the Work (and each Contributor provides its 152 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 153 | KIND, either express or implied, including, without limitation, any warranties 154 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR 155 | A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness 156 | of using or redistributing the Work and assume any risks associated with Your 157 | exercise of permissions under this License. 158 | 159 | 8. Limitation of Liability. In no event and under no legal theory, whether 160 | in tort (including negligence), contract, or otherwise, unless required by 161 | applicable law (such as deliberate and grossly negligent acts) or agreed to 162 | in writing, shall any Contributor be liable to You for damages, including 163 | any direct, indirect, special, incidental, or consequential damages of any 164 | character arising as a result of this License or out of the use or inability 165 | to use the Work (including but not limited to damages for loss of goodwill, 166 | work stoppage, computer failure or malfunction, or any and all other commercial 167 | damages or losses), even if such Contributor has been advised of the possibility 168 | of such damages. 169 | 170 | 9. Accepting Warranty or Additional Liability. While redistributing the Work 171 | or Derivative Works thereof, You may choose to offer, and charge a fee for, 172 | acceptance of support, warranty, indemnity, or other liability obligations 173 | and/or rights consistent with this License. However, in accepting such obligations, 174 | You may act only on Your own behalf and on Your sole responsibility, not on 175 | behalf of any other Contributor, and only if You agree to indemnify, defend, 176 | and hold each Contributor harmless for any liability incurred by, or claims 177 | asserted against, such Contributor by reason of your accepting any such warranty 178 | or additional liability. END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following boilerplate 183 | notice, with the fields enclosed by brackets "[]" replaced with your own identifying 184 | information. (Don't include the brackets!) The text should be enclosed in 185 | the appropriate comment syntax for the file format. We also recommend that 186 | a file or class name and description of purpose be included on the same "printed 187 | page" as the copyright notice for easier identification within third-party 188 | archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | 194 | you may not use this file except in compliance with the License. 195 | 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | 204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 205 | 206 | See the License for the specific language governing permissions and 207 | 208 | limitations under the License. 209 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easy UI5 Generator 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![Build Status][test-image]][test-url] 5 | [![Dependency Status][librariesio-image]][repo-url] 6 | [![License Status][license-image]][license-url] 7 | [![REUSE status][reuse-image]][reuse-url] 8 | 9 | ## Description 10 | 11 | Easy UI5 (`easy-ui5`) 💙 is a [Yeoman](http://yeoman.io/) generator which enables you to create simple [SAPUI5](https://ui5.sap.com)/[OpenUI5](https://sdk.openui5.org)-based web-apps and other UI5-related projects within seconds. 12 | 13 | This generator has been created to simplify the creation of your UI5 prototypes. Now you can scaffold simple UI5 projects from the shell/terminal of your choice. The current best practices (such as [async](https://blogs.sap.com/2018/12/18/ui5ers-buzz-41-best-practices-for-async-loading-in-ui5/)) are already baked into our templates so you don't have to worry about the definition of the metadata files. 14 | 15 | The purpose of the `project` subgenerator is to guide you on your first steps with [SAPUI5](https://sapui5.hana.ondemand.com/) and [SAP BTP](https://www.sap.com/products/business-technology-platform.html) deployments. Once you are familiar with those technologies, you might want to tweak the projects to adapt them for productive use-cases (such as continuous deployment pipelines and full i18n). 16 | 17 | > :warning: Starting with Easy UI5 v3, all templates will be outsource to repositories in the [UI5 Community](https://github.com/ui5-community/). This project is from now on a shell that will offer all generators hosted on that GitHub org. Easy UI5 will download and install these repositories when needed. 18 | > 19 | > By default, it will download the repository [generator-ui5-project](https://github.com/ui5-community/generator-ui5-project/) which contains the templates that were previously integrated in Easy UI5 < 3. 20 | 21 | ## Requirements 22 | 23 | - Get [Node.js](https://nodejs.org/en/download/) (:warning: **version 18 or higher**) 24 | 25 | ## Download and Installation 26 | 27 | 1. Install the generator 28 | ```sh 29 | npm install -g yo generator-easy-ui5 30 | ``` 31 | 2. Verify your installation to see if Yeoman has been installed correctly 32 | ```sh 33 | yo 34 | ``` 35 | Make sure you see the `easy-ui5` generator listed. 36 | 37 | ## Bootstrapping a new UI5 project 38 | 39 | > Create your first UI5 App within a few seconds! 40 | 41 | 1. Scaffold your UI5 project 42 | ``` 43 | yo easy-ui5 project 44 | ``` 45 | 2. Answer the prompts to create your new project 46 | 3. Run it locally 47 | ``` 48 | cd 49 | npm start # or "yarn start" 50 | ``` 51 | 52 | ## Target platforms 53 | 54 | During the prompting phase, the generator will ask on which target platform your app should run. Currently, the following options are available: 55 | 56 | - Static webserver 57 | - SAP BTP 58 | - SAP HANA XS Advanced 59 | - SAP NetWeaver 60 | 61 | > Have a look at [this plugin project](https://github.com/ui5-community/generator-ui5-project/) for more usage instruction and information about the available subcommands. 62 | 63 | ## More generators 64 | 65 | And this is just the start! 66 | 67 | We made Easy UI5 extensible, so that the entire [UI5 Community](https://github.com/ui5-community/) can build additional plugins to scaffold any UI5-related development activity. 68 | 69 | By default, this generator comes with the [project-creation-plugin](https://github.com/ui5-community/generator-ui5-project) but there are many others as well: 70 | 71 | - Create new UI5 libraries [[ui5-community/generator-ui5-library]](https://github.com/ui5-community/generator-ui5-library) 72 | - More are coming! 73 | 77 | 78 | To download and use any of the plugins above, run the following command 79 | 80 | ```sh 81 | yo easy-ui5 [project|library] # this is the name of the repositorty without the "generator-ui5-" prefix 82 | ``` 83 | 84 | 85 | 86 | ## Calling generators 87 | 88 | Run the following command to see all subgenerators of a given plugin 89 | 90 | ```sh 91 | yo easy-ui5 [project|library] --list 92 | ``` 93 | 94 | Once you decided on the subgenerator, run: 95 | 96 | Run the following command to see all subgenerators of a given plugin 97 | 98 | ```sh 99 | yo easy-ui5 [project|library] 100 | ``` 101 | 102 | ## Proxy settings 103 | 104 | If you are running Easy UI5 behind a coporate proxy, just use the default proxy environment variables for Node.js to configure your corporate proxy: 105 | 106 | - `HTTP_PROXY`: Specify the value to use as the HTTP proxy for all connections, e.g., `HTTP_PROXY="http://proxy.mycompany.com:8080/"`. 107 | - `HTTPS_PROXY`: Specify the value to use as the HTTPS proxy for all connections, e.g., `HTTPS_PROXY="http://proxy.mycompany.com:8080/"`. 108 | - `NO_PROXY`: Define the hosts that should bypass the proxy, e.g., `NO_PROXY="localhost,.mycompany.com,192.168.6.254:80"`. 109 | 110 | In addition, Easy UI5 also supports proxy configuration from the `.npmrc` configuration: 111 | 112 | ```text 113 | http-proxy=http://proxy.mycompany.com:8080/ 114 | https-proxy=http://proxy.mycompany.com:8080/ 115 | proxy=http://proxy.mycompany.com:8080/ 116 | no-proxy=localhost,.mycompany.com,192.168.6.254:80 117 | ``` 118 | 119 | This configuration is shared with npm itself since this proxy configuration is used to download the packages from npm. 120 | 121 | Proxies can be passed as env variables or as npm config options. The highest precedence have the `GLOBAL_AGENT_*` env variables before the regular env variables followed by the npm configuration options, e.g.: 122 | 123 | 1. env: `GLOBAL_AGENT_HTTP_PROXY` 124 | 2. env: `HTTP_PROXY` 125 | 3. npm: `http-proxy` 126 | 4. npm: `proxy` 127 | 128 | ## How to obtain support 129 | 130 | Please use the GitHub bug tracking system to post questions, bug reports or to create pull requests. 131 | 132 | ## Contributing 133 | 134 | We welcome any type of contribution (code contributions, pull requests, issues) to this easy-ui5 generator equally. 135 | 136 | Please follow our instructions if you would like to [contribute](https://github.com/SAP/generator-easy-ui5/blob/master/CONTRIBUTING.md). 137 | 138 | [npm-image]: https://img.shields.io/npm/v/generator-easy-ui5.svg 139 | [npm-url]: https://www.npmjs.com/package/generator-easy-ui5 140 | [test-image]: https://github.com/SAP/generator-easy-ui5/actions/workflows/main.yml/badge.svg 141 | [test-url]: https://github.com/SAP/generator-easy-ui5/actions/workflows/main.yml 142 | [librariesio-image]: https://img.shields.io/librariesio/github/SAP/generator-easy-ui5 143 | [repo-url]: https://github.com/SAP/generator-easy-ui5 144 | [license-image]: https://img.shields.io/npm/l/generator-easy-ui5.svg 145 | [license-url]: https://github.com/SAP/generator-easy-ui5/blob/master/LICENSE 146 | [reuse-image]: https://api.reuse.software/badge/github.com/SAP/generator-easy-ui5/ 147 | [reuse-url]: https://api.reuse.software/info/github.com/SAP/generator-easy-ui5/ 148 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | SPDX-PackageName = "Generator-easy-ui5" 3 | SPDX-PackageSupplier = "Marius Obert " 4 | SPDX-PackageDownloadLocation = "https://github.com/SAP/generator-easy-ui5" 5 | SPDX-PackageComment = "The code in this project may include calls to APIs (“API Calls”) of\n SAP or third-party products or services developed outside of this project\n (“External Products”).\n “APIs” means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." 6 | 7 | [[annotations]] 8 | path = "**" 9 | precedence = "aggregate" 10 | SPDX-FileCopyrightText = "2018-2024 SAP SE or an SAP affiliate company and generator-easy-ui5 contributors" 11 | SPDX-License-Identifier = "Apache-2.0" 12 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | import Generator from "yeoman-generator"; 2 | 3 | import path from "path"; 4 | import fs from "fs"; 5 | import os from "os"; 6 | import url from "url"; 7 | 8 | import { glob } from "glob"; 9 | import chalk from "chalk"; 10 | import yosay from "yosay"; 11 | import libnpmconfig from "libnpmconfig"; 12 | import AdmZip from "adm-zip"; 13 | import { request } from "@octokit/request"; 14 | import { Octokit } from "@octokit/rest"; 15 | import { throttling } from "@octokit/plugin-throttling"; 16 | const MyOctokit = Octokit.plugin(throttling); 17 | import spawn from "cross-spawn"; 18 | import nodeFetch from "node-fetch"; 19 | 20 | const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); 21 | 22 | // helper to retrieve config entries from npm 23 | // --> npm config set easy-ui5_addGhOrg XYZ 24 | let npmConfig; 25 | const getNPMConfig = (configName, prefix = "easy-ui5_") => { 26 | if (!npmConfig) { 27 | npmConfig = libnpmconfig.read(); 28 | } 29 | return npmConfig && npmConfig[`${prefix}${configName}`]; 30 | }; 31 | 32 | // apply proxy settings to GLOBAL_AGENT to support the proxy configuration for node-fetch using the GLOBAL_AGENT 33 | // ==> the configuration is derived from the environment variables ([GLOBAL_AGENT_](HTTP|HTTPS|NO)_PROXY) and the npm config ((http|https|no)-proxy) 34 | // ==> empty values will allow to override the more general proxy settings and make the proxy value undefined 35 | let HTTP_PROXY, HTTPS_PROXY, NO_PROXY; 36 | if (global?.GLOBAL_AGENT) { 37 | HTTP_PROXY = process.env.GLOBAL_AGENT_HTTP_PROXY ?? process.env.HTTP_PROXY ?? process.env.http_proxy ?? getNPMConfig("http-proxy", "") ?? getNPMConfig("proxy", ""); 38 | global.GLOBAL_AGENT.HTTP_PROXY = HTTP_PROXY = HTTP_PROXY || global.GLOBAL_AGENT.HTTP_PROXY; 39 | HTTPS_PROXY = process.env.GLOBAL_AGENT_HTTPS_PROXY ?? process.env.HTTPS_PROXY ?? process.env.https_proxy ?? getNPMConfig("https-proxy", "") ?? getNPMConfig("proxy", ""); 40 | global.GLOBAL_AGENT.HTTPS_PROXY = HTTPS_PROXY = HTTPS_PROXY || global.GLOBAL_AGENT.HTTPS_PROXY; 41 | NO_PROXY = process.env.GLOBAL_AGENT_NO_PROXY ?? process.env.NO_PROXY ?? process.env.no_proxy ?? getNPMConfig("no-proxy", ""); 42 | global.GLOBAL_AGENT.NO_PROXY = NO_PROXY = NO_PROXY || global.GLOBAL_AGENT.NO_PROXY; 43 | } 44 | 45 | // the command line options of the generator 46 | const generatorOptions = { 47 | pluginsHome: { 48 | type: String, 49 | description: "Home directory of the plugin generators", 50 | default: path.join(os.homedir(), ".npm", "_generator-easy-ui5", "plugin-generators"), 51 | hide: true, // shouldn't be needed 52 | npmConfig: true, 53 | }, 54 | plugins: { 55 | type: Boolean, 56 | alias: "p", 57 | description: "List detailed information about installed plugin generators", 58 | }, 59 | pluginsWithDevDeps: { 60 | type: Boolean, 61 | alias: "dev", 62 | description: "Installs the plugin generators with dev dependencies (compat mode)", 63 | }, 64 | ghBaseUrl: { 65 | type: String, 66 | description: "Base URL for the Octokit API (defaults to https://api.github.com if undefined)", 67 | hide: true, // shouldn't be needed 68 | npmConfig: true, 69 | }, 70 | ghAuthToken: { 71 | type: String, 72 | description: "GitHub authToken to optionally access private generator repositories", 73 | npmConfig: true, 74 | }, 75 | ghOrg: { 76 | type: String, 77 | description: "GitHub organization to lookup for available generators", 78 | default: "ui5-community", 79 | hide: true, // we don't want to recommend to use this option 80 | }, 81 | ghThreshold: { 82 | type: Number, 83 | default: 100, 84 | hide: true, // shouldn't be needed 85 | }, 86 | subGeneratorPrefix: { 87 | type: String, 88 | description: "Prefix used for the lookup of the available generators", 89 | default: "generator-ui5-", 90 | hide: true, // we don't want to recommend to use this option 91 | }, 92 | addGhBaseUrl: { 93 | type: String, 94 | description: "Base URL for the Octokit API for the additional generators (defaults to https://api.github.com if undefined)", 95 | hide: true, // shouldn't be needed 96 | npmConfig: true, 97 | }, 98 | addGhOrg: { 99 | type: String, 100 | description: "GitHub organization to lookup for additional available generators", 101 | hide: true, // we don't want to recommend to use this option 102 | npmConfig: true, 103 | }, 104 | addSubGeneratorPrefix: { 105 | type: String, 106 | description: "Prefix used for the lookup of the additional available generators", 107 | default: "generator-", 108 | hide: true, // we don't want to recommend to use this option 109 | npmConfig: true, 110 | }, 111 | embed: { 112 | type: Boolean, 113 | description: "Embeds the selected plugin generator", 114 | hide: true, // shouldn't be needed 115 | }, 116 | list: { 117 | type: Boolean, 118 | description: "List the available subcommands of the generator", 119 | }, 120 | skipUpdate: { 121 | type: Boolean, 122 | description: "Skip the update of the plugin generator", 123 | }, 124 | forceUpdate: { 125 | type: Boolean, 126 | description: "Force the update of the plugin generator", 127 | }, 128 | offline: { 129 | type: Boolean, 130 | alias: "o", 131 | description: "Running easy-ui5 in offline mode", 132 | }, 133 | verbose: { 134 | type: Boolean, 135 | description: "Enable detailed logging", 136 | }, 137 | next: { 138 | type: Boolean, 139 | description: "Preview the next mode to consume subgenerators from bestofui5.org", 140 | }, 141 | skipNested: { 142 | type: Boolean, 143 | description: "Skips the nested generators and runs only the first subgenerator", 144 | }, 145 | }; 146 | 147 | const generatorArgs = { 148 | generator: { 149 | type: String, 150 | required: false, 151 | description: `Name of the generator to invoke (without the ${chalk.yellow("generator-ui5-")} prefix)`, 152 | }, 153 | subcommand: { 154 | type: String, 155 | required: false, 156 | description: `Name of the subcommand to invoke (without the ${chalk.yellow("generator:")} prefix)`, 157 | }, 158 | }; 159 | 160 | // The Easy UI5 Generator! 161 | export default class extends Generator { 162 | constructor(args, opts) { 163 | super(args, opts, { 164 | // disable the Yeoman 5 package-manager logic (auto install)! 165 | customInstallTask: "disabled", 166 | }); 167 | 168 | Object.keys(generatorArgs).forEach((argName) => { 169 | // register the argument for being displayed in the help 170 | this.argument(argName, generatorArgs[argName]); 171 | }); 172 | 173 | Object.keys(generatorOptions).forEach((optionName) => { 174 | const initialValue = this.options[optionName]; 175 | // register the option for being displayed in the help 176 | this.option(optionName, generatorOptions[optionName]); 177 | const defaultedValue = this.options[optionName]; 178 | if (generatorOptions[optionName].npmConfig) { 179 | // if a value is set, use the set value (parameter has higher precedence than npm config) 180 | // => this.option(...) applies the default value to this.options[...] used as last resort 181 | this.options[optionName] = initialValue || getNPMConfig(optionName) || defaultedValue; 182 | } 183 | }); 184 | } 185 | 186 | _showBusy(statusText) { 187 | this._clearBusy(); 188 | const progressChars = ["\\", "|", "/", "-"]; 189 | let i = 0; 190 | process.stdout.write(`\r${statusText} `); 191 | this._busy = { 192 | text: statusText, 193 | timer: setInterval(() => { 194 | process.stdout.write(`\r${statusText} ${progressChars[i++]}`); 195 | i %= progressChars.length; 196 | }, 250), 197 | }; 198 | } 199 | 200 | _clearBusy(newLine) { 201 | if (this._busy) { 202 | clearInterval(this._busy.timer); 203 | process.stdout.write("\r".padEnd(this._busy.text.length + 3) + (newLine ? "\n" : "")); 204 | delete this._busy; 205 | } 206 | } 207 | 208 | async _npmInstall(dir, withDevDeps) { 209 | return new Promise( 210 | function (resolve, reject) { 211 | spawn("npm", ["install", "--no-progress", "--ignore-engines", "--ignore-scripts"], { 212 | stdio: this.config.verbose ? "inherit" : "ignore", 213 | cwd: dir, 214 | env: { 215 | ...process.env, 216 | NO_UPDATE_NOTIFIER: true, 217 | NODE_ENV: withDevDeps ? undefined : "production", // do not install devDependencies! 218 | }, 219 | }) 220 | .on("exit", function (code) { 221 | resolve(code); 222 | }) 223 | .on("error", function (err) { 224 | reject(err); 225 | }); 226 | }.bind(this) 227 | ); 228 | } 229 | 230 | _unzip(pathOrBuffer, targetPath, zipInternalPath /* used for plugin generators from GitHub (e.g. TS tutorial) */) { 231 | const zip = new AdmZip(pathOrBuffer); 232 | const zipEntries = zip.getEntries(); 233 | zipEntries.forEach((entry) => { 234 | const match = !entry.isDirectory && entry.entryName.match(/[^\/]+(\/.+)/); 235 | let entryPath; 236 | if (zipInternalPath && match && match[1].startsWith(zipInternalPath)) { 237 | entryPath = path.dirname(match[1].substring(zipInternalPath.length)); 238 | } else if (!zipInternalPath && match) { 239 | entryPath = path.dirname(match[1]); 240 | } 241 | if (entryPath) { 242 | zip.extractEntryTo(entry, path.join(targetPath, entryPath), false, true); 243 | } 244 | }); 245 | } 246 | 247 | determineAppname() { 248 | return "Easy UI5"; 249 | } 250 | 251 | async _getGeneratorMetadata({ env, generatorPath }) { 252 | // filter the hidden subgenerators already 253 | // -> subgenerators must be found in env as they are returned by lookup! 254 | const lookupGeneratorMeta = await env.lookup({ localOnly: true, packagePaths: generatorPath }); 255 | const subGenerators = lookupGeneratorMeta.filter((sub) => { 256 | const subGenerator = env.get(sub.namespace); 257 | return !subGenerator.hidden; 258 | }); 259 | return subGenerators; 260 | } 261 | 262 | async _installGenerator({ octokit, generator, generatorPath }) { 263 | // lookup the default path of the generator if not set 264 | if (!generator.branch) { 265 | try { 266 | const repoInfo = await octokit.repos.get({ 267 | owner: generator.org, 268 | repo: generator.name, 269 | }); 270 | generator.branch = repoInfo.data.default_branch; 271 | } catch (e) { 272 | console.error(`Generator "${owner}/${repo}!${dir}${branch ? "#" + branch : ""}" not found! Run with --verbose for details!\n(Hint: ${e.message})`); 273 | if (this.options.verbose) { 274 | console.error(e); 275 | } 276 | return; 277 | } 278 | } 279 | // fetch the branch to retrieve the latest commit SHA 280 | let commitSHA; 281 | try { 282 | // determine the commitSHA 283 | const reqBranch = await octokit.repos.getBranch({ 284 | owner: generator.org, 285 | repo: generator.name, 286 | branch: generator.branch, 287 | }); 288 | commitSHA = reqBranch.data.commit.sha; 289 | } catch (ex) { 290 | console.error( 291 | chalk.red(`Failed to retrieve the branch "${generator.branch}" for repository "${generator.name}" for "${generator.org}" organization! Run with --verbose for details!\n(Hint: ${e.message})`) 292 | ); 293 | if (this.options.verbose) { 294 | console.error(chalk.red(ex.message)); 295 | } 296 | return; 297 | } 298 | 299 | if (this.options.verbose) { 300 | this.log(`Using commit ${commitSHA} from @${generator.org}/${generator.name}#${generator.branch}!`); 301 | } 302 | const shaMarker = path.join(generatorPath, `.${commitSHA}`); 303 | 304 | if (fs.existsSync(generatorPath) && !this.options.skipUpdate) { 305 | // check if the SHA marker exists to know whether the generator is up-to-date or not 306 | if (this.options.forceUpdate || !fs.existsSync(shaMarker)) { 307 | if (this.options.verbose) { 308 | this.log(`Generator ${chalk.yellow(generator.name)} in "${generatorPath}" is outdated!`); 309 | } 310 | // remove if the SHA marker doesn't exist => outdated! 311 | this._showBusy(` Deleting subgenerator ${chalk.yellow(generator.name)}...`); 312 | fs.rmSync(generatorPath, { recursive: true }); 313 | } 314 | } 315 | 316 | // re-fetch the generator and extract into local plugin folder 317 | if (!fs.existsSync(generatorPath)) { 318 | // unzip the archive 319 | if (this.options.verbose) { 320 | this.log(`Extracting ZIP to "${generatorPath}"...`); 321 | } 322 | this._showBusy(` Downloading subgenerator ${chalk.yellow(generator.name)}...`); 323 | const reqZIPArchive = await octokit.repos.downloadZipballArchive({ 324 | owner: generator.org, 325 | repo: generator.name, 326 | ref: commitSHA, 327 | }); 328 | 329 | this._showBusy(` Extracting subgenerator ${chalk.yellow(generator.name)}...`); 330 | const buffer = Buffer.from(new Uint8Array(reqZIPArchive.data)); 331 | this._unzip(buffer, generatorPath, generator.dir); 332 | 333 | // write the sha marker 334 | fs.writeFileSync(shaMarker, commitSHA); 335 | } 336 | 337 | // run npm install when not embedding the generator (always for self-healing!) 338 | if (!this.options.embed) { 339 | if (this.options.verbose) { 340 | this.log("Installing the subgenerator dependencies..."); 341 | } 342 | this._showBusy(` Preparing ${chalk.yellow(generator.name)}...`); 343 | await this._npmInstall(generatorPath, this.options.pluginsWithDevDeps); 344 | } 345 | 346 | this._clearBusy(true); 347 | } 348 | 349 | async prompting() { 350 | const home = path.join(__dirname, "..", ".."); 351 | const pkgJson = JSON.parse(fs.readFileSync(path.join(home, "package.json"), "utf8")); 352 | 353 | // Have Yeoman greet the user. 354 | if (!this.options.embedded) { 355 | this.log(yosay(`Welcome to the ${chalk.red("easy-ui5")} ${chalk.yellow(pkgJson.version)} generator!`)); 356 | } 357 | 358 | // by default we install the easy-ui5 plugin generators into the following folder: 359 | // %user_dir%/.npm/_generator-easy-ui5/plugin-generators 360 | let pluginsHome = this.options.pluginsHome; 361 | if (this.options.verbose) { 362 | console.info(" Context:"); 363 | console.info(` - sourceRoot = ${chalk.green(this.sourceRoot())}`); 364 | console.info(` - destinationRoot = ${chalk.green(this.destinationRoot())}`); 365 | console.info(" Options:"); 366 | Object.keys(generatorOptions).forEach((option) => { 367 | console.info(` - ${option} = ${chalk.green(this.options[option])}`); 368 | }); 369 | console.info(" Proxy:"); 370 | console.info(` - HTTP_PROXY = ${chalk.green(HTTP_PROXY)}`); 371 | console.info(` - HTTPS_PROXY = ${chalk.green(HTTPS_PROXY)}`); 372 | console.info(` - NO_PROXY = ${chalk.green(NO_PROXY)}`); 373 | } 374 | fs.mkdirSync(pluginsHome, { recursive: true }); 375 | 376 | // log the plugins and configuration 377 | if (this.options.plugins) { 378 | const { createRequire } = await import("node:module"); 379 | const require = createRequire(import.meta.url); 380 | 381 | const yeoman = require("yeoman-environment/package.json"); 382 | 383 | const components = { 384 | "Node.js": process.version, 385 | "yeoman-environment": yeoman.version, 386 | "generator-easy-ui5": pkgJson.version, 387 | home: home, 388 | pluginsHome: pluginsHome, 389 | }; 390 | 391 | Object.keys(components).forEach((component) => { 392 | this.log(`${chalk.green(component)}: ${components[component]}`); 393 | }); 394 | 395 | this.log(chalk.green("\nAvailable generators:")); 396 | glob.sync(`${pluginsHome}/*/package.json`).forEach((plugin) => { 397 | const name = plugin.match(/.*\/generator-(.+)\/package\.json/)[1]; 398 | const lib = require(plugin); 399 | this.log(` - ${chalk.green(name)}: ${lib.version}`); 400 | }); 401 | 402 | return; 403 | } 404 | 405 | // create the octokit client to retrieve the generators from GH org 406 | // when not running in offline mode! 407 | let octokit; 408 | if (this.options.offline) { 409 | this.log(`Running in ${chalk.yellow("offline")} mode!`); 410 | } else { 411 | // define the options for the Octokit API 412 | const octokitOptions = { 413 | userAgent: `${this.rootGeneratorName()}:${this.rootGeneratorVersion()}`, 414 | request: { 415 | fetch: (_url, _options) => { 416 | return nodeFetch(_url, { 417 | ..._options, 418 | }); 419 | }, 420 | }, 421 | auth: this.options.ghAuthToken, 422 | baseUrl: this.options.ghBaseUrl, 423 | throttle: { 424 | onRateLimit: (retryAfter, options) => { 425 | this.log(`${chalk.yellow("Hit the GitHub API limit!")} Request quota exhausted for this request.`); 426 | if (options.request.retryCount === 0) { 427 | // only retries once 428 | this.log( 429 | `Retrying after ${retryAfter} seconds. Alternatively, you can cancel this operation and supply an auth token with the \`--ghAuthToken\` option. For more details, run \`yo easy-ui5 --help\`. ` 430 | ); 431 | return true; 432 | } 433 | }, 434 | onSecondaryRateLimit: () => { 435 | // does not retry, only logs a warning 436 | this.log(`${chalk.red("Hit the GitHub API limit again!")} Please supply an auth token with the \`--ghAuthToken\` option. For more details, run \`yo easy-ui5 --help\` `); 437 | }, 438 | }, 439 | }; 440 | // create the octokit instance 441 | octokit = new MyOctokit(octokitOptions); 442 | } 443 | 444 | // helper for filtering repos with corresponding subGenerator prefix 445 | const filterReposWithSubGeneratorPrefix = (repos, subGeneratorPrefix) => { 446 | if (!Array.isArray(repos)) { 447 | return []; 448 | } 449 | return repos 450 | .filter((repo) => repo.name.startsWith(`${subGeneratorPrefix}`)) 451 | .map((repo) => { 452 | return { 453 | org: repo.owner?.login, 454 | name: repo.name, 455 | branch: repo.default_branch, 456 | subGeneratorName: repo.name.substring(subGeneratorPrefix.length), 457 | }; 458 | }); 459 | }; 460 | 461 | // helper to retrieve the available repositories for a GH org 462 | const listGeneratorsForOrg = async (ghOrg, subGeneratorPrefix, threshold) => { 463 | const response = await octokit.repos.listForOrg({ 464 | org: ghOrg, 465 | sort: "name", 466 | // eslint-disable-next-line camelcase 467 | per_page: threshold, 468 | }); 469 | return filterReposWithSubGeneratorPrefix(response?.data, subGeneratorPrefix); 470 | }; 471 | 472 | // helper to retrieve the available repositories for a GH user 473 | const listGeneratorsForUser = async (ghUser, subGeneratorPrefix) => { 474 | const response = await octokit.repos.listForUser({ 475 | username: ghUser, 476 | sort: "name", 477 | // eslint-disable-next-line camelcase 478 | per_page: threshold, 479 | }); 480 | return filterReposWithSubGeneratorPrefix(response?.data, subGeneratorPrefix); 481 | }; 482 | 483 | // determine the generator to be used 484 | let generator; 485 | 486 | // determine generator by ${owner}/${repo}(!${dir})? syntax, e.g.: 487 | // > yo easy-ui5 SAP-samples/ui5-typescript-tutorial 488 | // > yo easy-ui5 SAP-samples/ui5-typescript-tutorial#1.0 489 | // > yo easy-ui5 SAP-samples/ui5-typescript-tutorial\!/generator 490 | // > yo easy-ui5 SAP-samples/ui5-typescript-tutorial\!/generator#1.0 491 | const reGenerator = /([^\/]+)\/([^\!\#]+)(?:\!([^\#]+))?(?:\#(.+))?/; 492 | const matchGenerator = reGenerator.exec(this.options.generator); 493 | if (matchGenerator) { 494 | // derive and path the generator information from command line 495 | const [owner, repo, dir = "/generator", branch] = matchGenerator.slice(1); 496 | // the plugin path is derived from the owner, repo, dir and branch 497 | const pluginPath = `_/${owner}/${repo}${dir.replace(/[\/\\]/g, "_")}${branch ? `#${branch.replace(/[\/\\]/g, "_")}` : ""}`; 498 | generator = { 499 | org: owner, 500 | name: repo, 501 | branch, 502 | dir, 503 | pluginPath, 504 | }; 505 | // log which generator is being used! 506 | if (this.options.verbose) { 507 | this.log(`Using generator ${chalk.green(`${owner}/${repo}!${dir}${branch ? "#" + branch : ""}`)}`); 508 | } 509 | } 510 | 511 | // retrieve the available repositories (if no generator is specified specified directly) 512 | let availGenerators; 513 | if (!generator) { 514 | // lookup the non-installed embedded generator(s) 515 | const generatorsToBeInstalled = glob 516 | .sync(`${path.join(__dirname, "../../plugins")}/generator-*.zip`) 517 | .map((file) => { 518 | const generatorName = path.basename(file, ".zip"); 519 | const generatorPath = path.join(pluginsHome, generatorName); 520 | return { 521 | file, 522 | generatorName, 523 | generatorPath, 524 | }; 525 | }) 526 | .filter(({ generatorPath }) => !fs.existsSync(generatorPath)); 527 | // install (unzip) the missing embedded generator(s) 528 | if (generatorsToBeInstalled.length > 0) { 529 | this._showBusy(" Installing embedded subgenerators..."); 530 | generatorsToBeInstalled.map(({ file, generatorName, generatorPath }) => { 531 | this._showBusy(` Installing embedded subgenerator ${chalk.yellow(generatorName)}...`); 532 | this._unzip(file, generatorPath); 533 | }); 534 | this._clearBusy(true); 535 | } 536 | // offline mode means local generators only versus only mode 537 | if (this.options.offline) { 538 | availGenerators = glob.sync(`${pluginsHome}/generator-*/package.json`).map((plugin) => { 539 | const match = plugin.match(/.*\/(generator-(.+))\/package\.json/); 540 | return { 541 | org: "local", 542 | name: match[1], 543 | subGeneratorName: match[2].match(/(?:ui5-)?(.*)/)?.[1] || match[2], 544 | local: true, 545 | }; 546 | }); 547 | } else { 548 | // either lookup the generators from bestofui5.org (next option) 549 | // or fetch it from the ui5-community gh organization 550 | if (this.options.next) { 551 | // check bestofui5.org for generators 552 | try { 553 | const response = await request({ 554 | method: "GET", 555 | url: "https://raw.githubusercontent.com/ui5-community/bestofui5-data/live-data/data/data.json", 556 | }); 557 | const data = JSON.parse(response.data); 558 | 559 | availGenerators = data?.packages 560 | ?.filter((entry) => { 561 | return entry.type === "generator"; 562 | }) 563 | .map((entry) => { 564 | return { 565 | org: entry.gitHubOwner, 566 | name: entry.gitHubRepo, 567 | subGeneratorName: entry.gitHubRepo.match(/(?:generator-(?:ui5-)?)(.*)/)?.[1] || entry.gitHubRepo, 568 | }; 569 | }); 570 | } catch (e) { 571 | console.error(`Failed to connect to bestofui5.org to retrieve all available generators! Run with --verbose for details!\n(Hint: ${e.message})`); 572 | if (this.options.verbose) { 573 | console.error(e); 574 | } 575 | return; 576 | } 577 | } else { 578 | // check the main GH org for generators 579 | try { 580 | availGenerators = await listGeneratorsForOrg(this.options.ghOrg, this.options.subGeneratorPrefix, this.options.ghThreshold); 581 | } catch (e) { 582 | console.error(`Failed to connect to GitHub to retrieve all available generators for "${this.options.ghOrg}" organization! Run with --verbose for details!\n(Hint: ${e.message})`); 583 | if (this.options.verbose) { 584 | console.error(e); 585 | } 586 | return; 587 | } 588 | 589 | // check the additional GH org for generators with a different prefix 590 | try { 591 | if (this.options.addGhOrg && this.options.addSubGeneratorPrefix) { 592 | availGenerators = availGenerators.concat(await listGeneratorsForOrg(this.options.addGhOrg, this.options.addSubGeneratorPrefix, this.options.ghThreshold)); 593 | } 594 | } catch (e) { 595 | if (this.options.verbose) { 596 | this.log(`Failed to connect to GitHub retrieve additional generators for "${this.options.addGhOrg}" organization! Try to retrieve for user...`); 597 | } 598 | try { 599 | availGenerators = availGenerators.concat(await listGeneratorsForUser(this.options.addGhOrg, this.options.addSubGeneratorPrefix, this.options.ghThreshold)); 600 | } catch (e1) { 601 | console.error(`Failed to connect to GitHub to retrieve additional generators for organization or user "${this.options.addGhOrg}"! Run with --verbose for details!\n(Hint: ${e.message})`); 602 | if (this.options.verbose) { 603 | console.error(e1); 604 | } 605 | return; 606 | } 607 | } 608 | } 609 | } 610 | } 611 | 612 | // if no generator is provided and doesn't exist, ask for generator name 613 | if (!generator) { 614 | // check for provided generator being available on GH 615 | generator = this.options.generator && availGenerators.find((repo) => repo.subGeneratorName === this.options.generator); 616 | 617 | // if no generator is provided and doesn't exist, ask for generator name 618 | if (this.options.generator && !generator) { 619 | this.log(`The generator ${chalk.red(this.options.generator)} was not found. Please select an existing generator!`); 620 | } 621 | 622 | // still not found, select a generator 623 | if (!generator) { 624 | const generatorIdx = ( 625 | await this.prompt([ 626 | { 627 | type: "list", 628 | name: "generator", 629 | message: "Select your generator?", 630 | choices: availGenerators.map((availGenerator, idx) => ({ 631 | name: `${availGenerator.subGeneratorName}${this.options.addGhOrg ? ` [${availGenerator.org}]` : ""}`, 632 | value: idx, 633 | })), 634 | }, 635 | ]) 636 | ).generator; 637 | generator = availGenerators[generatorIdx]; 638 | } 639 | } 640 | 641 | // install the generator if not running in offline mode 642 | const generatorPath = path.join(pluginsHome, generator.pluginPath || generator.name); 643 | if (!this.options.offline) { 644 | await this._installGenerator({ octokit, generator, generatorPath }); 645 | } 646 | 647 | // do not execute the plugin generator during the setup/embed mode 648 | if (!this.options.embed) { 649 | // filter the local options and the help command 650 | const opts = Object.keys(this._options).filter((optionName) => !(generatorOptions.hasOwnProperty(optionName) || optionName === "help")); 651 | 652 | // create the env for the plugin generator 653 | let env = this.env; // in case of Yeoman UI the env is injected! 654 | if (!env) { 655 | const yeoman = require("yeoman-environment"); 656 | env = yeoman.createEnv(this.args, opts); 657 | } 658 | 659 | // read the generator metadata 660 | let subGenerators = await this._getGeneratorMetadata({ env, generatorPath }); 661 | 662 | // helper to derive the generator from the namespace 663 | function deriveGenerator(namespace, defaultValue) { 664 | const match = namespace.match(/([^:]+):.+/); 665 | return match ? match[1] : defaultValue === undefined ? namespace : defaultValue; 666 | } 667 | 668 | // helper to derive the subcommand from the namespace 669 | function deriveSubcommand(namespace, defaultValue) { 670 | const match = namespace.match(/^[^:]+:(.+)$/); 671 | return match ? match[1] : defaultValue === undefined ? namespace : defaultValue; 672 | } 673 | 674 | // list the available subgenerators in the console (as help) 675 | if (this.options.list) { 676 | let maxLength = 0; 677 | this.log( 678 | subGenerators 679 | .map((sub) => { 680 | maxLength = Math.max(sub.namespace.length, maxLength); 681 | return sub; 682 | }) 683 | .reduce((output, sub) => { 684 | const subGenerator = env.get(sub.namespace); 685 | const displayName = subGenerator.displayName || ""; 686 | let line = ` ${deriveSubcommand(sub.namespace).padEnd(maxLength + 2)}`; 687 | if (displayName) { 688 | line += ` # ${subGenerator.displayName}`; 689 | } 690 | return `${output}\n${line}`; 691 | }, `Subcommands (${subGenerators.length}):`) 692 | ); 693 | return; 694 | } 695 | 696 | // if a subcommand is provided as argument, identify the matching subgenerator 697 | // and remove the rest of the subgenerators from the list for later steps 698 | if (this.options.subcommand) { 699 | const selectedSubGenerator = subGenerators.filter((sub) => { 700 | // identify the subgenerator by subcommand 701 | return new RegExp(`:${this.options.subcommand}$`).test(sub.namespace); 702 | }); 703 | if (selectedSubGenerator.length == 1) { 704 | subGenerators = selectedSubGenerator; 705 | } else { 706 | this.log(`The generator ${chalk.red(this.options.generator)} has no subcommand ${chalk.red(this.options.subcommand)}. Please select an existing subcommand!`); 707 | } 708 | } 709 | 710 | // transform the list of the subgenerators and identify the 711 | // default subgenerator for the default selection 712 | let defaultSubGenerator; 713 | let maxLength = 0; 714 | subGenerators = subGenerators 715 | .map((sub) => { 716 | const subGenerator = env.get(sub.namespace); 717 | let subcommand = deriveSubcommand(sub.namespace); 718 | let displayName = subGenerator.displayName || subcommand; 719 | maxLength = Math.max(displayName.length, maxLength); 720 | return { 721 | subcommand, 722 | displayName, 723 | sub, 724 | }; 725 | }) 726 | .map(({ subcommand, displayName, sub }) => { 727 | const transformed = { 728 | name: `${displayName.padEnd(maxLength + 2)} [${subcommand}]`, 729 | value: sub.namespace, 730 | }; 731 | if (/:app$/.test(sub.namespace)) { 732 | defaultSubGenerator = transformed; 733 | } 734 | return transformed; 735 | }); 736 | 737 | // at least 1 subgenerator must be present 738 | if (subGenerators.length >= 1) { 739 | // by default the 1st subgenerator is used 740 | let subGenerator = subGenerators[0].value; 741 | 742 | // if more than 1 subgenerator is present 743 | // ask the developer to select one! 744 | if (subGenerators.length > 1) { 745 | subGenerator = ( 746 | await this.prompt([ 747 | { 748 | type: "list", 749 | name: "subGenerator", 750 | message: "What do you want to do?", 751 | default: defaultSubGenerator && defaultSubGenerator.value, 752 | choices: subGenerators, 753 | }, 754 | ]) 755 | ).subGenerator; 756 | } 757 | 758 | // determine the list of subgenerators to be executed 759 | const subGensToRun = [subGenerator]; 760 | 761 | // method to resolve nested generators (only once!) 762 | const resolved = []; 763 | const resolveNestedGenerator = async (generatorToResolve) => { 764 | const constructor = await env.get(generatorToResolve); 765 | await Promise.all( 766 | constructor.nestedGenerators?.map(async (nestedGenerator) => { 767 | const theNestedGenerator = deriveGenerator(nestedGenerator); 768 | if (resolved.indexOf(theNestedGenerator) === -1) { 769 | resolved.push(theNestedGenerator); 770 | const nestedGeneratorInfo = availGenerators.find((repo) => repo.subGeneratorName === theNestedGenerator); 771 | const nestedGeneratorPath = path.join(pluginsHome, nestedGeneratorInfo.pluginPath || nestedGeneratorInfo.name); 772 | await this._installGenerator({ octokit, generator: nestedGeneratorInfo, generatorPath: nestedGeneratorPath }); 773 | const nestedGens = await this._getGeneratorMetadata({ env, generatorPath: nestedGeneratorPath }); 774 | const subcommand = deriveSubcommand(nestedGenerator, ""); 775 | const theNestedGen = nestedGens.filter((nested) => { 776 | const nestedSubcommand = deriveSubcommand(nested.namespace, ""); 777 | return subcommand ? nestedSubcommand === subcommand : !nestedSubcommand; 778 | })?.[0]; 779 | if (theNestedGen) { 780 | subGensToRun.push(theNestedGen.namespace); 781 | await resolveNestedGenerator(theNestedGen.namespace); 782 | } else { 783 | this.log(`The nested generator "${nestedGeneratorInfo.org}/${nestedGeneratorInfo.name}" has no subgenerator "${subcommand || "default"}"! Ignoring execution...`); 784 | } 785 | } 786 | }) || [] 787 | ); 788 | }; 789 | 790 | // only resolve nested generators when they should not be skipped 791 | if (!this.options.skipNested) { 792 | await resolveNestedGenerator(subGenerator); 793 | } 794 | 795 | // intercept the environments runGenerator method to determine 796 | // and forward the destinationRoot between the generator executions 797 | const runGenerator = env.runGenerator; 798 | let cwd; 799 | env.runGenerator = async function (gen) { 800 | if (cwd) { 801 | // apply the cwd to the next gen 802 | gen.destinationRoot(cwd); 803 | } 804 | return runGenerator.apply(this, arguments).then((retval) => { 805 | // store the cwd from the current gen 806 | cwd = gen.destinationRoot(); 807 | return retval; 808 | }); 809 | }; 810 | 811 | // chain the execution of the generators 812 | let chain = Promise.resolve(); 813 | for (const subGen of subGensToRun) { 814 | chain = chain.then( 815 | function () { 816 | // we need to use env.run and not composeWith 817 | // to ensure that subgenerators can have different 818 | // dependencies than the root generator 819 | return env.run(subGen, { 820 | verbose: this.options.verbose, 821 | embedded: true, 822 | destinationRoot: this.destinationRoot(), 823 | }); 824 | }.bind(this) 825 | ); 826 | } 827 | 828 | if (this.options.verbose) { 829 | this.log(`Running generators in "${generatorPath}"...`); 830 | } 831 | } else { 832 | this.log(`The generator ${chalk.red(this.options.generator)} has no visible subgenerators!`); 833 | } 834 | } else { 835 | // zip the content of the plugin generator or 836 | // install the dependencies of the generator 837 | if (this.options.verbose) { 838 | this.log(`Embedding plugin generator ${chalk.yellow(generator.name)}...`); 839 | } 840 | const generatorZIP = new AdmZip(); 841 | const addLocalFile = (file) => { 842 | const filePath = path.join(generator.name, path.relative(generatorPath, file)); 843 | generatorZIP.addLocalFile(file, path.dirname(filePath), path.basename(filePath)); 844 | if (this.options.verbose) { 845 | this.log(` + file: ${file}`); 846 | } 847 | }; 848 | glob.sync(`${generatorPath}/*`, { nodir: true, dot: true }).forEach(addLocalFile); 849 | glob.sync(`${generatorPath}/!(node_modules)/**/*`, { nodir: true, dot: true }).forEach(addLocalFile); 850 | const generatorZIPPath = path.join(__dirname, "../../plugins", `${generator.name}.zip`); 851 | generatorZIP.writeZip(generatorZIPPath); 852 | if (this.options.verbose) { 853 | this.log(`Stored plugin generator ${chalk.yellow(generator.name)} zip to "${generatorZIPPath}"!`); 854 | } 855 | } 856 | } 857 | } 858 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-easy-ui5", 3 | "version": "3.8.1", 4 | "description": "Generator for UI5-based project", 5 | "main": "generators/app/index.js", 6 | "type": "module", 7 | "files": [ 8 | "generators", 9 | "plugins" 10 | ], 11 | "engines": { 12 | "node": ">=18" 13 | }, 14 | "scripts": { 15 | "prepublishOnly": "npx yo@4.3.1 ./ project --embed", 16 | "start": "yo easy-ui5 project", 17 | "test": "mocha", 18 | "test:subgen:list": "yo easy-ui5 project --list", 19 | "test:subgen:start": "yo easy-ui5 project app", 20 | "release:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0", 21 | "postversion": "npm run release:changelog && git commit --all --amend --no-edit", 22 | "lint": "eslint .", 23 | "lint:fix": "eslint . --fix", 24 | "lint:staged": "lint-staged", 25 | "format": "prettier --write .", 26 | "format:staged": "pretty-quick --staged --verbose", 27 | "prepare": "node ./.husky/skip.js || husky install", 28 | "hooks:commit-msg": "commitlint -e", 29 | "hooks:pre-commit": "npm-run-all --sequential format:staged lint:staged" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/SAP/generator-easy-ui5.git" 34 | }, 35 | "keywords": [ 36 | "yeoman-generator", 37 | "sap", 38 | "sapui5", 39 | "openui5", 40 | "sapbtp", 41 | "generator", 42 | "scaffold" 43 | ], 44 | "author": "SAP", 45 | "license": "Apache-2.0", 46 | "bugs": { 47 | "url": "https://github.com/SAP/generator-easy-ui5/issues" 48 | }, 49 | "homepage": "https://github.com/SAP/generator-easy-ui5#readme", 50 | "dependencies": { 51 | "@octokit/plugin-throttling": "^8.1.3", 52 | "@octokit/rest": "^20.0.2", 53 | "adm-zip": "^0.5.10", 54 | "chalk": "^5.3.0", 55 | "colors": "^1.4.0", 56 | "glob": "^10.3.10", 57 | "libnpmconfig": "^1.2.1", 58 | "node-fetch": "^3.3.2", 59 | "rimraf": "^5.0.5", 60 | "yeoman-environment": "^3.19.3", 61 | "yeoman-generator": "^5.10.0", 62 | "yosay": "^2.0.2" 63 | }, 64 | "devDependencies": { 65 | "@commitlint/cli": "^18.4.3", 66 | "@commitlint/config-conventional": "^18.4.3", 67 | "conventional-changelog-cli": "^4.1.0", 68 | "cz-conventional-changelog": "^3.3.0", 69 | "eslint": "^8.54.0", 70 | "husky": "^8.0.3", 71 | "lint-staged": "^15.1.0", 72 | "mocha": "^10.2.0", 73 | "npm-run-all": "^4.1.5", 74 | "prettier": "^2.8.8", 75 | "pretty-quick": "^3.1.3", 76 | "yeoman-assert": "^3.1.1", 77 | "yeoman-test": "^7.4.0" 78 | }, 79 | "config": { 80 | "commitizen": { 81 | "path": "cz-conventional-changelog" 82 | } 83 | }, 84 | "commitlint": { 85 | "extends": [ 86 | "@commitlint/config-conventional" 87 | ] 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Easy UI5 Generator Test Scenarios 2 | 3 | In the root directory of Easy UI5 just execute the following commands and check that it is working. 4 | 5 | ## Scenario 1: Standard Usage 6 | 7 | Retrieve the list of generators from the `ui5-community` GitHub organisation: 8 | 9 | ```sh 10 | yo ./ 11 | ``` 12 | 13 | Expected result: 14 | 15 | ```sh 16 | _-----_ 17 | | | ╭──────────────────────────╮ 18 | |--(o)--| │ Welcome to the easy-ui5 │ 19 | `---------´ │ generator! │ 20 | ( _´U`_ ) ╰──────────────────────────╯ 21 | /___A___\ / 22 | | ~ | 23 | __'.___.'__ 24 | ´ ` |° ´ Y ` 25 | 26 | ? Select your generator? (Use arrow keys) 27 | ❯ library 28 | middleware 29 | task 30 | control 31 | project 32 | app 33 | ts-app 34 | flp-plugin 35 | ``` 36 | 37 | By specifying an `addGhOrg` additional entries should be displayed: 38 | 39 | ```sh 40 | yo ./ --addGhOrg=petermuessig 41 | ``` 42 | 43 | Expected result: 44 | 45 | ```sh 46 | _-----_ 47 | | | ╭──────────────────────────╮ 48 | |--(o)--| │ Welcome to the easy-ui5 │ 49 | `---------´ │ generator! │ 50 | ( _´U`_ ) ╰──────────────────────────╯ 51 | /___A___\ / 52 | | ~ | 53 | __'.___.'__ 54 | ´ ` |° ´ Y ` 55 | 56 | ? Select your generator? (Use arrow keys) 57 | ❯ library [ui5-community] 58 | middleware [ui5-community] 59 | task [ui5-community] 60 | control [ui5-community] 61 | project [ui5-community] 62 | app [ui5-community] 63 | ts-app [ui5-community] 64 | flp-plugin [ui5-community] 65 | ``` 66 | 67 | ## Scenario 2: Next Usage 68 | 69 | Retrieve the list of generators from bestofui5.org: 70 | 71 | ```sh 72 | yo ./ --next 73 | ``` 74 | 75 | Expected result: 76 | 77 | ```sh 78 | _-----_ 79 | | | ╭──────────────────────────╮ 80 | |--(o)--| │ Welcome to the easy-ui5 │ 81 | `---------´ │ generator! │ 82 | ( _´U`_ ) ╰──────────────────────────╯ 83 | /___A___\ / 84 | | ~ | 85 | __'.___.'__ 86 | ´ ` |° ´ Y ` 87 | 88 | ? Select your generator? (Use arrow keys) 89 | ❯ project 90 | ts-app 91 | ts-app-fcl 92 | flp-plugin 93 | library 94 | control 95 | ``` 96 | 97 | ## Scenario 3: Offline Usage 98 | 99 | Retrieve the list of generators from bestofui5.org: 100 | 101 | ```sh 102 | yo ./ --offline 103 | ``` 104 | 105 | Expected result: 106 | 107 | ```sh 108 | _-----_ 109 | | | ╭──────────────────────────╮ 110 | |--(o)--| │ Welcome to the easy-ui5 │ 111 | `---------´ │ generator! │ 112 | ( _´U`_ ) ╰──────────────────────────╯ 113 | /___A___\ / 114 | | ~ | 115 | __'.___.'__ 116 | ´ ` |° ´ Y ` 117 | 118 | Running in offline mode! 119 | ? Select your generator? (Use arrow keys) 120 | ❯ library 121 | project 122 | ts-app 123 | ``` 124 | 125 | ## Scenario 4: Generator From GitHub Repository 126 | 127 | ```sh 128 | yo ./ SAP-samples/ui5-typescript-tutorial 129 | ``` 130 | 131 | Expected result: 132 | 133 | ```sh 134 | _-----_ 135 | | | ╭──────────────────────────╮ 136 | |--(o)--| │ Welcome to the easy-ui5 │ 137 | `---------´ │ generator! │ 138 | ( _´U`_ ) ╰──────────────────────────╯ 139 | /___A___\ / 140 | | ~ | 141 | __'.___.'__ 142 | ´ ` |° ´ Y ` 143 | 144 | ? How do you want to name this application? (myapp) 145 | ``` 146 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs-extra"; 3 | import assert from "yeoman-assert"; 4 | import helpers from "yeoman-test"; 5 | import url from "url"; 6 | 7 | const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); 8 | const pluginsHome = path.join(__dirname, "_/plugin-generators"); 9 | 10 | describe("Basic V4 project capabilities", function () { 11 | this.timeout(120000); 12 | 13 | before(function () { 14 | fs.mkdirSync(path.join(pluginsHome, "generator-ui5-test-v4"), { recursive: true }); 15 | fs.copySync(path.join(__dirname, "generator-ui5-test-v4"), path.join(pluginsHome, "generator-ui5-test-v4"), { recursive: true, overwrite: true }); 16 | }); 17 | 18 | it("should be able to run the test generator", async function () { 19 | return new Promise((resolve, reject) => { 20 | helpers 21 | .run(path.join(__dirname, "../generators/app")) 22 | .inTmpDir() 23 | .withArguments(["test-v4"]) 24 | .withOptions({ 25 | pluginsHome, 26 | offline: true, 27 | verbose: true, 28 | }) 29 | .on("end", (ctx) => { 30 | // ensure the async write took place 31 | setTimeout(() => { 32 | resolve(ctx); 33 | }, 1000); 34 | }) 35 | .on("error", (err) => { 36 | reject(err); 37 | }); 38 | }); 39 | }); 40 | 41 | it("should create the test file", function () { 42 | return assert.file(["testFileA.md"]); 43 | }); 44 | 45 | after(function () { 46 | fs.removeSync(path.join(pluginsHome, "generator-ui5-test-v4")); 47 | }); 48 | }); 49 | 50 | describe("Basic V5 project capabilities", function () { 51 | this.timeout(120000); 52 | 53 | before(function () { 54 | fs.mkdirSync(path.join(pluginsHome, "generator-ui5-test-v5"), { recursive: true }); 55 | fs.copySync(path.join(__dirname, "generator-ui5-test-v5"), path.join(pluginsHome, "generator-ui5-test-v5"), { recursive: true, overwrite: true }); 56 | }); 57 | 58 | it("should be able to run the test generator", async function () { 59 | return new Promise((resolve, reject) => { 60 | helpers 61 | .run(path.join(__dirname, "../generators/app")) 62 | .inTmpDir() 63 | .withArguments(["test-v5"]) 64 | .withOptions({ 65 | pluginsHome, 66 | offline: true, 67 | verbose: true, 68 | }) 69 | .on("end", (ctx) => { 70 | // ensure the async write took place 71 | setTimeout(() => { 72 | resolve(ctx); 73 | }, 1000); 74 | }) 75 | .on("error", (err) => { 76 | reject(err); 77 | }); 78 | }); 79 | }); 80 | 81 | it("should create the test file", function () { 82 | return assert.file(["testFileC.md"]); 83 | }); 84 | 85 | after(function () { 86 | fs.removeSync(path.join(pluginsHome, "generator-ui5-test-v5")); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v4/generators/app/index.js: -------------------------------------------------------------------------------- 1 | const Generator = require("yeoman-generator"), 2 | path = require("path"), 3 | yosay = require("yosay"), 4 | glob = require("glob"); 5 | 6 | module.exports = class extends Generator { 7 | static displayName = "This name should be displayed"; 8 | 9 | async writing() { 10 | if (!this.options.embedded) { 11 | this.log(yosay(`Welcome to the ${chalk.red("test-v4")} generator!`)); 12 | } 13 | 14 | const oConfig = this.config.getAll(); 15 | 16 | this.sourceRoot(path.join(__dirname, "templates")); 17 | glob 18 | .sync("**", { 19 | cwd: this.sourceRoot(), 20 | nodir: true, 21 | }) 22 | .forEach((file) => { 23 | const sOrigin = this.templatePath(file); 24 | const sTarget = this.destinationPath(file.replace(/^_/, "").replace(/\/_/, "/")); 25 | 26 | this.fs.copyTpl(sOrigin, sTarget, oConfig); 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v4/generators/app/templates/testFileA.md: -------------------------------------------------------------------------------- 1 | I'm just here for automation testing 2 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v4/generators/hiddenSub/index.js: -------------------------------------------------------------------------------- 1 | const Generator = require("yeoman-generator"); 2 | 3 | module.exports = class extends Generator { 4 | static hidden = true; 5 | 6 | prompting() { 7 | throw "This subgenerator shouldn't be called."; 8 | } 9 | 10 | async writing() {} 11 | }; 12 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v4/generators/hiddenSub/templates/testFileB.md: -------------------------------------------------------------------------------- 1 | I'm just here for automation testing 2 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-ui5-test-v4", 3 | "private": true, 4 | "main": "generators/app/index.js", 5 | "keywords": [ 6 | "yeoman-generator" 7 | ], 8 | "files": [ 9 | "helpers", 10 | "generators" 11 | ], 12 | "dependencies": { 13 | "glob": "^7.2.0", 14 | "yeoman-generator": "^4.13.0", 15 | "yosay": "^2.0.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v5/generators/app/index.js: -------------------------------------------------------------------------------- 1 | import Generator from "yeoman-generator"; 2 | import path from "path"; 3 | import yosay from "yosay"; 4 | import { glob } from "glob"; 5 | import url from "url"; 6 | 7 | const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); 8 | 9 | export default class extends Generator { 10 | static displayName = "This name should be displayed"; 11 | 12 | async writing() { 13 | if (!this.options.embedded) { 14 | this.log(yosay(`Welcome to the ${chalk.red("test-v5")} generator!`)); 15 | } 16 | 17 | const oConfig = this.config.getAll(); 18 | 19 | this.sourceRoot(path.join(__dirname, "templates")); 20 | glob 21 | .sync("**", { 22 | cwd: this.sourceRoot(), 23 | nodir: true, 24 | }) 25 | .forEach((file) => { 26 | const sOrigin = this.templatePath(file); 27 | const sTarget = this.destinationPath(file.replace(/^_/, "").replace(/\/_/, "/")); 28 | 29 | this.fs.copyTpl(sOrigin, sTarget, oConfig); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v5/generators/app/templates/testFileC.md: -------------------------------------------------------------------------------- 1 | I'm just here for automation testing 2 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v5/generators/hiddenSub/index.js: -------------------------------------------------------------------------------- 1 | import Generator from "yeoman-generator"; 2 | 3 | export default class extends Generator { 4 | static hidden = true; 5 | 6 | prompting() { 7 | throw "This subgenerator shouldn't be called."; 8 | } 9 | 10 | async writing() {} 11 | } 12 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v5/generators/hiddenSub/templates/testFileD.md: -------------------------------------------------------------------------------- 1 | I'm just here for automation testing 2 | -------------------------------------------------------------------------------- /test/generator-ui5-test-v5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-ui5-test-v5", 3 | "private": true, 4 | "module": "generators/app/index.js", 5 | "type": "module", 6 | "keywords": [ 7 | "yeoman-generator" 8 | ], 9 | "files": [ 10 | "helpers", 11 | "generators" 12 | ], 13 | "dependencies": { 14 | "glob": "^10.2.7", 15 | "yeoman-generator": "^5.9.0", 16 | "yosay": "^2.0.2" 17 | } 18 | } 19 | --------------------------------------------------------------------------------