Skip to content

Commit ba3ee5f

Browse files
authored
Add --run-codemod commandline option. (#162)
Add `--run-codemod` commandline option.
2 parents b34d1f6 + 419b9a9 commit ba3ee5f

File tree

4 files changed

+123
-52
lines changed

4 files changed

+123
-52
lines changed

commands/index.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const SHARED = {
4646
return configPath;
4747
},
4848

49-
_setFeature: async function (name, value) {
49+
_setFeature: async function (name, value, shouldRunCodemod) {
5050
let feature = FEATURES[name];
5151

5252
if (feature === undefined) {
@@ -62,7 +62,7 @@ const SHARED = {
6262
}
6363

6464
if (typeof feature.callback === 'function') {
65-
await feature.callback(this.project, value);
65+
await feature.callback(this.project, value, shouldRunCodemod);
6666
}
6767

6868
let config = {};
@@ -134,23 +134,41 @@ const ENABLE_FEATURE = Object.assign({
134134
name: 'feature:enable',
135135
description: 'Enable feature.',
136136
works: 'insideProject',
137+
availableOptions: [
138+
{
139+
name: 'run-codemod',
140+
type: Boolean,
141+
description: 'run any associated codemods without prompting'
142+
// intentionally not setting a default, when the value is undefined the
143+
// command will prompt the user
144+
},
145+
],
137146
anonymousOptions: [
138147
'<feature-name>'
139148
],
140-
run(_, args) {
141-
return this._setFeature(args[0], true);
149+
run(commandOptions, args) {
150+
return this._setFeature(args[0], true, commandOptions.runCodemod);
142151
}
143152
}, SHARED);
144153

145154
const DISABLE_FEATURE = Object.assign({
146155
name: 'feature:disable',
147156
description: 'Disable feature.',
148157
works: 'insideProject',
158+
availableOptions: [
159+
{
160+
name: 'run-codemod',
161+
type: Boolean,
162+
description: 'run any associated codemods without prompting'
163+
// intentionally not setting a default, when the value is undefined the
164+
// command will prompt the user
165+
},
166+
],
149167
anonymousOptions: [
150168
'<feature-name>'
151169
],
152-
run(_, args) {
153-
return this._setFeature(args[0], false);
170+
run(commandOptions, args) {
171+
return this._setFeature(args[0], false, commandOptions.runCodemod);
154172
}
155173
}, SHARED);
156174

features/application-template-wrapper.js

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = {
1313
url: 'https://github.com/emberjs/rfcs/pull/280',
1414
default: true,
1515
since: '3.1.0',
16-
callback: async function (project, value) {
16+
callback: async function (project, value, shouldRunCodemod) {
1717
if (value !== false) {
1818
return;
1919
}
@@ -44,34 +44,38 @@ module.exports = {
4444
}
4545
}
4646

47-
console.log(strip`
48-
Disabling ${chalk.bold('application-template-wrapper')}...
47+
if (shouldRunCodemod === undefined) {
48+
console.log(strip`
49+
Disabling ${chalk.bold('application-template-wrapper')}...
4950
50-
This will remove the \`<div class="ember-view">\` wrapper for the top-level application template (\`${templatePath}\`).
51+
This will remove the \`<div class="ember-view">\` wrapper for the top-level application template (\`${templatePath}\`).
5152
52-
While this may be a desirable change, it might break your styling in subtle ways:
53+
While this may be a desirable change, it might break your styling in subtle ways:
5354
54-
- Perhaps you were targeting the \`.ember-view\` class in your CSS.
55+
- Perhaps you were targeting the \`.ember-view\` class in your CSS.
5556
56-
- Perhaps you were targeting the \`<div>\` element in your CSS (e.g. \`body > div > .some-child\`).
57+
- Perhaps you were targeting the \`<div>\` element in your CSS (e.g. \`body > div > .some-child\`).
5758
58-
- Depending on your choice of \`rootElement\`, your app might not be wrapped inside a block-level element anymore.
59+
- Depending on your choice of \`rootElement\`, your app might not be wrapped inside a block-level element anymore.
5960
60-
For more information, see ${chalk.underline('https://github.com/emberjs/rfcs/pull/280')}.
61+
For more information, see ${chalk.underline('https://github.com/emberjs/rfcs/pull/280')}.
6162
62-
To be very conservative, I could add the \`<div class="ember-view">\` wrapper to your application.hbs. (You can always remove it later.)
63-
`);
63+
To be very conservative, I could add the \`<div class="ember-view">\` wrapper to your application.hbs. (You can always remove it later.)
64+
`);
6465

65-
let response = await inquirer.prompt({
66-
type: 'confirm',
67-
name: 'shouldRewrite',
68-
message: 'Would you like me to do that for you?',
69-
default: false
70-
});
66+
let response = await inquirer.prompt({
67+
type: 'confirm',
68+
name: 'shouldRewrite',
69+
message: 'Would you like me to do that for you?',
70+
default: false
71+
});
72+
73+
console.log();
7174

72-
console.log();
75+
shouldRunCodemod = response.shouldRewrite;
76+
}
7377

74-
if (response.shouldRewrite) {
78+
if (shouldRunCodemod) {
7579
let lines = originalContent.split('\n');
7680
let hasFinalNewLine;
7781

features/template-only-glimmer-components.js

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ const ComponentFile = strip`
1717
});
1818
`;
1919

20-
/* This forces strip`` to start counting the indentaiton */
20+
/* This forces strip`` to start counting the indentaton */
2121
const INDENT_START = '';
2222

2323
module.exports = {
2424
description: 'Use Glimmer Components semantics for template-only components (component templates with no corresponding .js file).',
2525
url: 'https://github.com/emberjs/rfcs/pull/278',
2626
default: false,
2727
since: '3.1.0',
28-
callback: async function (project, value) {
28+
callback: async function (project, value, shouldRunCodemod) {
2929
if (value !== true) {
3030
return;
3131
}
@@ -108,45 +108,49 @@ module.exports = {
108108
return;
109109
}
110110

111-
console.log(strip`
112-
Enabling ${chalk.bold('template-only-glimmer-components')}...
111+
if (shouldRunCodemod === undefined) {
112+
console.log(strip`
113+
Enabling ${chalk.bold('template-only-glimmer-components')}...
113114
114-
This will change the semantics for template-only components (components without a \`.js\` file).
115+
This will change the semantics for template-only components (components without a \`.js\` file).
115116
116-
Some notable differences include...
117+
Some notable differences include...
117118
118-
- They will not have a component instance, so statements like \`{{this}}\`, \`{{this.foo}}\` and \`{{foo}}\` will be \`null\` or \`undefined\`.
119+
- They will not have a component instance, so statements like \`{{this}}\`, \`{{this.foo}}\` and \`{{foo}}\` will be \`null\` or \`undefined\`.
119120
120-
- They will not have a wrapper element: what you have in the template will be what is rendered on the screen.
121+
- They will not have a wrapper element: what you have in the template will be what is rendered on the screen.
121122
122-
- Passing classes in the invocation (i.e. \`{{my-component class="..."}}\`) will not work, since there is no wrapper element to apply the classes to.
123+
- Passing classes in the invocation (i.e. \`{{my-component class="..."}}\`) will not work, since there is no wrapper element to apply the classes to.
123124
124-
For more information, see ${chalk.underline('https://github.com/emberjs/rfcs/pull/278')}.
125+
For more information, see ${chalk.underline('https://github.com/emberjs/rfcs/pull/278')}.
125126
126-
While these changes may be desirable for ${chalk.italic('new components')}, they may unexpectedly break the styling or runtime behavior of your ${chalk.italic('existing components')}.
127+
While these changes may be desirable for ${chalk.italic('new components')}, they may unexpectedly break the styling or runtime behavior of your ${chalk.italic('existing components')}.
127128
128-
To be conservative, it is recommended that you add a \`.js\` file for existing template-only components. (You can always delete them later if you aren't relying on the differences.)
129+
To be conservative, it is recommended that you add a \`.js\` file for existing template-only components. (You can always delete them later if you aren't relying on the differences.)
129130
130-
The following components are affected:`);
131+
The following components are affected:`);
131132

132-
for (let i=0; i<templates.length; i++) {
133-
console.log(strip`
133+
for (let i=0; i<templates.length; i++) {
134+
console.log(strip`
134135
${INDENT_START}
135136
- ${chalk.underline(templates[i])}
136137
${chalk.gray(`(Recommendation: add ${chalk.cyan.underline(components[i])})`)}
137-
`);
138-
}
138+
`);
139+
}
139140

140-
let response = await inquirer.prompt({
141-
type: 'confirm',
142-
name: 'shouldGenerate',
143-
message: 'Would you like me to generate these component files for you?',
144-
default: true
145-
});
141+
let response = await inquirer.prompt({
142+
type: 'confirm',
143+
name: 'shouldGenerate',
144+
message: 'Would you like me to generate these component files for you?',
145+
default: true
146+
});
147+
148+
shouldRunCodemod = response.shouldGenerate;
146149

147-
console.log();
150+
console.log();
151+
}
148152

149-
if (response.shouldGenerate) {
153+
if (shouldRunCodemod) {
150154
for (let i=0; i<components.length; i++) {
151155
let componentPath = components[i];
152156
console.log(` ${chalk.green('create')} ${componentPath}`);

tests/commands-test.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ const strip = require('../utils').strip;
1111

1212
const FEATURES = require('../features');
1313

14-
function run(/*command, ...args, options */) {
15-
let args = [].slice.call(arguments);
14+
function run(...args) {
1615
let options = {};
1716

1817
if (typeof args[args.length - 1] === 'object') {
@@ -241,6 +240,44 @@ QUnit.module('commands', hooks => {
241240
});
242241

243242
QUnit.module('feature:disable application-template-wrapper', () => {
243+
QUnit.test('it rewrites application.hbs without prompt when asked to', async function (assert) {
244+
project.write({
245+
app: {
246+
templates: {
247+
'application.hbs': strip`
248+
<ul>
249+
<li>One</li>
250+
<li>Two</li>
251+
<li>Three</li>
252+
</ul>
253+
254+
{{outlet}}
255+
256+
<!-- wow -->
257+
`
258+
}
259+
}
260+
});
261+
262+
await run('feature:disable', 'application-template-wrapper', '--run-codemod');
263+
264+
assert.deepEqual(project.read('app/templates'), {
265+
'application.hbs': strip`
266+
<div class="ember-view">
267+
<ul>
268+
<li>One</li>
269+
<li>Two</li>
270+
<li>Three</li>
271+
</ul>
272+
273+
{{outlet}}
274+
275+
<!-- wow -->
276+
</div>
277+
`
278+
}, 'it should have rewritten the template with the wrapper');
279+
});
280+
244281
QUnit.test('it rewrites application.hbs when asked to', async function (assert) {
245282
project.write({
246283
app: {
@@ -495,6 +532,14 @@ QUnit.module('commands', hooks => {
495532
assert.deepEqual(project.read('app'), CLASSIC_AFTER, 'it should have generated the component JS files');
496533
});
497534

535+
QUnit.test('it generates component files without prompt when asked to', async function (assert) {
536+
project.write({ app: CLASSIC_BEFORE });
537+
538+
await run('feature:enable', 'template-only-glimmer-components', '--run-codemod');
539+
540+
assert.deepEqual(project.read('app'), CLASSIC_AFTER, 'it should have generated the component JS files');
541+
});
542+
498543
QUnit.test('it works for pods', async function (assert) {
499544
project.write({
500545
app: PODS_BEFORE,

0 commit comments

Comments
 (0)