Skip to content

Commit

Permalink
v2.0.4 (#109)
Browse files Browse the repository at this point in the history
* feat(component sub-generator): `PropTypes` only imported from `prop-types` if it exists within project's package file - #105
* feat(sub-generators): `component`, `enhancer`, and `function` sub generators support [airbnb linting style](https://github.com/airbnb/javascript) - #105
  • Loading branch information
prescottprue authored Jun 28, 2018
1 parent 21b1d85 commit 24991dd
Show file tree
Hide file tree
Showing 19 changed files with 1,826 additions and 1,379 deletions.
38 changes: 33 additions & 5 deletions generators/component/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
'use strict'
const Generator = require('yeoman-generator')
const chalk = require('chalk')
const fs = require('fs')
const path = require('path')
const camelCase = require('lodash/camelCase')
const get = require('lodash/get')

const prompts = [
{
Expand All @@ -25,6 +28,20 @@ const prompts = [
}
]

function loadProjectPackageFile() {
const packagePath = path.join(process.cwd(), 'package.json')
// If functions package file does not exist, default to null
if (!fs.existsSync(packagePath)) {
return null
}
// Load package file handling errors
try {
return require(packagePath)
} catch(err) {
return null
}
}

module.exports = class extends Generator {
constructor (args, opts) {
super(args, opts)
Expand All @@ -46,18 +63,29 @@ module.exports = class extends Generator {
this.log(
`${chalk.blue('Generating')} -> React Component: ${chalk.green(this.options.name)}`
)

const projectPackageFile = loadProjectPackageFile()
return this.prompt(prompts).then((props) => {
this.answers = props
this.answers = Object.assign({}, props, {
// proptypes included by default if project package file not loaded
// (i.e. null due to throws: false in loadProjectPackageFile)
hasPropTypes: !projectPackageFile || !!get(projectPackageFile, 'dependencies.prop-types') || false,
airbnbLinting: !!get(projectPackageFile, 'devDependencies.eslint-config-airbnb') || false
})
})
}

writing () {
const basePathOption = this.options.basePath ? `${this.options.basePath}/` : ''
const basePath = `src/${basePathOption}components/${this.options.name}`
const filesArray = [
{ src: '_index.js', dest: `${basePath}/index.js` },
{ src: '_main.js', dest: `${basePath}/${this.options.name}.js` }
{
src: `_index${this.answers.airbnbLinting ? '-airbnb': ''}.js`,
dest: `${basePath}/index.js`
},
{
src: `_main${this.answers.airbnbLinting ? '-airbnb': ''}.js`,
dest: `${basePath}/${this.options.name}.js`
}
]

if (this.answers.addStyle) {
Expand All @@ -69,7 +97,7 @@ module.exports = class extends Generator {

if (this.answers.includeEnhancer) {
filesArray.push({
src: '_main.enhancer.js',
src: `_main${this.answers.airbnbLinting ? '-airbnb': ''}.enhancer.js`,
dest: `${basePath}/${this.options.name}.enhancer.js`
})
}
Expand Down
4 changes: 4 additions & 0 deletions generators/component/templates/_index-airbnb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import <%= name %> from './<%= name %>';<% if (includeEnhancer) { %>
import enhance from './<%= name %>.enhancer';<% } %>

export default <% if (includeEnhancer) { %>enhance(<%= name %>)<% } else { %><%= name %><% } %>;
8 changes: 8 additions & 0 deletions generators/component/templates/_main-airbnb.enhancer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { compose } from 'redux';
import { withHandlers } from 'recompose';

export default compose(
withHandlers({
// someHandler: props => value => {}
}),
);
16 changes: 16 additions & 0 deletions generators/component/templates/_main-airbnb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<% if (hasPropTypes) { %>import React from 'react'
import PropTypes from 'prop-types'<% } else { %>import React, { PropTypes } from 'react'<% } %>;<% if (addStyle) { %>
import classes from './<%= name %>.scss';<%}%>

export const <%= name %> = ({ <%= camelName %> }) => (
<% if (addStyle) { %><div className={classes.container}><% } else { %><div className='<%= name %>'><%}%>
<span><%= name %> Component</span>
<pre>{JSON.stringify(<%= camelName %>, null, 2)}</pre>
</div>
);

<%= name %>.propTypes = {
<%= camelName %>: PropTypes.object, <% if (includeEnhancer) { %> // from enhancer (firestoreConnect + connect)<% } %>
};

export default <%= name %>;
12 changes: 6 additions & 6 deletions generators/component/templates/_main.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'<% if (addStyle) { %>
<% if (hasPropTypes) { %>import React from 'react'
import PropTypes from 'prop-types'<% } else { %>import React, { PropTypes } from 'react'<% } %><% if (addStyle) { %>
import classes from './<%= name %>.scss'<%}%>

export const <%= name %> = ({ <%= lowerName %> }) => (
<% if (addStyle) { %><div className={classes.container}><%} else { %><div className='<%= name %>'><%}%>
export const <%= name %> = ({ <%= camelName %> }) => (
<% if (addStyle) { %><div className={classes.container}><% } else { %><div className='<%= name %>'><%}%>
<span><%= name %> Component</span>
<pre>{JSON.stringify(<%= lowerName %>, null, 2)}</pre>
<pre>{JSON.stringify(<%= camelName %>, null, 2)}</pre>
</div>
)

<%= name %>.propTypes = {
<%= lowerName %>: PropTypes.object // from enhancer (firestoreConnect + connect)
<%= camelName %>: PropTypes.object <% if (includeEnhancer) { %> // from enhancer (firestoreConnect + connect)<% } %>
}

export default <%= name %>
37 changes: 32 additions & 5 deletions generators/enhancer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
const Generator = require('yeoman-generator')
const chalk = require('chalk')
const camelCase = require('lodash/camelCase')
const get = require('lodash/get')
const fs = require('fs')
const path = require('path')

const prompts = [
{
Expand All @@ -12,6 +15,20 @@ const prompts = [
}
]

function loadProjectPackageFile() {
const packagePath = path.join(process.cwd(), 'package.json')
// If functions package file does not exist, default to null
if (!fs.existsSync(packagePath)) {
return null
}
// Load package file handling errors
try {
return require(packagePath)
} catch(err) {
return null
}
}

module.exports = class extends Generator {
constructor (args, opts) {
super(args, opts)
Expand All @@ -31,20 +48,30 @@ module.exports = class extends Generator {

prompting () {
this.log(
`${chalk.blue('Generating')} -> React Enhancer: ${chalk.green(this.options.name)}`
`${chalk.blue('Generating')} -> React Component Enhancer: ${chalk.green(this.options.name)}`
)

const projectPackageFile = loadProjectPackageFile()
return this.prompt(prompts).then((props) => {
this.answers = props
this.answers = Object.assign({}, props, {
// proptypes included by default if project package file not loaded
// (i.e. null due to throws: false in loadProjectPackageFile)
airbnbLinting: !!get(projectPackageFile, 'devDependencies.eslint-config-airbnb') || false
})
})
}

writing () {
const basePathOption = this.options.basePath ? `${this.options.basePath}/` : ''
const basePath = `src/${basePathOption}components/${this.options.name}`
const filesArray = [
{ src: '_index.js', dest: `${basePath}/index.js` },
{ src: '_main.enhancer.js', dest: `${basePath}/${this.options.name}.enhancer.js` }
{
src: `_index${this.answers.airbnbLinting ? '-airbnb': ''}.js`,
dest: `${basePath}/index.js`
},
{
src: `_main${this.answers.airbnbLinting ? '-airbnb': ''}.enhancer.js`,
dest: `${basePath}/${this.options.name}.enhancer.js`
}
]

filesArray.forEach(file => {
Expand Down
4 changes: 4 additions & 0 deletions generators/enhancer/templates/_index-airbnb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import <%= name %> from './<%= name %>';
import enhance from './<%= name %>.enhancer';

export default enhance(<%= name %>);
12 changes: 12 additions & 0 deletions generators/enhancer/templates/_main-airbnb.enhancer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { compose } from 'redux';
import { connect } from 'react-redux';
import { <% if (usingFirestore) { %>firestoreConnect<% } %><% if (!usingFirestore) { %>firebaseConnect<% } %> } from 'react-redux-firebase';

export default compose(
// create listener for <%= camelName %>, results go into redux state
<% if (!usingFirestore) { %>firebaseConnect([{ path: '<%= camelName %>' }]),<% } %><% if (usingFirestore) { %>firestoreConnect([{ collection: '<%= camelName %>' }]),<% } %>
// connect redux state to props
connect(({ <% if (!usingFirestore) { %>firebase<% } else { %>firestore<% } %>: { data } }) => ({
<%= camelName %>: data.<%= camelName %>,
}))
);
31 changes: 27 additions & 4 deletions generators/function/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,35 @@ const camelCase = require('lodash/camelCase')
const get = require('lodash/get')
const capitalize = require('lodash/capitalize')
const semver = require('semver')
const path = require('path')

function loadProjectPackageFile() {
const packagePath = path.join(process.cwd(), 'package.json')
// If functions package file does not exist, default to not functions v1.0.0
if (!fs.existsSync(packagePath)) {
return null
}
// Load package file handling errors
try {
return require(packagePath)
} catch(err) {
return null
}
}

function getFbToolsVersion () {
const functionsPkgPath = process.cwd() + '/functions/package.json'
// If functions package file does not exist, default to not functions v1.0.0
if (!fs.existsSync(functionsPkgPath)) {
return '0.0.0'
}
const pkgFile = require(functionsPkgPath)
return semver.coerce(get(pkgFile, 'dependencies.firebase-functions'))
// Load package file handling errors
try {
const pkgFile = require(functionsPkgPath)
return semver.coerce(get(pkgFile, 'dependencies.firebase-functions'))
} catch(err) {
return '0.0.0'
}
}

const functionsVersion = getFbToolsVersion()
Expand Down Expand Up @@ -123,9 +143,12 @@ module.exports = class extends Generator {
this.triggerFlag = functionTypeOptions.find(optionName =>
!!this.options[optionName]
)
const projectPackageFile = loadProjectPackageFile()
const prompts = buildPrompts(this)
return this.prompt(prompts).then((props) => {
this.answers = Object.assign({}, props, this.answers)
this.answers = Object.assign({}, props, this.answers, {
airbnbLinting: !!get(projectPackageFile, 'devDependencies.eslint-config-airbnb') || false
})

this.answers.functionsV1 = functionsV1
if (!functionsV1) {
Expand Down Expand Up @@ -164,7 +187,7 @@ module.exports = class extends Generator {
if (this.options.test || this.answers.includeTests) {
filesArray.push(
{
src: `_${triggerType}Test.js`,
src: `_${triggerType}Test${this.answers.airbnbLinting ? '-airbnb': ''}.js`,
dest: `functions/test/${camelName}/index.spec.js`
}
)
Expand Down
20 changes: 10 additions & 10 deletions generators/function/templates/_authFunction.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import * as functions from 'firebase-functions';
// import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions'<% if (airbnbLinting) { %>;<% } %>
// import * as admin from 'firebase-admin'<% if (airbnbLinting) { %>;<% } %>

<% if (functionsV1) { %>async function <%= camelName %>Event(userMetaData, context) {
// const { creationTime, lastSignInTime } = userMetadata<% if (airbnbLinting) { %>;<% } %>
}<% } else { %>function <%= camelName %>Event(event) {
// const user = event.data<% if (airbnbLinting) { %>;<% } %> // The Firebase user
// const { email, displayName } = user<% if (airbnbLinting) { %>;<% } %>
}<% } %><% if (airbnbLinting) { %>;<% } %>

/**
* @name <%= camelName %>
* Cloud Function triggered by Auth Event
* @type {functions.CloudFunction}
*/
export default functions.auth.user().<%= eventType %>(<%= camelName %>Event)

<% if (functionsV1) { %>async function <%= camelName %>Event(userMetaData, context) {
// const { creationTime, lastSignInTime } = userMetadata;
}<% } else { %>function <%= camelName %>Event(event) {
// const user = event.data; // The Firebase user
// const { email, displayName } = user
}<% } %>
export default functions.auth.user().<%= eventType %>(<%= camelName %>Event)<% if (airbnbLinting) { %>;<% } %>
26 changes: 13 additions & 13 deletions generators/function/templates/_firestoreFunction.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

/**
* @name <%= camelName %>
* Cloud Function triggered by Firestore Event
* @type {functions.CloudFunction}
*/
export default functions.firestore
.document('projects/{projectId}')
.<%= eventType %>(<%= camelName %>Event)
import * as functions from 'firebase-functions'<% if (airbnbLinting) { %>;<% } %>
import * as admin from 'firebase-admin'<% if (airbnbLinting) { %>;<% } %>

/**
* @param {functions.Event} event - Function event
Expand All @@ -23,8 +14,17 @@ export default functions.firestore
// const afterData = change.after.data(); // data after the write
// const ref = firestore.collection()
}<% } else { %>async function <%= camelName %>Event(event) {
// const beforeData = event.data.previous.data(); // data before the write
// const afterData = event.data.data(); // data after the write
// const beforeData = event.data.previous.data() // data before the write
// const afterData = event.data.data() // data after the write
// const firestore = admin.firestore()
// const ref = firestore.collection()
}<% } %>

/**
* @name <%= camelName %>
* Cloud Function triggered by Firestore Event
* @type {functions.CloudFunction}
*/
export default functions.firestore
.document('projects/{projectId}')
.<%= eventType %>(<%= camelName %>Event)<% if (airbnbLinting) { %>;<% } %>
Loading

0 comments on commit 24991dd

Please sign in to comment.