// @flow import nodePath from 'path' type LabelFormatOptions = { name: string, path: string } const invalidClassNameCharacters = /[!"#$%&'()*+,./:;<=>?@[\]^`|}~{]/g const sanitizeLabelPart = (labelPart: string) => labelPart.trim().replace(invalidClassNameCharacters, '-') function getLabel( identifierName?: string, labelFormat?: string | (LabelFormatOptions => string), filename: string ) { if (!identifierName) return null const sanitizedName = sanitizeLabelPart(identifierName) if (!labelFormat) { return sanitizedName } if (typeof labelFormat === 'function') { return labelFormat({ name: sanitizedName, path: filename }) } const parsedPath = nodePath.parse(filename) let localDirname = nodePath.basename(parsedPath.dir) let localFilename = parsedPath.name if (localFilename === 'index') { localFilename = localDirname } return labelFormat .replace(/\[local\]/gi, sanitizedName) .replace(/\[filename\]/gi, sanitizeLabelPart(localFilename)) .replace(/\[dirname\]/gi, sanitizeLabelPart(localDirname)) } export function getLabelFromPath(path: *, state: *, t: *) { return getLabel( getIdentifierName(path, t), state.opts.labelFormat, state.file.opts.filename ) } const getObjPropertyLikeName = (path, t) => { if ( (!t.isObjectProperty(path) && !t.isObjectMethod(path)) || path.node.computed ) { return null } if (t.isIdentifier(path.node.key)) { return path.node.key.name } if (t.isStringLiteral(path.node.key)) { return path.node.key.value.replace(/\s+/g, '-') } return null } function getDeclaratorName(path, t) { // $FlowFixMe const parent = path.findParent( p => p.isVariableDeclarator() || p.isAssignmentExpression() || p.isFunctionDeclaration() || p.isFunctionExpression() || p.isArrowFunctionExpression() || p.isObjectProperty() || p.isObjectMethod() ) if (!parent) { return '' } // we probably have a css call assigned to a variable // so we'll just return the variable name if (parent.isVariableDeclarator()) { if (t.isIdentifier(parent.node.id)) { return parent.node.id.name } return '' } if (parent.isAssignmentExpression()) { let { left } = parent.node if (t.isIdentifier(left)) { return left.name } if (t.isMemberExpression(left)) { let memberExpression = left let name = '' while (true) { if (!t.isIdentifier(memberExpression.property)) { return '' } name = `${memberExpression.property.name}${name ? `-${name}` : ''}` if (t.isIdentifier(memberExpression.object)) { return `${memberExpression.object.name}-${name}` } if (!t.isMemberExpression(memberExpression.object)) { return '' } memberExpression = memberExpression.object } } return '' } // we probably have an inline css prop usage if (parent.isFunctionDeclaration()) { return parent.node.id.name || '' } if (parent.isFunctionExpression()) { if (parent.node.id) { return parent.node.id.name || '' } return getDeclaratorName(parent, t) } if (parent.isArrowFunctionExpression()) { return getDeclaratorName(parent, t) } // we could also have an object property const objPropertyLikeName = getObjPropertyLikeName(parent, t) if (objPropertyLikeName) { return objPropertyLikeName } let variableDeclarator = parent.findParent(p => p.isVariableDeclarator()) if (!variableDeclarator || !variableDeclarator.get('id').isIdentifier()) { return '' } return variableDeclarator.node.id.name } function getIdentifierName(path: *, t: *) { let objPropertyLikeName = getObjPropertyLikeName(path.parentPath, t) if (objPropertyLikeName) { return objPropertyLikeName } // $FlowFixMe let classOrClassPropertyParent = path.findParent( p => t.isClassProperty(p) || t.isClass(p) ) if (classOrClassPropertyParent) { if ( t.isClassProperty(classOrClassPropertyParent) && classOrClassPropertyParent.node.computed === false && t.isIdentifier(classOrClassPropertyParent.node.key) ) { return classOrClassPropertyParent.node.key.name } if ( t.isClass(classOrClassPropertyParent) && classOrClassPropertyParent.node.id ) { return t.isIdentifier(classOrClassPropertyParent.node.id) ? classOrClassPropertyParent.node.id.name : '' } } let declaratorName = getDeclaratorName(path, t) // if the name starts with _ it was probably generated by babel so we should ignore it if (declaratorName.charAt(0) === '_') { return '' } return declaratorName }