@navikt/aksel
Version:
Aksel command line interface. Handles css-imports, codemods and more
220 lines (219 loc) • 8.31 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = transformer;
const legacy_tokens_1 = require("../../../../darkside/config/legacy.tokens");
const ast_1 = require("../../../utils/ast");
const lineterminator_1 = require("../../../utils/lineterminator");
const packageImports_1 = __importStar(require("../../../utils/packageImports"));
const propsAffected = ["background", "borderColor", "shadow"];
function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
const toSourceOptions = (0, lineterminator_1.getLineTerminator)(file.source);
if (file.source.includes("TODO: aksel box migration")) {
return root.toSource(toSourceOptions);
}
const localName = (0, ast_1.findComponentImport)({
root,
j,
name: "Box",
packageType: "react",
});
if (!localName) {
return;
}
const astElements = root.find(j.JSXElement, {
openingElement: {
name: {
type: "JSXIdentifier",
name: localName,
},
},
});
const tokenComments = [];
for (const astElement of astElements.paths()) {
let encounteredUnmigratableProp = false;
for (const prop of propsAffected) {
(0, ast_1.findProps)({ j, path: astElement, name: prop }).forEach((attr) => {
const attrvalue = attr.value.value;
if (attrvalue.type === "StringLiteral") {
const config = legacy_tokens_1.legacyTokenConfig[attrvalue.value];
if (config === null || config === void 0 ? void 0 : config.replacement) {
attrvalue.value = config.replacement;
}
else {
encounteredUnmigratableProp = true;
const tokenComment = {
prop,
token: attrvalue.value,
};
if (config === null || config === void 0 ? void 0 : config.comment) {
tokenComment.comment = config.comment;
}
tokenComments.push(tokenComment);
}
}
});
}
(0, ast_1.findProps)({ j, path: astElement, name: "borderRadius" }).forEach((attr) => {
const attrValue = attr.value.value;
if (attrValue.type === "StringLiteral") {
/* borderRadius="xlarge" */
attrValue.value = convertBorderRadiusToRadius(attrValue.value);
}
else if (attrValue.type === "JSXExpressionContainer") {
/* borderRadius={{xs: "xlarge", sm: "large"}} */
const expression = attrValue.expression;
if (expression.type === "ObjectExpression") {
/* xs, md, sm */
expression.properties.forEach((property) => {
if (property.type === "ObjectProperty") {
if (property.value.type === "StringLiteral") {
property.value.value = convertBorderRadiusToRadius(property.value.value);
}
}
});
}
}
});
if (!encounteredUnmigratableProp) {
// TODO: ?? Box -> BoxNew type fail? (but works)
astElement.node.openingElement.name.name = "BoxNew";
astElement.node.closingElement.name.name = "BoxNew";
}
}
const blockComment = createFileComments({ tokenComments });
const importAnalysis = analyzePartialMigration(j, root.toSource(toSourceOptions));
if (importAnalysis === "no new") {
// WHY: we do nothing to the import statements if we couldn't migrate any Box
}
if (importAnalysis === "mixed") {
// WHY: mixed Box and BoxNew == we keep old, and add the new import
(0, packageImports_1.addPackageImport)({
j,
root,
packageName: "@navikt/ds-react/Box",
specifiers: ["BoxNew"],
});
}
if (importAnalysis === "all new") {
// WHY: when we have only new boxes == we replace the old import with the new one
(0, packageImports_1.default)(j, root, {
fromImport: "@navikt/ds-react",
toImport: "@navikt/ds-react/Box",
fromName: "Box",
toName: "BoxNew",
ignoreAlias: true,
});
}
const output = `${blockComment ? blockComment + "\n\n" : ""}${root.toSource(toSourceOptions)}`;
return output;
}
const createFileComments = ({ tokenComments, }) => {
if (tokenComments.length === 0) {
return null;
}
let constructedComment = "/*\nTODO: aksel box migration:\nCould not migrate the following:\n";
for (const { prop, token, comment } of tokenComments.values()) {
constructedComment += ` - ${prop}=${token}\n`;
if (comment) {
constructedComment += ` - ${comment}\n`;
}
}
constructedComment += "*/";
return constructedComment;
};
const analyzePartialMigration = (j, source) => {
const root = j(source);
const astNewElements = (0, ast_1.findJSXElement)({
root,
j,
name: "BoxNew",
originalName: "BoxNew",
});
if (astNewElements.length === 0) {
return "no new";
}
const localName = (0, ast_1.findComponentImport)({
root,
j,
name: "Box",
packageType: "react",
});
if (!localName) {
// this should never happen
throw new Error('package imports have been tampered with before the package import "step" in the migration');
}
const astOldElements = (0, ast_1.findJSXElement)({
root,
j,
name: localName,
originalName: "Box",
});
if (astOldElements.length === 0) {
return "all new";
}
return "mixed";
};
const legacyBorderRadiusNameTokenLookup = {
full: "full",
xlarge: "12",
large: "8",
medium: "4",
small: "2",
};
/**
* Takes an old valid border-radius token and returns the new converted radius token
* oldValue: "xlarge", "full"
* @returns "12", "full"
*/
function convertBorderRadiusToRadius(oldValue) {
const radiusTokens = oldValue.split(" ");
const newRadius = [];
for (const radiusToken of radiusTokens) {
if (radiusToken === "full") {
newRadius.push(radiusToken);
}
else if (!(radiusToken in legacyBorderRadiusNameTokenLookup)) {
console.warn(`Possibly invalid radius token found: ${radiusToken}\n`);
newRadius.push(radiusToken);
}
else {
newRadius.push(`${legacyBorderRadiusNameTokenLookup[radiusToken]}`);
}
}
return newRadius.join(" ");
}