这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ script:
- 'npm run test:ci'
- yarn danger ci
after_success:
- './node_modules/.bin/jest --coverage && cat ./coverage/lcov.info | ./node_modules/.bin/codecov'
- './node_modules/.bin/jest --coverage --runInBand && cat ./coverage/lcov.info | ./node_modules/.bin/codecov'
notifications:
webhooks:
urls:
Expand Down
4 changes: 3 additions & 1 deletion .vscode/cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"infinite.red",
"info",
"macos",
"namespacing",
"newclear",
"npmignore",
"Plugin",
Expand All @@ -46,7 +47,8 @@
"tada",
"tempy",
"updtr",
"v1"
"v1",
"walkthrough"
],
// flagWords - list of words to be always considered incorrect
// This is useful for offensive words and common spelling errors.
Expand Down
8 changes: 7 additions & 1 deletion __tests__/__mocks__/mockContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const mockContext = {
system: {
startTimer: jest.fn(() => jest.fn()),
},
template: {
generate: jest.fn(),
},
print: {
error: jest.fn(),
success: jest.fn(),
Expand All @@ -33,19 +36,22 @@ const mockContext = {
green: jest.fn(),
red: jest.fn(),
blue: jest.fn(),
magenta: jest.fn(),
},
colors: {
green: jest.fn(),
red: jest.fn(),
blue: jest.fn(),
magenta: jest.fn(),
},
},
printSeparator: jest.fn(),
parameters: {
options: {},
},
prompt: {
ask: jest.fn(() => Promise.resolve({ createFile: true })),
ask: jest.fn(({ name }) => Promise.resolve({ [name]: 'taco', createFile: true })),
confirm: jest.fn(() => true)
},
solidarity: noConfigSolidarity,
}
Expand Down
20 changes: 20 additions & 0 deletions __tests__/command_helpers/__snapshots__/createPlugin.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`check result shape 1`] = `[Function]`;

exports[`investigate createPlugin 1`] = `
Array [
Array [
".gitignore.ejs",
".gitignore",
],
Array [
"README.md.ejs",
"README.md",
],
Array [
"package.json.ejs",
"package.json",
],
]
`;
16 changes: 16 additions & 0 deletions __tests__/command_helpers/createPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const createPlugin: Function = require('../../src/extensions/functions/createPlugin')
const context = require('mockContext')

test('check result shape', () => {
expect(createPlugin).toMatchSnapshot()
})

test('investigate createPlugin', async () => {

const result = await createPlugin(context)
expect(result).toMatchSnapshot()
expect(context.template.generate).toBeCalled()
expect(context.prompt.ask).toBeCalled()
expect(context.prompt.confirm).toBeCalled()
expect(context.print.success).toBeCalled()
})
9 changes: 9 additions & 0 deletions __tests__/commands/__snapshots__/create.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Snapshot create command 1`] = `
Object {
"alias": "c",
"description": "Displays this help",
"run": [Function],
}
`;
34 changes: 34 additions & 0 deletions __tests__/commands/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import createCommand from '../../src/commands/create'
const mockContext = require('mockContext')

test('Snapshot create command', () => {
expect(createCommand).toMatchSnapshot()
})

it('enforces required properties', () => {
expect(createCommand.description).toBeTruthy()
expect(createCommand.run).toBeTruthy()
expect(typeof createCommand.run).toBe('function')
})

test('check solidarity create with no parameter', async () => {
const result = await createCommand.run(mockContext)
expect(mockContext.print.error.mock.calls).toEqual([["Missing what to create"], ["solidarity create <wut?>"]])
expect(mockContext.print.info.mock.calls.length).toBe(1)
})

test('check solidarity create with plugin', async () => {
const goodContext = {
...mockContext,
parameters: { first: 'plugin' },
solidarity: { createPlugin: jest.fn() }
}
await createCommand.run(goodContext)
expect(goodContext.solidarity.createPlugin).toBeCalled()

// now make it fail
const errorString = 'ER MA GERD ARRAWRS'
goodContext.solidarity.createPlugin = jest.fn(() => throw Error(errorString))
await createCommand.run(goodContext)
expect(goodContext.print.error.mock.calls.slice(-1).toString()).toEqual(`Error: ${errorString}`)
})
1 change: 1 addition & 0 deletions __tests__/extensions/__snapshots__/extensionCheck.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Object {
"checkENV": [Function],
"checkFile": [Function],
"checkRequirement": [Function],
"createPlugin": [Function],
"getLineWithVersion": [Function],
"getSolidaritySettings": [Function],
"getVersion": [Function],
Expand Down
2 changes: 2 additions & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Writing your own plugins
If you're using a technology that doesn't have a plugin, or if you'd just like build your own custom rules to use in solidarity, we've made creating plugins extremely simple.

The following docs will show you all the features of how to create a plugin. If you'd like, you can generate a plugin-base by typing `solidarity create plugin`. This starts a walkthrough that will ask you questions to help you get started writing your plugin.

## Plugin Docs
* [Write the Simplest Plugin](/docs/simplePlugin.md)
* [Plugins that write Solidarity Files from Code](/docs/solidarityFromCode.md)
Expand Down
25 changes: 25 additions & 0 deletions src/commands/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { GluegunCommand } from 'gluegun'

const createables = ['plugin']

module.exports = {
alias: 'c',
description: 'Displays this help',
run: async context => {
const { print, solidarity, parameters } = context
switch (parameters.first && parameters.first.toLowerCase()) {
case 'plugin':
// Handle errors like grown-ups
try {
await solidarity.createPlugin(context)
} catch (e) {
print.error(e)
}
break
default:
print.error('Missing what to create')
print.error('solidarity create <wut?>')
print.info(`Things you can create: ${createables}`)
}
},
} as GluegunCommand
59 changes: 59 additions & 0 deletions src/extensions/functions/createPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module.exports = async context => {
let files = [['.gitignore.ejs', '.gitignore'], ['README.md.ejs', 'README.md'], ['package.json.ejs', 'package.json']]
const { print, template, prompt } = context
const { colors } = print

const answerPluginName = await prompt.ask({
type: 'input',
name: 'plugin',
message: 'Plugin name? (we will add the namespacing for you)',
})
if (!answerPluginName.plugin) throw Error('A plugin requires a name')
const pluginName = `solidarity-${answerPluginName.plugin.replace('solidarity-', '')}`

const description = await prompt.ask({
type: 'input',
name: 'pluginDesc',
message: 'Short plugin description (used in various places)'
})

const ruleChoices = [
'I do not want a generated rule file',
'Just a simple rule template',
'Template + optional rules',
]
const answer = await prompt.ask({
type: 'list',
name: 'ruleChoice',
message: 'Your initial rule file template?',
choices: ruleChoices,
})

if (answer.ruleChoice === ruleChoices[1]) {
files.push(['rules-template.json.ejs', `templates/${pluginName}-template.json`])
files.push(['simple-plugin.js.ejs', `extensions/${pluginName}.js`])
} else if (answer.ruleChoice === ruleChoices[2]) {
files.push(['rules-template.json.ejs', `templates/${pluginName}-template.json`])
files.push(['helpful-plugin.js.ejs', `extensions/${pluginName}.js`])
files.push(['addOptionalRules.js.ejs', `extensions/helpers/addOptionalRules.js`])
}

const customRules = await prompt.confirm('Custom rules? (e.g. Rules other than basic types)')

print.info(`Creating plugin ${pluginName}`)
// copy files over
files.map(fileSet => {
template.generate({
template: fileSet[0],
target: `${pluginName}/${fileSet[1]}`,
props: { pluginName, customRules, description: description.pluginDesc },
})
})

print.success(`
Done! ${colors.magenta('\n\nPlugin Docs: https://infinitered.github.io/solidarity/#/docs/plugins')}
`)

// for tests really
return files
}
9 changes: 9 additions & 0 deletions src/templates/.gitignore.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules/
yarn-error.log
.node-version
coverage
.nyc_output
.idea
dist/
.vscode/*
!.vscode/cSpell.json
12 changes: 12 additions & 0 deletions src/templates/README.md.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<a href='https://infinitered.github.io/solidarity/'><img src='https://github.com/infinitered/solidarity/raw/master/_art/plugin.jpg' align='left' height="60"/></a>

# <%= props.pluginName %>
## <%= props.description %>
Much longer description

## Install:
`npm i <%= props.pluginName %>` or `yarn add <%= props.pluginName %>`
This plugin will automatically be picked up by Solidarity (which should already be installed).

## What is Solidarity?
#### [:newspaper: Read More About Solidarity Here](https://github.com/infinitered/solidarity)
12 changes: 12 additions & 0 deletions src/templates/addOptionalRules.js.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = (context, requirements) => {
const { solidarity, filesystem } = context
const { binaryExists } = solidarity

// Conditionally add requirements and rules

// Example:
// if (binaryExists('yarn', context) && filesystem.exists('./yarn.lock') === 'file') {
// requirements['Yarn'] = [{rule: 'cli', binary: 'yarn', version: '--version', semver: '0.0.0'}]
// }

}
42 changes: 42 additions & 0 deletions src/templates/helpful-plugin.js.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const addOptionalRules = require('./helpers/addOptionalRules')

module.exports = (context) => {
// Register this plugin
context.addPlugin({
name: '<%= props.pluginName %>',
description: '<%= props.description %>',
snapshot: async (context) => {
// start with template
let solidarity = require('../templates/<%= props.pluginName %>-template.json')
// add optional rules
addOptionalRules(context, solidarity.requirements)
// write out .solidarity file
context.solidarity.setSolidaritySettings(solidarity, context)
// update file with local versions
await context.system.run('solidarity snapshot')
}<% if(props.customRules){ %>,
rules: {
ruleName: {
check: async (rule, context) => {
return {
pass: true,
message: 'Check always passes'
}
},
snapshot: async (rule, context) => [
{
prop: 'addedProp',
value: '12.0.0'
},
],
report: async (rule, context, report) => {
report.addCLI({
binary: 'imaginaryCLI',
version: '10',
desired: '12'
})
}
}
}<% } %>
})
}
12 changes: 12 additions & 0 deletions src/templates/package.json.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "<%= props.pluginName %>",
"version": "1.0.0",
"repository": "https://github.com/<me>/<%= props.pluginName %>",
"description": "<%= props.description %>",
"author": "Super Awesome Coder",
"license": "MIT",
"private": false,
"scripts": {
"test": "echo not yet"
}
}
6 changes: 6 additions & 0 deletions src/templates/rules-template.json.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "http://json.schemastore.org/solidaritySchema",
"requirements": {
"Node Example": [{ "rule": "cli", "binary": "node", "semver": "0.0.0"}]
}
}
31 changes: 31 additions & 0 deletions src/templates/simple-plugin.js.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports = (context) => {
// Register this plugin
context.addPlugin({
name: '<%= props.pluginName %>',
description: '<%= props.description %>',
snapshot: '<%= props.pluginName %>-template.json'<% if(props.customRules){ %>,
rules: {
ruleName: {
check: async (rule, context) => {
return {
pass: true,
message: 'Check always passes'
}
},
snapshot: async (rule, context) => [
{
prop: 'addedProp',
value: '12.0.0'
},
],
report: async (rule, context, report) => {
report.addCLI({
binary: 'imaginaryCLI',
version: '10',
desired: '12'
})
}
}
}<% } %>
})
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const solidarity = {
buildSpecificRequirement: require('./extensions/functions/buildSpecificRequirement'),
appendSolidaritySettings: require('./extensions/functions/appendSolidaritySettings'),
ruleHandlers: require('./extensions/functions/ruleHandlers'),
createPlugin: require('./extensions/functions/createPlugin'),
}

export interface SolidarityPlugin {
Expand Down