@tririga/tri-polymer-upgrade
Version:
A tool for converting TRIRIGA UX views from Polymer 1 to Polymer 3
266 lines (237 loc) • 7.35 kB
JavaScript
const astTypes = require("ast-types");
const parse5 = require("parse5");
const dom5 = require("dom5");
const os = require("os");
const jsc = require("jscodeshift");
const fse = require("fs-extra");
const path = require("path");
const polymerUrlMap = require("../config/polymer-url-map.json");
// Copyright (c) 2014 The Polymer Authors. All rights reserved.
/**
* Yields the NodePath for each statement at the top level of `program`.
*
* Like `yield* program.body` except it yields NodePaths rather than
* Nodes, so that the caller can mutate the AST with the NodePath api.
*/
function* getTopLevelStatements(program) {
const nodePaths = [];
astTypes.visit(program, {
visitNode(path) {
if (!path.parent) {
// toplevel program
this.traverse(path);
return;
}
if (path.parent.node.type !== 'Program') {
// not a toplevel statement, skip it
return false;
}
this.traverse(path);
// ok, path.node must be a toplevel statement of the program.
nodePaths.push(path);
return;
}
});
yield* nodePaths;
}
module.exports.getTopLevelStatements = getTopLevelStatements;
function escapeTemplateElement(cooked) {
const raw = cooked.replace(/(<\/script|\\|`|\$)/g, (_match, group) => {
switch (group) {
case `<\/script`:
return '</script';
case '\\':
return '\\\\';
case '`':
return '\\`';
case '$':
return '\\$';
default:
throw new Error(`oops!: ${group}`);
}
});
return raw;
}
module.exports.escapeTemplateElement = escapeTemplateElement;
/**
* Serialize a the element template to string and prepend the css styles to it.
* Wrapped the resulting string as a template literal.
*/
function serializeTemplate(scannedElement) {
const lines = parse5.serialize(scannedElement.template).split('\n');
// Remove empty / whitespace-only leading lines.
while (/^\s*$/.test(lines[0])) {
lines.shift();
}
// Remove empty / whitespace-only trailing lines.
while (/^\s*$/.test(lines[lines.length - 1])) {
lines.pop();
}
let cooked = lines.join(os.EOL);
cooked = `${os.EOL}${cooked}${os.EOL}\t`;
let css = scannedElement.css ? scannedElement.css.content : "";
let hasCss = css && css.length > 0;
let hasInclude = scannedElement.cssInclude && scannedElement.cssInclude.length > 0;
if (hasCss || hasInclude) {
css = css.replace(/\n\t/g, "\n\t\t");
cooked = `${os.EOL}\t\t<style${hasInclude ? ` include="${scannedElement.cssInclude}"` : ""}>${os.EOL}${css}${os.EOL}\t\t</style>${os.EOL}${cooked}`;
}
const raw = escapeTemplateElement(cooked);
return jsc.templateLiteral([jsc.templateElement({ cooked, raw }, true)], []);
}
module.exports.serializeTemplate = serializeTemplate;
function getPolymerElementDefinition(program) {
let polymerFunctionCallArgument = null;
astTypes.visit(program, {
visitCallExpression(path) {
// a call expression and the function name is Polymer
if (path.node.callee.type == "Identifier" &&
path.node.callee.name == "Polymer") {
polymerFunctionCallArgument = path.node.arguments[0];
this.abort();
}
this.traverse(path);
}
});
return polymerFunctionCallArgument;
}
module.exports.getPolymerElementDefinition = getPolymerElementDefinition;
function getPolymerElementCall(program) {
let polymerFunctionCall = null;
astTypes.visit(program, {
visitCallExpression(path) {
// a call expression and the function name is Polymer
if (path.node.callee.type == "Identifier" &&
path.node.callee.name == "Polymer") {
polymerFunctionCall = path.parent.node;
this.abort();
}
this.traverse(path);
}
});
return polymerFunctionCall;
}
module.exports.getPolymerElementCall = getPolymerElementCall;
function getCommentsFromTexts(commentTexts) {
const recastComments = commentTexts.map((comment) => {
const escapedComment = comment.replace(/\*\//g, '*\\/');
return {
type: "Block",
leading: true,
trailing: false,
value: escapedComment,
};
});
return recastComments;
}
module.exports.getCommentsFromTexts = getCommentsFromTexts;
function convertUrlImport(url) {
if (url.endsWith("polymer/polymer.html")) {
return url.replace("polymer/polymer.html", "@polymer/polymer/polymer-legacy.js")
}
const newUrl = url.replace(".html", ".js");
if (!newUrl.startsWith("../")) {
return "./" + newUrl
}
const urlPieces = newUrl.split("/");
let packageName = null;
let i = 0;
do {
++i;
packageName = urlPieces[i];
} while (packageName == "..");
if (packageName && polymerUrlMap[packageName]) {
urlPieces[i] = polymerUrlMap[packageName].url;
return urlPieces.join("/");
}
return newUrl;
}
module.exports.convertUrlImport = convertUrlImport;
function* getElementsUsingPolymerNamespace(arrayExpression) {
let elementsPath = [];
astTypes.visit(arrayExpression, {
visitMemberExpression(path) {
if (path.node.object.name == "Polymer") {
elementsPath.push(path);
}
return false;
}
});
yield* elementsPath;
}
module.exports.getElementsUsingPolymerNamespace = getElementsUsingPolymerNamespace;
function getRelativePath(filePath, baseDir) {
const fileStrToReplace = process.platform === "win32" ? "file:\\" : "file:";
const processedFilePath = decodeURIComponent(path.normalize(filePath)).replace(fileStrToReplace, "");
return path.relative(baseDir, processedFilePath);
}
module.exports.getRelativePath = getRelativePath;
function moveFile(srcPath, baseDir, outputDir) {
const relativePath = getRelativePath(srcPath, baseDir);
const destPath = path.join(outputDir, relativePath);
return fse.copy(srcPath.replace("file://", ""), destPath);
}
module.exports.moveFile = moveFile;
function dashToCamelCase(dash) {
if (dash.indexOf('-') < 0) {
return dash;
}
return dash.replace(/-[a-z]/g, m => m[1].toUpperCase());
}
module.exports.dashToCamelCase = dashToCamelCase;
function serializeHTMLNode(node) {
const container = parse5.treeAdapters.default.createDocumentFragment();
dom5.append(container, node);
return parse5.serialize(container);
}
module.exports.serializeHTMLNode = serializeHTMLNode;
function getNodePropertyByName(node, propertyName) {
let result = null;
astTypes.visit(node, {
visitProperty(path) {
if (path.parent.node == node &&
path.node.type == "Property" &&
path.node.key.type == "Identifier" &&
path.node.key.name == propertyName) {
result = path.node;
this.abort();
}
return false;
}
});
return result;
}
module.exports.getNodePropertyByName = getNodePropertyByName;
function addImport(scannedDoc, importUrl, exportName) {
for (let i = 0; i < scannedDoc.imports.length; i++) {
const item = scannedDoc.imports[i];
if (item.url == importUrl) {
if (item.exports.indexOf(exportName) < 0) {
item.exports.push(exportName);
};
return;
}
}
scannedDoc.imports.push({
url: importUrl,
exports: [exportName]
})
}
module.exports.addImport = addImport;
function addHtmlImport(imports, url) {
let exist = imports.some((importUrl) => {
return importUrl == url;
});
if (!exist) {
imports.push(url);
}
return imports;
}
module.exports.addHtmlImport = addHtmlImport;
function getModuleUrlCallExp(packageName, relativePath) {
return jsc.callExpression(
jsc.identifier("getModuleUrl"),
[jsc.literal(`${packageName}/${relativePath.replace(".html",".js")}`)]
);
}
module.exports.getModuleUrlCallExp = getModuleUrlCallExp;