-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BUGFIX beta] Fix
{{input on="foo" action="bar"}}
.
- Loading branch information
Showing
4 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
packages/ember-template-compiler/lib/plugins/transform-input-on-to-onEvent.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/** | ||
@module ember | ||
@submodule ember-htmlbars | ||
*/ | ||
|
||
/** | ||
An HTMLBars AST transformation that replaces all instances of | ||
```handlebars | ||
{{input on="enter" action="doStuff"}} | ||
{{input on="key-press" action="doStuff"}} | ||
``` | ||
with | ||
```handlebars | ||
{{input enter="doStuff"}} | ||
{{input key-press="doStuff"}} | ||
``` | ||
@private | ||
@class TransformInputOnToOnEvent | ||
*/ | ||
function TransformInputOnToOnEvent(options) { | ||
// set later within HTMLBars to the syntax package | ||
this.syntax = null; | ||
this.options = options || {}; | ||
} | ||
|
||
/** | ||
@private | ||
@method transform | ||
@param {AST} The AST to be transformed. | ||
*/ | ||
TransformInputOnToOnEvent.prototype.transform = function TransformInputOnToOnEvent_transform(ast) { | ||
const pluginContext = this; | ||
const b = pluginContext.syntax.builders; | ||
const walker = new pluginContext.syntax.Walker(); | ||
|
||
walker.visit(ast, function(node) { | ||
if (pluginContext.validate(node)) { | ||
let action = hashPairForKey(node.hash, 'action'); | ||
let on = hashPairForKey(node.hash, 'on'); | ||
let onEvent = hashPairForKey(node.hash, 'onEvent'); | ||
let normalizedOn = on || onEvent; | ||
let moduleInfo = pluginContext.calculateModuleInfo(node.loc); | ||
|
||
if (normalizedOn && normalizedOn.value.type !== 'StringLiteral') { | ||
Ember.deprecate( | ||
`Using a dynamic value for '#{normalizedOn.key}=' with the '{{input}}' helper ${moduleInfo} is deprecated.` | ||
); | ||
|
||
normalizedOn.key = 'onEvent'; | ||
return; // exit early, as we cannot transform further | ||
} | ||
|
||
removeFromHash(node.hash, normalizedOn); | ||
removeFromHash(node.hash, action); | ||
|
||
if (!action) { | ||
Ember.deprecate( | ||
`Using '{{input ${normalizedOn.key}="${normalizedOn.value.value}" ...}}' without specifying an action ${moduleInfo} will do nothing.` | ||
); | ||
|
||
return; // exit early, if no action was available there is nothing to do | ||
} | ||
|
||
|
||
let specifiedOn = normalizedOn ? `${normalizedOn.key}="${normalizedOn.value.value}" ` : ''; | ||
if (normalizedOn && normalizedOn.value.value === 'keyPress') { | ||
// using `keyPress` in the root of the component will | ||
// clobber the keyPress event handler | ||
normalizedOn.value.value = 'key-press'; | ||
} | ||
|
||
let expected = `${normalizedOn ? normalizedOn.value.value : 'enter'}="${action.value.original}"`; | ||
|
||
Ember.deprecate( | ||
`Using '{{input ${specifiedOn}action="${action.value.original}"}} ${moduleInfo} is deprecated. Please use '{{input ${expected}}}' instead.` | ||
); | ||
if (!normalizedOn) { | ||
normalizedOn = b.pair('onEvent', b.string('enter')); | ||
} | ||
|
||
node.hash.pairs.push(b.pair( | ||
normalizedOn.value.value, | ||
action.value | ||
)); | ||
} | ||
}); | ||
|
||
return ast; | ||
}; | ||
|
||
TransformInputOnToOnEvent.prototype.validate = function TransformWithAsToHash_validate(node) { | ||
return node.type === 'MustacheStatement' && | ||
node.path.original === 'input' && | ||
( | ||
hashPairForKey(node.hash, 'action') || | ||
hashPairForKey(node.hash, 'on') || | ||
hashPairForKey(node.hash, 'onEvent') | ||
); | ||
}; | ||
|
||
TransformInputOnToOnEvent.prototype.calculateModuleInfo = function TransformInputOnToOnEvent_calculateModuleInfo(loc) { | ||
let { column, line } = loc.start || {}; | ||
let moduleInfo = ''; | ||
if (this.options.moduleName) { | ||
moduleInfo += `'${this.options.moduleName}' `; | ||
} | ||
|
||
if (line !== undefined && column !== undefined) { | ||
moduleInfo += `@L${line}:C${column}`; | ||
} | ||
|
||
return moduleInfo; | ||
}; | ||
|
||
function hashPairForKey(hash, key) { | ||
for (let i = 0, l = hash.pairs.length; i < l; i++) { | ||
let pair = hash.pairs[i]; | ||
if (pair.key === key) { | ||
return pair; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
function removeFromHash(hash, pairToRemove) { | ||
var newPairs = []; | ||
for (let i = 0, l = hash.pairs.length; i < l; i++) { | ||
let pair = hash.pairs[i]; | ||
|
||
if (pair !== pairToRemove) { | ||
newPairs.push(pair); | ||
} | ||
} | ||
|
||
hash.pairs = newPairs; | ||
} | ||
|
||
export default TransformInputOnToOnEvent; |
43 changes: 43 additions & 0 deletions
43
packages/ember-template-compiler/tests/plugins/transform-input-on-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { compile } from "ember-template-compiler"; | ||
|
||
QUnit.module('ember-template-compiler: transform-input-on'); | ||
|
||
QUnit.test("Using `action` without `on` provides a deprecation", function() { | ||
expect(1); | ||
|
||
expectDeprecation(function() { | ||
compile('{{input action="foo"}}', { | ||
moduleName: 'foo/bar/baz' | ||
}); | ||
}, `Using '{{input action="foo"}} 'foo/bar/baz' @L1:C0 is deprecated. Please use '{{input enter="foo"}}' instead.`); | ||
}); | ||
|
||
QUnit.test("Using `action` with `on` provides a deprecation", function() { | ||
expect(1); | ||
|
||
expectDeprecation(function() { | ||
compile('{{input on="focus-in" action="foo"}}', { | ||
moduleName: 'foo/bar/baz' | ||
}); | ||
}, `Using '{{input on="focus-in" action="foo"}} 'foo/bar/baz' @L1:C0 is deprecated. Please use '{{input focus-in="foo"}}' instead.`); | ||
}); | ||
|
||
QUnit.test("Using `on='keyPress'` does not clobber `keyPress`", function() { | ||
expect(1); | ||
|
||
expectDeprecation(function() { | ||
compile('{{input on="keyPress" action="foo"}}', { | ||
moduleName: 'foo/bar/baz' | ||
}); | ||
}, `Using '{{input on="keyPress" action="foo"}} 'foo/bar/baz' @L1:C0 is deprecated. Please use '{{input key-press="foo"}}' instead.`); | ||
}); | ||
|
||
QUnit.test("Using `on='foo'` without `action='asdf'` raises specific deprecation", function() { | ||
expect(1); | ||
|
||
expectDeprecation(function() { | ||
compile('{{input on="asdf"}}', { | ||
moduleName: 'foo/bar/baz' | ||
}); | ||
}, `Using '{{input on="asdf" ...}}' without specifying an action 'foo/bar/baz' @L1:C0 will do nothing.`); | ||
}); |