Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
feat: Ignite 3 compability (#151 by @jamonholmgren, @yulolimum, @rmev…
Browse files Browse the repository at this point in the history
…ans9, @nirre7)

BREAKING CHANGE: Updated to be compatible with Ignite 3.0+
  • Loading branch information
jamonholmgren authored May 2, 2019
1 parent 3b94afd commit 25eaef0
Show file tree
Hide file tree
Showing 35 changed files with 626 additions and 424 deletions.
9 changes: 6 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defaults: &defaults

mac: &mac
macos:
xcode: "10.1.0"
xcode: '10.1.0'
working_directory: ~/repo

version: 2
Expand Down Expand Up @@ -45,6 +45,10 @@ jobs:
- v1-dependencies-bowser-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-bowser-
- run:
name: Install dependencies
command: |
yarn install
- run:
name: Install React Native CLI and Ignite CLI
command: |
Expand Down Expand Up @@ -101,7 +105,6 @@ jobs:
name: Remove temp directory
command: rm -rf tempApp


publish:
<<: *defaults
steps:
Expand Down Expand Up @@ -132,4 +135,4 @@ workflows:
- tests
filters:
branches:
only: master
only: master
139 changes: 83 additions & 56 deletions boilerplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { getReactNativeVersion } = require('./lib/react-native-version')
* @param {*} context - The gluegun context.
* @returns {boolean}
*/
const isAndroidInstalled = function (context) {
const isAndroidInstalled = function(context) {
const androidHome = process.env['ANDROID_HOME']
const hasAndroidEnv = !context.strings.isBlank(androidHome)
const hasAndroid = hasAndroidEnv && context.filesystem.exists(`${androidHome}/tools`) === 'dir'
Expand All @@ -32,45 +32,50 @@ async function install(context) {
system,
template,
prompt,
patching
} = context
const { colors } = print
const { red, yellow, bold, gray, blue, cyan } = colors
const isWindows = process.platform === 'win32'
const isMac = process.platform === 'darwin'

const perfStart = (new Date()).getTime()
const perfStart = new Date().getTime()

const name = parameters.third
const name = parameters.first
const spinner = print
.spin(`using the ${red('Infinite Red')} boilerplate v3 (code name 'Bowser')`)
.succeed()

// attempt to install React Native or die trying
const rnInstall = await reactNative.install({
let rnInstall = undefined
rnInstall = await reactNative.install({
name,
version: getReactNativeVersion(context)
version: getReactNativeVersion(context),
useNpm: true // CI seems to die without it
})
if (rnInstall.exitCode > 0) process.exit(rnInstall.exitCode)

// remove the __tests__ directory, App.js, and unnecessary config files that come with React Native
const filesToRemove = [
'__tests__',
'App.js',
'.flowconfig',
'.buckconfig',
]
const filesToRemove = ['__tests__', 'App.js', '.babelrc', '.flowconfig', '.buckconfig']
filesToRemove.map(filesystem.remove)

let includeDetox = false
if (isMac) {
const askAboutDetox = parameters.options.detox === undefined
includeDetox = askAboutDetox ? await prompt.confirm('Would you like to include Detox end-to-end tests?') : parameters.options.detox === true
includeDetox = askAboutDetox
? await prompt.confirm('Would you like to include Detox end-to-end tests?')
: parameters.options.detox === true

if (includeDetox) {
print.info(`You'll love Detox for testing your app! There are some additional requirements to install, so make sure to check out ${cyan('e2e/README.md')}!`)
// prettier-ignore
print.info(`
You'll love Detox for testing your app! There are some additional requirements to
install, so make sure to check out ${cyan('e2e/README.md')} in your generated app!
`)
}
} else {
if (parameters.options.detox === true) {
// prettier-ignore
if (isWindows) {
print.info("Skipping Detox because it is only supported on macOS, but you're running Windows")
} else {
Expand All @@ -95,12 +100,13 @@ async function install(context) {
matching: '!*.ejs'
})
filesystem.copy(`${__dirname}/boilerplate/bin`, `${process.cwd()}/bin`, {
overwrite: true,
})
includeDetox && filesystem.copy(`${__dirname}/boilerplate/e2e`, `${process.cwd()}/e2e`, {
overwrite: true,
matching: '!*.ejs'
overwrite: true
})
includeDetox &&
filesystem.copy(`${__dirname}/boilerplate/e2e`, `${process.cwd()}/e2e`, {
overwrite: true,
matching: '!*.ejs'
})
spinner.stop()

// generate some templates
Expand All @@ -115,10 +121,15 @@ async function install(context) {
{ template: '.solidarity', target: '.solidarity' },
{ template: '.babelrc', target: '.babelrc' },
{ template: 'tsconfig.json', target: 'tsconfig.json' },
{ template: 'tslint.json', target: 'tslint.json' },
{ template: 'app/app.tsx.ejs', target: 'app/app.tsx' },
{ template: 'app/screens/first-example-screen/first-example-screen.tsx.ejs', target: 'app/screens/first-example-screen/first-example-screen.tsx' },
{ template: 'app/screens/second-example-screen/second-example-screen.tsx.ejs', target: 'app/screens/second-example-screen/second-example-screen.tsx' },
{
template: 'app/screens/first-example-screen/first-example-screen.tsx.ejs',
target: 'app/screens/first-example-screen/first-example-screen.tsx'
},
{
template: 'app/screens/second-example-screen/second-example-screen.tsx.ejs',
target: 'app/screens/second-example-screen/second-example-screen.tsx'
}
]
const templateProps = {
name,
Expand All @@ -127,7 +138,7 @@ async function install(context) {
vectorIcons: false,
animatable: false,
i18n: false,
includeDetox,
includeDetox
}
await ignite.copyBatch(context, templates, templateProps, {
quiet: true,
Expand Down Expand Up @@ -157,19 +168,13 @@ async function install(context) {

// deep merge, lol
const newPackage = pipe(
assoc(
'dependencies',
merge(currentPackage.dependencies, newPackageJson.dependencies)
),
assoc('dependencies', merge(currentPackage.dependencies, newPackageJson.dependencies)),
assoc(
'devDependencies',
merge(currentPackage.devDependencies, newPackageJson.devDependencies)
),
assoc('scripts', merge(currentPackage.scripts, newPackageJson.scripts)),
merge(
__,
omit(['dependencies', 'devDependencies', 'scripts'], newPackageJson)
)
merge(__, omit(['dependencies', 'devDependencies', 'scripts'], newPackageJson))
)(currentPackage)

// write this out
Expand All @@ -181,9 +186,6 @@ async function install(context) {
// pass long the debug flag if we're running in that mode
const debugFlag = parameters.options.debug ? '--debug' : ''

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// NOTE(steve): I'm re-adding this here because boilerplates now hold permanent files
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
try {
// boilerplate adds itself to get plugin.js/generators etc
// Could be directory, npm@version, or just npm name. Default to passed in values
Expand All @@ -197,30 +199,40 @@ async function install(context) {
await system.spawn('react-native link', { stdio: 'ignore' })
spinner.stop()

await ignite.addModule('react-native-gesture-handler', { version: '1.0.9', link: true })
await ignite.addModule('react-native-gesture-handler', { version: '1.1.0', link: true })

ignite.patchInFile(`${process.cwd()}/android/app/src/main/java/com/${name.toLowerCase()}/MainActivity.java`, {
after: 'import com.facebook.react.ReactActivity;',
insert: `
ignite.patchInFile(
`${process.cwd()}/android/app/src/main/java/com/${name.toLowerCase()}/MainActivity.java`,
{
after: 'import com.facebook.react.ReactActivity;',
insert: `
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;`
})

ignite.patchInFile(`${process.cwd()}/android/app/src/main/java/com/${name.toLowerCase()}/MainActivity.java`, {
after: `public class MainActivity extends ReactActivity {`,
insert: '\n @Override\n' +
' protected ReactActivityDelegate createReactActivityDelegate() {\n' +
' return new ReactActivityDelegate(this, getMainComponentName()) {\n' +
' @Override\n' +
' protected ReactRootView createRootView() {\n' +
' return new RNGestureHandlerEnabledRootView(MainActivity.this);\n' +
' }\n' +
' };\n' +
' }'
})
}
)

ignite.patchInFile(
`${process.cwd()}/android/app/src/main/java/com/${name.toLowerCase()}/MainActivity.java`,
{
after: `public class MainActivity extends ReactActivity {`,
insert:
'\n @Override\n' +
' protected ReactActivityDelegate createReactActivityDelegate() {\n' +
' return new ReactActivityDelegate(this, getMainComponentName()) {\n' +
' @Override\n' +
' protected ReactRootView createRootView() {\n' +
' return new RNGestureHandlerEnabledRootView(MainActivity.this);\n' +
' }\n' +
' };\n' +
' }'
}
)
} catch (e) {
ignite.log(e)
print.error(`
There were errors while generating the project. Run with --debug to see verbose output.
`)
throw e
}

Expand All @@ -246,11 +258,22 @@ async function install(context) {
await system.spawn('react-native link', { stdio: 'ignore' })
spinner.succeed(`Linked dependencies`)

const perfDuration = parseInt(((new Date()).getTime() - perfStart) / 10) / 100
// for Windows, fix the settings.gradle file. Ref: https://github.com/oblador/react-native-vector-icons/issues/938#issuecomment-463296401
// for ease of use, just replace any backslashes with forward slashes
if (isWindows) {
await patching.update(`${process.cwd()}/android/settings.gradle`, contents => {
return contents.split('\\').join('/')
})
}

const perfDuration = parseInt((new Date().getTime() - perfStart) / 10) / 100
spinner.succeed(`ignited ${yellow(name)} in ${perfDuration}s`)

const androidInfo = isAndroidInstalled(context) ? ''
: `\n\nTo run in Android, make sure you've followed the latest react-native setup instructions at https://facebook.github.io/react-native/docs/getting-started.html before using ignite.\nYou won't be able to run ${bold('react-native run-android')} successfully until you have.`
const androidInfo = isAndroidInstalled(context)
? ''
: `\n\nTo run in Android, make sure you've followed the latest react-native setup instructions at https://facebook.github.io/react-native/docs/getting-started.html before using ignite.\nYou won't be able to run ${bold(
'react-native run-android'
)} successfully until you have.`

const successMessage = `
${red('Ignite CLI')} ignited ${yellow(name)} in ${gray(`${perfDuration}s`)}
Expand All @@ -261,10 +284,14 @@ async function install(context) {
react-native run-ios
react-native run-android${androidInfo}
ignite --help
${blue('Need additional help? Join our Slack community at http://community.infinite.red.')}
${cyan('Need additional help? Join our Slack community at http://community.infinite.red.')}
${bold('Now get cooking! 🍽')}
${gray(
'(Running yarn install one last time to make sure everything is installed -- please be patient!)'
)}
`

print.info(successMessage)
Expand Down
25 changes: 3 additions & 22 deletions boilerplate/app/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TouchableOpacity } from "react-native"
import { Text } from "../text"
import { viewPresets, textPresets } from "./button.presets"
import { ButtonProps } from "./button.props"
import { reduce } from "ramda"
import { mergeAll, flatten } from "ramda"

/**
* For your text displaying needs.
Expand All @@ -14,27 +14,8 @@ export function Button(props: ButtonProps) {
// grab the props
const { preset = "primary", tx, text, style: styleOverride, textStyle: textStyleOverride, children, ...rest } = props

// assemble the style
const viewPresetToUse = viewPresets[preset] || viewPresets.primary
const textPresetToUse = textPresets[preset] || textPresets.primary

let viewStyle
if (Array.isArray(styleOverride)) {
viewStyle = reduce((acc,term) => {
return { ...acc, ...term }
}, viewPresetToUse, styleOverride)
} else {
viewStyle = { ...viewPresetToUse, ...styleOverride }
}

let textStyle
if (Array.isArray(textStyleOverride)) {
textStyle = reduce((acc,term) => {
return { ...acc, ...term }
}, textPresetToUse, textStyleOverride)
} else {
textStyle = { ...textPresetToUse, ...textStyleOverride }
}
const viewStyle = mergeAll(flatten([viewPresets[preset] || viewPresets.primary, styleOverride]))
const textStyle = mergeAll(flatten([textPresets[preset] || textPresets.primary, textStyleOverride]))

const content = children || <Text tx={tx} text={text} style={textStyle} />

Expand Down
3 changes: 3 additions & 0 deletions boilerplate/app/components/checkbox/checkbox.story.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/* eslint-disable react-native/no-inline-styles */
/* eslint-disable react-native/no-color-literals */

import * as React from "react"
import { View, ViewStyle } from "react-native"
import { storiesOf } from "@storybook/react-native"
Expand Down
30 changes: 4 additions & 26 deletions boilerplate/app/components/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TouchableOpacity, TextStyle, ViewStyle, View } from "react-native"
import { Text } from "../text"
import { color, spacing } from "../../theme"
import { CheckboxProps } from "./checkbox.props"
import { reduce } from "ramda"
import { mergeAll, flatten } from "ramda"

const ROOT: ViewStyle = {
flexDirection: "row",
Expand Down Expand Up @@ -34,32 +34,10 @@ const LABEL: TextStyle = { paddingLeft: spacing[2] }
export function Checkbox(props: CheckboxProps) {
const numberOfLines = props.multiline ? 0 : 1

let rootStyle
if (Array.isArray(props.style)) {
rootStyle = reduce((acc,term) => {
return { ...acc, ...term }
}, ROOT, props.style)
} else {
rootStyle = { ...ROOT, ...props.style } as ViewStyle
}
const rootStyle = mergeAll(flatten([ROOT, props.style]))
const outlineStyle = mergeAll(flatten([OUTLINE, props.outlineStyle]))
const fillStyle = mergeAll(flatten([FILL, props.fillStyle]))

let outlineStyle
if (Array.isArray(props.outlineStyle)) {
outlineStyle = reduce((acc,term) => {
return { ...acc, ...term }
}, OUTLINE, props.outlineStyle)
} else {
outlineStyle = { ...OUTLINE, ...props.outlineStyle } as ViewStyle
}

let fillStyle
if (Array.isArray(props.fillStyle)) {
fillStyle = reduce((acc,term) => {
return { ...acc, ...term }
}, FILL, props.fillStyle)
} else {
fillStyle = { ...FILL, ...props.fillStyle } as ViewStyle
}
const onPress = props.onToggle ? () => props.onToggle && props.onToggle(!props.value) : null

return (
Expand Down
3 changes: 3 additions & 0 deletions boilerplate/app/components/form-row/form-row.story.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/* eslint-disable react-native/no-inline-styles */
/* eslint-disable react-native/no-color-literals */

import * as React from "react"
import { storiesOf } from "@storybook/react-native"
import { StoryScreen, Story, UseCase } from "../../../storybook/views"
Expand Down
Loading

0 comments on commit 25eaef0

Please sign in to comment.