From 8a1bb1f4caa6e7f657a38dafeae17b51b840d8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Thu, 1 Aug 2019 19:49:19 +0200 Subject: [PATCH 01/10] Added block in readme regarding upgrading --- readme.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/readme.md b/readme.md index e29042d3..3e8b8783 100644 --- a/readme.md +++ b/readme.md @@ -198,6 +198,15 @@ In addition to `redux` --> `mobx-state-tree`, we've also transitioned to using ` In Bowser, TypeScript is fully set up, so if you know TS, all you need to do is start coding! +## Upgrade + +To keep your React Native app updated: +- [React Native Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) great web based tool +- [rn-diff-purge](https://github.com/react-native-community/rn-diff-purge) + +To keep your Ignite Bowser based app updated: +- [ignite-bowser-difff-purge](https://github.com/nirre7/ignite-bowser-diff-purge) To help you see the diffs between versions + ### Resources If you are new to TypeScript, here are some of our favorite resources: From 3e769a506c8c2346f08d7a3127d205e6ff5abe02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Fri, 2 Aug 2019 07:21:59 +0200 Subject: [PATCH 02/10] Moved block above typescript --- readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 3e8b8783..1ff0a1fa 100644 --- a/readme.md +++ b/readme.md @@ -192,12 +192,6 @@ MobX and MobX State Tree can be a lot to learn if you're coming from Redux, so h - https://github.com/jamonholmgren/PlayerPopularity (simple implementation) - https://github.com/infinitered/ChainReactApp2019 (more in-depth implementation) -## TypeScript - -In addition to `redux` --> `mobx-state-tree`, we've also transitioned to using `TypeScript` vs plain `JavaScript`. We find that TypeScript streamlines the developer experience by catching errors _before_ you hit refresh on that simulator, and prevents costly bugs by enforcing type safety. - -In Bowser, TypeScript is fully set up, so if you know TS, all you need to do is start coding! - ## Upgrade To keep your React Native app updated: @@ -207,6 +201,12 @@ To keep your React Native app updated: To keep your Ignite Bowser based app updated: - [ignite-bowser-difff-purge](https://github.com/nirre7/ignite-bowser-diff-purge) To help you see the diffs between versions +## TypeScript + +In addition to `redux` --> `mobx-state-tree`, we've also transitioned to using `TypeScript` vs plain `JavaScript`. We find that TypeScript streamlines the developer experience by catching errors _before_ you hit refresh on that simulator, and prevents costly bugs by enforcing type safety. + +In Bowser, TypeScript is fully set up, so if you know TS, all you need to do is start coding! + ### Resources If you are new to TypeScript, here are some of our favorite resources: From 0cb6cff3d79244cd1cf9eed77bb2cf738bf69e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Sun, 29 Dec 2019 21:41:13 +0100 Subject: [PATCH 03/10] Added ability to generate a React.FunctionComponent aka "hooks enabled component". --- src/commands/generate/component.ts | 48 ++++++++++++++++++++++++---- templates/component.tsx.ejs | 1 + templates/function-component.tsx.ejs | 23 +++++++++++++ templates/styles.ts.ejs | 7 ++++ 4 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 templates/function-component.tsx.ejs create mode 100644 templates/styles.ts.ejs diff --git a/src/commands/generate/component.ts b/src/commands/generate/component.ts index 33e3393f..a4e2b965 100644 --- a/src/commands/generate/component.ts +++ b/src/commands/generate/component.ts @@ -3,7 +3,7 @@ import { GluegunToolbox } from "gluegun" export const description = "Generates a component, supporting files, and a storybook test." export const run = async function(toolbox: GluegunToolbox) { // grab some features - const { parameters, strings, print, ignite, patching, filesystem } = toolbox + const { parameters, strings, print, ignite, patching, filesystem, prompt } = toolbox const { pascalCase, isBlank } = strings // validation @@ -13,21 +13,57 @@ export const run = async function(toolbox: GluegunToolbox) { return } + const componentTypes = [ + { + name: 'functionComponent', + message: 'React.FunctionComponent, aka "hooks component"' + }, + { + name: 'statelessFunction', + message: 'Stateless function, aka the "classic" ignite-bowser component' + } + ] + + const { componentType } = await prompt.ask([ + { + name: 'componentType', + message: 'Which type of component do you want to generate?', + type: 'select', + choices: componentTypes, + }, + ]) + const name = parameters.first const pascalName = pascalCase(name) - const props = { name, pascalName } + const jobs = [ - { - template: 'component.tsx.ejs', - target: `app/components/${name}/${name}.tsx` - }, { template: 'component.story.tsx.ejs', target: `app/components/${name}/${name}.story.tsx` + }, + { + template: 'styles.ts.ejs', + target: `app/components/${name}/${name}.styles.ts` } ] + if (componentType === "functionComponent") { + jobs.push( + { + template: 'function-component.tsx.ejs', + target: `app/components/${name}/${name}.tsx` + } + ) + } else if (componentType === "statelessFunction") { + jobs.push( + { + template: 'component.tsx.ejs', + target: `app/components/${name}/${name}.tsx` + } + ) + } + await ignite.copyBatch(toolbox, jobs, props) // patch the barrel export file diff --git a/templates/component.tsx.ejs b/templates/component.tsx.ejs index f8f5d9ac..48863cfa 100644 --- a/templates/component.tsx.ejs +++ b/templates/component.tsx.ejs @@ -1,6 +1,7 @@ import * as React from "react" import { View, ViewStyle } from "react-native" import { Text } from "../" +import { <%= props.pascalName %>Styles as styles } from "./<%= props.name %>.styles" export interface <%= props.pascalName %>Props { /** diff --git a/templates/function-component.tsx.ejs b/templates/function-component.tsx.ejs new file mode 100644 index 00000000..e3b901da --- /dev/null +++ b/templates/function-component.tsx.ejs @@ -0,0 +1,23 @@ +import * as React from "react" +import { View } from "react-native" +import { useObserver } from "mobx-react-lite" +import { Text } from "../" +import { useStores } from "../../models/root-store" +import { <%= props.pascalName %>Styles as styles } from "./<%= props.name %>.styles" + +export interface <%= props.pascalName %>Props {} + +/** + * React.FunctionComponent for your hook(s) needs + * + * Component description here for TypeScript tips. + */ +export const <%= props.pascalName %>: React.FunctionComponent<<%= props.pascalName %>Props> = props => { + // const { someStore } = useStores() + + return useObserver(() => ( + + Hi Func + + )) +} diff --git a/templates/styles.ts.ejs b/templates/styles.ts.ejs new file mode 100644 index 00000000..b0a8a3fd --- /dev/null +++ b/templates/styles.ts.ejs @@ -0,0 +1,7 @@ +import { ViewStyle } from "react-native" + +export const <%= props.pascalName %>Styles = { + WRAPPER: { + justifyContent: 'center' + } as ViewStyle +} From 4cd147579250672d794044593564a3247cc07a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Sun, 29 Dec 2019 21:48:27 +0100 Subject: [PATCH 04/10] Fixed prettier commands so they work with zsh. Without the '' it won't work run prettier on nested folders. --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b59322ae..dd84eb3c 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,10 @@ }, "scripts": { "format": "npm-run-all format:*", - "format:js": "prettier --write {.,**}/*.js", - "format:json": "prettier --write {.,**}/*.json", - "format:md": "prettier --write {.,**}/*.md", - "format:ts": "prettier --write {.,**}/*.{ts,tsx}", + "format:js": "prettier --write '{.,**}/*.js'", + "format:json": "prettier --write '{.,**}/*.json'", + "format:md": "prettier --write '{.,**}/*.md'", + "format:ts": "prettier --write '{.,**}/*.{ts,tsx}'", "lint": "eslint src test --ext .ts --fix", "test": "yarn build && jest --runInBand && yarn lint && yarn clean", "watch": "jest --runInBand --watch", From 3a23cccde6c7069e7946313e6a6dcbc76429b169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Wed, 1 Jan 2020 21:34:46 +0100 Subject: [PATCH 05/10] Added parameters for choosing component type. Updated style reference to be camel case. --- src/commands/generate/component.ts | 49 +++++++++++++++------------- templates/component.tsx.ejs | 2 +- templates/function-component.tsx.ejs | 2 +- templates/styles.ts.ejs | 2 +- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/commands/generate/component.ts b/src/commands/generate/component.ts index a4e2b965..6e8cf52e 100644 --- a/src/commands/generate/component.ts +++ b/src/commands/generate/component.ts @@ -4,7 +4,7 @@ export const description = "Generates a component, supporting files, and a story export const run = async function(toolbox: GluegunToolbox) { // grab some features const { parameters, strings, print, ignite, patching, filesystem, prompt } = toolbox - const { pascalCase, isBlank } = strings + const { pascalCase, camelCase, isBlank } = strings // validation if (isBlank(parameters.first)) { @@ -13,29 +13,34 @@ export const run = async function(toolbox: GluegunToolbox) { return } - const componentTypes = [ - { - name: 'functionComponent', - message: 'React.FunctionComponent, aka "hooks component"' - }, - { - name: 'statelessFunction', - message: 'Stateless function, aka the "classic" ignite-bowser component' - } - ] + let componentType + if (!parameters.options["function-component"] && !parameters.options["stateless-function"]) { + const componentTypes = [ + { + name: "functionComponent", + message: "React.FunctionComponent, aka \"hooks component\"", + }, + { + name: "statelessFunction", + message: "Stateless function, aka the \"classic\" ignite-bowser component", + }, + ] - const { componentType } = await prompt.ask([ - { - name: 'componentType', - message: 'Which type of component do you want to generate?', - type: 'select', - choices: componentTypes, - }, - ]) + const { component } = await prompt.ask([ + { + name: "component", + message: "Which type of component do you want to generate?", + type: "select", + choices: componentTypes, + }, + ]) + componentType = component + } const name = parameters.first const pascalName = pascalCase(name) - const props = { name, pascalName } + const camelCaseName = camelCase(name) + const props = { name, pascalName, camelCaseName } const jobs = [ { @@ -48,14 +53,14 @@ export const run = async function(toolbox: GluegunToolbox) { } ] - if (componentType === "functionComponent") { + if (componentType === "functionComponent" || parameters.options["function-component"]) { jobs.push( { template: 'function-component.tsx.ejs', target: `app/components/${name}/${name}.tsx` } ) - } else if (componentType === "statelessFunction") { + } else if (componentType === "statelessFunction" || parameters.options["stateless-function"]) { jobs.push( { template: 'component.tsx.ejs', diff --git a/templates/component.tsx.ejs b/templates/component.tsx.ejs index 48863cfa..1e8c5ca6 100644 --- a/templates/component.tsx.ejs +++ b/templates/component.tsx.ejs @@ -1,7 +1,7 @@ import * as React from "react" import { View, ViewStyle } from "react-native" import { Text } from "../" -import { <%= props.pascalName %>Styles as styles } from "./<%= props.name %>.styles" +import { <%= props.camelCaseName %>Styles as styles } from "./<%= props.name %>.styles" export interface <%= props.pascalName %>Props { /** diff --git a/templates/function-component.tsx.ejs b/templates/function-component.tsx.ejs index e3b901da..a85823b3 100644 --- a/templates/function-component.tsx.ejs +++ b/templates/function-component.tsx.ejs @@ -3,7 +3,7 @@ import { View } from "react-native" import { useObserver } from "mobx-react-lite" import { Text } from "../" import { useStores } from "../../models/root-store" -import { <%= props.pascalName %>Styles as styles } from "./<%= props.name %>.styles" +import { <%= props.camelCaseName %>Styles as styles } from "./<%= props.name %>.styles" export interface <%= props.pascalName %>Props {} diff --git a/templates/styles.ts.ejs b/templates/styles.ts.ejs index b0a8a3fd..44ba3ced 100644 --- a/templates/styles.ts.ejs +++ b/templates/styles.ts.ejs @@ -1,6 +1,6 @@ import { ViewStyle } from "react-native" -export const <%= props.pascalName %>Styles = { +export const <%= props.camelCaseName %>Styles = { WRAPPER: { justifyContent: 'center' } as ViewStyle From 148d32de470cefe98c3580c1d35690531851f68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Wed, 1 Jan 2020 21:35:32 +0100 Subject: [PATCH 06/10] Updated component tests. --- test/generators-integration.test.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/test/generators-integration.test.ts b/test/generators-integration.test.ts index 4e408deb..0af57408 100644 --- a/test/generators-integration.test.ts +++ b/test/generators-integration.test.ts @@ -58,11 +58,24 @@ describe("a generated app", () => { ) }) - test("generates a component", async () => { - const simpleComponent = "Simple" - await execa(IGNITE, ["g", "component", simpleComponent], { preferLocal: false }) - expect(jetpack.exists(`app/components/${simpleComponent}/${simpleComponent}.tsx`)).toBe("file") - expect(jetpack.exists(`app/components/${simpleComponent}/${simpleComponent}.story.tsx`)).toBe( + test("generates a stateless function", async () => { + const statelessFunction = "Stateless" + await execa(IGNITE, ["g", "component", statelessFunction, "--stateless-function"], { preferLocal: false }) + expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.tsx`)).toBe("file") + expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.story.tsx`)).toBe( + expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.styles.ts`)).toBe( + "file", + ) + const lint = await execa("npm", ["-s", "run", "lint"]) + expect(lint.stderr).toBe("") + }) + + test("generates a function component", async () => { + const functionComponent = "FunctionComponent" + await execa(IGNITE, ["g", "component", functionComponent, "--function-component"], { preferLocal: false }) + expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.tsx`)).toBe("file") + expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.story.tsx`)).toBe( + expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.styles.ts`)).toBe( "file", ) const lint = await execa("npm", ["-s", "run", "lint"]) From da9b1064ac0451ff4b3a8b9c7d13ac912635bdc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Wed, 1 Jan 2020 21:39:22 +0100 Subject: [PATCH 07/10] Updated component tests. --- test/generators-integration.test.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/generators-integration.test.ts b/test/generators-integration.test.ts index 0af57408..19446718 100644 --- a/test/generators-integration.test.ts +++ b/test/generators-integration.test.ts @@ -62,10 +62,8 @@ describe("a generated app", () => { const statelessFunction = "Stateless" await execa(IGNITE, ["g", "component", statelessFunction, "--stateless-function"], { preferLocal: false }) expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.tsx`)).toBe("file") - expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.story.tsx`)).toBe( - expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.styles.ts`)).toBe( - "file", - ) + expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.story.tsx`)).toBe("file") + expect(jetpack.exists(`app/components/${statelessFunction}/${statelessFunction}.styles.ts`)).toBe("file") const lint = await execa("npm", ["-s", "run", "lint"]) expect(lint.stderr).toBe("") }) @@ -74,10 +72,8 @@ describe("a generated app", () => { const functionComponent = "FunctionComponent" await execa(IGNITE, ["g", "component", functionComponent, "--function-component"], { preferLocal: false }) expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.tsx`)).toBe("file") - expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.story.tsx`)).toBe( - expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.styles.ts`)).toBe( - "file", - ) + expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.story.tsx`)).toBe("file") + expect(jetpack.exists(`app/components/${functionComponent}/${functionComponent}.styles.ts`)).toBe("file") const lint = await execa("npm", ["-s", "run", "lint"]) expect(lint.stderr).toBe("") }) From 1acd22df4acebdad5c9995140ea94ba76da55428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Tue, 7 Jan 2020 08:43:30 +0100 Subject: [PATCH 08/10] Added documentation for generators. --- readme.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/readme.md b/readme.md index 503007e1..832c6667 100644 --- a/readme.md +++ b/readme.md @@ -50,6 +50,56 @@ You should see an app that looks like the screenshot above! Next step -- follow this tutorial to learn how to create a trivia app with Ignite Bowser: https://shift.infinite.red/creating-a-trivia-app-with-ignite-bowser-part-1-1987cc6e93a1 +## Generators +The true gem of Ignite Bowser. Generators help you scaffold your app very quick, be it for a poc, a demo or a production app, generators are there to save you time and help you with the basic structure for your app. +``` +ignite generate +``` +Will give you information of what generators are present + +### Component generator +This is the generator you will be using most often. +There are 2 flavors: +- React.FunctionComponent i.e "hooks enabled component" +- Stateless function aka the "classic ignite-bowser component" +``` +ignite generate component awesome-component +``` +- Creates the component/function +- Creates a style file +- Creates a storybook test +- Will make the required additions to configuration files. + +You can also bypass the choice by providing which component type you want to create: +``` +ignite generate component awesome-component --function-component +``` +Or +``` +ignite generate component awesome-component --stateless-function +``` +### Screen generator +Generates a "hooks enabled" screen. +``` +ignite generate screen awesome-screen +``` +- Creates the screen +- Will make the required additions to configuration files. + + +### Model generator +Creates a Mobx-State-Tree model +``` +ignite generate model awesome-model +``` +- Creates the model +- Creates a unit test file +- Will make the required additions to configuration files. + +### Advanced +The built in generators aren't enough? Fret not, you can create your own generators that suit your project/company. These generators can live with the default ignite-bowser generators. +Please refer to the [documentation on how to create your own generators.](https://github.com/infinitered/ignite/blob/master/docs/advanced-guides/creating-generators.md) + ## Explanation of folder structure The Ignite Bowser boilerplate project's structure will look similar to this: From 5e5669a29b15790e692c9781603607508db19b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20S=C3=B6derstr=C3=B6m?= Date: Tue, 7 Jan 2020 08:53:08 +0100 Subject: [PATCH 09/10] Added documentation for the navigation generator. --- readme.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 832c6667..3efe0314 100644 --- a/readme.md +++ b/readme.md @@ -85,8 +85,15 @@ ignite generate screen awesome-screen ``` - Creates the screen - Will make the required additions to configuration files. - - + +### Navigator generator +Helps you in a "wizard-manner" create a new [react-navigation](https://reactnavigation.org/docs/en/getting-started.html) navigator. +``` +ignite generate navigator awesome-navigator +``` +- Creates the navigator +- Will make the required additions to configuration files. + ### Model generator Creates a Mobx-State-Tree model ``` From b1ff75f75865aaa44b3e34f73cb09df3bc88c55e Mon Sep 17 00:00:00 2001 From: Jamon Holmgren Date: Thu, 16 Jan 2020 07:39:38 -0800 Subject: [PATCH 10/10] Added spacing, small tweaks --- readme.md | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 3efe0314..0af0483d 100644 --- a/readme.md +++ b/readme.md @@ -51,60 +51,81 @@ You should see an app that looks like the screenshot above! Next step -- follow this tutorial to learn how to create a trivia app with Ignite Bowser: https://shift.infinite.red/creating-a-trivia-app-with-ignite-bowser-part-1-1987cc6e93a1 ## Generators -The true gem of Ignite Bowser. Generators help you scaffold your app very quick, be it for a poc, a demo or a production app, generators are there to save you time and help you with the basic structure for your app. + +The true gem of Ignite Bowser. Generators help you scaffold your app very quickly, be it for a proof-of-concept, a demo, or a production app. Generators are there to save you time, keep your code consistent, and help you with the basic structure of your app. + ``` ignite generate ``` -Will give you information of what generators are present + +Will give you information of what generators are present. ### Component generator -This is the generator you will be using most often. -There are 2 flavors: -- React.FunctionComponent i.e "hooks enabled component" -- Stateless function aka the "classic ignite-bowser component" + +This is the generator you will be using most often. There are 2 flavors: + +- React.FunctionComponent (i.e. "hooks enabled component") +- Stateless function (i.e. the "classic ignite-bowser component") + ``` ignite generate component awesome-component ``` + - Creates the component/function - Creates a style file - Creates a storybook test - Will make the required additions to configuration files. You can also bypass the choice by providing which component type you want to create: + ``` ignite generate component awesome-component --function-component ``` + Or + ``` ignite generate component awesome-component --stateless-function ``` + ### Screen generator + Generates a "hooks enabled" screen. + ``` ignite generate screen awesome-screen ``` + - Creates the screen - Will make the required additions to configuration files. ### Navigator generator -Helps you in a "wizard-manner" create a new [react-navigation](https://reactnavigation.org/docs/en/getting-started.html) navigator. + +Helps you in a "wizard-style" create a new [react-navigation](https://reactnavigation.org/docs/en/getting-started.html) navigator. + ``` ignite generate navigator awesome-navigator ``` + - Creates the navigator - Will make the required additions to configuration files. ### Model generator -Creates a Mobx-State-Tree model + +Creates a Mobx-State-Tree model. + ``` ignite generate model awesome-model ``` + - Creates the model - Creates a unit test file - Will make the required additions to configuration files. ### Advanced + The built in generators aren't enough? Fret not, you can create your own generators that suit your project/company. These generators can live with the default ignite-bowser generators. + Please refer to the [documentation on how to create your own generators.](https://github.com/infinitered/ignite/blob/master/docs/advanced-guides/creating-generators.md) ## Explanation of folder structure