@marko/compiler
Version:
Marko template to JS compiler.
133 lines (114 loc) • 4.89 kB
JavaScript
;require("../types/patch");
var _traverse = _interopRequireWildcard(require("@babel/traverse"));
var t = _interopRequireWildcard(require("@babel/types"));
var _definitions = require("../types/definitions");function _getRequireWildcardCache(e) {if ("function" != typeof WeakMap) return null;var r = new WeakMap(),t = new WeakMap();return (_getRequireWildcardCache = function (e) {return e ? t : r;})(e);}function _interopRequireWildcard(e, r) {if (!r && e && e.__esModule) return e;if (null === e || "object" != typeof e && "function" != typeof e) return { default: e };var t = _getRequireWildcardCache(r);if (t && t.has(e)) return t.get(e);var n = { __proto__: null },a = Object.defineProperty && Object.getOwnPropertyDescriptor;for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {var i = a ? Object.getOwnPropertyDescriptor(e, u) : null;i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u];}return n.default = e, t && t.set(e, n), n;}
_definitions.MARKO_TYPES.forEach((typeName) => {
const checkKey = `is${typeName}`;
const assertKey = `assert${typeName}`;
const checkFn = t[checkKey];
const assertFn = t[assertKey];
_traverse.NodePath.prototype[checkKey] = function (opts) {
return checkFn(this.node, opts);
};
_traverse.NodePath.prototype[assertKey] = function (opts) {
assertFn(this.node, opts);
};
});
_definitions.MARKO_ALIAS_TYPES.forEach((aliasName) => {
const checkKey = `is${aliasName}`;
const originalProtoCheck = _traverse.NodePath.prototype[checkKey];
_traverse.NodePath.prototype[checkKey] = function (opts) {
return (
t.is(aliasName, this.node, opts) ||
originalProtoCheck.call(this, this.node, opts));
};
});
// Adds a one time patch to the scope collector visitors to include
// Marko bindings for params and tag vars.
const originalCrawl = _traverse.Scope.prototype.crawl;
const patchedVisitors = new WeakSet();
_traverse.Scope.prototype.crawl = function () {
const path = this.path;
const originalTraverse = path.traverse;
path.traverse = function (visitor, state) {
state.hoistableTagVarsByScope = new Map();
path.traverse = originalTraverse;
if (!patchedVisitors.has(visitor)) {
patchedVisitors.add(visitor);
Object.assign(
_traverse.default.explode(visitor),
_traverse.default.explode({
MarkoTagBody(body) {
for (const param of body.get("params")) {
body.scope.registerBinding("param", param);
}
},
MarkoTag(tag, state) {
const tagVar = tag.get("var");
if (tagVar.node) {
tag.scope.registerBinding("local", tagVar, tag);
for (const name in tagVar.getBindingIdentifiers()) {
let curScope = tag.scope;
const binding = curScope.getBinding(name);
while (curScope = curScope.parent) {
const hoistableTagVars =
state.hoistableTagVarsByScope.get(curScope);
if (hoistableTagVars) {
hoistableTagVars[name] = hoistableTagVars[name] ?
true :
binding;
} else {
state.hoistableTagVarsByScope.set(curScope, {
[name]: binding
});
}
}
}
}
}
})
);
}
if (this.type === "Program" && this.node.params?.length) {
this.scope.registerBinding("params", this);
}
this.traverse(visitor, state);
if (state.references.length) {
const movedBindings = new Map();
for (const ref of state.references) {
const { name } = ref.node;
let curScope = ref.scope;
if (curScope.hasBinding(name)) continue;
do {
const hoistableBinding =
state.hoistableTagVarsByScope.get(curScope)?.[name];
if (hoistableBinding) {
if (hoistableBinding === true) {
throw ref.buildCodeFrameError(
"Ambiguous reference, variable was defined in multiple places and was not shadowed."
);
}
const movedBinding = movedBindings.get(hoistableBinding);
if (
!movedBinding ||
getScopeDepth(movedBinding) > getScopeDepth(curScope))
{
movedBindings.set(hoistableBinding, curScope);
}
}
} while (curScope = curScope.parent);
}
for (const [binding, scope] of movedBindings) {
binding.scope.moveBindingTo(binding.identifier.name, scope);
}
}
};
originalCrawl.call(this);
path.traverse = originalTraverse;
};
function getScopeDepth(scope) {
let depth = 0;
let cur = scope;
while (cur = cur.parent) depth++;
return depth;
}