Skip to content

Commit 0f89280

Browse files
committed
Merged with upstream
2 parents 954c3d1 + 9c68254 commit 0f89280

File tree

11 files changed

+325
-7
lines changed

11 files changed

+325
-7
lines changed

generators/component/index.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ class ComponentGenerator extends Generators.Base {
2222
*/
2323
this.useCssModules = false;
2424

25+
/**
26+
* A string to prepended to the `className` attribute of generated components.
27+
* @type {string}
28+
*/
29+
this._cssClsPrefix = '';
30+
2531
/**
2632
* Flag indicating if stateful components should extends from React.PureComponent
2733
* @type {boolean}
@@ -58,6 +64,13 @@ class ComponentGenerator extends Generators.Base {
5864
});
5965
}
6066

67+
get cssClsPrefix() {
68+
return this._cssClsPrefix;
69+
}
70+
71+
set cssClsPrefix(val) {
72+
this._cssClsPrefix = (val === '') ? '' : `${val}-`;
73+
}
6174

6275
configuring() {
6376
// Read the requested major version or default it to the latest stable
@@ -69,6 +82,7 @@ class ComponentGenerator extends Generators.Base {
6982

7083
this.useStyles = !this.options.nostyle;
7184
this.useCssModules = this.config.get('cssmodules') || false;
85+
this.cssClsPrefix = this.config.get('cssClsPrefix') || '';
7286

7387
// Make sure we don't try to use template v3 with cssmodules
7488
if (this.generatorVersion < 4 && this.useStyles && this.useCssModules) {
@@ -82,7 +96,14 @@ class ComponentGenerator extends Generators.Base {
8296

8397
writing() {
8498
const settings =
85-
getAllSettingsFromComponentName(this.name, this.config.get('style'), this.useCssModules, this.options.pure, this.generatorVersion);
99+
getAllSettingsFromComponentName(
100+
this.name,
101+
this.config.get('style'),
102+
this.useCssModules,
103+
this.options.pure,
104+
this.generatorVersion,
105+
this.cssClsPrefix
106+
);
86107

87108
// Create the style template. Skipped if nostyle is set as command line flag
88109
if(this.useStyles) {

generators/setup-env/constants.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
};

generators/setup-env/index.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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/<EnvName>.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/<env_name>.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;
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import baseConfig from './base';
2+
3+
const config = {
4+
appEnv: '<%= envName %>',
5+
};
6+
7+
export default Object.freeze(Object.assign({}, baseConfig, config));

generators/setup-env/utils.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
};

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,21 @@
4747
"release:patch": "npm version prerelease && git push --follow-tags && npm publish --tag beta"
4848
},
4949
"dependencies": {
50-
"escodegen": "^1.7.0",
50+
"acorn": "^4.0.3",
51+
"escodegen": "^1.8.1",
52+
"esformatter": "^0.9.6",
5153
"esprima": "^3.1.1",
5254
"esprima-walk": "^0.1.0",
53-
"react-webpack-template": "^2.0.1-5",
55+
"jsonpath": "^0.2.7",
56+
"react-webpack-template": "^2.0.1-6",
5457
"underscore.string": "^3.2.2",
5558
"yeoman-generator": "^0.24.0",
5659
"yeoman-welcome": "^1.0.1"
5760
},
5861
"devDependencies": {
5962
"chai": "^3.2.0",
6063
"coveralls": "^2.11.12",
64+
"fs-extra": "^0.30.0",
6165
"istanbul": "^0.4.5",
6266
"mocha": "^3.0.0",
6367
"yeoman-assert": "^2.1.1",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
const foo = require('path');
4+
5+
module.exports = {
6+
foo
7+
};
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
});

test/utils/yeomanTest.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,17 @@ describe('Utilities:Yeoman', () => {
116116
};
117117

118118
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);
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);
120126
});
121127

122128
it('should build path information wo/ two slashes when dealing with a non-namespaced component', () => {
123-
expect(utils.getAllSettingsFromComponentName('test', 'css', true, false, 4)).to.deep.equal(expectionRoot);
129+
expect(utils.getAllSettingsFromComponentName('test', 'css', true, false, 4, '')).to.deep.equal(expectionRoot);
124130
});
125131
});
126132

0 commit comments

Comments
 (0)