Created
July 20, 2014 03:31
-
-
Save jameskeane/4ed3aef8ce99da104bf0 to your computer and use it in GitHub Desktop.
closure library for ternjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(mod) { | |
if (typeof exports == "object" && typeof module == "object") // CommonJS | |
return mod(require("../lib/infer"), require("../lib/tern")); | |
if (typeof define == "function" && define.amd) // AMD | |
return define(["../lib/infer", "../lib/tern"], mod); | |
mod(tern, tern); | |
})(function(infer, tern) { | |
"use strict"; | |
var WG_TEMP_OBJ = 40; | |
function resolvePath(base, path) { | |
if (path[0] == "/") return path; | |
var slash = base.lastIndexOf("/"), m; | |
if (slash >= 0) path = base.slice(0, slash + 1) + path; | |
while (m = /[^\/]*[^\/\.][^\/]*\/\.\.\//.exec(path)) | |
path = path.slice(0, m.index) + path.slice(m.index + m[0].length); | |
return path.replace(/(^|[^\.])\.\//g, "$1"); | |
} | |
function normPath(name) { return name.replace(/\\/g, "/"); } | |
function resolveProjectPath(server, pth) { | |
return resolvePath(normPath(server.options.projectDir) + "/", normPath(pth)); | |
} | |
function each(arr, fn) { | |
for (var i = 0; i < arr.length; i++) fn(arr[i], i, arr); | |
} | |
function map(arr, fn) { | |
var r = []; | |
each(arr, function(el, i, arr) { r.push(fn(el, i, arr)); }); | |
return r; | |
} | |
function defineFQN(name, server) { | |
var parts = name.split('.'); | |
var base = infer.cx().topScope; | |
var prop; | |
for (var i = 0; i < parts.length; i++) { | |
prop = base.defProp(parts[i]); | |
if (prop.getType()) { | |
base = prop.getType(); | |
} else { | |
base = new infer.Obj(true, parts.slice(0, i + 1).join('.')); | |
// Use a custom origin to prevent the purging of stand-in types on file | |
// reload. | |
base.origin = 'closure'; | |
base.propagate(prop, WG_TEMP_OBJ); | |
} | |
} | |
// Try to load the file providing the name. | |
var provide = server._closure.provides[name]; | |
if (provide) { | |
server.addFile(provide.path, null, server._closure.currentOrigin); | |
} | |
return prop; | |
} | |
/** | |
* Build up the dependency tree by reading the dep files. | |
*/ | |
infer.registerFunction("closureBuildDepTree", function(_self, _args, argNodes) { | |
if (!argNodes || !argNodes.length || argNodes.length < 3) return; | |
var cx = infer.cx(), server = cx.parent, data = server._closure, | |
file = argNodes[0].value; | |
var path = resolveProjectPath(server, resolvePath(data.currentFile, file)); | |
var requires = map(argNodes[2].elements, function(node) { | |
return node.value; | |
}); | |
var provides = map(argNodes[1].elements, function(node) { | |
return node.value; | |
}); | |
// build up the defines | |
data.modules[path] = { | |
path: path, | |
provides: provides, | |
requires: requires | |
}; | |
each(provides, function(def) { | |
data.provides[def] = data.modules[path]; | |
}); | |
}); | |
function getInterface(name, data) { | |
return data.interfaces[name] || (data.interfaces[name] = new infer.AVal()); | |
} | |
/** | |
* goog.provide implicitly defines the object structure. | |
* i.e. goog.provide('tern.closure.test') -> var tern = {closure: { test: {} }}; | |
*/ | |
infer.registerFunction("closureProvide", function(_self, _args, argNodes) { | |
var cx = infer.cx(), server = cx.parent, data = server._closure, | |
name = argNodes[0].value, scope = argNodes[0].sourceFile.scope; | |
defineFQN(name, server); | |
}); | |
/** | |
* goog.require implicitly defines the object structure and links the final | |
* part to the interface. | |
*/ | |
infer.registerFunction("closureRequire", function(_self, _args, argNodes) { | |
var cx = infer.cx(), server = cx.parent, data = server._closure, | |
name = argNodes[0].value, scope = argNodes[0].sourceFile.scope; | |
defineFQN(name, server); | |
}); | |
/** | |
* Set up the inheritence tree. | |
*/ | |
infer.registerFunction("closureInherits", function(_self, _args, argNodes) { | |
var child = _args[0].getType(), parent = _args[0].getType(); | |
if (!(child instanceof infer.Fn) || !(parent instanceof infer.Fn)) return; | |
// create a fn type that returns an instance of the obj | |
var proto = new infer.AVal(); | |
parent.getProp('prototype').propagate(proto); | |
child.defProp('prototype').addType(proto); | |
}); | |
/** | |
* Add a singleton getter to the obj. | |
*/ | |
infer.registerFunction("closureAddSingletonGetter", function(_self, _args, argNodes) { | |
var tp = _args[0].getType(); | |
if (!(tp instanceof infer.Fn)) return; | |
// create a fn type that returns an instance of the obj | |
var instance = new infer.AVal(); | |
tp.getProp('prototype').propagate(instance); | |
instance.origin = tp.origin; | |
var sngl = new infer.Fn('getInstance', infer.ANull, [], [], instance); | |
tp.defProp('getInstance').addType(sngl); | |
}); | |
tern.registerPlugin("closure", function(server, options) { | |
server._closure = { | |
modules: Object.create(null), | |
provides: Object.create(null), | |
provided: [], | |
interfaces: Object.create(null), | |
options: options || {}, | |
currentFile: null, | |
currentOrigin: null, | |
server: server | |
}; | |
server.on("beforeLoad", function(file) { | |
var path = this._closure.currentFile = resolveProjectPath(server, file.name); | |
var data = this._closure; | |
this._closure.currentOrigin = file.name; | |
}); | |
server.on("afterLoad", function(file) { | |
var path = this._closure.currentFile = resolveProjectPath(server, file.name); | |
this._closure.currentFile = null; | |
this._closure.currentOrigin = null; | |
this._closure.provided = []; | |
}); | |
server.on("reset", function() { | |
this._closure.modules = Object.create(null); | |
this._closure.provides = Object.create(null); | |
this._closure.interfaces = Object.create(null); | |
}); | |
// If pre loaded with deps files try to parse them | |
(options.deps || []).forEach(function(depfile) { | |
server.addFile(depfile); | |
}); | |
return { | |
defs: defs | |
}; | |
}); | |
var defs = { | |
"!name": "goog", | |
goog: { | |
addDependency: { | |
"!effects": ["custom closureBuildDepTree"], | |
"!type": "fn(path: string, defines: [string], dependencies: [string])" | |
}, | |
provide: { | |
"!effects": ["custom closureProvide"], | |
"!type": "fn(id: string)", | |
}, | |
require: { | |
"!effects": ["custom closureRequire"], | |
"!type": "fn(id: string)", | |
}, | |
inherits: { | |
"!effects": ["custom closureInherits"], | |
"!type": "fn(obj: fn(), parent: fn())" | |
}, | |
addSingletonGetter: { | |
"!effects": ["custom closureAddSingletonGetter"], | |
"!type": "fn(obj: fn())" | |
} | |
} | |
}; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment