diff --git a/docs/oauth2-oidc.md b/docs/oauth2-oidc.md index a4dd99e51..37df7c83a 100644 --- a/docs/oauth2-oidc.md +++ b/docs/oauth2-oidc.md @@ -36,12 +36,13 @@ adb reverse tcp:8080 tcp:8080 && adb reverse tcp:9080 tcp:9080 **Note:** You can do the following steps the Okta Dashboard if preferred. Using the [Okta CLI](https://cli.okta.com/), run `okta apps create` + - For "Type of Application" choose `Native` - For "Redirect URI" and "Post Logout Redirect URI" provide `http://localhost:19006/,https://auth.expo.io/@your-expo-username/reactNativeAppName` - Change `@your-expo-username` to match your Expo username (run `expo whoami`) - Change `reactNativeAppName` to the value provided during generation (look in your `.yo-rc.json`) -Copy the provided `clientId` to `app/config/app-config.js`, and set `nativeClientId` to the copied value. This is loaded in `login.sagas.js` during authentication. +Copy the provided `clientId` to `app/config/app-config.js`, and set `nativeClientId` to the copied value. This is loaded in `login.sagas.js` during authentication. > If you're using Auth0, you'll also need to change the `audience` in `app/modules/login/login.utils.ts`. For example: > @@ -59,7 +60,8 @@ Follow the instructions under "Add Claims to Access Token" in the [JHipster Ioni ### Log Out from the Identity Provider (iOS/Android) -Expo's auth proxy does not currently work with logging out from the identity provider (supported and enabled on Web). If you want to completely sign out on native apps: +Expo's auth proxy does not currently work with logging out from the identity provider (supported and enabled on Web). If you want to completely sign out on native apps: + - Change `useExpoAuthProxy` to `false` in `app-config.js` - Configure your redirect URLs for logout in your identity provider - Usually something like `exp://127.0.0.1:19000` and `exp://10.0.0.114:19000`, where `10.0.0.114` is your local IP diff --git a/generators/app/files.js b/generators/app/files.js index 3ebced39a..8b73a4ccc 100644 --- a/generators/app/files.js +++ b/generators/app/files.js @@ -7,9 +7,13 @@ const files = { '.editorconfig', '.eslintrc.js', '.gitattributes', + '.gitignore', 'App.js', + 'app.json', 'README.md', + 'babel.config.js', 'metro.config.js', + 'package.json', // templated files 'app/config/app-config.js', 'app/navigation/drawer/drawer-content.js', @@ -76,6 +80,7 @@ const files = { { file: 'app/shared/images/toggle-drawer-icon/toggle-drawer-icon@3x.ios.png', method: 'copy' }, { file: 'app/shared/images/toggle-drawer-icon/toggle-drawer-icon@4x.android.png', method: 'copy' }, { file: 'app/shared/images/toggle-drawer-icon/toggle-drawer-icon@4x.ios.png', method: 'copy' }, + { renameTo: () => 'app/shared/images/logo-jhipster.png', file: generator => `app/shared/images/jhipster_family_member_${generator.hipsterImage}_head.png`, diff --git a/generators/app/index.js b/generators/app/index.js index 65847531a..abd1f21ca 100644 --- a/generators/app/index.js +++ b/generators/app/index.js @@ -8,17 +8,7 @@ const jhipsterUtils = require('generator-jhipster/generators/utils'); const { askDetoxPrompt, askNamePrompt, askBackendPrompt } = require('./prompts'); const { writeFiles } = require('./files'); const packageJson = require('../../package.json'); -const { - printJHipsterLogo, - loadVariables, - setupVariables, - mergeReactNativePackageJson, - createEarlyFiles, - generateReactNativeApp, - appendFiles, - patchInFile, - patchBabel, -} = require('../../lib'); +const { printJHipsterLogo, loadVariables, setupVariables, createEarlyFiles, appendFiles, patchInFile, patchBabel } = require('../../lib'); module.exports = class extends AppGenerator { constructor(args, opts, features) { @@ -105,8 +95,6 @@ module.exports = class extends AppGenerator { this.context.reactNativeAppNameKebabCase = this._.kebabCase(this.context.reactNativeAppName); }, createEarlyFiles, - generateReactNativeApp: generateReactNativeApp.bind(this), - mergeRnPackageJson: mergeReactNativePackageJson.bind(this), writeFiles: writeFiles.bind(this), patchUriScheme() { const appConfig = this.fs.readJSON('app.json'); diff --git a/generators/app/resources/expo/package.json b/generators/app/resources/expo/package.json index f7dfa4f23..ef0ed2348 100644 --- a/generators/app/resources/expo/package.json +++ b/generators/app/resources/expo/package.json @@ -12,13 +12,19 @@ "@react-native-community/datetimepicker": "7.2.0", "@react-native-masked-view/masked-view": "0.2.9", "@react-native-picker/picker": "2.4.10", + "@react-navigation/devtools": "6.0.20", + "expo": "49.0.21", "expo-auth-session": "~5.0.2", "expo-constants": "~14.4.2", "expo-image-picker": "~14.3.2", "expo-linking": "~5.0.2", "expo-random": "~13.2.0", "expo-splash-screen": "~0.20.5", + "expo-status-bar": "~1.6.0", "expo-web-browser": "~12.3.2", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-native": "0.72.6", "react-native-gesture-handler": "~2.12.0", "react-native-reanimated": "~3.3.0", "react-native-safe-area-context": "4.6.3", @@ -26,8 +32,9 @@ "react-native-web": "~0.19.9" }, "devDependencies": { - "expo": "49.0.21", - "expo-template-blank-typescript": "49.0.23", + "@babel/core": "^7.20.0", + "@react-native-community/cli-server-api": "13.0.0", + "@types/react": "~18.2.14", "jest-expo": "^49.0.0", "typescript": "^5.3.3" } diff --git a/generators/app/templates/.gitignore.ejs b/generators/app/templates/.gitignore.ejs new file mode 100644 index 000000000..05647d55c --- /dev/null +++ b/generators/app/templates/.gitignore.ejs @@ -0,0 +1,35 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ + +# Native +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo diff --git a/generators/app/templates/app.json.ejs b/generators/app/templates/app.json.ejs new file mode 100644 index 000000000..dce6c861d --- /dev/null +++ b/generators/app/templates/app.json.ejs @@ -0,0 +1,32 @@ +{ + "expo": { + "name": "<%= context.reactNativeAppName %>", + "slug": "<%= context.reactNativeAppName %>", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "splash": { + "image": "./assets/splash.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "assetBundlePatterns": [ + "**/*" + ], + "ios": { + "bundleIdentifier": "com.anonymous.<%= context.reactNativeAppName %>", + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#ffffff" + }, + "package": "com.anonymous.<%= context.reactNativeAppName %>" + }, + "web": { + "favicon": "./assets/favicon.png" + } + } +} diff --git a/generators/app/templates/babel.config.js.ejs b/generators/app/templates/babel.config.js.ejs new file mode 100644 index 000000000..2900afe9d --- /dev/null +++ b/generators/app/templates/babel.config.js.ejs @@ -0,0 +1,6 @@ +module.exports = function(api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + }; +}; diff --git a/generators/app/templates/package.json b/generators/app/templates/package.json index 98df0b050..08eebc8ed 100644 --- a/generators/app/templates/package.json +++ b/generators/app/templates/package.json @@ -4,7 +4,6 @@ "description": "Dependencies managed by Blueprint - use latest versions", "dependencies": { "@react-native-masked-view/masked-view": "0.3.0", - "@react-navigation/devtools": "6.0.20", "@react-navigation/drawer": "6.6.6", "@react-navigation/native": "6.1.9", "@react-navigation/stack": "6.3.20", @@ -16,8 +15,8 @@ "net": "1.0.2", "querystringify": "2.2.0", "ramda": "0.29.1", - "react-datepicker": "4.24.0", "react-dom": "18.2.0", + "react-datepicker": "4.24.0", "react-native-keyboard-aware-scroll-view": "0.9.5", "react-native-modal-datetime-picker": "17.1.0", "react-native-picker-select": "9.0.0", diff --git a/generators/app/templates/package.json.ejs b/generators/app/templates/package.json.ejs index 83577e047..acfae5102 100644 --- a/generators/app/templates/package.json.ejs +++ b/generators/app/templates/package.json.ejs @@ -2,6 +2,8 @@ "name": "<%= context.reactNativeAppNameKebabCase %>", "version": "0.0.1", "scripts": { + "android": "expo start --android", + "ios": "expo start --ios", "test:watch": "jest --watch", "updateSnapshot": "jest --updateSnapshot", "coverage": "jest --coverage && open coverage/lcov-report/index.html || xdg-open coverage/lcov-report/index.html", @@ -19,19 +21,23 @@ "test:e2e": "./e2e/scripts/setup.sh && detox test --configuration ios.sim.release --retries 3 --loglevel trace --debug-synchronization 500", <%_ } _%> "postinstall": "patch-package", - "prettier": "prettier --write \"{,.,**/,.jhipster/**/}*.{md,json,yml,js,ts,tsx}\"" + "prettier": "prettier --write \"{,.,**/,.jhipster/**/}*.{md,json,yml,js,ts,tsx}\"", + "start": "expo start", + "web": "expo start --web" }, "dependencies": { "@expo/vector-icons": "EXPO_REPLACE_WITH_VERSION", "@react-native-async-storage/async-storage": "EXPO_REPLACE_WITH_VERSION", "@react-native-community/datetimepicker": "EXPO_REPLACE_WITH_VERSION", - "@react-native-masked-view/masked-view": "REPLACE_WITH_VERSION", + "@react-native-masked-view/masked-view": "EXPO_REPLACE_WITH_VERSION", "@react-native-picker/picker": "EXPO_REPLACE_WITH_VERSION", - "@react-navigation/devtools": "REPLACE_WITH_VERSION", + "@react-navigation/devtools": "EXPO_REPLACE_WITH_VERSION", "@react-navigation/drawer": "REPLACE_WITH_VERSION", "@react-navigation/native": "REPLACE_WITH_VERSION", "@react-navigation/stack": "REPLACE_WITH_VERSION", "apisauce": "REPLACE_WITH_VERSION", + "expo": "EXPO_REPLACE_WITH_VERSION", + "expo-status-bar": "EXPO_REPLACE_WITH_VERSION", <%_ if (context.authenticationType === 'oauth2') { _%> "expo-auth-session": "EXPO_REPLACE_WITH_VERSION", "expo-random": "EXPO_REPLACE_WITH_VERSION", @@ -48,7 +54,9 @@ "querystringify": "REPLACE_WITH_VERSION", "ramda": "REPLACE_WITH_VERSION", "react-datepicker": "REPLACE_WITH_VERSION", - "react-dom": "REPLACE_WITH_VERSION", + "react": "EXPO_REPLACE_WITH_VERSION", + "react-dom": "EXPO_REPLACE_WITH_VERSION", + "react-native": "EXPO_REPLACE_WITH_VERSION", "react-native-gesture-handler": "EXPO_REPLACE_WITH_VERSION", "react-native-keyboard-aware-scroll-view": "REPLACE_WITH_VERSION", "react-native-modal-datetime-picker": "REPLACE_WITH_VERSION", @@ -72,12 +80,15 @@ "yup": "REPLACE_WITH_VERSION" }, "devDependencies": { + "@babel/core": "EXPO_REPLACE_WITH_VERSION", "@react-native-community/eslint-config": "REPLACE_WITH_VERSION", + "@react-native-community/cli-server-api": "EXPO_REPLACE_WITH_VERSION", "@storybook/addons": "REPLACE_WITH_VERSION", "@storybook/react-native": "REPLACE_WITH_VERSION", "@storybook/theming": "REPLACE_WITH_VERSION", "@testing-library/jest-native": "REPLACE_WITH_VERSION", "@testing-library/react-native": "REPLACE_WITH_VERSION", + "@types/react": "EXPO_REPLACE_WITH_VERSION", "babel-jest": "REPLACE_WITH_VERSION", "babel-preset-env": "REPLACE_WITH_VERSION", "buffer": "REPLACE_WITH_VERSION", diff --git a/lib/generate-react-native-app.js b/lib/generate-react-native-app.js deleted file mode 100644 index 894763f77..000000000 --- a/lib/generate-react-native-app.js +++ /dev/null @@ -1,39 +0,0 @@ -const chalk = require('chalk'); -const fse = require('fs-extra'); -const dependenciesPackageJson = require('../generators/app/templates/package.json'); -const expoDependenciesPackageJson = require('../generators/app/resources/expo/package.json'); - -const createExpoAppVersion = dependenciesPackageJson.devDependencies['create-expo-app']; -const expoTemplateVersion = expoDependenciesPackageJson.devDependencies['expo-template-blank-typescript']; - -function generateReactNativeApp() { - const name = this.context.reactNativeAppName; - this.info(chalk.green("Running 'npx create-expo-app', this will take some time...")); - try { - // remove the expected folder in case it already exists - fse.removeSync(`${process.cwd()}/${name}/`); - - const generateCommand = `-y create-expo-app@${createExpoAppVersion} -t expo-template-blank-typescript@${expoTemplateVersion} --no-install ${name}`; - this.debug(generateCommand); - - // generate the expo app - this.spawnCommandSync('npx', generateCommand.split(' '), { - stdio: this.options.debug ? 'inherit' : 'ignore', - }); - - // collapse generated RN folder into parent folder - const rnFiles = ['.gitignore', 'app.json', 'babel.config.js', 'package.json', 'tsconfig.json']; - rnFiles.forEach(file => { - this.fs.move(`${process.cwd()}/${name}/${file}`, `${process.cwd()}/${file}`, { overwrite: true }); - }); - fse.removeSync(`${process.cwd()}/${name}/`); - } catch (e) { - this.debug(e); - if (process.cwd().includes('(') || process.cwd().includes(')')) { - this.error('Project path contains parenthesis - please generate in a folder without parenthesis.'); - } - this.error('Something went wrong generating the React Native app, please try with the --debug flag for more info.'); - } -} - -module.exports = { generateReactNativeApp }; diff --git a/lib/index.js b/lib/index.js index 85647339e..af2ddb15e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,8 +1,6 @@ const { printJHipsterLogo } = require('./print-jhipster-logo'); const { loadVariables, setupVariables } = require('./setup-variables'); -const { mergeReactNativePackageJson } = require('./merge-react-native-package-json'); const { createEarlyFiles } = require('./pre-write'); -const { generateReactNativeApp } = require('./generate-react-native-app'); const { appendFiles } = require('./append-files'); const { getAppFolder } = require('./get-app-folder'); const { patchBabel } = require('./patch-babel'); @@ -15,9 +13,7 @@ module.exports = { printJHipsterLogo, loadVariables, setupVariables, - mergeReactNativePackageJson, createEarlyFiles, - generateReactNativeApp, appendFiles, getAppFolder, patchBabel, diff --git a/lib/merge-react-native-package-json.js b/lib/merge-react-native-package-json.js deleted file mode 100644 index 4ef948a0d..000000000 --- a/lib/merge-react-native-package-json.js +++ /dev/null @@ -1,39 +0,0 @@ -const utils = require('generator-jhipster/generators/utils'); - -function mergeReactNativePackageJson() { - const done = this.async(); - // get react-native generated package.json - const rnPackageJson = this.fs.readJSON('package.json'); - // get templated package.json - const _this = this; - this.debug('Templating package.json.ejs...'); - utils.renderContent('package.json.ejs', _this, _this, {}, templatedPackageJsonAsString => { - this.debug(templatedPackageJsonAsString); - const mergedPackageJson = JSON.parse(templatedPackageJsonAsString); - // merge these sections, with a precedence to our templated version - const keys = ['scripts', 'dependencies', 'devDependencies']; - // loop through the keys - keys.forEach(packageJsonSectionKey => { - // get the section of the package.json from the react-native package - const packageJsonSection = rnPackageJson[packageJsonSectionKey]; - this.debug(`Updating package.json section: ${packageJsonSectionKey}`); - // loop through the keys in the package.json section - Object.keys(packageJsonSection).forEach(key => { - this.debug(`Checking for: ${packageJsonSectionKey}.${key}`); - // if the templated package.json does not have the key, add it to the merged package.json - // if the templated package.json has the key, it means we overwrote that section in the template - if (!Object.prototype.hasOwnProperty.call(mergedPackageJson[packageJsonSectionKey], key)) { - this.debug(`Adding ${key}: ${packageJsonSection[key]} to ${packageJsonSectionKey} from RN package.json`); - mergedPackageJson[packageJsonSectionKey][key] = packageJsonSection[key]; - } - }); - }); - - this.fs.writeJSON('package.json', mergedPackageJson); - this.debug("package.json merged with React Native's package.json"); - done(); - }); -} -module.exports = { - mergeReactNativePackageJson, -};