Created
December 7, 2017 02:41
-
-
Save jgilless/c7738f5785cf7ebf5fd80d222cdabbb5 to your computer and use it in GitHub Desktop.
Nextjs + Redux + React app with opinionated architecture.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const { execSync } = require('child_process'); | |
if (!process.argv[2]) { | |
console.log('useage: node create-next-app.js <appname>'); | |
process.exit(1); | |
} | |
const rootFolder = process.argv[2]; | |
if (!fs.existsSync(`./${rootFolder}`)) { | |
fs.mkdirSync(`./${rootFolder}`); | |
process.chdir(`./${rootFolder}`); | |
} else { | |
console.log('Folder already exists'); | |
process.exit(1); | |
} | |
/** | |
* This function creates the .env file for the app | |
*/ | |
function createEnv() { | |
console.log('Creating: .env file'); | |
fs.writeFile('./.env', 'NODE_ENV=development', function(err) { | |
if (err) { | |
return console.log(err); | |
} | |
console.log('Success: .env file written'); | |
}); | |
} | |
/** | |
* This function creates all the directories needed to fit the architecture | |
*/ | |
function setupDirectories() { | |
fs.mkdirSync('./src'); | |
fs.mkdirSync('./src/state'); | |
fs.mkdirSync('./src/state/example'); | |
fs.mkdirSync('./src/components'); | |
fs.mkdirSync('./pages'); | |
} | |
/** | |
* This function creates the redux store with opinionated middleware and devtools | |
*/ | |
function createStore() { | |
console.log('Creating: store.js'); | |
fs.writeFile( | |
'./src/state/store.js', | |
`import { createStore, combineReducers, compose, applyMiddleware } from 'redux'; | |
import thunk from 'redux-thunk'; | |
import { composeWithDevTools } from 'redux-devtools-extension'; | |
import example from './example/reducer.js'; | |
const { NODE_ENV } = process.env; | |
const reducers = { | |
example | |
}; | |
let store = null; | |
export const initStore = (initialState = {}) => { | |
const reducer = combineReducers(reducers); | |
if (typeof window === 'undefined') { | |
store = createStore(reducer, initialState, compose(applyMiddleware(thunk))); | |
return store; | |
} | |
const composeEnhancers = NODE_ENV === 'development' ? composeWithDevTools : compose; | |
if (!store) { | |
store = createStore(reducer, initialState, composeEnhancers(applyMiddleware(thunk))); | |
// set window.store for production debugging purposes | |
window.store = store; | |
} | |
return store; | |
};`, | |
function(err) { | |
if (err) { | |
return console.log(err); | |
} | |
console.log('Success: store.js file written'); | |
} | |
); | |
} | |
/** | |
* This function creates 3 files, aciton creators, reducer and constants. They work together in harmony. | |
*/ | |
function createExampleReducer() { | |
console.log('Creating: Example Reducer'); | |
//Create example constants | |
console.log('Creating: Example Reducer: Constants'); | |
fs.writeFile('./src/state/example/constants.js', `export const EXAMPLE_ACTION = 'EXAMPLE_ACTION';`, function(err) { | |
if (err) { | |
return console.log(err); | |
} | |
console.log('Success: Example Reducer: Constants written'); | |
}); | |
//Create axample action creator | |
console.log('Creating: Example Reducer: Action Creators'); | |
fs.writeFile( | |
'./src/state/example/actions.js', | |
`import { EXAMPLE_ACTION } from './constants'; | |
export const exampleAction = (data) => { | |
return { | |
type: EXAMPLE_ACTION, | |
data: data | |
} | |
};`, | |
function(err) { | |
if (err) { | |
return console.log(err); | |
} | |
console.log('Success: Example Reducer: Action Creators written'); | |
} | |
); | |
//Create axample reducer | |
console.log('Creating: Example Reducer: Reducer'); | |
fs.writeFile( | |
'./src/state/example/reducer.js', | |
`import { EXAMPLE_ACTION } from './constants'; | |
const defaultState = { | |
exampleKey: 'exampleValue' | |
}; | |
export default (state = defaultState, action) => { | |
const { type, data } = action; | |
switch (type) { | |
case EXAMPLE_ACTION: { | |
return Object.assign({}, state, { | |
data | |
}); | |
} | |
default: | |
return state; | |
} | |
};`, | |
function(err) { | |
if (err) { | |
return console.log(err); | |
} | |
console.log('Success: Example Reducer: Reducer written'); | |
} | |
); | |
} | |
/** | |
* This function creates an example component | |
*/ | |
function createExampleComponent() { | |
console.log('Creating: example component'); | |
fs.writeFile( | |
'./src/components/example.js', | |
`import React, { Component } from 'react'; | |
class ExampleComponent extends Component { | |
render() { | |
return ( | |
<div> | |
<h2>Example Component!</h2> | |
</div> | |
); | |
} | |
} | |
export default ExampleComponent;`, | |
function(err) { | |
if (err) { | |
return console.log(err); | |
} | |
console.log('Success: example component written'); | |
} | |
); | |
} | |
/** | |
* This function creates the index file in the pages folder that Nextjs uses for routing. | |
*/ | |
function createPages() { | |
console.log('Creating: pages'); | |
fs.writeFile( | |
'./pages/index.js', | |
`import React, { Component } from 'react'; | |
import withRedux from 'next-redux-wrapper'; | |
import { connect } from 'react-redux'; | |
import ExampleComponent from '../src/components/example'; | |
import { initStore } from '../src/state/store'; | |
class App extends Component { | |
render() { | |
return ( | |
<div> | |
<ExampleComponent /> | |
</div> | |
); | |
} | |
} | |
export default withRedux(initStore)(App);`, | |
function(err) { | |
if (err) { | |
return console.log(err); | |
} | |
console.log('Success: pages written'); | |
} | |
); | |
} | |
/** | |
* Just a console.log with some instructions to finish off the app. | |
*/ | |
function logEnding() { | |
console.log('*******************************'); | |
console.log('*'); | |
console.log('*'); | |
console.log('* Your App is now ready, run:'); | |
console.log('*'); | |
console.log(`* cd ${rootFolder} && npm run dev`); | |
console.log('*'); | |
console.log('*'); | |
console.log('*******************************'); | |
} | |
/** | |
* This modifies the package.json to add Nextjs scripts. | |
*/ | |
function addScriptsToPackage() { | |
console.log('Adding scripts to package.json'); | |
var fileName = './package.json'; | |
var file = JSON.parse(fs.readFileSync(fileName, 'utf8')); | |
file.scripts.dev = 'next'; | |
fs.writeFileSync(fileName, JSON.stringify(file, null, 2)); | |
console.log('Sucess: Scripts added to package.json'); | |
} | |
/** | |
* This runs npm install with the packages we need | |
*/ | |
function npmInstall() { | |
execSync( | |
'npm install --save next react react-dom redux react-redux next-redux-wrapper redux-thunk redux-devtools-extension', | |
{ stdio: 'inherit' } | |
); | |
} | |
/** | |
* This runs npm init with all defaults | |
*/ | |
function npmInit() { | |
execSync('npm init --yes', { stdio: 'inherit' }); | |
} | |
createEnv(); | |
npmInit(); | |
npmInstall(); | |
setupDirectories(); | |
createStore(); | |
createExampleReducer(); | |
createExampleComponent(); | |
createPages(); | |
addScriptsToPackage(); | |
logEnding(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment