vue-di-loader
Version:
Vue Dependency Injection Webpack Loader
413 lines • 18.6 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var classification_1 = require("../classification");
var globalization_1 = require("../globalization");
var path_1 = require("path");
var xmldom_1 = require("xmldom");
var compiler = require("vue-template-compiler");
var sass_1 = require("sass");
var fs_1 = require("fs");
var ts_simple_ast_1 = require("ts-simple-ast");
var typescript_1 = require("typescript");
var _1 = require("../.");
var argumenter_1 = require("@joejukan/argumenter");
var web_kit_1 = require("@joejukan/web-kit");
var RENDER_NAME = "__render__";
var STATIC_RENDER_NAME = "__staticrender__";
var COMPONENTS_NAME = "__components__";
var REGEX_KEY = /\'[\w\-\%\_]+\'(?=\:)|\"[\w\-\%\_]+\"(?=\:)/gm;
var REGEX_BEGIN_QUOTE = /^(?:\"|\')/gm;
var REGEX_END_QUOTE = /(?:\"|\')$/gm;
var REGEX_RENDER = /(?:\'|\")\%\%\%RENDER\%\%\%(?:\'|\")/i;
var REGEX_STATIC_RENDER = /(?:\'|\")\%\%\%STATICRENDER\%\%\%(?:\'|\")/i;
var REGEX_COMPONENTS = /(?:\'|\")\%\%\%COMPONENTS\%\%\%(?:\'|\")/i;
var REGEX_EXTRACT_STRING = /^\s*[\'\"\`](.*)[\'\"\`]\s*$/;
var ASTClass = /** @class */ (function (_super) {
__extends(ASTClass, _super);
function ASTClass() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var _this = _super.call(this) || this;
var argue = new argumenter_1.Argumenter(args);
_this.path = argue.string;
return _this;
}
Object.defineProperty(ASTClass.prototype, "name", {
get: function () {
var path = this.path;
if (path) {
var base = path_1.basename(path);
var ext = path_1.extname(path);
return base + "." + ext;
}
},
enumerable: true,
configurable: true
});
ASTClass.prototype.log = function (value) {
_1.log("[ast-class] " + value);
};
ASTClass.prototype.toImportDeclaration = function (dependency) {
var declaration = {
moduleSpecifier: dependency.relative(this.path)
};
if (dependency.type === _1.DependencyType.VUE) {
if (dependency.defaulted) {
declaration.defaultImport = dependency.symbol;
}
else {
declaration.namedImports = [{ name: dependency.symbol }];
}
}
return declaration;
};
ASTClass.prototype.processScript = function () {
var source = this.source = this.createSourceFile(this.name);
source.insertText(0, this.typescript);
var parser = new classification_1.ASTParser(source);
var classes = source.getClasses();
var template = this.template;
var _loop_1 = function (i) {
var cls = classes[i];
var symbol = cls.getName();
// handle decorator
var decorator = cls.getDecorator(function (dec) {
var name = dec.getName();
var isComp = /Component/i.test(name);
return isComp;
});
if (decorator) {
if (!decorator.isDecoratorFactory()) {
decorator.setIsDecoratorFactory(true);
}
var args = decorator.getArguments();
var arg = (args.length > 0 ? args[0] : decorator.addArgument("{}"));
var componentName = void 0;
var literal = parser.get('name', arg, ts_simple_ast_1.SyntaxKind.StringLiteral);
if (literal) {
var parts = REGEX_EXTRACT_STRING.exec(literal.getText());
if (parts && parts.length > 1) {
componentName = parts[1];
}
}
else {
componentName = web_kit_1.kebab(symbol);
}
var renders = compiler.compileToFunctions(_1.preCompile(this_1.template));
var statics = renders.staticRenderFns;
var staticStrings = "[ ";
for (var j = 0; j < statics.length; j++) {
var s = statics[j];
if (j > 0)
staticStrings += ", ";
staticStrings += "function () { " + _1.functionString(s) + " }";
}
staticStrings += " ]";
var imports = new Array();
// imports
for (var k in globalization_1.dependencies) {
var dependency = globalization_1.dependencies[k];
var name_1 = dependency.name;
var symbol_1 = dependency.symbol;
if (name_1 !== componentName) {
if (globalization_1.configuration.domParsing) {
var dom = new xmldom_1.DOMParser();
var doc = dom.parseFromString(template, 'text/html');
if (doc) {
var elements = doc.getElementsByTagName(dependency.name);
if (elements.length > 0) {
imports.push(dependency);
}
}
}
else {
var regexp = new RegExp("\\<\\s*" + name_1.replace(/\-/, '\\-') + "[\\s\\/\\>]+", 'mi');
if (regexp.test(template)) {
imports.push(dependency);
}
}
}
}
// template variable
source.insertVariableStatement(0, {
declarationKind: ts_simple_ast_1.VariableDeclarationKind.Const,
declarations: [
{
name: STATIC_RENDER_NAME,
type: "Array<Function>",
initializer: staticStrings
}
]
});
source.insertFunction(0, {
name: RENDER_NAME,
bodyText: _1.functionString(renders.render)
});
var componentsDec = source.insertVariableStatement(0, {
declarationKind: ts_simple_ast_1.VariableDeclarationKind.Const,
declarations: [
{
name: COMPONENTS_NAME,
initializer: "{}"
}
]
}).getDeclarations()[0];
var components_1 = componentsDec.getFirstChildByKind(ts_simple_ast_1.SyntaxKind.ObjectLiteralExpression);
imports.forEach(function (imp) {
components_1.addPropertyAssignment({ name: "'" + imp.name + "'", initializer: imp.symbol });
});
for (var j = 0; j < imports.length; j++) {
var imp = imports[j];
source.insertImportDeclaration(0, this_1.toImportDeclaration(imp));
}
// TODO: check if components already existing
arg.addPropertyAssignment({ name: 'render', initializer: RENDER_NAME });
arg.addPropertyAssignment({ name: 'staticRenderFns', initializer: STATIC_RENDER_NAME });
arg.addPropertyAssignment({ name: 'components', initializer: COMPONENTS_NAME });
this_1.mounted(cls);
this_1.injectHMRVue(source, globalization_1.dependencies[componentName]);
}
};
var this_1 = this;
for (var i = classes.length - 1; i >= 0; i--) {
_loop_1(i);
}
try {
source.organizeImports();
source.formatText({ indentSize: 4 });
}
catch (ex) {
}
this.typescript = source.getText();
};
ASTClass.prototype.transpile = function () {
this.javascript = typescript_1.transpile(this.typescript, {
module: typescript_1.ModuleKind.CommonJS,
moduleResolution: typescript_1.ModuleResolutionKind.NodeJs,
emitDecoratorMetadata: true,
experimentalDecorators: true,
allowSyntheticDefaultImports: true,
noImplicitUseStrict: true
});
};
ASTClass.prototype.load = function (content) {
var sfc = this.sfc = compiler.parseComponent(content);
this.typescript = sfc.script.content;
this.template = sfc.template.content;
if (sfc.styles.length > 0) {
var style = sfc.styles[0];
this.style = (style.content ? style : undefined);
}
this.processScript();
this.log("loading content from " + this.path + ":\n" + this.typescript);
};
ASTClass.prototype.pitch = function (path) {
this.log("pitching (" + path + ")");
var content = fs_1.readFileSync(path, 'utf-8');
content = "<source>" + content + "</source>";
var source = this.createSourceFile(web_kit_1.uuid() + ".ts");
source.insertText(0, content);
var parser = new classification_1.ASTParser(source);
var classes = source.getClasses();
for (var i = 0; i < classes.length; i++) {
var cls = classes[i];
var symbol = cls.getName();
var decorators = cls.getDecorators();
for (var j = 0; j < decorators.length; j++) {
var decorator = decorators[j];
if (decorator.getName() === "Component") {
var args = decorator.getArguments();
var arg = void 0;
if (!Array.isArray(args) || args.length === 0) {
arg = decorator.addArgument("{}");
}
else {
arg = args[0];
}
var name_2 = undefined;
var literal = parser.get('name', arg, ts_simple_ast_1.SyntaxKind.StringLiteral);
if (literal) {
var parts = REGEX_EXTRACT_STRING.exec(literal.getText());
if (parts && parts.length > 1) {
name_2 = parts[1];
}
}
else {
name_2 = web_kit_1.kebab(symbol);
}
if (name_2) {
globalization_1.dependencies[name_2] = new classification_1.DependencyClass(name_2, symbol, path, cls.isDefaultExport());
return;
}
}
}
}
};
ASTClass.prototype.inject = function (content) {
var source = this.createSourceFile(web_kit_1.uuid() + ".ts");
source.replaceWithText(content);
if (!source)
return;
var finder = new classification_1.ASTParser(source);
var declaration = finder.findVariableDeclaration('CombinedVueInstance');
if (!declaration)
return;
var lastChild = declaration.getLastChild();
var expression = declaration.getFirstChildByKind(ts_simple_ast_1.SyntaxKind.NewExpression);
if (!expression)
return;
var args = expression.getArguments();
if (!args || args.length != 1)
return;
var arg = args[0];
var object = arg.getFirstChildByKind(ts_simple_ast_1.SyntaxKind.SyntaxList);
if (!object)
return;
var pairs = object.getChildren();
if (!pairs)
return;
var components;
pairs.forEach(function (pair) {
var key = pair.getFirstChildByKind(ts_simple_ast_1.SyntaxKind.Identifier);
var kind = pair.getKind();
if (key && key.getText() === 'components') {
if (kind === ts_simple_ast_1.SyntaxKind.ShorthandPropertyAssignment) {
pair.replaceWithText('components: {...components}');
}
else if (kind === ts_simple_ast_1.SyntaxKind.PropertyAssignment) {
}
else {
pair.replaceWithText('components: {}');
}
components = pair.getFirstChildByKind(ts_simple_ast_1.SyntaxKind.ObjectLiteralExpression);
}
});
if (!components) {
if (object.getLastChild().getKind() !== ts_simple_ast_1.SyntaxKind.CommaToken) {
object.addChildText(',');
}
components = object.addChildText('components: {}')[0].getFirstChildByKind(ts_simple_ast_1.SyntaxKind.ObjectLiteralExpression);
}
for (var k in globalization_1.dependencies) {
var dependency = globalization_1.dependencies[k];
source.addImportDeclaration(this.toImportDeclaration(dependency));
if (dependency.type === _1.DependencyType.VUE) {
components.addPropertyAssignment({ name: "'" + dependency.name + "'", initializer: dependency.symbol });
}
}
this.injectHMREntry(source);
source.organizeImports();
source.formatText({
indentSize: 4,
});
this.typescript = source.getText();
this.log("after injection in (" + this.path + "):\n" + this.typescript);
};
ASTClass.prototype.injectHMREntry = function (source) {
if (globalization_1.configuration.hot) {
source.addStatements("\n declare let module: any;\n if(module.hot){\n module.hot.accept();\n }\n ");
}
};
ASTClass.prototype.injectHMRVue = function (source, depenency) {
if (globalization_1.configuration.hot) {
source.addStatements("\n import { HMRClass } from 'vue-di-kit';\n let hmr = new HMRClass('" + depenency.id + "', " + depenency.symbol + ");\n hmr.hot();\n ");
source.formatText({ indentSize: 4 });
}
};
ASTClass.prototype.mounted = function (cls) {
var _this = this;
if (this.style) {
// TODO: add business logic to search for @Mounted and @Updated when decorators are supported in vue-di-kit.
['created', 'mounted', 'updated'].forEach(function (name) {
var method = cls.getMethod(name);
if (method) {
var body = method.getBody();
var text = undefined;
if (body) {
var content = body.getLastChildByKind(ts_simple_ast_1.SyntaxKind.SyntaxList);
if (content) {
text = content.getText();
}
}
_this.codeEventMethods(method, text);
}
else {
method = cls.addMethod({ name: name, scope: ts_simple_ast_1.Scope.Public });
_this.codeEventMethods(method);
}
});
}
};
ASTClass.prototype.codeEventMethods = function (method, suffix) {
switch (method.getName()) {
case 'created':
this.codeCreateMethod(method, suffix);
break;
case 'mounted':
case 'updated':
this.codeMountedUpdatedMethods(method, suffix);
break;
}
};
ASTClass.prototype.codeMountedUpdatedMethods = function (method, suffix) {
if (this.style) {
var text = '/* auto generated code for DI styles */\n';
text += "if(this.$el.nodeType == 1 && !this.$DI.styled && this.$DI.style){\n";
text += "this.$el.appendChild( this.$DI.style );\n";
text += "}\n";
if (suffix) {
text += '\n/* appended code from previous method implementation */\n';
text += suffix;
}
method.setBodyText(text);
method.formatText({ indentStyle: ts_simple_ast_1.ts.IndentStyle.Smart });
}
};
ASTClass.prototype.codeCreateMethod = function (method, suffix) {
var text = '/* auto generated code for DI object */\n';
text += "this.$DI = { style: undefined };\n";
if (this.style) {
var id = "_style_di_" + Math.floor(10 + 100000 * Math.random());
var css = sass_1.renderSync({ data: this.style.content, includePaths: globalization_1.sass.path }).css.toString();
text += "let " + id + " = document.createElement( 'style' );\n";
text += id + ".appendChild( document.createTextNode( `" + css.replace(/\n/g, ' ').replace(/\s{2,}/g, ' ').trim() + "` ) );\n";
text += id + ".setAttribute('id','" + id + "');\n";
if (this.style.scoped) {
text += id + ".setAttribute('scoped', '');\n";
}
text += "this.$DI.style = " + id + ";\n";
text += "Object.defineProperty(this.$DI, 'styled', { enumerable: true, get: () =>( document.getElementById('" + id + "') ? true : false) } );\n";
}
if (suffix) {
text += '\n/* appended code from previous method implementation */\n';
text += suffix;
}
method.setBodyText(text);
method.formatText({ indentStyle: ts_simple_ast_1.ts.IndentStyle.Smart });
};
ASTClass.prototype.addFile = function (path) {
ASTClass.addFile(path);
};
ASTClass.addFile = function (path) {
// TODO: consider HOT loading
globalization_1.dependencies[web_kit_1.uuid()] = new classification_1.DependencyClass(path_1.basename(path), path_1.extname(path), path, _1.DependencyType.FILE);
};
return ASTClass;
}(ts_simple_ast_1.default));
exports.ASTClass = ASTClass;
//# sourceMappingURL=ast.class.js.map
;