diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc
new file mode 100644
index 00000000..cf6d3e55
--- /dev/null
+++ b/.markdownlint.jsonc
@@ -0,0 +1,6 @@
+{
+ "MD013": false, // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
+ "MD033": {
+ "allowed_elements": ["img", "table"]
+ }
+}
\ No newline at end of file
diff --git a/challenges/ecosystem/01.md b/challenges/ecosystem/01.md
index 1d29dcd7..22ea1c8f 100644
--- a/challenges/ecosystem/01.md
+++ b/challenges/ecosystem/01.md
@@ -59,7 +59,7 @@ Example:
... paste an example result here
```
-Feel free to copy/paste some [React Native snippets from this list](https://davidl.fr/pattern).
+Feel free to copy/paste some [React Native snippets from this list](https://davidl.fr/snippets).
### React Native Snippets
@@ -69,7 +69,7 @@ Feel free to copy/paste some [React Native snippets from this list](https://davi
code --install-extension dsznajder.es7-react-js-snippets
```
-- [ ] On your new `cheatcodes.md` write down [React Native components shortcuts](https://github.com/dsznajder/vs code-es7-javascript-react-snippets#react-native-components) you prefer (_Are you a `const` or a `function` person?_)
+- [ ] On your new `cheatcodes.md` write down [React Native components shortcuts](https://github.com/r5n-dev/vscode-react-javascript-snippets/blob/master/docs/Snippets.md#react-native-components) you prefer (_Are you a `const` or a `function` person?_).
**🔭 Hint:** Using `ts` before `rn**` wil automatically add typescript definitions 🚀
@@ -81,9 +81,9 @@ code --install-extension dsznajder.es7-react-js-snippets
code --install-extension nathanchapman.javascriptsnippets
```
-- [ ] add 3 [JavaScripts Snippets](https://github.com/nathanchapman/vs code-javascript-snippets) to your `cheatcodes.md`.
+- [ ] add 3 [JavaScripts Snippets](https://github.com/nathanchapman/vscode-javascript-snippets) to your `cheatcodes.md`.
-**🔭 Hint:** I use `map⇥` and `filter⇥` all the time.
+**🔭 Hint:** I use `fn⇥`, `map⇥` and `filter⇥` all the time.
### Turbo `console.log()`
@@ -134,7 +134,7 @@ In your `settings.json`:
-I use the theme [Night Owl](https://github.com/sdras/night-owl-vs code-theme)
+I use the theme [Night Owl](https://github.com/sdras/night-owl-vscode-theme)
```console
code --install-extension sdras.night-owl
diff --git a/challenges/ecosystem/02.md b/challenges/ecosystem/02.md
index e7a02c45..80ccfcfa 100644
--- a/challenges/ecosystem/02.md
+++ b/challenges/ecosystem/02.md
@@ -13,6 +13,8 @@
- [ ] ❗ Commit your work if it's not already done.
- [ ] Push your code to GitHub.
+You are going to create a `.eslintrc.js` file, here is a [preview of the one used on the spacecraft codebase](https://raw.githubusercontent.com/flexbox/react-native-bootcamp/main/hackathon/spacecraft/.eslintrc.js). We will create more or less the same with small steps.
+
## 👨🚀 Exercise 2
### [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) extensions
@@ -42,7 +44,7 @@ Prettier is an opinionated code formatter. It enforces a consistent style by par
![Prettier format on save preview on VS Code](https://raw.githubusercontent.com/flexbox/react-native-workshop/main/challenges/ecosystem/format-on-save.png)
-### Setup ESLint
+### Setup ESLint
- [ ] Setup ESLint in your project with the following command:
@@ -177,7 +179,7 @@ module.exports = {
"trailingComma": "all",
"tabWidth": 2,
"singleQuote": false,
- "endOfLine": "CRLF" // for windows users only
+ "endOfLine": "crlf" // for windows users only
}
```
@@ -273,10 +275,6 @@ In our case `cargo_capacity`, `cost_in_credits` are not using `camelCase` and we
## 👽 Bonus
-### Avoid data layer issues
-
-- [ ] Install [TanStack Query ESLint plugin](https://tanstack.com/query/latest/docs/eslint/eslint-plugin-query).
-
### Share your settings with your team
You can share your VSCode settings with your team by adding a `.vscode` folder at the root of your project with the following content.
@@ -296,3 +294,7 @@ This will make sure everyone in your team is using the same settings.
- [ ] create a `.vscode` folder.
- [ ] create a `settings.json` file.
- [ ] Add the content above.
+
+### Avoid data layer issues
+
+- [ ] Install [TanStack Query ESLint plugin](https://tanstack.com/query/latest/docs/eslint/eslint-plugin-query).
\ No newline at end of file
diff --git a/challenges/ecosystem/05.md b/challenges/ecosystem/05.md
index 49549d67..2e5cc4b4 100644
--- a/challenges/ecosystem/05.md
+++ b/challenges/ecosystem/05.md
@@ -216,7 +216,10 @@ Now you can run `npm run test:watch` to watch your files and run tests automatic
- https://reactnativetesting.io/
- https://github.com/vanGalilea/react-native-testing?tab=readme-ov-file#covered-examples-
-
### 👽 Bonus
-- [ ] Check the [React Native Testing Library Cookbook](https://callstack.github.io/react-native-testing-library/cookbook/index) and add a new test to your component.
\ No newline at end of file
+- [ ] Check the [React Native Testing Library Cookbook](https://callstack.github.io/react-native-testing-library/cookbook/index) and add a new test to your component.
+
+### Improve your ESLint setup
+
+- [ ] Install [eslint-plugin-testing-library](https://callstack.github.io/react-native-testing-library/docs/start/quick-start#eslint-plugin)
diff --git a/challenges/foundation/04.md b/challenges/foundation/04.md
index 5a18b839..8dbf22dc 100644
--- a/challenges/foundation/04.md
+++ b/challenges/foundation/04.md
@@ -70,7 +70,8 @@ We will use `JSON.stringify()` here to render a `string` version of the data.
function App() {
return (
//
-
+
+ //
);
};
```
diff --git a/hackathon/spacecraft/.eslintrc.js b/hackathon/spacecraft/.eslintrc.js
index 4c28c865..1392b7f7 100644
--- a/hackathon/spacecraft/.eslintrc.js
+++ b/hackathon/spacecraft/.eslintrc.js
@@ -2,49 +2,58 @@ module.exports = {
env: {
node: true,
},
- parser: "@typescript-eslint/parser", // Specifies the ESLint parser
- root: true, // make sure eslint picks up the config at the root of the directory
extends: [
"expo",
"eslint:recommended", // ESLint rules
"plugin:@typescript-eslint/recommended", // TypeScript rules
"plugin:react/recommended", // React rules
- "plugin:react/jsx-runtime", // support for React 17 JSX
+ "plugin:react/jsx-runtime", // Support for React 17 JSX
"plugin:prettier/recommended", // Prettier recommended rules
+ "plugin:perfectionist/recommended-natural-legacy",
+ ],
+ overrides: [
+ {
+ // Test files only
+ extends: ["plugin:testing-library/react"],
+ files: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
+ },
],
- plugins: ["react", "react-native", "simple-import-sort"], // add React and React Native plugins
+ parser: "@typescript-eslint/parser", // Specifies the ESLint parser
+ plugins: [
+ "react",
+ "react-native",
+ "perfectionist", // Keep only 'perfectionist'
+ ],
+ root: true, // Make sure eslint picks up the config at the root of the directory
rules: {
- camelcase: "off", // disable camelcase rule
- "@typescript-eslint/no-explicit-any": "warn", // detect usage of `any` type
- "prettier/prettier": [
- // Prettier rules
- "warn",
+ "@typescript-eslint/no-explicit-any": "warn", // Detect usage of `any` type
+ "@typescript-eslint/no-unused-vars": "warn", // Detect unused variables
+ camelcase: "off", // Disable camelcase rule
+ "perfectionist/sort-imports": "error", // Perfectionist import sorting https://perfectionist.dev/guide/introduction
+ "perfectionist/sort-interfaces": ["error"], // Perfectionist interfaces sorting
+ "perfectionist/sort-objects": [
+ "error",
{
- usePrettierrc: true,
+ ignorePattern: ["useQuery*", "queries*", "useMutation*", "mutations*"],
+ type: "natural",
},
],
- "react-native/no-color-literals": 2, // enforce color literals are not used
- "react-native/no-unused-styles": 2, // detect unused StyleSheet rules
- "react-native/no-raw-text": 0, // detect raw text outside of Text component
- "react-native/sort-styles": 2, // enforce style definitions are sorted
- "@typescript-eslint/no-unused-vars": "warn", // detect unused variables
- "simple-import-sort/exports": "warn", // enforce sorting exports within module
- "simple-import-sort/imports": [
+ "prettier/prettier": [
"warn",
{
- groups: [
- // Side effect imports.
- ["^\\u0000"],
- // Packages `react` related packages come first.
- ["^react", "^@?\\w"],
- // Environment variables
- ["^(@env)(/.*|$)"],
- // Parent imports. Put `..` last.
- ["^\\.\\.(?!/?$)", "^\\.\\./?$"],
- // Other relative imports. Put same-folder imports and `.` last.
- ["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
- ],
+ usePrettierrc: true,
},
],
+ "react/no-unescaped-entities": "off",
+ "react-native/no-color-literals": 2, // Enforce color literals are not used
+ "react-native/no-raw-text": 0, // Detect raw text outside of Text component
+ "react-native/no-unused-styles": 2, // Detect unused StyleSheet rules
+ "react-native/sort-styles": 2, // Enforce style definitions are sorted
+ },
+ settings: {
+ perfectionist: {
+ partitionByComment: true,
+ type: "line-length",
+ },
},
};
diff --git a/hackathon/spacecraft/App.tsx b/hackathon/spacecraft/App.tsx
index e6b8ed14..77bc1fb7 100644
--- a/hackathon/spacecraft/App.tsx
+++ b/hackathon/spacecraft/App.tsx
@@ -1,7 +1,6 @@
-import React from "react";
-import { Provider as PaperProvider } from "react-native-paper";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import Constants from "expo-constants";
+import { Provider as PaperProvider } from "react-native-paper";
import { AuthenticationProvider } from "~/context/Authentication";
import { NetworkProvider } from "~/context/Network";
diff --git a/hackathon/spacecraft/api/types.d.ts b/hackathon/spacecraft/api/types.d.ts
index a5117570..7007718d 100644
--- a/hackathon/spacecraft/api/types.d.ts
+++ b/hackathon/spacecraft/api/types.d.ts
@@ -1,5 +1,4 @@
export interface StarshipProps {
- MGLT?: string;
cargo_capacity?: string;
consumables?: string;
cost_in_credits?: string;
@@ -11,6 +10,7 @@ export interface StarshipProps {
length?: string;
manufacturer?: string;
max_atmosphering_speed?: string;
+ MGLT?: string;
model: string;
name: string;
passengers?: string;
@@ -20,20 +20,20 @@ export interface StarshipProps {
}
interface PeopleProps {
- name: string;
- height: string;
- mass: string;
- hair_color: string;
- skin_color: string;
- eye_color: string;
birth_year: string;
+ created: string;
+ edited: string;
+ eye_color: string;
+ films: string[];
gender: string;
+ hair_color: string;
+ height: string;
homeworld: string;
- films: string[];
+ mass: string;
+ name: string;
+ skin_color: string;
species: string[];
- vehicles: string[];
starships: string[];
- created: string;
- edited: string;
url: string;
+ vehicles: string[];
}
diff --git a/hackathon/spacecraft/app.config.js b/hackathon/spacecraft/app.config.js
index eaab6c91..202fa61c 100644
--- a/hackathon/spacecraft/app.config.js
+++ b/hackathon/spacecraft/app.config.js
@@ -1,64 +1,64 @@
module.exports = {
- name: "spacecraft",
- description: "Learning materials for the `react-native-bootcamp` repository.",
- slug: "spacecraft",
- owner: "weshipit",
- version: "1.0.2",
- orientation: "portrait",
- icon: "./assets/icon.png",
- splash: {
- image: "./assets/splash.png",
- resizeMode: "contain",
- backgroundColor: "#ffffff",
- },
- updates: {
- fallbackToCacheTimeout: 0,
- url: "https://u.expo.dev/012accc3-4ce5-4bae-9f4d-2f842489f07a",
- },
- assetBundlePatterns: ["**/*"],
- ios: {
- supportsTablet: true,
- bundleIdentifier: "weshipit.today.spacecraft",
- appStoreUrl:
- "https://apps.apple.com/fr/app/retail-shake-scanner/id1234567890",
- },
android: {
adaptiveIcon: {
- foregroundImage: "./assets/adaptive-icon.png",
backgroundColor: "#FFFFFF",
+ foregroundImage: "./assets/adaptive-icon.png",
},
package: "weshipit.today.spacecraft",
playStoreUrl:
"https://play.google.com/store/apps/details?id=weshipit.today.spacecraft",
},
- web: {
- favicon: "./assets/favicon.png",
- },
+ assetBundlePatterns: ["**/*"],
+ description: "Learning materials for the `react-native-bootcamp` repository.",
extra: {
- storybookEnabled: process.env.STORYBOOK_ENABLED,
eas: {
projectId: "012accc3-4ce5-4bae-9f4d-2f842489f07a",
},
+ storybookEnabled: process.env.STORYBOOK_ENABLED,
+ },
+ icon: "./assets/icon.png",
+ ios: {
+ appStoreUrl:
+ "https://apps.apple.com/fr/app/retail-shake-scanner/id1234567890",
+ bundleIdentifier: "weshipit.today.spacecraft",
+ supportsTablet: true,
},
+ name: "spacecraft",
+ orientation: "portrait",
+ owner: "weshipit",
plugins: [
[
"app-icon-badge",
{
// enable/ disable the plugin based on the environment (usually disabled for production builds)
- enabled: process.env.ENVIRONMENT === "production" ? false : true,
badges: [
{
+ background: "#FF0000", // by default it will be black and we are only supporting hex format for colors
+ color: "white", // by default it will be white and the only color supported for now is white and black
text: process.env.ENVIRONMENT || "unkown", // banner text
type: "banner", // banner or ribbon
- color: "white", // by default it will be white and the only color supported for now is white and black
- background: "#FF0000", // by default it will be black and we are only supporting hex format for colors
},
{
text: process.env.version,
type: "ribbon",
},
],
+ enabled: process.env.ENVIRONMENT === "production" ? false : true,
},
],
],
+ slug: "spacecraft",
+ splash: {
+ backgroundColor: "#ffffff",
+ image: "./assets/splash.png",
+ resizeMode: "contain",
+ },
+ updates: {
+ fallbackToCacheTimeout: 0,
+ url: "https://u.expo.dev/012accc3-4ce5-4bae-9f4d-2f842489f07a",
+ },
+ version: "1.0.2",
+ web: {
+ favicon: "./assets/favicon.png",
+ },
};
diff --git a/hackathon/spacecraft/babel.config.js b/hackathon/spacecraft/babel.config.js
index 39604ee1..df62dee5 100644
--- a/hackathon/spacecraft/babel.config.js
+++ b/hackathon/spacecraft/babel.config.js
@@ -1,7 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
- presets: ["babel-preset-expo"],
env: {
production: {
plugins: ["transform-remove-console", "react-native-paper/babel"],
@@ -10,5 +9,6 @@ module.exports = function (api) {
plugins: [
"react-native-reanimated/plugin", // order matters
],
+ presets: ["babel-preset-expo"],
};
};
diff --git a/hackathon/spacecraft/metro.config.js b/hackathon/spacecraft/metro.config.js
index 171ed8c0..6947fe6e 100644
--- a/hackathon/spacecraft/metro.config.js
+++ b/hackathon/spacecraft/metro.config.js
@@ -1,8 +1,7 @@
// metro.config.js
-const path = require("path");
-
-const { getDefaultConfig } = require("expo/metro-config");
const { generate } = require("@storybook/react-native/scripts/generate");
+const { getDefaultConfig } = require("expo/metro-config");
+const path = require("path");
generate({
configPath: path.resolve(__dirname, "./.storybook"),
diff --git a/hackathon/spacecraft/package.json b/hackathon/spacecraft/package.json
index 31da2548..43aa1d01 100644
--- a/hackathon/spacecraft/package.json
+++ b/hackathon/spacecraft/package.json
@@ -76,7 +76,7 @@
"@storybook/core-common": "^7.6.10",
"@storybook/react-native": "^7.6.15",
"@storybook/react-native-server": "^6.5.8",
- "@tanstack/eslint-plugin-query": "^5.17.20",
+ "@tanstack/eslint-plugin-query": "^5.53.0",
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^12.4.3",
"@types/jest": "^29.5.4",
@@ -87,18 +87,17 @@
"app-icon-badge": "^0.0.15",
"babel-loader": "^9.1.3",
"babel-plugin-module-resolver": "^5.0.0",
- "babel-plugin-root-import": "^6.6.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^8.56.0",
"eslint-config-expo": "^7.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-react-native-wcandillon": "^3.9.0",
- "eslint-import-resolver-babel-plugin-root-import": "^1.1.1",
- "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-perfectionist": "^3.3.0",
"eslint-plugin-prettier": "^5.1.3",
+ "eslint-plugin-query": "^0.9.1",
"eslint-plugin-react": "^7.34.2",
"eslint-plugin-react-native": "^4.1.0",
- "eslint-plugin-simple-import-sort": "^12.1.0",
+ "eslint-plugin-testing-library": "^6.3.0",
"jest": "^29.6.4",
"jest-expo": "~51.0.2",
"prettier": "^3.2.5",
diff --git a/hackathon/spacecraft/src/components/Button.stories.tsx b/hackathon/spacecraft/src/components/Button.stories.tsx
index 74e3da24..d24eef89 100644
--- a/hackathon/spacecraft/src/components/Button.stories.tsx
+++ b/hackathon/spacecraft/src/components/Button.stories.tsx
@@ -8,61 +8,61 @@ export default {
export const _Button = () => (
@@ -73,8 +73,8 @@ export const _ButtonSizes = () => (
diff --git a/hackathon/spacecraft/src/components/ButtonSupport.tsx b/hackathon/spacecraft/src/components/ButtonSupport.tsx
index 22527c29..84c9a76e 100644
--- a/hackathon/spacecraft/src/components/ButtonSupport.tsx
+++ b/hackathon/spacecraft/src/components/ButtonSupport.tsx
@@ -1,6 +1,5 @@
-import React from "react";
-import { Button } from "react-native-paper";
import * as WebBrowser from "expo-web-browser";
+import { Button } from "react-native-paper";
interface Props {
mode?: "contained" | "text";
@@ -15,8 +14,8 @@ export const ButtonSupport = ({ mode = "contained" }: Props) => {
return (