Skip to content

Commit 60191dd

Browse files
Merge pull request #62 from Turbo87/prop-override
Rewrite `PathExpression` visitor code
2 parents 421cc1e + 0c1885a commit 60191dd

8 files changed

Lines changed: 105 additions & 171 deletions

File tree

transforms/no-implicit-this/__testfixtures__/-mock-telemetry.json

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,6 @@
55
"block-component": { "type": "Component" },
66
"foo": { "type": "Component" },
77
"namespace/foo": { "type": "Component" },
8-
"built-in-helpers": {
9-
"type": "Component",
10-
"computedProperties": ["foo", "records"],
11-
"ownActions": ["myAction"]
12-
},
13-
"custom-helpers": { "type": "Component" },
14-
"angle-brackets-with-block-params": {
15-
"type": "Component",
16-
"computedProperties": ["foo", "property", "bar"],
17-
"ownActions": ["myAction"]
18-
},
19-
"angle-brackets-with-hash-params": {
20-
"type": "Component",
21-
"computedProperties": ["foo", "property"],
22-
"ownActions": ["myAction"]
23-
},
24-
"angle-brackets-without-params": {
25-
"type": "Component",
26-
"computedProperties": ["foo"]
27-
},
28-
"dont-assume-this": {
29-
"type": "Component",
30-
"computedProperties": ["foo"]
31-
},
32-
"handlebars-with-positional-params": {
33-
"type": "Component",
34-
"computedProperties": ["foo", "property"],
35-
"ownActions": ["myAction"]
36-
},
37-
"handlebars-with-wall-street-syntax": {
38-
"type": "Component",
39-
"computedProperties": ["foo", "property"],
40-
"ownActions": ["myAction"]
41-
},
42-
"handlebars-with-hash-params": {
43-
"type": "Component",
44-
"computedProperties": ["foo", "property"],
45-
"ownActions": ["myAction"]
46-
},
47-
"handlebars-with-block-params": {
48-
"type": "Component",
49-
"computedProperties": ["foo", "property"]
50-
},
51-
"handlebars-without-params": {
52-
"type": "Component",
53-
"computedProperties": ["foo", "property"],
54-
"getters": ["someGetter"]
55-
},
56-
"void-elements": {
57-
"type": "Component",
58-
"computedProperties": ["previewImageUrl"]
59-
},
608
"my-helper": { "type": "Helper" },
619
"a-helper": { "type": "Helper" }
6210
}

transforms/no-implicit-this/__testfixtures__/dont-assume-this.input.hbs

Lines changed: 0 additions & 2 deletions
This file was deleted.

transforms/no-implicit-this/__testfixtures__/dont-assume-this.options.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

transforms/no-implicit-this/__testfixtures__/dont-assume-this.output.hbs

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{my-component}}
22
{{a-helper}}
3-
{{this.foo}}
3+
{{foo}}
44
{{this.property}}
55
{{namespace/foo}}
66
{{this.someGetter}}

transforms/no-implicit-this/helpers/determine-this-usage.js

Lines changed: 0 additions & 30 deletions
This file was deleted.

transforms/no-implicit-this/helpers/plugin.js

Lines changed: 100 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ const KNOWN_HELPERS = require('./known-helpers');
77
/**
88
* plugin entrypoint
99
*/
10-
function transformPlugin(env, runtimeData, options = {}) {
10+
function transformPlugin(env, options = {}) {
1111
let { builders: b } = env.syntax;
1212

1313
let scopedParams = [];
1414
let [components, helpers] = populateInvokeables();
1515

16-
let nonThises = { scopedParams, components, helpers };
16+
let customHelpers = options.customHelpers || [];
1717

1818
let paramTracker = {
1919
enter(node) {
@@ -29,87 +29,121 @@ function transformPlugin(env, runtimeData, options = {}) {
2929
},
3030
};
3131

32-
return {
33-
Program: paramTracker,
34-
ElementNode: paramTracker,
35-
PathExpression(ast) {
36-
if (ast.data) return;
37-
if (ast.original === 'this') return;
32+
function handleParams(params) {
33+
for (let param of params) {
34+
if (param.type !== 'PathExpression') continue;
35+
handlePathExpression(param);
36+
}
37+
}
3838

39-
let token = ast.parts[0];
39+
function handleHash(hash) {
40+
for (let pair of hash.pairs) {
41+
if (pair.value.type !== 'PathExpression') continue;
42+
handlePathExpression(pair.value);
43+
}
44+
}
4045

41-
if (token !== 'this') {
42-
let isThisNeeded = doesTokenNeedThis(token, nonThises, runtimeData, options);
46+
function handlePathExpression(node) {
47+
// skip this.foo
48+
if (node.this) return;
4349

44-
if (isThisNeeded) {
45-
return b.path(`this.${ast.parts.join('.')}`);
46-
}
47-
}
48-
},
49-
};
50-
}
50+
// skip @foo
51+
if (node.data) return;
52+
53+
// skip {#foo as |bar|}}{{bar}}{{/foo}}
54+
// skip <Foo as |bar|>{{bar}}</Foo>
55+
let firstPart = node.parts[0];
56+
if (scopedParams.includes(firstPart)) return;
5157

52-
// Does the runtime data (for the c
53-
// urrent file)
54-
// contain a definition for the token?
55-
// - yes:
56-
// - in-let: false
57-
// - in-each: false
58-
// - true
59-
// - no:
60-
// - is-helper: false
61-
// - is-component: false
62-
function doesTokenNeedThis(
63-
token,
64-
{ components, helpers, scopedParams },
65-
runtimeData,
66-
{ dontAssumeThis, customHelpers }
67-
) {
68-
if (KNOWN_HELPERS.includes(token) || customHelpers.includes(token)) {
69-
return false;
58+
// add `this.` prefix
59+
Object.assign(node, b.path(`this.${node.original}`));
7060
}
7161

72-
let isBlockParam = scopedParams.includes(token);
62+
function isHelper(name) {
63+
return (
64+
KNOWN_HELPERS.includes(name) ||
65+
customHelpers.includes(name) ||
66+
Boolean(helpers.find(path => path.endsWith(name)))
67+
);
68+
}
7369

74-
if (isBlockParam) {
75-
return false;
70+
function isComponent(name) {
71+
return Boolean(components.find(path => path.endsWith(name)));
7672
}
7773

78-
let { computedProperties, getters, ownActions, ownProperties } = runtimeData;
79-
let isComputed = (computedProperties || []).includes(token);
80-
let isAction = (ownActions || []).includes(token);
81-
let isProperty = (ownProperties || []).includes(token);
82-
let isGetter = (getters || []).includes(token);
74+
let inAttrNode = false;
8375

84-
let needsThis = isComputed || isAction || isProperty || isGetter;
76+
return {
77+
Block: paramTracker,
78+
ElementNode: paramTracker,
8579

86-
if (needsThis) {
87-
return true;
88-
}
80+
AttrNode: {
81+
enter() {
82+
inAttrNode = true;
83+
},
84+
exit() {
85+
inAttrNode = false;
86+
},
87+
},
8988

90-
// This is to support the ember-holy-futuristic-template-namespacing-batman syntax
91-
// as well as support for Nested Invocations in Angle Bracket Syntax
92-
// Ref: https://github.com/rwjblue/ember-holy-futuristic-template-namespacing-batman
93-
if (token.includes('$')) {
94-
token = token.split('$')[1];
95-
}
96-
if (token.includes('::')) {
97-
token = token.replace(/::/g, '/');
98-
}
89+
MustacheStatement(node) {
90+
let { path, params, hash } = node;
9991

100-
let isComponent = components.find(path => path.endsWith(token));
92+
// {{foo BAR}}
93+
handleParams(params);
10194

102-
if (isComponent) {
103-
return false;
104-
}
95+
// {{foo bar=BAZ}}
96+
handleHash(hash);
10597

106-
let isHelper = helpers.find(path => path.endsWith(token));
98+
let hasParams = params.length !== 0;
99+
let hasHashPairs = hash.pairs.length !== 0;
107100

108-
if (isHelper) {
109-
return false;
110-
}
101+
// {{FOO}}
102+
if (path.type === 'PathExpression' && !hasParams && !hasHashPairs) {
103+
// {{FOO.bar}}
104+
if (path.parts > 1) {
105+
handlePathExpression(path);
106+
return;
107+
}
108+
109+
// skip ember-holy-futuristic-template-namespacing-batman component/helper invocations
110+
// (see https://github.com/rwjblue/ember-holy-futuristic-template-namespacing-batman)
111+
if (path.original.includes('$') || path.original.includes('::')) return;
112+
113+
// skip helpers
114+
if (isHelper(path.original)) return;
115+
116+
// skip components
117+
if (!inAttrNode && isComponent(path.original)) return;
118+
119+
handlePathExpression(path);
120+
}
121+
},
111122

112-
return dontAssumeThis ? false : true;
123+
BlockStatement(node) {
124+
// {{#foo BAR}}{{/foo}}
125+
handleParams(node.params);
126+
127+
// {{#foo bar=BAZ}}{{/foo}}
128+
handleHash(node.hash);
129+
},
130+
131+
SubExpression(node) {
132+
// (foo BAR)
133+
handleParams(node.params);
134+
135+
// (foo bar=BAZ)
136+
handleHash(node.hash);
137+
},
138+
139+
ElementModifierStatement(node) {
140+
// <div {{foo BAR}} />
141+
handleParams(node.params);
142+
143+
// <div {{foo bar=BAZ}} />
144+
handleHash(node.hash);
145+
},
146+
};
113147
}
114148

115149
function populateInvokeables() {

transforms/no-implicit-this/index.js

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
const path = require('path');
22
const fs = require('fs');
33

4-
const { parse: parseHbs, print: printHbs } = require('ember-template-recast');
5-
const { determineThisUsage } = require('./helpers/determine-this-usage');
4+
const recast = require('ember-template-recast');
5+
const transformPlugin = require('./helpers/plugin');
66
const { getOptions: getCLIOptions } = require('codemod-cli');
7-
const DEFAULT_OPTIONS = {
8-
dontAssumeThis: false,
9-
};
7+
const DEFAULT_OPTIONS = {};
108

119
/**
1210
* Accepts the config path for custom helpers and returns the array of helpers
@@ -35,7 +33,6 @@ function _getCustomHelpersFromConfig(configPath) {
3533
function getOptions() {
3634
let cliOptions = getCLIOptions();
3735
let options = {
38-
dontAssumeThis: cliOptions.dontAssumeThis,
3936
customHelpers: _getCustomHelpersFromConfig(cliOptions.config),
4037
};
4138
return options;
@@ -50,13 +47,5 @@ module.exports = function transformer(file /*, api */) {
5047
return;
5148
}
5249

53-
let root = parseHbs(file.source);
54-
55-
let replaced = determineThisUsage(root, file, options);
56-
57-
if (replaced) {
58-
return printHbs(replaced);
59-
}
60-
61-
return file.source;
50+
return recast.transform(file.source, env => transformPlugin(env, options)).code;
6251
};

0 commit comments

Comments
 (0)