cavendish 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +103 -0
- data/.circleci/setup-rubygems.sh +3 -0
- data/.editorconfig +24 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +496 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +143 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cavendish.gemspec +33 -0
- data/exe/cavendish +8 -0
- data/lib/cavendish.rb +13 -0
- data/lib/cavendish/assets/.circleci/config.yml +93 -0
- data/lib/cavendish/assets/.eslintrc.json +43 -0
- data/lib/cavendish/assets/.node-version +1 -0
- data/lib/cavendish/assets/App.jsx +12 -0
- data/lib/cavendish/assets/README.md.erb +59 -0
- data/lib/cavendish/assets/src/navigators/HomeNavigator.jsx +17 -0
- data/lib/cavendish/assets/src/screens/HomeScreen.jsx.erb +16 -0
- data/lib/cavendish/assets/src/screens/__specs__/HomeScreen.spec.js.erb +22 -0
- data/lib/cavendish/assets/src/utils/tailwindRn.js +7 -0
- data/lib/cavendish/assets/tailwind.config.js +8 -0
- data/lib/cavendish/cli.rb +53 -0
- data/lib/cavendish/commands/add_ci_config.rb +16 -0
- data/lib/cavendish/commands/add_eslint.rb +33 -0
- data/lib/cavendish/commands/add_react_navigation.rb +45 -0
- data/lib/cavendish/commands/add_readme.rb +15 -0
- data/lib/cavendish/commands/add_tailwind.rb +31 -0
- data/lib/cavendish/commands/add_testing.rb +81 -0
- data/lib/cavendish/commands/base.rb +9 -0
- data/lib/cavendish/commands/configure_git.rb +22 -0
- data/lib/cavendish/commands/create_expo_project.rb +20 -0
- data/lib/cavendish/config.rb +18 -0
- data/lib/cavendish/utils.rb +74 -0
- data/lib/cavendish/version.rb +5 -0
- 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,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,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
|