QL code and tests for C#/C++/JavaScript.

This commit is contained in:
Pavel Avgustinov
2018-08-02 17:53:23 +01:00
commit b55526aa58
10684 changed files with 581163 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Module imports in node.js can be cyclic, that is, a module can (directly or indirectly) import itself.
In order to prevent an infinite loop, such cyclic imports return incomplete copies of the loaded
module, which do not yet have all exported members available. Such incomplete modules are difficult
to work with, and cyclic dependencies in general make the code hard to maintain and understand.
Consequently, cyclic imports should be avoided.
</p>
</overview>
<recommendation>
<p>
Refactor the involved modules to break the dependency cycle.
</p>
</recommendation>
<example>
<p>
In the following example, module <code>a.js</code> depends on module <code>b.js</code>, which in turn
depends on module <code>a.js</code>, so they each import the other module, leading to an import cycle.
</p>
<sample src="examples/CyclicImport.js" />
<p>
The cyclic dependency can be fixed by moving <code>firstName</code> into module <code>b.js</code>,
so that it no longer depends on <code>a.js</code>:
</p>
<sample src="examples/CyclicImportGood.js" />
</example>
<references>
<li>Brad Harris: <a href="http://selfcontained.us/2012/05/08/node-js-circular-dependencies/">node.js and circular dependencies</a>.</li>
<li>Node.js Manual: <a href="http://nodejs.org/api/modules.html#modules_cycles">Modules</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,88 @@
/**
* @name Cyclic module import
* @description If a module indirectly imports itself, some modules involved in the import cycle may end up
* with partially loaded dependencies. This is error-prone and should be avoided.
* @kind problem
* @problem.severity warning
* @id js/node/cyclic-import
* @tags reliability
* maintainability
* frameworks/node.js
* @precision medium
*/
import javascript
/**
* Gets the shortest suffix of the path of `c1` that differs from the
* corresponding suffix of `c2`; if that suffix is a proper suffix, it is
* additionally prefixed with `.../`.
*/
string ppDiff(Container c1, Container c2) {
relatedAncestors(c1, c2) and
if c1.getBaseName() = c2.getBaseName() then
result = ppDiff(c1.getParentContainer(), c2.getParentContainer()) + "/" + c1.getBaseName()
else if not exists(c1.getParentContainer()) or
sourceLocationPrefix(c1.getParentContainer().getAbsolutePath()) then
result = "/" + c1.getBaseName()
else
result = ".../" + c1.getBaseName()
}
/**
* Holds if `c1` and `c2` are related modules (as determined by predicate
* `relatedModules`), or (transitive) parent folders of such modules.
*
* This predicate is used to restrict the domain of `ppDiff`.
*/
predicate relatedAncestors(Container c1, Container c2) {
exists (NodeModule m, NodeModule n | relatedModules(m, n) |
c1 = m.getFile() and c2 = n.getFile()
) or
relatedAncestors(c1.(Folder).getAChildContainer(), c2.(Folder).getAChildContainer())
}
/**
* Gets a pretty-printed name for `m` that distinguishes it from `other`:
* this is simply the name of `m` it is different from the name of `other`,
* or else a suffix of the path of `m` that is different from `other` as
* computed by `ppDiff`.
*/
string pp(NodeModule m, NodeModule other) {
relatedModules(m, other) and
if m.getName() = other.getName() and m != other then
result = ppDiff(m.getFile(), other.getFile())
else
result = m.getName()
}
/**
* Holds if `m` imports `n` or vice versa.
*
* This predicate is used to restrict the domain of `pp`.
*/
predicate relatedModules(NodeModule m, NodeModule n) {
n = m.getAnImportedModule() or m = n.getAnImportedModule()
}
from NodeModule m, Require r, NodeModule imported, string msg,
ASTNode linktarget, string linktext
where r = m.getAnImport() and
imported = r.getImportedModule() and
if imported = m then
// set linktarget and linktext to dummy values in this case
(msg = "directly imports itself" and linktarget = m and linktext = "")
else
// find an import in `imported` that (directly or indirectly) imports `m`
exists (Require r2, Module imported2 | r2 = imported.getAnImport() and imported2 = r2.getImportedModule() |
imported2.getAnImportedModule*() = m and
msg = "imports module " + pp(imported, m) + ", which in turn $@ it" and
linktarget = r2 and
// check whether it is a direct or indirect import
(if imported2 = m then
linktext = "imports"
else
// only report indirect imports if there is no direct import
(linktext = "indirectly imports" and not imported.getAnImportedModule() = m))
)
select r, "Module " + pp(m, imported) + " " + msg + ".", linktarget, linktext

View File

@@ -0,0 +1,47 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Since JavaScript is a dynamically typed language, module imports in node.js are not statically checked
for correctness: calls to <code>require</code> simply return an object containing all the exports of
the imported module, and accessing a member that was not, in fact, exported, yields
<code>undefined</code>. This is most likely unintentional and usually indicates a bug.
</p>
</overview>
<recommendation>
<p>
Examine the import in question and determine the correct name of the symbol to import.
</p>
</recommendation>
<example>
<p>
In the following example, module <code>point.js</code> exports the function <code>Point</code> by
assigning it to <code>module.exports</code>. The client module <code>client.js</code> tries to
import it by reading from the <code>Point</code> property, but since this property does not exist
the result will be <code>undefined</code>, and the <code>new</code> invocation will fail.
</p>
<sample src="examples/DubiousImport.js" />
<p>
Instead of reading the <code>Point</code> property, <code>client.js</code> should directly use
the result of the <code>require</code> call:
</p>
<sample src="examples/DubiousImportGood.js" />
</example>
<references>
<li>Node.js Manual: <a href="http://nodejs.org/api/modules.html">Modules</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,68 @@
/**
* @name Dubious import
* @description Importing a symbol from a module that does not export it most likely indicates a bug.
* @kind problem
* @problem.severity warning
* @id js/node/import-without-export
* @tags reliability
* maintainability
* frameworks/node.js
* @precision low
*/
import javascript
/** Holds if `m` is likely to have exports that are not picked up by the analysis. */
predicate hasUntrackedExports(NodeModule m) {
// look for assignments of the form `module.exports[p] = ...`, where we cannot
// determine the name of the exported property being assigned
exists (DataFlow::PropWrite pwn |
pwn.getBase().analyze().getAValue() = m.getAModuleExportsValue() and
not exists(pwn.getPropertyName())
)
or
// look for assignments of the form `module.exports = exp` where `exp` is indefinite
exists (AbstractModuleObject am, AnalyzedPropertyWrite apw, DataFlow::AnalyzedNode exp |
am.getModule() = m and
apw.writes(am, "exports", exp) and
exp.getAValue().isIndefinite(_)
)
or
// look for function calls of the form `f(module.exports)`
exists (InvokeExpr invk |
invk.getAnArgument().analyze().getAValue() = m.getAModuleExportsValue()
)
}
/**
* Holds if there is an assignment anywhere defining `prop` on the result of
* a `require` import of module `m`.
*/
predicate propDefinedOnRequire(NodeModule m, string prop) {
exists (DataFlow::ModuleImportNode imp |
imp.asExpr().(Require).getImportedModule() = m and
exists(imp.getAPropertyWrite(prop))
)
}
/**
* Holds if the base expression of `pacc` could refer to the result of
* a `require` import of module `m`.
*/
predicate propAccessOn(PropAccess pacc, NodeModule m) {
exists (DataFlow::ModuleImportNode imp |
imp.asExpr().(Require).getImportedModule() = m and
imp.flowsToExpr(pacc.getBase())
)
}
from NodeModule m, PropAccess pacc, string prop
where propAccessOn(pacc, m) and count(NodeModule mm | propAccessOn(pacc, mm)) = 1 and
prop = pacc.getPropertyName() and
// m doesn't export 'prop'
not prop = m.getAnExportedSymbol() and
// 'prop' isn't otherwise defined on m
not propDefinedOnRequire(m, prop) and
// m doesn't use complicated exports
not hasUntrackedExports(m)
select pacc, "Module $@ does not export symbol " + prop + ".", m, m.getName()

View File

@@ -0,0 +1,48 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Node.js modules that only export a single value commonly do so by assigning it directly to the
<code>module.exports</code> property. A common mistake is to assign it to the <code>exports</code>
variable instead, but this simply overwrites the value of <code>exports</code> without affecting
the value of <code>module.exports</code>, and does not lead to anything being exported.
</p>
</overview>
<recommendation>
<p>
Rewrite the assignment to assign to <code>module.exports</code> instead.
</p>
</recommendation>
<example>
<p>
In the following example, module <code>point.js</code> attempts to export the function <code>Point</code>
by assigning it to <code>exports</code>. As explained above, this does not work as expected: after
the assignment, the <code>exports</code> <i>variable</i> will contain a reference to <code>Point</code>, but
the <code>module.exports</code> <i>property</i> still contains a reference to an empty object.
Consequently, the client code in <code>client.js</code> will fail, since it attempts to call an object as
a constructor.
</p>
<sample src="examples/InvalidExport.js" />
<p>
Instead of assigning to <code>exports</code>, <code>point.js</code> should assign to <code>module.exports</code>:
</p>
<sample src="examples/InvalidExportGood.js" />
</example>
<references>
<li>Node.js Manual: <a href="http://nodejs.org/api/modules.html#modules_exports_alias">exports alias</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,49 @@
/**
* @name Assignment to exports variable
* @description Assigning to the special 'exports' variable only overwrites its value and does not export
* anything. Such an assignment is hence most likely unintentional.
* @kind problem
* @problem.severity warning
* @id js/node/assignment-to-exports-variable
* @tags maintainability
* frameworks/node.js
* external/cwe/cwe-563
* @precision very-high
*/
import javascript
/**
* Holds if `assign` assigns the value of `nd` to `exportsVar`, which is an `exports` variable
*/
predicate exportsAssign(Assignment assgn, Variable exportsVar, DataFlow::Node nd) {
exists (NodeModule m |
exportsVar = m.getScope().getVariable("exports") and
assgn.getLhs() = exportsVar.getAnAccess() and
nd = assgn.getRhs().flow()
)
or
exportsAssign(assgn, exportsVar, nd.getASuccessor())
}
/**
* Holds if `pw` assigns the value of `nd` to `module.exports`.
*/
predicate moduleExportsAssign(DataFlow::PropWrite pw, DataFlow::Node nd) {
pw.getBase().asExpr() instanceof ModuleAccess and
pw.getPropertyName() = "exports" and
nd = pw.getRhs()
or
moduleExportsAssign(pw, nd.getASuccessor())
}
from Assignment assgn, Variable exportsVar, DataFlow::Node exportsVal
where exportsAssign(assgn, exportsVar, exportsVal) and
not exists(exportsVal.getAPredecessor()) and
not (
// this is OK if `exportsVal` flows into `module.exports`
moduleExportsAssign(_, exportsVal) and
// however, if there are no further uses of `exports` the assignment is useless anyway
strictcount (exportsVar.getAnAccess()) > 1
)
select assgn, "Assigning to 'exports' does not export anything."

View File

@@ -0,0 +1,48 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Referencing an otherwise undeclared global variable in a module that exports
a definition of the same name is confusing and may indicate a bug.
</p>
</overview>
<recommendation>
<p>
If the global variable reference is intentional, consider adding a JSLint
<code>/*global ...*/</code> directive or an externs declaration to declare the variable.
</p>
<p>
If the global variable reference is unintentional, qualifying the reference
with <code>exports</code> will make it refer to the exported definition instead.
</p>
</recommendation>
<example>
<p>
In the following example, the module exports two functions <code>checkOne</code>
and <code>checkList</code>. The latter is also stored in a variable of the same name
that is local to the module, but the former is not. Hence the call <code>checkOne(xs[i])</code>
on line 7 does not refer to the function defined on line 1, but to an otherwise undeclared
global variable also called <code>checkOne</code>.
</p>
<sample src="examples/MissingExports.js" />
<p>
Assuming that the intention is to call the <code>checkOne</code> function defined on line 1,
the call should be qualified with <code>exports</code> like this:
</p>
<sample src="examples/MissingExportsGood.js" />
</example>
<references>
<li>Node.js: <a href="https://nodejs.org/api/modules.html">Modules</a>.</li>
<li>JSLint Help: <a href="http://www.jslint.com/help.html">JSLint Directives</a>.</li>
<li>Closure Compiler: <a href="https://developers.google.com/closure/compiler/docs/api-tutorial3">Advanced Compilation and Externs</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,43 @@
/**
* @name Missing exports qualifier
* @description Referencing an undeclared global variable in a module that exports
* a definition of the same name is confusing and may indicate a bug.
* @kind problem
* @problem.severity error
* @id js/node/missing-exports-qualifier
* @tags maintainability
* frameworks/node.js
* @precision high
*/
import javascript
/** Holds if variable `v` is assigned somewhere in module `m`. */
predicate definedInModule(GlobalVariable v, NodeModule m) {
exists (LValue def |
def.getTopLevel() = m and
def.(Expr).accessesGlobal(v.getName())
)
}
from NodeModule m, GlobalVariable f, InvokeExpr invk, ASTNode export, GlobalVarAccess acc
where m.exports(f.getName(), export) and
acc = f.getAnAccess() and
invk.getCallee() = acc and
invk.getTopLevel() = m and
// don't flag if the variable is defined in the same module
not definedInModule(f, m) and
// don't flag if there is a linter directive declaring the variable
not exists (Linting::GlobalDeclaration glob |
glob.declaresGlobalForAccess(acc)
) and
// don't flag if there is an externs declaration for the variable
not exists (ExternalGlobalDecl egd | egd.getName() = f.getName()) and
// don't flag if the invocation could refer to a property introduced by `with`
not exists (WithStmt with | with.mayAffect(invk.getCallee()))
select invk, "'" + f.getName() + "' references an undeclared global variable, "
+ "not the variable exported $@.", export, "here"

View File

@@ -0,0 +1,55 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Node.js modules can be imported either directly by specifying a file or folder, or indirectly
by specifying a module name, which will be looked up in a <code>node_modules</code> folder.
In the latter case, care should be taken that the imported module is either bundled with
the code that uses it, or declared as a dependency in the <code>package.json</code> file
to ensure that the import does not fail at runtime.
</p>
</overview>
<recommendation>
<p>
Declare the dependency in the <code>package.json</code> file or include an externs file
for it during extraction.
</p>
<p>
Externs files for all the core packages of Node.js are available in the
<code>tool/data/externs/nodejs</code> directory of the distribution, and are included
by default for projects created using bootstrap.
</p>
</recommendation>
<example>
<p>
In the following example, the <code>package.json</code> file specifies no dependencies,
but <code>index.js</code> imports <code>acorn</code>. This import will fail unless a copy
of <code>acorn</code> happens to be installed (for instance in the user's <code>node_modules</code>
folder). On the other hand, the import of <code>fs</code> is unproblematic, since
<code>fs</code> is a standard module that is included with every Node.js installation.
</p>
<sample src="examples/UnresolvableImport.js" />
<p>
The dependency on <code>acorn</code> should be declared in the <code>package.json</code> file:
</p>
<sample src="examples/UnresolvableImportGood.js" />
</example>
<references>
<li>Node.js Manual: <a href="https://nodejs.org/api/modules.html#modules_all_together">Module resolution algorithm</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,49 @@
/**
* @name Unresolvable import
* @description An import that cannot be resolved to a module will
* cause an exception at runtime.
* @kind problem
* @problem.severity warning
* @id js/node/unresolvable-import
* @tags maintainability
* frameworks/node.js
* @precision low
*/
import javascript
/**
* Gets the `package.json` of the nearest enclosing NPM package to which
* file `f` belongs.
*/
PackageJSON getClosestPackageJSON(Folder f) {
result = f.(NPMPackage).getPackageJSON() or
not f instanceof NPMPackage and result = getClosestPackageJSON(f.getParentContainer())
}
from Require r, string path, string mod
where path = r.getImportedPath().getValue() and
// the imported module is the initial segment of the path, up to
// `/` or the end of the string, whichever comes first; we exclude
// local paths starting with `.` or `/`, since they might refer to files
// downloaded or generated during the build
mod = path.regexpCapture("([^./][^/]*)(/.*|$)", 1) and
// exclude WebPack/Require.js loaders
not mod.matches("%!%") and
// import cannot be resolved statically
not exists (r.getImportedModule()) and
// no enclosing NPM package declares a dependency on `mod`
forex (NPMPackage pkg, PackageJSON pkgJSON |
pkg.getAModule() = r.getTopLevel() and pkgJSON = pkg.getPackageJSON() |
not pkgJSON.declaresDependency(mod, _) and
not pkgJSON.getPeerDependencies().getADependency(mod, _) and
// exclude packages depending on `fbjs`, which automatically pulls in many otherwise
// undeclared dependencies
not pkgJSON.declaresDependency("fbjs", _)
)
select r, "Module " + mod + " cannot be resolved, and is not declared as a dependency in $@.",
getClosestPackageJSON(r.getFile().getParentContainer()), "package.json"

View File

@@ -0,0 +1,73 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Specifying an unused dependency in <code>package.json</code> may make packages harder to install.
The unused dependency will still be downloaded by npm, and if it conflicts with another package
installation will become difficult or impossible.
</p>
<p>
Dependencies on packages that are only used during development (such as testing frameworks or
linters) should be listed under <code>devDependencies</code> rather than <code>dependencies</code>,
since they are not required for deployment.
</p>
</overview>
<recommendation>
<p>
Remove the unused dependency.
</p>
</recommendation>
<example>
<p>
In the following example, the <code>package.json</code> file specifies dependencies on both
<code>acorn</code> and <code>esprima</code>, but in fact only <code>acorn</code> is used.
The dependency on <code>esprima</code> can simply be removed.
</p>
<sample src="examples/UnusedDependency.js" />
<p>
As another example, the following <code>package.json</code> file specifies a dependency on
<code>eslint-plugin-babel</code>, a plugin for a popular linter:
</p>
<sample language="javascript">
{
"name": "another-example-package",
"version": "0.1.0",
"dependencies": {
"eslint-plugin-babel": "3.3.0"
}
}
</sample>
<p>
Since this dependency is only used during development, it should instead be listed under
<code>devDependencies</code>:
</p>
<sample language="javascript">
{
"name": "another-example-package",
"version": "0.1.0",
"devDependencies": {
"eslint-plugin-babel": "3.3.0"
}
}
</sample>
</example>
<references>
<li>NPM Manual: <a href="https://www.npmjs.org/doc/files/package.json.html">package.json</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,110 @@
/**
* @name Unused npm dependency
* @description If unnecessary package dependencies are included in package.json, the
* package will become harder to install.
* @kind problem
* @problem.severity warning
* @id js/node/unused-npm-dependency
* @tags maintainability
* frameworks/node.js
* @precision medium
*/
import javascript
/**
* Holds if the NPM package `pkg` declares a dependency on package `name`,
* and `dep` is the corresponding declaration in the `package.json` file.
*/
predicate declaresDependency(NPMPackage pkg, string name, JSONValue dep) {
dep = pkg.getPackageJSON().getDependencies().getPropValue(name)
}
/**
* Gets a path expression in a module belonging to `pkg`.
*/
PathExpr getAPathExpr(NPMPackage pkg) {
result.getEnclosingModule() = pkg.getAModule()
}
/**
* Gets a URL-valued attribute in a module or HTML file belonging to `pkg`.
*/
DOM::AttributeDefinition getAURLAttribute(NPMPackage pkg) {
result.getFile() = pkg.getAFile() and
DOM::isUrlValuedAttribute(result)
}
/**
* Gets the name of a script in the 'scripts' object of `pkg`.
* The script makes use of a declared `dependency` of `pkg`.
*/
string getPackageScriptNameWithDependency(NPMPackage pkg, string dependency){
exists (JSONObject scriptsObject, string scriptName, string script |
declaresDependency(pkg, dependency, _) and
scriptsObject = pkg.getPackageJSON().getPropValue("scripts") and
script = scriptsObject.getPropStringValue(scriptName) and
script.regexpMatch(".*\\b\\Q" + dependency + "\\E\\b.*") and
result = scriptName
)
}
/**
* Holds if the NPM package `pkg` declares a dependency on package `name`,
* and uses it at least once.
*/
predicate usesDependency(NPMPackage pkg, string name) {
declaresDependency(pkg, name, _) and
(
// there is a path expression (e.g., in a `require` or `import`) that
// references `pkg`
exists (PathExpr path | path = getAPathExpr(pkg) |
// check whether the path is `name` or starts with `name/`, ignoring a prefix that ends with '!' (example: "scriptloader!moment")
path.getValue().regexpMatch("(.*!)?\\Q" + name + "\\E(/.*)?")
)
or
// there is an HTML URL attribute that may reference `pkg`
exists (DOM::AttributeDefinition attr | attr = getAURLAttribute(pkg) |
// check whether the URL contains `node_modules/name`
attr.getStringValue().regexpMatch(".*\\bnode_modules/\\Q" + name + "\\E(/.*)?")
)
or
// there is a reference in a package.json white-listed script
exists (string packageScriptName |
packageScriptName = getPackageScriptNameWithDependency(pkg, name) |
packageScriptName = "preinstall" or
packageScriptName = "install" or
packageScriptName = "postinstall"
)
)
}
/**
* Holds if `pkg` implicitly requires module `name`.
*
* Currently, the only implicit requires that are recognized are Express
* view engine definitions, which (may) implicitly require the specified
* engine as a module.
*/
predicate implicitRequire(NPMPackage pkg, string name) {
// look for Express `set('view engine', ...)` calls
exists (MethodCallExpr setViewEngine, string engine |
Express::appCreation().flowsToExpr(setViewEngine.getReceiver()) and
setViewEngine.getMethodName() = "set" and
setViewEngine.getArgument(0).getStringValue() = "view engine" and
setViewEngine.getArgument(1).getStringValue() = engine and
setViewEngine.getTopLevel() = pkg.getAModule() |
// chop off leading dot, if any
if engine.matches(".%") then
name = engine.suffix(1)
else
name = engine
)
}
from NPMPackage pkg, string name, JSONValue dep
where exists (pkg.getAModule()) and
declaresDependency(pkg, name, dep) and
not usesDependency(pkg, name) and
not implicitRequire(pkg, name)
select dep, "Unused dependency '" + name + "'."

View File

@@ -0,0 +1,17 @@
// a.js
var b = require('./b');
var title = "Ms";
function example() {
return title + " " + b.fullName;
}
exports.firstName = "Ada";
// b.js
var a = require('./a');
var lastName = "Lovelace";
exports.fullName = a.firstName + " " + lastName;

View File

@@ -0,0 +1,14 @@
// a.js
var b = require('./b');
var title = "Ms";
function example() {
return title + " " + b.fullName;
}
// b.js
var firstName = "Ada",
lastName = "Lovelace";
exports.fullName = firstName + " " + lastName;

View File

@@ -0,0 +1,17 @@
// point.js
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.distance = function() {
return Math.sqrt(this.x*this.x+this.y*this.y);
};
module.exports = Point;
// client.js
var Point = require('./point').Point;
var pyth = new Point(3, 4);
console.log(pyth.distance());

View File

@@ -0,0 +1,17 @@
// point.js
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.distance = function() {
return Math.sqrt(this.x*this.x+this.y*this.y);
};
module.exports = Point;
// client.js
var Point = require('./point');
var pyth = new Point(3, 4);
console.log(pyth.distance());

View File

@@ -0,0 +1,17 @@
// point.js
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.distance = function() {
return Math.sqrt(this.x*this.x+this.y*this.y);
};
exports = Point;
// client.js
var Point = require('./point');
var pyth = new Point(3, 4);
console.log(pyth.distance());

View File

@@ -0,0 +1,17 @@
// point.js
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.distance = function() {
return Math.sqrt(this.x*this.x+this.y*this.y);
};
module.exports = Point;
// client.js
var Point = require('./point');
var pyth = new Point(3, 4);
console.log(pyth.distance());

View File

@@ -0,0 +1,8 @@
exports.checkOne = function(x) {
if (!x) throw new Error();
};
var checkList = exports.checkList = function(xs) {
for (var i=0; i<xs.length; ++i)
checkOne(xs[i]);
};

View File

@@ -0,0 +1,8 @@
exports.checkOne = function(x) {
if (!x) throw new Error();
};
var checkList = exports.checkList = function(xs) {
for (var i=0; i<xs.length; ++i)
exports.checkOne(xs[i]);
};

View File

@@ -0,0 +1,10 @@
// package.json
{
"name": "example-package",
"version": "0.1.0"
}
// index.js
var acorn = require('acorn'),
fs = require('fs');
acorn.parse(fs.readFileSync('tst.js'), 'utf-8');

View File

@@ -0,0 +1,13 @@
// package.json
{
"name": "example-package",
"version": "0.1.0",
"dependencies": {
"acorn": "*"
}
}
// index.js
var acorn = require('acorn'),
fs = require('fs');
acorn.parse(fs.readFileSync('tst.js'), 'utf-8');

View File

@@ -0,0 +1,14 @@
// package.json
{
"name": "example-package",
"version": "0.1.0",
"dependencies": {
"acorn": "*",
"esprima": "~2.0.0"
}
}
// index.js
var acorn = require('acorn'),
fs = require('fs');
acorn.parse(fs.readFileSync('tst.js'), 'utf-8');