vue-property-decorator-transpiler
Version:
Transpiles classes written in vue-property-decorator back to simple Vue component global registration.
226 lines • 9.78 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
const typescript_1 = __importStar(require("typescript"));
const vue_template_compiler_1 = require("vue-template-compiler");
function getFunction(m, s, name) {
var _a, _b;
if (!name)
name = (_a = m.name) === null || _a === void 0 ? void 0 : _a.getText(s);
let body = (_b = m.body) === null || _b === void 0 ? void 0 : _b.getText(s);
let param = m.parameters.map(p => p.name.getText(s)).join(",");
return `${name}(${param})${body}`;
}
function getDecoratorArguments(m, decorator, s) {
if (typeof m.decorators === "undefined")
return undefined;
for (let d of m.decorators) {
if (typescript_1.default.isCallExpression(d.expression) &&
typescript_1.default.isIdentifier(d.expression.expression) &&
d.expression.expression.escapedText == decorator) {
return d.expression.arguments.map(a => a.getText(s));
}
}
return undefined;
}
function getComponentOption(dec) {
if (dec.decorators)
for (let d of dec.decorators) {
if (typescript_1.default.isIdentifier(d.expression) &&
d.expression.escapedText == "Component")
return {};
if (typescript_1.default.isCallExpression(d.expression) &&
typescript_1.default.isIdentifier(d.expression.expression) &&
d.expression.expression.escapedText == "Component" &&
d.expression.arguments.length) {
let op = d.expression.arguments[0];
if (typescript_1.default.isObjectLiteralExpression(op)) {
let result = {};
for (let p of op.properties) {
if (typescript_1.default.isPropertyAssignment(p) && typescript_1.default.isIdentifier(p.name)) {
if (typescript_1.default.isStringLiteral(p.initializer)) {
if (p.name.escapedText == "name")
result.name = p.initializer.text;
if (p.name.escapedText == "template")
result.template = p.initializer.text;
}
else if (p.name.escapedText == "mixins") {
result.mixins = p.initializer;
}
}
}
return result;
}
}
}
return undefined;
}
/** Built-in hooks of Vue.js */
var LIFECYCLE_HOOKS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroy',
'destroyed',
'activated',
'deactivated',
'errorCaptured',
'serverPrefetch'
];
function findClassDeclaration(statements) {
var _a, _b, _c, _d;
for (var s of statements) {
if (typescript_1.default.isClassDeclaration(s) && ((_a = s.modifiers) === null || _a === void 0 ? void 0 : _a.some(m => m.kind == typescript_1.default.SyntaxKind.ExportKeyword)) && ((_b = s.modifiers) === null || _b === void 0 ? void 0 : _b.some(m => m.kind == typescript_1.default.SyntaxKind.DefaultKeyword))) {
let comOption = getComponentOption(s);
if (comOption !== undefined) {
let exp = (_d = (_c = s.heritageClauses) === null || _c === void 0 ? void 0 : _c[0].types) === null || _d === void 0 ? void 0 : _d[0].expression;
if (exp && typescript_1.default.isIdentifier(exp)) {
let parent = exp.escapedText;
if (parent != "Vue")
comOption.extends = parent;
}
return {
statement: s,
option: comOption
};
}
}
}
throw new Error("Cannot find class declaration");
}
function classToComponentOptionString(statement, sourceFile, option) {
let props = [];
let data = [];
let watch = [];
let computed = [];
let methods = [];
let events = [];
let provides = [];
let injects = [];
for (let member of statement.members) {
if (!member.name)
continue;
let name = member.name.getText(sourceFile);
if (typescript_1.default.isPropertyDeclaration(member)) {
let ini = member.initializer;
let init = ini ? ini.getText(sourceFile) : "undefined";
let prop = getDecoratorArguments(member, "Prop", sourceFile);
let inject = getDecoratorArguments(member, "Inject", sourceFile);
if (prop) {
if (ini)
props.push(`${name}:{type:${prop[0]},default:${init}}`);
else
props.push(`${name}:${prop[0]}`);
}
else if (inject !== undefined) {
if (!inject.length)
inject[0] = `'${name}'`;
injects.push(`${name}: ${inject[0]}`);
}
else {
data.push(`${name}:${init}`);
let provide = getDecoratorArguments(member, "Provide", sourceFile);
if (provide)
provides.push(`${provide}: this.${name}`);
}
}
if (typescript_1.default.isGetAccessor(member)) {
computed.push(getFunction(member, sourceFile));
}
if (typescript_1.default.isMethodDeclaration(member)) {
let func = getFunction(member, sourceFile), abstract = false;
if (member.modifiers)
for (let mod of member.modifiers) {
if (mod.kind == typescript_1.SyntaxKind.AbstractKeyword)
abstract = true;
if (mod.kind == typescript_1.SyntaxKind.AsyncKeyword)
func = "async " + func;
}
if (abstract)
continue;
let tag = getDecoratorArguments(member, "Watch", sourceFile);
if (tag)
watch.push(getFunction(member, sourceFile, tag[0]));
else {
if (LIFECYCLE_HOOKS.includes(name))
events.push(func);
else
methods.push(func);
}
}
}
let options = [];
if (option.extends)
options.push(`mixins: [${option.extends}]`);
else if (option.mixins)
options.push(`mixins: ${option.mixins.getText(sourceFile)}`);
if (data.length)
options.push(`data() { return { ${data.join(",")} }; }`);
if (props.length)
options.push(`props: { ${props.join(",")} }`);
if (provides.length)
options.push(`provide() { return { ${provides.join(",")} }; }`);
if (injects.length)
options.push(`inject: { ${injects.join(",")} }`);
if (watch.length)
options.push(`watch: { ${watch.join(",")} }`);
if (computed.length)
options.push(`computed: { ${computed.join(",")} }`);
if (methods.length)
options.push(`methods: { ${methods.join(",")} }`);
options.push(...events);
return options;
}
/**
* Transpile the default exported class to Vue component global registration.
* @param code Original Typescript code.
*/
function VPDtoJs(code, template, constMode = false) {
var _a;
var sourceFile = typescript_1.default.createSourceFile("vue.ts", code, typescript_1.default.ScriptTarget.ESNext, false);
var { statement, option } = findClassDeclaration(sourceFile.statements);
let comName = "name" in option ? option.name : (_a = statement.name) === null || _a === void 0 ? void 0 : _a.escapedText.toString();
if (!comName)
throw new Error("Component needs to have a name");
let options = classToComponentOptionString(statement, sourceFile, option);
if (template) {
let com = vue_template_compiler_1.compile(template);
if (com.staticRenderFns.length > 0) {
let fns = com.staticRenderFns.map(f => `function(){${f}}`).join(",");
options.unshift(`staticRenderFns: [ ${fns} ]`);
}
options.unshift(`render() { ${com.render} }`);
}
else if (option.template) {
options.unshift(`template: '${option.template}'`);
}
// Put into Vue component syntax
let output = constMode ?
`const ${comName} = { ${options.join(",")} };` :
`Vue.component('${comName.toLowerCase()}', { ${options.join(",")} });`;
// Transpile into JavaScript
output = typescript_1.default.transpile(output, { target: typescript_1.default.ScriptTarget.ESNext });
return output;
}
module.exports = VPDtoJs;
//# sourceMappingURL=index.js.map