Repository: ember-codemods/ember-angle-brackets-codemod
Pull request #241 (2020-02-04 22:35:31)

Add config to enable transform on valueless attrs
The current codemod skips on transforming nodes with valueless attributes. This prevents a full migration for those who use libraries like [ember-test-selectors](https://github.com/simplabs/ember-test-selectors).

This PR adds the config `includeValuelessDataAttributes` so the codemod includes data attributes that are valueless.

https://github.com/ember-codemods/ember-angle-brackets-codemod/issues/237


Patch:
diff --git a/transforms/angle-brackets/index.js b/transforms/angle-brackets/index.js
index fab4609..9003819 100755
--- a/transforms/angle-brackets/index.js
+++ b/transforms/angle-brackets/index.js
@@ -25,6 +25,7 @@ function getOptions() {
       options.skipFilesThatMatchRegex = new RegExp(config.skipFilesThatMatchRegex);
     }
 
+    options.includeValuelessDataTestAttributes = !!config.includeValuelessDataTestAttributes;
     options.skipBuiltInComponents = !!config.skipBuiltInComponents;
   }
 
diff --git a/transforms/angle-brackets/transform.js b/transforms/angle-brackets/transform.js
index 56d7132..4e75b2d 100755
--- a/transforms/angle-brackets/transform.js
+++ b/transforms/angle-brackets/transform.js
@@ -280,6 +280,21 @@ function hasValuelessDataParams(params) {
   return getDataAttributesFromParams(params).length > 0;
 }
 
+/**
+ *
+ * data-* attributes are generally omitted,
+ * but this config allows including nodes with data-test-* attributes.
+ */
+function shouldSkipDataTestParams(params, includeValuelessDataTestAttributes) {
+  if (includeValuelessDataTestAttributes) {
+    const dataAttrs = getDataAttributesFromParams(params);
+    // This is true for nodes with data-* attributes too,
+    // as long as there is one with data-test-* attribute.
+    return !dataAttrs.some(attr => attr.original.startsWith('data-test-'));
+  }
+  return true;
+}
+
 function transformNodeAttributes(tagName, node, config) {
   let attributes = transformAttrs(tagName, node.hash.pairs, config);
   return node.params.concat(attributes);
@@ -338,7 +353,10 @@ function nodeHasPositionalParameters(node) {
 }
 
 function transformNode(node, fileInfo, config) {
-  if (hasValuelessDataParams(node.params)) {
+  if (
+    hasValuelessDataParams(node.params) &&
+    shouldSkipDataTestParams(node.params, config.includeValuelessDataTestAttributes)
+  ) {
     return;
   }
   let selfClosing = node.type !== 'BlockStatement';


Test patch:
diff --git a/transforms/angle-brackets/transform.test.js b/transforms/angle-brackets/transform.test.js
index 16b226c..67827a1 100644
--- a/transforms/angle-brackets/transform.test.js
+++ b/transforms/angle-brackets/transform.test.js
@@ -144,6 +144,105 @@ test('data-attributes', () => {
   `);
 });
 
+test('data-test-attributes', () => {
+  let options = {
+    includeValuelessDataTestAttributes: true,
+  };
+  let input = `
+    {{x-foo data-foo=true}}
+    {{x-foo data-test-selector=true}}
+    {{x-foo data-test-selector=post.id}}
+    {{x-foo label="hi" data-test-selector=true}}
+    {{x-foo data-test-foo }}
+    {{#x-foo data-foo=true}}
+      block
+    {{/x-foo}}
+    {{#x-foo data-test-selector=true}}
+      block
+    {{/x-foo}}
+    {{#x-foo data-test-selector=post.id}}
+      block
+    {{/x-foo}}
+    {{#common/accordion-component data-test-accordion as |accordion|}}
+      block
+    {{/common/accordion-component}}
+    {{#link-to data-test-foo "posts"}}
+      Recent Posts
+    {{/link-to}}
+    {{#link-to data-test-foo this.dynamicPath (query-params direction="desc" showArchived=false)}}
+      Recent Posts
+    {{/link-to}}
+    {{#link-to data-test-foo data-foo this.dynamicPath (query-params direction="desc" showArchived=false)}}
+      Recent Posts
+    {{/link-to}}
+
+    {{x-foo
+      data-foo
+      name="Sophie"
+    }}
+    {{#x-foo data-foo}}
+      block
+    {{/x-foo}}
+    {{#common/accordion-component data-accordion as |accordion|}}
+      block
+    {{/common/accordion-component}}
+    {{#link-to data-foo "posts"}}
+      Recent Posts
+    {{/link-to}}
+    {{#link-to data-foo this.dynamicPath (query-params direction="desc" showArchived=false)}}
+      Recent Posts
+    {{/link-to}}
+  `;
+
+  expect(runTest('data-test-attributes.hbs', input, options)).toMatchInlineSnapshot(`
+    "
+        <XFoo @data-foo={{true}} />
+        <XFoo @data-test-selector={{true}} />
+        <XFoo @data-test-selector={{post.id}} />
+        <XFoo @label=\\"hi\\" @data-test-selector={{true}} />
+        <XFoo data-test-foo />
+        <XFoo @data-foo={{true}}>
+          block
+        </XFoo>
+        <XFoo @data-test-selector={{true}}>
+          block
+        </XFoo>
+        <XFoo @data-test-selector={{post.id}}>
+          block
+        </XFoo>
+        <Common::AccordionComponent data-test-accordion as |accordion|>
+          block
+        </Common::AccordionComponent>
+        <LinkTo @route=\\"posts\\" data-test-foo>
+          Recent Posts
+        </LinkTo>
+        <LinkTo @route={{this.dynamicPath}} @query={{hash direction=\\"desc\\" showArchived=false}} data-test-foo>
+          Recent Posts
+        </LinkTo>
+        <LinkTo @route={{this.dynamicPath}} @query={{hash direction=\\"desc\\" showArchived=false}} data-test-foo data-foo>
+          Recent Posts
+        </LinkTo>
+
+        {{x-foo
+          data-foo
+          name=\\"Sophie\\"
+        }}
+        {{#x-foo data-foo}}
+          block
+        {{/x-foo}}
+        {{#common/accordion-component data-accordion as |accordion|}}
+          block
+        {{/common/accordion-component}}
+        {{#link-to data-foo \\"posts\\"}}
+          Recent Posts
+        {{/link-to}}
+        {{#link-to data-foo this.dynamicPath (query-params direction=\\"desc\\" showArchived=false)}}
+          Recent Posts
+        {{/link-to}}
+      "
+  `);
+});
+
 test('deeply-nested-sub', () => {
   let input = `
     {{#some-component class=(concat foo (some-helper ted (some-dude bar (a b c)))) }}