├── .editorconfig
├── .eslintrc
├── .gitattributes
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── generators
├── app
│ ├── index.js
│ ├── postcss.js
│ └── prompts.js
├── component
│ ├── index.js
│ └── templates
│ │ ├── 3
│ │ ├── components
│ │ │ ├── StatefulNoStyles.js
│ │ │ ├── StatefulWithStyles.js
│ │ │ ├── StatelessNoStyles.js
│ │ │ └── StatelessWithStyles.js
│ │ ├── styles
│ │ │ ├── Component.css
│ │ │ ├── Component.less
│ │ │ ├── Component.sass
│ │ │ ├── Component.scss
│ │ │ └── Component.styl
│ │ └── tests
│ │ │ └── Base.js
│ │ └── 4
│ │ ├── components
│ │ ├── StatefulCssModules.js
│ │ ├── StatefulNoStyles.js
│ │ ├── StatefulWithStyles.js
│ │ ├── StatelessCssModules.js
│ │ ├── StatelessNoStyles.js
│ │ └── StatelessWithStyles.js
│ │ ├── styles
│ │ ├── Component.css
│ │ ├── Component.less
│ │ ├── Component.sass
│ │ ├── Component.scss
│ │ └── Component.styl
│ │ └── tests
│ │ └── Base.js
└── setup-env
│ ├── constants.js
│ ├── index.js
│ ├── templates
│ └── 4
│ │ ├── Env.js
│ │ └── runtimeConfig.js
│ └── utils.js
├── jsconfig.json
├── package.json
├── test
├── generators
│ ├── app
│ │ └── indexTest.js
│ ├── component
│ │ └── indexTest.js
│ └── setup-env
│ │ ├── assets
│ │ └── moduleIndex.js
│ │ └── setupEnvTest.js
├── mocha.opts
└── utils
│ ├── configTest.js
│ └── yeomanTest.js
└── utils
├── all.js
├── config.js
├── configopts.json
├── constants.js
└── yeoman.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": false,
4 | "amd": false,
5 | "es6": true,
6 | "node": true,
7 | "mocha": true
8 | },
9 | "rules": {
10 | "comma-dangle": 1,
11 | "quotes": [ 1, "single" ],
12 | "no-undef": 1,
13 | "global-strict": 0,
14 | "no-extra-semi": 1,
15 | "no-underscore-dangle": 0,
16 | "no-console": 0,
17 | "no-unused-vars": 1,
18 | "no-trailing-spaces": [1, { "skipBlankLines": true }],
19 | "no-unreachable": 1,
20 | "no-alert": 0
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sublime-workspace
2 | .DS_Store
3 | .AppleDouble
4 | .LSOverride
5 | Icon
6 | ._*
7 | .Spotlight-V100
8 | .Trashes
9 | Thumbs.db
10 | ehthumbs.db
11 | Desktop.ini
12 | $RECYCLE.BIN/
13 | node_modules/
14 | npm-debug.log
15 | .idea/
16 | test/temp-test
17 | coverage/
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - '4'
5 | - '6'
6 | before_install:
7 | - currentfolder=${PWD##*/}
8 | - if [ "$currentfolder" != 'generator-react-webpack' ]; then cd .. && eval "mv $currentfolder generator-react-webpack" && cd generator-react-webpack; fi
9 | after_success: 'npm run coverage'
10 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # generator-react-webpack - Changelog
2 |
3 | ## 3.3.2
4 |
5 | 1. Emergency fix: Reverted 3.3.1
6 |
7 | ## 3.3.1
8 |
9 | 1. Added new version of yeoman-generator, adjusted unit tests
10 |
11 | ## 3.3.0
12 |
13 | 1. Added new yeoman key "generatedWithVersion". Will be used for backwards compatibility of new major releases.
14 |
15 | ## 3.2.4
16 |
17 | 1. Added whitespaces to generated unit tests (made problems when using it with various eslint rules)
18 |
19 | ## 3.2.3
20 |
21 | 1. Adjusted postcss to work with react-webpack-template 1.4 upwards
22 | 2. Improved styling output (provided by [thewtex](https://github.com/thewtex))
23 |
24 | ## 3.2.2
25 |
26 | 1. Cleaned up formatting of unit tests
27 |
28 | ## 3.2.1
29 |
30 | 1. Updated tests for new version of react-webpack-template
31 |
32 | ## 3.2.0
33 |
34 | 1. Updated yeoman-generator package to new version 0.22.1 (some methods like generator.NamedBase are now deprecated!)
35 |
36 | ## 3.1.1
37 |
38 | 1. Added bugfix for https://github.com/newtriks/generator-react-webpack/issues/170
39 |
40 | ## 3.1.0
41 |
42 | 1. Added support for postcss (Patch provided by [stylesuxx](https://github.com/stylesuxx))
43 |
44 | ## 3.0.1
45 |
46 | 1. Unneeded files (License, .npmignore) are not copied anymore
47 | 2. Existence of .babelrc is now checked in unit tests
48 |
49 | ## 3.0.0
50 |
51 | 1. Updated react-webpack-template to 1.0.0 to include support for babel 6.
52 |
53 | ## 2.2.7
54 |
55 | 1. Updated yeoman to 0.21
56 | 2. Added some badges for the readme
57 |
58 | ## 2.2.6
59 |
60 | 1. Added new version of ```react-webpack-template``` (provides new features for continuous testing and better dist build support)
61 |
62 | ## 2.2.5
63 |
64 | 1. Added ability to create stateless components
65 | 2. Updated README with new installation instructions (need to install globally)
66 | 3. Added new tests for components (should be easier to handle in the future)
67 |
68 | ## 2.2.4
69 |
70 | 1. Added automatic generation of components displayName property. This makes it easier to keep track of components that reside in deep subfolders (like src/components/my/example/components/indexComponent) would become "index" as a displayName, but should instead be MyExampleComponentsIndexComponent instead.
71 |
72 | ## 2.2.3
73 |
74 | 1. Fixed .gitignore renaming (Patch provided by [VovanR](https://github.com/VovanR))
75 |
76 | ## 2.2.0:
77 |
78 | 1. Added new version of react-webpack-template, including support for React 0.14.
79 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2014 Simon Bailey
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # generator-react-webpack
2 |
3 | [](https://coveralls.io/github/react-webpack-generators/generator-react-webpack?branch=master) [](https://gitter.im/newtriks/generator-react-webpack?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://travis-ci.org/react-webpack-generators/generator-react-webpack)    
4 |
5 | > Yeoman generator for [ReactJS](http://facebook.github.io/react/) - lets you quickly set up a project including karma test runner and [Webpack](http://webpack.github.io/) module system.
6 |
7 | # About
8 | Generator-React-Webpack will help you build new React projects using modern technologies.
9 |
10 | Out of the box it comes with support for:
11 | - Webpack
12 | - ES2015 via Babel-Loader
13 | - Different supported style languages (sass, scss, less, stylus)
14 | - Style transformations via PostCSS
15 | - Automatic code linting via esLint
16 | - Ability to unit test components via Karma and Mocha/Chai
17 |
18 | ## Changes since version 2.0
19 | This generator is written in ES2015. This means it is ___not compatible with node.js versions before 4.0___.
20 |
21 | It also does __NOT__ include support for Flux-Frameworks anymore. Instead, we will use it as a base for other generators to build upon. This will make the base generator easier to use and update.
22 |
23 | If you are interested, feel free to write your own generator and use generator-react-webpack as a base (via composition).
24 |
25 | If you have built a generator using generator-react-webpack, tell us and we will add a link to our README.
26 |
27 | ## Generators that extend generator-react-webpack
28 | - [Generator-React-Webpack-Alt](https://github.com/weblogixx/generator-react-webpack-alt) (Adds ability to create actions, stores and sources for [alt.js](http://alt.js.org))
29 | - [Generator-React-Webpack-Redux](https://github.com/stylesuxx/generator-react-webpack-redux) (Adds ability to create actions and reducers for [Redux](https://github.com/rackt/redux))
30 |
31 | ---
32 |
33 | ## Installation
34 | ```bash
35 | # Make sure both is installed globally
36 | npm install -g yo
37 | npm install -g generator-react-webpack
38 | ```
39 |
40 | ## Setting up projects
41 | ```bash
42 | # Create a new directory, and `cd` into it:
43 | mkdir my-new-project && cd my-new-project
44 |
45 | # Run the generator
46 | yo react-webpack
47 | ```
48 |
49 | Please make sure to edit your newly generated `package.json` file to set description, author information and the like.
50 |
51 | ## Generating new components
52 | ```bash
53 | # After setup of course :)
54 | # cd my-new-project
55 | yo react-webpack:component my/namespaced/components/name
56 | ```
57 |
58 | The above command will create a new component, as well as its stylesheet and a basic testcase.
59 |
60 | ## Generating new stateless functional components
61 | ```
62 | yo react-webpack:component my/namespaced/components/name --stateless
63 | ```
64 |
65 | Stateless functional components where introduced in React v0.14. They have a much shorter syntax than regular ones and no state or lifecycle methods at all. Please read the [React 0.14 release notes](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html) to get more information about those components.
66 |
67 | ___Note___: You will still be able to set properties for stateless components!
68 |
69 | ## Adding PostCSS plugins
70 | If you have enabled [PostCSS](https://github.com/postcss/postcss) at generation time, install your PostCSS plugins via npm and *require* it in **postcss** function in *cfg/base.js*.
71 |
72 | Example for autoprefixer:
73 | ```bash
74 | cd my-new-project
75 | npm install autoprefixer
76 | ```
77 | Require in *cfg/base.js*
78 | ```JavaScript
79 | ...
80 | postcss: function () {
81 | return [
82 | require('autoprefixer')({
83 | browsers: ['last 2 versions', 'ie >= 8']
84 | })
85 | ];
86 | }
87 | ...
88 | ```
89 |
90 | ## Usage
91 | The following commands are available in your project:
92 | ```bash
93 | # Start for development
94 | npm start # or
95 | npm run serve
96 |
97 | # Start the dev-server with the dist version
98 | npm run serve:dist
99 |
100 | # Just build the dist version and copy static files
101 | npm run dist
102 |
103 | # Run unit tests
104 | npm test
105 |
106 | # Auto-run unit tests on file changes
107 | npm run test:watch
108 |
109 | # Lint all files in src (also automatically done AFTER tests are run)
110 | npm run lint
111 |
112 | # Clean up the dist directory
113 | npm run clean
114 |
115 | # Just copy the static assets
116 | npm run copy
117 | ```
118 |
119 | ### Naming Components
120 | We have opted to follow [@floydophone](https://twitter.com/floydophone) convention of uppercase for component file naming e.g. [Component.js](https://github.com/petehunt/ReactHack/tree/master/src/components). I am open to suggestions if there is a general objection to this decision.
121 |
122 | ### Modules
123 | Each component is a module and can be required using the [Webpack](http://webpack.github.io/) module system. [Webpack](http://webpack.github.io/) uses [Loaders](http://webpack.github.io/docs/loaders.html) which means you can also require CSS and a host of other file types. Read the [Webpack documentation](http://webpack.github.io/docs/home.html) to find out more.
124 |
125 | ## Props
126 | Thanks to [Edd Hannay](https://github.com/eddhannay) for his Webpack optimisations, my local merge and testing meant his additions lost his signature (my fault, sorry). So, big thanks Edd.
127 |
128 | ## Contribute
129 | Contributions are welcomed. When submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test alongside the fix.
130 |
131 | ### Running Tests
132 | `npm test` or `node node_modules/.bin/mocha`
133 |
134 | ## License
135 | [MIT license](http://opensource.org/licenses/mit-license.php)
136 |
--------------------------------------------------------------------------------
/generators/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Generators = require('yeoman-generator');
3 | const utils = require('../../utils/all');
4 | const prompts = require('./prompts');
5 | const path = require('path');
6 | const fs = require('fs');
7 | const packageInfo = require('../../package.json');
8 |
9 | // Set the base root directory for our files. Make sure to always use the node_modules
10 | // base path instead of the require call only. This is needed because require.resolve
11 | // also includes the path set in package.json main keys!
12 | const baseRootPath = path.join(path.dirname(require.resolve('react-webpack-template')), '..');
13 |
14 | /**
15 | * Base generator. Will copy all required files from react-webpack-template
16 | */
17 | class AppGenerator extends Generators.Base {
18 |
19 | constructor(args, options) {
20 |
21 | super(args, options);
22 |
23 | // Make options available
24 | this.option('skip-welcome-message', {
25 | desc: 'Skip the welcome message',
26 | type: Boolean,
27 | defaults: false
28 | });
29 | this.option('skip-install');
30 |
31 | // Use our plain template as source
32 | this.sourceRoot(baseRootPath);
33 |
34 | this.config.save();
35 | }
36 |
37 | initializing() {
38 |
39 | if(!this.options['skip-welcome-message']) {
40 | this.log(require('yeoman-welcome'));
41 | this.log('Out of the box I include Webpack and some default React components.\n');
42 | }
43 | }
44 |
45 | prompting() {
46 |
47 | return this.prompt(prompts).then((answers) => {
48 |
49 | // Make sure to get the correct app name if it is not the default
50 | if(answers.appName !== utils.yeoman.getAppName()) {
51 | answers.appName = utils.yeoman.getAppName(answers.appName);
52 | }
53 |
54 | // Set needed global vars for yo
55 | this.appName = answers.appName;
56 | this.style = answers.style;
57 | this.cssmodules = answers.cssmodules;
58 | this.postcss = answers.postcss;
59 | this.generatedWithVersion = parseInt(packageInfo.version.split('.').shift(), 10);
60 |
61 | // Set needed keys into config
62 | this.config.set('appName', this.appName);
63 | this.config.set('appPath', this.appPath);
64 | this.config.set('style', this.style);
65 | this.config.set('cssmodules', this.cssmodules);
66 | this.config.set('postcss', this.postcss);
67 | this.config.set('generatedWithVersion', this.generatedWithVersion);
68 | });
69 | }
70 |
71 | configuring() {
72 |
73 | // Generate our package.json. Make sure to also include the required dependencies for styles
74 | let defaultSettings = this.fs.readJSON(`${baseRootPath}/package.json`);
75 | let packageSettings = {
76 | name: this.appName,
77 | private: true,
78 | version: '0.0.1',
79 | description: `${this.appName} - Generated by generator-react-webpack`,
80 | main: 'src/index.js',
81 | scripts: defaultSettings.scripts,
82 | repository: '',
83 | keywords: [],
84 | author: 'Your name here',
85 | devDependencies: defaultSettings.devDependencies,
86 | dependencies: defaultSettings.dependencies
87 | };
88 |
89 | // Add needed loaders if we have special styles
90 | let styleConfig = utils.config.getChoiceByKey('style', this.style);
91 | if(styleConfig && styleConfig.packages) {
92 |
93 | for(let dependency of styleConfig.packages) {
94 | packageSettings.devDependencies[dependency.name] = dependency.version;
95 | }
96 | }
97 |
98 | // Add postcss module if enabled
99 | let postcssConfig = utils.config.getChoiceByKey('postcss', 'postcss');
100 | if(this.postcss && postcssConfig && postcssConfig.packages) {
101 |
102 | for(let dependency of postcssConfig.packages) {
103 | packageSettings.devDependencies[dependency.name] = dependency.version;
104 | }
105 | }
106 |
107 | // Add cssmodules if enabled
108 | const cssmoduleConfig = utils.config.getChoiceByKey('cssmodules', 'cssmodules');
109 | if(this.cssmodules && cssmoduleConfig && cssmoduleConfig.packages) {
110 | for(let dependency of cssmoduleConfig.packages) {
111 | packageSettings.dependencies[dependency.name] = dependency.version;
112 | }
113 | }
114 |
115 | this.fs.writeJSON(this.destinationPath('package.json'), packageSettings);
116 | }
117 |
118 | writing() {
119 |
120 | const excludeList = [
121 | 'LICENSE',
122 | 'README.md',
123 | 'CHANGELOG.md',
124 | 'node_modules',
125 | 'package.json',
126 | '.istanbul.yml',
127 | '.travis.yml'
128 | ];
129 |
130 | // Get all files in our repo and copy the ones we should
131 | fs.readdir(this.sourceRoot(), (err, items) => {
132 |
133 | for(let item of items) {
134 |
135 | // Skip the item if it is in our exclude list
136 | if(excludeList.indexOf(item) !== -1) {
137 | continue;
138 | }
139 |
140 | // Copy all items to our root
141 | let fullPath = path.join(baseRootPath, item);
142 | if(fs.lstatSync(fullPath).isDirectory()) {
143 | this.bulkDirectory(item, item);
144 | } else {
145 | if (item === '.npmignore') {
146 | this.copy(item, '.gitignore');
147 | } else {
148 | this.copy(item, item);
149 | }
150 | }
151 | }
152 | });
153 | }
154 |
155 | install() {
156 |
157 | // Currently buggy!
158 | if(this.postcss) {
159 | const postcss = require('./postcss');
160 | postcss.write(path.join(this.destinationRoot(), 'conf/webpack/Base.js'));
161 | }
162 |
163 | if(!this.options['skip-install']) {
164 | this.installDependencies({ bower: false });
165 | }
166 | }
167 | }
168 |
169 | module.exports = AppGenerator;
170 |
--------------------------------------------------------------------------------
/generators/app/postcss.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const esprima = require('esprima');
4 | const walk = require('esprima-walk');
5 | const escodegen = require('escodegen');
6 |
7 | module.exports = {
8 |
9 | /**
10 | * Add postcss support to the given webpack base configuration object
11 | * @param {String} path The config file name
12 | */
13 | write: function(path) {
14 |
15 | const data = fs.readFileSync(path, 'utf8');
16 | const ast = esprima.parse(data);
17 |
18 | // List of css dialects we want to add postCSS for
19 | // On regular css, we can add the loader to the end
20 | // of the chain. If we have a preprocessor, we will add
21 | // it before the initial loader
22 | const cssDialects = [
23 | '\\.cssmodule\\.css$',
24 | '^.((?!cssmodule).)*\\.css$'
25 | ];
26 |
27 | const preprocessorDialects = [
28 | '\\.cssmodule\\.(sass|scss)$',
29 | '^.((?!cssmodule).)*\\.(sass|scss)$',
30 | '\\.cssmodule\\.less$',
31 | '^.((?!cssmodule).)*\\.less$',
32 | '\\.cssmodule\\.styl$',
33 | '^.((?!cssmodule).)*\\.styl$'
34 | ];
35 |
36 | // Prepare postCSS statement for inclusion
37 | const postcssFunction = 'var postcss = { postcss: function() { return []; } }';
38 | const postcssAst = esprima.parse(postcssFunction);
39 | const postcss = postcssAst.body[0].declarations[0].init.properties[0];
40 |
41 | // The postcss loader item to add
42 | const postcssLoaderObject = 'var postcss = [{ loader: \'postcss-loader\'}]';
43 | const postcssLoaderAst = esprima.parse(postcssLoaderObject);
44 | const postcssLoader = postcssLoaderAst.body[0].declarations[0].init.elements[0];
45 |
46 |
47 | // Add postcss to the loaders array
48 | walk.walkAddParent(ast, (node) => {
49 |
50 |
51 | // Add the postcss key to the global configuration
52 |
53 | if(
54 | node.type === 'MethodDefinition' &&
55 | node.key.name === 'defaultSettings'
56 | ) {
57 | const returnStatement = node.value.body.body[1];
58 | returnStatement.argument.properties.push(postcss);
59 | }
60 |
61 | // Parse all property nodes that use a regex.
62 | // This should only be available under module.(pre)loaders
63 | if(
64 | node.type === 'Property' &&
65 | node.key.type === 'Identifier' &&
66 | node.key.name === 'test' &&
67 | typeof node.value.regex !== 'undefined'
68 | ) {
69 |
70 | // Regular css usage
71 | if(cssDialects.indexOf(node.value.regex.pattern) !== -1) {
72 | const loaderData = node.parent.properties[1];
73 | loaderData.value.elements.push(postcssLoader);
74 | }
75 |
76 |
77 | if(preprocessorDialects.indexOf(node.value.regex.pattern) !== -1) {
78 | const loaderData = node.parent.properties[1];
79 | const lastElm = loaderData.value.elements.pop();
80 | loaderData.value.elements.push(postcssLoader);
81 | loaderData.value.elements.push(lastElm);
82 | }
83 | }
84 | });
85 |
86 | // Prepare the final code and write it back
87 | const finalCode = escodegen.generate(ast, {
88 | format: {
89 | indent: {
90 | adjustMultilineComment: true,
91 | style: ' '
92 | }
93 | },
94 | comment: true
95 | });
96 |
97 | fs.writeFileSync(path, finalCode, 'utf8');
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/generators/app/prompts.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const utils = require('../../utils/all');
3 |
4 | module.exports = [
5 | {
6 | type: 'input',
7 | name: 'appName',
8 | message: 'Please choose your application name',
9 | default: utils.yeoman.getAppName()
10 | },
11 | {
12 | type: 'list',
13 | name: 'style',
14 | message: 'Which style language do you want to use?',
15 | choices: utils.config.getChoices('style'),
16 | default: utils.config.getDefaultChoice('style')
17 | },
18 | {
19 | type: 'confirm',
20 | name: 'cssmodules',
21 | message: 'Enable css module support? See https://github.com/gajus/react-css-modules for further info',
22 | default: true
23 | },
24 | {
25 | type: 'confirm',
26 | name: 'postcss',
27 | message: 'Enable postcss?',
28 | default: false
29 | }
30 | ];
31 |
--------------------------------------------------------------------------------
/generators/component/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Generators = require('yeoman-generator');
3 | const utils = require('../../utils/all');
4 | const C = utils.constants;
5 | const getAllSettingsFromComponentName = utils.yeoman.getAllSettingsFromComponentName;
6 |
7 |
8 | class ComponentGenerator extends Generators.Base {
9 |
10 | constructor(args, options) {
11 | super(args, options);
12 |
13 | /**
14 | * Flag indicating whether the component should be created with associated style files.
15 | * @type {boolean}
16 | */
17 | this.useStyles = false;
18 |
19 | /**
20 | * Flag indicating whether the component should make use of css modules.
21 | * @type {boolean}
22 | */
23 | this.useCssModules = false;
24 |
25 | /**
26 | * A string to prepended to the `className` attribute of generated components.
27 | * @type {string}
28 | */
29 | this._cssClsPrefix = '';
30 |
31 | /**
32 | * Flag indicating if stateful components should extends from React.PureComponent
33 | * @type {boolean}
34 | */
35 | this.usePureComponent = false;
36 |
37 | /**
38 | * Filename of the template that will be used to create the component.
39 | * @type {?string}
40 | */
41 | this.componentTemplateName = null;
42 |
43 | /**
44 | * Generator and template version to create the component from.
45 | * @type {?number}
46 | */
47 | this.generatorVersion = null;
48 |
49 | this.argument('name', { type: String, required: true });
50 |
51 | this.option('stateless', {
52 | desc: 'Create a stateless component instead of a full one',
53 | defaults: false
54 | });
55 |
56 | this.option('nostyle', {
57 | desc: 'Create a component without creating an associated style',
58 | defaults: false
59 | });
60 |
61 | this.option('pure', {
62 | desc: 'Create a pure component instead of a "regular" one. Will use React.PureComponent as a base instead of React.Component',
63 | defaults: false
64 | });
65 | }
66 |
67 | get cssClsPrefix() {
68 | return this._cssClsPrefix;
69 | }
70 |
71 | set cssClsPrefix(val) {
72 | this._cssClsPrefix = (val === '') ? '' : `${val}-`;
73 | }
74 |
75 | configuring() {
76 | // Read the requested major version or default it to the latest stable
77 | this.generatorVersion = this.config.get('generatedWithVersion') || 3;
78 |
79 | if (!C.SUPPORTED_GEN_VERSIONS.some(x => x === this.generatorVersion)) {
80 | this.env.error('Unsupported generator version');
81 | }
82 |
83 | this.useStyles = !this.options.nostyle;
84 | this.useCssModules = this.config.get('cssmodules') || false;
85 | this.cssClsPrefix = this.config.get('cssClsPrefix') || '';
86 |
87 | // Make sure we don't try to use template v3 with cssmodules
88 | if (this.generatorVersion < 4 && this.useStyles && this.useCssModules) {
89 | this.env.error('Creating components with cssmodules is only supported in generator versions 4+');
90 | }
91 |
92 | // Get the filename of the component template to be copied during this run
93 | this.componentTemplateName =
94 | utils.yeoman.getComponentTemplateName(this.options.stateless, this.useStyles, this.useCssModules);
95 | }
96 |
97 | writing() {
98 | const settings =
99 | getAllSettingsFromComponentName(
100 | this.name,
101 | this.config.get('style'),
102 | this.useCssModules,
103 | this.options.pure,
104 | this.generatorVersion,
105 | this.cssClsPrefix
106 | );
107 |
108 | // Create the style template. Skipped if nostyle is set as command line flag
109 | if(this.useStyles) {
110 | this.fs.copyTpl(
111 | this.templatePath(`${this.generatorVersion}/styles/Component${settings.style.suffix}`),
112 | this.destinationPath(settings.style.path + settings.style.fileName),
113 | settings
114 | );
115 | }
116 |
117 | // Create the component
118 | this.fs.copyTpl(
119 | this.templatePath(`${this.generatorVersion}/components/${this.componentTemplateName}`),
120 | this.destinationPath(settings.component.path + settings.component.fileName),
121 | settings
122 | );
123 |
124 | // Create the unit test
125 | this.fs.copyTpl(
126 | this.templatePath(`${this.generatorVersion}/tests/Base.js`),
127 | this.destinationPath(settings.test.path + settings.test.fileName),
128 | settings
129 | );
130 | }
131 | }
132 |
133 | module.exports = ComponentGenerator;
134 |
--------------------------------------------------------------------------------
/generators/component/templates/3/components/StatefulNoStyles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 |
5 | class <%= component.className %> extends React.Component {
6 | render() {
7 | return (
8 |
9 | Please edit <%= component.path %>/<%= component.fileName %> to update this component!
10 |
11 | );
12 | }
13 | }
14 |
15 | <%= component.className %>.displayName = '<%= component.displayName %>';
16 |
17 | // Uncomment properties you need
18 | // <%= component.className %>.propTypes = {};
19 | // <%= component.className %>.defaultProps = {};
20 |
21 | export default <%= component.className %>;
22 |
--------------------------------------------------------------------------------
/generators/component/templates/3/components/StatefulWithStyles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 |
5 | require('<%= style.webpackPath %>');
6 |
7 | class <%= component.className %> extends React.Component {
8 | render() {
9 | return (
10 |
11 | Please edit <%= component.path %>/<%= component.fileName %> to update this component!
12 |
13 | );
14 | }
15 | }
16 |
17 | <%= component.className %>.displayName = '<%= component.displayName %>';
18 |
19 | // Uncomment properties you need
20 | // <%= component.className %>.propTypes = {};
21 | // <%= component.className %>.defaultProps = {};
22 |
23 | export default <%= component.className %>;
24 |
--------------------------------------------------------------------------------
/generators/component/templates/3/components/StatelessNoStyles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 |
5 | let <%= component.className %> = () => (
6 |
7 | Please edit <%= component.path %>/<%= component.fileName %> to update this component!
8 |
9 | );
10 |
11 | <%= component.className %>.displayName = '<%= component.displayName %>';
12 |
13 | // Uncomment properties you need
14 | // <%= component.className %>.propTypes = {};
15 | // <%= component.className %>.defaultProps = {};
16 |
17 | export default <%= component.className %>;
18 |
--------------------------------------------------------------------------------
/generators/component/templates/3/components/StatelessWithStyles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 |
5 | require('<%= style.webpackPath %>');
6 |
7 | let <%= component.className %> = () => (
8 |
9 | Please edit <%= component.path %>/<%= component.fileName %> to update this component!
10 |
11 | );
12 |
13 | <%= component.className %>.displayName = '<%= component.displayName %>';
14 |
15 | // Uncomment properties you need
16 | // <%= component.className %>.propTypes = {};
17 | // <%= component.className %>.defaultProps = {};
18 |
19 | export default <%= component.className %>;
20 |
--------------------------------------------------------------------------------
/generators/component/templates/3/styles/Component.css:
--------------------------------------------------------------------------------
1 | .<%= style.className %> {
2 | border: 1px dashed #f00;
3 | }
4 |
--------------------------------------------------------------------------------
/generators/component/templates/3/styles/Component.less:
--------------------------------------------------------------------------------
1 | .<%= style.className %> {
2 | border: 1px dashed #f00;
3 | }
4 |
--------------------------------------------------------------------------------
/generators/component/templates/3/styles/Component.sass:
--------------------------------------------------------------------------------
1 | .<%= style.className %>
2 | border: 1px dashed #f00
3 |
--------------------------------------------------------------------------------
/generators/component/templates/3/styles/Component.scss:
--------------------------------------------------------------------------------
1 | .<%= style.className %> {
2 | border: 1px dashed #f00;
3 | }
4 |
--------------------------------------------------------------------------------
/generators/component/templates/3/styles/Component.styl:
--------------------------------------------------------------------------------
1 | .<%= style.className %>
2 | border 1px dashed #f00
3 |
--------------------------------------------------------------------------------
/generators/component/templates/3/tests/Base.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import <%= component.className %> from '<%= component.webpackPath %>';
11 |
12 | describe('<%= component.className %>', function () {
13 | let component;
14 |
15 | beforeEach(function () {
16 | component = createComponent(<%= component.className %>);
17 | });
18 |
19 | it('should have its component name as default className', function () {
20 | expect(component.props.className).to.equal('<%= style.className %>');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/generators/component/templates/4/components/StatefulCssModules.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cssmodules from 'react-css-modules';
3 | import styles from '<%= style.webpackPath %>';
4 |
5 | class <%= component.className %> extends <%= component.classBase %> {
6 |
7 | render() {
8 | return (
9 |
10 | Please edit <%= component.path %><%= component.fileName %> to update this component!
11 |
12 | );
13 | }
14 | }
15 |
16 | <%= component.className %>.displayName = '<%= component.displayName %>';
17 | <%= component.className %>.propTypes = {};
18 | <%= component.className %>.defaultProps = {};
19 |
20 | export default cssmodules(<%= component.className %>, styles);
21 |
--------------------------------------------------------------------------------
/generators/component/templates/4/components/StatefulNoStyles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class <%= component.className %> extends <%= component.classBase %> {
4 |
5 | render() {
6 | return (
7 |
8 | Please edit <%= component.path %><%= component.fileName %> to update this component!
9 |
10 | );
11 | }
12 | }
13 |
14 | <%= component.className %>.displayName = '<%= component.displayName %>';
15 | <%= component.className %>.propTypes = {};
16 | <%= component.className %>.defaultProps = {};
17 |
18 | export default <%= component.className %>;
19 |
--------------------------------------------------------------------------------
/generators/component/templates/4/components/StatefulWithStyles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '<%= style.webpackPath %>';
3 |
4 | class <%= component.className %> extends <%= component.classBase %> {
5 |
6 | render() {
7 | return (
8 |
9 | Please edit <%= component.path %><%= component.fileName %> to update this component!
10 |
11 | );
12 | }
13 | }
14 |
15 | <%= component.className %>.displayName = '<%= component.displayName %>';
16 | <%= component.className %>.propTypes = {};
17 | <%= component.className %>.defaultProps = {};
18 |
19 | export default <%= component.className %>;
20 |
--------------------------------------------------------------------------------
/generators/component/templates/4/components/StatelessCssModules.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cssmodules from 'react-css-modules';
3 | import styles from '<%= style.webpackPath %>';
4 |
5 | const <%= component.className %> = () => (
6 |
7 | Please edit <%= component.path %><%= component.fileName %> to update this component!
8 |
9 | );
10 |
11 | <%= component.className %>.displayName = '<%= component.displayName %>';
12 | <%= component.className %>.propTypes = {};
13 | <%= component.className %>.defaultProps = {};
14 |
15 | export default cssmodules(<%= component.className %>, styles);
16 |
--------------------------------------------------------------------------------
/generators/component/templates/4/components/StatelessNoStyles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const <%= component.className %> = () => (
4 |
5 | Please edit <%= component.path %><%= component.fileName %> to update this component!
6 |
7 | );
8 |
9 | <%= component.className %>.displayName = '<%= component.displayName %>';
10 | <%= component.className %>.propTypes = {};
11 | <%= component.className %>.defaultProps = {};
12 |
13 | export default <%= component.className %>;
14 |
--------------------------------------------------------------------------------
/generators/component/templates/4/components/StatelessWithStyles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '<%= style.webpackPath %>';
3 |
4 | const <%= component.className %> = () => (
5 |
6 | Please edit <%= component.path %><%= component.fileName %> to update this component!
7 |
8 | );
9 |
10 | <%= component.className %>.displayName = '<%= component.displayName %>';
11 | <%= component.className %>.propTypes = {};
12 | <%= component.className %>.defaultProps = {};
13 |
14 | export default <%= component.className %>;
15 |
--------------------------------------------------------------------------------
/generators/component/templates/4/styles/Component.css:
--------------------------------------------------------------------------------
1 | .<%= style.className %> {
2 | border: 1px dashed #f00;
3 | }
4 |
--------------------------------------------------------------------------------
/generators/component/templates/4/styles/Component.less:
--------------------------------------------------------------------------------
1 | .<%= style.className %> {
2 | border: 1px dashed #f00;
3 | }
4 |
--------------------------------------------------------------------------------
/generators/component/templates/4/styles/Component.sass:
--------------------------------------------------------------------------------
1 | .<%= style.className %>
2 | border: 1px dashed #f00
3 |
--------------------------------------------------------------------------------
/generators/component/templates/4/styles/Component.scss:
--------------------------------------------------------------------------------
1 | .<%= style.className %> {
2 | border: 1px dashed #f00;
3 | }
4 |
--------------------------------------------------------------------------------
/generators/component/templates/4/styles/Component.styl:
--------------------------------------------------------------------------------
1 | .<%= style.className %>
2 | border 1px dashed #f00
3 |
--------------------------------------------------------------------------------
/generators/component/templates/4/tests/Base.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import <%= component.className %> from '<%= component.webpackPath %>';
4 |
5 | describe('<<%= component.className %> />', function () {
6 |
7 | let component;
8 | beforeEach(function () {
9 | component = shallow(<<%= component.className %> />);
10 | });
11 |
12 | describe('when rendering the component', function () {
13 |
14 | it('should have a className of "<%= style.className %>"', function () {
15 | expect(component.hasClass('<%= style.className %>')).to.equal(true);
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/generators/setup-env/constants.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const esDefaultOpts = require('esformatter/lib/preset/default.json');
4 |
5 | const esOpts = Object.assign({}, esDefaultOpts, {
6 | 'lineBreak': {
7 | 'before': {
8 | 'AssignmentExpression': '>=2',
9 | 'ClassDeclaration': 2,
10 | 'EndOfFile': 1
11 | },
12 | 'after': {
13 | 'ClassClosingBrace': 2,
14 | 'FunctionDeclaration': '>=2',
15 | 'BlockStatementClosingBrace': '>=2'
16 | }
17 | }
18 | });
19 |
20 | module.exports = {
21 | esOpts
22 | };
23 |
--------------------------------------------------------------------------------
/generators/setup-env/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Generators = require('yeoman-generator');
4 | const classify = require('underscore.string').classify;
5 | const underscored = require('underscore.string').underscored;
6 |
7 | const formatCode = require('./utils').formatCode;
8 | const getModifiedConfigModuleIndex = require('./utils').getModifiedConfigModuleIndex;
9 |
10 |
11 | class EnvGenerator extends Generators.Base {
12 |
13 | constructor(args, options) {
14 | super(args, options);
15 |
16 | this.argument('envName', { type: String, required: true });
17 | }
18 |
19 | configuring() {
20 |
21 | /**
22 | * Currently used major version of the generator (defaults to latest stable).
23 | * @type {number}
24 | */
25 | this.generatorVersion = this.config.get('generatedWithVersion') || 3;
26 |
27 | // Make sure we don't try to use this subgen on V3 or lower.
28 | if (this.generatorVersion < 4) {
29 | this.env.error('Setting up new envs is only supported in generator versions 4+');
30 | }
31 | }
32 |
33 | writing() {
34 | const classedEnv = classify(this.envName);
35 | const snakedEnv = underscored(this.envName.toLowerCase());
36 |
37 | // Write conf/webpack/.js
38 | this.fs.copyTpl(
39 | this.templatePath(`${this.generatorVersion}/Env.js`),
40 | this.destinationPath(`conf/webpack/${classedEnv}.js`),
41 | { envName: snakedEnv }
42 | );
43 |
44 | // Write src/config/.js
45 | this.fs.copyTpl(
46 | this.templatePath(`${this.generatorVersion}/runtimeConfig.js`),
47 | this.destinationPath(`src/config/${snakedEnv}.js`),
48 | { envName: snakedEnv }
49 | );
50 |
51 | // Write conf/webpack/index.js
52 | const moduleIndexPath = this.destinationPath('conf/webpack/index.js');
53 | const updatedModuleIndex = formatCode(
54 | getModifiedConfigModuleIndex(this.fs.read(moduleIndexPath), snakedEnv, classedEnv)
55 | );
56 | this.fs.write(this.destinationPath('conf/webpack/index.js'), formatCode(updatedModuleIndex));
57 | }
58 |
59 | }
60 |
61 | module.exports = EnvGenerator;
62 |
--------------------------------------------------------------------------------
/generators/setup-env/templates/4/Env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Default dev server configuration.
5 | */
6 | const webpack = require('webpack');
7 | const WebpackBaseConfig = require('./Base');
8 |
9 | class WebpackDevConfig extends WebpackBaseConfig {
10 |
11 | constructor() {
12 | super();
13 | this.config = {
14 | // Update your env-specific configuration here!
15 | // To start, look at ./Dev.js or ./Dist.js for two example configurations
16 | // targeted at production or development builds.
17 | };
18 | }
19 |
20 | /**
21 | * Get the environment name
22 | * @return {String} The current environment
23 | */
24 | get env() {
25 | return '<%= envName %>';
26 | }
27 | }
28 |
29 | module.exports = WebpackDevConfig;
30 |
--------------------------------------------------------------------------------
/generators/setup-env/templates/4/runtimeConfig.js:
--------------------------------------------------------------------------------
1 | import baseConfig from './base';
2 |
3 | const config = {
4 | appEnv: '<%= envName %>',
5 | };
6 |
7 | export default Object.freeze(Object.assign({}, baseConfig, config));
8 |
--------------------------------------------------------------------------------
/generators/setup-env/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const acorn = require('acorn');
4 | const escodegen = require('escodegen');
5 | const esformatter = require('esformatter');
6 | const jp = require('jsonpath');
7 |
8 | const esOpts = require('./constants').esOpts;
9 |
10 |
11 | /**
12 | * Returns an AST Node for a {@code Property} in the {@code module.exports} object.
13 | *
14 | * @param {string} envName
15 | * @return {Object}
16 | */
17 | function createExportNode(envName) {
18 | return {
19 | 'type': 'Property',
20 | 'method': false,
21 | 'shorthand': true,
22 | 'computed': false,
23 | 'key': {
24 | 'type': 'Identifier',
25 | 'name': envName
26 | },
27 | 'kind': 'init',
28 | 'value': {
29 | 'type': 'Identifier',
30 | 'name': envName
31 | }
32 | }
33 | }
34 |
35 |
36 | /**
37 | * Returns updated index module requiring and exporting the newly created environment.
38 | *
39 | * @param {string} fileStr
40 | * @param {string} snakedEnv
41 | * @param {string} classedEnv
42 | * @return {string} file contents of updated conf/webpack/index.js
43 | */
44 | function getModifiedConfigModuleIndex(fileStr, snakedEnv, classedEnv) {
45 | // TODO [sthzg] we might want to rewrite the AST-mods in this function using a walker.
46 |
47 | const moduleFileAst = acorn.parse(fileStr, { module: true });
48 |
49 | // if required env was already created, just return the original string
50 | if (jp.paths(moduleFileAst, `$..[?(@.value=="./${classedEnv}" && @.type=="Literal")]`).length > 0) {
51 | return fileStr;
52 | }
53 |
54 | // insert require call for the new env
55 | const envImportAst = acorn.parse(`const ${snakedEnv} = require('./${classedEnv}');`);
56 | const insertAt = jp.paths(moduleFileAst, '$..[?(@.name=="require")]').pop()[2] + 1;
57 | moduleFileAst.body.splice(insertAt, 0, envImportAst);
58 |
59 | // add new env to module.exports
60 | const exportsAt = jp.paths(moduleFileAst, '$..[?(@.name=="exports")]').pop()[2];
61 | moduleFileAst.body[exportsAt].expression.right.properties.push(createExportNode(snakedEnv));
62 |
63 | return escodegen.generate(moduleFileAst, { format: { indent: { style: ' ' } } });
64 | }
65 |
66 |
67 | /**
68 | * Returns a beautified representation of {@code fileStr}.
69 | *
70 | * @param {string} fileStr
71 | * @return {string}
72 | */
73 | function formatCode(fileStr) {
74 | return esformatter.format(fileStr, esOpts);
75 | }
76 |
77 |
78 | module.exports = {
79 | createExportNode,
80 | formatCode,
81 | getModifiedConfigModuleIndex
82 | };
83 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | //Support for Visual Studio Code https://code.visualstudio.com/docs/languages/javascript
2 | {
3 | "compilerOptions": {
4 | "target": "ES6",
5 | "module": "commonjs"
6 | }
7 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-react-webpack",
3 | "version": "4.0.1-1",
4 | "description": "Yeoman generator for using React with Webpack via Babel",
5 | "keywords": [
6 | "yeoman-generator",
7 | "react",
8 | "reactjs",
9 | "webpack",
10 | "scaffold",
11 | "framework",
12 | "component",
13 | "frontend",
14 | "front-end",
15 | "app"
16 | ],
17 | "homepage": "https://github.com/react-webpack-generators/generator-react-webpack#readme",
18 | "bugs": "https://github.com/react-webpack-generators/generator-react-webpack/issues",
19 | "author": {
20 | "name": "Simon Bailey",
21 | "email": "simon@newtriks.com",
22 | "url": "https://github.com/newtriks"
23 | },
24 | "contributors": [
25 | {
26 | "name": "Christian Schilling",
27 | "email": "cs@weblogixx.de",
28 | "url": "https://github.com/weblogixx"
29 | }
30 | ],
31 | "main": "generators/app/index.js",
32 | "files": [
33 | "generators/app",
34 | "generators/component",
35 | "utils"
36 | ],
37 | "repository": {
38 | "type": "git",
39 | "url": "https://github.com/react-webpack-generators/generator-react-webpack.git"
40 | },
41 | "scripts": {
42 | "test": "istanbul cover _mocha",
43 | "test:watch": "mocha -w",
44 | "coverage": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls",
45 | "release:major": "npm version prerelease && git push --follow-tags && npm publish --tag beta",
46 | "release:minor": "npm version prerelease && git push --follow-tags && npm publish --tag beta",
47 | "release:patch": "npm version prerelease && git push --follow-tags && npm publish --tag beta"
48 | },
49 | "dependencies": {
50 | "acorn": "^4.0.3",
51 | "escodegen": "^1.8.1",
52 | "esformatter": "^0.9.6",
53 | "esprima": "^3.1.1",
54 | "esprima-walk": "^0.1.0",
55 | "jsonpath": "^0.2.7",
56 | "react-webpack-template": "^2.0.1-6",
57 | "underscore.string": "^3.2.2",
58 | "yeoman-generator": "^0.24.0",
59 | "yeoman-welcome": "^1.0.1"
60 | },
61 | "devDependencies": {
62 | "chai": "^3.2.0",
63 | "coveralls": "^2.11.12",
64 | "fs-extra": "^0.30.0",
65 | "istanbul": "^0.4.5",
66 | "mocha": "^3.0.0",
67 | "yeoman-assert": "^2.1.1",
68 | "yeoman-test": "^1.0.0"
69 | },
70 | "engines": {
71 | "node": ">=4.0.0",
72 | "iojs": ">=1.1.0"
73 | },
74 | "license": "MIT"
75 | }
76 |
--------------------------------------------------------------------------------
/test/generators/app/indexTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let path = require('path');
3 | let expect = require('chai').expect;
4 | let assert = require('yeoman-assert');
5 | let helpers = require('yeoman-test');
6 |
7 | // Default globals, used in all tests
8 | const defaultPrompts = require('../../../generators/app/prompts.js');
9 | const generatorBase = path.join(__dirname, '../../../generators/app');
10 |
11 | let generator;
12 |
13 | /**
14 | * Global before load function. Run in the before callbacks
15 | * @param {Object} prompts List of prompts to use
16 | * @return {Promise}
17 | */
18 | const beforeLoad = (prompts) => {
19 |
20 | return helpers.run(generatorBase)
21 | .inTmpDir()
22 | .withOptions({
23 | 'skip-welcome-message': true,
24 | 'skip-install': true
25 | })
26 | .withPrompts(prompts)
27 | .on('ready', function(instance) {
28 | generator = instance;
29 | })
30 | .toPromise();
31 | };
32 |
33 | describe('react-webpack:app', () => {
34 |
35 | let prompts = {};
36 | for(let p of defaultPrompts) {
37 | prompts[p.name] = p.default;
38 | }
39 |
40 | before(() => {
41 | return beforeLoad(prompts);
42 | });
43 |
44 | describe('#config', () => {
45 |
46 | it('should set the generatedWith key to the current generator major version', () => {
47 | expect(generator.config.get('generatedWithVersion')).to.equal(4);
48 | });
49 |
50 | it('should use "css" as default style language', () => {
51 | expect(generator.config.get('style')).to.equal('css');
52 | });
53 |
54 | it('should use "css modules" per default', () => {
55 | expect(generator.config.get('cssmodules')).to.be.true;
56 | });
57 |
58 | it('should not enable "PostCSS" by default', () => {
59 | expect(generator.config.get('postcss')).to.be.false;
60 | });
61 | });
62 |
63 | describe('configuring', () => {
64 | it('should add css module support', () => {
65 | assert.fileContent('package.json', 'react-css-modules');
66 | });
67 | });
68 |
69 | describe('#createFiles', () => {
70 |
71 | it('should generate dot files', () => {
72 |
73 | assert.file([
74 | '.babelrc',
75 | '.editorconfig',
76 | '.eslintrc',
77 | '.gitignore',
78 | '.yo-rc.json'
79 | ]);
80 | });
81 |
82 | it('should generate project configuration files', () => {
83 |
84 | assert.file([
85 | 'package.json'
86 | ]);
87 | });
88 |
89 | it('should generate the webpack configuration', () => {
90 |
91 | assert.file([
92 | 'conf/webpack/Base.js',
93 | 'conf/webpack/Dev.js',
94 | 'conf/webpack/Dist.js',
95 | 'conf/webpack/Test.js',
96 | 'webpack.config.js'
97 | ]);
98 | });
99 |
100 | it('should generate required source files', () => {
101 |
102 | assert.file([
103 | 'src/actions/README.md',
104 | 'src/client.js',
105 | 'src/components/App.js',
106 | 'src/components/app.css',
107 | 'src/static/favicon.ico',
108 | 'src/images/yeoman.png',
109 | 'src/index.html',
110 | 'src/sources/README.md',
111 | 'src/stores/README.md'
112 | ]);
113 | });
114 |
115 | it('should generate test configuration and basic tests', () => {
116 |
117 | assert.file([
118 | 'karma.conf.js',
119 | 'test/components/AppTest.js',
120 | 'test/config/ConfigTest.js',
121 | 'test/loadtests.js',
122 | 'test/.eslintrc'
123 | ]);
124 | });
125 | });
126 | });
127 |
128 | describe('react-webpack:app without cssmodules support', () => {
129 |
130 | let prompts = {};
131 | for(let p of defaultPrompts) {
132 | prompts[p.name] = p.default;
133 | }
134 | prompts.cssmodules = false;
135 |
136 | before(() => {
137 | return beforeLoad(prompts);
138 | });
139 |
140 | describe('#config', () => {
141 |
142 | it('should set the generatedWith key to the current generator major version', () => {
143 | expect(generator.config.get('generatedWithVersion')).to.equal(4);
144 | });
145 |
146 | it('should use "css" as default style language', () => {
147 | expect(generator.config.get('style')).to.equal('css');
148 | });
149 |
150 | it('should not use "css modules"', () => {
151 | expect(generator.config.get('cssmodules')).to.be.false;
152 | });
153 |
154 | it('should not enable "PostCSS" by default', () => {
155 | expect(generator.config.get('postcss')).to.be.false;
156 | });
157 | });
158 |
159 | describe('configuring', () => {
160 | it('should add no cssmodule support', () => {
161 | assert.noFileContent('package.json', 'react-css-modules');
162 | });
163 | });
164 | });
165 |
166 | describe('react-webpack:app with PostCSS support', () => {
167 |
168 | let prompts = {};
169 | for(let p of defaultPrompts) {
170 | prompts[p.name] = p.default;
171 | }
172 | prompts.postcss = true;
173 |
174 | before(() => {
175 | return beforeLoad(prompts);
176 | });
177 |
178 | describe('#config', () => {
179 |
180 | it('should set the generatedWith key to the current generator major version', () => {
181 | expect(generator.config.get('generatedWithVersion')).to.equal(4);
182 | });
183 |
184 | it('should use "css" as default style language', () => {
185 | expect(generator.config.get('style')).to.equal('css');
186 | });
187 |
188 | it('should use "css modules" per default', () => {
189 | expect(generator.config.get('cssmodules')).to.be.true;
190 | });
191 |
192 | it('should enable "PostCSS"', () => {
193 | expect(generator.config.get('postcss')).to.equal(true);
194 | });
195 | });
196 |
197 | describe('#createFiles', () => {
198 |
199 | it('should generate dot files', () => {
200 |
201 | assert.file([
202 | '.babelrc',
203 | '.editorconfig',
204 | '.eslintrc',
205 | '.gitignore',
206 | '.yo-rc.json'
207 | ]);
208 | });
209 |
210 | it('should generate project configuration files', () => {
211 |
212 | assert.file([
213 | 'package.json'
214 | ]);
215 | });
216 |
217 | it('should generate the webpack configuration', () => {
218 |
219 | assert.file([
220 | 'conf/webpack/Base.js',
221 | 'conf/webpack/Dev.js',
222 | 'conf/webpack/Dist.js',
223 | 'conf/webpack/Test.js',
224 | 'webpack.config.js'
225 | ]);
226 | });
227 |
228 | it('should insert the postcss loader into the style pipes', () => {
229 | assert.fileContent('conf/webpack/Base.js', `{
230 | loader: 'css-loader',
231 | query: cssModulesQuery
232 | },
233 | { loader: 'postcss-loader' }`);
234 | assert.fileContent('conf/webpack/Base.js', `{ loader: 'css-loader' },
235 | { loader: 'postcss-loader' }`);
236 | assert.fileContent('conf/webpack/Base.js', `{
237 | loader: 'css-loader',
238 | query: cssModulesQuery
239 | },
240 | { loader: 'postcss-loader' },
241 | { loader: 'sass-loader' }`);
242 | assert.fileContent('conf/webpack/Base.js', `{ loader: 'css-loader' },
243 | { loader: 'postcss-loader' },
244 | { loader: 'sass-loader' }`);
245 | assert.fileContent('conf/webpack/Base.js', `{
246 | loader: 'css-loader',
247 | query: cssModulesQuery
248 | },
249 | { loader: 'postcss-loader' },
250 | { loader: 'less-loader' }`);
251 | assert.fileContent('conf/webpack/Base.js', `{ loader: 'css-loader' },
252 | { loader: 'postcss-loader' },
253 | { loader: 'less-loader' }`);
254 | assert.fileContent('conf/webpack/Base.js', `{
255 | loader: 'css-loader',
256 | query: cssModulesQuery
257 | },
258 | { loader: 'postcss-loader' },
259 | { loader: 'stylus-loader' }`);
260 | assert.fileContent('conf/webpack/Base.js', `{ loader: 'css-loader' },
261 | { loader: 'postcss-loader' },
262 | { loader: 'stylus-loader' }`);
263 | });
264 |
265 | it('should append the postcss function to the base config', () => {
266 |
267 | assert.fileContent('conf/webpack/Base.js', 'postcss: function () {');
268 | });
269 |
270 | it('should generate required source files', () => {
271 |
272 | assert.file([
273 | 'src/actions/README.md',
274 | 'src/client.js',
275 | 'src/components/App.js',
276 | 'src/components/app.css',
277 | 'src/static/favicon.ico',
278 | 'src/images/yeoman.png',
279 | 'src/index.html',
280 | 'src/sources/README.md',
281 | 'src/stores/README.md'
282 | ]);
283 | });
284 |
285 | it('should generate test configuration and basic tests', () => {
286 |
287 | assert.file([
288 | 'karma.conf.js',
289 | 'test/components/AppTest.js',
290 | 'test/config/ConfigTest.js',
291 | 'test/loadtests.js',
292 | 'test/.eslintrc'
293 | ]);
294 | });
295 | });
296 | });
297 |
--------------------------------------------------------------------------------
/test/generators/component/indexTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let path = require('path');
3 | let assert = require('yeoman-assert');
4 | let helpers = require('yeoman-test');
5 |
6 |
7 | describe('react-webpack:component', () => {
8 |
9 | const generatorComponent = path.join(__dirname, '../../../generators/component');
10 |
11 | describe('when using version 3 of the generator', () => {
12 |
13 | // List of available style types. Please add a line that says
14 | // testComponentWithStyle(styleTypes.KEY); to the bottom of the file
15 | // to run all unit tests for this filetype.
16 | const styleTypes = {
17 | css: {
18 | type: 'css',
19 | fileName: 'src/styles/Mycomponent.css',
20 | expandedFileName: 'src/styles/my/littleSpecial/Test.css',
21 | assertions: {
22 | componentImport: 'require(\'styles/Mycomponent.css\');',
23 | styleContent: '.mycomponent-component'
24 | }
25 | },
26 | sass: {
27 | type: 'sass',
28 | fileName: 'src/styles/Mycomponent.sass',
29 | expandedFileName: 'src/styles/my/littleSpecial/Test.sass',
30 | assertions: {
31 | componentImport: 'require(\'styles/Mycomponent.sass\');',
32 | styleContent: '.mycomponent-component'
33 | }
34 | },
35 | scss: {
36 | type: 'scss',
37 | fileName: 'src/styles/Mycomponent.scss',
38 | expandedFileName: 'src/styles/my/littleSpecial/Test.scss',
39 | assertions: {
40 | componentImport: 'require(\'styles/Mycomponent.scss\');',
41 | styleContent: '.mycomponent-component'
42 | }
43 | },
44 | less: {
45 | type: 'less',
46 | fileName: 'src/styles/Mycomponent.less',
47 | expandedFileName: 'src/styles/my/littleSpecial/Test.less',
48 | assertions: {
49 | componentImport: 'require(\'styles/Mycomponent.less\');',
50 | styleContent: '.mycomponent-component'
51 | }
52 | },
53 | stylus: {
54 | type: 'stylus',
55 | fileName: 'src/styles/Mycomponent.styl',
56 | expandedFileName: 'src/styles/my/littleSpecial/Test.styl',
57 | assertions: {
58 | componentImport: 'require(\'styles/Mycomponent.styl\');',
59 | styleContent: '.mycomponent-component'
60 | }
61 | }
62 | };
63 |
64 | /**
65 | * Return a newly generated component with given name and style
66 | * @param {String} name Name of the component
67 | * @param {String} styleType Styling language to use
68 | * @param {Object} options Options to use for the generator
69 | * @param {Function} callback Test callback to run
70 | */
71 | function createGeneratedComponent(name, styleType, options, callback) {
72 | helpers.run(generatorComponent)
73 | .withArguments([name])
74 | .withOptions(options)
75 | .on('ready', (instance) => {
76 | instance.config.set('style', styleType);
77 | instance.config.set('generatedWithVersion', 3);
78 | })
79 | .on('end', callback);
80 | }
81 |
82 | /**
83 | * Test a component with styling applied
84 | * @param {Object} style The style to apply (see styleTypes above)
85 | * @param {Object} options Options to use [optional]
86 | */
87 | function testComponentWithStyle(style, options) {
88 |
89 | // Make sure we always have options
90 | if(!options) {
91 | options = {};
92 | }
93 |
94 | describe(`when using style type "${style.type}"`, () => {
95 |
96 | describe('when writing is called', () => {
97 |
98 | it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => {
99 | createGeneratedComponent('mycomponent', style.type, options, () => {
100 |
101 | assert.file([
102 | 'src/components/MycomponentComponent.js',
103 | style.fileName,
104 | 'test/components/MycomponentComponentTest.js'
105 | ]);
106 | done();
107 | });
108 | });
109 | });
110 |
111 | describe('when creating a component', () => {
112 |
113 | it('should always import REACT', (done) => {
114 | createGeneratedComponent('mycomponent', style.type, options, () => {
115 | assert.fileContent('src/components/MycomponentComponent.js', 'import React from \'react\';');
116 | done();
117 | });
118 | });
119 |
120 | it(`should require the created ${style.type} file`, (done) => {
121 | createGeneratedComponent('mycomponent', style.type, options, () => {
122 | assert.fileContent('src/components/MycomponentComponent.js', style.assertions.componentImport);
123 | done();
124 | });
125 | });
126 |
127 | it('should have its displayName set per default', (done) => {
128 | createGeneratedComponent('mycomponent', style.type, options, () => {
129 | assert.fileContent('src/components/MycomponentComponent.js', 'displayName = \'MycomponentComponent\';');
130 | done();
131 | });
132 | });
133 |
134 | it('should export the created component', (done) => {
135 | createGeneratedComponent('mycomponent', style.type, options, () => {
136 | assert.fileContent('src/components/MycomponentComponent.js', 'export default MycomponentComponent');
137 | done();
138 | });
139 | });
140 |
141 | it('should be possible to create components in a subfolder', (done) => {
142 | createGeneratedComponent('my/little !special/test', style.type, options, () => {
143 |
144 | assert.file([
145 | 'src/components/my/littleSpecial/TestComponent.js',
146 | style.expandedFileName,
147 | 'test/components/my/littleSpecial/TestComponentTest.js'
148 | ]);
149 | done();
150 | });
151 | });
152 |
153 | it(`should add the components ${style.type} class to the created stylesheet`, (done) => {
154 | createGeneratedComponent('mycomponent', style.type, options, () => {
155 | assert.fileContent(style.fileName, style.assertions.styleContent);
156 | done();
157 | });
158 | });
159 |
160 | it('should create a unit test that imports the generated component', (done) => {
161 | createGeneratedComponent('mycomponent', style.type, options, () => {
162 | assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components/MycomponentComponent.js\';');
163 | done();
164 | });
165 | });
166 | });
167 | });
168 | }
169 |
170 | // Run all tests for all available style types.
171 | // Stateless components will also be tested!
172 | for(const style in styleTypes) {
173 | testComponentWithStyle(styleTypes[style]);
174 | testComponentWithStyle(styleTypes[style], { stateless: true });
175 | }
176 | });
177 |
178 | describe('when using version 4 of the generator', () => {
179 |
180 | const cssModSuffix = (useCssModules) => useCssModules ? '.cssmodule' : '';
181 | const importAssertion = (useCssModules, ext) => useCssModules
182 | ? `import styles from './mycomponent.cssmodule.${ext}';`
183 | : `import './mycomponent.${ext}';`
184 | ;
185 |
186 | // List of available style types. Please add a line that says
187 | // testComponentWithStyle(styleTypes.KEY); to the bottom of the file
188 | // to run all unit tests for this filetype.
189 | const styleTypes = (useCssModules) => ({
190 | css: {
191 | type: 'css',
192 | fileName: `src/components/mycomponent${cssModSuffix(useCssModules)}.css`,
193 | expandedFileName: `src/components/my/littleSpecial/test${cssModSuffix(useCssModules)}.css`,
194 | assertions: {
195 | componentImport: importAssertion(useCssModules, 'css'),
196 | styleContent: '.mycomponent-component'
197 | }
198 | },
199 | sass: {
200 | type: 'sass',
201 | fileName: `src/components/mycomponent${cssModSuffix(useCssModules)}.sass`,
202 | expandedFileName: `src/components/my/littleSpecial/test${cssModSuffix(useCssModules)}.sass`,
203 | assertions: {
204 | componentImport: importAssertion(useCssModules, 'sass'),
205 | styleContent: '.mycomponent-component'
206 | }
207 | },
208 | scss: {
209 | type: 'scss',
210 | fileName: `src/components/mycomponent${cssModSuffix(useCssModules)}.scss`,
211 | expandedFileName: `src/components/my/littleSpecial/test${cssModSuffix(useCssModules)}.scss`,
212 | assertions: {
213 | componentImport: importAssertion(useCssModules, 'scss'),
214 | styleContent: '.mycomponent-component'
215 | }
216 | },
217 | less: {
218 | type: 'less',
219 | fileName: `src/components/mycomponent${cssModSuffix(useCssModules)}.less`,
220 | expandedFileName: `src/components/my/littleSpecial/test${cssModSuffix(useCssModules)}.less`,
221 | assertions: {
222 | componentImport: importAssertion(useCssModules, 'less'),
223 | styleContent: '.mycomponent-component'
224 | }
225 | },
226 | stylus: {
227 | type: 'stylus',
228 | fileName: `src/components/mycomponent${cssModSuffix(useCssModules)}.styl`,
229 | expandedFileName: `src/components/my/littleSpecial/test${cssModSuffix(useCssModules)}.styl`,
230 | assertions: {
231 | componentImport: importAssertion(useCssModules, 'styl'),
232 | styleContent: '.mycomponent-component'
233 | }
234 | }
235 | });
236 |
237 | /**
238 | * Return a newly generated component with given name and style
239 | * @param {String} name Name of the component
240 | * @param {String} styleType Styling language to use
241 | * @param {Object} options Options to use for the generator
242 | * @param {boolean} useCssModules useCssModules indicate whether to test with cssmodules enabled
243 | * @param {Function} callback Test callback to run
244 | */
245 | function createGeneratedComponent(name, styleType, options, useCssModules, callback) {
246 | helpers.run(generatorComponent)
247 | .withArguments([name])
248 | .withOptions(options)
249 | .on('ready', (instance) => {
250 | instance.config.set('style', styleType);
251 | instance.config.set('cssmodules', useCssModules);
252 | instance.config.set('generatedWithVersion', 4);
253 | })
254 | .on('end', callback);
255 | }
256 |
257 | /**
258 | * Test a component with styling applied
259 | * @param {Object} style The style to apply (see styleTypes above)
260 | * @param {Object} options Options to use [optional]
261 | * @param {boolean} useCssModules indicate whether to test with cssmodules enabled
262 | */
263 | function testComponentWithStyle(style, options, useCssModules) {
264 |
265 | // Make sure we always have options
266 | if(!options) {
267 | options = {};
268 | }
269 |
270 | const isStateless = options.stateless || false;
271 | const isPure = options.pure || false;
272 | const componentBase = isPure ? 'React.PureComponent' : 'React.Component';
273 |
274 | describe(`when using style type "${style.type}" with nostyle = false, pure rendering = ${isPure} and cssmodules = ${useCssModules}`, () => {
275 |
276 | describe('when writing is called', () => {
277 |
278 | it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => {
279 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
280 |
281 | assert.file([
282 | 'src/components/Mycomponent.js',
283 | style.fileName,
284 | 'test/components/MycomponentTest.js'
285 | ]);
286 | done();
287 | });
288 | });
289 | });
290 |
291 | describe('when creating a component', () => {
292 |
293 | it('should always import REACT', (done) => {
294 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
295 | assert.fileContent('src/components/Mycomponent.js', 'import React from \'react\';');
296 | done();
297 | });
298 | });
299 |
300 | it(`should require the created ${style.type} file`, (done) => {
301 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
302 | assert.fileContent('src/components/Mycomponent.js', style.assertions.componentImport);
303 | done();
304 | });
305 | });
306 |
307 | // Only run this if we are not in stateless mode
308 | if(!isStateless) {
309 | it(`should extend ${componentBase}`, (done) => {
310 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
311 | assert.fileContent(
312 | 'src/components/Mycomponent.js',
313 | `class Mycomponent extends ${componentBase}`
314 | );
315 | done();
316 | });
317 | });
318 | }
319 |
320 | it('should have its displayName set per default', (done) => {
321 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
322 | assert.fileContent('src/components/Mycomponent.js', 'Mycomponent.displayName = \'Mycomponent\';');
323 | done();
324 | });
325 | });
326 |
327 | it('should export the created component', (done) => {
328 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
329 |
330 | let exportAssertion;
331 | if(useCssModules) {
332 | exportAssertion = 'export default cssmodules(Mycomponent, styles);';
333 | } else {
334 | exportAssertion = 'export default Mycomponent;';
335 | }
336 | assert.fileContent('src/components/Mycomponent.js', exportAssertion);
337 | done();
338 | });
339 | });
340 |
341 | it('should be possible to create components in a subfolder', (done) => {
342 | createGeneratedComponent('my/little !special/test', style.type, options, useCssModules, () => {
343 |
344 | assert.file([
345 | 'src/components/my/littleSpecial/Test.js',
346 | style.expandedFileName,
347 | 'test/components/my/littleSpecial/TestTest.js'
348 | ]);
349 | done();
350 | });
351 | });
352 |
353 | it(`should add the components ${style.type} class to the created stylesheet`, (done) => {
354 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
355 | assert.fileContent(style.fileName, style.assertions.styleContent);
356 | done();
357 | });
358 | });
359 |
360 | it('should create a unit test that imports the generated component', (done) => {
361 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
362 | assert.fileContent('test/components/MycomponentTest.js', 'import Mycomponent from \'components/Mycomponent.js\';');
363 | done();
364 | });
365 | });
366 | });
367 | });
368 | }
369 |
370 | /**
371 | * Test a component with styling applied
372 | * @param {Object} style The style to apply (see styleTypes above)
373 | * @param {Object} options Options to use [optional]
374 | * @param {boolean} useCssModules indicate whether to test with cssmodules enabled
375 | */
376 | function testComponentWithoutStyle(style, options, useCssModules) {
377 |
378 | // Make sure we always have options
379 | if(!options) {
380 | options = {};
381 | }
382 |
383 | describe(`when using style type "${style.type}" with nostyle = true and cssmodules = ${useCssModules}`, () => {
384 |
385 | describe('when writing is called', () => {
386 |
387 | it('should create the react component and test file', (done) => {
388 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
389 |
390 | assert.file([
391 | 'src/components/Mycomponent.js',
392 | 'test/components/MycomponentTest.js'
393 | ]);
394 | done();
395 | });
396 | });
397 | });
398 |
399 | describe('when creating a component', () => {
400 |
401 | it('should always import REACT', (done) => {
402 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
403 | assert.fileContent('src/components/Mycomponent.js', 'import React from \'react\';');
404 | done();
405 | });
406 | });
407 |
408 | it('should have its displayName set per default', (done) => {
409 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
410 | assert.fileContent('src/components/Mycomponent.js', 'Mycomponent.displayName = \'Mycomponent\';');
411 | done();
412 | });
413 | });
414 |
415 | it('should export the created component', (done) => {
416 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
417 | assert.fileContent('src/components/Mycomponent.js', 'export default Mycomponent');
418 | done();
419 | });
420 | });
421 |
422 | it('should be possible to create components in a subfolder', (done) => {
423 | createGeneratedComponent('my/little !special/test', style.type, options, useCssModules, () => {
424 |
425 | assert.file([
426 | 'src/components/my/littleSpecial/Test.js',
427 | 'test/components/my/littleSpecial/TestTest.js'
428 | ]);
429 | done();
430 | });
431 | });
432 |
433 | it('should create a unit test that imports the generated component', (done) => {
434 | createGeneratedComponent('mycomponent', style.type, options, useCssModules, () => {
435 | assert.fileContent('test/components/MycomponentTest.js', 'import Mycomponent from \'components/Mycomponent.js\';');
436 | done();
437 | });
438 | });
439 | });
440 | });
441 | }
442 |
443 | // Run all tests for all available style types.
444 | // Stateless components will also be tested!
445 | for(const style in styleTypes(true)) {
446 | testComponentWithStyle(styleTypes(true)[style], {}, true);
447 | testComponentWithStyle(styleTypes(true)[style], { pure: true }, true);
448 | testComponentWithStyle(styleTypes(true)[style], { pure: false }, true);
449 | testComponentWithStyle(styleTypes(true)[style], { stateless: true }, true);
450 | testComponentWithoutStyle(styleTypes(true)[style], { nostyle: true }, true);
451 | }
452 | for(const style in styleTypes(false)) {
453 | testComponentWithStyle(styleTypes(false)[style], {}, false);
454 | testComponentWithStyle(styleTypes(false)[style], { pure: true }, false);
455 | testComponentWithStyle(styleTypes(false)[style], { pure: false }, false);
456 | testComponentWithStyle(styleTypes(false)[style], { stateless: true }, false);
457 | testComponentWithoutStyle(styleTypes(false)[style], { nostyle: true }, false);
458 | }
459 | });
460 | });
461 |
--------------------------------------------------------------------------------
/test/generators/setup-env/assets/moduleIndex.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const foo = require('path');
4 |
5 | module.exports = {
6 | foo
7 | };
8 |
--------------------------------------------------------------------------------
/test/generators/setup-env/setupEnvTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const acorn = require('acorn');
4 | const assert = require('yeoman-assert');
5 | const fs = require('fs-extra');
6 | const helpers = require('yeoman-test');
7 | const path = require('path');
8 | const walk = require('acorn/dist/walk');
9 |
10 |
11 | /**
12 | * Returns absolute path to (sub-)generator with {@code name}.
13 | * @param {string} name
14 | */
15 | const genpath = (name) =>
16 | path.join(__dirname, '../../../generators', name);
17 |
18 | /**
19 | * A mocked generator config object.
20 | * @type {{appName: string, style: string, cssmodules: boolean, postcss: boolean, generatedWithVersion: number}}
21 | */
22 | const cfg = {
23 | appName: 'testCfg',
24 | style: 'css',
25 | cssmodules: false,
26 | postcss: false,
27 | generatedWithVersion: 4
28 | };
29 |
30 |
31 | describe('react-webpack:setup-env', function () {
32 |
33 | describe('react-webpack:setup-env foobar', function () {
34 | before(function () {
35 | return helpers
36 | .run(genpath('setup-env'))
37 | .withArguments(['foobar'])
38 | .withLocalConfig(cfg)
39 | .inTmpDir(function (dir) {
40 | fs.copySync(
41 | path.join(__dirname, 'assets/moduleIndex.js'),
42 | path.join(dir, 'conf/webpack/index.js')
43 | );
44 | })
45 | .toPromise();
46 | });
47 |
48 | it('creates env files', function () {
49 | assert.file(['conf/webpack/Foobar.js']);
50 | assert.file(['src/config/foobar.js']);
51 | });
52 |
53 | it('requires the new env in conf/webpack/index.js', function () {
54 | assert.fileContent(
55 | 'conf/webpack/index.js',
56 | /const foobar = require\('\.\/Foobar'\)/
57 | );
58 | });
59 |
60 | it('exports the new env from conf/webpack/index.js', function () {
61 | const fileStr = fs.readFileSync('conf/webpack/index.js').toString();
62 | const ast = acorn.parse(fileStr);
63 |
64 | let found = false;
65 | walk.simple(ast, {
66 | 'Property': (node) => {
67 | if (node.key.name === 'foobar' && node.value.name === 'foobar') {
68 | found = true;
69 | }
70 | }
71 | });
72 |
73 | assert(found, 'Did not find a key and value of `foobar` on the module.exports AST node');
74 | });
75 | });
76 |
77 | });
78 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --reporter spec
2 | --recursive
3 |
--------------------------------------------------------------------------------
/test/utils/configTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let expect = require('chai').expect;
3 |
4 | let utils = require('../../utils/config');
5 | let originalSettings = require('../../utils/configopts.json');
6 |
7 | describe('Utilities:Config', () => {
8 |
9 | describe('#getSetting', () => {
10 |
11 | it('should return "null" if the key could not be found', () => {
12 | expect(utils.getSetting('bogus')).to.be.null;
13 | });
14 |
15 | it('should return a settings object if it exists', () => {
16 |
17 | let result = utils.getSetting('style');
18 | expect(result).to.be.an.object;
19 | expect(result).to.deep.equal(originalSettings.style);
20 | });
21 | });
22 |
23 | describe('#getChoices', () => {
24 |
25 | it('should return "null" if the key could not be found', () => {
26 | expect(utils.getChoices('bogus')).to.be.null;
27 | });
28 |
29 | it('should return an array of choices when queried correctly', () => {
30 |
31 | let result = utils.getChoices('style');
32 | expect(result).to.be.an.array;
33 | expect(result).to.deep.equal(originalSettings.style.options);
34 | });
35 | });
36 |
37 | describe('#getChoiceByKey', () => {
38 |
39 | it('should return "null" if the key or the setting could not be found', () => {
40 |
41 | expect(utils.getChoiceByKey('bogus', 'unknown')).to.be.null;
42 | expect(utils.getChoiceByKey('style', 'unknown')).to.be.null;
43 | });
44 |
45 | it('should return the configured object when it can be found', () => {
46 |
47 | expect(utils.getChoiceByKey('style', 'css')).to.equal(originalSettings.style.options[0]);
48 | expect(utils.getChoiceByKey('style', 'less')).to.equal(originalSettings.style.options[3]);
49 | });
50 | });
51 |
52 | describe('#getDefaultChoice', () => {
53 |
54 | it('should return "null" if the key could not be found', () => {
55 | expect(utils.getDefaultChoice('bogus')).to.be.null;
56 | });
57 |
58 | it('should return the default choice when queried correctly', () => {
59 |
60 | let result = utils.getDefaultChoice('style');
61 | expect(result).to.equal(originalSettings.style.default);
62 | });
63 | });
64 |
65 | });
66 |
--------------------------------------------------------------------------------
/test/utils/yeomanTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let expect = require('chai').expect;
3 | let path = require('path');
4 |
5 | let utils = require('../../utils/yeoman');
6 | let baseDir = path.basename(process.cwd());
7 |
8 | describe('Utilities:Yeoman', () => {
9 |
10 | describe('#getBaseDir', () => {
11 |
12 | it('should return the current run directory', () => {
13 | expect(utils.getBaseDir()).to.equal(baseDir);
14 | });
15 | });
16 |
17 | describe('#getCleanedPathName', () => {
18 |
19 | it('should return normalized paths', () => {
20 |
21 | let tests = {
22 | 'my/full œ!/path!': 'my/full/path',
23 | 'Test': 'test',
24 | 'I am a Test Component!': 'iAmATestComponent',
25 | 'A very\^specialChary!@componentName with !!!': 'aVerySpecialCharyComponentNameWith'
26 | };
27 |
28 | for(let test in tests) {
29 | expect(utils.getCleanedPathName(test)).to.be.equal(tests[test]);
30 | expect(utils.getCleanedPathName(test, 'suffix')).to.be.equal(tests[test] + 'Suffix');
31 | }
32 | });
33 | });
34 |
35 | describe('#getAppName', () => {
36 |
37 | it('should return a js friendly application name', () => {
38 | let result = utils.getAppName('this is a test using % special / chars!');
39 | expect(result).to.be.equal('this-is-a-test-using-special-chars');
40 | });
41 |
42 | it('should use the current path for creating the appName if the argument is omitted', () => {
43 |
44 | let resultWithoutArgs = utils.getAppName();
45 | let resultWithArgs = utils.getAppName(baseDir);
46 |
47 | expect(resultWithoutArgs).to.be.equal(resultWithArgs);
48 | });
49 | });
50 |
51 | describe('#getComponentStyleName', () => {
52 |
53 | it('should return a components css className', () => {
54 |
55 | let tests = {
56 | 'my/full œ!/path!': 'path-component',
57 | 'Test': 'test-component',
58 | 'I am a Test Component!': 'i-am-a-test-component-component',
59 | 'A very\^specialChary!@componentName with !!!': 'a-very-specialchary-componentname-with-component'
60 | };
61 |
62 | for(let test in tests) {
63 | expect(utils.getComponentStyleName(test)).to.be.equal(tests[test]);
64 | }
65 | });
66 | });
67 |
68 | describe('#getAllSettingsFromComponentName', () => {
69 |
70 | describe('when the generator version is set to 4', () => {
71 |
72 | const expectionNamespaced = {
73 | style: {
74 | webpackPath: './test.cssmodule.css',
75 | path: 'src/components/my/component/',
76 | fileName: 'test.cssmodule.css',
77 | className: 'test-component',
78 | suffix: '.css'
79 | },
80 | component: {
81 | webpackPath: 'components/my/component/Test.js',
82 | path: 'src/components/my/component/',
83 | fileName: 'Test.js',
84 | className: 'Test',
85 | classBase: 'React.Component',
86 | displayName: 'MyComponentTest',
87 | suffix: '.js'
88 | },
89 | test: {
90 | path: 'test/components/my/component/',
91 | fileName: 'TestTest.js'
92 | }
93 | };
94 |
95 | const expectionRoot = {
96 | style: {
97 | webpackPath: './test.cssmodule.css',
98 | path: 'src/components/',
99 | fileName: 'test.cssmodule.css',
100 | className: 'test-component',
101 | suffix: '.css'
102 | },
103 | component: {
104 | webpackPath: 'components/Test.js',
105 | path: 'src/components/',
106 | fileName: 'Test.js',
107 | className: 'Test',
108 | classBase: 'React.Component',
109 | displayName: 'Test',
110 | suffix: '.js'
111 | },
112 | test: {
113 | path: 'test/components/',
114 | fileName: 'TestTest.js'
115 | }
116 | };
117 |
118 | it('should get all required information for component creation from the components name', () => {
119 | expect(utils.getAllSettingsFromComponentName('my/component/test', 'css', true, false, 4, '')).to.deep.equal(expectionNamespaced);
120 | });
121 |
122 | it('should prepend a prefix to the style.className attribute', () => {
123 | const expectation = Object.assign({}, expectionNamespaced);
124 | expectation.style.className = 'myapp-test-component';
125 | expect(utils.getAllSettingsFromComponentName('my/component/test', 'css', true, false, 4, 'myapp-')).to.deep.equal(expectation);
126 | });
127 |
128 | it('should build path information wo/ two slashes when dealing with a non-namespaced component', () => {
129 | expect(utils.getAllSettingsFromComponentName('test', 'css', true, false, 4, '')).to.deep.equal(expectionRoot);
130 | });
131 | });
132 |
133 | describe('when the generator version is set to 3 (or not set at all)', () => {
134 |
135 | const expection = {
136 | style: {
137 | webpackPath: 'styles/my/component/Test.css',
138 | path: 'src/styles/my/component/',
139 | fileName: 'Test.css',
140 | className: 'test-component',
141 | suffix: '.css'
142 | },
143 | component: {
144 | webpackPath: 'components/my/component/TestComponent.js',
145 | path: 'src/components/my/component/',
146 | fileName: 'TestComponent.js',
147 | className: 'TestComponent',
148 | displayName: 'MyComponentTestComponent',
149 | suffix: '.js'
150 | },
151 | test: {
152 | path: 'test/components/my/component/',
153 | fileName: 'TestComponentTest.js'
154 | }
155 | };
156 |
157 | it('should get all required information for component creation from the components name', () => {
158 | expect(utils.getAllSettingsFromComponentName('my/component/test')).to.deep.equal(expection);
159 | expect(utils.getAllSettingsFromComponentName('my/component/test', 'css', false, 3)).to.deep.equal(expection);
160 | });
161 | });
162 | });
163 |
164 | describe('#getDestinationPath', () => {
165 |
166 | it('should return the correct filesystem path for all components', () => {
167 |
168 | expect(utils.getDestinationPath('test', 'action', 'Actions')).to.equal('src/actions/TestActions.js');
169 | expect(utils.getDestinationPath('subfolder/test', 'action', 'Actions')).to.equal('src/actions/subfolder/TestActions.js');
170 |
171 | expect(utils.getDestinationPath('test', 'source', 'Source')).to.equal('src/sources/TestSource.js');
172 | expect(utils.getDestinationPath('subfolder/test', 'source', 'Source')).to.equal('src/sources/subfolder/TestSource.js');
173 |
174 | expect(utils.getDestinationPath('test', 'store', 'Store')).to.equal('src/stores/TestStore.js');
175 | expect(utils.getDestinationPath('subfolder/test', 'store', 'Store')).to.equal('src/stores/subfolder/TestStore.js');
176 | });
177 | });
178 |
179 | describe('#getDestinationClassName', () => {
180 |
181 | it('should return the correct javascript class name for the given paths', () => {
182 | expect(utils.getDestinationClassName('test', 'action', 'Actions')).to.equal('TestActions');
183 | expect(utils.getDestinationClassName('test', 'source', 'Source')).to.equal('TestSource');
184 | expect(utils.getDestinationClassName('test', 'store', 'Store')).to.equal('TestStore');
185 | });
186 | });
187 |
188 | describe('#getComponentTemplateName', () => {
189 |
190 | it('should return a stateless component with styles if stateless is set to true and useStyles set to true', () => {
191 | expect(utils.getComponentTemplateName(true, true)).to.equal('StatelessWithStyles.js');
192 | });
193 |
194 | it('should return a stateless component without styles if stateless is set to true and useStyles set to false', () => {
195 | expect(utils.getComponentTemplateName(true, false)).to.equal('StatelessNoStyles.js');
196 | });
197 |
198 | it('should return a statefull component with styles if stateless is set to false and useStyles set to true', () => {
199 | expect(utils.getComponentTemplateName(false, true)).to.equal('StatefulWithStyles.js');
200 | });
201 |
202 | it('should return a statefull component without styles if stateless is set to false and useStyles set to false', () => {
203 | expect(utils.getComponentTemplateName(false, false)).to.equal('StatefulNoStyles.js');
204 | });
205 |
206 | it('should return a statefull component with styles and cssmodules if stateless is set to false and useCssModules set to true', () => {
207 | expect(utils.getComponentTemplateName(false, true, true)).to.equal('StatefulCssModules.js');
208 | });
209 | });
210 | });
211 |
--------------------------------------------------------------------------------
/utils/all.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const config = require('./config');
4 | const constants = require('./constants');
5 | const yeoman = require('./yeoman');
6 |
7 | module.exports = {
8 | config,
9 | constants,
10 | yeoman
11 | };
12 |
--------------------------------------------------------------------------------
/utils/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let opts = require('./configopts.json');
3 |
4 | /**
5 | * Get a setting
6 | * @param {String} setting
7 | * @return {Mixed} setting or null if not found
8 | */
9 | let getSetting = (setting) => {
10 | return opts[setting] !== undefined ? opts[setting] : null;
11 | }
12 |
13 | /**
14 | * Get choices for a given setting
15 | * @param {String} setting
16 | * @return {Mixed} Result or null if nothing was found
17 | */
18 | let getChoices = function getChoices(setting) {
19 |
20 | let config = getSetting(setting);
21 | return config && Array.isArray(config.options) ? config.options : null;
22 | }
23 |
24 | /**
25 | * Get the wanted choice by key
26 | * @param {String} setting
27 | * @param {String} key
28 | * @return {Object}
29 | */
30 | let getChoiceByKey = (setting, key) => {
31 |
32 | let choices = getChoices(setting);
33 | if(!choices) {
34 | return null;
35 | }
36 |
37 | let result = null;
38 |
39 | for(let choice of choices) {
40 |
41 | if(choice.name === key) {
42 | result = choice;
43 | break;
44 | }
45 | }
46 |
47 | return result;
48 | }
49 |
50 | /**
51 | * Get the default choice for a config setting
52 | * @param {String} setting
53 | * @return {Mixed}
54 | */
55 | let getDefaultChoice = (setting) => {
56 | let config = getSetting(setting);
57 | return config && config.default !== undefined && config.default.length > 0 ? config.default : null;
58 | }
59 |
60 | module.exports = {
61 | getSetting,
62 | getChoices,
63 | getChoiceByKey,
64 | getDefaultChoice
65 | };
66 |
--------------------------------------------------------------------------------
/utils/configopts.json:
--------------------------------------------------------------------------------
1 | {
2 | "path": {
3 | "options": [
4 | {
5 | "name": "base",
6 | "path": "src"
7 | },
8 | {
9 | "name": "action",
10 | "path": "src/actions"
11 | },
12 | {
13 | "name": "component",
14 | "path": "src/components"
15 | },
16 | {
17 | "name": "image",
18 | "path": "src/images"
19 | },
20 | {
21 | "name": "source",
22 | "path": "src/sources"
23 | },
24 | {
25 | "name": "store",
26 | "path": "src/stores"
27 | },
28 | {
29 | "name": "style",
30 | "path": "src/styles"
31 | },
32 | {
33 | "name": "test",
34 | "path": "test"
35 | },
36 | {
37 | "name": "dist",
38 | "path": "dist"
39 | }
40 | ]
41 | },
42 | "postcss": {
43 | "options": [
44 | {
45 | "name": "postcss",
46 | "value": "postcss",
47 | "packages": [
48 | { "name": "postcss", "version": "^5.0.21" },
49 | { "name": "postcss-loader", "version": "^0.9.1" }
50 | ]
51 | }
52 | ]
53 | },
54 | "cssmodules": {
55 | "options": [
56 | {
57 | "name": "cssmodules",
58 | "value": "cssmodules",
59 | "packages": [
60 | { "name": "react-css-modules", "version": "^3.7.10" }
61 | ]
62 | }
63 | ]
64 | },
65 | "style": {
66 | "options": [
67 | {
68 | "name": "css",
69 | "value": "css",
70 | "suffix": ".css"
71 | },
72 | {
73 | "name": "sass",
74 | "value": "sass",
75 | "suffix": ".sass",
76 | "packages": [
77 | { "name": "sass-loader", "version": "^3.2.0" },
78 | { "name": "node-sass", "version": "^3.7.0" }
79 | ]
80 | },
81 | {
82 | "name": "scss",
83 | "value": "scss",
84 | "suffix": ".scss",
85 | "packages": [
86 | { "name": "sass-loader", "version": "^3.2.0" },
87 | { "name": "node-sass", "version": "^3.7.0" }
88 | ]
89 | },
90 | {
91 | "name": "less",
92 | "value": "less",
93 | "suffix": ".less",
94 | "packages": [
95 | { "name": "less-loader", "version": "^2.2.3" },
96 | { "name": "less", "version": "^2.7.1" }
97 | ]
98 | },
99 | {
100 | "name": "stylus",
101 | "value": "stylus",
102 | "suffix": ".styl",
103 | "packages": [
104 | { "name": "stylus-loader", "version": "^2.1.0" },
105 | { "name": "stylus", "version": "^0.54.5" },
106 | { "name": "nib", "version": "~1.1.0" }
107 | ]
108 | }
109 | ],
110 | "default": "css"
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/utils/constants.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * List of supported generator versions.
5 | * @type {number[]}
6 | */
7 | const SUPPORTED_GEN_VERSIONS = [3, 4];
8 |
9 |
10 | /**
11 | * ENUM of supported component types.
12 | * @type {{STATEFUL: string, STATELESS: string}}
13 | */
14 | const COMP_TYPES = {
15 | STATEFUL: 'Stateful',
16 | STATELESS: 'Stateless'
17 | };
18 |
19 |
20 | /**
21 | * ENUM of supported style types.
22 | * @type {{WITH_STYLES: string, WITH_CSSMODULES: string, NO_STYLES: string}}
23 | */
24 | const STYLE_TYPES = {
25 | WITH_STYLES: 'WithStyles',
26 | WITH_CSSMODULES: 'CssModules',
27 | NO_STYLES: 'NoStyles'
28 | };
29 |
30 |
31 | module.exports = {
32 | SUPPORTED_GEN_VERSIONS,
33 | COMP_TYPES,
34 | STYLE_TYPES
35 | };
36 |
--------------------------------------------------------------------------------
/utils/yeoman.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const configUtils = require('./config');
5 | const _ = require('underscore.string');
6 | const C = require('./constants');
7 |
8 | // Needed directory paths
9 | const baseName = path.basename(process.cwd());
10 |
11 | /**
12 | * Get the base directory
13 | * @return {String}
14 | */
15 | let getBaseDir = () => {
16 | return baseName;
17 | };
18 |
19 | /**
20 | * Get all settings (paths and the like) from components name
21 | * @param {String} componentName The components name
22 | * @param {String} style Style language to use [optional]
23 | * @param {boolean} useCssModules Flag indicating whether to use css modules
24 | * @param {Boolean} isPure Use a pure component? [optional]
25 | * @param {String|Number} generatorVersion The version of the generator [optional]
26 | * @param {String} cssClsPrefix Prefix to prepended to the component class name
27 | * @return {Object} Component settings
28 | */
29 | let getAllSettingsFromComponentName = (componentName, style, useCssModules, isPure, generatorVersion, cssClsPrefix) => {
30 |
31 | // Use css per default
32 | if(!style) {
33 | style = 'css';
34 | }
35 |
36 | // Use version 3 fallback as default for projects
37 | if(!generatorVersion) {
38 | generatorVersion = 3;
39 | }
40 |
41 | // Clean up the path and pull it to parts
42 | let cleanedPaths = getCleanedPathName(componentName);
43 | let componentParts = cleanedPaths.split('/');
44 | let componentBaseName = _.capitalize(componentParts.pop());
45 | let componentPartPath = componentParts.join('/');
46 |
47 | // Get the components displayName property
48 | let componentFullName = _.classify(_.replaceAll(componentName, '/', '_'));
49 |
50 | // Configure Styles
51 | let stylePaths = configUtils.getChoiceByKey('path', 'style');
52 | let styleSettings = configUtils.getChoiceByKey('style', style);
53 |
54 | // Configure components
55 | let componentPath = configUtils.getChoiceByKey('path', 'component');
56 |
57 | // Configure tests
58 | let testPath = configUtils.getChoiceByKey('path', 'test');
59 |
60 | let settings;
61 |
62 | switch(generatorVersion) {
63 |
64 | case 4:
65 | settings = {
66 | style: {
67 | webpackPath: `./${componentBaseName.toLowerCase()}${useCssModules ? '.cssmodule' : ''}${styleSettings.suffix}`,
68 | path: path.normalize(`${componentPath.path}/${componentPartPath}/`),
69 | fileName: `${componentBaseName.toLowerCase()}${useCssModules ? '.cssmodule' : ''}${styleSettings.suffix}`,
70 | className: getComponentStyleName(cssClsPrefix + componentBaseName),
71 | suffix: styleSettings.suffix
72 | },
73 | component: {
74 | webpackPath: path.normalize(`components/${componentPartPath}/${componentBaseName}.js`),
75 | path: path.normalize(`${componentPath.path}/${componentPartPath}/`),
76 | fileName: `${componentBaseName}.js`,
77 | className: `${componentBaseName}`,
78 | classBase: isPure ? 'React.PureComponent' : 'React.Component',
79 | displayName: `${componentFullName}`,
80 | suffix: '.js'
81 | },
82 | test: {
83 | path: path.normalize(`${testPath.path}/components/${componentPartPath}/`),
84 | fileName: `${componentBaseName}Test.js`
85 | }
86 | };
87 | break;
88 |
89 | // Use version 3 style for the defaults and fallback
90 | // @deprecated
91 | case 3:
92 | default:
93 | settings = {
94 | style: {
95 | webpackPath: path.normalize(`styles/${componentPartPath}/${componentBaseName}${styleSettings.suffix}`),
96 | path: path.normalize(`${stylePaths.path}/${componentPartPath}/`),
97 | fileName: `${componentBaseName}${styleSettings.suffix}`,
98 | className: getComponentStyleName(componentBaseName),
99 | suffix: styleSettings.suffix
100 | },
101 | component: {
102 | webpackPath: path.normalize(`components/${componentPartPath}/${componentBaseName}Component.js`),
103 | path: path.normalize(`${componentPath.path}/${componentPartPath}/`),
104 | fileName: `${componentBaseName}Component.js`,
105 | className: `${componentBaseName}Component`,
106 | displayName: `${componentFullName}Component`,
107 | suffix: '.js'
108 | },
109 | test: {
110 | path: path.normalize(`${testPath.path}/components/${componentPartPath}/`),
111 | fileName: `${componentBaseName}ComponentTest.js`
112 | }
113 | };
114 | break;
115 | }
116 |
117 | return settings;
118 | };
119 |
120 | /**
121 | * Get a cleaned path name for a given path
122 | * @param {String} path
123 | * @param {String} suffix [optional]
124 | * @return {String}
125 | */
126 | let getCleanedPathName = (path, suffix) => {
127 |
128 | if(!suffix) {
129 | suffix = '';
130 | }
131 |
132 | // If we have filesystem separators, use them to build the full path
133 | let pathArray = path.split('/');
134 |
135 | // Build the full components name
136 | return pathArray.map((path) => {
137 | return _.camelize(_.slugify(_.humanize(path)));
138 | }).join('/') + _.capitalize(suffix);
139 | };
140 |
141 | /**
142 | * Get the css/less/whatever style name to use
143 | * @param {String} path
144 | * @return {String}
145 | */
146 | let getComponentStyleName = (path) => {
147 | let fileName = path.split('/').pop().toLowerCase();
148 | return _.slugify(_.humanize(fileName)) + '-component';
149 | };
150 |
151 | /**
152 | * Get a js friendly application name
153 | * @param {String} appName The input application name [optional]
154 | * @return {String}
155 | */
156 | let getAppName = (appName) => {
157 |
158 | // If appName is not given, use the current directory
159 | if(appName === undefined) {
160 | appName = getBaseDir();
161 | }
162 |
163 | return _.slugify(_.humanize(appName));
164 | };
165 |
166 | /**
167 | * Get the wanted destination path
168 | * @param {String} name Name of the file
169 | * @param {String} type The type to use (e.g. action, store, ...)
170 | * @param {Suffix} suffix The suffix to use for the file (e.g. Store, Actions, ...)
171 | * @return {String} Final path
172 | */
173 | let getDestinationPath = (name, type, suffix) => {
174 |
175 | let cleanedPaths = getCleanedPathName(name, suffix);
176 | let fsParts = cleanedPaths.split('/');
177 | let actionBaseName = _.capitalize(fsParts.pop());
178 | let partPath = fsParts.join('/');
179 |
180 | let fsPath = configUtils.getChoiceByKey('path', type).path;
181 |
182 | let parts = [ fsPath ];
183 | if(partPath.length > 0) {
184 | parts.push(partPath);
185 | }
186 | parts.push(actionBaseName);
187 | let fullPath = parts.join('/');
188 |
189 | return `${fullPath}.js`;
190 | };
191 |
192 | /**
193 | * Get the destinations class name
194 | * @param {String} name Name of the file
195 | * @param {String} type The type to use (e.g. action, store, ...)
196 | * @param {Suffix} suffix The suffix to use for the file (e.g. Store, Actions, ...)
197 | * @return {String} The javascript class name to use
198 | */
199 | let getDestinationClassName = (name, type, suffix) => {
200 |
201 | let fixedName = getDestinationPath(name, type, suffix);
202 | return _.capitalize(fixedName.split('/').pop().split('.js')[0]);
203 | };
204 |
205 | /**
206 | * Get the filename of the component template to copy.
207 | * @param {boolean} isStateless
208 | * @param {boolean} useStyles
209 | * @param {boolean} useCssModules
210 | * @return {string} The template filename including the .js suffix
211 | */
212 | let getComponentTemplateName = (isStateless, useStyles, useCssModules) => {
213 | const componentTypeFrag = isStateless ? C.COMP_TYPES.STATELESS : C.COMP_TYPES.STATEFUL;
214 | const styleTypeFrag = !useStyles
215 | ? C.STYLE_TYPES.NO_STYLES
216 | : useCssModules
217 | ? C.STYLE_TYPES.WITH_CSSMODULES
218 | : C.STYLE_TYPES.WITH_STYLES
219 | ;
220 |
221 | return `${componentTypeFrag}${styleTypeFrag}.js`;
222 | };
223 |
224 | module.exports = {
225 | getBaseDir,
226 | getAllSettingsFromComponentName,
227 | getAppName,
228 | getCleanedPathName,
229 | getComponentStyleName,
230 | getComponentTemplateName,
231 | getDestinationPath,
232 | getDestinationClassName
233 | };
234 |
--------------------------------------------------------------------------------