master 356ac6758859 cached
129 files
141.2 KB
40.0k tokens
48 symbols
1 requests
Download .txt
Repository: aerian-studios/ignite-typescript-boilerplate
Branch: master
Commit: 356ac6758859
Files: 129
Total size: 141.2 KB

Directory structure:
gitextract_1v1ble8m/

├── .gitignore
├── boilerplate/
│   ├── .babelrc
│   ├── .editorconfig
│   ├── App/
│   │   ├── Components/
│   │   │   ├── AlertMessage/
│   │   │   │   ├── AlertMessage.story.tsx
│   │   │   │   ├── AlertMessage.tsx
│   │   │   │   ├── AlertMessageStyles.ts
│   │   │   │   ├── AlertMessageTest.tsx
│   │   │   │   └── index.ts
│   │   │   ├── DrawerButton/
│   │   │   │   ├── DrawerButton.story.tsx
│   │   │   │   ├── DrawerButton.tsx
│   │   │   │   ├── DrawerButtonStyles.ts
│   │   │   │   ├── DrawerButtonTest.tsx
│   │   │   │   └── index.ts
│   │   │   ├── FullButton/
│   │   │   │   ├── FullButton.story.tsx
│   │   │   │   ├── FullButton.tsx
│   │   │   │   ├── FullButtonStyles.ts
│   │   │   │   ├── FullButtonTest.tsx
│   │   │   │   └── index.ts
│   │   │   ├── README.md
│   │   │   ├── RoundedButton/
│   │   │   │   ├── RoundedButton.story.tsx
│   │   │   │   ├── RoundedButton.tsx
│   │   │   │   ├── RoundedButtonStyles.ts
│   │   │   │   ├── RoundedButtonTest.tsx
│   │   │   │   └── index.ts
│   │   │   └── Stories.tsx
│   │   ├── Config/
│   │   │   ├── AppConfig.ts
│   │   │   ├── DebugConfig.js
│   │   │   ├── README.md
│   │   │   ├── ReactotronConfig.ts
│   │   │   ├── ReduxPersist.ts
│   │   │   └── index.ts
│   │   ├── Containers/
│   │   │   ├── App.tsx
│   │   │   ├── LaunchScreen/
│   │   │   │   ├── LaunchScreen.tsx
│   │   │   │   ├── LaunchScreenStyles.ts
│   │   │   │   └── index.ts
│   │   │   ├── README.md
│   │   │   └── RootContainer/
│   │   │       ├── RootContainer.tsx
│   │   │       ├── RootContainerStyles.ts
│   │   │       └── index.ts
│   │   ├── Fixtures/
│   │   │   ├── README.md
│   │   │   ├── gantman.json
│   │   │   ├── rateLimit.json
│   │   │   ├── root.json
│   │   │   └── skellock.json
│   │   ├── Images/
│   │   │   └── README.md
│   │   ├── Lib/
│   │   │   ├── README.md
│   │   │   └── ReduxHelpers.ts
│   │   ├── Navigation/
│   │   │   ├── AppNavigation.tsx
│   │   │   ├── ReduxNavigation.tsx
│   │   │   └── Styles/
│   │   │       └── NavigationStyles.ts
│   │   ├── Reducers/
│   │   │   ├── CreateStore.tsx
│   │   │   ├── GithubReducers/
│   │   │   │   ├── GithubReducersTest.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── NavigationReducers/
│   │   │   │   └── index.tsx
│   │   │   ├── ScreenTrackingMiddleware.tsx
│   │   │   ├── StartupReducers/
│   │   │   │   └── index.tsx
│   │   │   └── index.ts
│   │   ├── Sagas/
│   │   │   ├── GithubSagas/
│   │   │   │   ├── GithubSagaTest.ts
│   │   │   │   └── index.ts
│   │   │   ├── StartupSagas/
│   │   │   │   ├── StartupSagaTest.ts
│   │   │   │   └── index.ts
│   │   │   └── index.ts
│   │   ├── Services/
│   │   │   ├── Api.ts
│   │   │   ├── ExamplesRegistry.tsx
│   │   │   ├── FixtureAPITest.tsx
│   │   │   ├── FixtureApi.tsx
│   │   │   ├── GithubApi.tsx
│   │   │   ├── ImmutablePersistenceTransform.tsx
│   │   │   └── RehydrationServices.tsx
│   │   ├── Themes/
│   │   │   ├── ApplicationStyles.ts
│   │   │   ├── Colors.ts
│   │   │   ├── Fonts.ts
│   │   │   ├── Images.ts
│   │   │   ├── Metrics.ts
│   │   │   ├── README.md
│   │   │   └── index.ts
│   │   └── Transforms/
│   │       ├── ConvertFromKelvin.ts
│   │       └── README.md
│   ├── README.md
│   ├── Tests/
│   │   ├── Setup.tsx.ejs
│   │   └── StoriesTest.ts
│   ├── ignite.json.ejs
│   ├── index.js.ejs
│   ├── package.json.ejs
│   ├── rn-cli.config.js
│   ├── storybook/
│   │   ├── addons.js
│   │   ├── index.js
│   │   └── storybook.ejs
│   ├── tsconfig.json
│   ├── tslint.json
│   └── types/
│       ├── @storybook/
│       │   └── react-native.d.ts
│       └── reduxsauce/
│           └── index.d.ts
├── boilerplate.js
├── commands/
│   ├── component.js
│   ├── container.js
│   ├── list.js
│   ├── reducers.js
│   ├── saga.js
│   └── screen.js
├── ignite.json
├── lib/
│   ├── patterns.js
│   └── react-native-version.js
├── options.js
├── package.json
├── plugin.js
├── readme.md
├── templates/
│   ├── component-index.ejs
│   ├── component-story.ejs
│   ├── component-style.ejs
│   ├── component-test-jest.ejs
│   ├── component.ejs
│   ├── container-style.ejs
│   ├── container.ejs
│   ├── flatlist-grid-style.ejs
│   ├── flatlist-grid.ejs
│   ├── flatlist-sections.ejs
│   ├── flatlist.ejs
│   ├── listview-grid-style.ejs
│   ├── reducers-test-jest.ejs
│   ├── reducers.ejs
│   ├── saga-test-jest.ejs
│   ├── saga.ejs
│   ├── screen-index.ejs
│   ├── screen-style.ejs
│   └── screen.ejs
├── test/
│   ├── generators-integration.test.js
│   ├── interface.test.js
│   └── react-native-version.test.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# OSX
#
.DS_Store
npm-debug.log
npm-debug.log*
coverage
.nyc_output
yarn.lock
lerna-debug.log
node_modules
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
testgrounds
IntegrationTest
integration_test


================================================
FILE: boilerplate/.babelrc
================================================
{
  "presets": ["react-native"],
  "env": {
    "production": {
      "plugins": ["ignite-ignore-reactotron"]
    }
  }
}


================================================
FILE: boilerplate/.editorconfig
================================================
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true


[*.gradle]
indent_size = 4

================================================
FILE: boilerplate/App/Components/AlertMessage/AlertMessage.story.tsx
================================================
import { storiesOf } from "@storybook/react-native";
import * as React from "react";

import AlertMessage from "./AlertMessage";

storiesOf("AlertMessage")
  .add("Default", () => (
    <AlertMessage
      title="ALERT ALERT"
    />
  ))
  .add("Hidden", () => (
    <AlertMessage
      title="ALERT ALERT"
      show={false}
    />
  ))
  .add("Custom Style", () => (
    <AlertMessage
      title="ALERT ALERT"
      style={{ backgroundColor: "red" }}
    />
  ));


================================================
FILE: boilerplate/App/Components/AlertMessage/AlertMessage.tsx
================================================
import * as React from "react";
import { Text, View, ViewStyle } from "react-native";
import styles from "./AlertMessageStyles";

interface Props {
  icon?: string;
  show?: boolean;
  style?: ViewStyle;
  title?: string;
}

const AlertMessage: React.SFC<Props> = ({ icon, show = true, style, title }: Props) => show ? (
  <View style={[styles.container, style]}>
    <View style={styles.contentContainer}>
      <Text allowFontScaling={false} style={styles.message}>{title && title.toUpperCase()}</Text>
    </View>
  </View>
) : null;

export default AlertMessage;


================================================
FILE: boilerplate/App/Components/AlertMessage/AlertMessageStyles.ts
================================================
import { StyleSheet } from "react-native";
import { Colors, Fonts, Metrics } from "../../Themes/";

export default StyleSheet.create({
  container: {
    justifyContent: "center",
    marginVertical: Metrics.section,
  },
  contentContainer: {
    alignSelf: "center",
    alignItems: "center",
  },
  message: {
    marginTop: Metrics.baseMargin,
    marginHorizontal: Metrics.baseMargin,
    textAlign: "center",
    fontFamily: Fonts.type.base,
    fontSize: Fonts.size.regular,
    fontWeight: "bold",
    color: Colors.steel,
  },
  icon: {
    color: Colors.steel,
  },
});


================================================
FILE: boilerplate/App/Components/AlertMessage/AlertMessageTest.tsx
================================================
/// <reference types="@types/jest" />
import * as React from "react";
import "react-native";
import * as renderer from "react-test-renderer";
import AlertMessage from "./AlertMessage";

test("AlertMessage component renders correctly if show is true", () => {
  const tree = renderer.create(<AlertMessage title="howdy" />).toJSON();
  expect(tree).toMatchSnapshot();
});

test("AlertMessage component does not render if show is false", () => {
  const tree = renderer.create(<AlertMessage title="howdy" show={false} />).toJSON();
  expect(tree).toMatchSnapshot();
});

test("AlertMessage component renders correctly if backgroundColor prop is set", () => {
  const tree = renderer.create(<AlertMessage title="howdy" style={{backgroundColor: "red"}} />).toJSON();
  expect(tree).toMatchSnapshot();
});


================================================
FILE: boilerplate/App/Components/AlertMessage/index.ts
================================================
import AlertMessage from "./AlertMessage";

export default AlertMessage;


================================================
FILE: boilerplate/App/Components/DrawerButton/DrawerButton.story.tsx
================================================
import { storiesOf } from "@storybook/react-native";
import * as React from "react";
import { View } from "react-native";

import DrawerButton from "./DrawerButton";

storiesOf("DrawerButton")
  .add("Default", () => (
    <View style={{ backgroundColor: "black" }}>
      <DrawerButton
        text="Drawer Button"
        // tslint:disable-next-line:jsx-no-lambda
        onPress={() => alert("Hi")}
      />
    </View>
  ));


================================================
FILE: boilerplate/App/Components/DrawerButton/DrawerButton.tsx
================================================
import * as React from "react";
import { Text, TouchableOpacity } from "react-native";
import ExamplesRegistry from "../../Services/ExamplesRegistry";
import styles from "./DrawerButtonStyles";

// Note that this file (App/Components/DrawerButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.

// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample("Drawer Button", () =>
  (
  <DrawerButton
    text="Example left drawer button"
    // tslint:disable-next-line:jsx-no-lambda
    onPress={() => window.alert("Your drawers are showing")}
  />),
);

interface Props {
  onPress: () => void;
  text: string;
}

// tslint:disable-next-line:no-empty
const DrawerButton: React.SFC<Props> = ({text, onPress = () => {}}: Props) => (
  <TouchableOpacity onPress={onPress}>
    <Text style={styles.text}>{text}</Text>
  </TouchableOpacity>
);

export default DrawerButton;


================================================
FILE: boilerplate/App/Components/DrawerButton/DrawerButtonStyles.ts
================================================
import Colors from "../../Themes/Colors";
import Fonts from "../../Themes/Fonts";
import Metrics from "../../Themes/Metrics";

export default {
  text: {
    ...Fonts.style.h5,
    color: Colors.snow,
    marginVertical: Metrics.baseMargin,
  },
};


================================================
FILE: boilerplate/App/Components/DrawerButton/DrawerButtonTest.tsx
================================================
/// <reference types="@types/jest" />
import { shallow } from "enzyme";
import * as React from "react";
import "react-native";
import * as renderer from "react-test-renderer";
import DrawerButton from "./DrawerButton";

test("AlertMessage component renders correctly", () => {
  const tree = renderer.create(<DrawerButton onPress={() => {}} text="hi" />).toJSON();
  expect(tree).toMatchSnapshot();
});

test("onPress", () => {
  let i = 0;
  const onPress = () => i++;
  const wrapperPress = shallow(<DrawerButton onPress={onPress} text="hi" />);

  expect(wrapperPress.prop("onPress")).toBe(onPress); // uses the right handler
  expect(i).toBe(0);
  wrapperPress.simulate("press");
  expect(i).toBe(1);
});


================================================
FILE: boilerplate/App/Components/DrawerButton/index.ts
================================================
import DrawerButton from "./DrawerButton";

export default DrawerButton;


================================================
FILE: boilerplate/App/Components/FullButton/FullButton.story.tsx
================================================
import { storiesOf } from "@storybook/react-native";
import * as React from "react";

import FullButton from "./FullButton";

storiesOf("FullButton")
  .add("Default", () => (
    <FullButton
      text="A simple button"
    />
  ))
  .add("Custom Style", () => (
    <FullButton
      text="Style Me Up!"
      style={{ backgroundColor: "blue" }}
    />
  ));


================================================
FILE: boilerplate/App/Components/FullButton/FullButton.tsx
================================================
import * as React from "react";
import { Text, TouchableOpacity, ViewStyle } from "react-native";
import ExamplesRegistry from "../../Services/ExamplesRegistry";
import styles from "./FullButtonStyles";

// Note that this file (App/Components/FullButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.

// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample("Full Button", () => (
  <FullButton
    text="Hey there"
    // tslint:disable-next-line:jsx-no-lambda
    onPress={() => window.alert("Full Button Pressed!")}
  />),
);

interface Props {
    onPress?: () => void;
    style?: ViewStyle;
    text?: string;
}

// tslint:disable-next-line:no-empty
const FullButton: React.SFC<Props> = ({text, style, onPress = () => {}}: Props) => (
  <TouchableOpacity style={[styles.button, style]} onPress={onPress}>
    <Text style={styles.buttonText}>{text && text.toUpperCase()}</Text>
  </TouchableOpacity>
);

export default FullButton;


================================================
FILE: boilerplate/App/Components/FullButton/FullButtonStyles.ts
================================================
import { StyleSheet } from "react-native";
import Colors from "../../Themes/Colors";
import Fonts from "../../Themes/Fonts";

export default StyleSheet.create({
  button: {
    marginVertical: 5,
    borderTopColor: Colors.fire,
    borderBottomColor: Colors.bloodOrange,
    borderTopWidth: 1,
    borderBottomWidth: 1,
    backgroundColor: Colors.ember,
  },
  buttonText: {
    margin: 18,
    textAlign: "center",
    color: Colors.snow,
    fontSize: Fonts.size.medium,
    fontFamily: Fonts.type.bold,
  },
});


================================================
FILE: boilerplate/App/Components/FullButton/FullButtonTest.tsx
================================================
/// <reference types="@types/jest" />
import { shallow } from "enzyme";
import * as React from "react";
import "react-native";
import * as renderer from "react-test-renderer";
import FullButton from "./FullButton";

test("FullButton component renders correctly", () => {
  const tree = renderer.create(<FullButton onPress={() => {}} text="hi" />).toJSON();
  expect(tree).toMatchSnapshot();
});

test("onPress", () => {
  let i = 0; // i guess i could have used sinon here too... less is more i guess
  const onPress = () => i++;
  const wrapperPress = shallow(<FullButton onPress={onPress} text="hi" />);

  expect(wrapperPress.prop("onPress")).toBe(onPress); // uses the right handler
  expect(i).toBe(0);
  wrapperPress.simulate("press");
  expect(i).toBe(1);
});


================================================
FILE: boilerplate/App/Components/FullButton/index.ts
================================================
import FullButton from "./FullButton";

export default FullButton;


================================================
FILE: boilerplate/App/Components/README.md
================================================
### Components Folder
All components are stored and organized here

"In an ideal world, most of your components would be stateless functions because in the future we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. This is the recommended pattern, when possible." --React docs

================================================
FILE: boilerplate/App/Components/RoundedButton/RoundedButton.story.tsx
================================================
import { storiesOf } from "@storybook/react-native";
import * as React from "react";

import RoundedButton from "./RoundedButton";

storiesOf("RoundedButton", module)
  .add("Default", () => (
    <RoundedButton
      text="A simple rounded button"
    />
  ))
  .add("Text as children", () => (
    <RoundedButton>
        Hello from the children!
    </RoundedButton>
  ));


================================================
FILE: boilerplate/App/Components/RoundedButton/RoundedButton.tsx
================================================
import * as React from "react";
import { Text, TouchableOpacity } from "react-native";
import ExamplesRegistry from "../../Services/ExamplesRegistry";
import styles from "./RoundedButtonStyles";

// Note that this file (App/Components/RoundedButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.

// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample("Rounded Button", () =>
  (
    <RoundedButton
      text="real buttons have curves"
      // tslint:disable-next-line:jsx-no-lambda
      onPress={() => window.alert("Rounded Button Pressed!")}
    />),
);

interface Props {
  onPress?: () => any;
  text?: string;
  children?: string;
}

// tslint:disable-next-line:no-empty
const RoundedButton: React.SFC<Props> = ({ text, children, onPress = () => { } }: Props) => {
  const buttonText = (text || children || "").toUpperCase();
  return (
    <TouchableOpacity style={styles.button} onPress={onPress}>
      <Text style={styles.buttonText}>{buttonText}</Text>
    </TouchableOpacity>
  );
};

export default RoundedButton;


================================================
FILE: boilerplate/App/Components/RoundedButton/RoundedButtonStyles.ts
================================================
import { StyleSheet } from "react-native";
import Colors from "../../Themes/Colors";
import Fonts from "../../Themes/Fonts";
import Metrics from "../../Themes/Metrics";

export default StyleSheet.create({
  button: {
    height: 45,
    borderRadius: 5,
    marginHorizontal: Metrics.section,
    marginVertical: Metrics.baseMargin,
    backgroundColor: Colors.fire,
    justifyContent: "center",
  },
  buttonText: {
    color: Colors.snow,
    textAlign: "center",
    fontWeight: "bold",
    fontSize: Fonts.size.medium,
    marginVertical: Metrics.baseMargin,
  },
});


================================================
FILE: boilerplate/App/Components/RoundedButton/RoundedButtonTest.tsx
================================================
/// <reference types="@types/jest" />
import { shallow } from "enzyme";
import * as React from "react";
import "react-native";
import * as renderer from "react-test-renderer";
import RoundedButton from "./RoundedButton";

test("RoundedButton component renders correctly", () => {
  const tree = renderer.create(<RoundedButton onPress={() => {}} text="howdy" />).toJSON();
  expect(tree).toMatchSnapshot();
});

test("RoundedButton component with children renders correctly", () => {
  const tree = renderer.create(<RoundedButton onPress={() => {}}>Howdy</RoundedButton>).toJSON();
  expect(tree).toMatchSnapshot();
});

test("onPress", () => {
  let i = 0; // i guess i could have used sinon here too... less is more i guess
  const onPress = () => i++;
  const wrapperPress = shallow(<RoundedButton onPress={onPress} text="hi" />);

  expect(wrapperPress.prop("onPress")).toBe(onPress); // uses the right handler
  expect(i).toBe(0);
  wrapperPress.simulate("press");
  expect(i).toBe(1);
});


================================================
FILE: boilerplate/App/Components/RoundedButton/index.ts
================================================
import RoundedButton from "./RoundedButton";

export default RoundedButton;


================================================
FILE: boilerplate/App/Components/Stories.tsx
================================================
import "./AlertMessage/AlertMessage.story";
import "./DrawerButton/DrawerButton.story";
import "./FullButton/FullButton.story";
import "./RoundedButton/RoundedButton.story";


================================================
FILE: boilerplate/App/Config/AppConfig.ts
================================================
// Simple React Native specific changes

export default {
  // font scaling override - RN default is on
  allowTextFontScaling: true,
};


================================================
FILE: boilerplate/App/Config/DebugConfig.js
================================================
/**
 * This is a .js file because the Ignite scripts need to find it
 */
export default {
  showDevScreens: __DEV__,
  useFixtures: false,
  ezLogin: false,
  yellowBox: __DEV__,
  reduxLogging: __DEV__,
  includeExamples: __DEV__,
  useReactotron: __DEV__,
};


================================================
FILE: boilerplate/App/Config/README.md
================================================
### Config Folder
All application specific configuration falls in this folder.

`AppConfig.js` - production values.
`DebugConfig.js` - development-wide globals.
`ReactotronConfig.js` - Reactotron client settings.
`ReduxPersist.js` - rehydrate Redux state.


================================================
FILE: boilerplate/App/Config/ReactotronConfig.ts
================================================
import Reactotron from "reactotron-react-native";
import { reactotronRedux as reduxPlugin } from "reactotron-redux";
import sagaPlugin from "reactotron-redux-saga";
import ImmutableObject from "seamless-immutable";
import Config from "../Config/DebugConfig";

if (Config.useReactotron) {
  // https://github.com/infinitered/reactotron for more options!
  Reactotron
    .configure({ name: "Ignite App" })
    .useReactNative()
    .use(reduxPlugin({ onRestore: ImmutableObject }))
    .use(sagaPlugin())
    .connect();

  // Let's clear Reactotron on every time we load the app
  Reactotron.clear();

  // Totally hacky, but this allows you to not both importing reactotron-react-native
  // on every file.  This is just DEV mode, so no big deal.
  (console as any).tron = Reactotron;
}


================================================
FILE: boilerplate/App/Config/ReduxPersist.ts
================================================
import { AsyncStorage } from "react-native";
import immutablePersistenceTransform from "../Services/ImmutablePersistenceTransform";

// More info here:  https://shift.infinite.red/shipping-persistant-reducers-7341691232b1
const REDUX_PERSIST = {
  active: false,
  reducerVersion: "1.0",
  storeConfig: {
    storage: AsyncStorage,
    blacklist: ["login", "search", "nav"], // reducer keys that you do NOT want stored to persistence here
    // whitelist: [], Optionally, just specify the keys you DO want stored to
    // persistence. An empty array means 'don't store any reducers' -> infinitered/ignite#409
    transforms: [immutablePersistenceTransform],
  },
};

export default REDUX_PERSIST;


================================================
FILE: boilerplate/App/Config/index.ts
================================================
import { Text } from "react-native";
import AppConfig from "./AppConfig";
import DebugConfig from "./DebugConfig";

// Allow/disallow font-scaling in app
if (!Text.defaultProps) {
  Text.defaultProps = {};
}
Text.defaultProps.allowFontScaling = AppConfig.allowTextFontScaling;

if (__DEV__) {
  // If ReactNative's yellow box warnings are too much, it is possible to turn
  // it off, but the healthier approach is to fix the warnings.  =)
  console.disableYellowBox = !DebugConfig.yellowBox;
}


================================================
FILE: boilerplate/App/Containers/App.tsx
================================================
import * as React from "react";
import { Provider } from "react-redux";
import Reactotron from "reactotron-react-native";
import "../Config";
import DebugConfig from "../Config/DebugConfig";
import createStore from "../Reducers";
import RootContainer from "./RootContainer";

// create our store
const store = createStore();

/**
 * Provides an entry point into our application.  Both index.ios.js and index.android.js
 * call this component first.
 *
 * We create our Redux store here, put it into a provider and then bring in our
 * RootContainer.
 *
 * We separate like this to play nice with React Native's hot reloading.
 */
class App extends React.Component {
  public render() {
    return (
      <Provider store={store}>
        <RootContainer />
      </Provider>
    );
  }
}

// allow reactotron overlay for fast design in dev mode
export default DebugConfig.useReactotron
? Reactotron.overlay(App)
: App;


================================================
FILE: boilerplate/App/Containers/LaunchScreen/LaunchScreen.tsx
================================================
import React, { Component } from "react";
import { Image, ScrollView, Text, View } from "react-native";
import DevscreensButton from "../../../ignite/DevScreens/DevscreensButton";

import { Images } from "../../Themes";

// Styles
import styles from "./LaunchScreenStyles";

export default class LaunchScreen extends Component {
  public render() {
    return (
      <View style={styles.mainContainer}>
        <Image source={Images.background} style={styles.backgroundImage} resizeMode="stretch" />
        <ScrollView style={styles.container}>
          <View style={styles.centered}>
            <Image source={Images.launch} style={styles.logo} />
          </View>

          <View style={styles.section} >
            <Image source={Images.ready} />
            <Text style={styles.sectionText}>
              This probably isn't what your app is going to look like. Unless your designer
              handed you this screen and, in that case, congrats! You're ready to ship.
              For everyone else, this is where you'll see a live preview of your fully functioning app using Ignite.
            </Text>
          </View>

          <DevscreensButton />
        </ScrollView>
      </View>
    );
  }
}


================================================
FILE: boilerplate/App/Containers/LaunchScreen/LaunchScreenStyles.ts
================================================
import { StyleSheet } from "react-native";
import { ApplicationStyles, Metrics } from "../../Themes/index";

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    paddingBottom: Metrics.baseMargin,
  },
  logo: {
    marginTop: Metrics.doubleSection,
    height: Metrics.images.logo,
    width: Metrics.images.logo,
    resizeMode: "contain",
  },
  centered: {
    alignItems: "center",
  },
});


================================================
FILE: boilerplate/App/Containers/LaunchScreen/index.ts
================================================
import LaunchScreen from "./LaunchScreen";

export default LaunchScreen;


================================================
FILE: boilerplate/App/Containers/README.md
================================================
### Containers Folder
A container is what they call a "Smart Component" in Redux.  It is a component
which knows about Redux.  They are usually used as "Screens".

Also located in here are 2 special containers: `App.js` and `RootContainer.js`.

`App.js` is first component loaded after `index.ios.js` or `index.android.js`.  The purpose of this file is to setup Redux or any other non-visual "global" modules.  Having Redux setup here helps with the hot-reloading process in React Native during development as it won't try to reload your sagas and reducers should your colors change (for example).

`RootContainer.js` is the first visual component in the app.  It is the ancestor of all other screens and components.

You'll probably find you'll have great mileage in Ignite apps without even touching these 2 files.  They, of course, belong to you, so when you're ready to add something non-visual like Firebase or something visual like an overlay, you have spots to place these additions.


================================================
FILE: boilerplate/App/Containers/RootContainer/RootContainer.tsx
================================================
import * as React from "react";
import { StatusBar, View } from "react-native";
import { connect } from "react-redux";
import ReduxPersist from "../../Config/ReduxPersist";
import ReduxNavigation from "../../Navigation/ReduxNavigation";
import { StartupActions } from "../../Reducers/StartupReducers";

// Styles
import styles from "./RootContainerStyles";

interface Props {
  startup: () => void;
}

interface State {

}

export class RootContainer extends React.Component<Props, State> {
  public componentDidMount() {
    // if redux persist is not active fire startup action
    if (!ReduxPersist.active) {
      this.props.startup();
    }
  }

  public render() {
    return (
      <View style={styles.applicationView}>
        <StatusBar barStyle="light-content" />
        <ReduxNavigation />
      </View>
    );
  }
}

const mapDispatchToProps = (dispatch: any): Props => ({
  startup: () => dispatch(StartupActions.startup()),
});

export default connect(null, mapDispatchToProps)(RootContainer);


================================================
FILE: boilerplate/App/Containers/RootContainer/RootContainerStyles.ts
================================================
import {StyleSheet, TextStyle, ViewStyle } from "react-native";
import {Colors, Fonts, Metrics} from "../../Themes/index";

export default StyleSheet.create({
  applicationView: {
    flex: 1,
  },
  container: {
    flex: 1,
    justifyContent: "center",
    backgroundColor: Colors.background,
  },
  welcome: {
    fontSize: 20,
    textAlign: "center",
    fontFamily: Fonts.type.base,
    margin: Metrics.baseMargin,
  },
  myImage: {
    width: 200,
    height: 200,
    alignSelf: "center",
  },
});


================================================
FILE: boilerplate/App/Containers/RootContainer/index.ts
================================================
import RootContainer from "./RootContainer";

export default RootContainer;


================================================
FILE: boilerplate/App/Fixtures/README.md
================================================
### Fixtures folder
All key API responses are housed here.

These API responses can be used for several reasons.  _E.G._:
* To bypass logins when building any screen of the application
* To quickly test API parsing in unit tests
* To separate Network from Data concerns while coding


================================================
FILE: boilerplate/App/Fixtures/gantman.json
================================================
{
  "total_count": 7,
  "incomplete_results": false,
  "items": [
    {
      "login": "GantMan",
      "id": 997157,
      "avatar_url": "https://avatars.githubusercontent.com/u/997157?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/GantMan",
      "html_url": "https://github.com/GantMan",
      "followers_url": "https://api.github.com/users/GantMan/followers",
      "following_url": "https://api.github.com/users/GantMan/following{/other_user}",
      "gists_url": "https://api.github.com/users/GantMan/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/GantMan/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/GantMan/subscriptions",
      "organizations_url": "https://api.github.com/users/GantMan/orgs",
      "repos_url": "https://api.github.com/users/GantMan/repos",
      "events_url": "https://api.github.com/users/GantMan/events{/privacy}",
      "received_events_url": "https://api.github.com/users/GantMan/received_events",
      "type": "User",
      "site_admin": false,
      "score": 122.12115
    },
    {
      "login": "vlad-G",
      "id": 13520880,
      "avatar_url": "https://avatars.githubusercontent.com/u/13520880?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/vlad-G",
      "html_url": "https://github.com/vlad-G",
      "followers_url": "https://api.github.com/users/vlad-G/followers",
      "following_url": "https://api.github.com/users/vlad-G/following{/other_user}",
      "gists_url": "https://api.github.com/users/vlad-G/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/vlad-G/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/vlad-G/subscriptions",
      "organizations_url": "https://api.github.com/users/vlad-G/orgs",
      "repos_url": "https://api.github.com/users/vlad-G/repos",
      "events_url": "https://api.github.com/users/vlad-G/events{/privacy}",
      "received_events_url": "https://api.github.com/users/vlad-G/received_events",
      "type": "User",
      "site_admin": false,
      "score": 12.69848
    },
    {
      "login": "gantmani",
      "id": 3034094,
      "avatar_url": "https://avatars.githubusercontent.com/u/3034094?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/gantmani",
      "html_url": "https://github.com/gantmani",
      "followers_url": "https://api.github.com/users/gantmani/followers",
      "following_url": "https://api.github.com/users/gantmani/following{/other_user}",
      "gists_url": "https://api.github.com/users/gantmani/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/gantmani/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/gantmani/subscriptions",
      "organizations_url": "https://api.github.com/users/gantmani/orgs",
      "repos_url": "https://api.github.com/users/gantmani/repos",
      "events_url": "https://api.github.com/users/gantmani/events{/privacy}",
      "received_events_url": "https://api.github.com/users/gantmani/received_events",
      "type": "User",
      "site_admin": false,
      "score": 11.641713
    },
    {
      "login": "sgantman",
      "id": 5911526,
      "avatar_url": "https://avatars.githubusercontent.com/u/5911526?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/sgantman",
      "html_url": "https://github.com/sgantman",
      "followers_url": "https://api.github.com/users/sgantman/followers",
      "following_url": "https://api.github.com/users/sgantman/following{/other_user}",
      "gists_url": "https://api.github.com/users/sgantman/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/sgantman/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/sgantman/subscriptions",
      "organizations_url": "https://api.github.com/users/sgantman/orgs",
      "repos_url": "https://api.github.com/users/sgantman/repos",
      "events_url": "https://api.github.com/users/sgantman/events{/privacy}",
      "received_events_url": "https://api.github.com/users/sgantman/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.926345
    },
    {
      "login": "michaelgantman",
      "id": 16693070,
      "avatar_url": "https://avatars.githubusercontent.com/u/16693070?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/michaelgantman",
      "html_url": "https://github.com/michaelgantman",
      "followers_url": "https://api.github.com/users/michaelgantman/followers",
      "following_url": "https://api.github.com/users/michaelgantman/following{/other_user}",
      "gists_url": "https://api.github.com/users/michaelgantman/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/michaelgantman/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/michaelgantman/subscriptions",
      "organizations_url": "https://api.github.com/users/michaelgantman/orgs",
      "repos_url": "https://api.github.com/users/michaelgantman/repos",
      "events_url": "https://api.github.com/users/michaelgantman/events{/privacy}",
      "received_events_url": "https://api.github.com/users/michaelgantman/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.926345
    },
    {
      "login": "gantmanis",
      "id": 19141249,
      "avatar_url": "https://avatars.githubusercontent.com/u/19141249?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/gantmanis",
      "html_url": "https://github.com/gantmanis",
      "followers_url": "https://api.github.com/users/gantmanis/followers",
      "following_url": "https://api.github.com/users/gantmanis/following{/other_user}",
      "gists_url": "https://api.github.com/users/gantmanis/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/gantmanis/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/gantmanis/subscriptions",
      "organizations_url": "https://api.github.com/users/gantmanis/orgs",
      "repos_url": "https://api.github.com/users/gantmanis/repos",
      "events_url": "https://api.github.com/users/gantmanis/events{/privacy}",
      "received_events_url": "https://api.github.com/users/gantmanis/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.8813524
    },
    {
      "login": "Gantman2014",
      "id": 7669410,
      "avatar_url": "https://avatars.githubusercontent.com/u/7669410?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/Gantman2014",
      "html_url": "https://github.com/Gantman2014",
      "followers_url": "https://api.github.com/users/Gantman2014/followers",
      "following_url": "https://api.github.com/users/Gantman2014/following{/other_user}",
      "gists_url": "https://api.github.com/users/Gantman2014/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/Gantman2014/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/Gantman2014/subscriptions",
      "organizations_url": "https://api.github.com/users/Gantman2014/orgs",
      "repos_url": "https://api.github.com/users/Gantman2014/repos",
      "events_url": "https://api.github.com/users/Gantman2014/events{/privacy}",
      "received_events_url": "https://api.github.com/users/Gantman2014/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.8813524
    }
  ]
}

================================================
FILE: boilerplate/App/Fixtures/rateLimit.json
================================================
{
  "resources": {
    "core": {
      "limit": 60,
      "remaining": 42,
      "reset": 1488126913
    },
    "search": {
      "limit": 10,
      "remaining": 9,
      "reset": 1488126003
    }
  },
  "rate": {
    "limit": 60,
    "remaining": 42,
    "reset": 1488126913
  }
}

================================================
FILE: boilerplate/App/Fixtures/root.json
================================================
{
  "current_user_url": "https://api.github.com/user",
  "current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
  "authorizations_url": "https://api.github.com/authorizations",
  "code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
  "commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
  "emails_url": "https://api.github.com/user/emails",
  "emojis_url": "https://api.github.com/emojis",
  "events_url": "https://api.github.com/events",
  "feeds_url": "https://api.github.com/feeds",
  "followers_url": "https://api.github.com/user/followers",
  "following_url": "https://api.github.com/user/following{/target}",
  "gists_url": "https://api.github.com/gists{/gist_id}",
  "hub_url": "https://api.github.com/hub",
  "issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
  "issues_url": "https://api.github.com/issues",
  "keys_url": "https://api.github.com/user/keys",
  "notifications_url": "https://api.github.com/notifications",
  "organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
  "organization_url": "https://api.github.com/orgs/{org}",
  "public_gists_url": "https://api.github.com/gists/public",
  "rate_limit_url": "https://api.github.com/rate_limit",
  "repository_url": "https://api.github.com/repos/{owner}/{repo}",
  "repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
  "current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}",
  "starred_url": "https://api.github.com/user/starred{/owner}{/repo}",
  "starred_gists_url": "https://api.github.com/gists/starred",
  "team_url": "https://api.github.com/teams",
  "user_url": "https://api.github.com/users/{user}",
  "user_organizations_url": "https://api.github.com/user/orgs",
  "user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
  "user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
}

================================================
FILE: boilerplate/App/Fixtures/skellock.json
================================================
{
  "total_count": 1,
  "incomplete_results": false,
  "items": [
    {
      "login": "skellock",
      "id": 68273,
      "avatar_url": "https://avatars.githubusercontent.com/u/68273?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/skellock",
      "html_url": "https://github.com/skellock",
      "followers_url": "https://api.github.com/users/skellock/followers",
      "following_url": "https://api.github.com/users/skellock/following{/other_user}",
      "gists_url": "https://api.github.com/users/skellock/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/skellock/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/skellock/subscriptions",
      "organizations_url": "https://api.github.com/users/skellock/orgs",
      "repos_url": "https://api.github.com/users/skellock/repos",
      "events_url": "https://api.github.com/users/skellock/events{/privacy}",
      "received_events_url": "https://api.github.com/users/skellock/received_events",
      "type": "User",
      "site_admin": false,
      "score": 107.22611
    }
  ]
}

================================================
FILE: boilerplate/App/Images/README.md
================================================
### Images folder
Holds all images for the applications.

================================================
FILE: boilerplate/App/Lib/README.md
================================================
# Lib

At first glance, this could appear to be a "miscellaneous" folder, but we recommend that you treat this as proving ground for components that could be reusable outside your project.

Maybe you're writing a set of utilities that you could use outside your project, but they're not quite ready or battle tested.  This folder would be a great place to put them.  They ideally be pure functions have no dependencies on other things in your App folder.


================================================
FILE: boilerplate/App/Lib/ReduxHelpers.ts
================================================
import { ActionCreator, AnyAction, Reducer, ReducersMapObject } from "redux";
import { createAction, EmptyAction, FluxStandardAction, getType, PayloadAction, TypeGetter } from "typesafe-actions";

export type FluxActionCreator<T extends string, AC extends (...args: any[]) => FluxStandardAction<T> = any> = AC & TypeGetter<T>;

type ActionCreatorMap<T> = {
    readonly [P in keyof T]: FluxActionCreator<P>;
};

export type ReducerMap<A, S> = {
    readonly [T in keyof A]: Reducer<S>;
};

export function mapReducers<S, R extends ReducersMapObject>(
    initialState: S,
    reducers: R,
    actionCreators: ActionCreatorMap<R>): Reducer<S> {
    const reducerMap = new Map(Object.entries(actionCreators).map(([key, val]): [string, Reducer<any>] =>
        [getType(val), reducers[key]]));

    return (state: S = initialState, action: AnyAction) => {
        if (!("type" in action)) {
            return state;
        }
        const reducer = reducerMap.get(action.type);
        if (!reducer) {
            return state;
        }
        return reducer(state, action);
    };
}


================================================
FILE: boilerplate/App/Navigation/AppNavigation.tsx
================================================
import { StackNavigator } from "react-navigation";
import LaunchScreen from "../Containers/LaunchScreen";

import styles from "./Styles/NavigationStyles";

// Manifest of possible screens
const PrimaryNav = StackNavigator({
  LaunchScreen: { screen: LaunchScreen },
}, {
  // Default config for all screens
  headerMode: "none",
  initialRouteName: "LaunchScreen",
  navigationOptions: {
    headerStyle: styles.header,
  },
});

export default PrimaryNav;


================================================
FILE: boilerplate/App/Navigation/ReduxNavigation.tsx
================================================
import * as React from "react";
import * as ReactNavigation from "react-navigation";
import { connect } from "react-redux";
import AppNavigation from "./AppNavigation";

// here is our redux-aware smart component
function ReduxNavigation(props) {
  const { dispatch, nav } = props;
  const navigation = ReactNavigation.addNavigationHelpers({
    dispatch,
    state: nav,
  });

  return <AppNavigation navigation={navigation} />;
}

const mapStateToProps = (state) => ({ nav: state.nav });
export default connect(mapStateToProps)(ReduxNavigation);


================================================
FILE: boilerplate/App/Navigation/Styles/NavigationStyles.ts
================================================
import { StyleSheet } from "react-native";
import { Colors } from "../../Themes/index";

export default StyleSheet.create({
  header: {
    backgroundColor: Colors.background,
  },
});


================================================
FILE: boilerplate/App/Reducers/CreateStore.tsx
================================================
import Reactotron from "reactotron-react-native";
import { applyMiddleware, compose, createStore, Reducer } from "redux";
import sagaMiddlewareFactory, { Monitor, SagaIterator } from "redux-saga";
import Config from "../Config/DebugConfig";
import ScreenTracking from "./ScreenTrackingMiddleware";

// creates the store
export default (rootReducer: Reducer<any>, rootSaga: () => SagaIterator) => {
  /* ------------- Redux Configuration ------------- */

  const middleware = [];
  const enhancers = [];

  /* ------------- Analytics Middleware ------------- */
  if (Config.useReactotron) {
    middleware.push(ScreenTracking);
  }
  /* ------------- Saga Middleware ------------- */

  let opts = {};
  if (Config.useReactotron) {
    const sagaMonitor: Monitor = Reactotron.createSagaMonitor();
    opts = { sagaMonitor };
  }
  const sagaMiddleware = sagaMiddlewareFactory(opts);
  middleware.push(sagaMiddleware);

  /* ------------- Assemble Middleware ------------- */

  enhancers.push(applyMiddleware(...middleware));

  // if Reactotron is enabled (default for __DEV__), we'll create the store through Reactotron
  const createAppropriateStore = Config.useReactotron ? Reactotron.createStore : createStore;

  const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

  const store = createAppropriateStore(rootReducer, composeEnhancers(...enhancers));

  // kick off root saga
  const sagasManager = sagaMiddleware.run(rootSaga);

  return {
    store,
    sagasManager,
    sagaMiddleware,
  };
};


================================================
FILE: boilerplate/App/Reducers/GithubReducers/GithubReducersTest.tsx
================================================
/// <reference types="@types/jest" />
import { GithubActions, GithubReducer, INITIAL_STATE } from "./index";

test("request", () => {
  const username = "taco";
  const state = GithubReducer(INITIAL_STATE, GithubActions.userRequest({username}));

  expect(state.fetching).toBe(true);
  expect(state.username).toBe(username);
  expect(state.avatar).toBeNull();
});

test("success", () => {
  const avatar = "http://placekitten.com/200/300";
  const state = GithubReducer(INITIAL_STATE, GithubActions.userSuccess({avatar}));

  expect(state.fetching).toBe(false);
  expect(state.avatar).toBe(avatar);
  expect(state.error).toBeNull();
});

test("failure", () => {
  const state = GithubReducer(INITIAL_STATE, GithubActions.userFailure());

  expect(state.fetching).toBe(false);
  expect(state.error).toBe(true);
  expect(state.avatar).toBeNull();
});


================================================
FILE: boilerplate/App/Reducers/GithubReducers/index.tsx
================================================
import { Action, AnyAction, Reducer } from "redux";
import * as SI from "seamless-immutable";
import { createAction, PayloadAction } from "typesafe-actions";
import { mapReducers, ReducerMap } from "../../Lib/ReduxHelpers";

/* ------------- Types and Action Creators ------------- */
interface RequestParams {username: string; }
interface SuccessParams {avatar: string; }
const actions = {
  userRequest: createAction("githubUserRequest", (params: RequestParams) =>
    ({type: "githubUserRequest", payload: params})),
  userSuccess: createAction("githubUserSuccess", (params: SuccessParams) =>
    ({type: "githubUserSuccess", payload: params})),
  userFailure: createAction("githubUserFailure"),
};

export const GithubActions = actions;

interface GithubState {
  avatar?: string | null;
  fetching?: boolean | null;
  error?: boolean | null;
  username?: string | null;
}

export type GithubAction = PayloadAction<string, GithubState>;

export type ImmutableGithubState = SI.ImmutableObject<GithubState>;

/* ------------- Initial State ------------- */

export const INITIAL_STATE: ImmutableGithubState = SI.from({
  avatar: null,
  fetching: null,
  error: null,
  username: null,
});

/* ------------- Reducers ------------- */

// request the avatar for a user
export const userRequest: Reducer<ImmutableGithubState> =
  (state: ImmutableGithubState, { payload }: AnyAction & {payload?: RequestParams}) =>
    payload ? state.merge({ fetching: true, username: payload.username, avatar: null }) : state;

// successful avatar lookup
export const userSuccess: Reducer<ImmutableGithubState> =
  (state: ImmutableGithubState, { payload }: AnyAction & {payload?: SuccessParams}) =>
  payload ? state.merge({ fetching: false, error: null, avatar: payload.avatar }) : state;

// failed to get the avatar
export const userFailure: Reducer<ImmutableGithubState> = (state: ImmutableGithubState) =>
  state.merge({ fetching: false, error: true, avatar: null });

/* ------------- Hookup Reducers To Types ------------- */

const reducerMap: ReducerMap<typeof actions, ImmutableGithubState> = {
  userRequest,
  userSuccess,
  userFailure,
};

export const GithubReducer = mapReducers(INITIAL_STATE, reducerMap, actions);

export default GithubReducer;


================================================
FILE: boilerplate/App/Reducers/NavigationReducers/index.tsx
================================================
import { NavigationAction, NavigationState } from "react-navigation";
import AppNavigation from "../../Navigation/AppNavigation";

export type NavigationState = NavigationState;

export const NavigationReducer = (state: NavigationState, action: NavigationAction) => {
  const newState = AppNavigation.router.getStateForAction(action, state);
  return newState || state;
};


================================================
FILE: boilerplate/App/Reducers/ScreenTrackingMiddleware.tsx
================================================
import { NavigationActions } from "react-navigation";
import Reactotron from "reactotron-react-native";

// gets the current screen from navigation state
const getCurrentRouteName = (navigationState) => {
  if (!navigationState) {
    return null;
  }
  const route = navigationState.routes[navigationState.index];
  // dive into nested navigators
  if (route.routes) {
    return getCurrentRouteName(route);
  }
  return route.routeName;
};

const screenTracking = ({ getState }) => (next) => (action) => {
  if (
    action.type !== NavigationActions.NAVIGATE &&
    action.type !== NavigationActions.BACK
  ) {
    return next(action);
  }

  const currentScreen = getCurrentRouteName(getState().nav);
  const result = next(action);
  const nextScreen = getCurrentRouteName(getState().nav);
  if (nextScreen !== currentScreen) {
    try {
      Reactotron.log(`NAVIGATING ${currentScreen} to ${nextScreen}`);
      // Example: Analytics.trackEvent('user_navigation', {currentScreen, nextScreen})
    } catch (e) {
      Reactotron.log(e);
    }
  }
  return result;
};

export default screenTracking;


================================================
FILE: boilerplate/App/Reducers/StartupReducers/index.tsx
================================================
import { createAction } from "typesafe-actions";

/* ------------- Types and Action Creators ------------- */

const actions = {
  startup: createAction("startup"),
};

export const StartupActions = actions;


================================================
FILE: boilerplate/App/Reducers/index.ts
================================================
/// <reference types="@types/webpack-env" />
import { combineReducers } from "redux";
import root from "../Sagas";
import configureStore from "./CreateStore";
import { GithubReducer, ImmutableGithubState } from "./GithubReducers";
import { NavigationReducer, NavigationState } from "./NavigationReducers";

/* ------------- Assemble The Reducers ------------- */
export const reducers = combineReducers({
  nav: NavigationReducer,
  github: GithubReducer,
});

export interface State {
  github: ImmutableGithubState;
  nav: NavigationState;
}

export default () => {
  // tslint:disable-next-line:prefer-const
  let { store, sagasManager, sagaMiddleware } = configureStore(reducers, root);

  if (module.hot) {
    module.hot.accept(() => {
      const nextRootReducer = require("./").reducers;
      store.replaceReducer(nextRootReducer);

      const newYieldedSagas = require("../Sagas").default;
      sagasManager.cancel();
      sagasManager.done.then(() => {
        sagasManager = sagaMiddleware.run(newYieldedSagas);
      });
    });
  }

  return store;
};


================================================
FILE: boilerplate/App/Sagas/GithubSagas/GithubSagaTest.ts
================================================
/// <reference types="@types/jest" />
import { path } from "ramda";
import { call, put } from "redux-saga/effects";
import { GithubActions } from "../../Reducers/GithubReducers";
import FixtureAPI from "../../Services/FixtureApi";
import { getUserAvatar } from "./index";

const stepper = (fn) => (mock?) => fn.next(mock).value;

test("first calls API", () => {
  const step = stepper(getUserAvatar(FixtureAPI, GithubActions.userRequest({username: "ascorbic"})));
  // first yield is API
  expect(step()).toEqual(call(FixtureAPI.getUser, "ascorbic"));
});

test("success path", () => {
  FixtureAPI.getUser("ascorbic").then((response) => {

    // tslint:disable-next-line:max-line-length
    const step = stepper(getUserAvatar(FixtureAPI, GithubActions.userRequest({username: "ascorbic"})));
    // first step API
    step();
    // Second step successful return
    const stepResponse = step(response);
    // Get the avatar Url from the response
    const firstUser = path(["data", "items"], response)[0];
    const avatar = firstUser.avatar_url;
    expect(stepResponse).toEqual(put(GithubActions.userSuccess(avatar)));
  });
});

test("failure path", () => {
  const response = {ok: false};
  const step = stepper(getUserAvatar(FixtureAPI, GithubActions.userRequest({username: "ascorbic"})));
  // first step API
  step();
  // Second step failed response
  expect(step(response)).toEqual(put(GithubActions.userFailure()));
});


================================================
FILE: boilerplate/App/Sagas/GithubSagas/index.ts
================================================
import { ApiResponse } from "apisauce";
import { path } from "ramda";
import { SagaIterator } from "redux-saga";
import { call, put } from "redux-saga/effects";
import { GithubAction, GithubActions } from "../../Reducers/GithubReducers";
import { GithubApi, GithubResponse, GithubUser } from "../../Services/GithubApi";

export function * getUserAvatar(api: GithubApi, action: GithubAction): SagaIterator {
  const { payload } = action;
  // make the call to the api
  const response: ApiResponse<GithubResponse> = yield call(api.getUser, payload.username);

  if (response.ok) {
    const firstUser = path<GithubUser[]>(["data", "items"], response)[0];
    const avatar = firstUser.avatar_url;
    // do data conversion here if needed
    yield put(GithubActions.userSuccess({avatar}));
  } else {
    yield put(GithubActions.userFailure());
  }
}


================================================
FILE: boilerplate/App/Sagas/StartupSagas/StartupSagaTest.ts
================================================
/// <reference types="@types/jest" />
import { put, select } from "redux-saga/effects";
import { GithubActions } from "../../Reducers/GithubReducers";
import { selectAvatar, startup } from "./index";

const stepper = (fn) => (mock) => fn.next(mock).value;

test("watches for the right action", () => {
  const step = stepper(startup());
  expect(step()).toEqual(select(selectAvatar));
  expect(step()).toEqual(put(GithubActions.userRequest({username: "ascorbic"})));
});


================================================
FILE: boilerplate/App/Sagas/StartupSagas/index.ts
================================================
import { is } from "ramda";
import Reactotron from "reactotron-react-native";
import { Action } from "redux";
import { SagaIterator } from "redux-saga";
import { put, select } from "redux-saga/effects";
import { GithubAction, GithubActions } from "../../Reducers/GithubReducers";
import { StartupActions } from "../../Reducers/StartupReducers";

// exported to make available for tests
export const selectAvatar = (state: any) => state.github.avatar;

// process STARTUP actions
export function * startup(action?: Action): SagaIterator {
  if (__DEV__) {
    // straight-up string logging
    Reactotron.log("Hello, I'm an example of how to log via Reactotron.");
    Reactotron.log(action);
    // logging an object for better clarity
    Reactotron.log({
      message: "pass objects for better logging",
      someGeneratorFunction: selectAvatar,
    });

    // fully customized!
    const subObject = { a: 1, b: [1, 2, 3], c: true, circularDependency: undefined as any};
    subObject.circularDependency = subObject; // osnap!
    Reactotron.display({
      name: "🔥 IGNITE 🔥",
      preview: "You should totally expand this",
      value: {
        "💃": "Welcome to the future!",
        subObject,
        "someInlineFunction": () => true,
        "someGeneratorFunction": startup,
        "someNormalFunction": selectAvatar,
      },
    });
  }
  const avatar = yield select(selectAvatar);
  // only get if we don't have it yet
  if (!is(String, avatar)) {
    yield put(GithubActions.userRequest({username: "ascorbic"}));
  }

}


================================================
FILE: boilerplate/App/Sagas/index.ts
================================================
import { all, takeLatest } from "redux-saga/effects";
import { getType } from "typesafe-actions";

import DebugConfig from "../Config/DebugConfig";
import FixtureAPI from "../Services/FixtureApi";
import {createAPI, GithubApi} from "../Services/GithubApi";

/* ------------- Types ------------- */

import { GithubActions } from "../Reducers/GithubReducers";
import { StartupActions } from "../Reducers/StartupReducers";

/* ------------- Sagas ------------- */

import { getUserAvatar } from "./GithubSagas";
import { startup } from "./StartupSagas";

/* ------------- API ------------- */

// The API we use is only used from Sagas, so we create it here and pass along
// to the sagas which need it.
const api = DebugConfig.useFixtures ? FixtureAPI : createAPI();

/* ------------- Connect Types To Sagas ------------- */

export default function * root() {
  yield all([
    // some sagas only receive an action
    takeLatest(getType(StartupActions.startup), startup),

    // some sagas receive extra parameters in addition to an action
    takeLatest(getType(GithubActions.userRequest), getUserAvatar, api),
  ]);
}


================================================
FILE: boilerplate/App/Services/Api.ts
================================================
// This is for the devscreens
import {createAPI} from "./GithubApi";

export default {
    create: createAPI,
};


================================================
FILE: boilerplate/App/Services/ExamplesRegistry.tsx
================================================
import R from "ramda";
import * as React from "react";
import { Text, View } from "react-native";
import DebugConfig from "../Config/DebugConfig";
import { ApplicationStyles } from "../Themes";
const globalComponentExamplesRegistry = [];
const globalPluginExamplesRegistry = [];

export const addComponentExample = (title, usage = () => {}) => { if (DebugConfig.includeExamples) globalComponentExamplesRegistry.push({title, usage}); }; // eslint-disable-line

export const addPluginExample = (title, usage = () => {}) => { if (DebugConfig.includeExamples) globalPluginExamplesRegistry.push({title, usage}); }; // eslint-disable-line

const renderComponentExample = (example) => {
  return (
    <View key={example.title}>
      <View style={ApplicationStyles.darkLabelContainer}>
        <Text style={ApplicationStyles.darkLabel}>{example.title}</Text>
      </View>
      {example.usage.call()}
    </View>
  );
};

const renderPluginExample = (example) => {
  return (
    <View key={example.title}>
      <View style={ApplicationStyles.darkLabelContainer}>
        <Text style={ApplicationStyles.darkLabel}>{example.title}</Text>
      </View>
      {example.usage.call()}
    </View>
  );
};

export const renderComponentExamples = () => R.map(renderComponentExample, globalComponentExamplesRegistry);

export const renderPluginExamples = () => R.map(renderPluginExample, globalPluginExamplesRegistry);

// Default for readability
export default {
  renderComponentExamples,
  addComponentExample,
  renderPluginExamples,
  addPluginExample,
};


================================================
FILE: boilerplate/App/Services/FixtureAPITest.tsx
================================================
/// <reference types="@types/jest" />
import * as R from "ramda";
import FixtureAPI from "../../App/Services/FixtureApi";
import API from "../../App/Services/GithubApi";

test("All fixtures map to actual API", () => {
  const fixtureKeys = R.keys(FixtureAPI).sort();
  const apiKeys = R.keys(API.createAPI());

  const intersection = R.intersection(fixtureKeys, apiKeys).sort();

  // There is no difference between the intersection and all fixtures
  expect(R.equals(fixtureKeys, intersection)).toBe(true);
});

test("FixtureAPI getRate returns the right file", () => {
  const expectedFile = require("../../App/Fixtures/rateLimit.json");

  return FixtureAPI.getRate().then((data) => expect(data).toEqual({
    ok: true,
    data: expectedFile,
  }));
});

test("FixtureAPI getUser returns the right file for gantman", () => {
  const expectedFile = require("../../App/Fixtures/gantman.json");
  return FixtureAPI.getUser("GantMan").then((data) => expect(data).toEqual({
    ok: true,
    data: expectedFile,
  }));
});

test("FixtureAPI getUser returns the right file for skellock as default", () => {
  const expectedFile = require("../../App/Fixtures/skellock.json");
  return FixtureAPI.getUser("Whatever").then((data) => expect(data).toEqual({
    ok: true,
    data: expectedFile,
  }));
});


================================================
FILE: boilerplate/App/Services/FixtureApi.tsx
================================================
import {ApiResponse} from "apisauce";
import {GithubApi, GithubResponse} from "./GithubApi";

export default {
  // Functions return fixtures
  getRoot: (): Promise<ApiResponse<GithubResponse>> => {
    return Promise.resolve({
      ok: true,
      data: require("../Fixtures/root.json"),
    } as ApiResponse<any>);
  },
  getRate: (): Promise<ApiResponse<GithubResponse>> => {
    return Promise.resolve({
      ok: true,
      data: require("../Fixtures/rateLimit.json"),
    } as ApiResponse<any>);
  },
  getUser: (username: string): Promise<ApiResponse<GithubResponse>> => {
    // This fixture only supports gantman or else returns skellock
    const gantmanData = require("../Fixtures/gantman.json");
    const skellockData = require("../Fixtures/skellock.json");
    return Promise.resolve({
      ok: true,
      data: username.toLowerCase() === "gantman" ? gantmanData : skellockData,
    } as ApiResponse<GithubResponse>);
  },
} as GithubApi;


================================================
FILE: boilerplate/App/Services/GithubApi.tsx
================================================
// a library to wrap and simplify api calls
import {ApiResponse, create as apicreate} from "apisauce";

export interface GithubApi {
  getRoot: () => Promise<ApiResponse<{}>>;
  getRate: () => Promise<ApiResponse<{}>>;
  getUser: (username: string) => Promise<ApiResponse<GithubResponse>>;
}

export interface GithubUser {
  login: string;
  id: number;
  avatar_url: string;
  gravatar_id: string;
  url: string;
  html_url: string;
  followers_url: string;
  following_url: string;
  gists_url: string;
  starred_url: string;
  subscriptions_url: string;
  organizations_url: string;
  repos_url: string;
  events_url: string;
  received_events_url: string;
  type: "User";
  site_admin: boolean;
  score: number;
}

export interface GithubResponse {
  total_count: number;
  incomplete_results: false;
  items: GithubUser[];
}

// our "constructor"
export const createAPI = (baseURL = "https://api.github.com/"): GithubApi => {
  // ------
  // STEP 1
  // ------
  //
  // Create and configure an apisauce-based api object.
  //
  const api = apicreate({
    // base URL is read from the "constructor"
    baseURL,
    // here are some default headers
    headers: {
      "Cache-Control": "no-cache",
    },
    // 10 second timeout...
    timeout: 10000,
  });

  // ------
  // STEP 2
  // ------
  //
  // Define some functions that call the api.  The goal is to provide
  // a thin wrapper of the api layer providing nicer feeling functions
  // rather than "get", "post" and friends.
  //
  // I generally don't like wrapping the output at this level because
  // sometimes specific actions need to be take on `403` or `401`, etc.
  //
  // Since we can't hide from that, we embrace it by getting out of the
  // way at this level.
  //
  const getRoot = () => api.get<GithubResponse>("");
  const getRate = () => api.get<GithubResponse>("rate_limit");
  const getUser = (username: string) => api.get<GithubResponse>("search/users", {q: username});

  // ------
  // STEP 3
  // ------
  //
  // Return back a collection of functions that we would consider our
  // interface.  Most of the time it'll be just the list of all the
  // methods in step 2.
  //
  // Notice we're not returning back the `api` created in step 1?  That's
  // because it is scoped privately.  This is one way to create truly
  // private scoped goodies in JavaScript.
  //
  return {
    // a list of the API functions from step 2
    getRoot,
    getRate,
    getUser,
  };
};

// let's return back our create method as the default.
export default {
  createAPI,
};


================================================
FILE: boilerplate/App/Services/ImmutablePersistenceTransform.tsx
================================================
import R from "ramda";
import * as SeamlessImmutable from "seamless-immutable";

// is this object already Immutable?
const isImmutable = R.has("asMutable");

// change this Immutable object into a JS object
const convertToJs = (state: any) => state.asMutable({deep: true});

// optionally convert this object into a JS object if it is Immutable
const fromImmutable = R.when(isImmutable, convertToJs);

// convert this JS object into an Immutable object
const toImmutable = (raw: any) => SeamlessImmutable(raw);

// the transform interface that redux-persist is expecting
export default {
  out: (state: any) => {
    // console.log({ retrieving: state })
    return toImmutable(state);
  },
  in: (raw: any) => {
    // console.log({ storing: raw })
    return fromImmutable(raw);
  },
};


================================================
FILE: boilerplate/App/Services/RehydrationServices.tsx
================================================
import { AsyncStorage } from "react-native";
import { persistStore } from "redux-persist";
import DebugConfig from "../Config/DebugConfig";
import ReduxPersist from "../Config/ReduxPersist";
import StartupActions from "../Redux/StartupRedux";

const updateReducers = (store) => {
  const reducerVersion = ReduxPersist.reducerVersion;
  const config = ReduxPersist.storeConfig;
  const startup = () => store.dispatch(StartupActions.startup());

  // Check to ensure latest reducer version
  AsyncStorage.getItem("reducerVersion").then((localVersion) => {
    if (localVersion !== reducerVersion) {
      if (DebugConfig.useReactotron) {
        console.tron.display({
          name: "PURGE",
          value: {
            "Old Version:": localVersion,
            "New Version:": reducerVersion,
          },
          preview: "Reducer Version Change Detected",
          important: true,
        });
      }
      // Purge store
      persistStore(store, config, startup).purge();
      AsyncStorage.setItem("reducerVersion", reducerVersion);
    } else {
      persistStore(store, config, startup);
    }
  }).catch(() => {
    persistStore(store, config, startup);
    AsyncStorage.setItem("reducerVersion", reducerVersion);
  });
};

export default {updateReducers};


================================================
FILE: boilerplate/App/Themes/ApplicationStyles.ts
================================================
import { TextStyle, ViewStyle } from "react-native";

import Colors from "./Colors";
import Fonts from "./Fonts";
import Metrics from "./Metrics";

// This file is for a reusable grouping of Theme items.
// Similar to an XML fragment layout in Android

const ApplicationStyles = {
  screen: {
    mainContainer: {
      flex: 1,
      backgroundColor: Colors.transparent,
    } as ViewStyle,
    backgroundImage: {
      position: "absolute",
      top: 0,
      left: 0,
      bottom: 0,
      right: 0,
    } as ViewStyle,
    container: {
      flex: 1,
      paddingTop: Metrics.baseMargin,
      backgroundColor: Colors.transparent,
    } as ViewStyle,
    section: {
      margin: Metrics.section,
      padding: Metrics.baseMargin,
    } as ViewStyle,
    sectionText: {
      ...Fonts.style.normal,
      paddingVertical: Metrics.doubleBaseMargin,
      color: Colors.snow,
      marginVertical: Metrics.smallMargin,
      textAlign: "center",
    } as TextStyle,
    subtitle: {
      color: Colors.snow,
      padding: Metrics.smallMargin,
      marginBottom: Metrics.smallMargin,
      marginHorizontal: Metrics.smallMargin,
    } as TextStyle,
    titleText: {
      ...Fonts.style.h2,
      fontSize: 14,
      color: Colors.text,
    } as TextStyle,
  },
  darkLabelContainer: {
    padding: Metrics.smallMargin,
    paddingBottom: Metrics.doubleBaseMargin,
    borderBottomColor: Colors.border,
    borderBottomWidth: 1,
    marginBottom: Metrics.baseMargin,
  } as ViewStyle,
  darkLabel: {
    fontFamily: Fonts.type.bold,
    color: Colors.snow,
  } as TextStyle,
  groupContainer: {
    margin: Metrics.smallMargin,
    flexDirection: "row",
    justifyContent: "space-around",
    alignItems: "center",
  } as ViewStyle,
  sectionTitle: {
    ...Fonts.style.h4,
    color: Colors.coal,
    backgroundColor: Colors.ricePaper,
    padding: Metrics.smallMargin,
    marginTop: Metrics.smallMargin,
    marginHorizontal: Metrics.baseMargin,
    borderWidth: 1,
    borderColor: Colors.ember,
    alignItems: "center",
    textAlign: "center",
  } as TextStyle,
};

export default ApplicationStyles;


================================================
FILE: boilerplate/App/Themes/Colors.ts
================================================
const colors = {
  background: "#1F0808",
  clear: "rgba(0,0,0,0)",
  facebook: "#3b5998",
  transparent: "rgba(0,0,0,0)",
  silver: "#F7F7F7",
  steel: "#CCCCCC",
  error: "rgba(200, 0, 0, 0.8)",
  ricePaper: "rgba(255,255,255, 0.75)",
  frost: "#D8D8D8",
  cloud: "rgba(200,200,200, 0.35)",
  windowTint: "rgba(0, 0, 0, 0.4)",
  panther: "#161616",
  charcoal: "#595959",
  coal: "#2d2d2d",
  bloodOrange: "#fb5f26",
  snow: "white",
  ember: "rgba(164, 0, 48, 0.5)",
  fire: "#e73536",
  drawer: "rgba(30, 30, 29, 0.95)",
  eggplant: "#251a34",
  border: "#483F53",
  banner: "#5F3E63",
  text: "#E0D7E5",
};

export default colors;


================================================
FILE: boilerplate/App/Themes/Fonts.ts
================================================
const type = {
  base: "Avenir-Book",
  bold: "Avenir-Black",
  emphasis: "HelveticaNeue-Italic",
};

const size = {
  h1: 38,
  h2: 34,
  h3: 30,
  h4: 26,
  h5: 20,
  h6: 19,
  input: 18,
  regular: 17,
  medium: 14,
  small: 12,
  tiny: 8.5,
};

const style = {
  h1: {
    fontFamily: type.base,
    fontSize: size.h1,
  },
  h2: {
    fontWeight: "bold",
    fontSize: size.h2,
  },
  h3: {
    fontFamily: type.emphasis,
    fontSize: size.h3,
  },
  h4: {
    fontFamily: type.base,
    fontSize: size.h4,
  },
  h5: {
    fontFamily: type.base,
    fontSize: size.h5,
  },
  h6: {
    fontFamily: type.emphasis,
    fontSize: size.h6,
  },
  normal: {
    fontFamily: type.base,
    fontSize: size.regular,
  },
  description: {
    fontFamily: type.base,
    fontSize: size.medium,
  },
};

export default {
  type,
  size,
  style,
};


================================================
FILE: boilerplate/App/Themes/Images.ts
================================================
// leave off @2x/@3x
const images = {
  logo: require("../Images/ir.png"),
  clearLogo: require("../Images/top_logo.png"),
  launch: require("../Images/launch-icon.png"),
  ready: require("../Images/your-app.png"),
  ignite: require("../Images/ignite_logo.png"),
  igniteClear: require("../Images/ignite-logo-transparent.png"),
  tileBg: require("../Images/tile_bg.png"),
  background: require("../Images/BG.png"),
  buttonBackground: require("../Images/button-bg.png"),
  api: require("../Images/Icons/icon-api-testing.png"),
  components: require("../Images/Icons/icon-components.png"),
  deviceInfo: require("../Images/Icons/icon-device-information.png"),
  faq: require("../Images/Icons/faq-icon.png"),
  home: require("../Images/Icons/icon-home.png"),
  theme: require("../Images/Icons/icon-theme.png"),
  usageExamples: require("../Images/Icons/icon-usage-examples.png"),
  chevronRight: require("../Images/Icons/chevron-right.png"),
  hamburger: require("../Images/Icons/hamburger.png"),
  backButton: require("../Images/Icons/back-button.png"),
  closeButton: require("../Images/Icons/close-button.png"),
};

export default images;


================================================
FILE: boilerplate/App/Themes/Metrics.ts
================================================
import {Dimensions, Platform} from "react-native";

const { width, height } = Dimensions.get("window");

// Used via Metrics.baseMargin
const metrics = {
  marginHorizontal: 10,
  marginVertical: 10,
  section: 25,
  baseMargin: 10,
  doubleBaseMargin: 20,
  smallMargin: 5,
  doubleSection: 50,
  horizontalLineHeight: 1,
  screenWidth: width < height ? width : height,
  screenHeight: width < height ? height : width,
  navBarHeight: (Platform.OS === "ios") ? 64 : 54,
  buttonRadius: 4,
  icons: {
    tiny: 15,
    small: 20,
    medium: 30,
    large: 45,
    xl: 50,
  },
  images: {
    small: 20,
    medium: 40,
    large: 60,
    logo: 200,
  },
};

export default metrics;


================================================
FILE: boilerplate/App/Themes/README.md
================================================
### Themes Folder
Application specific themes
* Base Styles
* Fonts
* Metrics
* Colors

etc.


================================================
FILE: boilerplate/App/Themes/index.ts
================================================
import ApplicationStyles from "./ApplicationStyles";
import Colors from "./Colors";
import Fonts from "./Fonts";
import Images from "./Images";
import Metrics from "./Metrics";

export { Colors, Fonts, Images, Metrics, ApplicationStyles };


================================================
FILE: boilerplate/App/Transforms/ConvertFromKelvin.ts
================================================
export default (kelvin: number) => {
  const celsius = kelvin - 273.15;
  const fahrenheit = (celsius * 1.8000) + 32;

  return Math.round(fahrenheit);
};


================================================
FILE: boilerplate/App/Transforms/README.md
================================================
# Transforms

A common pattern when working with APIs is to change data to play nice between your app & the API.  

We've found this to be the case in every project we've worked on.  So much so that we're recommending that you create a folder dedicated to these transformations.

Transforms are not necessarily a bad thing (although an API might have you transforming more than you'd like).

For example, you may:

* turn appropriate strings to date objects
* convert snake case to camel case
* normalize or denormalize things
* create lookup tables


================================================
FILE: boilerplate/README.md
================================================
#  <%= props.name %>

* TypeScript React Native App Utilizing [Ignite](https://github.com/infinitered/ignite)

## :arrow_up: How to Setup

**Step 1:** git clone this repo:

**Step 2:** cd to the cloned repo:

**Step 3:** Install the Application with `yarn` or `npm i`


## :arrow_forward: How to Run App

1. cd to the repo
2. Run `npm run compile`
3. Run Build for either OS
  * for iOS
    * run `react-native run-ios`
  * for Android
    * Run Genymotion
    * run `react-native run-android`

**To Lint on Commit**

This is implemented using [husky](https://github.com/typicode/husky). There is no additional setup needed.

## :closed_lock_with_key: Secrets

This project uses [react-native-config](https://github.com/luggit/react-native-config) to expose config variables to your javascript code in React Native. You can store API keys
and other sensitive information in a `.env` file:

```
API_URL=https://myapi.com
GOOGLE_MAPS_API_KEY=abcdefgh
```

and access them from React Native like so:

```
import Secrets from 'react-native-config'

Secrets.API_URL  // 'https://myapi.com'
Secrets.GOOGLE_MAPS_API_KEY  // 'abcdefgh'
```

The `.env` file is ignored by git keeping those secrets out of your repo.

### Get started:
1. Copy .env.example to .env
2. Add your config variables
3. Follow instructions at [https://github.com/luggit/react-native-config#setup](https://github.com/luggit/react-native-config#setup)
4. Done!


================================================
FILE: boilerplate/Tests/Setup.tsx.ejs
================================================
/// <reference types="@types/jest" />
// Mock your external modules here if needed
<%_ if (props.i18n === 'react-native-i18n') { _%>
jest
.mock('react-native-i18n', () => {
  const english = require('../App/I18n/languages/english.json')
  const keys = require('ramda')
  const replace = require('ramda')
  const forEach = require('ramda')

  return {
    t: (key, replacements) => {
      let value = english[key]
      if (!value) return key
      if (!replacements) return value

      forEach((r) => {
        value = replace(`{{${r}}}`, replacements[r], value)
      }, keys(replacements))
      return value
    }
  }
})
<%_ } else { _%>
// jest
// .mock('react-native-device-info', () => {
//   return { isTablet: jest.fn(() => { return false }) }
// })
<%_ } _%>


================================================
FILE: boilerplate/Tests/StoriesTest.ts
================================================
import initStoryshots from "@storybook/addon-storyshots";

initStoryshots();


================================================
FILE: boilerplate/ignite.json.ejs
================================================
{
  "createdWith": "<%= props.igniteVersion %>",
  "examples": "classic",
  "navigation": "react-navigation",
  "askToOverride": true
}


================================================
FILE: boilerplate/index.js.ejs
================================================
import "./App/Config/ReactotronConfig"
import { AppRegistry } from 'react-native';
import App from "./App/Containers/App";

AppRegistry.registerComponent("<%= props.name %>", () => App);


================================================
FILE: boilerplate/package.json.ejs
================================================
{
  "version": "0.0.1",
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest",
    "clean": "rm -rf $TMPDIR/react-* && watchman watch-del-all && npm cache clean --force",
    "clean:android": "cd android/ && ./gradlew clean && cd .. && react-native run-android",
    "newclear": "rm -rf $TMPDIR/react-* && watchman watch-del-all && rm -rf ios/build && rm -rf node_modules/ && npm cache clean --force && npm i",
    "test:watch": "jest --watch",
    "updateSnapshot": "jest --updateSnapshot",
    "coverage": "jest --coverage && open coverage/lcov-report/index.html || xdg-open coverage/lcov-report/index.html",
    "android:build": "cd android && ./gradlew assembleRelease",
    "android:install": "cd android && ./gradlew assembleRelease && ./gradlew installRelease",
    "android:hockeyapp": "cd android && ./gradlew assembleRelease && puck -submit=auto app/build/outputs/apk/app-release.apk",
    "android:devices": "$ANDROID_HOME/platform-tools/adb devices",
    "android:logcat": "$ANDROID_HOME/platform-tools/adb logcat *:S ReactNative:V ReactNativeJS:V",
    "android:shake": "$ANDROID_HOME/platform-tools/adb devices | grep '\\t' | awk '{print $1}' | sed 's/\\s//g' | xargs -I {} $ANDROID_HOME/platform-tools/adb -s {} shell input keyevent 82",
    "storybook": "storybook start -p 7007",
    "lint": "tslint --project . -e '**/*.js' -t verbose | snazzy",
    "lintdiff": "git diff --name-only --cached --relative | grep '\\.tsx?$' | xargs tslint | snazzy",
    "fixcode": "tslint --project . -e '**/*.js' --fix",
    "git-hook": "npm run lint -s && npm run test -s"
  },
  "dependencies": {
    "apisauce": "^0.14.0",
    "format-json": "^1.0.3",
    "lodash": "^4.17.2",
    "querystringify": "0.0.4",
    "ramda": "^0.24.1",
    "react-native-config": "^0.6.0",
    "react-native-drawer": "^2.3.0",
    "react-navigation": "^1.0.0-beta.7",
    "react-redux": "^5.0.2",
    "react-redux-typescript": "^2.3.0",
    "redux": "^3.6.0",
    "redux-persist": "^4.1.0",
    "redux-saga": "^0.15.6",
    "seamless-immutable": "^7.0.1",
    "typesafe-actions": "1.1.2"    
  },
  "devDependencies": {
    "@storybook/addon-storyshots": "^3.2.3",
    "@storybook/react-native": "^3.2.3",
    "@types/enzyme": "^3.1.0",
    "@types/ramda": "^0.24.17",
    "@types/react-navigation": "^1.0.21",
    "@types/react-redux": "^5.0.10",
    "@types/redux": "^3.6.0",
    "@types/jest": "^21.1.4",
    "@types/react": "^16.0.13",
    "@types/react-native": "^0.49.2",
    "@types/react-test-renderer": "^16.0.0",
    "@types/seamless-immutable": "^7.1.1",
    "@types/storybook__react": "^3.0.5",
    "@types/webpack-env": "^1.13.2",
    "babel-jest": "21.2.0",
    "babel-plugin-ignite-ignore-reactotron": "^0.3.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react-native": "3.0.2",
    "enzyme": "^2.6.0",
    "jest-preset-typescript-react-native": "^1.2.0",
    "husky": "^0.13.1",
    "ignite-animatable": "^1.0.0",
    "ignite-dev-screens": "^2.2.0",
    "ignite-vector-icons": "^1.1.0",
    "jest": "^21.2.1",
    "mockery": "^2.0.0",
    "react-addons-test-utils": "~15.4.1",
    "react-dom": "16.0.0-alpha.12",
    "react-test-renderer": "16.0.0-beta.5",
    "reactotron-react-native": "^1.12.0",
    "reactotron-redux": "^1.11.1",
    "reactotron-redux-saga": "^1.11.1",
    "snazzy": "^7.0.0",
    "ts-jest": "^21.1.3",
    "tslint": "^5.7.0",
    "tslint-react": "^3.2.0",
    "typescript": "^2.5.3",
    "react-native-typescript-transformer": "^1.1.4"
  },
  "jest": {
    "preset": "jest-preset-typescript-react-native",
    "testMatch": [
      "**/Tests/**/*.ts?(x)",
      "**/App/**/*Test.ts?(x)"
    ],
    "testPathIgnorePatterns": [
      "\\.snap$",
      "<rootDir>/node_modules/",
      "<rootDir>/lib/",
      "Tests/Setup"
    ],
    "setupFiles": [
      "./Tests/Setup.tsx"
    ],
    "moduleFileExtensions": [
      "js",
      "jsx",
      "ts",
      "tsx",
      "json"
    ],
    "cacheDirectory": ".jest/cache"
  },
  "config": {}
}


================================================
FILE: boilerplate/rn-cli.config.js
================================================
module.exports = {  
    getTransformModulePath() {
      return require.resolve('react-native-typescript-transformer')
    },
    getSourceExts() {
      return ['ts', 'tsx'];
    }
  }

================================================
FILE: boilerplate/storybook/addons.js
================================================
import '@storybook/addon-actions/register'
import '@storybook/addon-links/register'


================================================
FILE: boilerplate/storybook/index.js
================================================
import StorybookUI from './storybook'

export default StorybookUI


================================================
FILE: boilerplate/storybook/storybook.ejs
================================================
import { AppRegistry } from 'react-native'
import { getStorybookUI, configure } from '@storybook/react-native'

// import stories
configure(() => {
  require('../App/Components/Stories')
}, module)

// This assumes that storybook is running on the same host as your RN packager,
// to set manually use, e.g. host: 'localhost' option
const StorybookUI = getStorybookUI({ port: 7007, onDeviceUI: true })
AppRegistry.registerComponent('<%= props.name %>', () => StorybookUI)
export default StorybookUI


================================================
FILE: boilerplate/tsconfig.json
================================================
{
  "compilerOptions": {
    /* Basic Options */
    "target": "ESNEXT",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
    "module": "ESNext",                     /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    // "lib": [],                             /* Specify library files to be included in the compilation:  */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    "jsx": "react-native",                    /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    "sourceMap": true,                        /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    //"outDir": "./dist",                        /* Redirect output structure to the directory. */
    // "rootDir": "./src",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "removeComments": true,                /* Do not emit comments to output. */
    // "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                            /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "baseUrl": "types",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    "typeRoots": ["types"],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    "allowSyntheticDefaultImports": true  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */

    /* Source Map Options */
    // "sourceRoot": "./",                    /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "./",                       /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
  }
  // "include": [
  //   "index.ts", "App/Components/Stories.tsx"
  // ]
}

================================================
FILE: boilerplate/tslint.json
================================================
{
    "extends": [
        "tslint:latest", "tslint-react"
    ],
    "rules": {
        "interface-name": false,
        "object-literal-sort-keys": false,
        "no-object-literal-type-assertion": false,
        "no-empty-interface": false,
        "no-submodule-imports": false
    }
}

================================================
FILE: boilerplate/types/@storybook/react-native.d.ts
================================================
// Type definitions for @storybook/react 3.0
// Project: https://github.com/storybooks/storybook
// Definitions by: Joscha Feth <https://github.com/joscha>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3

/// <reference types="webpack-env" />

import * as React from 'react';

export type Renderable = React.ComponentType | JSX.Element;
export type RenderFunction = () => Renderable;

export type StoryDecorator = (story: RenderFunction, context: { kind: string, story: string }) => Renderable | null;

export interface Story {
    readonly kind: string;
    add(storyName: string, callback: RenderFunction): this;
    addDecorator(decorator: StoryDecorator): this;
}

export function addDecorator(decorator: StoryDecorator): void;
export function configure(fn: () => void, module?: NodeModule): void;
export function setAddon(addon: object): void;
export function storiesOf(name: string, module?: NodeModule): Story;
export function storiesOf<T>(name: string, module?: NodeModule): Story & T;

export interface StoryObject {
    name: string;
    render: RenderFunction;
}

export interface StoryBucket {
    kind: string;
    stories: StoryObject[];
}

export function getStorybook(): StoryBucket[];


================================================
FILE: boilerplate/types/reduxsauce/index.d.ts
================================================
import { Action, AnyAction, Reducer, ReducersMapObject } from "redux";
export interface ActionTypes {
    [key: string]: string;
}

export interface ActionConfig {
    [key: string]: string[] | ActionCreator | {[key: string]: any} | null;
}

export type ActionCreator = (...args: any[]) => Action;

export interface ActionCreators {
    [key: string]: ActionCreator;
}

export function createActions(config: ActionConfig, options?: {prefix?: string}): {Types: ActionTypes, Creators: ActionCreators };

/**
 * Creates a reducer.
 * @param {object} initialState - The initial state for this reducer.
 * @param {object} handlers - Keys are action types (strings), values are reducers (functions).
 * @return {Reducer<S>} A reducer object.
 */
export function createReducer<S>(initialState: S, handlers: ReducersMapObject): Reducer<S>;

export function createTypes(types: string, options?: {prefix?: string, [key: string]: any}): ActionTypes;

/**
 * Allows your reducers to be reset.
 *
 * @param {string} typeToReset - The action type to listen for.
 * @param {Reducer<T>} originalReducer - The reducer to wrap.
 */
export function resettableReducer<S>(type: string, originalReducer: Reducer<S>): Reducer<S>;

export function resettableReducer<S>(type: string): (originalReducer: Reducer<S>) => Reducer<S>;


================================================
FILE: boilerplate.js
================================================
const options = require('./options')
const { merge, pipe, assoc, omit, __ } = require('ramda')
const { getReactNativeVersion } = require('./lib/react-native-version')

/**
 * Is Android installed?
 *
 * $ANDROID_HOME/tools folder has to exist.
 *
 * @param {*} context - The gluegun context.
 * @returns {boolean}
 */
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'

  return Boolean(hasAndroid)
}

/**
 * Let's install.
 *
 * @param {any} context - The gluegun context.
 */
async function install (context) {
  const {
    filesystem,
    parameters,
    ignite,
    reactNative,
    print,
    system,
    prompt,
    template
  } = context
  const { colors } = print
  const { red, yellow, bold, gray, blue, green } = colors

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

  const name = parameters.third
  const logo = red(`


                -aeaeaeaeaeae—             
            -eaeaeaeaeaeaeaeaeaeae-            
         /aeaeaeaeaeaeaeaeaeaeaeaeae\\         
       /aeaeaeaeaeaeaeaeaeaeaeaeaeaeae\\       
     /eaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaea\\       
    /aeaeaeaeaeaeaeaeaeaea/ |aeaeaeaeaeae\\      
   /aaeaeaeaeaeaeaeaeaeae/  |aeaeaeaeaeaea\\     
   aeaeaeaeaeaeaeaeaeae/    |eaeaeaeaeaeaea    
  |aeaeaeaeaeaeaeaeae/      |eaeaeaeaeaeaea|    
  aeaeaeaeaeaeaeaea/        |eaeaeaeaeaeaeae    
  eaeaeaeaeaeaeae/`) + yellow(`:`) + red(`\\        |aeaeaeaeaeaeaea    
  aeaeaeaeaeaea/`) + yellow(`::::`) + red(`\\       |eaeaeaeaeaeaeae    
  |aeaeaeaeae/`) + yellow(`:::::::`) + red(`\\      |eaeaeaeaeaeaea|    
   aeaeaeaeaeaeaeaeaea\\     |`) + yellow(`::::`) + red(`/aeaeaeaea   
   \\eaeaeaeaeaeaeaeaeaea\\   |`) + yellow(`:::`) + red(`/aeaeaeaea/     
    \\aeaeaeaeaeaeaeaeaeae\\  |`) + yellow(`::`) + red(`/aeaeaeaea/      
     \\aeaeaeaeaeaeaeaeaeae\\ |`) + yellow(`:`) + red(`/eaeeaeaea/       
       \\aeaeaeaeaeaeaeaeaea\\|/aeaeaeae/       
         \\aeaeaeaeaeaeaeaeaeaeaeaeae/         
            -eaeaeaeaeaeaeaeaeaeae-            
                -aeaeaeaeaeae—      

           __ _  ___ _ __ _  __ _ _ __  
          / _' |/ _ \\ '__| |/ _' | '_ \\ 
         | (_| |  __/ |  | | (_| | | | |
          \\__,_|\\___|_|  |_|\\__,_|_| |_|

`) + green(`
    🌳   Crafted with care in the Cotswolds.  🌳`) + yellow(`

                https://aerian.com/    

`);
  
  print.info(logo)
  const spinner = print
    .spin(`using the TypeScript boilerplate from Aerian Studios. You might want to make a cuppa while we get this ready. ☕️`)
    .succeed()

  // attempt to install React Native or die trying
  const rnInstall = await reactNative.install({
    name,
    version: getReactNativeVersion(context)
  })
  if (rnInstall.exitCode > 0) process.exit(rnInstall.exitCode)

  // remove the __tests__ directory and App.js that come with React Native
  filesystem.remove('__tests__')
  filesystem.remove('App.js')
  // copy our App, Tests & storybook directories
  spinner.text = '▸ copying files'
  spinner.start()
  filesystem.copy(`${ignite.ignitePluginPath()}/boilerplate/App`, `${process.cwd()}/App`, {
    overwrite: true,
    matching: '!*.ejs'
  })
  filesystem.copy(`${ignite.ignitePluginPath()}/boilerplate/Tests`, `${process.cwd()}/Tests`, {
    overwrite: true,
    matching: '!*.ejs'
  })
  filesystem.copy(`${ignite.ignitePluginPath()}/boilerplate/storybook`, `${process.cwd()}/storybook`, {
    overwrite: true,
    matching: '!*.ejs'
  })
  filesystem.copy(`${ignite.ignitePluginPath()}/boilerplate/types`, `${process.cwd()}/types`, {
    overwrite: true,
    matching: '!*.ejs'
  })
  spinner.stop()

  // --max, --min, interactive
  let answers
  if (parameters.options.max) {
    answers = options.answers.max
  } else if (parameters.options.min) {
    answers = options.answers.min
  } else {
    answers = await prompt.ask(options.questions)
  }

  // generate some templates
  spinner.text = '▸ generating files'
  const templates = [
    { template: 'index.js.ejs', target: 'index.js' },
    { template: 'README.md', target: 'README.md' },
    { template: 'ignite.json.ejs', target: 'ignite/ignite.json' },
    { template: '.editorconfig', target: '.editorconfig' },
    { template: '.babelrc', target: '.babelrc' },
    { template: 'tsconfig.json', target: 'tsconfig.json' },
    { template: 'tslint.json', target: 'tslint.json' },
    { template: 'rn-cli.config.js', target: 'rn-cli.config.js' },
    { template: 'Tests/Setup.tsx.ejs', target: 'Tests/Setup.tsx' },
    { template: 'storybook/storybook.ejs', target: 'storybook/storybook.js' },
    { template: '.env.example', target: '.env.example' }
  ]
  const templateProps = {
    name,
    igniteVersion: ignite.version,
    reactNativeVersion: rnInstall.version,
    vectorIcons: answers['vector-icons'],
    animatable: answers['animatable'],
    i18n: answers['i18n']
  }
  await ignite.copyBatch(context, templates, templateProps, {
    quiet: false,
    directory: `${ignite.ignitePluginPath()}/boilerplate`
  })

  /**
   * Append to files
   */
  // https://github.com/facebook/react-native/issues/12724
  filesystem.appendAsync('.gitattributes', '*.bat text eol=crlf')
  filesystem.append('.gitignore', '\n# Misc\n#')
  filesystem.append('.gitignore', '\n.env.example\n')
  filesystem.append('.gitignore', '.env\n')
  filesystem.append('.gitignore', 'dist\n')
  filesystem.append('.gitignore', '.jest\n')
  
  
  /**
   * Merge the package.json from our template into the one provided from react-native init.
   */
  async function mergePackageJsons () {
    // transform our package.json in case we need to replace variables
    const rawJson = await template.generate({
      directory: `${ignite.ignitePluginPath()}/boilerplate`,
      template: 'package.json.ejs',
      props: templateProps
    })
    const newPackageJson = JSON.parse(rawJson)

    // read in the react-native created package.json
    const currentPackage = filesystem.read('package.json', 'json')
    
    // deep merge, lol
    const newPackage = pipe(
      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)
      )
    )(currentPackage)

    // write this out
    filesystem.write('package.json', newPackage, { jsonIndent: 2 })
  }
  await mergePackageJsons()

  spinner.stop()

  // react native link -- must use spawn & stdio: ignore or it hangs!! :(
  spinner.text = `▸ linking native libraries`
  spinner.start()
  await system.spawn('react-native link', { stdio: 'ignore' })
  spinner.stop()

  // 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
    const boilerplate = parameters.options.b || parameters.options.boilerplate || 'ignite-typescript-boilerplate'

    await system.spawn(`ignite add ${boilerplate} ${debugFlag}`, { stdio: 'inherit' })

    // now run install of Ignite Plugins
    if (answers['dev-screens'] === 'Yes') {
      await system.spawn(`ignite add dev-screens@"~>2.2.0" ${debugFlag}`, {
        stdio: 'inherit'
      })
    }

    if (answers['vector-icons'] === 'react-native-vector-icons') {
      await system.spawn(`ignite add vector-icons@"~>1.0.0" ${debugFlag}`, {
        stdio: 'inherit'
      })
    }

    if (answers['i18n'] === 'react-native-i18n') {
      await system.spawn(`ignite add i18n@"~>1.0.0" ${debugFlag}`, { stdio: 'inherit' })
    }

    if (answers['animatable'] === 'react-native-animatable') {
      await system.spawn(`ignite add animatable@"~>1.0.0" ${debugFlag}`, {
        stdio: 'inherit'
      })
    }

  } catch (e) {
    ignite.log(e)
    throw e
  }

  // git configuration
  const gitExists = await filesystem.exists('./.git')
  if (!gitExists && !parameters.options['skip-git'] && system.which('git')) {
    // initial git
    const spinner = print.spin('configuring git')

    // TODO: Make husky hooks optional
    const huskyCmd = '' // `&& node node_modules/husky/bin/install .`
    system.run(`git init . && git add . && git commit -m "Initial commit." ${huskyCmd}`)

    spinner.succeed(`configured git`)
  }

  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 successMessage = `
    ${red('Ignite CLI')} ignited ${yellow(name)} in ${gray(`${perfDuration}s`)}

    To get started:

      cd ${name}
      react-native run-ios
      react-native run-android${androidInfo}
      ignite --help

    ${gray('Read the walkthrough at https://github.com/aerian-studios/ignite-typescript-boilerplate/blob/master/readme.md#boilerplate-walkthrough')}

    ${bold('Now get cooking! 🍽')}
  `


  print.info(successMessage)
}

module.exports = {
  install
}


================================================
FILE: commands/component.js
================================================
// @cliDescription  Generates a stateless component, styles, and an optional test.

module.exports = async function (context) {
  // grab some features
  const { parameters, strings, print, ignite } = context
  const { pascalCase, isBlank } = strings
  const config = ignite.loadIgniteConfig()
  const { tests } = config

  // validation
  if (isBlank(parameters.first)) {
    print.info(`${context.runtime.brand} generate component <name>\n`)
    print.info('A name is required.')
    return
  }

  // read some configuration
  const name = pascalCase(parameters.first)
  const props = { name }
  const jobs = [
    {
      template: 'component.ejs',
      target: `App/Components/${name}/${name}.tsx`
    },
    {
      template: 'component-style.ejs',
      target: `App/Components/${name}/${name}Style.ts`
    },
    {
      template: 'component-index.ejs',
      target: `App/Components/${name}/index.ts`
    },
    {
      template: 'component-story.ejs',
      target: `App/Components/${name}/${name}.story.tsx`
    },
    tests === 'ava' &&
    {
      template: 'component-test-ava.ejs',
      target: `App/Components/${name}/${name}Test.tsx`
    },
    tests === 'jest' &&
    {
      template: 'component-test-jest.ejs',
      target: `App/Components/${name}/${name}Test.tsx`
    }
  ]

  await ignite.copyBatch(context, jobs, props)
}


================================================
FILE: commands/container.js
================================================
// @cliDescription  Generates a redux smart component.

const patterns = require('../lib/patterns')

module.exports = async function (context) {
  // grab some features
  const { parameters, strings, print, ignite, filesystem } = context
  const { pascalCase, isBlank } = strings
  const config = ignite.loadIgniteConfig()

  // validation
  if (isBlank(parameters.first)) {
    print.info(`${context.runtime.brand} generate container <name>\n`)
    print.info('A name is required.')
    return
  }

  const name = pascalCase(parameters.first)
  const props = { name }

  const jobs = [
    {
      template: 'container.ejs',
      target: `App/Containers/${name}/${name}.tsx`
    },
    {
      template: 'container-style.ejs',
      target: `App/Containers/${name}/${name}Style.ts`
    }
  ]

  await ignite.copyBatch(context, jobs, props)

  // if using `react-navigation` go the extra step
  // and insert the container into the nav router
  if (config.navigation === 'react-navigation') {
    const containerName = name
    const appNavFilePath = `${process.cwd()}/App/Navigation/AppNavigation.tsx`
    const importToAdd = `import ${containerName} from "../Containers/${containerName}";`
    const routeToAdd = `  ${containerName}: { screen: ${containerName} },`

    if (!filesystem.exists(appNavFilePath)) {
      const msg = `No '${appNavFilePath}' file found.  Can't insert container.`
      print.error(msg)
      process.exit(1)
    }

    // insert container import
    ignite.patchInFile(appNavFilePath, {
      after: patterns[patterns.constants.PATTERN_IMPORTS],
      insert: importToAdd
    })

    // insert container route
    ignite.patchInFile(appNavFilePath, {
      after: patterns[patterns.constants.PATTERN_ROUTES],
      insert: routeToAdd
    })
  } else {
    print.info('Container created, manually add it to your navigation')
  }
}


================================================
FILE: commands/list.js
================================================
// @cliDescription  Generates a screen with a ListView/Flatlist/SectionList + walkthrough.

const patterns = require('../lib/patterns')

module.exports = async function (context) {
  // grab some features
  const { print, parameters, strings, ignite, filesystem } = context
  const { pascalCase, isBlank } = strings
  const config = ignite.loadIgniteConfig()

  // validation
  if (isBlank(parameters.first)) {
    print.info(`${context.runtime.brand} generate list <name>\n`)
    print.info('A name is required.')
    return
  }

  const name = pascalCase(parameters.first)
  const props = { name }


  // which type of layout?
  const typeMessage = 'What kind of List would you like to generate?'
  const typeChoices = ['Row', 'Grid']

  // Sections or no?
  const typeDataMessage = 'How will your data be presented on this list?'
  const typeDataChoices = ['Single', 'Sectioned']

  // Check for parameters to bypass questions
  let typeCode = parameters.options.codeType
  let type = parameters.options.type
  let dataType = parameters.options.dataType

  // only prompt if type is not defined
  if (!typeCode) {
    typeCode = 'flatlist';
  }

  if (!type) {
    // ask question 2
    const answers = await context.prompt.ask({
      name: 'type',
      type: 'list',
      message: typeMessage,
      choices: typeChoices
    })
    type = answers.type
  }

  if (!dataType) {
    // ask question 3
    const dataAnswers = await context.prompt.ask({
      name: 'type',
      type: 'list',
      message: typeDataMessage,
      choices: typeDataChoices
    })
    dataType = dataAnswers.type
  }

  // Sorry the following is so confusing, but so are React Native lists
  // There are 3 options and therefore 8 possible combinations
  let componentTemplate = dataType.toLowerCase() === 'sectioned'
    ? typeCode + '-sections'
    : typeCode
  let styleTemplate = ''
  // Different logic depending on code types
  if (typeCode === 'flatlist') {
    /*
    * The following mess is because FlatList supports numColumns
    * where SectionList does not.
    */
    if (type.toLowerCase() === 'grid' && dataType.toLowerCase() === 'sectioned') {
      // grid + section means we need wrap
      styleTemplate = 'listview-grid-style'
    } else if (type.toLowerCase() === 'grid') {
      componentTemplate = componentTemplate + '-grid'
      // grid + single = no wrap, use columns
      styleTemplate = 'flatlist-grid-style'
    } else {
      // no grids, flatlist basic
      styleTemplate = 'listview-style'
    }
  } else {
    // listview builder
    styleTemplate = type.toLowerCase() === 'grid'
      ? 'listview-grid-style'
      : 'listview-style'
  }

  const jobs = [
    {
      template: `${componentTemplate}.ejs`,
      target: `App/Containers/${name}/${name}.tsx`
    },
    {
      template: `${styleTemplate}.ejs`,
      target: `App/Containers/${name}/${name}Style.ts`
    }
  ]

  await ignite.copyBatch(context, jobs, props)

  // if using `react-navigation` go the extra step
  // and insert the screen into the nav router
  if (config.navigation === 'react-navigation') {
    const screenName = `${name}`
    const appNavFilePath = `${process.cwd()}/App/Navigation/AppNavigation.tsx`
    const importToAdd = `import { ${screenName} } from "../Containers/${screenName}";`
    const routeToAdd = `  ${screenName}: { screen: ${screenName} },`

    if (!filesystem.exists(appNavFilePath)) {
      const msg = `No '${appNavFilePath}' file found.  Can't insert list screen.`
      print.error(msg)
      process.exit(1)
    }

    // insert list screen import
    ignite.patchInFile(appNavFilePath, {
      after: patterns[patterns.constants.PATTERN_IMPORTS],
      insert: importToAdd
    })

    // insert list screen route
    ignite.patchInFile(appNavFilePath, {
      after: patterns[patterns.constants.PATTERN_ROUTES],
      insert: routeToAdd
    })
  } else {
    print.info('List screen created, manually add it to your navigation')
  }
}


================================================
FILE: commands/reducers.js
================================================
// @cliDescription  Generates a action/creator/reducer set for Redux.

module.exports = async function (context) {
  // grab some features
  const { parameters, ignite, strings, print } = context
  const { isBlank, pascalCase } = strings
  const config = ignite.loadIgniteConfig()

  // validation
  if (isBlank(parameters.first)) {
    print.info(`${context.runtime.brand} generate reducers <name>\n`)
    print.info('A name is required.')
    return
  }

  const name = pascalCase(parameters.first)
  const props = { name }

  const jobs = [{ template: `reducers.ejs`, target: `App/Reducers/${name}Reducers/index.tsx` }]
  if (config.tests && config.tests !== 'none') {
    jobs.push({
      template: `reducers-test-${config.tests}.ejs`,
      target: `App/Reducers/${name}Reducers/${name}ReducersTest.tsx`
    })
  }

  await ignite.copyBatch(context, jobs, props)
}


================================================
FILE: commands/saga.js
================================================
// @cliDescription  Generates a saga with an optional test.

module.exports = async function (context) {
  // grab some features
  const { parameters, ignite, print, strings } = context
  const { pascalCase, isBlank } = strings
  const config = ignite.loadIgniteConfig()
  const { tests } = config

  // validation
  if (isBlank(parameters.first)) {
    print.info(`${context.runtime.brand} generate saga <name>\n`)
    print.info('A name is required.')
    return
  }

  const name = pascalCase(parameters.first)
  const props = { name }

  const jobs = [{ template: `saga.ejs`, target: `App/Sagas/${name}Sagas/index.ts` }]
  if (tests) {
    jobs.push({
      template: `saga-test-${tests}.ejs`,
      target: `App/Sagas/${name}Sagas/${name}SagaTest.ts`
    })
  }

  // make the templates
  await ignite.copyBatch(context, jobs, props)
}


================================================
FILE: commands/screen.js
================================================
// @cliDescription  Generates an opinionated container.

const patterns = require('../lib/patterns')

module.exports = async function (context) {
  // grab some features
  const { parameters, print, strings, ignite, filesystem } = context
  const { pascalCase, isBlank } = strings
  const config = ignite.loadIgniteConfig()

  // validation
  if (isBlank(parameters.first)) {
    print.info(`${context.runtime.brand} generate screen <name>\n`)
    print.info('A name is required.')
    return
  }

  const name = pascalCase(parameters.first)
  const screenName = name.endsWith('Screen') ? name : `${name}Screen`
  const props = { name: screenName }

  const jobs = [
    {
      template: `screen.ejs`,
      target: `App/Containers/${screenName}/${screenName}.tsx`
    },
    {
      template: `screen-style.ejs`,
      target: `App/Containers/${screenName}/${screenName}Style.tsx`
    },
    {
      template: 'component-index.ejs',
      target: `App/Containers/${screenName}/index.ts`
    }
  ]

  // make the templates
  await ignite.copyBatch(context, jobs, props)

  // if using `react-navigation` go the extra step
  // and insert the screen into the nav router
  if (config.navigation === 'react-navigation') {
    const appNavFilePath = `${process.cwd()}/App/Navigation/AppNavigation.tsx`
    const importToAdd = `import ${screenName} from "../Containers/${screenName}";`
    const routeToAdd = `  ${screenName}: { screen: ${screenName} },`

    if (!filesystem.exists(appNavFilePath)) {
      const msg = `No '${appNavFilePath}' file found.  Can't insert screen.`
      print.error(msg)
      process.exit(1)
    }

    // insert screen import
    ignite.patchInFile(appNavFilePath, {
      after: patterns[patterns.constants.PATTERN_IMPORTS],
      insert: importToAdd
    })

    // insert screen route
    ignite.patchInFile(appNavFilePath, {
      after: patterns[patterns.constants.PATTERN_ROUTES],
      insert: routeToAdd
    })
  } else {
    print.info(`Screen ${screenName} created, manually add it to your navigation`)
  }
}


================================================
FILE: ignite.json
================================================
{
  "generators": [
    "component",
    "container",
    "list",
    "reducers",
    "saga",
    "screen"
  ]
}


================================================
FILE: lib/patterns.js
================================================
const constants = {
  PATTERN_IMPORTS: 'imports',
  PATTERN_ROUTES: 'routes'
}

module.exports = {
  constants,
  [constants.PATTERN_IMPORTS]: `import[\\s\\S]*from\\s+"react-navigation";?`,
  [constants.PATTERN_ROUTES]: 'const PrimaryNav'
}


================================================
FILE: lib/react-native-version.js
================================================
const { pathOr, is } = require('ramda')

// the default React Native version for this boilerplate
const REACT_NATIVE_VERSION = '0.51.0'

// where the version lives under gluegun
const pathToVersion = ['parameters', 'options', 'react-native-version']

// accepts the context and returns back the version
const getVersionFromContext = pathOr(REACT_NATIVE_VERSION, pathToVersion)

/**
 * Gets the React Native version to use.
 *
 * Attempts to read it from the command line, and if not there, falls back
 * to the version we want for this boilerplate.  For example:
 *
 *   $ ignite new Custom --react-native-version 0.44.1
 *
 * @param {*} context - The gluegun context.
 */
const getReactNativeVersion = (context = {}) => {
  const version = getVersionFromContext(context)
  return is(String, version) ? version : REACT_NATIVE_VERSION
}

module.exports = {
  REACT_NATIVE_VERSION,
  getReactNativeVersion
}


================================================
FILE: options.js
================================================
/**
 * The questions to ask during the install process.
 */
const questions = [
  {
    name: 'dev-screens',
    message: 'Would you like Ignite Development Screens?',
    type: 'list',
    choices: ['No', 'Yes']
  },
  {
    name: 'vector-icons',
    message: 'What vector icon library will you use?',
    type: 'list',
    choices: ['none', 'react-native-vector-icons']
  },
  {
    name: 'i18n',
    message: 'What internationalization library will you use?',
    type: 'list',
    choices: ['none', 'react-native-i18n']
  },
  {
    name: 'animatable',
    message: 'What animation library will you use?',
    type: 'list',
    choices: ['none', 'react-native-animatable']
  },
  {
    name: 'tests',
    message: 'What test library will you use?',
    type: 'list',
    choices: ['none', 'jest']
  }
]

/**
 * The max preset.
 */
const max = {
  'dev-screens': 'Yes',
  'vector-icons': 'react-native-vector-icons',
  i18n: 'react-native-i18n',
  animatable: 'react-native-animatable',
  tests: 'jest'
}

/**
 * The min preset.
 */
const min = {
  'dev-screens': 'No',
  'vector-icons': 'none',
  i18n: 'none',
  animatable: 'none',
  tests: 'none'
}

module.exports = {
  questions,
  answers: { min, max }
}


================================================
FILE: package.json
================================================
{
  "name": "ignite-typescript-boilerplate",
  "description": "TypeScript boilerplate for React Native.",
  "license": "MIT",
  "repository": "aerian-studios/ignite-typescript-boilerplate",
  "homepage": "https://github.com/aerian-studios/ignite-typescript-boilerplate",
  "version": "0.1.5",
  "files": [
    "boilerplate",
    "commands",
    "lib",
    "templates",
    "boilerplate.js",
    "ignite.json",
    "options.js",
    "readme.md",
    "plugin.js"
  ],
  "author": {
    "name": "Aerian Studios",
    "email": "matt.kane@aerian.com",
    "url": "https://github.com/aerian-studios/ignite-typescript-boilerplate"
  },
  "scripts": {
    "lint": "tslint",
    "test": "jest",
    "watch": "jest --runInBand --watch",
    "coverage": "jest --runInBand --coverage",
    "shipit": "np"
  },
  "devDependencies": {
    "@types/jest": "^21.1.5",
    "@types/ramda": "^0.24.18",
    "@types/react": "^16.0.18",
    "@types/react-native": "^0.49.2",
    "@types/react-navigation": "^1.0.21",
    "@types/react-redux": "^5.0.10",
    "@types/redux": "^3.6.0",
    "@types/seamless-immutable": "^7.1.1",
    "@types/webpack-env": "^1.13.2",
    "fs-jetpack": "^1.0.0",
    "jest": "^20.0.4",
    "np": "^2.15.0",
    "react": "16.0.0-beta.5",
    "redux": "^3.7.2",
    "redux-saga": "^0.16.0",
    "reduxsauce": "^0.7.0",
    "seamless-immutable": "^7.1.2",
    "sinon": "^2.3.1",
    "tempy": "^0.1.0",
    "tslint": "^5.8.0",
    "tslint-react": "^3.2.0",
    "typesafe-actions": "^1.1.2",
    "typescript": "^2.6.1"
  },
  "dependencies": {
    "ramda": "^0.23.0"
  }
}


================================================
FILE: plugin.js
================================================
// Ignite CLI plugin for Ts
// ----------------------------------------------------------------------------


const add = async function (context) {
  // No-op, as we do this all in `boilerplate.js`
}

/**
 * Remove yourself from the project.
 */
const remove = async function (context) {
  // No-op, as we do this all in `boilerplate.js`  
}

// Required in all Ignite CLI plugins
module.exports = { add, remove }


================================================
FILE: readme.md
================================================
## Ignite TypeScript Boilerplate for React Native

### The easiest way to develop React Native apps in TypeScript.
Get up and running with TypeScript React Native development in minutes. A batteries-included, opinionated starter project, and code generators for your components, reducers, sagas and more. 
Originally based on a port of the [Ignite IR Boilerplate](https://github.com/infinitered/ignite-ir-boilerplate) to TypeScript.

Currently includes:

* React Native 0.51.0 (but you can change this if you want to experiment)
* React Navigation
* Redux
* Redux Sagas
* And more!

## Quick Start

When you've installed the [Ignite CLI](https://github.com/infinitered/ignite), (tl;dr: `npm install -g ignite-cli`) you can get started with this boilerplate like this:

```sh
ignite new MyLatestCreation --b ignite-typescript-boilerplate
```

You can also change the React Native version, just keep in mind, we may not have tested this just yet.

```sh
ignite new MyLatestCreation --b ignite-typescript-boilerplate --react-native-version 0.46.0-rc.2
```

By default we'll ask you some questions during install as to which features you'd like.  If you just want them all, you can skip the questions:

```sh
ignite new MyLatestCreation --b ignite-typescript-boilerplate --max
```

If you want very few of these extras:

```sh
ignite new MyLatestCreation --b ignite-typescript-boilerplate --min
```

## Using TypeScript with React Native

Thanks to the beauty of [react-native-typescript-transformer](https://github.com/ds300/react-native-typescript-transformer), we can seamlessly use TypeScript in our React Native project. Source maps and hot reloading all work just like you would expect.

## Coding style

We use `tslint` to enforce coding style, with rules based on [Palantir's tslint-react](https://github.com/palantir/tslint-react), 
and a few changes to accommodate some Ignite quirks. If you install a plugin, your editor can probably automatically fix problems. 
In VS Code, set `"tslint.autoFixOnSave": true` in your 
workspace settings. You can run the linter from the command line. `npm run lint` runs the linter, while `npm run fixcode` tries to autofix problems.

## Boilerplate walkthrough

Your `App` folder is where most of the goodies are found in an Ignite app. Let's walk through them in more detail. Start with `Containers/App.tsx` (described below) and work your way down the walkthrough in order.

### Components

React components go here. We generate these as stateless functional components by default, as recommended by the React team.

### Containers

Containers are Redux-connected components, and are mostly full screens.

* `App.tsx` - your main application. We create a Redux store and configure it here
* `RootContainer.tsx` - main view of your application. Contains your status bar and navigation component
* `LaunchScreen.tsx` - this is the first screen shown in your application. It's loaded into the Navigation component

### Navigation

Your primary and other navigation components reside here.

* `AppNavigation.tsx` - loads in your initial screen and creates your menu(s) in a StackNavigation
* `Styles` - styling for the navigation

### Storybook

[Storybook](https://storybook.js.org/) has been setup to show off components in the different states. Storybook is a great way to develop and test components outside of use in your app. Simply run `yarn run storybook` to get started. All stories are contained in the `*.story.tsx` files along side the components.

### Themes

Styling themes used throughout your app styles.

* `ApplicationStyles.ts` - app-wide styles
* `Colors.ts` - defined colors for your app
* `Fonts.ts` - defined fonts for your app
* `Images.ts` - loads and caches images used in your app
* `Metrics.ts` - useful measurements of things like navBarHeight

### Config

Initialize and configure things here.

* `AppConfig.ts` - simple React Native configuration here
* `DebugConfig.js` - define how you want your debug environment to act. This is a .js file because that's what
Ignite expects to find.
* `ReactotronConfig.ts` - configures [Reactotron](https://github.com/infinitered/reactotron) in your project (Note: this [will be extracted](https://github.com/infinitered/ignite/issues/779) into a plugin in the future)

### Fixtures

Contains json files that mimic API responses for quicker development. These are used by the `Services/FixtureApi.ts` object to mock API responses.

### Redux, Sagas

Contains a preconfigured Redux and Redux-Sagas setup. Review each file carefully to see how Redux interacts with your application. You will find these in the Reducers and Sagas folders. We use [typesafe-actions](https://github.com/piotrwitek/typesafe-actions) to get lovely
type checking of our reducers and actions. Take a look at `Lib/ReduxHelpers.ts` for some extra functions that
we use to make them more Ignite-y.

### Services

Contains your API service and other important utilities for your application.

* `Api.tsx` - main API service, giving you an interface to communicate with your back end
* `ExamplesRegistry.tsx` - lets you view component and Ignite plugin examples in your app
* `FixtureApi.tsx` - mocks your API service, making it faster to develop early on in your app


### Lib

We recommend using this folder for modules that can be extracted into their own NPM packages at some point.

### Images

Contains actual images (usually png) used in your application.

### Transforms

Helpers for transforming data between API and your application and vice versa. An example is provided that you can look at to see how it works.

### Tests

We create Jest tests alongside the components, reducers and sagas. Enable this by adding `"tests": "jest"` to `ignite/ignite.json`.

### Code generation

Currently, the following code generation commands work properly:
* `ignite generate component MyComponent` - generates a stateless functional component.
* `ignite generate container MyContainer` - generates a Redux-connected React.Component, with state and view lifecycle.
* `ignite generate screen MyScreen` - generates a Redux-connected React.Component, with state, view lifecycle and react-navigation.
* `ignite generate reducers MyNew` - generates a set of Redux reducers.
* `ignite generate saga MySaga` - generates a Redux Saga
* `ignite generate list MyList` - generates a FlatList, formatted either as a grid or list.

### Further reading

A comprehensive guide to best practice with TypeScript in React is [the React Redux TypeScript Guide](https://github.com/piotrwitek/react-redux-typescript-guide), which covers a lot more than just Redux. We have adopted a lot of the patterns from this. The `typesafe-actions` library that we use was created by @piotrwitek, the author of the guide.

Microsoft created [TypeScript React Native Starter](https://github.com/Microsoft/TypeScript-React-Native-Starter), which includes a walkthrough on switching projects to TypeScript.

[React TypeScript Tutorial](https://github.com/DanielRosenwasser/React-TypeScript-Tutorial) is React rather than React Native, but has useful guides.

[This post](http://blog.novanet.no/easy-typescript-with-react-native/) is a good run-through of the [react-native-typescript-transfomer](https://github.com/ds300/react-native-typescript-transformer), which allows us to skip the transpile step that we were using before. Thanks [@wormyy] for the heads-up on this.

### Credits
Created by [Matt Kane](https://github.com/ascorbic) at [Aerian Studios](https://www.aerian.com). Based on [Ignite IR Boilerplate](https://github.com/infinitered/ignite-ir-boilerplate), by Infinite Red. 

================================================
FILE: templates/component-index.ejs
================================================
import <%= props.name %> from "./<%= props.name %>";

export default <%= props.name %>;


================================================
FILE: templates/component-story.ejs
================================================
import { storiesOf } from "@storybook/react-native";
import * as React from "react";

import <%= props.name %> from "./<%= props.name %>";

storiesOf("<%= props.name %>", module)
  .add("Default", () => (
    <<%= props.name %> />
  ));

================================================
FILE: templates/component-style.ejs
================================================
import { StyleSheet } from "react-native";

export default StyleSheet.create({
  container: {
    flex: 1,
  },
});


================================================
FILE: templates/component-test-jest.ejs
================================================
/// <reference types="@types/jest" />
import * as React from "react";
import { <%= props.name %> } from "./<%= props.name %>";
import * as renderer from "react-test-renderer";


test("<%= props.name %> component renders correctly", () => {
  const tree = renderer.create(<<%= props.name %> someProperty="howdy" anotherProperty={false} />).toJSON();
  expect(tree).toMatchSnapshot();
});

================================================
FILE: templates/component.ejs
================================================
import * as React from "react";
import { Text, View } from "react-native";
import styles from "./<%= props.name %>Style";

interface Props {
  someProperty: string;
  anotherProperty: boolean;
}

const <%= props.name %>: React.SFC<Props> = ({someProperty, anotherProperty}: Props) =>  (
  <View style={styles.container}>
    <Text><%= props.name %> Component</Text>
  </View>
);

export default <%= props.name %>;


================================================
FILE: templates/container-style.ejs
================================================
import { StyleSheet } from "react-native";
import { Colors, Metrics } from "../../Themes/";

export default StyleSheet.create({
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background,
  },
});


================================================
FILE: templates/container.ejs
================================================
import * as React from "react";
import { Text, View } from "react-native";
import { connect } from "react-redux";
import * as Redux from "redux";
import { RootState } from "../../Reducers";
import { Images } from "../../Themes";
import Metrics from "../../Themes/Metrics";

// Styles
import styles from "./<%= props.name %>Style";

/**
 * The properties passed to the component
 */
export interface OwnProps {

}
/**
 * The properties mapped from Redux dispatch
 */
export interface DispatchProps {

}

/**
 * The properties mapped from the global state
 */
export interface StateProps {

}

/**
 * The local state
 */
export interface State {

}

type Props = StateProps & DispatchProps & OwnProps;

class <%= props.name %> extends
  React.Component<Props, State> {
  public state = {

  }

  public render() {
    return (
      <View style={styles.container}>
        <Text>Hello <%= props.name %></Text>
      </View>
    );
  }
}

const mapDispatchToProps = (dispatch: Redux.Dispatch<RootState>): DispatchProps => ({
  
});

const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => {
  return {};
};

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>) as React.ComponentClass<OwnProps>;


================================================
FILE: templates/flatlist-grid-style.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../../Themes'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    backgroundColor: Colors.background
  },
  row: {
    flex: 1,
    backgroundColor: Colors.fire,
    marginVertical: Metrics.smallMargin,
    justifyContent: 'center',
    margin: 10,
    padding: 5,
    paddingVertical: 10,
    borderRadius: Metrics.smallMargin
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    textAlign: 'center',
    color: Colors.snow
  },
  listContent: {
    marginTop: Metrics.baseMargin
  }
})


================================================
FILE: templates/flatlist-grid.ejs
================================================
import * as React from "react";
import { View, Text, FlatList } from "react-native";
import { connect } from "react-redux";

// More info here: https://facebook.github.io/react-native/docs/flatlist.html

// Styles
import styles from "./<%= props.name %>Style";

export interface RowItem {
  title: string;
  description: string;
}
interface Props {
  data: RowItem[];
}

export default class <%= props.name %> extends React.PureComponent<Props> {

  /* ***********************************************************
  * `renderRow` function. How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={item.title} description={item.description} />
  *************************************************************/
  renderRow ({item}: {item: RowItem}) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>{item.title}</Text>
        <Text style={styles.label}>{item.description}</Text>
      </View>
    )
  }

  // Render a header?
  renderHeader = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Header - </Text>

  // Render a footer?
  renderFooter = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Footer - </Text>

  // Show this when data is empty
  renderEmpty = () =>
    <Text style={styles.label}> - Nothing to See Here - </Text>

  renderSeparator = () =>
    <Text style={styles.label}> - ~~~~~ - </Text>

  // The default function if no Key is provided is index
  // an identifiable key is important if you plan on
  // item reordering.  Otherwise index is fine
  keyExtractor = (item: RowItem, index: number) => index

  // How many items should be kept im memory as we scroll?
  oneScreensWorth = 20

  // extraData is for anything that is not indicated in data
  // for instance, if you kept "favorites" in `this.state.favs`
  // pass that in, so changes in favorites will cause a re-render
  // and your renderItem will have access to change depending on state
  // e.g. `extraData`={this.state.favs}

  // Optimize your list if the height of each item can be calculated
  // by supplying a constant height, there is no need to measure each
  // item after it renders.  This can save significant time for lists
  // of a size 100+
  // e.g. itemLayout={(data, index) => (
  //   {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
  // )}

  render () {
    return (
      <View style={styles.container}>
        <FlatList
          contentContainerStyle={styles.listContent}
          data={this.props.data}
          renderItem={this.renderRow}
          numColumns={2}
          keyExtractor={this.keyExtractor}
          initialNumToRender={this.oneScreensWorth}
          ListHeaderComponent={this.renderHeader}
          ListFooterComponent={this.renderFooter}
          ListEmptyComponent={this.renderEmpty}
          ItemSeparatorComponent={this.renderSeparator}
        />
      </View>
    )
  }
}


================================================
FILE: templates/flatlist-sections.ejs
================================================
import * as React from "react";
import { View, SectionList, Text } from "react-native";

// More info here: https://facebook.github.io/react-native/docs/sectionlist.html

// Styles
import styles from "./<%= props.name %>Style";

export interface Section {
  key: string;
  data: RowItem[];
}
export interface RowItem {
  title: string;
  description: string;
}
interface Props {
  data: Section[];
}

class <%= props.name %> extends React.PureComponent {


  /* ***********************************************************
  * `renderItem` function - How each cell should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
  *   return <MyCustomCell title={item.title} description={item.description} />
  *
  * For sections with different cells (heterogeneous lists), you can do branch
  * logic here based on section.key OR at the data level, you can provide
  * `renderItem` functions in each section.
  *
  * Note: You can remove section/separator functions and jam them in here
  *************************************************************/
  renderItem ({section, item}: {section: Section, item: RowItem}) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>Section {section.key} - {item.title}</Text>
        <Text style={styles.label}>{item.description}</Text>
      </View>
    )
  }

  // Conditional branching for section headers, also see step 3
  renderSectionHeader ({section}: {section: Section}) {
    switch (section.key) {
      case "First":
        return <View style={styles.sectionHeader}><Text style={styles.boldLabel}>First Section</Text></View>
      default:
        return <View style={styles.sectionHeader}><Text style={styles.boldLabel}>Second Section</Text></View>
    }
  }

  /* ***********************************************************
  * Consider the configurations we've set below.  Customize them
  * to your liking!  Each with some friendly advice.
  *
  * Removing a function here will make SectionList use default
  *************************************************************/
  // Render a header?
  renderHeader = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Full List Header - </Text>

  // Render a footer?
  renderFooter = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Full List Footer - </Text>

  // Does each section need a footer?
  renderSectionFooter = () =>
    <Text style={styles.label}> END SECTION!!!! </Text>

  // Show this when data is empty
  renderEmpty = () =>
    <Text style={styles.label}> - Nothing to See Here - </Text>

  renderSeparator = () =>
    <Text style={styles.label}> - ~~~~~ - </Text>

  renderSectionSeparator = () =>
    <Text style={styles.label}> \/\/\/\/\/\/\/\/ </Text>

  // The default function if no Key is provided is index
  // an identifiable key is important if you plan on
  // item reordering.  Otherwise index is fine
  keyExtractor = (item, index) => index

  // How many items should be kept im memory as we scroll?
  oneScreensWorth = 20

  // extraData is for anything that is not indicated in data
  // for instance, if you kept "favorites" in `this.state.favs`
  // pass that in, so changes in favorites will cause a re-render
  // and your renderItem will have access to change depending on state
  // e.g. `extraData`={this.state.favs}

  // Optimize your list if the height of each item can be calculated
  // by supplying a constant height, there is no need to measure each
  // item after it renders.  This can save significant time for lists
  // of a size 100+
  // e.g. itemLayout={(data, index) => (
  //   {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
  // )}

  render () {
    return (
      <View style={styles.container}>
        <SectionList
          renderSectionHeader={this.renderSectionHeader}
          sections={this.props.data}
          contentContainerStyle={styles.listContent}
          data={this.props.data}
          renderItem={this.renderItem}
          keyExtractor={this.keyExtractor}
          initialNumToRender={this.oneScreensWorth}
          ListHeaderComponent={this.renderHeader}
          SectionSeparatorComponent={this.renderSectionSeparator}
          ListFooterComponent={this.renderFooter}
          ListEmptyComponent={this.renderEmpty}
          ItemSeparatorComponent={this.renderSeparator}
          renderSectionFooter={this.renderSectionFooter}
        />
      </View>
    )
  }
}


================================================
FILE: templates/flatlist.ejs
================================================
import * as React from "react";
import { View, Text, FlatList } from "react-native";

// More info here: https://facebook.github.io/react-native/docs/flatlist.html

// Styles
import styles from "./<%= props.name %>Style";

export interface RowItem {
  title: string;
  description: string;
}
interface Props {
  data: RowItem[];
}

export default class <%= props.name %> extends React.PureComponent<Props> {

  /* ***********************************************************
  * `renderRow` function. How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={item.title} description={item.description} />
  *************************************************************/
  renderRow ({item}: {item: RowItem}) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>{item.title}</Text>
        <Text style={styles.label}>{item.description}</Text>
      </View>
    )
  }

  // Render a header?
  renderHeader = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Header - </Text>

  // Render a footer?
  renderFooter = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Footer - </Text>

  // Show this when data is empty
  renderEmpty = () =>
    <Text style={styles.label}> - Nothing to See Here - </Text>

  renderSeparator = () =>
    <Text style={styles.label}> - ~~~~~ - </Text>

  // The default function if no Key is provided is index
  // an identifiable key is important if you plan on
  // item reordering.  Otherwise index is fine
  keyExtractor = (item: RowItem, index: number) => index

  // How many items should be kept im memory as we scroll?
  oneScreensWorth = 20

  // extraData is for anything that is not indicated in data
  // for instance, if you kept "favorites" in `this.state.favs`
  // pass that in, so changes in favorites will cause a re-render
  // and your renderItem will have access to change depending on state
  // e.g. `extraData`={this.state.favs}

  // Optimize your list if the height of each item can be calculated
  // by supplying a constant height, there is no need to measure each
  // item after it renders.  This can save significant time for lists
  // of a size 100+
  // e.g. itemLayout={(data, index) => (
  //   {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
  // )}

  render () {
    return (
      <View style={styles.container}>
        <FlatList
          contentContainerStyle={styles.listContent}
          data={this.props.data}
          renderItem={this.renderRow}
          keyExtractor={this.keyExtractor}
          initialNumToRender={this.oneScreensWorth}
          ListHeaderComponent={this.renderHeader}
          ListFooterComponent={this.renderFooter}
          ListEmptyComponent={this.renderEmpty}
          ItemSeparatorComponent={this.renderSeparator}
        />
      </View>
    )
  }
}


================================================
FILE: templates/listview-grid-style.ejs
================================================
import { StyleSheet } from "react-native";
import { ApplicationStyles, Metrics, Colors } from "../../../Themes";

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    backgroundColor: Colors.background
  },
  row: {
    width: 100,
    height: 100,
    justifyContent: 'center',
    alignItems: 'center',
    margin: Metrics.baseMargin,
    backgroundColor: Colors.fire,
    borderRadius: Metrics.smallMargin
  },
  sectionHeader: {
    paddingTop: Metrics.doubleBaseMargin,
    width: Metrics.screenWidth,
    alignSelf: 'center',
    margin: Metrics.baseMargin,
    backgroundColor: Colors.background
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center'
  },
  listContent: {
    justifyContent: 'space-around',
    flexDirection: 'row',
    flexWrap: 'wrap'
  }
})


================================================
FILE: templates/reducers-test-jest.ejs
================================================
import <%= props.name %>Actions, <%= props.name %>Reducer as reducer, INITIAL_STATE from "./<%= props.name %>Reducers";

it("attempt", () => {
  const state = reducer(INITIAL_STATE, <%= props.name %>Actions.request('data'))

  expect(state.fetching).toBe(true)
})

it("success", () => {
  const state = reducer(INITIAL_STATE, <%= props.name %>Actions.success('hi'))

  expect(state.payload).toBe('hi')
})

it("failure", () => {
  const state = reducer(INITIAL_STATE, <%= props.name %>Actions.failure())

  expect(state.fetching).toBe(false)
  expect(state.error).toBe(true)
})


================================================
FILE: templates/reducers.ejs
================================================
import { Action, AnyAction, Reducer } from "redux";
import * as SI from "seamless-immutable";
import { createAction, PayloadAction } from "typesafe-actions";
import { mapReducers, ReducerMap } from "../../Lib/ReduxHelpers";

/* ------------- Types and Action Creators ------------- */
interface <%= pascalCase(props.name) %>SuccessParams {data: string; }

const actionCreators = {
  request: createAction("<%= snakeCase(props.name).toUpperCase() %>_REQUEST"),
  success: (payload: <%= pascalCase(props.name) %>SuccessParams)  => ({type: "<%= snakeCase(props.name).toUpperCase() %>_SUCCESS", payload})),
  failure: createAction("<%= snakeCase(props.name).toUpperCase() %>_FAILURE"),
};

export const <%= pascalCase(props.name) %>Actions = actionCreators;

export interface <%= pascalCase(props.name) %>State {
  data?: string | null;
  error?: boolean | null;
  fetching?: boolean | null;
}

export type <%= pascalCase(props.name) %>Action = PayloadAction<string, <%= pascalCase(props.name) %>State>;

export type Immutable<%= pascalCase(props.name) %>State = SI.ImmutableObject<<%= pascalCase(props.name) %>State>;

/* ------------- Initial State ------------- */

export const INITIAL_STATE: Immutable<%= pascalCase(props.name) %>State = SI.from({
  data: null,
  error: null,
  fetching: null,
});

/* ------------- Reducers ------------- */

export const request: Reducer<Immutable<%= pascalCase(props.name) %>State> =
(state: Immutable<%= pascalCase(props.name) %>State) =>
  state.merge({ fetching: true });

export const success: Reducer<Immutable<%= pascalCase(props.name) %>State> =
(state: Immutable<%= pascalCase(props.name) %>State, action: AnyAction & {payload?: <%= pascalCase(props.name) %>SuccessParams}) => {
  if (!action.payload) {
    return failure(state, action);
  }
  const { data } = action.payload;
  
  return state.merge({ fetching: false, error: null, data });
};

export const failure: Reducer<Immutable<%= pascalCase(props.name) %>State> = (state: Immutable<%= pascalCase(props.name) %>State) =>
  state.merge({ fetching: false, error: true, data: null });

/* ------------- Hookup Reducers To Types ------------- */

const reducerMap: ReducerMap<typeof actionCreators, Immutable<%= pascalCase(props.name) %>State> = {
  request,
  failure,
  success,
};

export const <%= pascalCase(props.name) %>Reducer = mapReducers(INITIAL_STATE, reducerMap, actionCreators);

export default <%= pascalCase(props.name) %>Reducer;


================================================
FILE: templates/saga-test-jest.ejs
================================================
/* ***********************************************************
* Wiring Instructions
* To make this test work, you'll need to:
*  - Add a Fixture named get<%= props.name %> to the
*    ./App/Services/FixtureApi file. You can just keep adding
*    functions to that file.
*************************************************************/

import FixtureAPI from '../../Services/FixtureApi'
import { call, put } from 'redux-saga/effects'
import { get<%= props.name %> } from './index'
import <%= props.name %>Actions from '../../Reducers/<%= props.name %>Reducers'

const stepper = (fn) => (mock) => fn.next(mock).value

it('first calls API', () => {
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // first yield is the API
  expect(step()).toEqual(call(FixtureAPI.get<%= props.name %>, 'taco'))
})

it('success path', () => {
  const response = FixtureAPI.get<%= props.name %>('taco')
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // Step 1: Hit the api
  step()
  // Step 2: Successful return and data!
  expect(step(response)).toEqual(put(<%= pascalCase(props.name) %>Actions.<%= camelCase(props.name) %>Success(21)))
})

it('failure path', () => {
  const response = {ok: false}
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // Step 1: Hit the api
  step()
  // Step 2: Failed response.
  expect(step(response)).toEqual(put(<%= pascalCase(props.name) %>Actions.<%= camelCase(props.name) %>Failure()))
})


================================================
FILE: templates/saga.ejs
================================================
/* ***********************************************************
* A short word on how to use this automagically generated file.
* We're often asked in the ignite gitter channel how to connect
* to a to a third party api, so we thought we'd demonstrate - but
* you should know you can use sagas for other flow control too.
*
* Other points:
*  - You'll need to add this saga to sagas/index.ts
*  - This template uses the api declared in sagas/index.ts, so
*    you'll need to define a constant in that file.
*************************************************************/
import { ApiResponse } from "apisauce";
import { SagaIterator } from "redux-saga";
import { call, put } from "redux-saga/effects";
import { <%= props.name %>Actions, <%= props.name %>Action } from "../../Reducers/<%= props.name %>Reducers";
import { <%= props.name %>Api, <%= props.name %>Response } from "../../Services/<%= props.name %>Api";


export function * get<%= props.name %> (api: <%= props.name %>Api, action: <%= props.name %>Action): SagaIterator {
  const { data } = action;
  // make the call to the api
  const response: ApiResponse<<%= props.name %>Response>  = yield call(api.get<%= camelCase(props.name) %>, data);

  // success?
  if (response.ok) {
    // You might need to change the response here - do this with a 'transform',
    // located in ../../Transforms/. Otherwise, just pass the data back from the api.
    yield put(<%= props.name %>Actions.success({data: response.data}));
  } else {
    yield put(<%= props.name %>Actions.failure());
  }
}


================================================
FILE: templates/screen-index.ejs
================================================
import <%= props.name %> from "./<%= props.name %>";

export default <%= props.name %>;


================================================
FILE: templates/screen-style.ejs
================================================
import { StyleSheet } from "react-native";
import { Colors, Metrics } from "../../Themes/";

export default StyleSheet.create({
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background,
  },
});


================================================
FILE: templates/screen.ejs
================================================
import * as React from "react";
import { Alert, Image, Text, TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/FontAwesome";
import { NavigationAction, NavigationDrawerScreenOptions, NavigationScreenProps, NavigationState } from "react-navigation";
import { connect } from "react-redux";
import * as Redux from "redux";
import { RootState } from "../../Reducers";
import { Images } from "../../Themes";
import Metrics from "../../Themes/Metrics";

// Styles
import styles from "./<%= props.name %>Style";

/**
 * The properties passed to the component
 */
export interface OwnProps {

}
/**
 * The properties mapped from Redux dispatch
 */
export interface DispatchProps {

}

/**
 * The properties mapped from the global state
 */
export interface StateProps {

}

/**
 * The local state
 */
export interface State {

}

type Props = StateProps & DispatchProps & OwnProps & NavigationScreenProps<{}>;

class <%= props.name %> extends
  React.Component<Props, State> {
  
  public state = {

  };

  public static navigationOptions: NavigationDrawerScreenOptions = {
    drawerLabel: "Welcome",
    drawerIcon: ({ tintColor, focused }: {tintColor: string, focused: boolean}) => (
      <Icon
        name="home"
        size={20}
        style={{ color: tintColor }}
      />
    ),
  };

  public render() {
    return (
      <View style={styles.mainContainer}>
        <Text>Hello <%= props.name %></Text>
      </View>
    );
  }
}

const mapDispatchToProps = (dispatch: Redux.Dispatch<RootState>): DispatchProps => ({
  
});

const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => {
  return {};
};

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>) as React.ComponentClass<OwnProps>;


================================================
FILE: test/generators-integration.test.js
================================================
const execa = require('execa')
const jetpack = require('fs-jetpack')
const tempy = require('tempy')

const IGNITE = 'ignite'
const APP = 'IntegrationTest'
const BOILERPLATE = `${__dirname}/..`
console.warn(BOILERPLATE)
// calling the ignite cli takes a while
jasmine.DEFAULT_TIMEOUT_INTERVAL = 600000

describe('without a linter', () => {
  beforeAll(async () => {
    // creates a new temp directory
    process.chdir(tempy.directory())
    await execa(IGNITE, ['new', APP, '--min', '--skip-git', '--no-lint', '--boilerplate', BOILERPLATE])
    process.chdir(APP)
  })

  test('does not have a linting script', async () => {
    expect(jetpack.read('package.json', 'json')['scripts']['lint']).toBe(undefined)
  })
})

describe('generators', () => {
  beforeAll(async () => {
    // creates a new temp directory
    process.chdir(tempy.directory())
    await execa(IGNITE, ['new', APP, '--min', '--skip-git', '--boilerplate', BOILERPLATE])
    process.chdir(APP)
  })

  test('generates a component', async () => {
    await execa(IGNITE, ['g', 'component', 'Test'], { preferLocal: false })
    expect(jetpack.exists('App/Components/Test.tsx')).toBe('file')
    expect(jetpack.exists('App/Components/Styles/TestStyle.ts')).toBe('file')
    const lint = await execa('npm', ['-s', 'run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate listview of type row works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestRow', '--type=Row', '--codeType=listview', '--dataType=Single'], { preferLocal: false })
    expect(jetpack.exists('App/Containers/TestRow.tsx')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestRowStyle.ts')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate flatlist of type row works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestFlatRow', '--type=Row', '--codeType=flatlist', '--dataType=Single'], { preferLocal: false })
    expect(jetpack.exists('App/Containers/TestFlatRow.tsx')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestFlatRowStyle.ts')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate listview of sections works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestSection', '--type=Row', '--codeType=listview', '--dataType=Sectioned'], { preferLocal: false })
    expect(jetpack.exists('App/Containers/TestSection.tsx')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestSectionStyle.ts')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate flatlist of sections works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestFlatSection', '--type=Row', '--codeType=flatlist', '--dataType=Sectioned'], { preferLocal: false })
    expect(jetpack.exists('App/Containers/TestFlatSection.tsx')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestFlatSectionStyle.ts')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate listview of type grid works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestGrid', '--type=Grid', '--codeType=listview', '--dataType=Single'], { preferLocal: false })
    expect(jetpack.exists('App/Containers/TestGrid.tsx')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestGridStyle.ts')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate redux works', async () => {
    await execa(IGNITE, ['g', 'redux', 'Test'], { preferLocal: false })
    expect(jetpack.exists('App/Redux/TestRedux.tsx')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate container works', async () => {
    await execa(IGNITE, ['g', 'container', 'Container'], { preferLocal: false })
    expect(jetpack.exists('App/Containers/Container.tsx')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/ContainerStyle.ts')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate saga works', async () => {
    await execa(IGNITE, ['g', 'saga', 'Test'], { preferLocal: false })
    expect(jetpack.exists('App/Sagas/TestSagas.tsx')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })

  test('generate screen works', async () => {
    await execa(IGNITE, ['g', 'screen', 'Test'], { preferLocal: false })
    expect(jetpack.exists('App/Containers/TestScreen.tsx')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestScreenStyle.ts')).toBe('file')
    const lint = await execa('npm', ['run', 'lint'])
    expect(lint.stderr).toBe('')
  })
})


================================================
FILE: test/interface.test.js
================================================
const boilerplate = require('../boilerplate')
const plugin = require('../plugin')

test('boilerplate interface', async () => {
  expect(typeof boilerplate.install).toBe('function')
})

test('plugin interface', async () => {
  expect(typeof plugin.add).toBe('function')
  expect(typeof plugin.remove).toBe('function')
})


================================================
FILE: test/react-native-version.test.js
================================================
const boilerplate = require('../lib/react-native-version')

// grab a few things from the boilerplate module
const get = boilerplate.getReactNativeVersion
const DEFAULT = boilerplate.REACT_NATIVE_VERSION

/**
 * Runs with a valid gluegun context and a staged version number.
 *
 * @param {*} reactNativeVersion The React Native version to use.
 * @return {string} The version number we should be using.
 */
const mock = reactNativeVersion => get({
  parameters: {
    options: {
      'react-native-version': reactNativeVersion
    }
  }
})

// this would only happen if we screwed something up in our boilerplate.js
test('it handles strange inputs from code', () => {
  expect(get()).toBe(DEFAULT)
  expect(get(null)).toBe(DEFAULT)
  expect(get(true)).toBe(DEFAULT)
  expect(get(8)).toBe(DEFAULT)
  expect(get('hello')).toBe(DEFAULT)
  expect(get([])).toBe(DEFAULT)
  expect(get({})).toBe(DEFAULT)
  expect(get(() => true)).toBe(DEFAULT)
})

// this could happen because it's valid input via minimist from the user
test('it handles strange input from the user', () => {
  expect(mock(true)).toBe(DEFAULT)
  expect(mock(false)).toBe(DEFAULT)
  expect(mock([])).toBe(DEFAULT)
  expect(mock({})).toBe(DEFAULT)
})

// very edge-casey
test('it handles not-quite semver numbers', () => {
  expect(mock(0)).toBe(DEFAULT)
  expect(mock(0.25)).toBe(DEFAULT)
})

// happy path
test('it handles valid versions', () => {
  expect(mock('0.41.0')).toBe('0.41.0')
  expect(mock('0.41.0-beta.1')).toBe('0.41.0-beta.1')
  expect(mock(DEFAULT)).toBe(DEFAULT)
  expect(mock('next')).toBe('next')
})


================================================
FILE: tsconfig.json
================================================
{
    "compilerOptions": {
      /* Basic Options */
      "target": "ESNEXT",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
      "module": "ESNext",                     /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
      // "lib": [],                             /* Specify library files to be included in the compilation:  */
      "allowJs": true,                       /* Allow javascript files to be compiled. */
      // "checkJs": true,                       /* Report errors in .js files. */
      "jsx": "react-native",                    /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
      // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
      "sourceMap": true,                        /* Generates corresponding '.map' file. */
      // "outFile": "./",                       /* Concatenate and emit output to single file. */
      "outDir": "./dist",                        /* Redirect output structure to the directory. */
      // "rootDir": "./src",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
      // "removeComments": true,                /* Do not emit comments to output. */
      // "noEmit": true,                        /* Do not emit outputs. */
      // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
      // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
      // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
  
      /* Strict Type-Checking Options */
      "strict": true,                            /* Enable all strict type-checking options. */
      // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
      // "strictNullChecks": true,              /* Enable strict null checks. */
      // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
      // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */
  
      /* Additional Checks */
      // "noUnusedLocals": true,                /* Report errors on unused locals. */
      // "noUnusedParameters": true,            /* Report errors on unused parameters. */
      // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
      // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
  
      /* Module Resolution Options */
      "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
      "baseUrl": "./boilerplate/",                       /* Base directory to resolve non-absolute module names. */
      // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
      // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
      "typeRoots": ["./boilerplate/types/", "./node_modules/"],                       /* List of folders to include type definitions from. */
      // "types": [],                           /* Type declaration files to be included in compilation. */
      "allowSyntheticDefaultImports": true  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
      // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
  
      /* Source Map Options */
      // "sourceRoot": "./",                    /* Specify the location where debugger should locate TypeScript files instead of source locations. */
      // "mapRoot": "./",                       /* Specify the location where debugger should locate map files instead of generated locations. */
      // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
      // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
  
      /* Experimental Options */
      // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
      // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
    }
  }
Download .txt
gitextract_1v1ble8m/

├── .gitignore
├── boilerplate/
│   ├── .babelrc
│   ├── .editorconfig
│   ├── App/
│   │   ├── Components/
│   │   │   ├── AlertMessage/
│   │   │   │   ├── AlertMessage.story.tsx
│   │   │   │   ├── AlertMessage.tsx
│   │   │   │   ├── AlertMessageStyles.ts
│   │   │   │   ├── AlertMessageTest.tsx
│   │   │   │   └── index.ts
│   │   │   ├── DrawerButton/
│   │   │   │   ├── DrawerButton.story.tsx
│   │   │   │   ├── DrawerButton.tsx
│   │   │   │   ├── DrawerButtonStyles.ts
│   │   │   │   ├── DrawerButtonTest.tsx
│   │   │   │   └── index.ts
│   │   │   ├── FullButton/
│   │   │   │   ├── FullButton.story.tsx
│   │   │   │   ├── FullButton.tsx
│   │   │   │   ├── FullButtonStyles.ts
│   │   │   │   ├── FullButtonTest.tsx
│   │   │   │   └── index.ts
│   │   │   ├── README.md
│   │   │   ├── RoundedButton/
│   │   │   │   ├── RoundedButton.story.tsx
│   │   │   │   ├── RoundedButton.tsx
│   │   │   │   ├── RoundedButtonStyles.ts
│   │   │   │   ├── RoundedButtonTest.tsx
│   │   │   │   └── index.ts
│   │   │   └── Stories.tsx
│   │   ├── Config/
│   │   │   ├── AppConfig.ts
│   │   │   ├── DebugConfig.js
│   │   │   ├── README.md
│   │   │   ├── ReactotronConfig.ts
│   │   │   ├── ReduxPersist.ts
│   │   │   └── index.ts
│   │   ├── Containers/
│   │   │   ├── App.tsx
│   │   │   ├── LaunchScreen/
│   │   │   │   ├── LaunchScreen.tsx
│   │   │   │   ├── LaunchScreenStyles.ts
│   │   │   │   └── index.ts
│   │   │   ├── README.md
│   │   │   └── RootContainer/
│   │   │       ├── RootContainer.tsx
│   │   │       ├── RootContainerStyles.ts
│   │   │       └── index.ts
│   │   ├── Fixtures/
│   │   │   ├── README.md
│   │   │   ├── gantman.json
│   │   │   ├── rateLimit.json
│   │   │   ├── root.json
│   │   │   └── skellock.json
│   │   ├── Images/
│   │   │   └── README.md
│   │   ├── Lib/
│   │   │   ├── README.md
│   │   │   └── ReduxHelpers.ts
│   │   ├── Navigation/
│   │   │   ├── AppNavigation.tsx
│   │   │   ├── ReduxNavigation.tsx
│   │   │   └── Styles/
│   │   │       └── NavigationStyles.ts
│   │   ├── Reducers/
│   │   │   ├── CreateStore.tsx
│   │   │   ├── GithubReducers/
│   │   │   │   ├── GithubReducersTest.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── NavigationReducers/
│   │   │   │   └── index.tsx
│   │   │   ├── ScreenTrackingMiddleware.tsx
│   │   │   ├── StartupReducers/
│   │   │   │   └── index.tsx
│   │   │   └── index.ts
│   │   ├── Sagas/
│   │   │   ├── GithubSagas/
│   │   │   │   ├── GithubSagaTest.ts
│   │   │   │   └── index.ts
│   │   │   ├── StartupSagas/
│   │   │   │   ├── StartupSagaTest.ts
│   │   │   │   └── index.ts
│   │   │   └── index.ts
│   │   ├── Services/
│   │   │   ├── Api.ts
│   │   │   ├── ExamplesRegistry.tsx
│   │   │   ├── FixtureAPITest.tsx
│   │   │   ├── FixtureApi.tsx
│   │   │   ├── GithubApi.tsx
│   │   │   ├── ImmutablePersistenceTransform.tsx
│   │   │   └── RehydrationServices.tsx
│   │   ├── Themes/
│   │   │   ├── ApplicationStyles.ts
│   │   │   ├── Colors.ts
│   │   │   ├── Fonts.ts
│   │   │   ├── Images.ts
│   │   │   ├── Metrics.ts
│   │   │   ├── README.md
│   │   │   └── index.ts
│   │   └── Transforms/
│   │       ├── ConvertFromKelvin.ts
│   │       └── README.md
│   ├── README.md
│   ├── Tests/
│   │   ├── Setup.tsx.ejs
│   │   └── StoriesTest.ts
│   ├── ignite.json.ejs
│   ├── index.js.ejs
│   ├── package.json.ejs
│   ├── rn-cli.config.js
│   ├── storybook/
│   │   ├── addons.js
│   │   ├── index.js
│   │   └── storybook.ejs
│   ├── tsconfig.json
│   ├── tslint.json
│   └── types/
│       ├── @storybook/
│       │   └── react-native.d.ts
│       └── reduxsauce/
│           └── index.d.ts
├── boilerplate.js
├── commands/
│   ├── component.js
│   ├── container.js
│   ├── list.js
│   ├── reducers.js
│   ├── saga.js
│   └── screen.js
├── ignite.json
├── lib/
│   ├── patterns.js
│   └── react-native-version.js
├── options.js
├── package.json
├── plugin.js
├── readme.md
├── templates/
│   ├── component-index.ejs
│   ├── component-story.ejs
│   ├── component-style.ejs
│   ├── component-test-jest.ejs
│   ├── component.ejs
│   ├── container-style.ejs
│   ├── container.ejs
│   ├── flatlist-grid-style.ejs
│   ├── flatlist-grid.ejs
│   ├── flatlist-sections.ejs
│   ├── flatlist.ejs
│   ├── listview-grid-style.ejs
│   ├── reducers-test-jest.ejs
│   ├── reducers.ejs
│   ├── saga-test-jest.ejs
│   ├── saga.ejs
│   ├── screen-index.ejs
│   ├── screen-style.ejs
│   └── screen.ejs
├── test/
│   ├── generators-integration.test.js
│   ├── interface.test.js
│   └── react-native-version.test.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (48 symbols across 21 files)

FILE: boilerplate.js
  function install (line 26) | async function install (context) {

FILE: boilerplate/App/Components/AlertMessage/AlertMessage.tsx
  type Props (line 5) | interface Props {

FILE: boilerplate/App/Components/DrawerButton/DrawerButton.tsx
  type Props (line 21) | interface Props {

FILE: boilerplate/App/Components/FullButton/FullButton.tsx
  type Props (line 20) | interface Props {

FILE: boilerplate/App/Components/RoundedButton/RoundedButton.tsx
  type Props (line 21) | interface Props {

FILE: boilerplate/App/Config/ReduxPersist.ts
  constant REDUX_PERSIST (line 5) | const REDUX_PERSIST = {

FILE: boilerplate/App/Containers/App.tsx
  class App (line 21) | class App extends React.Component {
    method render (line 22) | public render() {

FILE: boilerplate/App/Containers/LaunchScreen/LaunchScreen.tsx
  class LaunchScreen (line 10) | class LaunchScreen extends Component {
    method render (line 11) | public render() {

FILE: boilerplate/App/Containers/RootContainer/RootContainer.tsx
  type Props (line 11) | interface Props {
  type State (line 15) | interface State {
  class RootContainer (line 19) | class RootContainer extends React.Component<Props, State> {
    method componentDidMount (line 20) | public componentDidMount() {
    method render (line 27) | public render() {

FILE: boilerplate/App/Lib/ReduxHelpers.ts
  type FluxActionCreator (line 4) | type FluxActionCreator<T extends string, AC extends (...args: any[]) => ...
  type ActionCreatorMap (line 6) | type ActionCreatorMap<T> = {
  type ReducerMap (line 10) | type ReducerMap<A, S> = {
  function mapReducers (line 14) | function mapReducers<S, R extends ReducersMapObject>(

FILE: boilerplate/App/Navigation/ReduxNavigation.tsx
  function ReduxNavigation (line 7) | function ReduxNavigation(props) {

FILE: boilerplate/App/Reducers/GithubReducers/index.tsx
  type RequestParams (line 7) | interface RequestParams {username: string; }
  type SuccessParams (line 8) | interface SuccessParams {avatar: string; }
  type GithubState (line 19) | interface GithubState {
  type GithubAction (line 26) | type GithubAction = PayloadAction<string, GithubState>;
  type ImmutableGithubState (line 28) | type ImmutableGithubState = SI.ImmutableObject<GithubState>;
  constant INITIAL_STATE (line 32) | const INITIAL_STATE: ImmutableGithubState = SI.from({

FILE: boilerplate/App/Reducers/NavigationReducers/index.tsx
  type NavigationState (line 4) | type NavigationState = NavigationState;

FILE: boilerplate/App/Reducers/index.ts
  type State (line 14) | interface State {

FILE: boilerplate/App/Services/GithubApi.tsx
  type GithubApi (line 4) | interface GithubApi {
  type GithubUser (line 10) | interface GithubUser {
  type GithubResponse (line 31) | interface GithubResponse {

FILE: boilerplate/rn-cli.config.js
  method getTransformModulePath (line 2) | getTransformModulePath() {
  method getSourceExts (line 5) | getSourceExts() {

FILE: boilerplate/types/@storybook/react-native.d.ts
  type Renderable (line 11) | type Renderable = React.ComponentType | JSX.Element;
  type RenderFunction (line 12) | type RenderFunction = () => Renderable;
  type StoryDecorator (line 14) | type StoryDecorator = (story: RenderFunction, context: { kind: string, s...
  type Story (line 16) | interface Story {
  type StoryObject (line 28) | interface StoryObject {
  type StoryBucket (line 33) | interface StoryBucket {

FILE: boilerplate/types/reduxsauce/index.d.ts
  type ActionTypes (line 2) | interface ActionTypes {
  type ActionConfig (line 6) | interface ActionConfig {
  type ActionCreator (line 10) | type ActionCreator = (...args: any[]) => Action;
  type ActionCreators (line 12) | interface ActionCreators {

FILE: lib/react-native-version.js
  constant REACT_NATIVE_VERSION (line 4) | const REACT_NATIVE_VERSION = '0.51.0'

FILE: test/generators-integration.test.js
  constant IGNITE (line 5) | const IGNITE = 'ignite'
  constant APP (line 6) | const APP = 'IntegrationTest'
  constant BOILERPLATE (line 7) | const BOILERPLATE = `${__dirname}/..`

FILE: test/react-native-version.test.js
  constant DEFAULT (line 5) | const DEFAULT = boilerplate.REACT_NATIVE_VERSION
Condensed preview — 129 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (160K chars).
[
  {
    "path": ".gitignore",
    "chars": 251,
    "preview": "# OSX\n#\n.DS_Store\nnpm-debug.log\nnpm-debug.log*\ncoverage\n.nyc_output\nyarn.lock\nlerna-debug.log\nnode_modules\n.vscode/*\n!.v"
  },
  {
    "path": "boilerplate/.babelrc",
    "chars": 122,
    "preview": "{\n  \"presets\": [\"react-native\"],\n  \"env\": {\n    \"production\": {\n      \"plugins\": [\"ignite-ignore-reactotron\"]\n    }\n  }\n"
  },
  {
    "path": "boilerplate/.editorconfig",
    "chars": 311,
    "preview": "# EditorConfig is awesome: http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with"
  },
  {
    "path": "boilerplate/App/Components/AlertMessage/AlertMessage.story.tsx",
    "chars": 467,
    "preview": "import { storiesOf } from \"@storybook/react-native\";\nimport * as React from \"react\";\n\nimport AlertMessage from \"./AlertM"
  },
  {
    "path": "boilerplate/App/Components/AlertMessage/AlertMessage.tsx",
    "chars": 567,
    "preview": "import * as React from \"react\";\nimport { Text, View, ViewStyle } from \"react-native\";\nimport styles from \"./AlertMessage"
  },
  {
    "path": "boilerplate/App/Components/AlertMessage/AlertMessageStyles.ts",
    "chars": 580,
    "preview": "import { StyleSheet } from \"react-native\";\nimport { Colors, Fonts, Metrics } from \"../../Themes/\";\n\nexport default Style"
  },
  {
    "path": "boilerplate/App/Components/AlertMessage/AlertMessageTest.tsx",
    "chars": 800,
    "preview": "/// <reference types=\"@types/jest\" />\nimport * as React from \"react\";\nimport \"react-native\";\nimport * as renderer from \""
  },
  {
    "path": "boilerplate/App/Components/AlertMessage/index.ts",
    "chars": 73,
    "preview": "import AlertMessage from \"./AlertMessage\";\n\nexport default AlertMessage;\n"
  },
  {
    "path": "boilerplate/App/Components/DrawerButton/DrawerButton.story.tsx",
    "chars": 429,
    "preview": "import { storiesOf } from \"@storybook/react-native\";\nimport * as React from \"react\";\nimport { View } from \"react-native\""
  },
  {
    "path": "boilerplate/App/Components/DrawerButton/DrawerButton.tsx",
    "chars": 987,
    "preview": "import * as React from \"react\";\nimport { Text, TouchableOpacity } from \"react-native\";\nimport ExamplesRegistry from \"../"
  },
  {
    "path": "boilerplate/App/Components/DrawerButton/DrawerButtonStyles.ts",
    "chars": 249,
    "preview": "import Colors from \"../../Themes/Colors\";\nimport Fonts from \"../../Themes/Fonts\";\nimport Metrics from \"../../Themes/Metr"
  },
  {
    "path": "boilerplate/App/Components/DrawerButton/DrawerButtonTest.tsx",
    "chars": 709,
    "preview": "/// <reference types=\"@types/jest\" />\nimport { shallow } from \"enzyme\";\nimport * as React from \"react\";\nimport \"react-na"
  },
  {
    "path": "boilerplate/App/Components/DrawerButton/index.ts",
    "chars": 73,
    "preview": "import DrawerButton from \"./DrawerButton\";\n\nexport default DrawerButton;\n"
  },
  {
    "path": "boilerplate/App/Components/FullButton/FullButton.story.tsx",
    "chars": 361,
    "preview": "import { storiesOf } from \"@storybook/react-native\";\nimport * as React from \"react\";\n\nimport FullButton from \"./FullButt"
  },
  {
    "path": "boilerplate/App/Components/FullButton/FullButton.tsx",
    "chars": 1058,
    "preview": "import * as React from \"react\";\nimport { Text, TouchableOpacity, ViewStyle } from \"react-native\";\nimport ExamplesRegistr"
  },
  {
    "path": "boilerplate/App/Components/FullButton/FullButtonStyles.ts",
    "chars": 517,
    "preview": "import { StyleSheet } from \"react-native\";\nimport Colors from \"../../Themes/Colors\";\nimport Fonts from \"../../Themes/Fon"
  },
  {
    "path": "boilerplate/App/Components/FullButton/FullButtonTest.tsx",
    "chars": 767,
    "preview": "/// <reference types=\"@types/jest\" />\nimport { shallow } from \"enzyme\";\nimport * as React from \"react\";\nimport \"react-na"
  },
  {
    "path": "boilerplate/App/Components/FullButton/index.ts",
    "chars": 67,
    "preview": "import FullButton from \"./FullButton\";\n\nexport default FullButton;\n"
  },
  {
    "path": "boilerplate/App/Components/README.md",
    "chars": 361,
    "preview": "### Components Folder\nAll components are stored and organized here\n\n\"In an ideal world, most of your components would be"
  },
  {
    "path": "boilerplate/App/Components/RoundedButton/RoundedButton.story.tsx",
    "chars": 376,
    "preview": "import { storiesOf } from \"@storybook/react-native\";\nimport * as React from \"react\";\n\nimport RoundedButton from \"./Round"
  },
  {
    "path": "boilerplate/App/Components/RoundedButton/RoundedButton.tsx",
    "chars": 1152,
    "preview": "import * as React from \"react\";\nimport { Text, TouchableOpacity } from \"react-native\";\nimport ExamplesRegistry from \"../"
  },
  {
    "path": "boilerplate/App/Components/RoundedButton/RoundedButtonStyles.ts",
    "chars": 573,
    "preview": "import { StyleSheet } from \"react-native\";\nimport Colors from \"../../Themes/Colors\";\nimport Fonts from \"../../Themes/Fon"
  },
  {
    "path": "boilerplate/App/Components/RoundedButton/RoundedButtonTest.tsx",
    "chars": 994,
    "preview": "/// <reference types=\"@types/jest\" />\nimport { shallow } from \"enzyme\";\nimport * as React from \"react\";\nimport \"react-na"
  },
  {
    "path": "boilerplate/App/Components/RoundedButton/index.ts",
    "chars": 76,
    "preview": "import RoundedButton from \"./RoundedButton\";\n\nexport default RoundedButton;\n"
  },
  {
    "path": "boilerplate/App/Components/Stories.tsx",
    "chars": 174,
    "preview": "import \"./AlertMessage/AlertMessage.story\";\nimport \"./DrawerButton/DrawerButton.story\";\nimport \"./FullButton/FullButton."
  },
  {
    "path": "boilerplate/App/Config/AppConfig.ts",
    "chars": 137,
    "preview": "// Simple React Native specific changes\n\nexport default {\n  // font scaling override - RN default is on\n  allowTextFontS"
  },
  {
    "path": "boilerplate/App/Config/DebugConfig.js",
    "chars": 261,
    "preview": "/**\n * This is a .js file because the Ignite scripts need to find it\n */\nexport default {\n  showDevScreens: __DEV__,\n  u"
  },
  {
    "path": "boilerplate/App/Config/README.md",
    "chars": 256,
    "preview": "### Config Folder\nAll application specific configuration falls in this folder.\n\n`AppConfig.js` - production values.\n`Deb"
  },
  {
    "path": "boilerplate/App/Config/ReactotronConfig.ts",
    "chars": 788,
    "preview": "import Reactotron from \"reactotron-react-native\";\nimport { reactotronRedux as reduxPlugin } from \"reactotron-redux\";\nimp"
  },
  {
    "path": "boilerplate/App/Config/ReduxPersist.ts",
    "chars": 699,
    "preview": "import { AsyncStorage } from \"react-native\";\nimport immutablePersistenceTransform from \"../Services/ImmutablePersistence"
  },
  {
    "path": "boilerplate/App/Config/index.ts",
    "chars": 495,
    "preview": "import { Text } from \"react-native\";\nimport AppConfig from \"./AppConfig\";\nimport DebugConfig from \"./DebugConfig\";\n\n// A"
  },
  {
    "path": "boilerplate/App/Containers/App.tsx",
    "chars": 918,
    "preview": "import * as React from \"react\";\nimport { Provider } from \"react-redux\";\nimport Reactotron from \"reactotron-react-native\""
  },
  {
    "path": "boilerplate/App/Containers/LaunchScreen/LaunchScreen.tsx",
    "chars": 1219,
    "preview": "import React, { Component } from \"react\";\nimport { Image, ScrollView, Text, View } from \"react-native\";\nimport Devscreen"
  },
  {
    "path": "boilerplate/App/Containers/LaunchScreen/LaunchScreenStyles.ts",
    "chars": 428,
    "preview": "import { StyleSheet } from \"react-native\";\nimport { ApplicationStyles, Metrics } from \"../../Themes/index\";\n\nexport defa"
  },
  {
    "path": "boilerplate/App/Containers/LaunchScreen/index.ts",
    "chars": 73,
    "preview": "import LaunchScreen from \"./LaunchScreen\";\n\nexport default LaunchScreen;\n"
  },
  {
    "path": "boilerplate/App/Containers/README.md",
    "chars": 991,
    "preview": "### Containers Folder\nA container is what they call a \"Smart Component\" in Redux.  It is a component\nwhich knows about R"
  },
  {
    "path": "boilerplate/App/Containers/RootContainer/RootContainer.tsx",
    "chars": 1010,
    "preview": "import * as React from \"react\";\nimport { StatusBar, View } from \"react-native\";\nimport { connect } from \"react-redux\";\ni"
  },
  {
    "path": "boilerplate/App/Containers/RootContainer/RootContainerStyles.ts",
    "chars": 507,
    "preview": "import {StyleSheet, TextStyle, ViewStyle } from \"react-native\";\nimport {Colors, Fonts, Metrics} from \"../../Themes/index"
  },
  {
    "path": "boilerplate/App/Containers/RootContainer/index.ts",
    "chars": 76,
    "preview": "import RootContainer from \"./RootContainer\";\n\nexport default RootContainer;\n"
  },
  {
    "path": "boilerplate/App/Fixtures/README.md",
    "chars": 283,
    "preview": "### Fixtures folder\nAll key API responses are housed here.\n\nThese API responses can be used for several reasons.  _E.G._"
  },
  {
    "path": "boilerplate/App/Fixtures/gantman.json",
    "chars": 7470,
    "preview": "{\n  \"total_count\": 7,\n  \"incomplete_results\": false,\n  \"items\": [\n    {\n      \"login\": \"GantMan\",\n      \"id\": 997157,\n  "
  },
  {
    "path": "boilerplate/App/Fixtures/rateLimit.json",
    "chars": 281,
    "preview": "{\n  \"resources\": {\n    \"core\": {\n      \"limit\": 60,\n      \"remaining\": 42,\n      \"reset\": 1488126913\n    },\n    \"search\""
  },
  {
    "path": "boilerplate/App/Fixtures/root.json",
    "chars": 2164,
    "preview": "{\n  \"current_user_url\": \"https://api.github.com/user\",\n  \"current_user_authorizations_html_url\": \"https://github.com/set"
  },
  {
    "path": "boilerplate/App/Fixtures/skellock.json",
    "chars": 1111,
    "preview": "{\n  \"total_count\": 1,\n  \"incomplete_results\": false,\n  \"items\": [\n    {\n      \"login\": \"skellock\",\n      \"id\": 68273,\n  "
  },
  {
    "path": "boilerplate/App/Images/README.md",
    "chars": 56,
    "preview": "### Images folder\nHolds all images for the applications."
  },
  {
    "path": "boilerplate/App/Lib/README.md",
    "chars": 455,
    "preview": "# Lib\n\nAt first glance, this could appear to be a \"miscellaneous\" folder, but we recommend that you treat this as provin"
  },
  {
    "path": "boilerplate/App/Lib/ReduxHelpers.ts",
    "chars": 1085,
    "preview": "import { ActionCreator, AnyAction, Reducer, ReducersMapObject } from \"redux\";\nimport { createAction, EmptyAction, FluxSt"
  },
  {
    "path": "boilerplate/App/Navigation/AppNavigation.tsx",
    "chars": 457,
    "preview": "import { StackNavigator } from \"react-navigation\";\nimport LaunchScreen from \"../Containers/LaunchScreen\";\n\nimport styles"
  },
  {
    "path": "boilerplate/App/Navigation/ReduxNavigation.tsx",
    "chars": 549,
    "preview": "import * as React from \"react\";\nimport * as ReactNavigation from \"react-navigation\";\nimport { connect } from \"react-redu"
  },
  {
    "path": "boilerplate/App/Navigation/Styles/NavigationStyles.ts",
    "chars": 185,
    "preview": "import { StyleSheet } from \"react-native\";\nimport { Colors } from \"../../Themes/index\";\n\nexport default StyleSheet.creat"
  },
  {
    "path": "boilerplate/App/Reducers/CreateStore.tsx",
    "chars": 1534,
    "preview": "import Reactotron from \"reactotron-react-native\";\nimport { applyMiddleware, compose, createStore, Reducer } from \"redux\""
  },
  {
    "path": "boilerplate/App/Reducers/GithubReducers/GithubReducersTest.tsx",
    "chars": 849,
    "preview": "/// <reference types=\"@types/jest\" />\nimport { GithubActions, GithubReducer, INITIAL_STATE } from \"./index\";\n\ntest(\"requ"
  },
  {
    "path": "boilerplate/App/Reducers/GithubReducers/index.tsx",
    "chars": 2250,
    "preview": "import { Action, AnyAction, Reducer } from \"redux\";\nimport * as SI from \"seamless-immutable\";\nimport { createAction, Pay"
  },
  {
    "path": "boilerplate/App/Reducers/NavigationReducers/index.tsx",
    "chars": 373,
    "preview": "import { NavigationAction, NavigationState } from \"react-navigation\";\nimport AppNavigation from \"../../Navigation/AppNav"
  },
  {
    "path": "boilerplate/App/Reducers/ScreenTrackingMiddleware.tsx",
    "chars": 1104,
    "preview": "import { NavigationActions } from \"react-navigation\";\nimport Reactotron from \"reactotron-react-native\";\n\n// gets the cur"
  },
  {
    "path": "boilerplate/App/Reducers/StartupReducers/index.tsx",
    "chars": 208,
    "preview": "import { createAction } from \"typesafe-actions\";\n\n/* ------------- Types and Action Creators ------------- */\n\nconst act"
  },
  {
    "path": "boilerplate/App/Reducers/index.ts",
    "chars": 1069,
    "preview": "/// <reference types=\"@types/webpack-env\" />\nimport { combineReducers } from \"redux\";\nimport root from \"../Sagas\";\nimpor"
  },
  {
    "path": "boilerplate/App/Sagas/GithubSagas/GithubSagaTest.ts",
    "chars": 1433,
    "preview": "/// <reference types=\"@types/jest\" />\nimport { path } from \"ramda\";\nimport { call, put } from \"redux-saga/effects\";\nimpo"
  },
  {
    "path": "boilerplate/App/Sagas/GithubSagas/index.ts",
    "chars": 849,
    "preview": "import { ApiResponse } from \"apisauce\";\nimport { path } from \"ramda\";\nimport { SagaIterator } from \"redux-saga\";\nimport "
  },
  {
    "path": "boilerplate/App/Sagas/StartupSagas/StartupSagaTest.ts",
    "chars": 471,
    "preview": "/// <reference types=\"@types/jest\" />\nimport { put, select } from \"redux-saga/effects\";\nimport { GithubActions } from \"."
  },
  {
    "path": "boilerplate/App/Sagas/StartupSagas/index.ts",
    "chars": 1539,
    "preview": "import { is } from \"ramda\";\nimport Reactotron from \"reactotron-react-native\";\nimport { Action } from \"redux\";\nimport { S"
  },
  {
    "path": "boilerplate/App/Sagas/index.ts",
    "chars": 1122,
    "preview": "import { all, takeLatest } from \"redux-saga/effects\";\nimport { getType } from \"typesafe-actions\";\n\nimport DebugConfig fr"
  },
  {
    "path": "boilerplate/App/Services/Api.ts",
    "chars": 113,
    "preview": "// This is for the devscreens\nimport {createAPI} from \"./GithubApi\";\n\nexport default {\n    create: createAPI,\n};\n"
  },
  {
    "path": "boilerplate/App/Services/ExamplesRegistry.tsx",
    "chars": 1549,
    "preview": "import R from \"ramda\";\nimport * as React from \"react\";\nimport { Text, View } from \"react-native\";\nimport DebugConfig fro"
  },
  {
    "path": "boilerplate/App/Services/FixtureAPITest.tsx",
    "chars": 1300,
    "preview": "/// <reference types=\"@types/jest\" />\nimport * as R from \"ramda\";\nimport FixtureAPI from \"../../App/Services/FixtureApi\""
  },
  {
    "path": "boilerplate/App/Services/FixtureApi.tsx",
    "chars": 957,
    "preview": "import {ApiResponse} from \"apisauce\";\nimport {GithubApi, GithubResponse} from \"./GithubApi\";\n\nexport default {\n  // Func"
  },
  {
    "path": "boilerplate/App/Services/GithubApi.tsx",
    "chars": 2554,
    "preview": "// a library to wrap and simplify api calls\nimport {ApiResponse, create as apicreate} from \"apisauce\";\n\nexport interface"
  },
  {
    "path": "boilerplate/App/Services/ImmutablePersistenceTransform.tsx",
    "chars": 790,
    "preview": "import R from \"ramda\";\nimport * as SeamlessImmutable from \"seamless-immutable\";\n\n// is this object already Immutable?\nco"
  },
  {
    "path": "boilerplate/App/Services/RehydrationServices.tsx",
    "chars": 1273,
    "preview": "import { AsyncStorage } from \"react-native\";\nimport { persistStore } from \"redux-persist\";\nimport DebugConfig from \"../C"
  },
  {
    "path": "boilerplate/App/Themes/ApplicationStyles.ts",
    "chars": 2115,
    "preview": "import { TextStyle, ViewStyle } from \"react-native\";\n\nimport Colors from \"./Colors\";\nimport Fonts from \"./Fonts\";\nimport"
  },
  {
    "path": "boilerplate/App/Themes/Colors.ts",
    "chars": 636,
    "preview": "const colors = {\n  background: \"#1F0808\",\n  clear: \"rgba(0,0,0,0)\",\n  facebook: \"#3b5998\",\n  transparent: \"rgba(0,0,0,0)"
  },
  {
    "path": "boilerplate/App/Themes/Fonts.ts",
    "chars": 845,
    "preview": "const type = {\n  base: \"Avenir-Book\",\n  bold: \"Avenir-Black\",\n  emphasis: \"HelveticaNeue-Italic\",\n};\n\nconst size = {\n  h"
  },
  {
    "path": "boilerplate/App/Themes/Images.ts",
    "chars": 1140,
    "preview": "// leave off @2x/@3x\nconst images = {\n  logo: require(\"../Images/ir.png\"),\n  clearLogo: require(\"../Images/top_logo.png\""
  },
  {
    "path": "boilerplate/App/Themes/Metrics.ts",
    "chars": 684,
    "preview": "import {Dimensions, Platform} from \"react-native\";\n\nconst { width, height } = Dimensions.get(\"window\");\n\n// Used via Met"
  },
  {
    "path": "boilerplate/App/Themes/README.md",
    "chars": 93,
    "preview": "### Themes Folder\nApplication specific themes\n* Base Styles\n* Fonts\n* Metrics\n* Colors\n\netc.\n"
  },
  {
    "path": "boilerplate/App/Themes/index.ts",
    "chars": 240,
    "preview": "import ApplicationStyles from \"./ApplicationStyles\";\nimport Colors from \"./Colors\";\nimport Fonts from \"./Fonts\";\nimport "
  },
  {
    "path": "boilerplate/App/Transforms/ConvertFromKelvin.ts",
    "chars": 155,
    "preview": "export default (kelvin: number) => {\n  const celsius = kelvin - 273.15;\n  const fahrenheit = (celsius * 1.8000) + 32;\n\n "
  },
  {
    "path": "boilerplate/App/Transforms/README.md",
    "chars": 550,
    "preview": "# Transforms\n\nA common pattern when working with APIs is to change data to play nice between your app & the API.  \n\nWe'v"
  },
  {
    "path": "boilerplate/README.md",
    "chars": 1425,
    "preview": "#  <%= props.name %>\n\n* TypeScript React Native App Utilizing [Ignite](https://github.com/infinitered/ignite)\n\n## :arrow"
  },
  {
    "path": "boilerplate/Tests/Setup.tsx.ejs",
    "chars": 770,
    "preview": "/// <reference types=\"@types/jest\" />\n// Mock your external modules here if needed\n<%_ if (props.i18n === 'react-native-"
  },
  {
    "path": "boilerplate/Tests/StoriesTest.ts",
    "chars": 77,
    "preview": "import initStoryshots from \"@storybook/addon-storyshots\";\n\ninitStoryshots();\n"
  },
  {
    "path": "boilerplate/ignite.json.ejs",
    "chars": 136,
    "preview": "{\n  \"createdWith\": \"<%= props.igniteVersion %>\",\n  \"examples\": \"classic\",\n  \"navigation\": \"react-navigation\",\n  \"askToOv"
  },
  {
    "path": "boilerplate/index.js.ejs",
    "chars": 187,
    "preview": "import \"./App/Config/ReactotronConfig\"\nimport { AppRegistry } from 'react-native';\nimport App from \"./App/Containers/App"
  },
  {
    "path": "boilerplate/package.json.ejs",
    "chars": 4022,
    "preview": "{\n  \"version\": \"0.0.1\",\n  \"scripts\": {\n    \"start\": \"node node_modules/react-native/local-cli/cli.js start\",\n    \"test\":"
  },
  {
    "path": "boilerplate/rn-cli.config.js",
    "chars": 186,
    "preview": "module.exports = {  \n    getTransformModulePath() {\n      return require.resolve('react-native-typescript-transformer')\n"
  },
  {
    "path": "boilerplate/storybook/addons.js",
    "chars": 84,
    "preview": "import '@storybook/addon-actions/register'\nimport '@storybook/addon-links/register'\n"
  },
  {
    "path": "boilerplate/storybook/index.js",
    "chars": 66,
    "preview": "import StorybookUI from './storybook'\n\nexport default StorybookUI\n"
  },
  {
    "path": "boilerplate/storybook/storybook.ejs",
    "chars": 499,
    "preview": "import { AppRegistry } from 'react-native'\nimport { getStorybookUI, configure } from '@storybook/react-native'\n\n// impor"
  },
  {
    "path": "boilerplate/tsconfig.json",
    "chars": 4795,
    "preview": "{\n  \"compilerOptions\": {\n    /* Basic Options */\n    \"target\": \"ESNEXT\",                          /* Specify ECMAScript "
  },
  {
    "path": "boilerplate/tslint.json",
    "chars": 290,
    "preview": "{\n    \"extends\": [\n        \"tslint:latest\", \"tslint-react\"\n    ],\n    \"rules\": {\n        \"interface-name\": false,\n      "
  },
  {
    "path": "boilerplate/types/@storybook/react-native.d.ts",
    "chars": 1248,
    "preview": "// Type definitions for @storybook/react 3.0\n// Project: https://github.com/storybooks/storybook\n// Definitions by: Josc"
  },
  {
    "path": "boilerplate/types/reduxsauce/index.d.ts",
    "chars": 1341,
    "preview": "import { Action, AnyAction, Reducer, ReducersMapObject } from \"redux\";\r\nexport interface ActionTypes {\r\n    [key: string"
  },
  {
    "path": "boilerplate.js",
    "chars": 9924,
    "preview": "const options = require('./options')\nconst { merge, pipe, assoc, omit, __ } = require('ramda')\nconst { getReactNativeVer"
  },
  {
    "path": "commands/component.js",
    "chars": 1347,
    "preview": "// @cliDescription  Generates a stateless component, styles, and an optional test.\n\nmodule.exports = async function (con"
  },
  {
    "path": "commands/container.js",
    "chars": 1862,
    "preview": "// @cliDescription  Generates a redux smart component.\n\nconst patterns = require('../lib/patterns')\n\nmodule.exports = as"
  },
  {
    "path": "commands/list.js",
    "chars": 3965,
    "preview": "// @cliDescription  Generates a screen with a ListView/Flatlist/SectionList + walkthrough.\n\nconst patterns = require('.."
  },
  {
    "path": "commands/reducers.js",
    "chars": 871,
    "preview": "// @cliDescription  Generates a action/creator/reducer set for Redux.\n\nmodule.exports = async function (context) {\n  // "
  },
  {
    "path": "commands/saga.js",
    "chars": 841,
    "preview": "// @cliDescription  Generates a saga with an optional test.\n\nmodule.exports = async function (context) {\n  // grab some "
  },
  {
    "path": "commands/screen.js",
    "chars": 2047,
    "preview": "// @cliDescription  Generates an opinionated container.\n\nconst patterns = require('../lib/patterns')\n\nmodule.exports = a"
  },
  {
    "path": "ignite.json",
    "chars": 113,
    "preview": "{\n  \"generators\": [\n    \"component\",\n    \"container\",\n    \"list\",\n    \"reducers\",\n    \"saga\",\n    \"screen\"\n  ]\n}\n"
  },
  {
    "path": "lib/patterns.js",
    "chars": 241,
    "preview": "const constants = {\n  PATTERN_IMPORTS: 'imports',\n  PATTERN_ROUTES: 'routes'\n}\n\nmodule.exports = {\n  constants,\n  [const"
  },
  {
    "path": "lib/react-native-version.js",
    "chars": 906,
    "preview": "const { pathOr, is } = require('ramda')\n\n// the default React Native version for this boilerplate\nconst REACT_NATIVE_VER"
  },
  {
    "path": "options.js",
    "chars": 1214,
    "preview": "/**\n * The questions to ask during the install process.\n */\nconst questions = [\n  {\n    name: 'dev-screens',\n    message"
  },
  {
    "path": "package.json",
    "chars": 1575,
    "preview": "{\n  \"name\": \"ignite-typescript-boilerplate\",\n  \"description\": \"TypeScript boilerplate for React Native.\",\n  \"license\": \""
  },
  {
    "path": "plugin.js",
    "chars": 415,
    "preview": "// Ignite CLI plugin for Ts\n// ----------------------------------------------------------------------------\n\n\nconst add "
  },
  {
    "path": "readme.md",
    "chars": 7627,
    "preview": "## Ignite TypeScript Boilerplate for React Native\n\n### The easiest way to develop React Native apps in TypeScript.\nGet u"
  },
  {
    "path": "templates/component-index.ejs",
    "chars": 88,
    "preview": "import <%= props.name %> from \"./<%= props.name %>\";\n\nexport default <%= props.name %>;\n"
  },
  {
    "path": "templates/component-story.ejs",
    "chars": 236,
    "preview": "import { storiesOf } from \"@storybook/react-native\";\nimport * as React from \"react\";\n\nimport <%= props.name %> from \"./<"
  },
  {
    "path": "templates/component-style.ejs",
    "chars": 116,
    "preview": "import { StyleSheet } from \"react-native\";\n\nexport default StyleSheet.create({\n  container: {\n    flex: 1,\n  },\n});\n"
  },
  {
    "path": "templates/component-test-jest.ejs",
    "chars": 386,
    "preview": "/// <reference types=\"@types/jest\" />\nimport * as React from \"react\";\nimport { <%= props.name %> } from \"./<%= props.nam"
  },
  {
    "path": "templates/component.ejs",
    "chars": 414,
    "preview": "import * as React from \"react\";\nimport { Text, View } from \"react-native\";\nimport styles from \"./<%= props.name %>Style\""
  },
  {
    "path": "templates/container-style.ejs",
    "chars": 242,
    "preview": "import { StyleSheet } from \"react-native\";\nimport { Colors, Metrics } from \"../../Themes/\";\n\nexport default StyleSheet.c"
  },
  {
    "path": "templates/container.ejs",
    "chars": 1241,
    "preview": "import * as React from \"react\";\nimport { Text, View } from \"react-native\";\nimport { connect } from \"react-redux\";\nimport"
  },
  {
    "path": "templates/flatlist-grid-style.ejs",
    "chars": 755,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles, Metrics, Colors } from '../../../Themes'\n\nexport d"
  },
  {
    "path": "templates/flatlist-grid.ejs",
    "chars": 2976,
    "preview": "import * as React from \"react\";\nimport { View, Text, FlatList } from \"react-native\";\nimport { connect } from \"react-redu"
  },
  {
    "path": "templates/flatlist-sections.ejs",
    "chars": 4458,
    "preview": "import * as React from \"react\";\nimport { View, SectionList, Text } from \"react-native\";\n\n// More info here: https://face"
  },
  {
    "path": "templates/flatlist.ejs",
    "chars": 2912,
    "preview": "import * as React from \"react\";\nimport { View, Text, FlatList } from \"react-native\";\n\n// More info here: https://faceboo"
  },
  {
    "path": "templates/listview-grid-style.ejs",
    "chars": 1005,
    "preview": "import { StyleSheet } from \"react-native\";\nimport { ApplicationStyles, Metrics, Colors } from \"../../../Themes\";\n\nexport"
  },
  {
    "path": "templates/reducers-test-jest.ejs",
    "chars": 577,
    "preview": "import <%= props.name %>Actions, <%= props.name %>Reducer as reducer, INITIAL_STATE from \"./<%= props.name %>Reducers\";\n"
  },
  {
    "path": "templates/reducers.ejs",
    "chars": 2448,
    "preview": "import { Action, AnyAction, Reducer } from \"redux\";\nimport * as SI from \"seamless-immutable\";\nimport { createAction, Pay"
  },
  {
    "path": "templates/saga-test-jest.ejs",
    "chars": 1489,
    "preview": "/* ***********************************************************\n* Wiring Instructions\n* To make this test work, you'll ne"
  },
  {
    "path": "templates/saga.ejs",
    "chars": 1545,
    "preview": "/* ***********************************************************\n* A short word on how to use this automagically generated"
  },
  {
    "path": "templates/screen-index.ejs",
    "chars": 88,
    "preview": "import <%= props.name %> from \"./<%= props.name %>\";\n\nexport default <%= props.name %>;\n"
  },
  {
    "path": "templates/screen-style.ejs",
    "chars": 242,
    "preview": "import { StyleSheet } from \"react-native\";\nimport { Colors, Metrics } from \"../../Themes/\";\n\nexport default StyleSheet.c"
  },
  {
    "path": "templates/screen.ejs",
    "chars": 1782,
    "preview": "import * as React from \"react\";\nimport { Alert, Image, Text, TouchableOpacity, View } from \"react-native\";\nimport Icon f"
  },
  {
    "path": "test/generators-integration.test.js",
    "chars": 4865,
    "preview": "const execa = require('execa')\nconst jetpack = require('fs-jetpack')\nconst tempy = require('tempy')\n\nconst IGNITE = 'ign"
  },
  {
    "path": "test/interface.test.js",
    "chars": 320,
    "preview": "const boilerplate = require('../boilerplate')\nconst plugin = require('../plugin')\n\ntest('boilerplate interface', async ("
  },
  {
    "path": "test/react-native-version.test.js",
    "chars": 1581,
    "preview": "const boilerplate = require('../lib/react-native-version')\n\n// grab a few things from the boilerplate module\nconst get ="
  },
  {
    "path": "tsconfig.json",
    "chars": 4866,
    "preview": "{\n    \"compilerOptions\": {\n      /* Basic Options */\n      \"target\": \"ESNEXT\",                          /* Specify ECMAS"
  }
]

About this extraction

This page contains the full source code of the aerian-studios/ignite-typescript-boilerplate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 129 files (141.2 KB), approximately 40.0k tokens, and a symbol index with 48 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!