@ethan-jones-vizio/sveld
Version:
Generate TypeScript definitions for your Svelte components.
563 lines (562 loc) • 29.4 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
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 (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;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
exports.__esModule = true;
var compiler_1 = require("svelte/compiler");
var commentParser = __importStar(require("comment-parser"));
var element_tag_map_1 = require("./element-tag-map");
var DEFAULT_SLOT_NAME = "__default__";
var ComponentParser = /** @class */ (function () {
function ComponentParser(options) {
this.reactive_vars = new Set();
this.vars = new Set();
this.props = new Map();
this.moduleExports = new Map();
this.slots = new Map();
this.events = new Map();
this.typedefs = new Map();
this.bindings = new Map();
this.options = options;
}
ComponentParser.mapToArray = function (map) {
return Array.from(map, function (_a) {
var key = _a[0], value = _a[1];
return value;
});
};
ComponentParser.assignValue = function (value) {
return value === undefined || value === "" ? undefined : value;
};
ComponentParser.formatComment = function (comment) {
var formatted_comment = comment;
if (!formatted_comment.startsWith("/*")) {
formatted_comment = "/*" + formatted_comment;
}
if (!formatted_comment.endsWith("*/")) {
formatted_comment += "*/";
}
return formatted_comment;
};
ComponentParser.prototype.sourceAtPos = function (start, end) {
var _a;
return (_a = this.source) === null || _a === void 0 ? void 0 : _a.slice(start, end);
};
ComponentParser.prototype.collectReactiveVars = function () {
var _this = this;
var _a;
(_a = this.compiled) === null || _a === void 0 ? void 0 : _a.vars.filter(function (_a) {
var reassigned = _a.reassigned, writable = _a.writable;
return reassigned && writable;
}).forEach(function (_a) {
var name = _a.name;
return _this.reactive_vars.add(name);
});
};
ComponentParser.prototype.addProp = function (prop_name, data) {
if (ComponentParser.assignValue(prop_name) === undefined)
return;
if (this.props.has(prop_name)) {
var existing_slot = this.props.get(prop_name);
this.props.set(prop_name, __assign(__assign({}, existing_slot), data));
}
else {
this.props.set(prop_name, data);
}
};
ComponentParser.prototype.addModuleExport = function (prop_name, data) {
if (ComponentParser.assignValue(prop_name) === undefined)
return;
if (this.moduleExports.has(prop_name)) {
var existing_slot = this.moduleExports.get(prop_name);
this.moduleExports.set(prop_name, __assign(__assign({}, existing_slot), data));
}
else {
this.moduleExports.set(prop_name, data);
}
};
ComponentParser.prototype.aliasType = function (type) {
if (type === "*")
return "any";
return type;
};
ComponentParser.prototype.addSlot = function (slot_name, slot_props, slot_fallback) {
var default_slot = slot_name === undefined || slot_name === "";
var name = default_slot ? DEFAULT_SLOT_NAME : slot_name;
var fallback = ComponentParser.assignValue(slot_fallback);
var props = ComponentParser.assignValue(slot_props);
if (this.slots.has(name)) {
var existing_slot = this.slots.get(name);
this.slots.set(name, __assign(__assign({}, existing_slot), { fallback: fallback, slot_props: existing_slot.slot_props === undefined ? props : existing_slot.slot_props }));
}
else {
this.slots.set(name, {
name: name,
"default": default_slot,
fallback: fallback,
slot_props: slot_props
});
}
};
ComponentParser.prototype.addDispatchedEvent = function (_a) {
var name = _a.name, detail = _a.detail, has_argument = _a.has_argument;
if (name === undefined)
return;
/**
* `e.detail` should be `null` if the dispatcher
* is not provided a second argument and if
* `@event` is not specified.
*/
var default_detail = !has_argument && !detail ? "null" : ComponentParser.assignValue(detail);
if (this.events.has(name)) {
var existing_event = this.events.get(name);
this.events.set(name, __assign(__assign({}, existing_event), { detail: existing_event.detail === undefined ? default_detail : existing_event.detail }));
}
else {
this.events.set(name, {
type: "dispatched",
name: name,
detail: default_detail
});
}
};
ComponentParser.prototype.parseCustomTypes = function () {
var _this = this;
commentParser.parse(this.source, { spacing: "preserve" }).forEach(function (_a) {
var tags = _a.tags;
tags.forEach(function (_a) {
var tag = _a.tag, tagType = _a.type, name = _a.name, description = _a.description;
var type = _this.aliasType(tagType);
switch (tag) {
case "extends":
_this["extends"] = {
interface: name,
"import": type
};
break;
case "restProps":
_this.rest_props = {
type: "Element",
name: type
};
break;
case "slot":
_this.addSlot(name, type);
break;
case "event":
_this.addDispatchedEvent({ name: name, detail: type, has_argument: false });
break;
case "typedef":
_this.typedefs.set(name, {
type: type,
name: name,
description: ComponentParser.assignValue(description),
ts: /(\}|\};)$/.test(type) ? "interface ".concat(name, " ").concat(type) : "type ".concat(name, " = ").concat(type)
});
break;
}
});
});
};
ComponentParser.prototype.cleanup = function () {
this.source = undefined;
this.compiled = undefined;
this.parsed = undefined;
this.rest_props = undefined;
this["extends"] = undefined;
this.componentComment = undefined;
this.reactive_vars.clear();
this.props.clear();
this.moduleExports.clear();
this.slots.clear();
this.events.clear();
this.typedefs.clear();
this.bindings.clear();
};
ComponentParser.prototype.parseSvelteComponent = function (source, diagnostics) {
var _this = this;
var _a, _b, _c;
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.verbose) {
console.log("[parsing] \"".concat(diagnostics.moduleName, "\" ").concat(diagnostics.filePath));
}
this.cleanup();
this.source = source;
this.compiled = (0, compiler_1.compile)(source);
this.parsed = (0, compiler_1.parse)(source);
this.collectReactiveVars();
this.parseCustomTypes();
if ((_b = this.parsed) === null || _b === void 0 ? void 0 : _b.module) {
(0, compiler_1.walk)((_c = this.parsed) === null || _c === void 0 ? void 0 : _c.module, {
enter: function (node) {
var _a, _b, _c, _d, _e, _f, _g, _h;
if (node.type === "ExportNamedDeclaration") {
var _j = ((_a = node.declaration) === null || _a === void 0 ? void 0 : _a.declarations) ? node.declaration.declarations[0] : node.declaration, declaration_type = _j.type, id = _j.id, init = _j.init, body = _j.body;
var prop_name = id.name;
var value = undefined;
var type = undefined;
var kind = node.declaration.kind;
var description = undefined;
var isFunction = false;
var isFunctionDeclaration = false;
if (init != null) {
if (init.type === "ObjectExpression" ||
init.type === "BinaryExpression" ||
init.type === "ArrayExpression" ||
init.type === "ArrowFunctionExpression") {
value = (_b = _this.sourceAtPos(init.start, init.end)) === null || _b === void 0 ? void 0 : _b.replace(/\n/g, " ");
type = value;
isFunction = init.type === "ArrowFunctionExpression";
if (init.type === "BinaryExpression") {
if ((init === null || init === void 0 ? void 0 : init.left.type) === "Literal" && typeof (init === null || init === void 0 ? void 0 : init.left.value) === "string") {
type = "string";
}
}
}
else {
if (init.type === "UnaryExpression") {
value = _this.sourceAtPos(init.start, init.end);
type = typeof ((_c = init.argument) === null || _c === void 0 ? void 0 : _c.value);
}
else {
value = init.raw;
type = init.value == null ? undefined : typeof init.value;
}
}
}
if (declaration_type === "FunctionDeclaration") {
value = "() => " + ((_d = _this.sourceAtPos(body.start, body.end)) === null || _d === void 0 ? void 0 : _d.replace(/\n/g, " "));
type = "() => any";
kind = "function";
isFunction = true;
isFunctionDeclaration = true;
}
if (node.leadingComments) {
var last_comment = node.leadingComments[node.leadingComments.length - 1];
var comment = commentParser.parse(ComponentParser.formatComment(last_comment.value), {
spacing: "preserve"
});
var tag = (_e = comment[0]) === null || _e === void 0 ? void 0 : _e.tags[((_f = comment[0]) === null || _f === void 0 ? void 0 : _f.tags.length) - 1];
if ((tag === null || tag === void 0 ? void 0 : tag.tag) === "type")
type = _this.aliasType(tag.type);
description = ComponentParser.assignValue((_h = (_g = comment[0]) === null || _g === void 0 ? void 0 : _g.description) === null || _h === void 0 ? void 0 : _h.trim());
}
if (!description && _this.typedefs.has(type)) {
description = _this.typedefs.get(type).description;
}
_this.addModuleExport(prop_name, {
name: prop_name,
kind: kind,
description: description,
type: type,
value: value,
isFunction: isFunction,
isFunctionDeclaration: isFunctionDeclaration,
isRequired: false,
constant: kind === "const",
reactive: false
});
}
}
});
}
var dispatcher_name = undefined;
var callees = [];
(0, compiler_1.walk)({ html: this.parsed.html, instance: this.parsed.instance }, {
enter: function (node, parent, prop) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
if (node.type === "CallExpression") {
if (node.callee.name === "createEventDispatcher") {
dispatcher_name = parent === null || parent === void 0 ? void 0 : parent.id.name;
}
callees.push({
name: node.callee.name,
arguments: node.arguments
});
}
if (node.type === "Spread" && (node === null || node === void 0 ? void 0 : node.expression.name) === "$$restProps") {
if (_this.rest_props === undefined && ((parent === null || parent === void 0 ? void 0 : parent.type) === "InlineComponent" || (parent === null || parent === void 0 ? void 0 : parent.type) === "Element")) {
_this.rest_props = {
type: parent.type,
name: parent.name
};
}
}
if (node.type === "VariableDeclaration") {
_this.vars.add(node);
}
if (node.type === "ExportNamedDeclaration") {
// Handle renamed exports
var prop_name = void 0;
if (node.declaration == null && ((_a = node.specifiers[0]) === null || _a === void 0 ? void 0 : _a.type) === "ExportSpecifier") {
var specifier = node.specifiers[0];
var localName_1 = specifier.local.name, exportedName = specifier.exported.name;
var declaration_1;
// Search through all variable declarations for this variable
// Limitation: the variable must have been declared before the export
_this.vars.forEach(function (varDecl) {
if (varDecl.declarations.some(function (decl) { return decl.id.type === "Identifier" && decl.id.name === localName_1; })) {
declaration_1 = varDecl;
}
});
node.declaration = declaration_1;
prop_name = exportedName;
}
var _q = node.declaration.declarations ? node.declaration.declarations[0] : node.declaration, declaration_type = _q.type, id = _q.id, init = _q.init, body = _q.body;
prop_name !== null && prop_name !== void 0 ? prop_name : (prop_name = id.name);
var value = undefined;
var type = undefined;
var kind = node.declaration.kind;
var description_1 = undefined;
var isFunction = false;
var isFunctionDeclaration = false;
var isRequired_1 = false;
if (init != null) {
if (init.type === "ObjectExpression" ||
init.type === "BinaryExpression" ||
init.type === "ArrayExpression" ||
init.type === "ArrowFunctionExpression") {
value = (_b = _this.sourceAtPos(init.start, init.end)) === null || _b === void 0 ? void 0 : _b.replace(/\n/g, " ");
type = value;
isFunction = init.type === "ArrowFunctionExpression";
if (init.type === "BinaryExpression") {
if ((init === null || init === void 0 ? void 0 : init.left.type) === "Literal" && typeof (init === null || init === void 0 ? void 0 : init.left.value) === "string") {
type = "string";
}
}
}
else {
if (init.type === "UnaryExpression") {
value = _this.sourceAtPos(init.start, init.end);
type = typeof ((_c = init.argument) === null || _c === void 0 ? void 0 : _c.value);
}
else {
value = init.raw;
type = init.value == null ? undefined : typeof init.value;
}
}
}
if (declaration_type === "FunctionDeclaration") {
value = "() => " + ((_d = _this.sourceAtPos(body.start, body.end)) === null || _d === void 0 ? void 0 : _d.replace(/\n/g, " "));
type = "() => any";
kind = "function";
isFunction = true;
isFunctionDeclaration = true;
}
if (node.leadingComments) {
var last_comment = node.leadingComments[node.leadingComments.length - 1];
var comment = commentParser.parse(ComponentParser.formatComment(last_comment.value), {
spacing: "preserve"
});
var tag = (_e = comment[0]) === null || _e === void 0 ? void 0 : _e.tags[((_f = comment[0]) === null || _f === void 0 ? void 0 : _f.tags.length) - 1];
if ((tag === null || tag === void 0 ? void 0 : tag.tag) === "type")
type = _this.aliasType(tag.type);
description_1 = ComponentParser.assignValue((_h = (_g = comment[0]) === null || _g === void 0 ? void 0 : _g.description) === null || _h === void 0 ? void 0 : _h.trim());
var additional_tags = (_k = (_j = comment[0]) === null || _j === void 0 ? void 0 : _j.tags.filter(function (tag) { return !["type", "extends", "restProps", "slot", "event", "typedef"].includes(tag.tag); })) !== null && _k !== void 0 ? _k : [];
if (additional_tags.length > 0 && description_1 === undefined) {
description_1 = "";
}
additional_tags.forEach(function (tag) {
isRequired_1 = tag.tag === "required";
if (!isRequired_1) {
description_1 += "".concat(description_1 ? "\n" : "", "@").concat(tag.tag, " ").concat(tag.name).concat(tag.description ? " ".concat(tag.description) : "");
}
});
}
if (!description_1 && _this.typedefs.has(type)) {
description_1 = _this.typedefs.get(type).description;
}
_this.addProp(prop_name, {
name: prop_name,
kind: kind,
description: description_1,
type: type,
value: value,
isFunction: isFunction,
isFunctionDeclaration: isFunctionDeclaration,
isRequired: isRequired_1,
constant: kind === "const",
reactive: _this.reactive_vars.has(prop_name)
});
}
if (node.type === "Comment") {
var data = (_m = (_l = node === null || node === void 0 ? void 0 : node.data) === null || _l === void 0 ? void 0 : _l.trim()) !== null && _m !== void 0 ? _m : "";
if (/^/.test(data)) {
_this.componentComment = data.replace(/^/, "");
}
}
if (node.type === "Slot") {
var slot_name = (_o = node.attributes.find(function (attr) { return attr.name === "name"; })) === null || _o === void 0 ? void 0 : _o.value[0].data;
var slot_props = node.attributes
.filter(function (attr) { return attr.name !== "name"; })
.reduce(function (slot_props, _a) {
var _b;
var name = _a.name, value = _a.value;
var slot_prop_value = {
value: undefined,
replace: false
};
if (value === undefined)
return {};
if (value[0]) {
var _c = value[0], type = _c.type, expression = _c.expression, raw = _c.raw, start = _c.start, end = _c.end;
if (type === "Text") {
slot_prop_value.value = raw;
}
else if (type === "AttributeShorthand") {
slot_prop_value.value = expression.name;
slot_prop_value.replace = true;
}
if (expression) {
if (expression.type === "Literal") {
slot_prop_value.value = expression.value;
}
else if (expression.type !== "Identifier") {
if (expression.type === "ObjectExpression" || expression.type === "TemplateLiteral") {
slot_prop_value.value = _this.sourceAtPos(start + 1, end - 1);
}
else {
slot_prop_value.value = _this.sourceAtPos(start, end);
}
}
}
}
return __assign(__assign({}, slot_props), (_b = {}, _b[name] = slot_prop_value, _b));
}, {});
var fallback = (_p = node.children) === null || _p === void 0 ? void 0 : _p.map(function (_a) {
var start = _a.start, end = _a.end;
return _this.sourceAtPos(start, end);
}).join("").trim();
_this.addSlot(slot_name, JSON.stringify(slot_props, null, 2), fallback);
}
if (node.type === "EventHandler" && node.expression == null) {
if (!_this.events.has(node.name) && parent !== undefined) {
_this.events.set(node.name, {
type: "forwarded",
name: node.name,
element: parent.name
});
}
}
if ((parent === null || parent === void 0 ? void 0 : parent.type) === "Element" && node.type === "Binding" && node.name === "this") {
var prop_name = node.expression.name;
var element_name = parent.name;
if (_this.bindings.has(prop_name)) {
var existing_bindings = _this.bindings.get(prop_name);
if (!existing_bindings.elements.includes(element_name)) {
_this.bindings.set(prop_name, __assign(__assign({}, existing_bindings), { elements: __spreadArray(__spreadArray([], existing_bindings.elements, true), [element_name], false) }));
}
}
else {
_this.bindings.set(prop_name, {
elements: [element_name]
});
}
}
}
});
if (dispatcher_name !== undefined) {
callees.forEach(function (callee) {
var _a;
if (callee.name === dispatcher_name) {
var event_name = (_a = callee.arguments[0]) === null || _a === void 0 ? void 0 : _a.value;
var event_argument = callee.arguments[1];
var event_detail = event_argument === null || event_argument === void 0 ? void 0 : event_argument.value;
_this.addDispatchedEvent({
name: event_name,
detail: event_detail,
has_argument: Boolean(event_argument)
});
}
});
}
return {
props: ComponentParser.mapToArray(this.props).map(function (prop) {
if (_this.bindings.has(prop.name)) {
return __assign(__assign({}, prop), { type: "null | " +
_this.bindings
.get(prop.name)
.elements.sort()
.map(function (element) { return (0, element_tag_map_1.getElementByTag)(element); })
.join(" | ") });
}
return prop;
}),
moduleExports: ComponentParser.mapToArray(this.moduleExports),
slots: ComponentParser.mapToArray(this.slots)
.map(function (slot) {
try {
var slot_props_1 = JSON.parse(slot.slot_props);
var new_props_1 = [];
Object.keys(slot_props_1).forEach(function (key) {
var _a;
if (slot_props_1[key].replace && slot_props_1[key].value !== undefined) {
slot_props_1[key].value = (_a = _this.props.get(slot_props_1[key].value)) === null || _a === void 0 ? void 0 : _a.type;
}
if (slot_props_1[key].value === undefined)
slot_props_1[key].value = "any";
new_props_1.push("".concat(key, ": ").concat(slot_props_1[key].value));
});
var formatted_slot_props = new_props_1.length === 0 ? "{}" : "{ " + new_props_1.join(", ") + " }";
return __assign(__assign({}, slot), { slot_props: formatted_slot_props });
}
catch (e) {
return slot;
}
})
.sort(function (a, b) {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
}),
events: ComponentParser.mapToArray(this.events),
typedefs: ComponentParser.mapToArray(this.typedefs),
rest_props: this.rest_props,
"extends": this["extends"],
componentComment: this.componentComment
};
};
return ComponentParser;
}());
exports["default"] = ComponentParser;