Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(generators): support for component subdirectory #2285

Merged
merged 8 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions boilerplate/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
const plugins = [
[
"babel-plugin-root-import",
frankcalise marked this conversation as resolved.
Show resolved Hide resolved
{
paths: [
{
rootPathPrefix: "app/",
rootPathSuffix: "app",
},
{
rootPathPrefix: "assets/",
rootPathSuffix: "assets",
},
],
},
],
[
"@babel/plugin-proposal-decorators",
{
Expand Down
6 changes: 3 additions & 3 deletions boilerplate/ignite/templates/component/NAME.tsx.ejs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
patch:
path: "app/components/index.ts"
append: "export * from \"./<%= props.pascalCaseName %>\"\n"
append: "export * from \"./<%= props.subdirectory %><%= props.pascalCaseName %>\"\n"
skip: <%= props.skipIndexFile %>
---
import * as React from "react"
import { StyleProp, TextStyle, View, ViewStyle } from "react-native"
import { observer } from "mobx-react-lite"
import { colors, typography } from "../theme"
import { Text } from "./Text"
import { colors, typography } from "app/theme"
import { Text } from "app/components/Text"

export interface <%= props.pascalCaseName %>Props {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React from "react"
import { createStackNavigator } from "@react-navigation/stack"
import {
WelcomeScreen
} from "../screens"
} from "app/screens"

export type <%= props.pascalCaseName %>NavigatorParamList = {
Demo: undefined
Expand Down
6 changes: 3 additions & 3 deletions boilerplate/ignite/templates/screen/NAMEScreen.tsx.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import React, { FC } from "react"
import { observer } from "mobx-react-lite"
import { ViewStyle } from "react-native"
import { StackScreenProps } from "@react-navigation/stack"
import { AppStackScreenProps } from "../navigators"
import { Screen, Text } from "../components"
import { AppStackScreenProps } from "app/navigators"
import { Screen, Text } from "app/components"
// import { useNavigation } from "@react-navigation/native"
// import { useStores } from "../models"
// import { useStores } from "app/models"

// STOP! READ ME FIRST!
// To fix the TS error below, you'll need to add the following things in your navigation config:
Expand Down
1 change: 1 addition & 0 deletions boilerplate/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"@typescript-eslint/parser": "5.28.0",
"babel-jest": "26.6.3",
"babel-loader": "8.2.5",
"babel-plugin-root-import": "^6.6.0",
"detox": "19.12.1",
"detox-expo-helpers": "0.6.0",
"eslint": "8.17.0",
Expand Down
7 changes: 6 additions & 1 deletion boilerplate/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
"target": "esnext",
"lib": ["esnext", "dom"],
"skipLibCheck": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"paths": {
"app/*": ["app/*"],
"assets/*": ["assets/*"]
},
"baseUrl": "./"
frankcalise marked this conversation as resolved.
Show resolved Hide resolved
},
"exclude": ["node_modules"],
"include": ["index.js", "App.js", "app", "test"],
Expand Down
1 change: 1 addition & 0 deletions docs/Generator-Templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ props.filename // string, the name of the file being generated (e.g. "User
props.pascalCaseName // string, PascalCase version of the name that is passed in (e.g. "UserModel")
props.camelCaseName // string, camelCase version of the name (e.g. "userModel")
props.kebabCaseName // string, kebab-case version of the name (e.g. "user-model")
props.subdirectory // string, the subdirectory path to the file being generated (e.g. "my/sub/path/")
```

Example of using these in a template:
Expand Down
13 changes: 12 additions & 1 deletion src/commands/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { GluegunToolbox } from "gluegun"
import { generateFromTemplate, runGenerator } from "../tools/generators"
import { command, heading, p, warning } from "../tools/pretty"

const SUB_DIR_DELIMITER = "/"

module.exports = {
alias: ["g", "generator", "generators"],
description: "Generates components and other features from templates",
Expand All @@ -18,14 +20,22 @@ async function generate(toolbox: GluegunToolbox) {
const generator = parameters.first.toLowerCase()

// we need a name for this component
const name = parameters.second
let name = parameters.second
if (!name) {
warning(`⚠️ Please specify a name for your ${generator}:`)
p()
command(`ignite g ${generator} MyName`)
return
}

// parse any subdirectories from the specified name
let subdirectory = ""
if (name.indexOf(SUB_DIR_DELIMITER) > -1) {
const lastSlashIndex = name.lastIndexOf(SUB_DIR_DELIMITER)
subdirectory = name.substring(0, lastSlashIndex + 1)
name = name.substring(lastSlashIndex + 1)
}

// avoid the my-component-component phenomenon
const pascalGenerator = strings.pascalCase(generator)
let pascalName = strings.pascalCase(name)
Expand All @@ -44,6 +54,7 @@ async function generate(toolbox: GluegunToolbox) {
generateFromTemplate(generator, {
name: pascalName,
skipIndexFile: parameters.options.skipIndexFile,
subdirectory,
}),
)
heading(`Generated new files:`)
Expand Down
3 changes: 2 additions & 1 deletion src/tools/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ function installedGenerators(): string[] {
type GeneratorOptions = {
name: string
skipIndexFile?: boolean
subdirectory: string
}

/**
Expand Down Expand Up @@ -269,7 +270,7 @@ export function generateFromTemplate(
await handlePatches(data)

// where are we copying to?
const generatorDir = path(appDir(), pluralize(generator))
const generatorDir = path(appDir(), pluralize(generator), options.subdirectory)
const defaultDestinationDir = generatorDir // e.g. app/components, app/screens, app/models
const templateDestinationDir = data.destinationDir
const destinationDir = templateDestinationDir
Expand Down
134 changes: 134 additions & 0 deletions test/vanilla/ignite-generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,138 @@ describe("ignite-cli generate", () => {
`)
})
})

describe("components", () => {
it("should generate Topping component and patch index components export", async () => {
const result = await runIgnite(`generate component Topping`, options)

expect(replaceHomeDir(result)).toMatchInlineSnapshot(`
"

Generated new files:
/user/home/ignite/app/components/Topping.tsx
"
`)
expect(read(`${TEMP_DIR}/app/components/Topping.tsx`)).toMatchInlineSnapshot(`
"import * as React from \\"react\\"
import { StyleProp, TextStyle, View, ViewStyle } from \\"react-native\\"
import { observer } from \\"mobx-react-lite\\"
import { colors, typography } from \\"app/theme\\"
import { Text } from \\"app/components/Text\\"

export interface ToppingProps {
/**
* An optional style override useful for padding & margin.
*/
style?: StyleProp<ViewStyle>
}

/**
* Describe your component here
*/
export const Topping = observer(function Topping(props: ToppingProps) {
const { style } = props
const $styles = [$container, style]

return (
<View style={$styles}>
<Text style={$text}>Hello</Text>
</View>
)
})

const $container: ViewStyle = {
justifyContent: \\"center\\",
}

const $text: TextStyle = {
fontFamily: typography.primary.normal,
fontSize: 14,
color: colors.palette.primary500,
}
"
`)
expect(read(`${TEMP_DIR}/app/components/index.ts`)).toMatchInlineSnapshot(`
"export * from \\"./AutoImage\\"
export * from \\"./Button\\"
export * from \\"./Card\\"
export * from \\"./Header\\"
export * from \\"./Icon\\"
export * from \\"./ListItem\\"
export * from \\"./Screen\\"
export * from \\"./Text\\"
export * from \\"./TextField\\"
export * from \\"./Toggle\\"
export * from \\"./EmptyState\\"
export * from \\"./Topping\\"
"
`)
})

it("should generate Topping component in subdirectory and patch index components export", async () => {
const result = await runIgnite(`generate component sub/to/my/Topping`, options)

expect(replaceHomeDir(result)).toMatchInlineSnapshot(`
"

Generated new files:
/user/home/ignite/app/components/sub/to/my/Topping.tsx
"
`)
expect(read(`${TEMP_DIR}/app/components/sub/to/my/Topping.tsx`)).toMatchInlineSnapshot(`
"import * as React from \\"react\\"
import { StyleProp, TextStyle, View, ViewStyle } from \\"react-native\\"
import { observer } from \\"mobx-react-lite\\"
import { colors, typography } from \\"app/theme\\"
import { Text } from \\"app/components/Text\\"

export interface ToppingProps {
/**
* An optional style override useful for padding & margin.
*/
style?: StyleProp<ViewStyle>
}

/**
* Describe your component here
*/
export const Topping = observer(function Topping(props: ToppingProps) {
const { style } = props
const $styles = [$container, style]

return (
<View style={$styles}>
<Text style={$text}>Hello</Text>
</View>
)
})

const $container: ViewStyle = {
justifyContent: \\"center\\",
}

const $text: TextStyle = {
fontFamily: typography.primary.normal,
fontSize: 14,
color: colors.palette.primary500,
}
"
`)
expect(read(`${TEMP_DIR}/app/components/index.ts`)).toMatchInlineSnapshot(`
"export * from \\"./AutoImage\\"
export * from \\"./Button\\"
export * from \\"./Card\\"
export * from \\"./Header\\"
export * from \\"./Icon\\"
export * from \\"./ListItem\\"
export * from \\"./Screen\\"
export * from \\"./Text\\"
export * from \\"./TextField\\"
export * from \\"./Toggle\\"
export * from \\"./EmptyState\\"
export * from \\"./sub/to/my/Topping\\"
"
`)
})
})
})