Skip to content

Commit 21c9c65

Browse files
committed
Move transform() function to a dedicated file
1 parent 997c94d commit 21c9c65

2 files changed

Lines changed: 305 additions & 300 deletions

File tree

lib/index.js

Lines changed: 1 addition & 300 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,8 @@
1-
const fs = require('fs');
2-
const chalk = require('chalk');
31
const pkgDir = require('pkg-dir');
42
const globby = require('globby');
5-
const stringUtils = require('ember-cli-string-utils');
6-
const j = require('jscodeshift').withParser('ts');
73
const debug = require('debug')('tagless-ember-components-codemod');
84

9-
const EVENT_HANDLER_METHODS = [
10-
// Touch events
11-
'touchStart',
12-
'touchMove',
13-
'touchEnd',
14-
'touchCancel',
15-
16-
// Keyboard events
17-
'keyDown',
18-
'keyUp',
19-
'keyPress',
20-
21-
// Mouse events
22-
'mouseDown',
23-
'mouseUp',
24-
'contextMenu',
25-
'click',
26-
'doubleClick',
27-
'focusIn',
28-
'focusOut',
29-
30-
// Form events
31-
'submit',
32-
'change',
33-
'focusIn',
34-
'focusOut',
35-
'input',
36-
37-
// Drag and drop events
38-
'dragStart',
39-
'drag',
40-
'dragEnter',
41-
'dragLeave',
42-
'dragOver',
43-
'dragEnd',
44-
'drop',
45-
];
5+
const { transform } = require('./transform');
466

477
async function run() {
488
let path = await pkgDir();
@@ -67,263 +27,4 @@ async function runForPath(path, options = {}) {
6727
debug('runForPath() finished');
6828
}
6929

70-
function transform(componentPath) {
71-
let dimPath = chalk.dim(componentPath);
72-
73-
let source = fs.readFileSync(componentPath, 'utf8');
74-
75-
let root = j(source);
76-
77-
// find `export default Component.extend({ ... });` AST node
78-
let exportDefaultDeclarations = root.find(j.ExportDefaultDeclaration, {
79-
declaration: {
80-
type: 'CallExpression',
81-
callee: {
82-
type: 'MemberExpression',
83-
object: { name: 'Component' },
84-
property: { name: 'extend' },
85-
},
86-
},
87-
});
88-
89-
if (exportDefaultDeclarations.length !== 1) {
90-
console.log(
91-
chalk.yellow(`${dimPath}: Could not find \`export default Component.extend({ ... });\``)
92-
);
93-
return;
94-
}
95-
96-
let exportDefaultDeclaration = exportDefaultDeclarations.get();
97-
98-
// find first `{ ... }` inside `Component.extend()` arguments
99-
let extendObjectArgs = exportDefaultDeclaration
100-
.get('declaration', 'arguments')
101-
.filter(path => path.value.type === 'ObjectExpression');
102-
103-
let objectArg = extendObjectArgs[0];
104-
if (!objectArg) {
105-
console.log(
106-
chalk.yellow(
107-
`${dimPath}: Could not find object argument in \`export default Component.extend({ ... });\``
108-
)
109-
);
110-
return;
111-
}
112-
113-
// find `tagName` property if it exists
114-
let properties = objectArg.get('properties');
115-
116-
let tagName = findTagName(properties);
117-
if (tagName.constructor.name === 'NodePath') {
118-
console.log(chalk.yellow(`${dimPath}: Unexpected \`tagName\` value: ${j(tagName).toSource()}`));
119-
return;
120-
}
121-
122-
// skip tagless components (silent)
123-
if (tagName === '') {
124-
debug(`${componentPath}: tagName: %o -> skip`, tagName);
125-
return;
126-
}
127-
128-
debug(`${componentPath}: tagName: %o`, tagName);
129-
130-
// skip components that use `this.element`
131-
let thisElementPaths = j(objectArg).find(j.MemberExpression, {
132-
object: { type: 'ThisExpression' },
133-
property: { name: 'element' },
134-
});
135-
if (thisElementPaths.length !== 0) {
136-
console.log(
137-
chalk.yellow(`${dimPath}: Using \`this.element\` is not supported in tagless components`)
138-
);
139-
return;
140-
}
141-
142-
// skip components that use `click()` etc.
143-
for (let methodName of EVENT_HANDLER_METHODS) {
144-
let handlerMethod = properties.filter(path => isMethod(path, methodName))[0];
145-
if (handlerMethod) {
146-
console.log(
147-
chalk.yellow(`${dimPath}: Using \`${methodName}()\` is not supported in tagless components`)
148-
);
149-
return;
150-
}
151-
}
152-
153-
// analyze `elementId`, `attributeBindings`, `classNames` and `classNameBindings`
154-
let elementId = findElementId(properties);
155-
if (elementId !== null && elementId.constructor.name === 'NodePath') {
156-
console.log(
157-
chalk.yellow(`${dimPath}: Unexpected \`elementId\` value: ${j(elementId).toSource()}`)
158-
);
159-
return;
160-
}
161-
debug(`${componentPath}: elementId: %o`, elementId);
162-
163-
let attributeBindings = findAttributeBindings(properties);
164-
if (attributeBindings.constructor.name === 'NodePath') {
165-
console.log(
166-
chalk.yellow(
167-
`${dimPath}: Unexpected \`attributeBindings\` value: ${j(attributeBindings).toSource()}`
168-
)
169-
);
170-
return;
171-
}
172-
debug(`${componentPath}: attributeBindings: %o`, attributeBindings);
173-
174-
let classNames = findClassNames(properties);
175-
if (classNames.constructor.name === 'NodePath') {
176-
console.log(
177-
chalk.yellow(`${dimPath}: Unexpected \`classNames\` value: ${j(classNames).toSource()}`)
178-
);
179-
return;
180-
}
181-
debug(`${componentPath}: classNames: %o`, classNames);
182-
183-
let classNameBindings = findClassNameBindings(properties);
184-
if (classNameBindings.constructor.name === 'NodePath') {
185-
console.log(
186-
chalk.yellow(
187-
`${dimPath}: Unexpected \`classNameBindings\` value: ${j(classNameBindings).toSource()}`
188-
)
189-
);
190-
return;
191-
}
192-
debug(`${componentPath}: classNameBindings: %o`, classNameBindings);
193-
194-
// TODO skip on error (warn incl. please report)
195-
196-
// TODO find corresponding template files
197-
// TODO skip if not found (warn)
198-
199-
// TODO set `tagName: ''` and remove `attributeBindings`, `classNames`, ...
200-
// TODO wrap existing template with root element
201-
}
202-
203-
function isProperty(path, name) {
204-
let node = path.value;
205-
return node.type === 'ObjectProperty' && node.key.type === 'Identifier' && node.key.name === name;
206-
}
207-
208-
function isMethod(path, name) {
209-
let node = path.value;
210-
return node.type === 'ObjectMethod' && node.key.type === 'Identifier' && node.key.name === name;
211-
}
212-
213-
function findTagName(properties) {
214-
let tagNameProperty = properties.filter(path => isProperty(path, 'tagName'))[0];
215-
if (!tagNameProperty) {
216-
return 'div';
217-
}
218-
219-
let tagNamePath = tagNameProperty.get('value');
220-
if (tagNamePath.value.type === 'StringLiteral') {
221-
return tagNamePath.value.value;
222-
}
223-
224-
return tagNamePath;
225-
}
226-
227-
function findElementId(properties) {
228-
let elementIdProperty = properties.filter(path => isProperty(path, 'elementId'))[0];
229-
if (!elementIdProperty) {
230-
return null;
231-
}
232-
233-
let elementIdPath = elementIdProperty.get('value');
234-
if (elementIdPath.value.type === 'StringLiteral') {
235-
return elementIdPath.value.value;
236-
}
237-
238-
return elementIdPath;
239-
}
240-
241-
function findAttributeBindings(properties) {
242-
let attrBindingsProperty = properties.filter(path => isProperty(path, 'attributeBindings'))[0];
243-
if (!attrBindingsProperty) {
244-
return new Map();
245-
}
246-
247-
let arrayPath = attrBindingsProperty.get('value');
248-
if (arrayPath.value.type !== 'ArrayExpression') {
249-
return arrayPath;
250-
}
251-
252-
let elements = arrayPath.get('elements').value;
253-
254-
let attrBindings = new Map();
255-
for (let element of elements) {
256-
if (element.type !== 'StringLiteral') {
257-
return arrayPath;
258-
}
259-
260-
let [from, to] = element.value.split(':');
261-
attrBindings.set(from, to || from);
262-
}
263-
264-
return attrBindings;
265-
}
266-
267-
function findClassNames(properties) {
268-
let classNamesProperty = properties.filter(path => isProperty(path, 'classNames'))[0];
269-
if (!classNamesProperty) {
270-
return [];
271-
}
272-
273-
let arrayPath = classNamesProperty.get('value');
274-
if (arrayPath.value.type !== 'ArrayExpression') {
275-
return arrayPath;
276-
}
277-
278-
let elements = arrayPath.get('elements').value;
279-
280-
let classNames = [];
281-
for (let element of elements) {
282-
if (element.type !== 'StringLiteral') {
283-
return arrayPath;
284-
}
285-
286-
classNames.push(element.value);
287-
}
288-
289-
return classNames;
290-
}
291-
292-
function findClassNameBindings(properties) {
293-
let classNameBindingsProperty = properties.filter(path =>
294-
isProperty(path, 'classNameBindings')
295-
)[0];
296-
if (!classNameBindingsProperty) {
297-
return new Map();
298-
}
299-
300-
let arrayPath = classNameBindingsProperty.get('value');
301-
if (arrayPath.value.type !== 'ArrayExpression') {
302-
return arrayPath;
303-
}
304-
305-
let elements = arrayPath.get('elements').value;
306-
307-
let classNameBindings = new Map();
308-
for (let element of elements) {
309-
if (element.type !== 'StringLiteral') {
310-
return arrayPath;
311-
}
312-
313-
let parts = element.value.split(':');
314-
315-
if (parts.length === 1) {
316-
classNameBindings.set(parts[0], [stringUtils.dasherize(parts[0]), null]);
317-
} else if (parts.length === 2) {
318-
classNameBindings.set(parts[0], [parts[1], null]);
319-
} else if (parts.length === 3) {
320-
classNameBindings.set(parts[0], [parts[1] || null, parts[2]]);
321-
} else {
322-
return arrayPath;
323-
}
324-
}
325-
326-
return classNameBindings;
327-
}
328-
32930
module.exports = { run };

0 commit comments

Comments
 (0)