UNPKG

@marko/compiler

Version:
133 lines (114 loc) 4.89 kB
"use strict";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; }