Merge pull request #214 from strapi/feature/strapi-helper-plugin

Strapi Helper Plugin
This commit is contained in:
Pierre BURGY 2017-06-09 14:23:30 +01:00 committed by GitHub
commit 62fa4ff58d
162 changed files with 1824 additions and 955 deletions

View File

@ -8,6 +8,14 @@
"policies": []
}
},
{
"method": "GET",
"path": "/:plugin/main.js",
"handler": "Admin.pluginFile",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/:file",

View File

@ -18,6 +18,15 @@ module.exports = {
}
},
pluginFile: async ctx => {
try {
const file = fs.readFileSync(path.resolve(process.cwd(), 'plugins', ctx.params.plugin, 'admin', 'build', 'main.js'));
ctx.body = file;
} catch (err) {
ctx.body = ctx.notFound();
}
},
file: async ctx => {
try {
const file = fs.readFileSync(path.resolve(__dirname, '..', 'public', 'build', ctx.params.file));

View File

@ -14,18 +14,16 @@ class PluginHeader extends React.Component { // eslint-disable-line react/prefer
render() {
return (
<div className={`${styles.pluginHeader} row`}>
<div className="row">
<div className="col-lg-6">
<PluginHeaderTitle
title={this.props.title}
description={this.props.description}
/>
</div>
<div className="col-lg-6">
<PluginHeaderActions
actions={this.props.actions}
/>
</div>
<div className="col-lg-6">
<PluginHeaderTitle
title={this.props.title}
description={this.props.description}
/>
</div>
<div className="col-lg-6">
<PluginHeaderActions
actions={this.props.actions}
/>
</div>
</div>
);

View File

@ -33,7 +33,7 @@ module.exports = {
_.forEach(strapi.plugins, (value, pluginName) => {
if (!_.includes(ignoredPlugins, pluginName)) {
// Main plugin `js` file
const pluginMainScript = $('<script>').attr('src', '/' + pluginName + '/main.js');
const pluginMainScript = $('<script>').attr('src', `/admin/${pluginName}/main.js`);
parsedHTML('body').append(pluginMainScript);
}
});
@ -41,5 +41,4 @@ module.exports = {
// Finally, return the HTML file with injected scripts
return parsedHTML.html();
}
};

View File

@ -1,38 +0,0 @@
/**
*
* PluginHeaderActions
*
*/
import React from 'react';
import styles from './styles.scss';
class PluginHeaderActions extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const actions = this.props.actions && this.props.actions.map((action, i) => (
<button
key={i}
className={`btn ${action.class} ${styles.btn}`}
onClick={action.onClick}
disabled={action.disabled}
>
{action.label}
</button>
));
return (
<div className={styles.pluginHeaderActions}>
<div className="pull-xs-right">
{actions}
</div>
</div>
);
}
}
PluginHeaderActions.propTypes = {
actions: React.PropTypes.array,
};
export default PluginHeaderActions;

View File

@ -1,11 +0,0 @@
// import PluginHeaderActions from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<PluginHeaderActions />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(false);
});
});

View File

@ -23,14 +23,9 @@ const logger = require('strapi-utils').logger;
*/
module.exports = (scope, cb) => {
let defaultName = scope.name;
if (defaultName === '.' || !defaultName) {
defaultName = path.basename(process.cwd());
}
// App info.
_.defaults(scope, {
name: defaultName,
name: scope.name === '.' || !scope.name ? scope.name : path.basename(process.cwd()),
author: process.env.USER || 'A Strapi developer',
email: process.env.EMAIL || '',
year: (new Date()).getFullYear(),

View File

@ -0,0 +1,7 @@
root = true
[*]
end_of_line = lf
insert_final_newline = false
indent_style = space
indent_size = 2

View File

@ -0,0 +1,103 @@
# From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
# Handle line endings automatically for files detected as text
# and leave all files detected as binary untouched.
* text=auto
#
# The above will handle all files NOT found below
#
#
## These files are text and should be normalized (Convert crlf => lf)
#
# source code
*.php text
*.css text
*.sass text
*.scss text
*.less text
*.styl text
*.js text eol=lf
*.coffee text
*.json text
*.htm text
*.html text
*.xml text
*.svg text
*.txt text
*.ini text
*.inc text
*.pl text
*.rb text
*.py text
*.scm text
*.sql text
*.sh text
*.bat text
# templates
*.ejs text
*.hbt text
*.jade text
*.haml text
*.hbs text
*.dot text
*.tmpl text
*.phtml text
# git config
.gitattributes text
.gitignore text
.gitconfig text
# code analysis config
.jshintrc text
.jscsrc text
.jshintignore text
.csslintrc text
# misc config
*.yaml text
*.yml text
.editorconfig text
# build config
*.npmignore text
*.bowerrc text
# Heroku
Procfile text
.slugignore text
# Documentation
*.md text
LICENSE text
AUTHORS text
#
## These files are binary and should be left untouched
#
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary

View File

@ -0,0 +1 @@
# Strapi plugin

View File

@ -0,0 +1,26 @@
/**
*
* Button
*
*/
import React from 'react';
import styles from './styles.scss';
class Button extends React.Component {
// eslint-disable-line react/prefer-stateless-function
render() {
return (
<button className={`btn btn-primary ${styles.button}`} {...this.props}>
{this.props.label}
</button>
);
}
}
Button.propTypes = {
label: React.PropTypes.string.isRequired,
};
export default Button;

View File

@ -0,0 +1,3 @@
.button {
}

View File

@ -0,0 +1,5 @@
/*
*
* App actions
*
*/

View File

@ -0,0 +1,5 @@
/*
*
* App constants
*
*/

View File

@ -0,0 +1,48 @@
/**
*
* This component is the skeleton around the actual pages, and should only
* contain code that should be seen on all pages. (e.g. navigation bar)
*
*/
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { pluginId } from 'app';
class App extends React.Component {
render() {
// Assign plugin component to children
const content = React.Children.map(this.props.children, child =>
React.cloneElement(child, {
exposedComponents: this.props.exposedComponents,
})
);
return (
<div className={pluginId}>
{React.Children.toArray(content)}
</div>
);
}
}
App.contextTypes = {
router: React.PropTypes.object.isRequired,
};
App.propTypes = {
children: React.PropTypes.node.isRequired,
exposedComponents: React.PropTypes.object.isRequired,
};
export function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const mapStateToProps = createStructuredSelector({});
// Wrap the component to inject dispatch and state into it
export default connect(mapStateToProps, mapDispatchToProps)(App);

View File

@ -0,0 +1,18 @@
/*
*
* App reducer
*
*/
import { fromJS } from 'immutable';
const initialState = fromJS({});
function appReducer(state = initialState, action) {
switch (action.type) {
default:
return state;
}
}
export default appReducer;

View File

@ -0,0 +1,25 @@
// import { createSelector } from 'reselect';
/**
* Direct selector to the list state domain
*/
// const selectGlobalDomain = () => state => state.get('global');
const selectLocationState = () => {
let prevRoutingState;
let prevRoutingStateJS;
return state => {
const routingState = state.get('route'); // or state.route
if (!routingState.equals(prevRoutingState)) {
prevRoutingState = routingState;
prevRoutingStateJS = routingState.toJS();
}
return prevRoutingStateJS;
};
};
export { selectLocationState };

View File

@ -0,0 +1,20 @@
/*
*
* HomePage actions
*
*/
import { LOAD_DATA, LOADED_DATA } from './constants';
export function loadData() {
return {
type: LOAD_DATA,
};
}
export function loadedData(data) {
return {
type: LOADED_DATA,
data,
};
}

View File

@ -0,0 +1,8 @@
/*
*
* HomePage constants
*
*/
export const LOAD_DATA = 'HomePage/LOAD_DATA';
export const LOADED_DATA = 'HomePage/LOADED_DATA';

View File

@ -0,0 +1,99 @@
/*
*
* HomePage
*
*/
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { injectIntl } from 'react-intl';
import { pluginId, pluginName, pluginDescription } from 'app';
import Button from 'components/Button';
import styles from './styles.scss';
import { loadData } from './actions';
import { makeSelectLoading, makeSelectData } from './selectors';
export class HomePage extends React.Component {
generateDataBlock() {
let dataBlock;
if (this.props.data) {
const items = this.props.data.map((item, i) => <li key={i}>{item}</li>);
dataBlock = (
<div>
<p>Data:</p>
<ul>{items}</ul>
</div>
);
}
return dataBlock;
}
render() {
// Generate the data block
const dataBlock = this.generateDataBlock();
// Plugin header config
const PluginHeader = this.props.exposedComponents.PluginHeader;
const pluginHeaderTitle = pluginName;
const pluginHeaderDescription = pluginDescription;
return (
<div className={styles.homePage}>
<PluginHeader
title={{
id: `${pluginId}-title`,
defaultMessage: pluginHeaderTitle,
}}
description={{
id: `${pluginId}-description`,
defaultMessage: pluginHeaderDescription,
}}
/>
<div className="row">
<div className="col-md-12">
<p>This is an example of a fake API call.</p>
<p>Loading: {this.props.loading ? 'yes' : 'no'}.</p>
{dataBlock}
<Button
label={this.props.loading ? 'Loading...' : 'Submit'}
disabled={this.props.loading}
onClick={this.props.loadData}
/>
</div>
</div>
</div>
);
}
}
HomePage.contextTypes = {
router: React.PropTypes.object.isRequired,
};
HomePage.propTypes = {
data: React.PropTypes.oneOfType([
React.PropTypes.bool,
React.PropTypes.object,
]),
exposedComponents: React.PropTypes.object.isRequired,
loadData: React.PropTypes.func.isRequired,
loading: React.PropTypes.bool.isRequired,
};
function mapDispatchToProps(dispatch) {
return {
loadData: () => dispatch(loadData()),
dispatch,
};
}
const mapStateToProps = createStructuredSelector({
loading: makeSelectLoading(),
data: makeSelectData(),
});
export default connect(mapStateToProps, mapDispatchToProps)(
injectIntl(HomePage)
);

View File

@ -0,0 +1,27 @@
/*
*
* HomePage reducer
*
*/
import { fromJS } from 'immutable';
import { LOAD_DATA, LOADED_DATA } from './constants';
const initialState = fromJS({
loading: false,
data: false,
});
function homePageReducer(state = initialState, action) {
switch (action.type) {
case LOAD_DATA:
return state.set('loading', true);
case LOADED_DATA:
return state.set('loading', false).set('data', fromJS(action.data));
default:
return state;
}
}
export default homePageReducer;

View File

@ -0,0 +1,32 @@
import { takeLatest } from 'redux-saga';
import { LOCATION_CHANGE } from 'react-router-redux';
import { put, fork, take, cancel } from 'redux-saga/effects';
import { loadedData } from './actions';
import { LOAD_DATA } from './constants';
export function* loadData() {
// Fake API request delay
yield new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
// Generate a random array
const data = Array(4).fill(0).map(() => Math.floor(Math.random() * 100));
yield put(loadedData(data));
}
// Individual exports for testing
export function* defaultSaga() {
const loadDataWatcher = yield fork(takeLatest, LOAD_DATA, loadData);
// Suspend execution until location changes
yield take(LOCATION_CHANGE);
yield cancel(loadDataWatcher);
}
// All sagas to be loaded
export default [defaultSaga];

View File

@ -0,0 +1,18 @@
import { createSelector } from 'reselect';
/**
* Direct selector to the homePage state domain
*/
const selectHomePageDomain = () => state => state.get('homePage');
/**
* Default selector used by HomePage
*/
const makeSelectLoading = () =>
createSelector(selectHomePageDomain(), substate => substate.get('loading'));
const makeSelectData = () =>
createSelector(selectHomePageDomain(), substate => substate.get('data'));
export { makeSelectLoading, makeSelectData };

View File

@ -0,0 +1,3 @@
.homePage {
}

View File

@ -0,0 +1,6 @@
{
"/": {
"name": "homePage",
"container": "HomePage"
}
}

View File

@ -0,0 +1,72 @@
'use strict';
/**
* Module dependencies
*/
// Public node modules.
const _ = require('lodash');
/**
* Expose main package JSON of the application
* with basic info, dependencies, etc.
*/
module.exports = scope => {
const cliPkg = scope.strapiPackageJSON || {};
// Finally, return the JSON.
return _.merge(scope.appPackageJSON || {}, {
'name': `strapi-plugin-${scope.id}`,
'version': '0.0.0',
'description': 'This is the description of the plugin.',
'strapi': {
'name': scope.name,
'icon': 'ion-document-text'
},
'scripts': {
'analyze:clean': 'node_modules/strapi-helper-plugin/node_modules/rimraf/bin.js stats.json',
'preanalyze': 'npm run analyze:clean',
'analyze': 'node node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js',
'postinstall': 'npm run build:dll',
'prebuild': 'npm run build:clean && npm run test',
'build': 'node_modules/strapi-helper-plugin/node_modules/cross-env/bin/cross-env.js NODE_ENV=production node_modules/strapi-helper-plugin/node_modules/webpack/bin/webpack.js --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress',
'build:clean': 'node_modules/strapi-helper-plugin/node_modules/rimraf/bin.js admin/build',
'build:dll': 'node node_modules/strapi-helper-plugin/lib/internals/scripts/dependencies.js',
'start': 'node_modules/strapi-helper-plugin/node_modules/cross-env/bin/cross-env.js NODE_ENV=development node node_modules/strapi-helper-plugin/lib/server',
'generate': 'node_modules/strapi-helper-plugin/node_modules/plop/plop.js --plopfile node_modules/strapi-helper-plugin/lib/internals/generators/index.js',
'lint': 'node_modules/strapi-helper-plugin/node_modules/eslint/bin/eslint.js --ignore-path .gitignore --config node_modules/strapi-helper-plugin/lib/internals/eslint/.eslintrc.json admin',
'pretest': 'npm run lint',
'prettier': 'node_modules/strapi-helper-plugin/node_modules/prettier/bin/prettier.js --single-quote --trailing-comma es5 --write \'{admin,__{tests,mocks}__}/**/*.js\'',
'test': 'echo Tests are not implemented.',
'prepublish': 'npm run build'
},
'dependencies': {},
'devDependencies': {
'strapi-helper-plugin': '3.0.0-alpha.3'
},
'author': {
'name': scope.author || 'A Strapi developer',
'email': scope.email || '',
'url': scope.website || ''
},
'maintainers': [{
'name': scope.author || 'A Strapi developer',
'email': scope.email || '',
'url': scope.website || ''
}],
'engines': {
'node': '>= 7.0.0',
'npm': '>= 3.0.0'
},
'license': scope.license || 'MIT'
});
};
/**
* Get dependencies version
*/
function getDependencyVersion(packageJSON, module) {
return module === packageJSON.name ? packageJSON.version : packageJSON.dependencies && packageJSON.dependencies[module];
}

View File

@ -6,6 +6,8 @@
// Public node modules.
const _ = require('lodash');
const fs = require('fs-extra');
const path = require('path');
/**
* This `before` function is run before generating targets.
@ -31,19 +33,29 @@ module.exports = (scope, cb) => {
ext: '.js'
});
// Plugin info.
_.defaults(scope, {
name: scope.args.name || scope.id,
author: scope.author || 'A Strapi developer',
email: scope.email || '',
year: (new Date()).getFullYear(),
license: 'MIT'
});
// Take another pass to take advantage of the defaults absorbed in previous passes.
_.defaults(scope, {
filename: `${scope.globalID}${scope.ext}`
});
// Humanize output.
_.defaults(scope, {
humanizeId: _.camelCase(scope.id).toLowerCase(),
humanizeId: scope.id.toLowerCase(),
humanizedPath: '`./plugins`'
});
// Copy the admin files.
fs.copySync(path.resolve(__dirname, '..', 'files'), path.resolve(scope.rootPath, 'plugins', scope.humanizeId));
// Trigger callback with no error to proceed.
return cb.success();
};

View File

@ -8,6 +8,7 @@
const path = require('path');
// Local dependencies.
const packageJSON = require('../json/package.json.js');
const routesJSON = require('../json/routes.json.js');
/**
@ -35,6 +36,11 @@ module.exports = {
// Generate routes.
'plugins/:humanizeId/config/routes.json': {
jsonfile: routesJSON
}
},
// Main package.
'plugins/:humanizeId/package.json': {
jsonfile: packageJSON
},
}
};

View File

@ -13,6 +13,7 @@
"lib": "./lib"
},
"dependencies": {
"fs-extra": "~0.30.0",
"lodash": "^4.16.5",
"pluralize": "~3.1.0"
},

View File

@ -0,0 +1,10 @@
# Don't check auto-generated stuff into git
coverage
build
node_modules
stats.json
# Cruft
.DS_Store
npm-debug.log
.idea

View File

@ -0,0 +1,5 @@
# Strapi Helper Plugin
## Description
Helper to develop Strapi plugins.

View File

@ -2,11 +2,9 @@ const resolve = require('path').resolve;
const pullAll = require('lodash/pullAll');
const uniq = require('lodash/uniq');
const merge = require('lodash/uniq');
const ReactBoilerplate = {
// This refers to the react-boilerplate version this project is based on.
version: '3.0.0',
const StrapiPlugin = {
/**
* The DLL Plugin provides a dramatic speed increase to webpack build and hot module reloading
* by caching the module metadata for all of our npm dependencies. We enable it by default
@ -26,9 +24,7 @@ const ReactBoilerplate = {
'compression',
'cross-env',
'express',
'ip',
'minimist',
'sanitize.css',
],
/**
@ -38,20 +34,20 @@ const ReactBoilerplate = {
include: ['core-js', 'eventsource-polyfill', 'babel-polyfill', 'lodash'],
// The path where the DLL manifest and bundle will get built
path: resolve('../node_modules/react-boilerplate-dlls'),
path: resolve('../node_modules/strapi-plugin-dlls'),
},
entry(pkg) {
const dependencyNames = Object.keys(pkg.dependencies);
const exclude = pkg.dllPlugin.exclude || ReactBoilerplate.dllPlugin.defaults.exclude;
const include = pkg.dllPlugin.include || ReactBoilerplate.dllPlugin.defaults.include;
entry(helperPkg, pluginPkg) {
const dependencyNames = merge(Object.keys(helperPkg.dependencies), Object.keys(pluginPkg.dependencies));
const exclude = pluginPkg.dllPlugin.exclude || StrapiPlugin.dllPlugin.defaults.exclude;
const include = pluginPkg.dllPlugin.include || StrapiPlugin.dllPlugin.defaults.include;
const includeDependencies = uniq(dependencyNames.concat(include));
return {
reactBoilerplateDeps: pullAll(includeDependencies, exclude),
strapiPluginDeps: pullAll(includeDependencies, exclude),
};
},
},
};
module.exports = ReactBoilerplate;
module.exports = StrapiPlugin;

View File

@ -0,0 +1,93 @@
{
"parser": "babel-eslint",
"extends": [
"airbnb",
"prettier"
],
"env": {
"browser": true,
"node": true,
"mocha": true,
"es6": true
},
"plugins": [
"redux-saga",
"react",
"jsx-a11y"
],
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"comma-dangle": [
2,
"always-multiline"
],
"import/newline-after-import": 0,
"import/no-dynamic-require": 0,
"import/no-extraneous-dependencies": 0,
"import/no-named-as-default": 0,
"import/no-unresolved": 2,
"import/prefer-default-export": 0,
"import/order": [
"error",
{
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index"
]
}
],
"import/imports-first": 2,
"indent": [
2,
2,
{
"SwitchCase": 1
}
],
"jsx-a11y/aria-props": 2,
"jsx-a11y/heading-has-content": 0,
"jsx-a11y/href-no-hash": 2,
"jsx-a11y/label-has-for": 2,
"jsx-a11y/mouse-events-have-key-events": 2,
"jsx-a11y/role-has-required-aria-props": 2,
"jsx-a11y/role-supports-aria-props": 2,
"max-len": 0,
"newline-per-chained-call": 0,
"no-console": 1,
"no-use-before-define": 0,
"prefer-template": 2,
"class-methods-use-this": 0,
"react/forbid-prop-types": 0,
"react/jsx-first-prop-new-line": [
2,
"multiline"
],
"react/jsx-filename-extension": 0,
"react/jsx-no-target-blank": 0,
"react/require-extension": 0,
"react/self-closing-comp": 0,
"redux-saga/no-yield-in-race": 2,
"redux-saga/yield-effects": 2,
"require-yield": 0,
"react/no-unescaped-entities": 0,
"react/prefer-stateless-function": 0,
"react/sort-prop-types": 2
},
"settings": {
"import/resolver": {
"webpack": {
"config": "./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.test.babel.js"
}
}
}
}

View File

@ -0,0 +1,8 @@
const config = require('./.eslintrc.json');
// Update the eslint configuration for `strapi-helper-plugin` module
if (process.env.IS_HELPER) {
config.settings['import/resolver'].webpack.config = './lib/internals/webpack/webpack.test.babel.js'
}
module.exports = config;

View File

@ -39,12 +39,12 @@ module.exports = {
// Generate index.js and index.test.js
const actions = [{
type: 'add',
path: '../../app/components/{{properCase name}}/index.js',
path: '../../../../../admin/src/components/{{properCase name}}/index.js',
templateFile: data.type === 'ES6 Class' ? './component/es6.js.hbs' : './component/stateless.js.hbs',
abortOnFail: true,
}, {
type: 'add',
path: '../../app/components/{{properCase name}}/tests/index.test.js',
path: '../../../../../admin/src/components/{{properCase name}}/tests/index.test.js',
templateFile: './component/test.js.hbs',
abortOnFail: true,
}];
@ -53,7 +53,7 @@ module.exports = {
if (data.wantCSS) {
actions.push({
type: 'add',
path: '../../app/components/{{properCase name}}/styles.scss',
path: '../../../../../admin/src/components/{{properCase name}}/styles.scss',
templateFile: './component/styles.scss.hbs',
abortOnFail: true,
});
@ -63,7 +63,7 @@ module.exports = {
if (data.wantMessages) {
actions.push({
type: 'add',
path: '../../app/components/{{properCase name}}/messages.js',
path: '../../../../../admin/src/components/{{properCase name}}/messages.js',
templateFile: './component/messages.js.hbs',
abortOnFail: true,
});

View File

@ -0,0 +1,7 @@
/*
*
* {{ properCase name }} constants
*
*/
export const DEFAULT_ACTION = 'src/{{ properCase name }}/DEFAULT_ACTION';

View File

@ -48,12 +48,12 @@ module.exports = {
// Generate index.js and index.test.js
const actions = [{
type: 'add',
path: '../../app/containers/{{properCase name}}/index.js',
path: '../../../../../admin/src/containers/{{properCase name}}/index.js',
templateFile: './container/index.js.hbs',
abortOnFail: true,
}, {
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/index.test.js',
path: '../../../../../admin/src/containers/{{properCase name}}/tests/index.test.js',
templateFile: './container/test.js.hbs',
abortOnFail: true,
}];
@ -62,7 +62,7 @@ module.exports = {
if (data.wantCSS) {
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/styles.scss',
path: '../../../../../admin/src/containers/{{properCase name}}/styles.scss',
templateFile: './container/styles.scss.hbs',
abortOnFail: true,
});
@ -72,7 +72,7 @@ module.exports = {
if (data.wantMessages) {
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/messages.js',
path: '../../../../../admin/src/containers/{{properCase name}}/messages.js',
templateFile: './container/messages.js.hbs',
abortOnFail: true,
});
@ -84,13 +84,13 @@ module.exports = {
// Actions
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/actions.js',
path: '../../../../../admin/src/containers/{{properCase name}}/actions.js',
templateFile: './container/actions.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/actions.test.js',
path: '../../../../../admin/src/containers/{{properCase name}}/tests/actions.test.js',
templateFile: './container/actions.test.js.hbs',
abortOnFail: true,
});
@ -98,7 +98,7 @@ module.exports = {
// Constants
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/constants.js',
path: '../../../../../admin/src/containers/{{properCase name}}/constants.js',
templateFile: './container/constants.js.hbs',
abortOnFail: true,
});
@ -106,13 +106,13 @@ module.exports = {
// Selectors
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/selectors.js',
path: '../../../../../admin/src/containers/{{properCase name}}/selectors.js',
templateFile: './container/selectors.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/selectors.test.js',
path: '../../../../../admin/src/containers/{{properCase name}}/tests/selectors.test.js',
templateFile: './container/selectors.test.js.hbs',
abortOnFail: true,
});
@ -120,13 +120,13 @@ module.exports = {
// Reducer
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/reducer.js',
path: '../../../../../admin/src/containers/{{properCase name}}/reducer.js',
templateFile: './container/reducer.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/reducer.test.js',
path: '../../../../../admin/src/containers/{{properCase name}}/tests/reducer.test.js',
templateFile: './container/reducer.test.js.hbs',
abortOnFail: true,
});
@ -136,13 +136,13 @@ module.exports = {
if (data.wantSagas) {
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/sagas.js',
path: '../../../../../admin/src/containers/{{properCase name}}/sagas.js',
templateFile: './container/sagas.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/sagas.test.js',
path: '../../../../../admin/src/containers/{{properCase name}}/tests/sagas.test.js',
templateFile: './container/sagas.test.js.hbs',
abortOnFail: true,
});

View File

@ -5,6 +5,7 @@
*/
const fs = require('fs');
const path = require('path');
const componentGenerator = require('./component/index.js');
const containerGenerator = require('./container/index.js');
@ -18,10 +19,10 @@ module.exports = (plop) => {
plop.setGenerator('language', languageGenerator);
plop.addHelper('directory', (comp) => {
try {
fs.accessSync(`app/containers/${comp}`, fs.F_OK);
return `containers/${comp}`;
fs.accessSync(`${path.resolve(process.cwd(), 'admin', 'src', 'containers', comp)}`, fs.F_OK);
return `${path.resolve(process.cwd(), 'admin', 'src', 'containers', comp)}`;
} catch (e) {
return `components/${comp}`;
return `${path.resolve(process.cwd(), 'admin', 'src', 'components', comp)}`;
}
});
plop.addHelper('curly', (object, open) => (open ? '{' : '}'));

View File

@ -23,43 +23,43 @@ module.exports = {
const actions = [];
actions.push({
type: 'modify',
path: '../../app/i18n.js',
path: '../../../../../admin/src/i18n.js',
pattern: /('react-intl\/locale-data\/[a-z]+';\n)(?!.*'react-intl\/locale-data\/[a-z]+';)/g,
templateFile: './language/intl-locale-data.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
path: '../../../../../admin/src/i18n.js',
pattern: /([\n\s'[a-z]+',)(?!.*[\n\s'[a-z]+',)/g,
templateFile: './language/app-locale.hbs',
templateFile: './language/src-locale.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
path: '../../../../../admin/src/i18n.js',
pattern: /(from\s'.\/translations\/[a-z]+.json';\n)(?!.*from\s'.\/translations\/[a-z]+.json';)/g,
templateFile: './language/translation-messages.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
path: '../../../../../admin/src/i18n.js',
pattern: /(addLocaleData\([a-z]+LocaleData\);\n)(?!.*addLocaleData\([a-z]+LocaleData\);)/g,
templateFile: './language/add-locale-data.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
path: '../../../../../admin/src/i18n.js',
pattern: /([a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),\n)(?!.*[a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),)/g,
templateFile: './language/format-translation-messages.hbs',
});
actions.push({
type: 'add',
path: '../../app/translations/{{language}}.json',
path: '../../../../../admin/src/translations/{{language}}.json',
templateFile: './language/translations-json.hbs',
abortOnFail: true,
});
actions.push({
type: 'modify',
path: '../../app/app.js',
path: '../../../../../admin/src/app.js',
pattern: /(System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),\n)(?!.*System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),)/g,
templateFile: './language/polyfill-intl-locale.hbs',
});

View File

@ -7,7 +7,7 @@ const componentExists = require('../utils/componentExists');
function reducerExists(comp) {
try {
fs.accessSync(`app/containers/${comp}/reducer.js`, fs.F_OK);
fs.accessSync(`src/containers/${comp}/reducer.js`, fs.F_OK);
return true;
} catch (e) {
return false;
@ -16,7 +16,7 @@ function reducerExists(comp) {
function sagasExists(comp) {
try {
fs.accessSync(`app/containers/${comp}/sagas.js`, fs.F_OK);
fs.accessSync(`src/containers/${comp}/sagas.js`, fs.F_OK);
return true;
} catch (e) {
return false;
@ -63,14 +63,14 @@ module.exports = {
data.useSagas = sagasExists(data.component); // eslint-disable-line no-param-reassign
actions.push({
type: 'modify',
path: '../../app/routes.js',
path: '../../../../../admin/src/routes.js',
pattern: /(\s{\n\s{0,}path: '\*',)/g,
template: trimTemplateFile('routeWithReducer.hbs'),
});
} else {
actions.push({
type: 'modify',
path: '../../app/routes.js',
path: '../../../../../admin/src/routes.js',
pattern: /(\s{\n\s{0,}path: '\*',)/g,
template: trimTemplateFile('route.hbs'),
});

View File

@ -5,8 +5,10 @@
*/
const fs = require('fs');
const pageComponents = fs.readdirSync('app/components');
const pageContainers = fs.readdirSync('app/containers');
const path = require('path');
const pageComponents = fs.readdirSync(path.resolve(process.cwd(), 'admin', 'src', 'components'));
const pageContainers = fs.readdirSync(path.resolve(process.cwd(), 'admin', 'src', 'containers'));
const components = pageComponents.concat(pageContainers);
function componentExists(comp) {

View File

@ -0,0 +1,28 @@
#!/usr/bin/env node
const path = require('path');
const chalk = require('chalk');
const shelljs = require('shelljs');
const animateProgress = require('./helpers/progress');
const addCheckMark = require('./helpers/checkmark');
const progress = animateProgress('Generating stats');
// Generate stats.json file with webpack
shelljs.exec(
`./node_modules/strapi-helper-plugin/node_modules/webpack/bin/webpack.js --config ${path.resolve(__dirname, '..', 'webpack', 'webpack.prod.babel.js')} --profile --json > stats.json`,
addCheckMark.bind(null, callback) // Output a checkmark on completion
);
// Called after webpack has finished generating the stats.json file
function callback() {
clearInterval(progress);
process.stdout.write(
`
\n\nOpen ${chalk.magenta('http://webpack.github.io/analyse/')} in your browser and upload the stats.json file!
${chalk.blue('\n(Tip: (\'CMD + double-click\') the link!)\n\n')}
`
);
}

View File

@ -0,0 +1,49 @@
/*eslint-disable*/
// No need to build the DLL in production
if (process.env.NODE_ENV === 'production') {
process.exit(0);
}
require('shelljs/global');
const path = require('path');
const fs = require('fs');
const exists = fs.existsSync;
const writeFile = fs.writeFileSync;
const defaults = require('lodash/defaultsDeep');
const pkg = require(path.join(process.cwd(), 'package.json'));
const config = require('../config');
const dllConfig = defaults(pkg.dllPlugin, config.dllPlugin.defaults);
const outputPath = path.join(process.cwd(), dllConfig.path);
const dllManifestPath = path.join(outputPath, 'package.json');
/**
* I use node_modules/strapi-plugin-dlls by default just because
* it isn't going to be version controlled and babel wont try to parse it.
*/
mkdir('-p', outputPath);
echo('Building the Webpack DLL...');
/**
* Create a manifest so npm install doesnt warn us
*/
if (!exists(dllManifestPath)) {
writeFile(
dllManifestPath,
JSON.stringify(defaults({
name: 'strapi-plugin-dlls',
private: true,
author: pkg.author,
repository: pkg.repository,
version: pkg.version
}), null, 2),
'utf8'
)
}
// the BUILDING_DLL env var is set to avoid confusing the development environment
exec('./node_modules/strapi-helper-plugin/node_modules/cross-env/bin/cross-env.js BUILDING_DLL=true ./node_modules/strapi-helper-plugin/node_modules/webpack/bin/webpack.js --display-chunks --color --config ./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.dll.babel.js');

View File

@ -1,4 +1,4 @@
var chalk = require('chalk');
const chalk = require('chalk');
/**
* Adds mark check symbol

View File

@ -0,0 +1,19 @@
const readline = require('readline');
/**
* Adds an animated progress indicator
*
* @param {string} message The message to write next to the indicator
*/
const animateProgress = (message) => {
const amountOfDots = 3;
let i = 0;
return setInterval(() => {
readline.cursorTo(process.stdout, 0);
i = (i + 1) % (amountOfDots + 1);
const dots = new Array(i + 1).join('.');
process.stdout.write(message + dots);
}, 500);
}
module.exports = animateProgress;

View File

@ -3,7 +3,7 @@ var exec = require('child_process').exec;
exec('npm -v', function (err, stdout, stderr) {
if (err) throw err;
if (parseFloat(stdout) < 3) {
throw new Error('[ERROR: React Boilerplate] You need npm version @>=3');
throw new Error('[ERROR: Strapi plugin] You need npm version @>=3');
process.exit(1);
}
});

View File

@ -9,20 +9,57 @@ const webpack = require('webpack');
module.exports = (options) => ({
entry: options.entry,
output: Object.assign({ // Compile into js/build.js
path: path.resolve(process.cwd(), 'build'),
path: path.resolve(process.cwd(), 'admin', 'build'),
publicPath: '/',
}, options.output), // Merge with env dependent settings
module: {
loaders: [{
test: /\.js$/, // Transform all .js files required somewhere with Babel
loader: 'babel',
exclude: /node_modules/,
query: options.babelQuery,
use: {
loader: 'babel',
options: {
"presets": [
[
require.resolve('babel-preset-latest'),
{
"es2015": {
"modules": false,
},
},
],
require.resolve('babel-preset-react'),
require.resolve('babel-preset-stage-0'),
],
"env": {
"production": {
"only": [
"src",
],
"plugins": [
require.resolve('babel-plugin-transform-react-remove-prop-types'),
require.resolve('babel-plugin-transform-react-constant-elements'),
require.resolve('babel-plugin-transform-react-inline-elements'),
],
},
"test": {
"plugins": [
"istanbul",
],
},
},
},
},
include: [
path.join(process.cwd(), 'admin', 'src'),
// Add the `strapi-helper-plugin` folders watched by babel
path.join(process.cwd(), 'node_modules', 'strapi-helper-plugin', 'lib', 'src'),
],
}, {
// Transform our own .css files with PostCSS and CSS-modules
// Transform our own .scss files
test: /\.scss$/,
exclude: /node_modules/,
loader: options.cssLoaders,
// loader: 'null-loader'
use: options.cssLoaders,
}, {
// Do not transform vendor's CSS with CSS-modules
// The point is that they remain in global scope.
@ -66,7 +103,13 @@ module.exports = (options) => ({
new webpack.NamedModulesPlugin(),
]),
resolve: {
modules: ['node_modules'],
modules: [
'admin/src',
'node_modules/strapi-helper-plugin/lib/src',
'node_modules/strapi-helper-plugin/node_modules',
'node_modules',
],
symlinks: false,
extensions: [
'.js',
'.jsx',
@ -78,6 +121,13 @@ module.exports = (options) => ({
'main',
],
},
resolveLoader: {
modules: [
path.join(__dirname, '..', '..', '..', 'node_modules'),
path.join(process.cwd(), 'node_modules'),
],
},
devtool: options.devtool,
target: 'web', // Make web variables accessible to webpack, e.g. window
});

View File

@ -2,8 +2,8 @@
* DEVELOPMENT WEBPACK CONFIGURATION
*/
const path = require('path');
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const argv = require('minimist')(process.argv.slice(2));
@ -28,7 +28,7 @@ module.exports = require('./webpack.base.babel')({
entry: [
'eventsource-polyfill', // Necessary for hot reloading with IE
`webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr`,
path.join(process.cwd(), 'app/app.js'), // Start with js/app.js
path.join(process.cwd(), 'node_modules', 'strapi-helper-plugin', 'lib', 'src', 'app.js'),
],
// Don't use hashes in dev mode for better performance
@ -41,9 +41,25 @@ module.exports = require('./webpack.base.babel')({
// Add development plugins
plugins: dependencyHandlers().concat(plugins), // eslint-disable-line no-use-before-define
// Load the SCSS in a style tag in development
// cssLoaders: 'style-loader!css-loader?modules&importLoaders=1&sourceMap!postcss-loader!sass-loader',
cssLoaders: `style-loader!css-loader?localIdentName=${pluginId}[local]__[path][name]__[hash:base64:5]&modules&importLoaders=1&sourceMap!postcss-loader!sass-loader`,
// Transform our own .scss files
cssLoaders: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
options: {
localIdentName: `${pluginId}[local]__[path][name]__[hash:base64:5]`,
modules: true,
importLoaders: 1,
sourceMap: true,
},
}, {
loader: 'postcss-loader',
options: {
config: path.resolve(__dirname, '..', 'postcss', 'postcss.config.js'),
},
}, {
loader: 'sass-loader',
}],
// Process the CSS with PostCSS
postcssPlugins: [
@ -91,16 +107,14 @@ function dependencyHandlers() {
];
}
const dllPath = path.resolve(process.cwd(), dllPlugin.path || 'node_modules/react-boilerplate-dlls');
const dllPath = path.resolve(process.cwd(), dllPlugin.path || 'node_modules/strapi-plugin-dlls');
/**
* If DLLs aren't explicitly defined, we assume all production dependencies listed in package.json
* Reminder: You need to exclude any server side dependencies by listing them in dllConfig.exclude
*
* @see https://github.com/mxstbr/react-boilerplate/tree/master/docs/general/webpack.md
*/
if (!dllPlugin.dlls) {
const manifestPath = path.resolve(dllPath, 'reactBoilerplateDeps.json');
const manifestPath = path.resolve(dllPath, 'strapiPluginDeps.json');
if (!fs.existsSync(manifestPath)) {
logger.error('The DLL manifest is missing. Please run `npm run build:dll`');

View File

@ -14,15 +14,16 @@ const defaults = require('lodash/defaultsDeep');
const webpack = require('webpack');
const dllPlugin = require('../config').dllPlugin;
const helperPkg = require(join(__dirname, '..', '..', '..', 'package.json'));
const pkg = require(join(process.cwd(), 'package.json'));
if (!pkg.dllPlugin) { process.exit(0); }
const dllConfig = defaults(pkg.dllPlugin, dllPlugin.defaults);
const pluginPkg = require(join(process.cwd(), 'package.json'));
if (!pluginPkg.dllPlugin) { pluginPkg.dllPlugin = {}; }
const dllConfig = defaults(pluginPkg.dllPlugin, dllPlugin.defaults);
const outputPath = join(process.cwd(), dllConfig.path);
module.exports = {
context: process.cwd(),
entry: dllConfig.dlls ? dllConfig.dlls : dllPlugin.entry(pkg),
entry: dllPlugin.entry(helperPkg, pluginPkg),
devtool: 'eval',
output: {
filename: '[name].dll.js',
@ -32,4 +33,10 @@ module.exports = {
plugins: [
new webpack.DllPlugin({ name: '[name]', path: join(outputPath, '[name].json') }), // eslint-disable-line no-new
],
resolve: {
modules: [
'node_modules',
'node_modules/strapi-helper-plugin/node_modules',
],
},
};

View File

@ -12,20 +12,35 @@ const pluginId = pkg.name.replace(/^strapi-/i, '');
module.exports = require('./webpack.base.babel')({
// In production, we skip all hot-reloading stuff
entry: [
path.join(process.cwd(), 'app/app.js'),
path.join(process.cwd(), 'node_modules', 'strapi-helper-plugin', 'lib', 'src', 'app.js'),
],
// Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
output: {
filename: '[name].js',
chunkFilename: '[name].[chunkhash].chunk.js',
// publicPath: 'http://localhost:1337/settings-manager/',
publicPath: '/content-manager/',
publicPath: `/${pluginId}/`,
},
// We use ExtractTextPlugin so we get a seperate SCSS file instead
// of the CSS being in the JS and injected as a style tag
cssLoaders: `style-loader!css-loader?localIdentName=${pluginId}[local]__[path][name]__[hash:base64:5]&modules&importLoaders=1&sourceMap!postcss-loader!sass-loader`,
// Transform our own .scss files
cssLoaders: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
options: {
localIdentName: `${pluginId}[local]__[path][name]__[hash:base64:5]`,
modules: true,
importLoaders: 1,
sourceMap: true,
},
}, {
loader: 'postcss-loader',
options: {
config: path.resolve(__dirname, '..', 'postcss', 'postcss.config.js'),
},
}, {
loader: 'sass-loader',
}],
// In production, we minify our CSS with cssnano
postcssPlugins: [
@ -45,10 +60,6 @@ module.exports = require('./webpack.base.babel')({
async: true,
}),
// Merge all duplicate modules
new webpack.optimize.DedupePlugin(),
// Minify and optimize the JavaScript
new webpack.optimize.UglifyJsPlugin({
compress: {

View File

@ -7,7 +7,10 @@ const path = require('path');
const webpack = require('webpack');
const modules = [
'src',
'node_modules',
'node_modules/strapi-helper-plugin/lib/src',
'node_modules/strapi-helper-plugin/node_modules',
];
module.exports = {
@ -27,7 +30,7 @@ module.exports = {
preLoaders: [
{ test: /\.js$/,
loader: 'isparta',
include: path.resolve('app/'),
include: path.resolve('src/'),
},
],
loaders: [

View File

@ -45,6 +45,8 @@ const addDevMiddlewares = (app, webpackConfig) => {
*/
module.exports = (app) => {
const webpackConfig = require('../../internals/webpack/webpack.dev.babel');
// const webpackConfig = require(path.resolve(process.cwd(), 'node_modules', 'strapi-helper-plugin', 'internals', 'webpack', 'webpack.dev.babel'));
addDevMiddlewares(app, webpackConfig);
return app;

View File

@ -8,18 +8,20 @@
import React from 'react';
import { Provider } from 'react-redux';
import { syncHistoryWithStore } from 'react-router-redux';
import App from './containers/App';
import createRoutes from './routes';
import configureStore from './store';
import { selectLocationState } from './containers/App/selectors';
import { translationMessages } from './i18n';
import App from 'containers/App'; // eslint-disable-line
import { selectLocationState } from 'containers/App/selectors'; // eslint-disable-line
import configureStore from 'store';
import createRoutes from 'routes';
// import { translationMessages } from 'i18n';
// Plugin identifier based on the package.json `name` value
const pluginId = require('../package.json').name.replace(
const pluginPkg = require('../../../../package.json');
const pluginId = pluginPkg.name.replace(
/^strapi-plugin-/i,
''
);
const pluginName = pluginPkg.strapi.name;
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const apiUrl = window.Strapi && `${window.Strapi.apiUrl}/${pluginId}`;
const router = window.Strapi.router;
@ -51,17 +53,16 @@ Comp.contextTypes = {
};
// Register the plugin
if (window.Strapi) {
window.Strapi.registerPlugin({
name: 'Content Manager',
icon: 'ion-document-text',
id: pluginId,
leftMenuLinks: [],
mainComponent: Comp,
routes: createRoutes(store),
translationMessages,
});
}
window.Strapi.registerPlugin({
name: pluginPkg.strapi.name,
icon: pluginPkg.strapi.icon,
id: pluginId,
leftMenuLinks: [],
mainComponent: Comp,
routes: createRoutes(store),
// translationMessages,
});
// Export store
export { store, apiUrl, pluginId, router };
export { store, apiUrl, pluginId, pluginName, pluginDescription, router };

View File

@ -0,0 +1,24 @@
/**
* i18n.js
*
* This will setup the i18n language files and locale data for your plugin.
*
*/
import { defineMessages } from 'react-intl';
import enTranslationMessages from './translations/en.json'; // eslint-disable-line
import frTranslationMessages from './translations/fr.json'; // eslint-disable-line
const translationMessages = {
en: enTranslationMessages,
fr: frTranslationMessages,
};
const define = messages => {
defineMessages(messages);
};
export { translationMessages, define };

View File

@ -7,7 +7,7 @@ import { combineReducers } from 'redux-immutable';
import { fromJS } from 'immutable';
import { LOCATION_CHANGE } from 'react-router-redux';
import globalReducer from './containers/App/reducer';
import globalReducer from 'containers/App/reducer'; // eslint-disable-line
/*
* routeReducer

View File

@ -0,0 +1,41 @@
// These are the pages you can go to.
// They are all wrapped in the App component, which should contain the navbar etc
// See http://blog.mxstbr.com/2016/01/react-apps-with-pages for more information
// about the code splitting business
import { map } from 'lodash';
import { getAsyncInjectors } from 'utils/asyncInjectors';
import routes from 'routes.json'; // eslint-disable-line
// Try to require a node module without throwing an error
const tryRequire = (path) => {
try {
return require(`containers/${path}.js`); // eslint-disable-line global-require
} catch (err) {
return null;
}
};
export default function createRoutes(store) {
// Create reusable async injectors using getAsyncInjectors factory
const { injectReducer, injectSagas } = getAsyncInjectors(store); // eslint-disable-line no-unused-vars
// Inject app sagas
const appSagas = tryRequire('App/sagas');
if (appSagas) injectSagas(appSagas.default);
return map(routes, (route, key) => ({
path: key === '/' ? '' : key,
name: route.name,
getComponent(nextState, cb) {
const reducer = tryRequire(`${route.container}/reducer`); // eslint-disable-line global-require
const sagas = tryRequire(`${route.container}/sagas`); // eslint-disable-line global-require
const component = tryRequire(`${route.container}/index`); // eslint-disable-line global-require
process.nextTick(() => {
if (reducer) injectReducer(route.name, reducer.default);
if (sagas) injectSagas(sagas.default);
cb(null, component.default);
});
},
}));
}

View File

@ -1,8 +1,7 @@
import { conformsTo, isEmpty, isFunction, isObject, isString } from 'lodash';
import invariant from 'invariant';
import warning from 'warning';
import createReducer from '../reducers';
import createReducer from 'reducers';
/**
* Validate the shape of redux store
@ -18,7 +17,7 @@ export function checkStore(store) {
};
invariant(
conformsTo(store, shape),
'(app/utils...) asyncInjectors: Expected a valid redux store'
'(src/utils...) asyncInjectors: Expected a valid redux store'
);
}
@ -31,7 +30,7 @@ export function injectAsyncReducer(store, isValid) {
invariant(
isString(name) && !isEmpty(name) && isFunction(asyncReducer),
'(app/utils...) injectAsyncReducer: Expected `asyncReducer` to be a reducer function'
'(src/utils...) injectAsyncReducer: Expected `asyncReducer` to be a reducer function'
);
store.asyncReducers[name] = asyncReducer; // eslint-disable-line no-param-reassign
@ -48,12 +47,12 @@ export function injectAsyncSagas(store, isValid) {
invariant(
Array.isArray(sagas),
'(app/utils...) injectAsyncSagas: Expected `sagas` to be an array of generator functions'
'(src/utils...) injectAsyncSagas: Expected `sagas` to be an array of generator functions'
);
warning(
!isEmpty(sagas),
'(app/utils...) injectAsyncSagas: Received an empty `sagas` array'
'(src/utils...) injectAsyncSagas: Received an empty `sagas` array'
);
sagas.map(store.runSaga);

View File

@ -0,0 +1,124 @@
{
"name": "strapi-helper-plugin",
"version": "3.0.0-alpha.3",
"description": "Helper for Strapi plugins development",
"engines": {
"node": ">= 7.0.0",
"npm": ">= 3.0.0"
},
"author": {
"email": "hi@strapi.io",
"name": "Strapi team",
"url": "http://strapi.io"
},
"maintainers": [
{
"name": "Strapi team",
"email": "hi@strapi.io",
"url": "http://strapi.io"
}
],
"license": "MIT",
"scripts": {
"lint": "./node_modules/cross-env/bin/cross-env.js IS_HELPER=true ./node_modules/eslint/bin/eslint.js --ignore-path .gitignore --config ./lib/internals/eslint/eslint-config.js .",
"pretest": "npm run lint",
"prettier": "./node_modules/prettier/bin/prettier.js --single-quote --trailing-comma es5 --write \"{lib,__{tests,mocks}__}/**/*.js\"",
"test": "echo Tests are not implemented."
},
"pre-commit": [
"lint:admin"
],
"dependencies": {
"babel-polyfill": "6.13.0",
"chalk": "1.1.3",
"compression": "1.6.2",
"cross-env": "3.1.3",
"express": "4.14.0",
"fontfaceobserver": "2.0.1",
"history": "3.0.0",
"immutable": "3.8.1",
"intl": "1.2.4",
"invariant": "2.2.1",
"lodash": "4.15.0",
"react": "15.3.0",
"react-dom": "15.3.0",
"react-helmet": "3.1.0",
"react-intl": "2.1.3",
"react-redux": "4.4.5",
"react-router": "2.6.1",
"react-router-redux": "4.0.5",
"react-router-scroll": "0.2.1",
"redux": "3.5.2",
"redux-immutable": "3.0.7",
"redux-saga": "0.11.0",
"reselect": "2.5.3",
"warning": "3.0.0",
"whatwg-fetch": "1.0.0"
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-core": "6.18.0",
"babel-eslint": "7.1.0",
"babel-loader": "6.2.7",
"babel-plugin-istanbul": "2.0.3",
"babel-plugin-react-intl": "2.2.0",
"babel-plugin-react-transform": "2.0.2",
"babel-plugin-transform-async-to-generator": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.22.0",
"babel-plugin-transform-react-constant-elements": "6.9.1",
"babel-plugin-transform-react-inline-elements": "6.8.0",
"babel-plugin-transform-react-remove-prop-types": "0.2.10",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-es2017": "^6.22.0",
"babel-preset-latest": "6.16.0",
"babel-preset-react": "6.16.0",
"babel-preset-react-hmre": "1.1.1",
"babel-preset-stage-0": "6.16.0",
"chai": "3.5.0",
"chai-enzyme": "0.5.2",
"cheerio": "0.22.0",
"coveralls": "2.11.14",
"css-loader": "0.25.0",
"enzyme": "2.5.1",
"eslint": "3.9.0",
"eslint-config-airbnb": "12.0.0",
"eslint-config-airbnb-base": "9.0.0",
"eslint-config-prettier": "^2.0.0",
"eslint-import-resolver-webpack": "0.6.0",
"eslint-plugin-import": "2.0.1",
"eslint-plugin-jsx-a11y": "2.2.3",
"eslint-plugin-react": "6.4.1",
"eslint-plugin-redux-saga": "0.1.5",
"eventsource-polyfill": "0.9.6",
"expect": "1.20.2",
"expect-jsx": "2.6.0",
"exports-loader": "0.6.3",
"file-loader": "0.9.0",
"image-webpack-loader": "2.0.0",
"imports-loader": "0.6.5",
"json-loader": "0.5.4",
"lint-staged": "3.2.0",
"mocha": "3.1.2",
"node-sass": "^3.13.0",
"null-loader": "0.1.1",
"plop": "1.5.0",
"postcss-cssnext": "^2.8.0",
"postcss-focus": "^1.0.0",
"postcss-loader": "^1.1.1",
"postcss-reporter": "^2.0.0",
"postcss-smart-import": "^0.6.7",
"pre-commit": "1.1.3",
"precss": "^1.4.0",
"prettier": "^1.3.1",
"psi": "2.0.4",
"rimraf": "2.5.4",
"sass-loader": "^4.0.2",
"shelljs": "0.7.5",
"sinon": "2.0.0-pre",
"style-loader": "0.13.1",
"url-loader": "0.5.7",
"webpack": "2.1.0-beta.25",
"webpack-dev-middleware": "1.8.4",
"webpack-hot-middleware": "2.13.1"
}
}

Some files were not shown because too many files have changed in this diff Show More