1.0.0 • Published 6 years ago

babel-plugin-jsx-map-class-props v1.0.0

Weekly downloads
3
License
MIT
Repository
github
Last release
6 years ago

babel-plugin-jsx-map-class-props

Travis build status NPM version

Merges class names from any attribute with className or other compatible attributes.

Installation

When babel-plugin-jsx-map-class-props cannot resolve attribute value at compile time, it imports helper functions (read Runtime attribute value formatting and merging). Therefore, you must install babel-plugin-jsx-map-class-props as a direct dependency of the project.

npm install babel-plugin-jsx-map-class-props --save

Why?

The idea was born when I tried to identify the components in our E2E tests. First of all I tried to find a property that most components pass to the DOM. I read about testId, data-test-id and some others, but our components library isn't aware of any of them. Then I thought about className. It is supported by most components. But we use CSS-modules, which decorates the class names, and so they cannot be simply recognized by e2e test. So we have to use additional classes for tests and combine them with the existing ones, and that makes a mess. Also we need to strip them in production. Finally, the combination of both directions gave me a new idea: i could put an identifier into a different property and merge it with the className at compile time. It also solved the issue of stripping these properties for production.

Configuration

Configure the options for the plugin within your .babelrc as follows:

{
  "plugins": [
    ["jsx-map-class-props", {
      "option": "value"
    }]
  ]
}

Options

NameTypeDescriptionDefault
context?stringScoped names will be calculated relative to this path.process.cwd()
format?GenerateScopedNameConfigurationTypeGlobal pattern for class names formatting. Can be overridden by mapping option. Refer to Generating scoped names and Interpolate Name.none
clean?booleanRemoves all matching props. This option can be used for removing debug/test classNames from production build. Can be overridden by mapping option. Ignored if falsefalse
mappingsAttributeMappingType []Array of attribute mapping options[]

Attribute Mapping Options

NameTypeDescriptionDefault
format?GenerateScopedNameConfigurationTypePattern for class names formatting. Can override global option. Set null to prevent formatting. Refer to Generating scoped names and Interpolate Name.none
clean?booleanRemoves all matching props. This option can be used for removing debug/test classNames from production build. Can override global option by being set to false explicitly.none
sourceName?stringName of an attribute to be matched.
sourceMask?stringRegExp pattern string to find matching attributes.
prefix?stringString to find matching attributes by prefix. Target attribute's name will be calculated by removing prefix, if other options not specified.
targetName?stringName of the attribute the value will be merged with. This option has the highest priority for target calculation. Can be used with any source option.
targetMask?string, ?functionSecond parameter of String.replace. Can only be used with sourceMask, which will be used as a first parameter. Allows to calculate attribute name to merge value with.

Missing a configuration? Raise an issue.

Allowed combinations of source-target options

(in priority order)

SourceTargetStrategy Description
sourceNametargetNamematch attribute by exact sourceName, merge with targetName attribute
sourceMasktargetNamematch attribute by sourceMask RegExp, merge with targetName attribute
sourceMasktargetMaskmatch attribute by sourceMask RegExp, calculate target using foundName.replace(sourceMask, targetMask), merge with target attribute
prefixtargetNamematch attribute by prefix, merge with targetName attribute
prefixmatch attribute by prefix, get target by stripping prefix, merge with target attribute

Notes:

  • only one combination per mapping is allowed
  • other options will be ignored (according to priority)
  • if attribute is matched by a mapping, other mappings are ignored (in other words, an attribute can have only one target)

How does it work?

  1. Iterates through all JSX element declarations.
  2. Finds matched source attributes according to the mapping options.
    • If clean option is true, removes the source attribute that was found from the element and continues to the next iteration.
  3. Calculates target attribute names according to the mapping options.
  4. Formats source attribute value according to the mapping options:
    • If value is a string literal, generates a string literal value.
    • If value is a jSXExpressionContainer and format option is valid, uses a helper function (getClassName) to format value at runtime.
  5. Merges source attribute value with the target attribute:
    • If target attribute doesn't exist, moves formatted source attribute to the target.
    • If source and target values are a string literal, generates a string literal value.
    • If source or target value is a jSXExpressionContainer, uses a helper function (joinClassNames) to join values at runtime.
  6. Removes the source attribute from the element.

Runtime attribute value formatting and merging

When the value of a source attribute cannot be determined at compile time, babel-plugin-jsx-map-class-props uses getClassName helper function to format the value at runtime.

Input:

<div test-className={Math.random() > .5 ? 'a' : 'b'} />;

Output:

import _getClassName from 'babel-plugin-jsx-map-class-props/dist/browser/getClassName';

<div className={
  _getClassName(Math.random() > .5 ? 'a' : 'b', 'e2e-|')
} />;

When the value of a source or a target attribute cannot be determined at compile time, babel-plugin-jsx-map-class-props uses joinClassNames helper function to join the values at runtime.

Input:

<div test-className='my-component' className={styles.myComponent} />;

Output:

import _joinClassNames from 'babel-plugin-jsx-map-class-props/dist/browser/joinClassNames';

<div className={
  _joinClassNames('e2e-my-component', styles.myComponent)
} />;

Have a question or want to suggest an improvement?