Skip to content

Commit

Permalink
fix(plugins): Updates plugin generator and adds integration test (#1421
Browse files Browse the repository at this point in the history
… by @jamonholmgren)

Fixes #1418.
  • Loading branch information
jamonholmgren authored May 15, 2019
1 parent ed79e4c commit 6c937e1
Show file tree
Hide file tree
Showing 19 changed files with 156 additions and 131 deletions.
6 changes: 3 additions & 3 deletions docs/advanced-guides/creating-generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ By answering `yes` to the generator question, you will be given the files necess
├── index.js
├── package.json
└── templates
└── MyGeneratorExample.js
└── my-generator-example.js
```

`commands/generate/example.js` exports a function whose responsibility is to queue a job to copy the template to the project. `templates/MyGeneratorExample.js` (or `{generator name}Example.js`) is the template that will be copied over. Rename or copy these files appropriately for your generator. The other files are standard plugin files; see the plugin guide to review plugins.
`commands/generate/example.js` exports a function whose responsibility is to queue a job to copy the template to the project. `templates/my-generator-example.js` (or `{generator name}-example.js`) is the template that will be copied over. Rename or copy these files appropriately for your generator. The other files are standard plugin files; see the plugin guide to review plugins.

In the example `commands/generate/example.js`, you can see that the generator function accepts a `toolbox` parameter. You can use this to prompt the user to select options from a list. The following is an example of this from the ListView generator.

Expand All @@ -58,7 +58,7 @@ This example uses the resulting `type` to select a template from several availab
```javascript
// commands/listview.js
const componentTemplate = type === 'With Sections' ? 'listview-sections' : 'listview'
const jobs = [{ template: `${componentTemplate}.ejs`, target: `App/Containers/${name}.js` }]
const jobs = [{ template: `${componentTemplate}.ejs`, target: `app/components/${name}.js` }]

// Job handler
await generate({
Expand Down
20 changes: 2 additions & 18 deletions docs/advanced-guides/creating-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ We will be using https://github.com/ArnaudRinquin/react-native-radio-buttons as

### Generate a basic plugin structure


Run the provided plugin generator. Ignite CLI will automatically prepend your package name with `ignite-`.

```
Expand Down Expand Up @@ -43,27 +42,13 @@ The `plugin.js` file is the entrypoint for your plugin and provides the add/remo

### Add content to the example template

`templates/RadioButtonsExample.js.ejs`
`templates/radio-buttons-example.js.ejs`

```
import React from 'react'
import { View, Text, TouchableWithoutFeedback } from 'react-native'
import ExamplesRegistry from '../../../App/Services/ExamplesRegistry'
import { RadioButtons } from 'react-native-radio-buttons'
// Example
ExamplesRegistry.addPluginExample('RadioButtons', () =>
<View style={{margin: 20}}>
<RadioButtons
options={options}
onSelection={ setSelectedOption.bind(this) }
selectedOption={ options.first } // In your application, this would be { this.state.selectedOption }
renderOption={ renderOption }
renderContainer={ renderContainer }
/>
</View>
)
const options = [
"Option 1",
"Option 2"
Expand All @@ -89,7 +74,6 @@ const renderOption = (option, selected, onSelect, index) => {
const renderContainer = (optionNodes) => {
return <View>{optionNodes}</View>
}
```

### Add the plugin to the Ignite application
Expand All @@ -98,7 +82,7 @@ const renderContainer = (optionNodes) => {
ignite add radio-buttons
```

NOTE: If your plugin is not on npm yet, Make sure you have `IGNITE_PLUGIN_PATH` set as an ENV variable in your shell profile. It should point to the directory that contains the plugin you are writing.
NOTE: If your plugin is not on npm yet, make sure you have `IGNITE_PLUGIN_PATH` set as an ENV variable in your shell profile. It should point to the directory that contains the plugin you are writing.

```
~/.bash_profile
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"test": "jest tests/fast",
"watch": "jest tests/fast --watch",
"lint": "tslint -p .",
"integration": "jest tests/integration",
"integration": "jest --runInBand tests/integration",
"ci:test": "yarn test && yarn integration",
"ci:publish": "yarn build && yarn semantic-release",
"semantic-release": "semantic-release"
Expand Down
37 changes: 12 additions & 25 deletions src/commands/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { IgniteToolbox, IgniteCopyJob } from '../types'
* @returns {Object} The answers.
*/
const walkthrough = async (toolbox: IgniteToolbox) => {
const minOptions = { template: 'No', command: 'No' }
const maxOptions = { template: 'Yes', command: 'Yes' }
const minOptions = { boilerplate: 'No', generator: 'No' }
const maxOptions = { boilerplate: 'Yes', generator: 'Yes' }
if (toolbox.parameters.options.min) {
return minOptions
}
Expand All @@ -27,13 +27,7 @@ const walkthrough = async (toolbox: IgniteToolbox) => {
choices: ['No', 'Yes'],
},
{
name: 'template',
message: 'Will your plugin have an example component?',
type: 'list',
choices: ['No', 'Yes'],
},
{
name: 'command',
name: 'generator',
message: 'Will your plugin have a generator command? (e.g. ignite generate <mygenerator> <name>)',
type: 'list',
choices: ['No', 'Yes'],
Expand Down Expand Up @@ -66,30 +60,23 @@ const createNewPlugin = async (toolbox: IgniteToolbox) => {
{ template: 'plugin/test/add.js.ejs', target: `${pluginName}/test/add.js` },
{ template: 'plugin/test/remove.js.ejs', target: `${pluginName}/test/remove.js` },
{ template: 'plugin/test/interface.js.ejs', target: `${pluginName}/test/interface.js` },
answers.template === 'Yes' && {
template: 'plugin/templates/Example.js.ejs',
target: `${pluginName}/templates/${name}Example.js.ejs`,
},
answers.command === 'Yes' && {
// generator command template example
answers.generator === 'Yes' && {
template: 'plugin/commands/thing.js.ejs',
target: `${pluginName}/commands/thing.js`,
target: `${pluginName}/commands/generate/${pluginName}.js`,
},
answers.command === 'Yes' && {
answers.generator === 'Yes' && {
template: 'plugin/templates/thing.js.ejs.ejs',
target: `${pluginName}/templates/thing.js.ejs`,
target: `${pluginName}/templates/${pluginName}.js.ejs`,
},
]
if (answers.boilerplate === 'Yes') {
copyJobs.push({ template: 'plugin/boilerplate.js.ejs', target: `${pluginName}/boilerplate.js` })
copyJobs.push({ template: 'plugin/boilerplate/index.js.ejs.ejs', target: `${pluginName}/boilerplate/index.js.ejs` })
copyJobs.push({ template: 'plugin/boilerplate/App/App.js', target: `${pluginName}/boilerplate/App/App.js` })
copyJobs.push({
template: 'plugin/boilerplate/Tests/AppTest.js',
target: `${pluginName}/boilerplate/Tests/AppTest.js`,
})
copyJobs.push({ template: 'plugin/boilerplate/app/app.js', target: `${pluginName}/boilerplate/app/app.js` })
copyJobs.push({
template: 'plugin/boilerplate/ignite/ignite.json',
target: `${pluginName}/boilerplate/ignite/ignite.json`,
template: 'plugin/boilerplate/tests/app.test.js',
target: `${pluginName}/boilerplate/tests/app.test.js`,
})
}

Expand All @@ -99,7 +86,7 @@ const createNewPlugin = async (toolbox: IgniteToolbox) => {
pluginName,
answers,
igniteVersion: meta.version(),
isGenerator: answers.command === 'Yes',
isGenerator: answers.generator === 'Yes',
})
}

Expand Down
1 change: 0 additions & 1 deletion src/extensions/ignite/add-plugin-screen-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export default (toolbox: IgniteToolbox) => {
map(file => {
// turn things like "examples/This File-Example.js" into "ThisFileExample"
// for decent component names
// TODO: check for collisions in the future
const exampleFileName = takeLast(1, split(path.sep, file.screen))[0]
const componentName = replace(/.js|\s|-/g, '', exampleFileName)

Expand Down
2 changes: 2 additions & 0 deletions src/lib/validate-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { IgniteToolbox } from '../types'
export default (pluginName: string, toolbox: IgniteToolbox): string => {
const { strings, print } = toolbox

pluginName = pluginName.toLowerCase()

if (strings.isBlank(pluginName)) {
print.info(`ignite plugin new ignite-foo\n`)
print.error('Plugin name is required')
Expand Down
1 change: 0 additions & 1 deletion src/templates/plugin/boilerplate/Tests/AppTest.js

This file was deleted.

File renamed without changes.
4 changes: 2 additions & 2 deletions src/templates/plugin/boilerplate/index.js.ejs.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/

import { AppRegistry } from 'react-native'
import App from './App/App.js'
import App from './app/app.js'

AppRegistry.registerComponent('<%- '<' + '%= props.name %' + '>' %>', () => App)
AppRegistry.registerComponent('<%- "<" + "%= props.name %" + ">" %>', () => App)

5 changes: 5 additions & 0 deletions src/templates/plugin/boilerplate/tests/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const test = require('ava')

test('sample test', async t => {
t.true(true)
})
55 changes: 32 additions & 23 deletions src/templates/plugin/commands/thing.js.ejs
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
// @cliDescription Example <%= props.name %> command
// Generates a "thing" (rename this to whatever -- component, model, anything).
/**
* This is an example Ignite plugin generator. You can run it when it's installed to
* your project by doing `ignite generate <%= props.name %> foo`.
*
* You can rename this command to anything you'd like, or add others.
*
* For more information on plugins, check out https://github.com/infinitered/gluegun/blob/master/docs/plugins.md.
*/

module.exports = async function (context) {
// Learn more about context: https://infinitered.github.io/gluegun/#/context-api.md
const { parameters, strings, print, ignite } = context
const { pascalCase, isBlank } = strings
module.exports = {
description: "Example <%= props.name %> generator",
run: async function (toolbox) {
// Learn more about toolbox: https://infinitered.github.io/gluegun/#/toolbox-api.md
const { parameters, strings, print, ignite } = toolbox
const { pascalCase, isBlank } = strings

// validation
if (isBlank(parameters.first)) {
print.info(`ignite generate thing <name>\n`)
print.info('A name is required.')
return
}
// validation
if (isBlank(parameters.first)) {
print.info(`ignite generate <%= props.name %> <name>\n`)
print.info('A name is required.')
return
}

const name = pascalCase(parameters.first)
const props = { name }
const name = pascalCase(parameters.first)
const props = { name }

// Copies the `thing.js.ejs` in your plugin's templates folder
// into App/Things/${name}.js.
const jobs = [{
template: 'thing.js.ejs',
target: `App/Things/${name}.js`
}]
// Copies the `<%= props.name %>.js.ejs` in your plugin's templates folder
// into App/Things/${name}.js.
const jobs = [{
template: '<%= props.name %>.js.ejs',
target: `app/${name}.js`
}]

// make the templates and pass in props with the third argument here
await ignite.copyBatch(context, jobs, props)
}
// make the templates and pass in props with the third argument here
await ignite.copyBatch(toolbox, jobs, props)
}
}
1 change: 1 addition & 0 deletions src/templates/plugin/gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
npm-debug.log
generated
.vscode
15 changes: 8 additions & 7 deletions src/templates/plugin/package.json.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@
},
"scripts": {
"lint": "standard",
"format": "prettier --write \"{**/*.ts,**/*.js}\" --loglevel error && standard --fix",
"test": "ava",
"watch": "ava --watch",
"coverage": "nyc ava",
"shipit": "np"
},
"prettier": {},
"standard": {
"parser": "babel-eslint"
},
"url": "https://example.com/path/to/your/plugin",
"repository": "githuborg/reponame",
"devDependencies": {
"ava": "^0.18.2",
"babel-eslint": "^7.1.1",
"np": "^2.12.0",
"nyc": "^10.1.2",
"sinon": "^1.17.7",
"standard": "^8.6.0"
"ava": "^1.4.1",
"babel-eslint": "^10.0.1",
"np": "^5.0.1",
"sinon": "^7.3.2",
"standard": "^12.0.1",
"prettier": "^1.17.1"
}
}

Expand Down
40 changes: 18 additions & 22 deletions src/templates/plugin/plugin.js.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,46 @@ const NPM_MODULE_VERSION = '0.0.1'

// const PLUGIN_PATH = __dirname
// const APP_PATH = process.cwd()
<% if (props.answers.template === 'Yes') { %>const EXAMPLE_FILE = '<%= props.name %>Example.js.ejs'<% } %>
<% if (props.answers.template === 'Yes') { %>const EXAMPLE_FILE = '<%= props.name %>.js.ejs'<% } %>

const add = async function (context) {
// Learn more about context: https://infinitered.github.io/gluegun/#/context-api.md
const { ignite, filesystem } = context
const add = async function (toolbox) {
// Learn more about toolbox: https://infinitered.github.io/gluegun/#/toolbox-api.md
const { ignite } = toolbox

// install an NPM module and link it
await ignite.addModule(NPM_MODULE_NAME, { link: true, version: NPM_MODULE_VERSION })

<% if (props.answers.template === 'Yes') { %>await ignite.addPluginComponentExample(EXAMPLE_FILE, { title: '<%= props.name %> Example' })<% } %>

// Example of copying templates/<%= props.name %> to App/<%= props.name %>
// if (!filesystem.exists(`${APP_PATH}/App/<%= props.name %>`)) {
// filesystem.copy(`${PLUGIN_PATH}/templates/<%= props.name %>`, `${APP_PATH}/App/<%= props.name %>`)
// Example of copying templates/<%= props.name %> to app/<%= props.pluginName %>
// if (!toolbox.filesystem.exists(`${APP_PATH}/app/<%= props.pluginName %>`)) {
// toolbox.filesystem.copy(`${PLUGIN_PATH}/templates/<%= props.pluginName %>`, `${APP_PATH}/app/<%= props.pluginName %>`)
// }

// Example of patching a file
// ignite.patchInFile(`${APP_PATH}/App/Config/AppConfig.js`, {
// insert: `import '../<%= props.name %>/<%= props.name %>'\n`,
// ignite.patchInFile(`${APP_PATH}/app/config/app-config.js`, {
// insert: `import '../<%= props.pluginName %>/<%= props.pluginName %>'\n`,
// before: `export default {`
// })
}

/**
* Remove yourself from the project.
*/
const remove = async function (context) {
// Learn more about context: https://infinitered.github.io/gluegun/#/context-api.md
const { ignite, filesystem } = context
const remove = async function (toolbox) {
// Learn more about toolbox: https://infinitered.github.io/gluegun/#/toolbox-api.md
const { ignite } = toolbox

// remove the npm module and unlink it
await ignite.removeModule(NPM_MODULE_NAME, { unlink: true })

<% if (props.answers.template === 'Yes') { %>await ignite.removePluginComponentExample(EXAMPLE_FILE)<% } %>

// Example of removing App/<%= props.name %> folder
// const remove<%= props.name %> = await context.prompt.confirm(
// 'Do you want to remove App/<%= props.name %>?'
// Example of removing app/<%= props.name %> folder
// const remove<%= props.pluginName %> = await toolbox.prompt.confirm(
// 'Do you want to remove app/<%= props.pluginName %>?'
// )
// if (remove<%= props.name %>) { filesystem.remove(`${APP_PATH}/App/<%= props.name %>`) }
// if (remove<%= props.pluginName %>) { toolbox.filesystem.remove(`${APP_PATH}/app/<%= props.pluginName %>`) }

// Example of unpatching a file
// ignite.patchInFile(`${APP_PATH}/App/Config/AppConfig.js`, {
// delete: `import '../<%= props.name %>/<%= props.name %>'\n`
// ignite.patchInFile(`${APP_PATH}/app/config/app-config.js`, {
// delete: `import '../<%= props.pluginName %>/<%= props.pluginName %>'\n`
// )
}

Expand Down
10 changes: 0 additions & 10 deletions src/templates/plugin/templates/Example.js.ejs

This file was deleted.

2 changes: 0 additions & 2 deletions src/templates/plugin/templates/thing.js.ejs.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@

// Pass in props from your command.
<%-
// i'm so sorry - the right answer is in
// https://github.com/mde/ejs/blob/master/docs/syntax.md somewhere
'<' + '%= props.name %' + '>'
%>
Loading

0 comments on commit 6c937e1

Please sign in to comment.