├── .blueprint ├── cli │ └── commands.mjs ├── generate-sample │ ├── command.mjs │ ├── generator.mjs │ ├── index.mjs │ └── templates │ │ ├── ios.js │ │ └── samples │ │ ├── 21points-jwt.jdl │ │ ├── app-jwt.jdl │ │ ├── app-oauth2.jdl │ │ ├── app-websocket.jdl │ │ └── flickr2-jwt.jdl └── github-build-matrix │ ├── __snapshots__ │ └── generator.spec.mjs.snap │ ├── command.mjs │ ├── generator.mjs │ ├── generator.spec.mjs │ └── index.mjs ├── .circleci └── config.yml ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── dependabot.yml └── workflows │ ├── app.yml │ ├── copyright-update.yml │ ├── generator.yml │ ├── ios.yml │ ├── merge.yml │ └── publish.yml ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── .prettierrc.yml ├── .yo-rc.json ├── .yo-resolve ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── cli ├── cli-customizations.cjs ├── cli.cjs ├── commands.cjs └── print-jhipster-logo.js ├── docs ├── cli-flags.md ├── detox.md ├── distributing-and-updating.md ├── generators.md ├── images │ ├── jh-rn-logo.png │ ├── jh-rn-logo@0.5x.png │ ├── jh-rn-logo@2x.png │ └── jh-rn-logo@3x.png ├── oauth2-oidc.md ├── project-structure.md ├── sample-app-privacy-policy.md ├── storybook.md └── websockets.md ├── eslint.config.js ├── generators ├── app │ ├── __snapshots__ │ │ └── generator.spec.mjs.snap │ ├── command.mjs │ ├── generator.mjs │ ├── generator.spec.mjs │ └── index.mjs ├── constants.mjs ├── heroku │ ├── index.mjs │ └── templates │ │ └── Procfile.ejs ├── react-native │ ├── __snapshots__ │ │ ├── generator-entity.spec.mjs.snap │ │ └── generator.spec.mjs.snap │ ├── command.mjs │ ├── entity-files.mjs │ ├── files.mjs │ ├── generator-entity.spec.mjs │ ├── generator.mjs │ ├── generator.spec.mjs │ ├── index.mjs │ ├── resources │ │ └── expo │ │ │ └── package.json │ ├── support │ │ ├── append-files.js │ │ ├── entity-helpers.js │ │ ├── index.js │ │ ├── patch-babel.js │ │ ├── patch-entity-api.js │ │ ├── patch-in-file.js │ │ └── patch-navigation.js │ └── templates │ │ ├── .detoxrc.cjs.ejs │ │ ├── .gitattributes.jhi.react-native.ejs │ │ ├── .gitignore.jhi.react-native.ejs │ │ ├── .prettierignore.jhi.react-native.ejs │ │ ├── .storybook │ │ ├── index.ts.ejs │ │ ├── main.ts.ejs │ │ ├── preview.tsx.ejs │ │ └── storybook.requires.ts.ejs │ │ ├── App.js.ejs │ │ ├── README.md.ejs │ │ ├── app.json.ejs │ │ ├── app │ │ ├── config │ │ │ ├── app-config.js.ejs │ │ │ └── redux-persist.js.ejs │ │ ├── modules │ │ │ ├── account │ │ │ │ ├── password-reset │ │ │ │ │ ├── forgot-password-screen.js.ejs │ │ │ │ │ ├── forgot-password-screen.styles.js.ejs │ │ │ │ │ ├── forgot-password.reducer.js.ejs │ │ │ │ │ └── forgot-password.sagas.js.ejs │ │ │ │ ├── password │ │ │ │ │ ├── change-password-screen.js.ejs │ │ │ │ │ ├── change-password-screen.styles.js.ejs │ │ │ │ │ ├── change-password.reducer.js.ejs │ │ │ │ │ └── change-password.sagas.js.ejs │ │ │ │ ├── register │ │ │ │ │ ├── register-screen.js.ejs │ │ │ │ │ ├── register-screen.styles.js.ejs │ │ │ │ │ ├── register.reducer.js.ejs │ │ │ │ │ └── register.sagas.js.ejs │ │ │ │ └── settings │ │ │ │ │ ├── settings-screen.js.ejs │ │ │ │ │ └── settings-screen.styles.js.ejs │ │ │ ├── chat │ │ │ │ ├── chat-screen.js.ejs │ │ │ │ ├── chat-screen.styles.js.ejs │ │ │ │ └── chat.reducer.js.ejs │ │ │ ├── entities │ │ │ │ ├── _entityFolder_ │ │ │ │ │ ├── _entityFile_-delete-modal.js.ejs │ │ │ │ │ ├── _entityFile_-detail-screen.js.ejs │ │ │ │ │ ├── _entityFile_-edit-screen.js.ejs │ │ │ │ │ ├── _entityFile_-screen.js.ejs │ │ │ │ │ ├── _entityFile_-styles.js.ejs │ │ │ │ │ ├── _entityFile_.reducer.js.ejs │ │ │ │ │ └── _entityFile_.sagas.js.ejs │ │ │ │ ├── entities-screen.js.ejs │ │ │ │ └── entities-screen.styles.js.ejs │ │ │ ├── home │ │ │ │ ├── home-screen.js.ejs │ │ │ │ ├── home-screen.styles.js.ejs │ │ │ │ └── learn-more-links.component.js.ejs │ │ │ └── login │ │ │ │ ├── login-screen.js.ejs │ │ │ │ ├── login-screen.oauth2.js.ejs │ │ │ │ ├── login-screen.styles.js.ejs │ │ │ │ ├── login.reducer.js.ejs │ │ │ │ ├── login.sagas.js.ejs │ │ │ │ └── login.utils.ts.ejs │ │ ├── navigation │ │ │ ├── drawer │ │ │ │ ├── drawer-button.js.ejs │ │ │ │ ├── drawer-content.js.ejs │ │ │ │ └── touchable-item.tsx.ejs │ │ │ ├── entity-stack.js.ejs │ │ │ ├── modal-screen.js.ejs │ │ │ ├── nav-container.js.ejs │ │ │ ├── nav-ref.js.ejs │ │ │ ├── not-found-screen.tsx.ejs │ │ │ └── oauth-redirect-screen.tsx.ejs │ │ └── shared │ │ │ ├── components │ │ │ ├── alert-message │ │ │ │ ├── alert-message.js.ejs │ │ │ │ ├── alert-message.story.js.ejs │ │ │ │ └── alert-message.styles.js.ejs │ │ │ ├── form │ │ │ │ ├── inputs │ │ │ │ │ ├── jhi-date-input.js.ejs │ │ │ │ │ ├── jhi-date-input.web.js.ejs │ │ │ │ │ ├── jhi-image-input.js.ejs │ │ │ │ │ ├── jhi-list-input.js.ejs │ │ │ │ │ ├── jhi-multi-list-input.js.ejs │ │ │ │ │ ├── jhi-switch-input.js.ejs │ │ │ │ │ └── jhi-text-input.js.ejs │ │ │ │ ├── jhi-form-button.js.ejs │ │ │ │ ├── jhi-form-field.js.ejs │ │ │ │ ├── jhi-form-field.story.js.ejs │ │ │ │ └── jhi-form.js.ejs │ │ │ ├── rounded-button │ │ │ │ ├── rounded-button.js.ejs │ │ │ │ ├── rounded-button.story.js.ejs │ │ │ │ └── rounded-button.styles.js.ejs │ │ │ └── search-bar │ │ │ │ ├── search-bar.js.ejs │ │ │ │ ├── search-bar.story.js.ejs │ │ │ │ └── search-bar.styles.js.ejs │ │ │ ├── fixtures │ │ │ ├── README.md.ejs │ │ │ ├── entity-get-all.json.ejs │ │ │ ├── get-_entityFile_.json.ejs │ │ │ ├── get-account.json.ejs │ │ │ ├── get-oauth-info.json.ejs │ │ │ ├── get-user.json.ejs │ │ │ ├── get-users.json.ejs │ │ │ ├── login.json.ejs │ │ │ ├── update-_entityFile_.json.ejs │ │ │ └── update-user.json.ejs │ │ │ ├── images │ │ │ ├── jhipster_family_member_0_head.png │ │ │ ├── jhipster_family_member_0_head@2x.png │ │ │ ├── jhipster_family_member_0_head@3x.png │ │ │ ├── jhipster_family_member_1_head.png │ │ │ ├── jhipster_family_member_1_head@2x.png │ │ │ ├── jhipster_family_member_1_head@3x.png │ │ │ ├── jhipster_family_member_2_head.png │ │ │ ├── jhipster_family_member_2_head@2x.png │ │ │ ├── jhipster_family_member_2_head@3x.png │ │ │ ├── jhipster_family_member_3_head.png │ │ │ ├── jhipster_family_member_3_head@2x.png │ │ │ ├── jhipster_family_member_3_head@3x.png │ │ │ └── toggle-drawer-icon │ │ │ │ ├── toggle-drawer-icon.png │ │ │ │ ├── toggle-drawer-icon@1.5x.android.png │ │ │ │ ├── toggle-drawer-icon@1.5x.ios.png │ │ │ │ ├── toggle-drawer-icon@1x.android.png │ │ │ │ ├── toggle-drawer-icon@1x.ios.png │ │ │ │ ├── toggle-drawer-icon@2x.android.png │ │ │ │ ├── toggle-drawer-icon@2x.ios.png │ │ │ │ ├── toggle-drawer-icon@3x.android.png │ │ │ │ ├── toggle-drawer-icon@3x.ios.png │ │ │ │ ├── toggle-drawer-icon@4x.android.png │ │ │ │ └── toggle-drawer-icon@4x.ios.png │ │ │ ├── reducers │ │ │ ├── account.reducer.js.ejs │ │ │ ├── app-state.reducer.js.ejs │ │ │ ├── auth-info.reducer.js.ejs │ │ │ ├── create-store.js.ejs │ │ │ ├── index.js.ejs │ │ │ ├── startup.reducer.js.ejs │ │ │ └── user.reducer.js.ejs │ │ │ ├── sagas │ │ │ ├── account.sagas.js.ejs │ │ │ ├── auth-info.saga.js.ejs │ │ │ ├── call-api.saga.js.ejs │ │ │ ├── index.js.ejs │ │ │ ├── startup.saga.js.ejs │ │ │ └── user.sagas.js.ejs │ │ │ ├── services │ │ │ ├── api.js.ejs │ │ │ ├── fixture-api.js.ejs │ │ │ └── rehydration.service.js.ejs │ │ │ ├── themes │ │ │ ├── application.styles.js.ejs │ │ │ ├── colors.js.ejs │ │ │ ├── fonts.js.ejs │ │ │ ├── images.js.ejs │ │ │ ├── index.js.ejs │ │ │ └── metrics.js.ejs │ │ │ ├── util │ │ │ ├── date-transforms.js.ejs │ │ │ ├── immutable-persistence-transform.js.ejs │ │ │ ├── pagination-utils.js.ejs │ │ │ ├── snake-to-camel-case.js.ejs │ │ │ ├── url-utils.js.ejs │ │ │ └── use-did-update-effect.js.ejs │ │ │ └── websockets │ │ │ ├── websocket.sagas.js.ejs │ │ │ └── websocket.service.js.ejs │ │ ├── assets │ │ ├── adaptive-icon.png │ │ ├── favicon.png │ │ ├── icon.png │ │ └── splash.png │ │ ├── babel.config.js.ejs │ │ ├── e2e │ │ ├── account │ │ │ ├── change-password-screen.spec.js.ejs │ │ │ ├── login-screen.spec.js.ejs │ │ │ └── settings-screen.spec.js.ejs │ │ ├── entities │ │ │ └── _entityFile_.spec.js.ejs │ │ ├── home-screen.spec.js.ejs │ │ ├── jest.config.cjs.ejs │ │ ├── scripts │ │ │ ├── download-expo.sh.ejs │ │ │ └── setup.sh.ejs │ │ ├── utils.js.ejs │ │ └── websockets │ │ │ └── chat-screen.spec.js.ejs │ │ ├── eslint.config.js.jhi.react-native.ejs │ │ ├── metro.config.js.ejs │ │ ├── package.json │ │ ├── package.json.ejs │ │ ├── patches │ │ ├── react-native-keyboard-aware-scroll-view+0.9.4.patch.ejs │ │ └── react-native-sectioned-multi-select+0.8.1.patch.ejs │ │ └── test │ │ ├── setup.js.ejs │ │ └── spec │ │ ├── modules │ │ ├── account │ │ │ ├── password-reset │ │ │ │ ├── forgot-password.reducer.spec.js.ejs │ │ │ │ └── forgot-password.sagas.spec.js.ejs │ │ │ ├── password │ │ │ │ ├── change-password.reducer.spec.js.ejs │ │ │ │ └── change-password.sagas.spec.js.ejs │ │ │ └── register │ │ │ │ ├── register.reducer.spec.js.ejs │ │ │ │ └── register.sagas.spec.js.ejs │ │ ├── chat │ │ │ └── chat.reducer.spec.js.ejs │ │ ├── entities │ │ │ └── _entityFolder_ │ │ │ │ ├── _entityFile_.reducer.spec.js.ejs │ │ │ │ └── _entityFile_.sagas.spec.js.ejs │ │ └── login │ │ │ ├── login.reducer.spec.js.ejs │ │ │ └── login.sagas.spec.js.ejs │ │ └── shared │ │ ├── components │ │ ├── alert-message │ │ │ └── alert-message.spec.js.ejs │ │ ├── rounded-button │ │ │ └── rounded-button.spec.js.ejs │ │ └── search-bar │ │ │ └── search-bar.spec.js.ejs │ │ ├── navigation │ │ └── drawer │ │ │ └── drawer-button.spec.js.ejs │ │ ├── reducers │ │ ├── account.reducer.spec.js.ejs │ │ ├── app-state.reducer.spec.js.ejs │ │ └── user.reducer.spec.js.ejs │ │ ├── sagas │ │ ├── account.sagas.spec.js.ejs │ │ ├── call-api.saga.spec.js.ejs │ │ ├── startup.saga.spec.js.ejs │ │ └── user.saga.spec.js.ejs │ │ ├── services │ │ └── fixture-api.spec.js.ejs │ │ └── websockets │ │ ├── websocket-service.spec.js.ejs │ │ └── websocket.sagas.spec.js.ejs └── spring-boot │ ├── __snapshots__ │ └── generator.spec.mjs.snap │ ├── command.mjs │ ├── generator.mjs │ ├── generator.spec.mjs │ ├── index.mjs │ └── templates │ └── src │ └── main │ └── resources │ └── config │ └── application-e2e-cors.yml ├── package-lock.json ├── package.json ├── test ├── expected-files.js ├── jdl │ ├── app.jdl │ ├── entities-21points.jdl │ ├── entities-flickr2.jdl │ └── entities.jdl └── scripts │ ├── copy-jdl-file.sh │ ├── display-tools.sh │ ├── generate-jhipster-backend.sh │ ├── generate-react-native-app.sh │ ├── git-config.sh │ ├── install-detox.sh │ ├── install-heroku-cli.sh │ ├── install-node-dependencies.sh │ ├── package-jhipster-backend.sh │ ├── rename-detox-screenshots.sh │ ├── run-detox-tests.sh │ ├── run-react-native-tests.sh │ ├── start-android-emulator.sh │ └── stop-android-emulator.sh ├── tsconfig.json ├── vitest.config.ts └── vitest.test-setup.ts /.blueprint/cli/commands.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2025 the original author or authors from the JHipster project. 3 | * 4 | * This file is part of the JHipster project, see https://www.jhipster.tech/ 5 | * for more information. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * https://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | const defaultCommands = { 21 | 'generate-sample': { 22 | desc: 'Generate a test sample', 23 | blueprint: '@jhipster/jhipster-dev', 24 | }, 25 | 'github-build-matrix': { 26 | desc: 'Build a matrix of jobs for github actions', 27 | blueprint: '@jhipster/jhipster-dev', 28 | }, 29 | }; 30 | 31 | export default defaultCommands; 32 | -------------------------------------------------------------------------------- /.blueprint/generate-sample/index.mjs: -------------------------------------------------------------------------------- 1 | export { default } from './generator.mjs'; 2 | export { default as command } from './command.mjs'; 3 | -------------------------------------------------------------------------------- /.blueprint/generate-sample/templates/ios.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url'; 2 | import { getGithubSamplesGroup } from 'generator-jhipster/testing'; 3 | 4 | const { samples } = await getGithubSamplesGroup(fileURLToPath(new URL('./', import.meta.url)), 'samples'); 5 | 6 | export default Object.fromEntries( 7 | Object.entries(samples).map(([sample, spec]) => [ 8 | sample, 9 | { 10 | ...spec, 11 | 'docker-services': `${!sample.includes('oauth2')}`, 12 | ...(sample.includes('oauth2') 13 | ? { os: 'macos-13', 'default-environment': 'prod', 'skip-e2e': 'true' } 14 | : { os: 'macos-15', 'default-environment': 'dev' }), 15 | }, 16 | ]), 17 | ); 18 | -------------------------------------------------------------------------------- /.blueprint/generate-sample/templates/samples/21points-jwt.jdl: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the application and entity model for the 21-Points (https://github.com/mraible/21-points) application from Matt Raible 3 | */ 4 | 5 | application { 6 | config { 7 | authenticationType jwt 8 | skipClient true 9 | devDatabaseType h2Disk 10 | creationTimestamp 1617901618886 11 | jwtSecretKey "ZjY4MTM4YjI5YzMwZjhjYjI2OTNkNTRjMWQ5Y2Q0Y2YwOWNmZTE2NzRmYzU3NTMwM2NjOTE3MTllOTM3MWRkMzcyYTljMjVmNmQ0Y2MxOTUzODc0MDhhMTlkMDIxMzI2YzQzZDM2ZDE3MmQ3NjVkODk3OTVmYzljYTQyZDNmMTQ=" 12 | } 13 | entities * 14 | } 15 | 16 | @ChangelogDate(20240101000000) 17 | entity BloodPressure { 18 | timestamp ZonedDateTime required 19 | systolic Integer required 20 | diastolic Integer required 21 | } 22 | @ChangelogDate(20240102000000) 23 | entity Weight { 24 | timestamp ZonedDateTime required 25 | weight Double required 26 | } 27 | @ChangelogDate(20240103000000) 28 | entity Points { 29 | date LocalDate required 30 | exercise Integer 31 | meals Integer 32 | alcohol Integer 33 | notes String maxlength(140) 34 | } 35 | @ChangelogDate(20240105000000) 36 | entity Preferences { 37 | weeklyGoal Integer required min(10) max(21) 38 | weightUnits Units required 39 | } 40 | 41 | enum Units { 42 | KG, 43 | LB 44 | } 45 | 46 | relationship OneToOne { 47 | Preferences{user(login)} to User with builtInEntity 48 | } 49 | relationship ManyToOne { 50 | BloodPressure{user(login)} to User with builtInEntity 51 | Weight{user(login)} to User with builtInEntity 52 | Points{user(login)} to User with builtInEntity 53 | } 54 | 55 | paginate BloodPressure, Weight with infinite-scroll 56 | paginate Points with pagination 57 | -------------------------------------------------------------------------------- /.blueprint/generate-sample/templates/samples/app-jwt.jdl: -------------------------------------------------------------------------------- 1 | application { 2 | config { 3 | authenticationType jwt 4 | creationTimestamp 1617901618886 5 | jwtSecretKey "ZjY4MTM4YjI5YzMwZjhjYjI2OTNkNTRjMWQ5Y2Q0Y2YwOWNmZTE2NzRmYzU3NTMwM2NjOTE3MTllOTM3MWRkMzcyYTljMjVmNmQ0Y2MxOTUzODc0MDhhMTlkMDIxMzI2YzQzZDM2ZDE3MmQ3NjVkODk3OTVmYzljYTQyZDNmMTQ=" 6 | skipClient true 7 | devDatabaseType h2Disk 8 | } 9 | entities * 10 | } 11 | 12 | @ChangelogDate(20240101000000) 13 | entity Region { 14 | regionName String 15 | } 16 | 17 | @ChangelogDate(20240102000000) 18 | entity Country { 19 | countryName String 20 | } 21 | 22 | @ChangelogDate(20240103000000) 23 | entity Location { 24 | streetAddress String 25 | postalCode String 26 | city String 27 | stateProvince String 28 | } 29 | 30 | @ChangelogDate(20240104000000) 31 | entity Department { 32 | departmentName String required 33 | departmentPic ImageBlob 34 | departmentDescription TextBlob 35 | departmentInfoPdf Blob 36 | } 37 | 38 | @ChangelogDate(20240105000000) 39 | entity Task { 40 | title String 41 | description String 42 | } 43 | 44 | @ChangelogDate(20240106000000) 45 | entity Employee { 46 | firstName String 47 | lastName String 48 | email String 49 | phoneNumber String 50 | hireDate Instant 51 | salary Long 52 | commissionPct Long 53 | } 54 | 55 | @ChangelogDate(20240107000000) 56 | entity Job { 57 | jobTitle String 58 | minSalary Long 59 | maxSalary Long 60 | } 61 | 62 | @ChangelogDate(20240108000000) 63 | entity JobHistory { 64 | startDate Instant 65 | endDate Instant 66 | language Language 67 | } 68 | 69 | enum Language { 70 | FRENCH, ENGLISH, SPANISH 71 | } 72 | 73 | relationship ManyToMany { 74 | Job{task(title)} to Task{job(jobTitle)} 75 | } 76 | 77 | relationship OneToMany { 78 | Employee{job(jobTitle)} to Job{employee(email)} 79 | Department{employee(email)} to 80 | Employee{department(departmentName)} 81 | } 82 | 83 | relationship ManyToOne { 84 | Department{location(streetAddress)} to Location 85 | Location{country(countryName)} to Country 86 | Country{region(regionName)} to Region 87 | Employee{manager} to Employee 88 | } 89 | 90 | relationship OneToOne { 91 | JobHistory{job} to Job 92 | JobHistory{department} to Department 93 | JobHistory{employee} to Employee 94 | } 95 | 96 | paginate JobHistory, Employee with infinite-scroll 97 | paginate Job with pagination 98 | 99 | service all with serviceClass 100 | -------------------------------------------------------------------------------- /.blueprint/generate-sample/templates/samples/app-oauth2.jdl: -------------------------------------------------------------------------------- 1 | application { 2 | config { 3 | authenticationType oauth2 4 | skipClient true 5 | devDatabaseType h2Disk 6 | creationTimestamp 1617901618886 7 | jwtSecretKey "ZjY4MTM4YjI5YzMwZjhjYjI2OTNkNTRjMWQ5Y2Q0Y2YwOWNmZTE2NzRmYzU3NTMwM2NjOTE3MTllOTM3MWRkMzcyYTljMjVmNmQ0Y2MxOTUzODc0MDhhMTlkMDIxMzI2YzQzZDM2ZDE3MmQ3NjVkODk3OTVmYzljYTQyZDNmMTQ=" 8 | } 9 | entities * 10 | } 11 | 12 | @ChangelogDate(20240101000000) 13 | entity Region { 14 | regionName String 15 | } 16 | 17 | @ChangelogDate(20240102000000) 18 | entity Country { 19 | countryName String 20 | } 21 | 22 | @ChangelogDate(20240103000000) 23 | entity Location { 24 | streetAddress String 25 | postalCode String 26 | city String 27 | stateProvince String 28 | } 29 | 30 | @ChangelogDate(20240104000000) 31 | entity Department { 32 | departmentName String required 33 | departmentPic ImageBlob 34 | departmentDescription TextBlob 35 | departmentInfoPdf Blob 36 | } 37 | 38 | @ChangelogDate(20240105000000) 39 | entity Task { 40 | title String 41 | description String 42 | } 43 | 44 | @ChangelogDate(20240106000000) 45 | entity Employee { 46 | firstName String 47 | lastName String 48 | email String 49 | phoneNumber String 50 | hireDate Instant 51 | salary Long 52 | commissionPct Long 53 | } 54 | 55 | @ChangelogDate(20240107000000) 56 | entity Job { 57 | jobTitle String 58 | minSalary Long 59 | maxSalary Long 60 | } 61 | 62 | @ChangelogDate(20240108000000) 63 | entity JobHistory { 64 | startDate Instant 65 | endDate Instant 66 | language Language 67 | } 68 | 69 | enum Language { 70 | FRENCH, ENGLISH, SPANISH 71 | } 72 | 73 | relationship ManyToMany { 74 | Job{task(title)} to Task{job(jobTitle)} 75 | } 76 | 77 | relationship OneToMany { 78 | Employee{job(jobTitle)} to Job{employee(email)} 79 | Department{employee(email)} to 80 | Employee{department(departmentName)} 81 | } 82 | 83 | relationship ManyToOne { 84 | Department{location(streetAddress)} to Location 85 | Location{country(countryName)} to Country 86 | Country{region(regionName)} to Region 87 | Employee{manager} to Employee 88 | } 89 | 90 | relationship OneToOne { 91 | JobHistory{job} to Job 92 | JobHistory{department} to Department 93 | JobHistory{employee} to Employee 94 | } 95 | 96 | paginate JobHistory, Employee with infinite-scroll 97 | paginate Job with pagination 98 | 99 | service all with serviceClass 100 | -------------------------------------------------------------------------------- /.blueprint/generate-sample/templates/samples/app-websocket.jdl: -------------------------------------------------------------------------------- 1 | application { 2 | config { 3 | authenticationType jwt 4 | websocket spring-websocket 5 | skipClient true 6 | devDatabaseType h2Disk 7 | creationTimestamp 1617901618886 8 | jwtSecretKey "ZjY4MTM4YjI5YzMwZjhjYjI2OTNkNTRjMWQ5Y2Q0Y2YwOWNmZTE2NzRmYzU3NTMwM2NjOTE3MTllOTM3MWRkMzcyYTljMjVmNmQ0Y2MxOTUzODc0MDhhMTlkMDIxMzI2YzQzZDM2ZDE3MmQ3NjVkODk3OTVmYzljYTQyZDNmMTQ=" 9 | } 10 | entities * 11 | } 12 | 13 | @ChangelogDate(20240101000000) 14 | entity Region { 15 | regionName String 16 | } 17 | 18 | @ChangelogDate(20240102000000) 19 | entity Country { 20 | countryName String 21 | } 22 | 23 | @ChangelogDate(20240103000000) 24 | entity Location { 25 | streetAddress String 26 | postalCode String 27 | city String 28 | stateProvince String 29 | } 30 | 31 | @ChangelogDate(20240104000000) 32 | entity Department { 33 | departmentName String required 34 | departmentPic ImageBlob 35 | departmentDescription TextBlob 36 | departmentInfoPdf Blob 37 | } 38 | 39 | @ChangelogDate(20240105000000) 40 | entity Task { 41 | title String 42 | description String 43 | } 44 | 45 | @ChangelogDate(20240106000000) 46 | entity Employee { 47 | firstName String 48 | lastName String 49 | email String 50 | phoneNumber String 51 | hireDate Instant 52 | salary Long 53 | commissionPct Long 54 | } 55 | 56 | @ChangelogDate(20240107000000) 57 | entity Job { 58 | jobTitle String 59 | minSalary Long 60 | maxSalary Long 61 | } 62 | 63 | @ChangelogDate(20240108000000) 64 | entity JobHistory { 65 | startDate Instant 66 | endDate Instant 67 | language Language 68 | } 69 | 70 | enum Language { 71 | FRENCH, ENGLISH, SPANISH 72 | } 73 | 74 | relationship ManyToMany { 75 | Job{task(title)} to Task{job(jobTitle)} 76 | } 77 | 78 | relationship OneToMany { 79 | Employee{job(jobTitle)} to Job{employee(email)} 80 | Department{employee(email)} to 81 | Employee{department(departmentName)} 82 | } 83 | 84 | relationship ManyToOne { 85 | Department{location(streetAddress)} to Location 86 | Location{country(countryName)} to Country 87 | Country{region(regionName)} to Region 88 | Employee{manager} to Employee 89 | } 90 | 91 | relationship OneToOne { 92 | JobHistory{job} to Job 93 | JobHistory{department} to Department 94 | JobHistory{employee} to Employee 95 | } 96 | 97 | paginate JobHistory, Employee with infinite-scroll 98 | paginate Job with pagination 99 | 100 | service all with serviceClass 101 | -------------------------------------------------------------------------------- /.blueprint/generate-sample/templates/samples/flickr2-jwt.jdl: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the application and entity model for the Flickr app (https://github.com/mraible/mobile-jhipster/) application from Matt Raible 3 | */ 4 | 5 | application { 6 | config { 7 | authenticationType jwt 8 | skipClient true 9 | devDatabaseType h2Disk 10 | creationTimestamp 1617901618886 11 | jwtSecretKey "ZjY4MTM4YjI5YzMwZjhjYjI2OTNkNTRjMWQ5Y2Q0Y2YwOWNmZTE2NzRmYzU3NTMwM2NjOTE3MTllOTM3MWRkMzcyYTljMjVmNmQ0Y2MxOTUzODc0MDhhMTlkMDIxMzI2YzQzZDM2ZDE3MmQ3NjVkODk3OTVmYzljYTQyZDNmMTQ=" 12 | } 13 | entities * 14 | } 15 | 16 | @ChangelogDate(20240101000000) 17 | entity Album { 18 | title String required 19 | description TextBlob 20 | created Instant 21 | } 22 | 23 | @ChangelogDate(20240102000000) 24 | entity Photo { 25 | title String required 26 | description TextBlob 27 | image ImageBlob required 28 | height Integer 29 | width Integer 30 | taken Instant 31 | uploaded Instant 32 | } 33 | 34 | @ChangelogDate(20240103000000) 35 | entity Tag { 36 | name String required minlength(2) 37 | } 38 | 39 | relationship ManyToOne { 40 | Album{user(login)} to User with builtInEntity 41 | Photo{album(title)} to Album 42 | } 43 | 44 | relationship ManyToMany { 45 | Photo{tag(name)} to Tag{photo} 46 | } 47 | 48 | paginate Album with pagination 49 | paginate Photo, Tag with infinite-scroll 50 | -------------------------------------------------------------------------------- /.blueprint/github-build-matrix/command.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('generator-jhipster').JHipsterCommandDefinition} 3 | */ 4 | const command = { 5 | configs: { 6 | samplesFolder: { 7 | description: 'Samples folder', 8 | cli: { 9 | type: String, 10 | }, 11 | scope: 'generator', 12 | }, 13 | samplesGroup: { 14 | description: 'Samples Group', 15 | argument: { 16 | type: String, 17 | }, 18 | default: 'samples', 19 | scope: 'generator', 20 | }, 21 | }, 22 | options: {}, 23 | }; 24 | 25 | export default command; 26 | -------------------------------------------------------------------------------- /.blueprint/github-build-matrix/generator.mjs: -------------------------------------------------------------------------------- 1 | import { join } from 'node:path'; 2 | import BaseGenerator from 'generator-jhipster/generators/base'; 3 | import { convertToGitHubMatrix, getGithubOutputFile, getGithubSamplesGroup, setGithubTaskOutput } from 'generator-jhipster/testing'; 4 | 5 | export default class extends BaseGenerator { 6 | /** @type {string} */ 7 | samplesFolder; 8 | /** @type {string} */ 9 | samplesGroup; 10 | /** @type {object} */ 11 | matrix; 12 | 13 | constructor(args, opts, features) { 14 | super(args, opts, { ...features, queueCommandTasks: true, jhipsterBootstrap: false }); 15 | } 16 | 17 | get [BaseGenerator.WRITING]() { 18 | return this.asWritingTaskGroup({ 19 | async buildMatrix() { 20 | const { samplesGroup = 'samples' } = this; 21 | const templatePath = this.templatePath('../../generate-sample/templates/'); 22 | const samplesFolder = this.samplesFolder ? join(templatePath, this.samplesFolder) : templatePath; 23 | const { samples, warnings } = await getGithubSamplesGroup(samplesFolder, samplesGroup); 24 | if (warnings.length > 0) { 25 | this.log.info(warnings.join('\n')); 26 | } 27 | this.matrix = convertToGitHubMatrix(samples); 28 | const githubOutputFile = getGithubOutputFile(); 29 | this.log.info('matrix', this.matrix); 30 | if (githubOutputFile) { 31 | setGithubTaskOutput('matrix', JSON.stringify(this.matrix)); 32 | } 33 | }, 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.blueprint/github-build-matrix/generator.spec.mjs: -------------------------------------------------------------------------------- 1 | import { basename, dirname, join } from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | import { beforeAll, describe, expect, it } from 'vitest'; 4 | import { getGithubSamplesGroups, defaultHelpers as helpers, runResult } from 'generator-jhipster/testing'; 5 | 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = dirname(__filename); 8 | 9 | const generator = basename(__dirname); 10 | 11 | describe(`generator - ${generator}`, async () => { 12 | const groups = await getGithubSamplesGroups(join(__dirname, '../generate-sample/templates/')); 13 | for (const workflow of groups.map(sample => sample.split('.')[0])) { 14 | describe(`with ${workflow}`, () => { 15 | beforeAll(async () => { 16 | await helpers.runJHipster(join(__dirname, 'index.mjs'), { useEnvironmentBuilder: true }).withArguments(workflow); 17 | }); 18 | 19 | it('should match matrix value', () => { 20 | expect(runResult.generator.matrix).toMatchSnapshot(); 21 | }); 22 | }); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /.blueprint/github-build-matrix/index.mjs: -------------------------------------------------------------------------------- 1 | export { default } from './generator.mjs'; 2 | export { default as command } from './command.mjs'; 3 | -------------------------------------------------------------------------------- /.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 | 9 | # We recommend you to keep these unchanged 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | # Change these settings to your own preference 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | **Describe the bug** 7 | 8 | 9 | 10 | **Instructions To reproduce** 11 | 12 | 13 | 14 | **JHipster app's `.yo-rc.json`** 15 | 16 | 17 | 18 | **Entity JDL or JSON files** 19 | 20 | 21 | 22 | **Versions (please complete the following information):** 23 | 24 | - JHipster React Native Version: 25 | - `jhipster info` output: 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/generators/react-native/templates/' 5 | schedule: 6 | interval: 'weekly' 7 | # Check for npm updates on Wednesday due to Expo updates 8 | day: 'wednesday' 9 | open-pull-requests-limit: 30 10 | versioning-strategy: increase 11 | groups: 12 | redux: 13 | patterns: 14 | - '@reduxjs/*' 15 | - '*-redux' 16 | - 'redux*' 17 | storybook: 18 | patterns: 19 | - '@storybook/*' 20 | react-navigation: 21 | patterns: 22 | - '@react-navigation/*' 23 | eslint: 24 | patterns: 25 | - '@eslint/js' 26 | - 'eslint' 27 | - package-ecosystem: 'npm' 28 | directory: '/generators/react-native/resources/expo/' 29 | schedule: 30 | interval: 'weekly' 31 | # Check for npm updates on Wednesday due to Expo updates 32 | day: 'wednesday' 33 | open-pull-requests-limit: 30 34 | versioning-strategy: increase 35 | groups: 36 | # Packages managed by expo, must be updated using expo cli, keep for reference and check only. 37 | expo-managed: 38 | patterns: 39 | - '@react-native-async-storage/async-storage' 40 | - '@react-native-community/datetimepicker' 41 | - '@react-native-masked-view/masked-view' 42 | - '@react-native-picker/picker' 43 | - 'expo-auth-session' 44 | - 'expo-constants' 45 | - 'expo-image-picker' 46 | - 'expo-linking' 47 | - 'expo-random' 48 | - 'expo-splash-screen' 49 | - 'expo-status-bar' 50 | - 'expo-web-browser' 51 | - 'jest-expo' 52 | - 'react-native-gesture-handler' 53 | - 'react-native-reanimated' 54 | - 'react-native-safe-area-context' 55 | - 'react-native-screens' 56 | update-types: 57 | - 'minor' 58 | - 'major' 59 | - package-ecosystem: 'github-actions' 60 | directory: '/' 61 | schedule: 62 | interval: 'weekly' 63 | # Check for npm updates on Wednesday due to Expo updates 64 | day: 'wednesday' 65 | open-pull-requests-limit: 30 66 | -------------------------------------------------------------------------------- /.github/workflows/copyright-update.yml: -------------------------------------------------------------------------------- 1 | name: Copyright Update 2 | on: 3 | schedule: 4 | - cron: '0 0 31 12 *' # Repeats December 31st every year 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | build: 11 | permissions: 12 | contents: write # for peter-evans/create-pull-request to create branch 13 | pull-requests: write # for peter-evans/create-pull-request to create a PR 14 | name: copyright update 15 | if: github.repository == 'jhipster/generator-jhipster-react-native' 16 | runs-on: ubuntu-latest 17 | timeout-minutes: 40 18 | steps: 19 | # Checkout 20 | - uses: actions/checkout@v4.1.1 21 | with: 22 | ref: main 23 | fetch-depth: 0 24 | # Update the copyright headers 25 | - name: Find and Replace 26 | run: | 27 | CURRENT_YEAR=$(date +'%Y') 28 | NEW_YEAR=$(($CURRENT_YEAR + 1)) 29 | grep -rlZE "Copyright ([0-9]+)-$CURRENT_YEAR" . | xargs -0 sed -i -E "s/Copyright ([0-9]+)-$CURRENT_YEAR/Copyright \1-$NEW_YEAR/g" 30 | # Create PR 31 | - name: Create Pull Request 32 | uses: peter-evans/create-pull-request@v7 33 | with: 34 | token: ${{ secrets.GITHUB_TOKEN }} 35 | commit-message: 'Update copyright headers' 36 | title: 'Update Copyright Headers' 37 | body: 'This is an automated pull request to update the copyright headers' 38 | branch: 'copyright-date-update' 39 | author: 'jhipster-bot ' 40 | -------------------------------------------------------------------------------- /.github/workflows/generator.yml: -------------------------------------------------------------------------------- 1 | name: Generator Tests 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - '*' 7 | - '!dependabot/**' 8 | pull_request: 9 | defaults: 10 | run: 11 | shell: bash 12 | concurrency: 13 | # Group by workflow and ref, limit to 1 for pull requests 14 | group: ${{ github.workflow }}-${{ github.ref }}-${{ startsWith(github.ref, 'refs/pull/') || github.run_number }} 15 | # Cancel intermediate pull request builds 16 | cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} 17 | jobs: 18 | lint-and-test: 19 | runs-on: ubuntu-latest 20 | if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.pull_request.title, '[skip ci]') && !contains(github.event.pull_request.title, '[ci skip]') && !contains(github.event.ref_type, '[tag]')" 21 | timeout-minutes: 20 22 | strategy: 23 | fail-fast: false 24 | steps: 25 | - uses: actions/checkout@v4.1.1 26 | - uses: actions/setup-node@v4.0.2 27 | with: 28 | node-version: 20.x 29 | - run: git --no-pager log -n 10 --graph --pretty='%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue) <%an>%Creset' --abbrev-commit 30 | - name: Config git variables 31 | env: 32 | JHI_SCRIPTS: ./test/scripts 33 | run: $JHI_SCRIPTS/git-config.sh 34 | - name: npm ci 35 | run: npm ci 36 | - name: lint and test 37 | run: npm run test 38 | - name: check-dependencies 39 | run: | 40 | cd generators/react-native/resources/expo 41 | npm run check-dependencies 42 | env: 43 | EXPO_OFFLINE: 1 44 | -------------------------------------------------------------------------------- /.github/workflows/merge.yml: -------------------------------------------------------------------------------- 1 | name: Auto Merge 2 | 3 | on: pull_request_target 4 | 5 | permissions: 6 | pull-requests: write 7 | contents: write 8 | 9 | jobs: 10 | enable-auto-merge: 11 | runs-on: ubuntu-latest 12 | if: ${{ github.repository == 'jhipster/generator-jhipster-react-native' && github.event.pull_request.user.login == 'dependabot[bot]' }} 13 | steps: 14 | - name: Dependabot metadata 15 | id: dependabot-metadata 16 | uses: dependabot/fetch-metadata@v2.3.0 17 | - name: Enable auto-merge for Dependabot PRs 18 | if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} 19 | run: gh pr merge --auto --merge "$PR_URL" 20 | env: 21 | PR_URL: ${{github.event.pull_request.html_url}} 22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 23 | - uses: actions/checkout@v4 24 | with: 25 | ref: ${{ github.head_ref }} 26 | if: steps.dependabot-metadata.outputs.dependency-names == 'expo' 27 | - uses: jhipster/actions/setup-git@v0 28 | if: steps.dependabot-metadata.outputs.dependency-names == 'expo' 29 | - run: | 30 | cd generators/react-native/resources/expo 31 | npm run update-dependencies 32 | git commit -a -m "update expo managed dependencies" || true 33 | git push -u origin || true 34 | if: steps.dependabot-metadata.outputs.dependency-names == 'expo' 35 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4.1.1 12 | - uses: actions/setup-node@v4.0.2 13 | with: 14 | node-version: 20 15 | registry-url: https://registry.npmjs.org/ 16 | - run: npm ci 17 | - run: npm publish --access public 18 | env: 19 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 20 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | ./node_modules/.bin/lint-staged 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | .git 4 | 5 | # blueprint rules: 6 | generators/**/templates/**/ 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | printWidth: 140 2 | singleQuote: true 3 | tabWidth: 2 4 | useTabs: false 5 | arrowParens: avoid 6 | bracketSameLine: false 7 | plugins: 8 | - prettier-plugin-packagejson 9 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-jhipster": { 3 | "additionalSubGenerators": "", 4 | "baseName": "react-native", 5 | "caret": true, 6 | "cli": true, 7 | "cliName": "rnhipster", 8 | "entities": [], 9 | "generators": { 10 | "app": { 11 | "command": false, 12 | "priorities": ["initializing", "prompting", "configuring", "loading", "default", "writing", "install", "end"], 13 | "sbs": true, 14 | "written": true 15 | }, 16 | "react-native": { 17 | "command": false, 18 | "priorities": ["initializing", "prompting", "configuring", "default", "end"], 19 | "sbs": true, 20 | "written": true 21 | }, 22 | "spring-boot": { 23 | "command": false, 24 | "priorities": ["initializing", "prompting", "configuring", "default", "end"], 25 | "sbs": true, 26 | "written": true 27 | } 28 | }, 29 | "githubWorkflows": true, 30 | "jhipsterVersion": "8.10.0", 31 | "js": false, 32 | "localBlueprint": false, 33 | "packageJsonType": "module", 34 | "sampleWritten": true, 35 | "skipCommitHook": true, 36 | "subGenerators": ["app", "react-native", "spring-boot"] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.yo-resolve: -------------------------------------------------------------------------------- 1 | .github/workflows/** skip 2 | README.md skip 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Git Repository Setup 4 | 5 | Fork the repository and clone it to your workspace. Then add this repository as the upstream repository: 6 | 7 | ```sh 8 | git remote add upstream https://github.com/jhipster/generator-jhipster-react-native.git 9 | ``` 10 | 11 | ## Running Local Blueprint for Development 12 | 13 | During development of blueprint, please note the below steps. They are very important. 14 | 15 | 1. Link your blueprint globally 16 | 17 | ```bash 18 | cd generator-jhipster-react-native 19 | npm link 20 | ``` 21 | 22 | 2. Link a development version of JHipster to your blueprint (optional: required only if you want to use a non-released JHipster version, like the main branch or your own custom fork) 23 | 24 | ```bash 25 | cd generator-jhipster 26 | npm link 27 | 28 | cd generator-jhipster-react-native 29 | npm link generator-jhipster 30 | ``` 31 | 32 | ## Generating an App 33 | 34 | Generating an app using your local `generator-jhipster-react-native` blueprint is similar to normal. 35 | 36 | ```sh 37 | jhipster --blueprints react-native 38 | ``` 39 | 40 | ## Making Changes to the Generator 41 | 42 | It's recommended to create and test the changes in a generated app, then transfer the changes to the boilerplate files. Use `git` to help track your progress. 43 | 44 | When generating an app, make sure to link the blueprint after installing `node_modules`. 45 | 46 | ```sh 47 | # in your generated app folder 48 | npm link generator-jhipster-react-native 49 | ``` 50 | 51 | After linking `generator-jhipster-react-native`, running any of the subgenerators (such as `entity` or `jdl`) will use your local blueprint code. 52 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | JHipster 2 | Copyright 2017-2025 the original author or authors from the JHipster project. 3 | 4 | For more information on the JHipster project, see https://www.jhipster.tech/ 5 | -------------------------------------------------------------------------------- /cli/cli-customizations.cjs: -------------------------------------------------------------------------------- 1 | // This file will not be overwritten by generate-blueprint 2 | module.exports = { 3 | defaultCommand: 'react-native', 4 | printBlueprintLogo: undefined, 5 | printLogo: async () => { 6 | const { printJHipsterLogo } = await import('./print-jhipster-logo.js'); 7 | await printJHipsterLogo(); 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /cli/cli.cjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { dirname, basename, join } = require('path'); 4 | const { version, bin } = require('../package.json'); 5 | 6 | // Get package name to use as namespace. 7 | // Allows blueprints to be aliased. 8 | const packagePath = dirname(__dirname); 9 | const packageFolderName = basename(packagePath); 10 | const devBlueprintPath = join(packagePath, '.blueprint'); 11 | const blueprint = packageFolderName.startsWith('jhipster-') ? `generator-${packageFolderName}` : packageFolderName; 12 | 13 | (async () => { 14 | const { runJHipster, done, logger } = await import('generator-jhipster/cli'); 15 | const executableName = Object.keys(bin)[0]; 16 | 17 | runJHipster({ 18 | executableName, 19 | executableVersion: version, 20 | defaultCommand: 'app', 21 | devBlueprintPath, 22 | blueprints: { 23 | [blueprint]: version, 24 | }, 25 | printBlueprintLogo: () => { 26 | console.log('===================== JHipster React Native ====================='); 27 | console.log(''); 28 | }, 29 | lookups: [{ packagePaths: [packagePath] }], 30 | ...require('./cli-customizations.cjs'), 31 | }).catch(done); 32 | 33 | process.on('unhandledRejection', up => { 34 | logger.error('Unhandled promise rejection at:'); 35 | logger.fatal(up); 36 | }); 37 | })(); 38 | -------------------------------------------------------------------------------- /cli/commands.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'react-native': { 3 | description: 'Run react-native sub-generator (react-native blueprint)', 4 | blueprint: 'generator-jhipster-react-native', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /cli/print-jhipster-logo.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | import chalk from 'chalk'; 3 | 4 | const fileUrl = new URL('../package.json', import.meta.url); 5 | const packagejs = JSON.parse(readFileSync(fileUrl)); 6 | 7 | export const printJHipsterLogo = () => { 8 | console.log('\n'); 9 | console.log(`${chalk.cyan(' ██╗ ██╗ ██╗')}${chalk.green(' ')}${chalk.red('██████╗ ███╗ ██╗ ')}`); 10 | console.log(`${chalk.cyan(' ██║ ██║ ██║')}${chalk.green(' ')}${chalk.red('██╔══██╗ ████╗ ██║ ')}`); 11 | console.log(`${chalk.cyan(' ██║ ███████║')}${chalk.green(' █████╗ ')}${chalk.red('██████╔╝ ██╔██╗ ██║ ')}`); 12 | console.log(`${chalk.cyan(' ██╗ ██║ ██╔══██║')}${chalk.green(' ╚════╝ ')}${chalk.red('██╔══██╗ ██║╚██╗██║ ')}`); 13 | console.log(`${chalk.cyan(' ╚██████╔╝ ██║ ██║')}${chalk.green(' ')}${chalk.red('██║ ██║ ██║ ╚████║ ')}`); 14 | console.log(`${chalk.cyan(' ╚═════╝ ╚═╝ ╚═╝')}${chalk.green(' ')}${chalk.red('╚═╝ ╚═╝ ╚═╝ ╚═══╝ ')}\n`); 15 | console.log(chalk.white.bold(' https://www.jhipster.tech\n')); 16 | console.log(chalk.white('Welcome to JHipster React Native ') + chalk.yellow(`v${packagejs.version}`)); 17 | console.log(chalk.white(`Application files will be generated in folder: ${chalk.yellow(process.cwd())}`)); 18 | console.log( 19 | chalk.green(' _______________________________________________________________________________________________________________\n'), 20 | ); 21 | console.log( 22 | chalk.white( 23 | ` Documentation for creating an application is at ${chalk.yellow( 24 | 'https://github.com/jhipster/generator-jhipster-react-native/blob/main/README.md#getting-started', 25 | )}`, 26 | ), 27 | ); 28 | console.log( 29 | chalk.white( 30 | ` If you find JHipster React Native useful, consider sponsoring the project at ${chalk.yellow( 31 | 'https://opencollective.com/generator-jhipster', 32 | )}`, 33 | ), 34 | ); 35 | console.log( 36 | chalk.green(' _______________________________________________________________________________________________________________\n'), 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /docs/cli-flags.md: -------------------------------------------------------------------------------- 1 | ## CLI Flags 2 | 3 | You can pass flags for each of the options when running the `app` generator: 4 | 5 | | Flag | Possible Values | Action | 6 | | -------------------- | --------------- | --------------------------------------------------------------------- | 7 | | `--with-entities` | | Generates entities from a pre-existing JHipster backend folder | 8 | | `--defaults` | | Uses default values (name=RnApp, appDirectory=../backend, detox=true) | 9 | | `--skip-git` | | Skips git init | 10 | | `--skip-commit-hook` | | Skips `lint-staged` initialization | 11 | | `--skip-install` | | Skips install run of `npm install` and `npx pod-install` | 12 | -------------------------------------------------------------------------------- /docs/detox.md: -------------------------------------------------------------------------------- 1 | ## Detox 2 | 3 | Currently, Detox only supports iOS/macOS and does not support tests requiring authentication for OAuth2 apps. 4 | 5 | If enabled during generation, [Detox](https://github.com/wix/Detox) is configured for the project. Sample tests are available for the account screens, and are generated for entity screens when you add an entity. For example: 6 | 7 | - [`e2e/settings-screen.spec.js`](https://github.com/jhipster/generator-jhipster-react-native/blob/main/generators/app/templates/e2e/account/settings-screen.spec.js.ejs) 8 | - [`e2e/login-screen.spec.js`](https://github.com/jhipster/generator-jhipster-react-native/blob/main/generators/app/templates/e2e/account/login-screen.spec.js.ejs) 9 | 10 | To run the Detox tests: 11 | 12 | ```sh 13 | npm run test:e2e 14 | ``` 15 | 16 | You will need to have [`jq`](https://stedolan.github.io/jq/download/) installed for this command to work. 17 | 18 | To customize the build and test parameters (such as which device to test), modify the `.detoxrc.json` file found in the root of the project. 19 | 20 | For more information on Detox configuration and writing tests, check the [official Detox documentation](https://github.com/wix/Detox/blob/master/docs/README.md). 21 | -------------------------------------------------------------------------------- /docs/distributing-and-updating.md: -------------------------------------------------------------------------------- 1 | ## Expo 2 | 3 | For full documentation, refer to the [official Expo Docs](https://docs.expo.io/). 4 | 5 | ### Distributing Your App 6 | 7 | Expo makes it simple to build and deploy your application. It's only required to build and submit your app to the App Stores when there are changes to native code. This only happens on upgrades to the Expo SDK. 8 | 9 | If you need to have multiple environments (such as `dev` and `prod`), use [Release Channels](https://docs.expo.io/distribution/release-channels/). 10 | 11 | #### React Native iOS and Android Apps 12 | 13 | For more info, see [Building Standalone Apps](https://docs.expo.io/distribution/building-standalone-apps/). 14 | 15 | ```bash 16 | npm run build:ios 17 | npm run build:android 18 | ``` 19 | 20 | #### React Native Web Apps (PWAs) 21 | 22 | For more info, see [Publishing Websites](https://docs.expo.io/distribution/publishing-websites/). 23 | 24 | ```bash 25 | npm run build:web 26 | ``` 27 | 28 | ### Updating Your App 29 | 30 | Expo comes pre-configured with support for Over-the-Air updates. As mentioned above, you will not need to redeploy your app to the App Stores unless you make changes to your app's version of the Expo SDK. For more info, see [Configuring OTA Updates](https://docs.expo.io/guides/configuring-ota-updates/). 31 | 32 | ```bash 33 | npm run publish 34 | 35 | # equivalent to 36 | expo publish 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/generators.md: -------------------------------------------------------------------------------- 1 | ## Generators 2 | 3 | ### Entity 4 | 5 | ```sh 6 | jhipster --blueprints react-native entity 7 | ``` 8 | 9 | - Prompts for the path to the entity's config (`.jhipster` folder in your app) 10 | - Generates all files needed for fetching and displaying the entity 11 | - Includes the API endpoints, redux/saga config, and the user interface 12 | 13 | ### Import JDL 14 | 15 | ```sh 16 | jhipster --blueprints react-native jdl 17 | ``` 18 | 19 | - Import several entities at once using JDL 20 | - Runs the entity generator for each entity present in the JDL 21 | - Can also generate a full application with entities, if the JDL contains application config 22 | -------------------------------------------------------------------------------- /docs/images/jh-rn-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/generator-jhipster-react-native/c46c0d36b3b06d33a790218a9580697b693b0d1f/docs/images/jh-rn-logo.png -------------------------------------------------------------------------------- /docs/images/jh-rn-logo@0.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/generator-jhipster-react-native/c46c0d36b3b06d33a790218a9580697b693b0d1f/docs/images/jh-rn-logo@0.5x.png -------------------------------------------------------------------------------- /docs/images/jh-rn-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/generator-jhipster-react-native/c46c0d36b3b06d33a790218a9580697b693b0d1f/docs/images/jh-rn-logo@2x.png -------------------------------------------------------------------------------- /docs/images/jh-rn-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/generator-jhipster-react-native/c46c0d36b3b06d33a790218a9580697b693b0d1f/docs/images/jh-rn-logo@3x.png -------------------------------------------------------------------------------- /docs/storybook.md: -------------------------------------------------------------------------------- 1 | ## Storybook 2 | 3 | Storybook for React Native is configured out-of-the-box in DEV builds only (local device or simulator). You can access it in the Side Menu by clicking on "Storybook". 4 | 5 | ### Stories 6 | 7 | Sample "stories" are available for the generated components. They allow you to preview the components in various states, dependant on the props passed to them. 8 | 9 | - [`search-bar.story.js`](https://github.com/jhipster/generator-jhipster-react-native/blob/main/generators/app/templates/app/shared/components/search-bar/search-bar.story.js.ejs) 10 | - [`alert-message.story.js`](https://github.com/jhipster/generator-jhipster-react-native/blob/main/generators/app/templates/app/shared/components/alert-message/alert-message.story.js.ejs) 11 | - [`jhi-form-field.story.js`](https://github.com/jhipster/generator-jhipster-react-native/blob/main/generators/app/templates/app/shared/components/alert-message/alert-message.story.js.ejs) 12 | 13 | Use fixtures (JSON files containing sample data) to write stories for your own components. 14 | 15 | For more information on Storybook configuration and writing stories, check the [official Storybook documentation](https://storybook.js.org/docs/guides/guide-react-native/). 16 | -------------------------------------------------------------------------------- /docs/websockets.md: -------------------------------------------------------------------------------- 1 | ## WebSockets 2 | 3 | A websockets example is present under "Chat" in the Drawer Menu. JHipster React Native uses the existing `/websocket/tracker` endpoint to connect. It sends and receives chat messages using the `/topic/chat` message mapping. 4 | 5 | **Note:** The default JHipster websockets configuration requires users to authenticate before connecting. 6 | 7 | For a more advanced websockets example, see the [tracker.service.ts][1] and [ActivityService.java][2] in the generated JHipster webapp. 8 | 9 | [1]: https://github.com/jhipster/jhipster-sample-app-websocket/blob/main/src/main/webapp/app/core/tracker/tracker.service.ts 10 | [2]: https://github.com/jhipster/jhipster-sample-app-websocket/blob/main/src/main/java/io/github/jhipster/sample/web/websocket/ActivityService.java 11 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import prettier from 'eslint-plugin-prettier/recommended'; 3 | import jhipster from 'generator-jhipster/eslint'; 4 | // jhipster-needle-eslint-add-import - JHipster will add additional import here 5 | 6 | export default [ 7 | { 8 | languageOptions: { 9 | globals: { 10 | ...globals.node, 11 | }, 12 | }, 13 | }, 14 | { ignores: ['coverage/**'] }, 15 | jhipster.recommended, 16 | // jhipster-needle-eslint-add-config - JHipster will add additional config here 17 | prettier, 18 | ]; 19 | -------------------------------------------------------------------------------- /generators/app/command.mjs: -------------------------------------------------------------------------------- 1 | import { asCommand } from 'generator-jhipster'; 2 | 3 | export default asCommand({ 4 | options: { 5 | reactNativeDir: { 6 | desc: 'Directory of JHipster application', 7 | type: String, 8 | scope: 'blueprint', 9 | }, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /generators/app/generator.mjs: -------------------------------------------------------------------------------- 1 | import { relative } from 'node:path'; 2 | import BaseApplicationGenerator from 'generator-jhipster/generators/base-application'; 3 | import { DEFAULT_REACT_NATIVE_PATH, REACT_NATIVE_NAMESPACE } from '../constants.mjs'; 4 | import command from './command.mjs'; 5 | 6 | export default class extends BaseApplicationGenerator { 7 | constructor(args, opts, features) { 8 | super(args, opts, { ...features, sbsBlueprint: true }); 9 | } 10 | 11 | beforeQueue() { 12 | if (this.blueprintConfig.appDir) { 13 | throw new Error('jhipster-reactNative:app generator must run in backend application directory'); 14 | } 15 | } 16 | 17 | get [BaseApplicationGenerator.INITIALIZING]() { 18 | return this.asInitializingTaskGroup({ 19 | async initializingTemplateTask() { 20 | this.parseJHipsterArguments(command.arguments); 21 | this.parseJHipsterOptions(command.options); 22 | }, 23 | loadConfigFromJHipster() { 24 | if (this.options.defaults || this.options.force) { 25 | this.blueprintStorage.defaults({ reactNativeDir: DEFAULT_REACT_NATIVE_PATH }); 26 | } 27 | }, 28 | }); 29 | } 30 | 31 | get [BaseApplicationGenerator.PROMPTING]() { 32 | return this.asPromptingTaskGroup({ 33 | async promptForReactNativeDir() { 34 | await this.prompt( 35 | [ 36 | { 37 | type: 'input', 38 | name: 'reactNativeDir', 39 | message: 'Where do you want to generate a React Native application?', 40 | default: DEFAULT_REACT_NATIVE_PATH, 41 | }, 42 | ], 43 | this.blueprintStorage, 44 | ); 45 | this.blueprintStorage.defaults({ reactNativeDir: DEFAULT_REACT_NATIVE_PATH }); 46 | }, 47 | }); 48 | } 49 | 50 | get [BaseApplicationGenerator.COMPOSING]() { 51 | return this.asComposingTaskGroup({ 52 | async composeReactNative() { 53 | if (this.jhipsterConfig.applicationType === 'microservice') return; 54 | const reactNativeDir = this.destinationPath(this.blueprintConfig.reactNativeDir); 55 | const appDir = relative(reactNativeDir, this.destinationPath()); 56 | await this.composeWithJHipster(`${REACT_NATIVE_NAMESPACE}:react-native`, { 57 | generatorOptions: { 58 | destinationRoot: reactNativeDir, 59 | appDir, 60 | }, 61 | }); 62 | }, 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /generators/app/generator.spec.mjs: -------------------------------------------------------------------------------- 1 | import { beforeAll, describe, expect, it } from 'vitest'; 2 | 3 | import { defaultHelpers as helpers, result } from 'generator-jhipster/testing'; 4 | 5 | const SUB_GENERATOR = 'app'; 6 | const BLUEPRINT_NAMESPACE = `jhipster:${SUB_GENERATOR}`; 7 | 8 | describe('SubGenerator app of reactNative JHipster blueprint', () => { 9 | describe('run', () => { 10 | beforeAll(async function () { 11 | await helpers 12 | .run(BLUEPRINT_NAMESPACE) 13 | .withJHipsterConfig({ 14 | // Skip server and client for speed 15 | skipServer: true, 16 | skipClient: true, 17 | }) 18 | .withOptions({ 19 | ignoreNeedlesError: true, 20 | blueprint: ['react-native'], 21 | }) 22 | .withJHipsterLookup() 23 | .withParentBlueprintLookup(); 24 | }); 25 | 26 | it('should succeed', () => { 27 | expect(result.getStateSnapshot()).toMatchSnapshot(); 28 | }); 29 | }); 30 | 31 | describe('with custom reactNative path', () => { 32 | beforeAll(async function () { 33 | await helpers 34 | .run(BLUEPRINT_NAMESPACE) 35 | .withJHipsterConfig({ 36 | // Skip server and client for speed 37 | skipServer: true, 38 | skipClient: true, 39 | }) 40 | .withOptions({ 41 | skipChecks: true, 42 | ignoreNeedlesError: true, 43 | blueprint: ['react-native'], 44 | reactNativeDir: '../reactNative-app', 45 | }) 46 | .withJHipsterLookup() 47 | .withParentBlueprintLookup(); 48 | }); 49 | 50 | it('should succeed', () => { 51 | expect(result.getStateSnapshot()).toMatchSnapshot(); 52 | }); 53 | 54 | it('generates a package.json file at custom folder', () => { 55 | result.assertFile(['../reactNative-app/package.json']); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /generators/app/index.mjs: -------------------------------------------------------------------------------- 1 | export { default } from './generator.mjs'; 2 | export { default as command } from './command.mjs'; 3 | -------------------------------------------------------------------------------- /generators/constants.mjs: -------------------------------------------------------------------------------- 1 | export const REACT_NATIVE_NAMESPACE = 'jhipster-react-native'; 2 | export const DEFAULT_REACT_NATIVE_PATH = '../client'; 3 | export const DEFAULT_BACKEND_PATH = '../backend'; 4 | export const DEFAULT_REACT_NATIVE_APP_NAME = 'RnApp'; 5 | export const DEFAULT_ENABLE_DETOX = true; 6 | -------------------------------------------------------------------------------- /generators/heroku/templates/Procfile.ejs: -------------------------------------------------------------------------------- 1 | web: npx http-server dist --brotli --proxy http://localhost:$PORT? 2 | -------------------------------------------------------------------------------- /generators/react-native/command.mjs: -------------------------------------------------------------------------------- 1 | import { asCommand } from 'generator-jhipster'; 2 | import { DEFAULT_ENABLE_DETOX } from '../constants.mjs'; 3 | 4 | export default asCommand({ 5 | configs: { 6 | detox: { 7 | description: 'Generate Detox tests', 8 | prompt: { 9 | name: 'detox', 10 | type: 'confirm', 11 | message: 'Do you want to enable end-to-end tests with Detox?', 12 | default: DEFAULT_ENABLE_DETOX, 13 | }, 14 | scope: 'blueprint', 15 | }, 16 | }, 17 | options: { 18 | defaults: { 19 | desc: 'Use default options', 20 | type: String, 21 | }, 22 | authenticationType: { 23 | desc: 'Authentication type', 24 | type: String, 25 | }, 26 | baseName: { 27 | desc: 'Base name', 28 | type: String, 29 | }, 30 | appDir: { 31 | desc: 'Directory for JHipster application', 32 | type: String, 33 | }, 34 | standalone: { 35 | desc: 'Skip backend', 36 | type: Boolean, 37 | }, 38 | }, 39 | import: ['jhipster:git'], 40 | }); 41 | -------------------------------------------------------------------------------- /generators/react-native/entity-files.mjs: -------------------------------------------------------------------------------- 1 | import { clientRootTemplatesBlock } from 'generator-jhipster/generators/client/support'; 2 | 3 | const files = { 4 | common: [ 5 | clientRootTemplatesBlock({ 6 | templates: [ 7 | 'app/modules/entities/_entityFolder_/_entityFile_-delete-modal.js', 8 | 'app/modules/entities/_entityFolder_/_entityFile_-detail-screen.js', 9 | 'app/modules/entities/_entityFolder_/_entityFile_-edit-screen.js', 10 | 'app/modules/entities/_entityFolder_/_entityFile_-screen.js', 11 | 'app/modules/entities/_entityFolder_/_entityFile_-styles.js', 12 | 'app/modules/entities/_entityFolder_/_entityFile_.reducer.js', 13 | 'test/spec/modules/entities/_entityFolder_/_entityFile_.reducer.spec.js', 14 | 'app/modules/entities/_entityFolder_/_entityFile_.sagas.js', 15 | 'test/spec/modules/entities/_entityFolder_/_entityFile_.sagas.spec.js', 16 | ], 17 | }), 18 | ], 19 | fixtures: [ 20 | clientRootTemplatesBlock({ 21 | templates: [ 22 | 'app/shared/fixtures/get-_entityFile_.json', 23 | { 24 | file: 'app/shared/fixtures/entity-get-all.json', 25 | // Workaround for entityPluralFileName 'xxxundefined' due to angularSuffix == undefined 26 | renameTo: generator => `app/shared/fixtures/get-all-${generator.entityNamePluralizedAndSpinalCased}.json`, 27 | }, 28 | 'app/shared/fixtures/update-_entityFile_.json', 29 | ], 30 | }), 31 | clientRootTemplatesBlock({ 32 | condition: generator => generator.searchEngineAny, 33 | templates: [ 34 | { 35 | file: 'app/shared/fixtures/entity-get-all.json', 36 | // Workaround for entityPluralFileName 'xxxundefined' due to angularSuffix == undefined 37 | renameTo: generator => `app/shared/fixtures/search-${generator.entityNamePluralizedAndSpinalCased}.json`, 38 | }, 39 | ], 40 | }), 41 | ], 42 | detox: [ 43 | clientRootTemplatesBlock({ 44 | condition: ctx => ctx.detox, 45 | templates: ['e2e/entities/_entityFile_.spec.js'], 46 | }), 47 | ], 48 | }; 49 | 50 | export default files; 51 | -------------------------------------------------------------------------------- /generators/react-native/index.mjs: -------------------------------------------------------------------------------- 1 | export { default } from './generator.mjs'; 2 | export { default as command } from './command.mjs'; 3 | -------------------------------------------------------------------------------- /generators/react-native/resources/expo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expo-dependencies", 3 | "version": "0.0.1", 4 | "description": "Dependencies managed by Expo - need a specific version", 5 | "scripts": { 6 | "check-dependencies": "npm install && npx expo install --check", 7 | "update-dependencies": "npm install --force && npx expo install --fix && rm -rf package-lock.json node_modules" 8 | }, 9 | "dependencies": { 10 | "@expo/metro-runtime": "~4.0.0", 11 | "@expo/vector-icons": "^14.1.0", 12 | "@react-native-async-storage/async-storage": "1.23.1", 13 | "@react-native-community/datetimepicker": "8.2.0", 14 | "@react-native-masked-view/masked-view": "0.3.2", 15 | "@react-native-picker/picker": "2.9.0", 16 | "@react-navigation/devtools": "7.0.26", 17 | "expo": "52.0.46", 18 | "expo-auth-session": "~6.0.0", 19 | "expo-constants": "~17.0.2", 20 | "expo-image-picker": "~16.0.1", 21 | "expo-linking": "~7.0.2", 22 | "expo-random": "~14.0.1", 23 | "expo-splash-screen": "~0.29.8", 24 | "expo-status-bar": "~2.0.0", 25 | "expo-web-browser": "~14.0.0", 26 | "react": "18.3.1", 27 | "react-dom": "18.3.1", 28 | "react-native": "0.76.9", 29 | "react-native-gesture-handler": "~2.20.2", 30 | "react-native-reanimated": "~3.16.1", 31 | "react-native-safe-area-context": "4.12.0", 32 | "react-native-screens": "~4.4.0", 33 | "react-native-web": "~0.19.13" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "^7.27.1", 37 | "@react-native-community/cli-server-api": "18.0.0", 38 | "@types/react": "~18.3.13", 39 | "jest-expo": "~52.0.0", 40 | "typescript": "~5.8.3" 41 | }, 42 | "expo": { 43 | "install": { 44 | "exclude": [ 45 | "expo" 46 | ] 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /generators/react-native/support/append-files.js: -------------------------------------------------------------------------------- 1 | function appendFiles() { 2 | this.editFile( 3 | '.gitignore', 4 | content => `${content} 5 | # Misc 6 | .env 7 | ios/Index/DataStore 8 | ios/Carthage 9 | ios/Pods 10 | /dist 11 | .artifacts 12 | e2e/Exponent.app 13 | .yo-repository 14 | `, 15 | ); 16 | } 17 | 18 | export { appendFiles }; 19 | -------------------------------------------------------------------------------- /generators/react-native/support/index.js: -------------------------------------------------------------------------------- 1 | export { appendFiles } from './append-files.js'; 2 | export { patchBabel } from './patch-babel.js'; 3 | export { patchNavigationForEntity } from './patch-navigation.js'; 4 | export { patchInFile } from './patch-in-file.js'; 5 | export { patchEntityApi } from './patch-entity-api.js'; 6 | export { getEntityFormField, getRelationshipFormField, getFieldValidateType, getEntityFormFieldType } from './entity-helpers.js'; 7 | -------------------------------------------------------------------------------- /generators/react-native/support/patch-babel.js: -------------------------------------------------------------------------------- 1 | function patchBabel() { 2 | this.patchInFile('babel.config.js', { 3 | after: 'presets', 4 | insert: "plugins: ['@babel/plugin-transform-export-namespace-from', 'react-native-reanimated/plugin'],", 5 | ignoreIfContains: 'react-native-reanimated', 6 | }); 7 | } 8 | 9 | export { patchBabel }; 10 | -------------------------------------------------------------------------------- /generators/react-native/templates/.detoxrc.cjs.ejs: -------------------------------------------------------------------------------- 1 | /** @type {Detox.DetoxConfig} */ 2 | module.exports = { 3 | testRunner: { 4 | args: { 5 | '$0': 'jest', 6 | config: 'e2e/jest.config.cjs' 7 | }, 8 | jest: { 9 | setupTimeout: 120000 10 | } 11 | }, 12 | apps: { 13 | 'ios.debug': { 14 | type: 'ios.app', 15 | binaryPath: 'e2e/Exponent.app', 16 | build: 'xcodebuild -workspace ios/<%- baseName %>.xcworkspace -scheme <%- baseName %> -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build' 17 | }, 18 | 'ios.release': { 19 | type: 'ios.app', 20 | binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/<%- baseName %>.app', 21 | build: 'xcodebuild -workspace ios/<%- baseName %>.xcworkspace -scheme <%- baseName %> -configuration Release -sdk iphonesimulator -derivedDataPath ios/build' 22 | }, 23 | 'android.debug': { 24 | type: 'android.apk', 25 | binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', 26 | build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', 27 | reversePorts: [ 28 | 8081 29 | ] 30 | }, 31 | 'android.release': { 32 | type: 'android.apk', 33 | binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', 34 | build: 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release' 35 | } 36 | }, 37 | devices: { 38 | simulator: { 39 | type: 'ios.simulator', 40 | device: { 41 | type: 'iPhone 16' 42 | } 43 | }, 44 | attached: { 45 | type: 'android.attached', 46 | device: { 47 | adbName: '.*' 48 | } 49 | }, 50 | emulator: { 51 | type: 'android.emulator', 52 | device: { 53 | avdName: 'Pixel_3a_API_30_x86' 54 | } 55 | } 56 | }, 57 | configurations: { 58 | 'ios.sim.debug': { 59 | device: 'simulator', 60 | app: 'ios.debug' 61 | }, 62 | 'ios.sim.release': { 63 | device: 'simulator', 64 | app: 'ios.release' 65 | }, 66 | 'android.att.debug': { 67 | device: 'attached', 68 | app: 'android.debug' 69 | }, 70 | 'android.att.release': { 71 | device: 'attached', 72 | app: 'android.release' 73 | }, 74 | 'android.emu.debug': { 75 | device: 'emulator', 76 | app: 'android.debug' 77 | }, 78 | 'android.emu.release': { 79 | device: 'emulator', 80 | app: 'android.release' 81 | } 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /generators/react-native/templates/.gitattributes.jhi.react-native.ejs: -------------------------------------------------------------------------------- 1 | <&_ if (!fragment.section) { -&> 2 | ## .PBXPROJ FILES 3 | *.pbxproj binary 4 | <&_ } -&> 5 | -------------------------------------------------------------------------------- /generators/react-native/templates/.gitignore.jhi.react-native.ejs: -------------------------------------------------------------------------------- 1 | <&_ if (!fragment.section) { -&> 2 | ###################### 3 | # React Native 4 | ###################### 5 | 6 | # Expo 7 | .expo/ 8 | web-build/ 9 | 10 | # Native 11 | *.orig.* 12 | *.jks 13 | *.p8 14 | *.p12 15 | *.key 16 | *.mobileprovision 17 | 18 | # Metro 19 | .metro-health-check* 20 | 21 | # debug 22 | npm-debug.* 23 | yarn-debug.* 24 | yarn-error.* 25 | 26 | # macOS 27 | *.pem 28 | 29 | # local env files 30 | .env*.local 31 | 32 | # typescript 33 | *.tsbuildinfo 34 | <&_ } -&> 35 | -------------------------------------------------------------------------------- /generators/react-native/templates/.prettierignore.jhi.react-native.ejs: -------------------------------------------------------------------------------- 1 | <&_ if (!fragment.section) { -&> 2 | android 3 | ios 4 | .expo 5 | web-build 6 | dist 7 | .jhipster 8 | package-lock.json 9 | yarn.lock 10 | <&_ } -&> 11 | -------------------------------------------------------------------------------- /generators/react-native/templates/.storybook/index.ts.ejs: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-async-storage/async-storage'; 2 | import { view } from './storybook.requires'; 3 | 4 | import AppConfig from '../app/config/app-config'; 5 | import { View } from 'react-native'; 6 | 7 | const StorybookUIRoot = AppConfig.debugMode ? view.getStorybookUI({ 8 | storage: { 9 | getItem: AsyncStorage.getItem, 10 | setItem: AsyncStorage.setItem, 11 | }, 12 | }) : View; 13 | 14 | export default StorybookUIRoot; 15 | -------------------------------------------------------------------------------- /generators/react-native/templates/.storybook/main.ts.ejs: -------------------------------------------------------------------------------- 1 | import { StorybookConfig } from '@storybook/react-native'; 2 | 3 | const main: StorybookConfig = { 4 | stories: ['../app/**/*.stor?(y|ies).?(ts|tsx|js|jsx)'], 5 | addons: ['@storybook/addon-ondevice-controls', '@storybook/addon-ondevice-actions'], 6 | }; 7 | 8 | export default main; 9 | -------------------------------------------------------------------------------- /generators/react-native/templates/.storybook/preview.tsx.ejs: -------------------------------------------------------------------------------- 1 | import type { Preview } from '@storybook/react'; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/, 9 | }, 10 | }, 11 | }, 12 | }; 13 | 14 | export default preview; 15 | -------------------------------------------------------------------------------- /generators/react-native/templates/.storybook/storybook.requires.ts.ejs: -------------------------------------------------------------------------------- 1 | /* do not change this file, it is auto generated by storybook. */ 2 | 3 | import { start, updateView } from "@storybook/react-native"; 4 | 5 | import "@storybook/addon-ondevice-controls/register"; 6 | import "@storybook/addon-ondevice-actions/register"; 7 | 8 | const normalizedStories = [ 9 | { 10 | titlePrefix: "", 11 | directory: "./app", 12 | files: "**/*.stor?(y|ies).?(ts|tsx|js|jsx)", 13 | importPathMatcher: 14 | /^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stor(?:y|ies)?\.(?:ts|tsx|js|jsx)?)$/, 15 | // @ts-ignore 16 | req: require.context( 17 | "../app", 18 | true, 19 | /^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stor(?:y|ies)?\.(?:ts|tsx|js|jsx)?)$/ 20 | ), 21 | }, 22 | ]; 23 | 24 | declare global { 25 | var view: ReturnType; 26 | var STORIES: typeof normalizedStories; 27 | } 28 | 29 | const annotations = [ 30 | require("./preview.tsx.ejs"), 31 | require("@storybook/react-native/preview"), 32 | require("@storybook/addon-ondevice-actions/preview"), 33 | ]; 34 | 35 | global.STORIES = normalizedStories; 36 | 37 | // @ts-ignore 38 | module?.hot?.accept?.(); 39 | 40 | if (!global.view) { 41 | global.view = start({ 42 | annotations, 43 | storyEntries: normalizedStories, 44 | }); 45 | } else { 46 | updateView(global.view, annotations, normalizedStories); 47 | } 48 | 49 | export const view = global.view; 50 | -------------------------------------------------------------------------------- /generators/react-native/templates/App.js.ejs: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import createStore from './app/shared/reducers'; 4 | import * as SplashScreen from 'expo-splash-screen'; 5 | import 'setimmediate'; 6 | 7 | import NavContainer from './app/navigation/nav-container'; 8 | 9 | const store = createStore(); 10 | 11 | function App() { 12 | // prevent the splashscreen from disappearing until the redux store is completely ready (hidden in nav-container.js) 13 | const [displayApp, setDisplayApp] = React.useState(false); 14 | React.useEffect(() => { 15 | if (!displayApp) { 16 | SplashScreen.preventAutoHideAsync() 17 | .then(() => setDisplayApp(true)) 18 | .catch(() => setDisplayApp(true)); 19 | } 20 | }, [displayApp, setDisplayApp]); 21 | 22 | return displayApp ? ( 23 | 24 | 25 | 26 | ) : null; 27 | } 28 | 29 | let AppEntryPoint = App; 30 | 31 | if (process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true') { 32 | AppEntryPoint = require('./.storybook').default; 33 | } 34 | 35 | export default AppEntryPoint; 36 | -------------------------------------------------------------------------------- /generators/react-native/templates/README.md.ejs: -------------------------------------------------------------------------------- 1 | # <%= baseName %> 2 | > Generated by [JHipster React Native](https://github.com/jhipster/generator-jhipster-react-native) v<%= blueprintPackageVersion %> 3 | 4 |
5 | 6 | JHipster React Native 7 | 8 |
9 | 10 | JHipster React Native is designed to be used with a JHipster backend. 11 | 12 | ## Table of Contents 13 | 14 | 1. [Getting Started](#getting-started) 15 | 2. [Generating Entities](#entities) 16 | <%_ if (authenticationType === 'oauth2') { _%> 17 | 3. [OAuth2 Configuration](#oauth2-configuration) 18 | <%_ } _%> 19 | <%_ if (detox) { _%> 20 | 4. [E2E Tests](#e2e-tests) 21 | <%_ } _%> 22 | 5. [Tips](#tips) 23 | 24 | ## Getting Started 25 | 26 | To start the Expo packager, run: 27 | 28 | ```bash 29 | npm start 30 | ``` 31 | 32 | To run on iOS, after the Expo packager starts: 33 | - Press `i` 34 | - To choose the emulator, press `Shift+i` 35 | 36 | To run on Android, after the Expo packager starts: 37 | - Press `a` 38 | - To choose the emulator, press `Shift+a` 39 | 40 | To run on Web, after the Expo packager starts: 41 | - Press `w` 42 | 43 | You can find out more about the Expo CLI and other Expo Features in the [Expo documentation](https://docs.expo.io/). 44 | 45 | ## Generate Entities 46 | 47 | To generate entities: 48 | 49 | ```bash 50 | jhipster entity 51 | ``` 52 | 53 | Or to import JDL: 54 | 55 | ```bash 56 | jhipster jdl 57 | ``` 58 | 59 | <%_ if (authenticationType === 'oauth2') { _%> 60 | ## OAuth2 Configuration 61 | 62 | For configuring your redirect URIs, please see [the JHipster React Native OAuth2.0 docs](https://github.com/jhipster/generator-jhipster-react-native/blob/main/docs/oauth2-oidc.md). 63 | 64 | <%_ } _%> 65 | 66 | <%_ if (detox) { _%> 67 | ## E2E Tests 68 | 69 | See the example end-to-end test in [`e2e/home-screen.spec.js`](e2e/home-screen.spec.js). 70 | 71 | To run the e2e tests: 72 | 73 | ```bash 74 | npm run test:e2e 75 | ``` 76 | 77 | You will need to have [`jq`](https://stedolan.github.io/jq/download/) installed for this command to work. 78 | 79 | <%_ } _%> 80 | ## Tips 81 | 82 | - When running your JHipster backend locally for Android, make sure to run `adb reverse tcp:8080 tcp:8080` so the app can communicate with your backend. 83 | - When running your JHipster backend on Web, make sure to enable CORS in your backend's `src/main/resources/config/application-*.yml` files. 84 | -------------------------------------------------------------------------------- /generators/react-native/templates/app.json.ejs: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "<%= baseName %>", 4 | "slug": "<%= baseName %>", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "assetBundlePatterns": [ 15 | "**/*" 16 | ], 17 | "ios": { 18 | "bundleIdentifier": "com.anonymous.<%= baseName %>", 19 | "supportsTablet": true 20 | }, 21 | "android": { 22 | "adaptiveIcon": { 23 | "foregroundImage": "./assets/adaptive-icon.png", 24 | "backgroundColor": "#ffffff" 25 | }, 26 | "package": "com.anonymous.<%= baseName %>" 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/config/app-config.js.ejs: -------------------------------------------------------------------------------- 1 | <%_ if (authenticationType === 'oauth2') { _%> 2 | import { Platform } from 'react-native'; 3 | <%_ } _%> 4 | import Constants from 'expo-constants'; 5 | 6 | // load extra config from the app.json file 7 | const extra = Constants.manifest?.extra ?? {}; 8 | 9 | export default { 10 | // use 10.0.2.2 for Android to connect to host machine 11 | apiUrl: 'http://localhost:8080/', 12 | <%_ if (authenticationType === 'oauth2') { _%> 13 | // leave blank if using Keycloak 14 | nativeClientId: '', 15 | // use expo auth proxy with login, disable to enable logout completely from oauth provider 16 | useExpoAuthProxy: Platform.select({ web: false, default: true }), 17 | <%_ } _%> 18 | // use fixtures instead of real API requests 19 | useFixtures: false, 20 | // debug mode 21 | debugMode: __DEV__, 22 | extra, 23 | }; 24 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/config/redux-persist.js.ejs: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-async-storage/async-storage'; 2 | 3 | import immutablePersistenceTransform from '../shared/util/immutable-persistence-transform' 4 | 5 | const REDUX_PERSIST = { 6 | active: true, 7 | reducerVersion: '1.0', 8 | storeConfig: { 9 | key: 'primary', 10 | storage: AsyncStorage, 11 | blacklist: ['appState'<%_ if (authenticationType === 'oauth2') { _%>, 'authInfo'<%_ } _%>], // reducer keys that you do NOT want stored to persistence here 12 | // whitelist: [], Optionally, just specify the keys you DO want stored to 13 | // persistence. An empty array means 'don't store any reducers' -> infinitered/ignite#409 14 | transforms: [immutablePersistenceTransform] 15 | } 16 | } 17 | 18 | export default REDUX_PERSIST 19 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/password-reset/forgot-password-screen.styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | import { ApplicationStyles } from '../../../shared/themes' 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | }) 8 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/password-reset/forgot-password.reducer.js.ejs: -------------------------------------------------------------------------------- 1 | import { createReducer, createActions } from 'reduxsauce' 2 | import Immutable from 'seamless-immutable' 3 | /* ------------- Types and Action Creators ------------- */ 4 | 5 | const { Types, Creators } = createActions({ 6 | forgotPasswordRequest: ['email'], 7 | forgotPasswordSuccess: ['response'], 8 | forgotPasswordFailure: ['error'] 9 | }) 10 | 11 | export const ForgotPasswordTypes = Types 12 | export default Creators 13 | 14 | /* ------------- Initial State ------------- */ 15 | 16 | export const INITIAL_STATE = Immutable({ 17 | response: null, 18 | error: null, 19 | fetching: false 20 | }) 21 | 22 | /* ------------- Reducers ------------- */ 23 | 24 | // we're attempting to request a password reset email 25 | export const request = (state) => state.merge({ fetching: true }) 26 | 27 | // we've successfully request to reset the password 28 | export const success = (state) => state.merge({ fetching: false, error: null }) 29 | 30 | // we've had a problem requesting to reset the password 31 | export const failure = (state, { error }) => state.merge({ fetching: false, error }) 32 | 33 | /* ------------- Hookup Reducers To Types ------------- */ 34 | 35 | export const reducer = createReducer(INITIAL_STATE, { 36 | [Types.FORGOT_PASSWORD_REQUEST]: request, 37 | [Types.FORGOT_PASSWORD_SUCCESS]: success, 38 | [Types.FORGOT_PASSWORD_FAILURE]: failure 39 | }) 40 | 41 | /* ------------- Selectors ------------- */ 42 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/password-reset/forgot-password.sagas.js.ejs: -------------------------------------------------------------------------------- 1 | import { call, put } from 'redux-saga/effects' 2 | 3 | import ForgotPasswordActions from './forgot-password.reducer' 4 | 5 | // attempts to request a password reset 6 | export function * forgotPassword (api, { email }) { 7 | const response = yield call(api.forgotPassword, email) 8 | // success? 9 | if (response.ok) { 10 | console.log('ForgotPasswordRequest - OK') 11 | yield put(ForgotPasswordActions.forgotPasswordSuccess(response.data)) 12 | } else { 13 | console.log('ForgotPassword - FAIL') 14 | yield put(ForgotPasswordActions.forgotPasswordFailure((response.data && response.data.title) || 'Something when wrong resetting your password')) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/password/change-password-screen.styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | import { ApplicationStyles } from '../../../shared/themes' 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | }) 8 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/password/change-password.reducer.js.ejs: -------------------------------------------------------------------------------- 1 | import { createReducer, createActions } from 'reduxsauce' 2 | import Immutable from 'seamless-immutable' 3 | /* ------------- Types and Action Creators ------------- */ 4 | 5 | const { Types, Creators } = createActions({ 6 | changePasswordRequest: ['currentPassword', 'newPassword'], 7 | changePasswordSuccess: ['response'], 8 | changePasswordFailure: ['error'] 9 | }) 10 | 11 | export const ChangePasswordTypes = Types 12 | export default Creators 13 | 14 | /* ------------- Initial State ------------- */ 15 | 16 | export const INITIAL_STATE = Immutable({ 17 | response: null, 18 | error: null, 19 | fetching: false 20 | }) 21 | 22 | /* ------------- Reducers ------------- */ 23 | 24 | // we're attempting to request a password reset email 25 | export const request = (state) => state.merge({ fetching: true }) 26 | 27 | // we've successfully request to reset the password 28 | export const success = (state) => state.merge({ fetching: false, error: null }) 29 | 30 | // we've had a problem requesting to reset the password 31 | export const failure = (state, { error }) => state.merge({ fetching: false, error }) 32 | 33 | /* ------------- Hookup Reducers To Types ------------- */ 34 | 35 | export const reducer = createReducer(INITIAL_STATE, { 36 | [Types.CHANGE_PASSWORD_REQUEST]: request, 37 | [Types.CHANGE_PASSWORD_SUCCESS]: success, 38 | [Types.CHANGE_PASSWORD_FAILURE]: failure 39 | }) 40 | 41 | /* ------------- Selectors ------------- */ 42 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/password/change-password.sagas.js.ejs: -------------------------------------------------------------------------------- 1 | import { call, put } from 'redux-saga/effects' 2 | 3 | import ChangePasswordActions from './change-password.reducer' 4 | import { callApi } from '../../../shared/sagas/call-api.saga' 5 | 6 | // attempts to request a password change 7 | export function * changePassword (api, { currentPassword, newPassword }) { 8 | const apiCall = call(api.changePassword, currentPassword, newPassword) 9 | const response = yield call(callApi, apiCall) 10 | // success? 11 | if (response.ok) { 12 | console.log('ChangePasswordRequest - OK') 13 | yield put(ChangePasswordActions.changePasswordSuccess()) 14 | } else { 15 | console.log('ChangePassword - FAIL') 16 | yield put(ChangePasswordActions.changePasswordFailure((response.data && response.data.title) || 'Failed to change password')) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/register/register-screen.styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | import { ApplicationStyles } from '../../../shared/themes' 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | }) 8 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/register/register.reducer.js.ejs: -------------------------------------------------------------------------------- 1 | import { createReducer, createActions } from 'reduxsauce' 2 | import Immutable from 'seamless-immutable' 3 | /* ------------- Types and Action Creators ------------- */ 4 | 5 | const { Types, Creators } = createActions({ 6 | registerRequest: ['user'], 7 | registerSuccess: [], 8 | registerFailure: ['error'] 9 | }) 10 | 11 | export const RegisterTypes = Types 12 | export default Creators 13 | 14 | /* ------------- Initial State ------------- */ 15 | 16 | export const INITIAL_STATE = Immutable({ 17 | error: null, 18 | fetching: false 19 | }) 20 | 21 | /* ------------- Reducers ------------- */ 22 | 23 | // we're attempting to register 24 | export const request = (state) => state.merge({ fetching: true }) 25 | 26 | // we've successfully registered 27 | export const success = (state) => state.merge({ fetching: false, error: null }) 28 | 29 | // we've had a problem registering 30 | export const failure = (state, { error }) => state.merge({ fetching: false, error }) 31 | 32 | /* ------------- Hookup Reducers To Types ------------- */ 33 | 34 | export const reducer = createReducer(INITIAL_STATE, { 35 | [Types.REGISTER_REQUEST]: request, 36 | [Types.REGISTER_SUCCESS]: success, 37 | [Types.REGISTER_FAILURE]: failure 38 | }) 39 | 40 | /* ------------- Selectors ------------- */ 41 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/register/register.sagas.js.ejs: -------------------------------------------------------------------------------- 1 | import { call, put } from 'redux-saga/effects' 2 | 3 | import RegisterActions from './register.reducer' 4 | 5 | // attempts to register 6 | export function * register (api, { user }) { 7 | const response = yield call(api.register, { langKey: 'en', ...user }); 8 | // success? 9 | if (response.ok) { 10 | console.log('Register - OK') 11 | yield put(RegisterActions.registerSuccess()) 12 | } else { 13 | console.log('Register - FAIL') 14 | yield put(RegisterActions.registerFailure((response.data && response.data.title) || 'Registration failed')) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/account/settings/settings-screen.styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | import { ApplicationStyles } from '../../../shared/themes' 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | }) 8 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/chat/chat-screen.styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | import { ApplicationStyles, Metrics, Colors } from '../../shared/themes'; 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | row: { 8 | marginVertical: 1, 9 | justifyContent: 'center', 10 | }, 11 | boldLabel: { 12 | fontWeight: 'bold', 13 | alignSelf: 'center', 14 | textAlign: 'center', 15 | marginBottom: Metrics.smallMargin, 16 | }, 17 | messageInput: { 18 | flex: 1, 19 | padding: 10, 20 | backgroundColor: Colors.white, 21 | }, 22 | inputContainer: { 23 | flexDirection: 'row', 24 | paddingBottom: 20, 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/chat/chat.reducer.js.ejs: -------------------------------------------------------------------------------- 1 | import { createReducer, createActions } from 'reduxsauce' 2 | import Immutable from 'seamless-immutable' 3 | 4 | /* ------------- Types and Action Creators ------------- */ 5 | 6 | const { Types, Creators } = createActions({ 7 | chatReset: [], 8 | chatSuccess: ['chat'] 9 | }) 10 | 11 | export const ChatTypes = Types 12 | export default Creators 13 | 14 | /* ------------- Initial State ------------- */ 15 | 16 | export const INITIAL_STATE = Immutable({ 17 | chat: [] 18 | }) 19 | 20 | /* ------------- Reducers ------------- */ 21 | 22 | // request to add a single chat to list 23 | export const chatSuccess = (state, { chat }) => { 24 | return state.merge({ chat }) 25 | } 26 | 27 | export const reset = _state => { 28 | return INITIAL_STATE 29 | } 30 | 31 | /* ------------- Hookup Reducers To Types ------------- */ 32 | 33 | export const reducer = createReducer(INITIAL_STATE, { 34 | [Types.CHAT_SUCCESS]: chatSuccess, 35 | [Types.CHAT_RESET]: reset 36 | }) 37 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/entities/_entityFolder_/_entityFile_-styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | import { ApplicationStyles } from '../../../shared/themes'; 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | ...ApplicationStyles.entity, 8 | ...ApplicationStyles.entityDeleteModal, 9 | }); 10 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/entities/entities-screen.js.ejs: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ScrollView, Text } from 'react-native'; 3 | // Styles 4 | import RoundedButton from '../../shared/components/rounded-button/rounded-button'; 5 | 6 | import styles from './entities-screen.styles'; 7 | 8 | export default function EntitiesScreen({ navigation }) { 9 | return ( 10 | 11 | JHipster Entities will appear below 12 | {/* jhipster-react-native-entity-screen-needle */} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/entities/entities-screen.styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | import { ApplicationStyles, Colors } from '../../shared/themes' 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | centerText: { 8 | textAlign: 'center', 9 | }, 10 | buttonText: { 11 | fontSize: 18, 12 | color: 'white', 13 | alignSelf: 'center' 14 | }, 15 | button: { 16 | height: 36, 17 | backgroundColor: Colors.jhipsterBlue, 18 | borderColor: Colors.jhipsterBlue, 19 | borderWidth: 1, 20 | borderRadius: 8, 21 | marginBottom: 10, 22 | alignSelf: 'stretch', 23 | justifyContent: 'center' 24 | }, 25 | }) 26 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/home/home-screen.styles.js.ejs: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | import { Metrics, ApplicationStyles, Colors } from '../../shared/themes'; 4 | 5 | export default StyleSheet.create({ 6 | ...ApplicationStyles.screen, 7 | mainContainer: { 8 | backgroundColor: Colors.jhipsterBlue, 9 | }, 10 | welcomeText: { 11 | textAlign: 'center', 12 | fontSize: 20, 13 | color: Colors.white, 14 | fontWeight: '600', 15 | }, 16 | hairline: { 17 | borderBottomColor: Colors.white, 18 | borderBottomWidth: 1, 19 | marginHorizontal: 20, 20 | marginTop: 32, 21 | }, 22 | logo: { 23 | marginTop: Metrics.section, 24 | height: Metrics.images.logo, 25 | width: Metrics.images.logo, 26 | resizeMode: 'contain', 27 | }, 28 | centered: { 29 | alignItems: 'center', 30 | }, 31 | scrollView: { 32 | paddingBottom: Metrics.baseMargin, 33 | backgroundColor: Colors.lighter, 34 | }, 35 | engine: { 36 | position: 'absolute', 37 | right: 0, 38 | }, 39 | body: { 40 | backgroundColor: Colors.transparent, 41 | }, 42 | sectionContainer: { 43 | marginTop: 32, 44 | paddingHorizontal: 24, 45 | }, 46 | sectionTitle: { 47 | fontSize: 24, 48 | fontWeight: '600', 49 | color: Colors.white, 50 | }, 51 | sectionDescription: { 52 | marginTop: 8, 53 | fontSize: 18, 54 | fontWeight: '400', 55 | color: Colors.white, 56 | }, 57 | highlight: { 58 | fontWeight: '700', 59 | }, 60 | footer: { 61 | color: Colors.white, 62 | fontSize: 12, 63 | fontWeight: '600', 64 | padding: 4, 65 | paddingRight: 12, 66 | textAlign: 'right', 67 | }, 68 | authContainer: { 69 | margin: 30, 70 | padding: 5, 71 | borderRadius: 5, 72 | }, 73 | authContainerTrue: { 74 | backgroundColor: '#02b875', 75 | }, 76 | authContainerFalse: { 77 | backgroundColor: '#efbb6d', 78 | }, 79 | authText: { 80 | textAlign: 'center', 81 | fontSize: 18, 82 | color: Colors.white, 83 | fontWeight: '600', 84 | }, 85 | }); 86 | -------------------------------------------------------------------------------- /generators/react-native/templates/app/modules/login/login-screen.oauth2.js.ejs: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Text, ActivityIndicator, View } from 'react-native'; 3 | import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; 4 | import { connect } from 'react-redux'; 5 | 6 | import LoginActions from './login.reducer'; 7 | import { useDidUpdateEffect } from '../../shared/util/use-did-update-effect'; 8 | import styles from './login-screen.styles'; 9 | 10 | function LoginScreen(props) { 11 | const { account, navigation, fetching, error, attemptLogin, fetchingAccount, fetchingAuthInfo, authInfoError } = props; 12 | // setup error state for displaying error messages 13 | const [loginError, setLoginError] = React.useState(''); 14 | 15 | // if the user is already logged in, send them home 16 | React.useEffect(() => { 17 | if (account !== null) { 18 | navigation.navigate('Home'); 19 | } 20 | }, [account, navigation]); 21 | 22 | // skip the first render but check for API responses and show error if not fetching 23 | useDidUpdateEffect(() => { 24 | if (!fetching && error) { 25 | setLoginError(error); 26 | } 27 | }, [fetching]); 28 | 29 | return ( 30 | 31 |