cavendish 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +103 -0
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.editorconfig +24 -0
  5. data/.gitignore +12 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +496 -0
  8. data/.ruby-version +1 -0
  9. data/CHANGELOG.md +11 -0
  10. data/Gemfile +7 -0
  11. data/Gemfile.lock +143 -0
  12. data/Guardfile +5 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +82 -0
  15. data/Rakefile +1 -0
  16. data/bin/console +14 -0
  17. data/bin/setup +8 -0
  18. data/cavendish.gemspec +33 -0
  19. data/exe/cavendish +8 -0
  20. data/lib/cavendish.rb +13 -0
  21. data/lib/cavendish/assets/.circleci/config.yml +93 -0
  22. data/lib/cavendish/assets/.eslintrc.json +43 -0
  23. data/lib/cavendish/assets/.node-version +1 -0
  24. data/lib/cavendish/assets/App.jsx +12 -0
  25. data/lib/cavendish/assets/README.md.erb +59 -0
  26. data/lib/cavendish/assets/src/navigators/HomeNavigator.jsx +17 -0
  27. data/lib/cavendish/assets/src/screens/HomeScreen.jsx.erb +16 -0
  28. data/lib/cavendish/assets/src/screens/__specs__/HomeScreen.spec.js.erb +22 -0
  29. data/lib/cavendish/assets/src/utils/tailwindRn.js +7 -0
  30. data/lib/cavendish/assets/tailwind.config.js +8 -0
  31. data/lib/cavendish/cli.rb +53 -0
  32. data/lib/cavendish/commands/add_ci_config.rb +16 -0
  33. data/lib/cavendish/commands/add_eslint.rb +33 -0
  34. data/lib/cavendish/commands/add_react_navigation.rb +45 -0
  35. data/lib/cavendish/commands/add_readme.rb +15 -0
  36. data/lib/cavendish/commands/add_tailwind.rb +31 -0
  37. data/lib/cavendish/commands/add_testing.rb +81 -0
  38. data/lib/cavendish/commands/base.rb +9 -0
  39. data/lib/cavendish/commands/configure_git.rb +22 -0
  40. data/lib/cavendish/commands/create_expo_project.rb +20 -0
  41. data/lib/cavendish/config.rb +18 -0
  42. data/lib/cavendish/utils.rb +74 -0
  43. data/lib/cavendish/version.rb +5 -0
  44. metadata +260 -0
@@ -0,0 +1,43 @@
1
+ {
2
+ "env": {
3
+ "es6": true,
4
+ "jest": true
5
+ },
6
+ "extends": "airbnb",
7
+ "globals": {
8
+ "Atomics": "readonly",
9
+ "SharedArrayBuffer": "readonly",
10
+ "jest": true
11
+ },
12
+ "parser": "babel-eslint",
13
+ "parserOptions": {
14
+ "ecmaFeatures": {
15
+ "jsx": true
16
+ },
17
+ "ecmaVersion": 2018,
18
+ "sourceType": "module"
19
+ },
20
+ "plugins": ["react"],
21
+ "rules": {
22
+ "no-console": 2,
23
+ "react/prop-types": ["error", { "ignore": ["route", "navigation"] }],
24
+ "react/jsx-first-prop-new-line": [1, "multiline"],
25
+ "react/jsx-max-props-per-line": [1, { "maximum": 1 }],
26
+ "react/jsx-one-expression-per-line": "off",
27
+ "global-require": "off",
28
+ "import/prefer-default-export": "off",
29
+ "import/no-extraneous-dependencies": [
30
+ "error",
31
+ { "devDependencies": ["**/*.spec.js", "src/factories/**"] }
32
+ ],
33
+ "react/jsx-filename-extension": [1, { "extensions": [".spec.js", ".jsx"] }],
34
+ "react/jsx-props-no-spreading": 0
35
+ },
36
+ "settings": {
37
+ "import/resolver": {
38
+ "alias": {
39
+ "extensions": [".js", ".jsx"]
40
+ }
41
+ }
42
+ }
43
+ }
@@ -0,0 +1 @@
1
+ 14
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { NavigationContainer } from '@react-navigation/native';
3
+
4
+ import HomeNavigator from './src/navigators/HomeNavigator';
5
+
6
+ export default function App() {
7
+ return (
8
+ <NavigationContainer>
9
+ <HomeNavigator />
10
+ </NavigationContainer>
11
+ );
12
+ }
@@ -0,0 +1,59 @@
1
+ # <%= @config.human_project_name %>
2
+
3
+ This is a React Native + Expo application, initially generated using [Cavendish](https://github.com/platanus/cavendish) by Platanus.
4
+
5
+ ## Instalation and development
6
+
7
+ Assuming you've cloned the repo and that you have [Node](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/), the first thing you need to install is the Expo CLI:
8
+
9
+ ```bash
10
+ yarn global add expo-cli@4.8.x
11
+ ```
12
+
13
+ Then install the project dependencies in your local machine:
14
+
15
+ ```bash
16
+ yarn install
17
+ ```
18
+
19
+ And you're ready to go! You can now run the project with `yarn start` and scan the QR shown in console with the [Expo Go App](https://expo.dev/client). Remember that your phone and laptop have to be connected to the same wi-fi network in order for this to work!
20
+
21
+ ## Continuous Integration and deployment
22
+
23
+ The project has a setup to run tests and style guides in CircleCI. You can also run the test locally simulating the production environment using [CircleCI's method](https://circleci.com/docs/2.0/local-cli/).
24
+
25
+ The CI workflow also takes care of deploying and [publishing your application to Expo](https://docs.expo.io/workflow/publishing/). In order for this to work, you need to:
26
+
27
+ 1. Create a new account in [Expo](https://expo.dev/)
28
+ 1. [_Optional_] Create an [organization](https://docs.expo.io/accounts/account-types/#creating-new-organizations)
29
+ 1. Create an [access token](https://docs.expo.io/accounts/programmatic-access/)
30
+ 1. Configure the token as a [CircleCI environment variable](https://circleci.com/docs/2.0/env-vars/)
31
+
32
+ And you are done! Each time there is a commit in master, the pipeline will try to deploy the application.
33
+
34
+ If it succeeds, you will be able to scan and share the app QR code through `https://expo.io/@organization-or-user-name/<%= @config.project_name %>`.
35
+
36
+ ## Style Guides
37
+
38
+ Style guides are enforced through a CircleCI [job](.circleci/config.yml) with [reviewdog](https://github.com/reviewdog/reviewdog) as a reporter, using per-project dependencies and style configurations.
39
+
40
+ Please note that this reviewdog implementation requires a GitHub user token to comment on pull requests. A token can be generated [here](https://github.com/settings/tokens), and it should have at least the `repo` option checked.
41
+
42
+ The included `config.yml` assumes your CircleCI organization has a context named `org-global` with the required token under the environment variable `REVIEWDOG_GITHUB_API_TOKEN`.
43
+
44
+ The project comes bundled with configuration files available in this repository. You can add or modify rules by editing the [`.eslintrc.json`](.eslintrc.json) file.
45
+
46
+ You can (and should) use linter integrations for your text editor of choice, using the project's configuration.
47
+
48
+ ## Internal Dependencies
49
+
50
+ ### Tailwind RN
51
+ We use the TailwindCSS adaptation for React Native: [`tailwind-rn`](https://github.com/vadimdemedes/tailwind-rn), in order to reduce the friction in mobile apps styling.
52
+
53
+ If you need to add custom styles, make sure you follow the instructions given in the package's README.
54
+
55
+ ### React Navigation
56
+ We use [React Navigation](https://reactnavigation.org/)@<%= Cavendish::REACT_NAVIGATION_VERSION %> to handle the shown screens and navigation interactions of the application.
57
+
58
+ ### Testing
59
+ We use [Jest](https://jestjs.io/) and [React Native Testing Library](https://testing-library.com/docs/react-native-testing-library/intro/) in order to test the components of this app.
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { createStackNavigator } from '@react-navigation/stack';
3
+
4
+ import HomeScreen from '../screens/HomeScreen';
5
+
6
+ const Stack = createStackNavigator();
7
+
8
+ export default function HomeNavigator() {
9
+ return (
10
+ <Stack.Navigator>
11
+ <Stack.Screen
12
+ name="Home"
13
+ component={HomeScreen}
14
+ />
15
+ </Stack.Navigator>
16
+ );
17
+ }
@@ -0,0 +1,16 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text } from 'react-native';
3
+
4
+ import tailwind from '../utils/tailwindRn';
5
+
6
+ export default function HomeScreen() {
7
+ const [projectName] = useState('<%= @config.human_project_name %>');
8
+
9
+ return (
10
+ <View style={tailwind('p-4 bg-gray-900 flex-1 justify-center items-center')}>
11
+ <Text style={tailwind('text-white')}>
12
+ Hello {projectName}!
13
+ </Text>
14
+ </View>
15
+ );
16
+ }
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ <% if @config.use_enzyme? %>
3
+ import { shallow } from 'enzyme';
4
+ import HomeScreen from '../HomeScreen';
5
+
6
+ describe('HomeScreen specs', () => {
7
+ it('shows application name', () => {
8
+ const component = shallow(<HomeScreen />);
9
+ expect(component.contains('<%= @config.human_project_name %>')).toBe(true);
10
+ });
11
+ });
12
+ <% else %>
13
+ import { render } from '@testing-library/react-native';
14
+ import HomeScreen from '../HomeScreen';
15
+
16
+ describe('HomeScreen specs', () => {
17
+ it('shows application name', () => {
18
+ const component = render(<HomeScreen />);
19
+ expect(component.queryByText('Hello <%= @config.human_project_name %>!')).not.toBeNull();
20
+ });
21
+ });
22
+ <% end %>
@@ -0,0 +1,7 @@
1
+ import { create } from 'tailwind-rn';
2
+ import styles from '../../styles.json';
3
+
4
+ const { tailwind, getColor } = create(styles);
5
+
6
+ export { getColor };
7
+ export default tailwind;
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ theme: {
3
+ extend: {},
4
+ },
5
+ variants: {
6
+ extend: {},
7
+ },
8
+ };
@@ -0,0 +1,53 @@
1
+ module Cavendish
2
+ class CLI
3
+ include Commander::Methods
4
+
5
+ def run
6
+ config = Cavendish::Config.new
7
+ define_program
8
+ define_create_command(config)
9
+ run!
10
+ end
11
+
12
+ private
13
+
14
+ def define_program
15
+ program :name, 'Cavendish'
16
+ program :version, Cavendish::VERSION
17
+ program :description, 'React Native + Expo project generator for Platanus'
18
+ end
19
+
20
+ def define_create_command(config)
21
+ command('create') do |c|
22
+ c.syntax = 'cavendish create'
23
+ c.description = 'Create a new React Native + Expo project'
24
+ c.action do |args|
25
+ setup_config(config, args)
26
+ create_command_steps.each { |command| command.for(config: config) }
27
+ end
28
+ end
29
+ end
30
+
31
+ def create_command_steps
32
+ [
33
+ Cavendish::Commands::CreateExpoProject,
34
+ Cavendish::Commands::AddTailwind,
35
+ Cavendish::Commands::AddReactNavigation,
36
+ Cavendish::Commands::AddEslint,
37
+ Cavendish::Commands::AddTesting,
38
+ Cavendish::Commands::AddCiConfig,
39
+ Cavendish::Commands::AddReadme,
40
+ Cavendish::Commands::ConfigureGit
41
+ ]
42
+ end
43
+
44
+ def setup_config(config, args)
45
+ config.project_name = args.first
46
+ config.testing_library = choose(
47
+ 'Which testing library would you like to use?',
48
+ 'Enzyme (unit orientated library)',
49
+ '@testing-library/react-native (integration orientated library)'
50
+ )
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,16 @@
1
+ module Cavendish
2
+ module Commands
3
+ class AddCiConfig < Cavendish::Commands::Base
4
+ def perform
5
+ copy_ci_and_version_files
6
+ end
7
+
8
+ private
9
+
10
+ def copy_ci_and_version_files
11
+ copy_file(".circleci/config.yml", ".circleci/config.yml")
12
+ copy_file(".node-version", ".node-version")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ module Cavendish
2
+ module Commands
3
+ class AddEslint < Cavendish::Commands::Base
4
+ def perform
5
+ copy_config_file
6
+ install_eslint_dependencies
7
+ end
8
+
9
+ private
10
+
11
+ def copy_config_file
12
+ copy_file(".eslintrc.json", ".eslintrc.json")
13
+ end
14
+
15
+ def install_eslint_dependencies
16
+ run_in_project("yarn add -D #{eslint_dependencies.join(' ')}")
17
+ end
18
+
19
+ def eslint_dependencies
20
+ %w[
21
+ babel-eslint
22
+ eslint
23
+ eslint-config-airbnb
24
+ eslint-import-resolver-alias
25
+ eslint-plugin-import
26
+ eslint-plugin-jsx-a11y
27
+ eslint-plugin-react
28
+ eslint-plugin-react-hooks
29
+ ]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ module Cavendish
2
+ module Commands
3
+ class AddReactNavigation < Cavendish::Commands::Base
4
+ def perform
5
+ install_dependencies
6
+ add_example_navigator_and_screens
7
+ replace_app_entrypoint
8
+ end
9
+
10
+ private
11
+
12
+ def install_dependencies
13
+ run_in_project("yarn add #{react_navigation_core_dependencies.join(' ')}")
14
+ run_in_project("expo install #{react_navigation_side_dependencies.join(' ')}")
15
+ end
16
+
17
+ def add_example_navigator_and_screens
18
+ copy_template('src/screens/HomeScreen.jsx', 'src/screens/HomeScreen.jsx')
19
+ copy_file('src/navigators/HomeNavigator.jsx', 'src/navigators/HomeNavigator.jsx')
20
+ end
21
+
22
+ def replace_app_entrypoint
23
+ remove_in_project('App.js')
24
+ copy_file('App.jsx', 'App.jsx')
25
+ end
26
+
27
+ def react_navigation_core_dependencies
28
+ [
29
+ "@react-navigation/native@#{Cavendish::REACT_NAVIGATION_VERSION}",
30
+ "@react-navigation/stack@#{Cavendish::REACT_NAVIGATION_VERSION}"
31
+ ]
32
+ end
33
+
34
+ def react_navigation_side_dependencies
35
+ %w[
36
+ react-native-gesture-handler
37
+ react-native-reanimated
38
+ react-native-screens
39
+ react-native-safe-area-context
40
+ @react-native-community/masked-view
41
+ ]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ module Cavendish
2
+ module Commands
3
+ class AddReadme < Cavendish::Commands::Base
4
+ def perform
5
+ parse_and_copy_file
6
+ end
7
+
8
+ private
9
+
10
+ def parse_and_copy_file
11
+ copy_template('README.md', 'README.md')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ module Cavendish
2
+ module Commands
3
+ class AddTailwind < Cavendish::Commands::Base
4
+ def perform
5
+ install_tailwind_rn_dependencies
6
+ copy_tailwind_config_file
7
+ generate_tailwind_rn_styles_json
8
+ copy_tailwind_utils
9
+ end
10
+
11
+ private
12
+
13
+ def install_tailwind_rn_dependencies
14
+ run_in_project('yarn add tailwind-rn')
15
+ run_in_project('yarn add -D tailwindcss')
16
+ end
17
+
18
+ def copy_tailwind_config_file
19
+ copy_file('tailwind.config.js', 'tailwind.config.js')
20
+ end
21
+
22
+ def generate_tailwind_rn_styles_json
23
+ run_in_project('yarn run create-tailwind-rn')
24
+ end
25
+
26
+ def copy_tailwind_utils
27
+ copy_file('src/utils/tailwindRn.js', 'src/utils/tailwindRn.js')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ module Cavendish
2
+ module Commands
3
+ class AddTesting < Cavendish::Commands::Base
4
+ def perform
5
+ install_dependencies
6
+ add_config_to_package
7
+ @config.use_enzyme? ? add_enzyme_options : add_rn_testing_library_options
8
+ add_example_test_file
9
+ end
10
+
11
+ private
12
+
13
+ def install_dependencies
14
+ run_in_project("yarn add -D #{jest_dependencies.join(' ')}")
15
+ end
16
+
17
+ def add_config_to_package
18
+ inject_to_json_file('package.json', package_configuration)
19
+ end
20
+
21
+ def add_enzyme_options
22
+ run_in_project("yarn add -D #{enzyme_dependencies.join(' ')}")
23
+ inject_to_json_file('package.json', enzyme_configuration)
24
+ end
25
+
26
+ def add_rn_testing_library_options
27
+ run_in_project("yarn add -D #{rn_testing_library_dependencies.join(' ')}")
28
+ end
29
+
30
+ def add_example_test_file
31
+ copy_template(
32
+ 'src/screens/__specs__/HomeScreen.spec.js',
33
+ 'src/screens/__specs__/HomeScreen.spec.js'
34
+ )
35
+ end
36
+
37
+ def jest_dependencies
38
+ %w[
39
+ jest
40
+ fishery
41
+ jest-expo
42
+ ]
43
+ end
44
+
45
+ def rn_testing_library_dependencies
46
+ %w[
47
+ @testing-library/jest-dom
48
+ @testing-library/react-native
49
+ ]
50
+ end
51
+
52
+ def enzyme_dependencies
53
+ %w[
54
+ enzyme
55
+ enzyme-adapter-react-16
56
+ jest-enzyme
57
+ jest-environment-enzyme
58
+ ]
59
+ end
60
+
61
+ def package_configuration
62
+ {
63
+ scripts: { test: 'jest' },
64
+ jest: {
65
+ preset: 'jest-expo',
66
+ transform: { '^.+\\.[jt]sx?$': 'babel-jest' }
67
+ }
68
+ }
69
+ end
70
+
71
+ def enzyme_configuration
72
+ {
73
+ jest: {
74
+ setupFilesAfterEnv: ['jest-enzyme'],
75
+ testEnvironment: 'enzyme'
76
+ }
77
+ }
78
+ end
79
+ end
80
+ end
81
+ end