@cbs-consulting/generator-btp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +105 -0
  2. package/generators/app/index.js +48 -0
  3. package/generators/cap/additions/.vscode/extensions.json +74 -0
  4. package/generators/cap/additions/.vscode/settings.json +57 -0
  5. package/generators/cap/additions/package.json +31 -0
  6. package/generators/cap/dependencies.json +17 -0
  7. package/generators/cap/index.js +194 -0
  8. package/generators/cap/templates/_.devcontainer/Dockerfile +1 -0
  9. package/generators/cap/templates/_.devcontainer/devcontainer.json +183 -0
  10. package/generators/cap/templates/_.devcontainer/post-create.sh +16 -0
  11. package/generators/cap/templates/_.gitattributes +3 -0
  12. package/generators/cap/templates/_.gitconfig.aliases +16 -0
  13. package/generators/cap/templates/_.gitignore +48 -0
  14. package/generators/cap/templates/_.npmrc +1 -0
  15. package/generators/cap/templates/azure-pipelines.yaml +31 -0
  16. package/generators/cap/templates/base.tsconfig.json +14 -0
  17. package/generators/cap/templates/eslint.config.mjs +4 -0
  18. package/generators/cap/templates/mta-ext/dev.mtaext +11 -0
  19. package/generators/cap/templates/mta-ext/prod.mtaext +11 -0
  20. package/generators/cap/templates/mta-ext/test.mtaext +11 -0
  21. package/generators/cap/templates/mta.yaml +207 -0
  22. package/generators/cap/templates/prettier.config.mjs +2 -0
  23. package/generators/ui5/additions/package.json +9 -0
  24. package/generators/ui5/additions/tsconfig.json +6 -0
  25. package/generators/ui5/dependencies.json +9 -0
  26. package/generators/ui5/index.js +120 -0
  27. package/generators/ui5/templates/eslint.config.mjs +2 -0
  28. package/generators/ui5/templates/ui5lint.config.mjs +2 -0
  29. package/package.json +42 -0
  30. package/utils/jsonFile.js +37 -0
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # CAP/UI5 Foundation Generator
2
+
3
+ A Yeoman generator for creating the foundation of CAP/UI5 projects with TypeScript, ESLint, Prettier, and other essential development configurations.
4
+
5
+ ## Features
6
+
7
+ This generator sets up a solid foundation for your CAP/UI5 projects with:
8
+
9
+ - ✅ **TypeScript** - Full TypeScript configuration with strict settings
10
+ - ✅ **ESLint** - Code quality and linting rules optimized for TypeScript
11
+ - ✅ **Prettier** - Consistent code formatting
12
+ - ✅ **Husky** - Git hooks for pre-commit quality checks
13
+ - ✅ **Jest** - Testing framework with TypeScript support
14
+ - ✅ **GitHub Actions** - CI/CD pipeline for automated testing and building
15
+ - ✅ **VS Code** - Optimized settings and recommended extensions
16
+ - ✅ **Docker** - Container configuration for deployment (optional)
17
+
18
+ ## Installation
19
+
20
+ First, install Yeoman and this generator globally:
21
+
22
+ ```bash
23
+ npm install -g yo
24
+ npm install -g @cbs-consulting/generator-btp
25
+ ```
26
+
27
+ Or install locally in your project:
28
+
29
+ ```bash
30
+ npm install --save-dev @cbs-consulting/generator-btp
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ This generator is split into two sub-generators.
36
+
37
+ 1. CAP flow
38
+
39
+ ```bash
40
+ mkdir my-cap
41
+ cd my-cap
42
+ yo @cbs-consulting/btp:cap
43
+ # read and execute the printed steps shown in the terminal
44
+ ```
45
+
46
+ 2. UI5 flow
47
+
48
+ ```bash
49
+ mkdir my-ui5
50
+ cd my-ui5
51
+ # ensure you have a UI5 project skeleton (or after cds init)
52
+ yo @cbs-consulting/btp:ui5
53
+ # read and execute the printed steps in the terminal
54
+ ```
55
+
56
+ The CAP sub-generator copies all templates from generators/app/templatesCAP into your project and merges additions from generators/app/additionsCAP into existing files. The scripting.txt is shown but not executed automatically.
57
+
58
+ ## Generated Project Structure
59
+
60
+ ```
61
+ my-project/
62
+ ├── src/ # Source TypeScript files
63
+ ├── tests/ # Test files (if Jest is selected)
64
+ ├── dist/ # Compiled output (after build)
65
+ ├── .github/workflows/ # GitHub Actions CI/CD (if selected)
66
+ ├── .vscode/ # VS Code settings (if selected)
67
+ ├── .eslintrc.js # ESLint configuration
68
+ ├── .prettierrc # Prettier configuration
69
+ ├── .gitignore # Git ignore rules
70
+ ├── tsconfig.json # TypeScript configuration
71
+ ├── jest.config.js # Jest configuration (if selected)
72
+ ├── package.json # Project dependencies and scripts
73
+ └── README.md # Project documentation
74
+ ```
75
+
76
+ ## Available Scripts
77
+
78
+ The generated project includes these npm scripts:
79
+
80
+ - `npm run build` - Compile TypeScript to JavaScript
81
+ - `npm run build:watch` - Watch mode compilation
82
+ - `npm run lint` - Run ESLint on your code
83
+ - `npm run lint:fix` - Fix auto-fixable ESLint issues
84
+ - `npm run format` - Format code with Prettier
85
+ - `npm run format:check` - Check if code is properly formatted
86
+ - `npm test` - Run Jest tests (if enabled)
87
+ - `npm run test:watch` - Run tests in watch mode
88
+ - `npm run test:coverage` - Generate test coverage report
89
+
90
+ ## Development
91
+
92
+ To develop this generator locally:
93
+
94
+ 1. Clone this repository
95
+ 2. Install dependencies: `npm install`
96
+ 3. Link the generator: `npm link`
97
+ 4. Test in a new directory: `yo @cbs-consulting/btp`
98
+
99
+ ## Contributing
100
+
101
+ 1. Fork the repository
102
+ 2. Create a feature branch
103
+ 3. Make your changes
104
+ 4. Add tests if applicable
105
+ 5. Submit a pull request
@@ -0,0 +1,48 @@
1
+ const Generator = require("yeoman-generator");
2
+ const chalk = require("chalk");
3
+ const yosay = require("yosay");
4
+ const { resolve } = require("node:path");
5
+
6
+ module.exports = class extends Generator {
7
+ async prompting() {
8
+ this.log(
9
+ yosay(
10
+ `Hey, welcome to the ${chalk.hex("e38330")("@cbs-consulting/generator-btp")}. I'm pretty sure, you will love me!`,
11
+ ),
12
+ );
13
+
14
+ const { kind } = await this.prompt([
15
+ {
16
+ type: "list",
17
+ name: "kind",
18
+ message: "What do you want to do?",
19
+ choices: [
20
+ {
21
+ name: "Bootstrap a new CAP project",
22
+ value: "cap",
23
+ },
24
+ {
25
+ name: "Add best practices to existing UI5 project",
26
+ value: "ui5",
27
+ },
28
+ ],
29
+ },
30
+ ]);
31
+
32
+ this.kind = kind;
33
+ }
34
+
35
+ writing() {
36
+ if (this.kind === "cap") {
37
+ this.composeWith(resolve(__dirname, "../cap/index.js"));
38
+ } else if (this.kind === "ui5") {
39
+ this.composeWith(resolve(__dirname, "../ui5/index.js"));
40
+ } else {
41
+ this.log(chalk.red("Unknown selection"));
42
+ }
43
+ }
44
+
45
+ end() {
46
+ this.log(chalk.green("Done."));
47
+ }
48
+ };
@@ -0,0 +1,74 @@
1
+ {
2
+ "recommendations": [
3
+ // GitHub Copilot
4
+ "GitHub.copilot-chat",
5
+ // JavaScript/TypeScript linting
6
+ "dbaeumer.vscode-eslint",
7
+ // Code formatting
8
+ "esbenp.prettier-vscode",
9
+ // Prettify TS errors
10
+ "YoavBls.pretty-ts-errors",
11
+ // CSV Editing
12
+ "janisdd.vscode-edit-csv",
13
+ // English spell checking
14
+ "streetsidesoftware.code-spell-checker",
15
+ // German spell checking
16
+ "streetsidesoftware.code-spell-checker-german",
17
+ // Enhanced git functionalities
18
+ "eamodio.gitlens",
19
+ // i18n tools and annotations
20
+ "lokalise.i18n-ally",
21
+ // Autocomplete path names
22
+ "christian-kohler.path-intellisense",
23
+ // XML language support
24
+ "redhat.vscode-xml",
25
+ // Markdown linting, editing and preview
26
+ "DavidAnson.vscode-markdownlint",
27
+ "yzhang.markdown-all-in-one",
28
+ "shd101wyy.markdown-preview-enhanced",
29
+
30
+ // easy switch between projects
31
+ "alefragnani.project-manager",
32
+ // resource monitor
33
+ "mutantdino.resourcemonitor",
34
+ // bookmark code places
35
+ "alefragnani.Bookmarks",
36
+ // structured view on ToDo. FixMe commandserrr
37
+ "Gruntfuggly.todo-tree",
38
+ // highlight columns in CSV, TSV
39
+ "mechatroner.rainbow-csv",
40
+ // useful for tempplate string
41
+ "meganrogge.template-string-converter",
42
+ // display errors inline
43
+ "usernamehw.errorlens",
44
+ //live share VS code
45
+ "MS-vsliveshare.vsliveshare",
46
+ // show package.json dependencies updates
47
+ "codeandstuff.package-json-upgrade",
48
+
49
+ // ====================
50
+ // CAP
51
+ // ====================
52
+ // SAP CDS language support
53
+ "SAPSE.vscode-cds",
54
+ // SQLite database viewer
55
+ "qwtel.sqlite-viewer",
56
+ // REST API testing
57
+ "humao.rest-client",
58
+
59
+ // ====================
60
+ // UI5 / Fiori
61
+ // ====================
62
+ // Auto Renaming e.g. XML tags
63
+ "formulahendry.auto-rename-tag",
64
+ // Multiple Fiori Tools
65
+ "SAPSE.sap-ux-fiori-tools-extension-pack",
66
+
67
+ // ====================
68
+ // Tasks
69
+ // ====================
70
+ // Better variables in tasks
71
+ "rioj7.command-variable"
72
+ ],
73
+ "unwantedRecommendations": []
74
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ // ====================
3
+ // Editor
4
+ // ====================
5
+ "editor.formatOnSave": true,
6
+ "editor.formatOnPaste": true,
7
+ "editor.codeActionsOnSave": {
8
+ "source.fixAll.eslint": "always"
9
+ },
10
+ "editor.rulers": [80],
11
+ "editor.minimap.renderCharacters": false, // Small performance boost
12
+ "workbench.startupEditor": "none",
13
+
14
+ // Automatically update import paths when files are moved or renamed
15
+ "typescript.updateImportsOnFileMove.enabled": "always",
16
+ "javascript.updateImportsOnFileMove.enabled": "always",
17
+
18
+ // Default formatters for specific file types
19
+ "[cds]": {
20
+ "editor.defaultFormatter": "SAPSE.vscode-cds"
21
+ },
22
+ "[markdown]": {
23
+ "editor.defaultFormatter": "yzhang.markdown-all-in-one"
24
+ },
25
+ "[javascript][typescript][json][typescriptreact]": {
26
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
27
+ },
28
+
29
+ // ====================
30
+ // Extensions
31
+ // ====================
32
+ "extensions.ignoreRecommendations": true,
33
+ "github.copilot.nextEditSuggestions.enabled": true,
34
+ "gitlens.hovers.currentLine.over": "line",
35
+ "i18n-ally.displayLanguage": "de",
36
+ "i18n-ally.enabledFrameworks": ["ui5"],
37
+ "i18n-ally.keystyle": "flat",
38
+ "i18n-ally.localesPaths": ["i18n", "webapp/i18n"],
39
+ "i18n-ally.sourceLanguage": "de",
40
+ // Usage pattern for i18n keys in translation function t('key')
41
+ "i18n-ally.regex.usageMatchAppend": ["\\bt\\(['\"`]({key})['\"`]\\)"],
42
+
43
+ // ====================
44
+ // Scripts
45
+ // ====================
46
+ "npm.scriptRunner": "node",
47
+
48
+ // ====================
49
+ // Git
50
+ // ====================
51
+ "git.openRepositoryInParentFolders": "always",
52
+
53
+ // ====================
54
+ // Quality
55
+ // ====================
56
+ "cSpell.language": "en,de-DE"
57
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "scripts": {
3
+ "format": "prettier --write \"**/*.(ts|js|json|yml|yaml|xml)\"",
4
+ "lint": "eslint .",
5
+ "lint-fix": "eslint . --fix",
6
+ "lint-quiet": "eslint . --quiet",
7
+ "test": "CDS_TYPESCRIPT='true' jest",
8
+ "build": "rm -rf resources mta_archives dist gen && npx -p @sap/cds-dk cds build --production",
9
+ "deploy-dev": "npm run build && mbt build --mtar archive.mtar && cf deploy mta_archives/archive.mtar -e mta-ext/dev.mtaext --strategy blue-green --skip-testing-phase --skip-idle-start --abort-on-error",
10
+ "coverage-report": "npx serve coverage/lcov-report",
11
+ "git-config": "git config --local include.path ../.gitconfig.aliases",
12
+ "check-deps": "ncu",
13
+ "upgrade-deps": "ncu -u"
14
+ },
15
+ "simple-git-hooks": {
16
+ "pre-commit": "npx lint-staged"
17
+ },
18
+ "lint-staged": {
19
+ "*.{ts,js,json,yml,yaml,xml}": "npm run format",
20
+ "*.{ts,js}": "npm run lint-fix"
21
+ },
22
+ "cds": {
23
+ "requires": {
24
+ "[test]": {
25
+ "auth": {
26
+ "kind": "dummy"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "devDependencies": [
3
+ "cbs-tools",
4
+ "prettier",
5
+ "prettier-plugin-organize-imports",
6
+ "@prettier/plugin-xml",
7
+ "simple-git-hooks",
8
+ "lint-staged",
9
+ "eslint",
10
+ "typescript-eslint",
11
+ "eslint-config-prettier",
12
+ "@sap/eslint-plugin-cds@3",
13
+ "@cap-js/cds-test",
14
+ "ts-jest",
15
+ "npm-check-updates"
16
+ ]
17
+ }
@@ -0,0 +1,194 @@
1
+ const chalk = require("chalk");
2
+ const glob = require("glob");
3
+ const mergewith = require("lodash.mergewith");
4
+ const fs = require("node:fs");
5
+ const { join, resolve } = require("node:path");
6
+ const Generator = require("yeoman-generator");
7
+ const {
8
+ mergeArray,
9
+ readJsonC,
10
+ readJsonCSafe,
11
+ } = require("../../utils/jsonFile");
12
+ const globSync = glob.globSync || glob.sync;
13
+
14
+ module.exports = class extends Generator {
15
+ _tplRoot = resolve(__dirname, "./templates");
16
+ _addRoot = resolve(__dirname, "./additions");
17
+ _capRoot = __dirname;
18
+
19
+ async initializing() {
20
+ const aPrompt = [];
21
+
22
+ aPrompt.push(
23
+ {
24
+ type: "input",
25
+ name: "projectName",
26
+ message: "Enter the project name:",
27
+ validate(sInput) {
28
+ if (sInput.length > 0) {
29
+ return true;
30
+ }
31
+ return "Project name must not be empty.";
32
+ },
33
+ default: "My new CAP project",
34
+ },
35
+ {
36
+ type: "input",
37
+ name: "customerName",
38
+ message: "Enter the customer's name:",
39
+ validate(sInput) {
40
+ if (sInput.length > 0) {
41
+ return true;
42
+ }
43
+ return "Customer name must not be empty.";
44
+ },
45
+ default: "My Customer",
46
+ },
47
+ {
48
+ type: "input",
49
+ name: "dbSchema",
50
+ message: "Enter the database schema name?",
51
+ validate(sInput) {
52
+ if (sInput.length > 0) {
53
+ return true;
54
+ }
55
+ return "Database schema name must not be empty.";
56
+ },
57
+ default: "MY_DB_SCHEMA",
58
+ },
59
+ {
60
+ type: "confirm",
61
+ name: "useAzureDevOps",
62
+ message: "Is Azure DevOps used for version control and CI/CD?",
63
+ default: true,
64
+ },
65
+ );
66
+
67
+ const answers = await this.prompt(aPrompt);
68
+
69
+ // Check if .git directory exists, if not run git init
70
+ if (!fs.existsSync(this.destinationPath(".git"))) {
71
+ this.log(chalk.bold("\n🔧 Initializing git repository..."));
72
+ this.spawnCommandSync("git", ["init"]);
73
+ }
74
+ this.destinationRoot(this.destinationPath());
75
+
76
+ this.log(chalk.bold("\n🚀 Initializing CAP project..."));
77
+ this.spawnCommandSync(
78
+ "cds",
79
+ [
80
+ "init",
81
+ "--add",
82
+ "typescript,mta,xsuaa,hana,sqlite,html5-repo,tiny-sample,http",
83
+ ],
84
+ {
85
+ cwd: this.destinationPath(),
86
+ },
87
+ );
88
+
89
+ answers.projectNameNormalized = answers.projectName
90
+ .toLowerCase()
91
+ .replace(/\s+/g, "-");
92
+ answers.customerNameNormalized = answers.customerName
93
+ .toLowerCase()
94
+ .replace(/\s+/g, "-");
95
+ this.answers = answers;
96
+ }
97
+
98
+ writing() {
99
+ this.log(chalk.bold("\n✍️ Writing files..."));
100
+
101
+ this.sourceRoot(join(__dirname, "templates"));
102
+ globSync("**", {
103
+ cwd: this.sourceRoot(),
104
+ nodir: true,
105
+ }).forEach((fileName) => {
106
+ let sOrigin;
107
+ let sTarget;
108
+
109
+ if (fileName === "azure-pipelines.yaml" && !this.answers.useAzureDevOps) {
110
+ return;
111
+ }
112
+
113
+ sOrigin = this.templatePath(fileName);
114
+ sTarget = this.destinationPath(
115
+ fileName.replace(/^_/, "").replace(/\/_/, "/"),
116
+ );
117
+
118
+ this.fs.copyTpl(sOrigin, sTarget, this.answers);
119
+ });
120
+
121
+ // 2) Merge additions into existing files (scripting.txt is excluded by design)
122
+ // package.json
123
+ const destPkgPath = this.destinationPath("package.json");
124
+ const srcPkgPath = join(this._addRoot, "package.json");
125
+ const destPkg = readJsonCSafe(this, destPkgPath, {});
126
+ const addPkg = readJsonC(srcPkgPath, {});
127
+ const mergedPkg = mergewith({}, destPkg, addPkg, mergeArray);
128
+ mergedPkg.name = this.answers.projectNameNormalized;
129
+ this.fs.writeJSON(destPkgPath, mergedPkg);
130
+
131
+ // tsconfig.json -> ensure it extends base.tsconfig.json
132
+ const destTsPath = this.destinationPath("tsconfig.json");
133
+ const destTs = readJsonCSafe(this, destTsPath, {});
134
+ if (destTs.extends !== "./base.tsconfig.json") {
135
+ destTs.extends = "./base.tsconfig.json";
136
+ }
137
+ this.fs.writeJSON(destTsPath, destTs);
138
+
139
+ // .vscode/settings.json
140
+ const destVsSettingsPath = this.destinationPath(".vscode/settings.json");
141
+ const addVsSettingsPath = join(this._addRoot, ".vscode/settings.json");
142
+ const destVsSettings = readJsonCSafe(this, destVsSettingsPath, {});
143
+ const addVsSettings = readJsonC(addVsSettingsPath, {});
144
+ const mergedVsSettings = mergewith(
145
+ {},
146
+ destVsSettings,
147
+ addVsSettings,
148
+ mergeArray,
149
+ );
150
+ this.fs.writeJSON(destVsSettingsPath, mergedVsSettings);
151
+
152
+ // .vscode/extensions.json (merge arrays)
153
+ const destVsExtPath = this.destinationPath(".vscode/extensions.json");
154
+ const addVsExtPath = join(this._addRoot, ".vscode/extensions.json");
155
+ const destVsExt = readJsonCSafe(this, destVsExtPath, {
156
+ recommendations: [],
157
+ unwantedRecommendations: [],
158
+ });
159
+ const addVsExt = readJsonC(addVsExtPath, {
160
+ recommendations: [],
161
+ unwantedRecommendations: [],
162
+ });
163
+ const mergedVsExt = mergewith({}, destVsExt, addVsExt, mergeArray);
164
+ this.fs.writeJSON(destVsExtPath, mergedVsExt);
165
+
166
+ // Remove undeploy.json from db folder
167
+ const undeployPath = this.destinationPath("db/undeploy.json");
168
+ this.fs.delete(undeployPath);
169
+ }
170
+
171
+ install() {
172
+ this.log(chalk.bold("\n📦 Installing npm dependencies..."));
173
+ const dependenciesPath = join(this._capRoot, "dependencies.json");
174
+ const dependencies = readJsonCSafe(this, dependenciesPath, {});
175
+ const devDeps = Array.isArray(dependencies.devDependencies)
176
+ ? dependencies.devDependencies
177
+ : [];
178
+ if (devDeps.length > 0) {
179
+ this.spawnCommandSync(
180
+ "npm",
181
+ ["install", "--save-dev", "--save-exact"].concat(devDeps),
182
+ );
183
+ }
184
+ }
185
+
186
+ end() {
187
+ this.log(chalk.bold("\n🪝 Initializing simple git hooks..."));
188
+ this.spawnCommandSync("npx", ["simple-git-hooks"]);
189
+
190
+ this.log(chalk.bold.green("\n✅ CAP best practices applied."));
191
+ this.log("Next steps:");
192
+ this.log("1) Review/commit the changes.");
193
+ }
194
+ };
@@ -0,0 +1 @@
1
+ FROM cbsgroupdocker/cbs-default:latest