ember-codemod-remove-global-styles
Version:
Codemod to localize global styles
184 lines (183 loc) • 6.99 kB
JavaScript
import { AST } from '@codemod-utils/ast-template';
export class Processor {
args;
constructor(args) {
this.args = args;
}
getLocalClass(className) {
return `${this.getStyles()}.${className}`;
}
getStyles() {
return this.args.isHbs ? 'this.styles' : 'styles';
}
isLocal(className) {
return this.args.classToStyles.has(className);
}
processConcatStatement(nodeValue) {
const parts = nodeValue.parts
.map((part) => {
switch (part.type) {
case 'MustacheStatement': {
return [
this.processMustacheStatement(part),
AST.builders.text(' '),
];
}
case 'TextNode': {
return [this.processTextNode(part), AST.builders.text(' ')];
}
default: {
return part;
}
}
})
.flat();
return AST.builders.concat(parts);
}
processMustacheStatement(nodeValue) {
switch (nodeValue.path.type) {
case 'PathExpression': {
switch (nodeValue.path.original) {
case 'if':
case 'unless': {
if (nodeValue.params[1]?.type === 'StringLiteral') {
// @ts-expect-error: Incorrect type
nodeValue.params[1] = this.processStringLiteral(nodeValue.params[1]);
}
if (nodeValue.params[2]?.type === 'StringLiteral') {
// @ts-expect-error: Incorrect type
nodeValue.params[2] = this.processStringLiteral(nodeValue.params[2]);
}
break;
}
}
break;
}
case 'StringLiteral': {
// @ts-expect-error: Incorrect type
nodeValue.path = this.processStringLiteral(nodeValue.path);
break;
}
}
return nodeValue;
}
processStringLiteral(nodeValue) {
const classNames = nodeValue.original.split(/\s+/).filter(Boolean);
if (classNames.length === 0) {
return AST.builders.text('');
}
if (classNames.length === 1) {
const className = classNames[0];
return this.isLocal(className)
? AST.builders.path(this.getLocalClass(className))
: nodeValue;
}
const allGlobal = classNames.every((className) => !this.isLocal(className));
if (allGlobal) {
return nodeValue;
}
const allLocal = classNames.every(this.isLocal.bind(this));
if (allLocal) {
const parts = [
AST.builders.path(this.getStyles()),
...classNames.map((className) => AST.builders.string(className)),
];
return AST.builders.sexpr(AST.builders.path('local'), parts);
}
const parts = [];
const globalClasses = [];
classNames.forEach((className) => {
if (!this.isLocal(className)) {
globalClasses.push(className);
return;
}
if (globalClasses.length > 0) {
parts.push(AST.builders.string(globalClasses.join(' ')));
parts.push(AST.builders.string(' '));
}
parts.push(AST.builders.path(this.getLocalClass(className)));
parts.push(AST.builders.string(' '));
globalClasses.length = 0;
});
if (globalClasses.length > 0) {
parts.push(AST.builders.string(globalClasses.join(' ')));
parts.push(AST.builders.string(' '));
}
// Remove space at the end
parts.splice(-1);
return AST.builders.sexpr(AST.builders.path('concat'), parts);
}
processSubExpression(nodeValue) {
switch (nodeValue.path.type) {
case 'PathExpression': {
switch (nodeValue.path.original) {
case 'if':
case 'unless': {
if (nodeValue.params[1]?.type === 'StringLiteral') {
// @ts-expect-error: Incorrect type
nodeValue.params[1] = this.processStringLiteral(nodeValue.params[1]);
}
if (nodeValue.params[2]?.type === 'StringLiteral') {
// @ts-expect-error: Incorrect type
nodeValue.params[2] = this.processStringLiteral(nodeValue.params[2]);
}
break;
}
}
break;
}
case 'StringLiteral': {
// @ts-expect-error: Incorrect type
nodeValue.path = this.processStringLiteral(nodeValue.path);
break;
}
}
return nodeValue;
}
processTextNode(nodeValue) {
const classNames = nodeValue.chars.split(/\s+/).filter(Boolean);
if (classNames.length === 0) {
return AST.builders.text('');
}
if (classNames.length === 1) {
const className = classNames[0];
return this.isLocal(className)
? AST.builders.mustache(this.getLocalClass(className))
: nodeValue;
}
const allGlobal = classNames.every((className) => !this.isLocal(className));
if (allGlobal) {
return nodeValue;
}
const allLocal = classNames.every(this.isLocal.bind(this));
if (allLocal) {
const parts = [
AST.builders.path(this.getStyles()),
...classNames.map((className) => AST.builders.string(className)),
];
return AST.builders.mustache(AST.builders.path('local'), parts);
}
const parts = [];
const globalClasses = [];
classNames.forEach((className) => {
if (!this.isLocal(className)) {
globalClasses.push(className);
return;
}
if (globalClasses.length > 0) {
parts.push(AST.builders.string(globalClasses.join(' ')));
parts.push(AST.builders.string(' '));
}
parts.push(AST.builders.path(this.getLocalClass(className)));
parts.push(AST.builders.string(' '));
globalClasses.length = 0;
});
if (globalClasses.length > 0) {
parts.push(AST.builders.string(globalClasses.join(' ')));
parts.push(AST.builders.string(' '));
}
// Remove space at the end
parts.splice(-1);
return AST.builders.mustache(AST.builders.path('concat'), parts);
}
}