Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save karlbecker/50b98a1f511cbd02cd78bff430087c85 to your computer and use it in GitHub Desktop.

Select an option

Save karlbecker/50b98a1f511cbd02cd78bff430087c85 to your computer and use it in GitHub Desktop.
Convert ember-cli-page-object legacy collection approach involving itemScope to newer selector-first API
// jscodeshift codemod to migrate ember-cli-page-object legacy collection({ ... })
// to the newer selector-first API: collection('selector', { ... })
//
// Usage:
// npx jscodeshift -t scripts/collection-api-codemod.js tests/pages
//
// The transform handles the common pattern that was deprecated:
//
// items: collection({
// itemScope: '.item',
// item: {
// name: text('.name')
// },
// at: 1,
// // any other options / helpers
// });
//
// Becomes:
// items: collection('.item', {
// name: text('.name'),
// at: 1,
// // …
// });
//
// • "itemScope" is moved into the first positional parameter.
// • The object passed to "item" is hoisted into the definition object.
// • All other keys except "itemScope" and "item" are preserved inside
// the definition object.
module.exports = function transformer(fileInfo, api) {
const j = api.jscodeshift;
const root = j(fileInfo.source);
// helper to detect collection(...) call
function isCollectionCall(node) {
return (
node &&
node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
node.callee.name === 'collection'
);
}
root
.find(j.CallExpression, {
callee: { type: 'Identifier', name: 'collection' },
arguments: { length: 1 }
})
.forEach(path => {
const arg = path.node.arguments[0];
if (arg.type !== 'ObjectExpression') return; // only legacy style
// extract itemScope
const itemScopeProp = arg.properties.find(
p => p.key && ((p.key.name === 'itemScope') || (p.key.value === 'itemScope'))
);
if (!itemScopeProp) return; // nothing to do
const selector = itemScopeProp.value; // AST node representing string literal or expression
// extract item definition
const itemProp = arg.properties.find(
p => p.key && ((p.key.name === 'item') || (p.key.value === 'item'))
);
// build new definition object
const newProps = [];
// include properties from itemProp first (if exists)
if (itemProp && itemProp.value.type === 'ObjectExpression') {
newProps.push(...itemProp.value.properties);
}
// include remaining original top-level props except itemScope and item
arg.properties.forEach(p => {
const keyName = p.key && (p.key.name || p.key.value);
if (keyName === 'itemScope' || keyName === 'item') return;
newProps.push(p);
});
const newDefinitionObj = j.objectExpression(newProps);
// replace arguments: collection(selector, newDefinitionObj)
path.node.arguments = [selector, newDefinitionObj];
});
return root.toSource({ quote: 'single' });
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment