jss
jss copied to clipboard
Classnaming missing component name
Expected behavior: Wrong or Missing classnames if I follow the basic example from docu. The basic example output (https://cssinjs.org/react-jss/?v=v10.3.0#basic ) should be
<button class="Button-myButton-1-25">
<span class="Button-myLabel-1-26">
Submit
</span>
</button>
Describe the bug: if using the basic example from the name of the component/file isn´t included in classnames as described here https://cssinjs.org/react-jss/?v=v10.3.0#basic
output is:
<button class="myButton-0-2-1">
<span class="myLabel-0-2-2">sdsds</span>
</button>
Codesandbox link: https://codesandbox.io/s/condescending-bush-lzdjv?fontsize=14&hidenavigation=1&theme=dark
Versions (please complete the following information):
- jss: 10.4.0
- Browser [e.g. chrome, safari]: chrome
- OS [e.g. Windows, macOS]: mac
Solved by
const useStyles = createUseStyles({
myButton: {
color: 'green',
margin: {
// jss-plugin-expand gives more readable syntax
top: 5, // jss-plugin-default-unit makes this 5px
right: 0,
bottom: 0,
left: '1rem'
},
'& span': {
// jss-plugin-nested applies this to a child span
fontWeight: 'bold' // jss-plugin-camel-case turns this into 'font-weight'
}
},
myLabel: {
fontStyle: 'italic'
}
}, {name: "writing-documentation"})
Why not adding the displayName by default, as like within withStyles/InjectStyleSheets HOCs And maybe some documentation for the Hooks?
not possible by default, there is no name to use like it is the case with withStyles
They did not know it was impossible so they did it https://styled-components.com/docs/tooling
Impossible without a babel plugin, if you want to write it, let me know.
module.exports = transform;
var pathMod = require('path')
function transform (babel) {
return {
visitor: {
ClassDeclaration: function (path, state) {
if (classHasRenderMethod(path)) {
setDisplayNameAfter(path, path.node.id, babel.types)
}
},
FunctionDeclaration: function (path, state) {
if (doesReturnJSX(path.node.body) || (path.node.id && path.node.id.name &&
isKnownComponent(path.node.id.name, state.opts.knownComponents))) {
var displayName
if (path.parentPath.node.type === 'ExportDefaultDeclaration') {
if (path.node.id == null) {
// An anonymous function declaration in export default declaration.
// Transform `export default function () { ... }`
// to `var _uid1 = function () { .. }; export default __uid;`
// then add displayName to _uid1
var extension = pathMod.extname(state.file.opts.filename)
var name = pathMod.basename(state.file.opts.filename, extension)
var id = path.scope.generateUidIdentifier("uid");
path.node.id = id
displayName = 'JSS_'+name
}
setDisplayNameAfter(path, path.node.id, babel.types, displayName)
}else if(path.parentPath.node.type === 'Program' || path.parentPath.node.type == 'ExportNamedDeclaration') {
setDisplayNameAfter(path, path.node.id, babel.types, displayName)
}
}
},
FunctionExpression: function (path, state) {
if(shouldSetDisplayNameForFuncExpr(path, state.opts.knownComponents)) {
var id = findCandidateNameForExpression(path)
if (id) {
setDisplayNameAfter(path, id, babel.types)
}
}
},
ArrowFunctionExpression: function (path, state) {
if(shouldSetDisplayNameForFuncExpr(path, state.opts.knownComponents)) {
var id = findCandidateNameForExpression(path)
if (id) {
setDisplayNameAfter(path, id, babel.types)
}
}
}
}
}
}
function isKnownComponent(name, knownComponents) {
return (name && knownComponents && knownComponents.indexOf(name) > -1)
}
function componentNameFromFilename(filename) {
var extension = pathMod.extname(filename);
var name = pathMod.basename(filename, extension)
return name
}
function shouldSetDisplayNameForFuncExpr(path, knownComponents) {
// Parent must be either 'AssignmentExpression' or 'VariableDeclarator' or 'CallExpression' with a parent of 'VariableDeclarator'
var id
if (path.parentPath.node.type === 'AssignmentExpression' &&
path.parentPath.node.left.type !== 'MemberExpression' && // skip static members
path.parentPath.parentPath.node.type == 'ExpressionStatement' &&
path.parentPath.parentPath.parentPath.node.type == 'Program') {
id = path.parentPath.node.left
}else{
// if parent is a call expression, we have something like (function () { .. })()
// move up, past the call expression and run the rest of the checks as usual
if(path.parentPath.node.type === 'CallExpression') {
path = path.parentPath
}
if(path.parentPath.node.type === 'VariableDeclarator') {
if (path.parentPath.parentPath.parentPath.node.type === 'ExportNamedDeclaration' ||
path.parentPath.parentPath.parentPath.node.type === 'Program') {
id = path.parentPath.node.id
}
}
}
if (id) {
if (id.name && isKnownComponent(id.name, knownComponents)) {
return true
}
return doesReturnJSX(path.node.body)
}
return false
}
function classHasRenderMethod(path) {
if(!path.node.body) {
return false
}
var members = path.node.body.body
for(var i = 0; i < members.length; i++) {
if (members[i].type == 'ClassMethod' && members[i].key.name == 'render') {
return true
}
}
return false
}
// https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-react-display-name/src/index.js#L62-L77
// crawl up the ancestry looking for possible candidates for displayName inference
function findCandidateNameForExpression(path) {
var id
path.find(function (path) {
if (path.isAssignmentExpression()) {
id = path.node.left;
// } else if (path.isObjectProperty()) {
// id = path.node.key;
} else if (path.isVariableDeclarator()) {
id = path.node.id;
} else if (path.isStatement()) {
// we've hit a statement, we should stop crawling up
return true;
}
// we've got an id! no need to continue
if (id) return true;
});
return id
}
function doesReturnJSX (body) {
if (!body) return false
if (body.type === 'JSXElement') {
return true
}
var block = body.body
if (block && block.length) {
var lastBlock = block.slice(0).pop()
if (lastBlock.type === 'ReturnStatement') {
return lastBlock.argument !== null && lastBlock.argument.type === 'JSXElement'
}
}
return false
}
function setDisplayNameAfter(path, nameNodeId, t, displayName) {
if (!displayName) {
displayName = 'JSS_'+nameNodeId.name
}
var blockLevelStmnt
path.find(function (path) {
if (path.parentPath.isBlock()) {
blockLevelStmnt = path
return true
}
})
if (blockLevelStmnt) {
var trailingComments = blockLevelStmnt.node.trailingComments
delete blockLevelStmnt.node.trailingComments
var setDisplayNameStmn = t.expressionStatement(t.assignmentExpression(
'=',
t.memberExpression(nameNodeId, t.identifier('displayName')),
t.stringLiteral(displayName)
))
blockLevelStmnt.insertAfter(setDisplayNameStmn)
}
}
We would need a package, tests and docs to make it work for everyone
Reads like https://pasteboard.co/Jqif3TEc.png
Currently the displayname is hardcoded prefixed with 'JSS_' Test it from here https://github.com/Chartieer/babel-plugin-react-jss
Don´t forget to add the impossible default value. ;-) There is also a Pull request for the missing docu
Hope it helps