diff --git a/docs/README.md b/docs/README.md index e013ac1d1..abfa826cd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,25 +1,19 @@ # Ignite Documentation -This is a list of documentation ideas. - -## Advanced Guides +## Quick Start -* Creating plugins from scratch -* Creating plugins from an existing component -* Sporking template (why and how) -* How to test your plugin -* Creating your own "extensions" (these are 'libraries' things that can be used by plugins - such as file patching) +* Installing +* [Getting started](./quick-start/getting-started.md) +* [Ignite commands](./quick-start/ignite-commands.md) +* [Using boilerplates](./quick-start/using-boilerplates.md) +* Removing plugins +* Spork! - tweaking 3rd-party generators -## API +## Advanced Guides -## Quick Start +* [Creating plugins](./advanced-guides/creating-plugins.md) +* [Creating project plugins](./advanced-guides/creating-project-plugins.md) +* Writing tests for plugins +* Releasing plugins +* Creating extensions -* Using boilerplates -* Removing plugins you don't like -* Project plugins vs 3rd party plugins -* Gluegun and its features -* App structure -* Use and adding Generators -* Adding plugins -* Editing generators and the templates they use -* Ignite commands diff --git a/docs/advanced-guides/creating-plugins.md b/docs/advanced-guides/creating-plugins.md index b70189cad..ad248b9a4 100644 --- a/docs/advanced-guides/creating-plugins.md +++ b/docs/advanced-guides/creating-plugins.md @@ -21,7 +21,7 @@ Open up `package.json` and add your desired info. { "name": "ignite-radio-buttons", "version": "0.0.1", - "description": "", + "description": "An Ignite plugin for react-native-radio-buttons.", "license": "MIT", "devDependencies": {}, "url": "https://github.com/infinitered/ignite-radio-buttons/issues", @@ -29,17 +29,13 @@ Open up `package.json` and add your desired info. "name": "Robin Heinze", "email": "robin@infinite.red", "url": "https://infinite.red" - }, - "devEngines": { - "node": ">=7.x", - "npm": ">=4.x" } } ``` ### Edit the `index.js` -The `index.js` is the entrypoint for your plugin and provides the add/remove functionality. We need to make sure that the npm packages installed and component examples are copied over. +The `index.js` is the entrypoint for your plugin and provides the add/remove functionality. We need to make sure that the npm packages installed and component examples are copied over. 1. Change the `NPM_MODULE_NAME` to `react-native-radio-buttons` 2. Since this npm package does not require a linking step, we will change `ignite.addModule` to have `link: false` and `ignite.removeModule` to have `unlink: false` diff --git a/docs/advanced-guides/creating-project-plugins.md b/docs/advanced-guides/creating-project-plugins.md new file mode 100644 index 000000000..215ff8b64 --- /dev/null +++ b/docs/advanced-guides/creating-project-plugins.md @@ -0,0 +1,72 @@ +# Creating Project Plugins + +First read the [creating plugins guide](./creating-plugins.md). It covers the structure of plugins. + +Project-based plugins are plugins which stay within your repo. They're a great way to add some features to Ignite without going through the hassle or commitment of publishing to NPM. + +Some examples of this might be: + +* generators that make little sense outside your project +* scripts that are a bit more complex than a one liner inside your `package.json` +* a playground for exploring ignite +* a proving arena for plugins you intend to build & release, but aren't quite ready yet + +## The ignite/plugins Directory + +You place your plugins in the `ignite/plugins` directory. Plugins are directories themselves, so begin by creating a `sample` directory there. + +In this directory, create a file called `ignite.json` and put this empty object inside: + +```json +{} +``` + +Next create a new text file in that directory, naming it `ignite.toml`. Inside, place this: + +```toml +description = "🔥🔥🔥 It's plugin time!🔥🔥🔥" +``` + +> Note! This will be replaced shortly with `ignite.json`. I just need to patch up gluegun to provide a post-load hook so ignite can do this. + +## Running Your Plugin + +Back in the project root, type: + +```sh +ignite +``` + +You should see your plugin appear. Now, let's list the commands that you've made: + +```sh +ignite sample +``` + +Empty. Let's make one by creating a new directory: `ignite/plugins/sample/commands`. In that directory place this `online.js`. + +```js +// @cliDescription Let's gather some useful data on this mission! + +module.exports = context => { + const { filesystem, print } = context + const { colors } = print + + const pkg = filesystem.read('package.json', 'json') + const depsCount = Object.keys(pkg.dependencies || {}).length + print.info(`You have ${colors.bold(depsCount)} direct dependencies. And they are awesome.`) +} + +``` + +```sh +ignite sample +``` + +Now you have one. Let's run it. + +```sh +ignite sample online +``` + +For more details on creating commands (including generators), check out [the guide to plugins](./creating-plugins.md) and the [context API guide](https://infinitered.github.io/gluegun/#/context-api). diff --git a/packages/ignite-cli/src/cli/cli.js b/packages/ignite-cli/src/cli/cli.js index c04a58396..3987f3957 100644 --- a/packages/ignite-cli/src/cli/cli.js +++ b/packages/ignite-cli/src/cli/cli.js @@ -9,6 +9,7 @@ const buildIgnite = () => { return build() .brand('ignite') .loadDefault(`${__dirname}/..`) + .loadAll(`${process.cwd()}/ignite/plugins`) .loadAll(`${process.cwd()}/node_modules`, { matching: 'ignite-*', hidden: true }) .token('commandName', 'cliCommand') .token('commandHidden', 'cliHidden') diff --git a/packages/ignite-cli/src/extensions/ignite/findIgnitePlugins.js b/packages/ignite-cli/src/extensions/ignite/findIgnitePlugins.js index fbc43fe64..69b24f127 100644 --- a/packages/ignite-cli/src/extensions/ignite/findIgnitePlugins.js +++ b/packages/ignite-cli/src/extensions/ignite/findIgnitePlugins.js @@ -1,20 +1,33 @@ -const { pipe, filter, propSatisfies, sortBy, prop } = require('ramda') +const { + contains, + anyPass, + pipe, + filter, + propSatisfies, + sortBy, + prop +} = require('ramda') const { startsWith } = require('ramdasauce') module.exports = (plugin, command, context) => { + // gluegun stuff + const { runtime, filesystem: { separator } } = context + + // how to identify ignite plugins + const ignitePrefixed = propSatisfies(startsWith('ignite-'), 'name') + const isInRightLocation = contains(`ignite${separator}plugins`) + const inProjectPlugins = propSatisfies(isInRightLocation, 'directory') + const onlyIgnitePlugins = filter(anyPass([ignitePrefixed, inProjectPlugins])) + const getIgnitePlugins = pipe(onlyIgnitePlugins, sortBy(prop('name'))) + /** - * Finds the gluegun plugins that are also ignite plugins. + * Finds the gluegun plugins that are also ignite plugins. These are + * plugins which have 1 of the following: + * + * - the name starts with "ignite-" + * - the directory contains "ignite/plugins" * * @returns {Plugin[]} - an array of ignite plugins */ - function findIgnitePlugins () { - const { runtime } = context - - return pipe( - filter(propSatisfies(startsWith('ignite-'), 'name')), - sortBy(prop('name')) - )(runtime.plugins) - } - - return findIgnitePlugins + return () => getIgnitePlugins(runtime.plugins) } diff --git a/packages/ignite-cli/tests/cli/cliConfig.test.js b/packages/ignite-cli/tests/cli/cliConfig.test.js index 610ce85f4..326f827f3 100644 --- a/packages/ignite-cli/tests/cli/cliConfig.test.js +++ b/packages/ignite-cli/tests/cli/cliConfig.test.js @@ -25,6 +25,10 @@ test('ignite', async t => { t.deepEqual(builderProps, { brand: 'ignite', loadAlls: [ + { + dir: `${process.cwd()}/ignite/plugins`, + opts: {} + }, { dir: `${process.cwd()}/node_modules`, opts: { hidden: true, matching: 'ignite-*' } diff --git a/packages/ignite-cli/tests/extensions/findIgnitePlugins.test.js b/packages/ignite-cli/tests/extensions/findIgnitePlugins.test.js new file mode 100644 index 000000000..7065863ae --- /dev/null +++ b/packages/ignite-cli/tests/extensions/findIgnitePlugins.test.js @@ -0,0 +1,54 @@ +const test = require('ava') +const extension = require('../../src/extensions/ignite/findIgnitePlugins') + +test('has the right interface', t => { + t.is(typeof extension, 'function') + const context = { filesystem: { separator: '/' } } + const findIgnitePlugin = extension(null, null, context) + t.is(typeof findIgnitePlugin, 'function') +}) + +test('plugin-less', t => { + const context = { + filesystem: { separator: '/' }, + runtime: { + plugins: [] + } + } + const findIgnitePlugin = extension(null, null, context) + t.deepEqual(findIgnitePlugin(), []) +}) + +test('skips non-ignite plugins', t => { + const context = { + filesystem: { separator: '/' }, + runtime: { + plugins: [{ name: 'x', directory: 'y' }] + } + } + const findIgnitePlugin = extension(null, null, context) + t.deepEqual(findIgnitePlugin(), []) +}) + +test('finds ignite- prefixed plugins', t => { + const context = { + filesystem: { separator: '/' }, + runtime: { + plugins: [{ name: 'ignite-foo', directory: 'y' }] + } + } + const findIgnitePlugin = extension(null, null, context) + t.deepEqual(findIgnitePlugin(), [{ name: 'ignite-foo', directory: 'y' }]) +}) + +test('finds project plugins', t => { + const dir = `${process.cwd()}/ignite/plugins/y` + const context = { + filesystem: { separator: '/' }, + runtime: { + plugins: [{ name: 'x', directory: dir }] + } + } + const findIgnitePlugin = extension(null, null, context) + t.deepEqual(findIgnitePlugin(), [{ name: 'x', directory: dir }]) +}) diff --git a/packages/ignite-cli/tests/extensions/ignite.test.js b/packages/ignite-cli/tests/extensions/ignite.test.js index d11cfefeb..512f73aee 100644 --- a/packages/ignite-cli/tests/extensions/ignite.test.js +++ b/packages/ignite-cli/tests/extensions/ignite.test.js @@ -12,6 +12,9 @@ test('has the right interface', t => { }, system: { which: () => true + }, + filesystem: { + separator: '/' } } const extension = attach(plugin, command, context)